PageRenderTime 3904ms CodeModel.GetById 23ms RepoModel.GetById 1ms app.codeStats 0ms

/src/SfxrApp.as

http://as3sfxr.googlecode.com/
ActionScript | 968 lines | 563 code | 140 blank | 265 comment | 46 complexity | c5ccd2e4f1081209eb7363f7c4a90678 MD5 | raw file
  1. package
  2. {
  3. import flash.display.CapsStyle;
  4. import flash.display.DisplayObject;
  5. import flash.display.GraphicsPath;
  6. import flash.display.GraphicsSolidFill;
  7. import flash.display.GraphicsStroke;
  8. import flash.display.IGraphicsData;
  9. import flash.display.JointStyle;
  10. import flash.display.LineScaleMode;
  11. import flash.display.Sprite;
  12. import flash.events.ContextMenuEvent;
  13. import flash.events.Event;
  14. import flash.events.KeyboardEvent;
  15. import flash.events.MouseEvent;
  16. import flash.events.TextEvent;
  17. import flash.geom.Rectangle;
  18. import flash.net.FileFilter;
  19. import flash.net.FileReference;
  20. import flash.net.navigateToURL;
  21. import flash.net.URLRequest;
  22. import flash.text.Font;
  23. import flash.text.TextField;
  24. import flash.text.TextFieldType;
  25. import flash.text.TextFormat;
  26. import flash.text.TextFormatAlign;
  27. import flash.ui.ContextMenu;
  28. import flash.ui.Keyboard;
  29. import flash.ui.Mouse;
  30. import flash.ui.MouseCursor;
  31. import flash.utils.ByteArray;
  32. import flash.utils.Dictionary;
  33. import flash.utils.Endian;
  34. import ui.TinyButton;
  35. import ui.TinyCheckbox;
  36. import ui.TinySlider;
  37. /**
  38. * SfxrApp
  39. *
  40. * Copyright 2010 Thomas Vian
  41. *
  42. * Licensed under the Apache License, Version 2.0 (the "License");
  43. * you may not use this file except in compliance with the License.
  44. * You may obtain a copy of the License at
  45. *
  46. * http://www.apache.org/licenses/LICENSE-2.0
  47. *
  48. * Unless required by applicable law or agreed to in writing, software
  49. * distributed under the License is distributed on an "AS IS" BASIS,
  50. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  51. * See the License for the specific language governing permissions and
  52. * limitations under the License.
  53. *
  54. * @author Thomas Vian
  55. */
  56. [SWF(width='640', height='500', backgroundColor='#C0B090', frameRate='25')]
  57. public class SfxrApp extends Sprite
  58. {
  59. //--------------------------------------------------------------------------
  60. //
  61. // Properties
  62. //
  63. //--------------------------------------------------------------------------
  64. [Embed(source = "assets/amiga4ever.ttf", fontName = "Amiga4Ever", mimeType = "application/x-font", embedAsCFF = "false")]
  65. private var Amiga4Ever:Class; // Pixel font, original was in a tga file
  66. [Embed(source = "assets/as3sfxr.png")]
  67. private var As3sfxrLogo:Class; // as3sfxr logo, for the top right
  68. [Embed(source = "assets/sfbtom.png")]
  69. private var SfbtomLogo:Class; // SFBTOM logo, for the bottom left corner
  70. private var _synth:SfxrSynth; // Synthesizer instance
  71. private var _sampleRate:uint = 44100; // Sample rate to export .wav at
  72. private var _bitDepth:uint = 16; // Bit depth to export .wav at
  73. private var _playOnChange:Boolean = true; // If the sound should be played after releasing a slider or changing type
  74. private var _mutePlayOnChange:Boolean; // If the change playing should be muted because of non-user changes
  75. private var _propLookup:Dictionary; // Look up for property names using a slider key
  76. private var _sliderLookup:Object; // Look up for sliders using a property name key
  77. private var _waveformLookup:Array; // Look up for waveform buttons
  78. private var _squareLookup:Array; // Look up for sliders controlling a square wave property
  79. private var _back:TinyButton; // Button to skip back a sound
  80. private var _forward:TinyButton; // Button to skip forward a sound
  81. private var _history:Vector.<SfxrParams>; // List of generated settings
  82. private var _historyPos:int; // Current history position
  83. private var _copyPaste:TextField; // Input TextField for the settings
  84. private var _fileRef:FileReference; // File reference for loading in sfs file
  85. private var _logoRect:Rectangle; // Click rectangle for SFB website link
  86. private var _sfxrRect:Rectangle; // Click rectangle for LD website link
  87. private var _volumeRect:Rectangle; // Click rectangle for resetting volume
  88. //--------------------------------------------------------------------------
  89. //
  90. // Constructor
  91. //
  92. //--------------------------------------------------------------------------
  93. /**
  94. * Waits until on the stage before init
  95. */
  96. public function SfxrApp()
  97. {
  98. if (stage) init();
  99. else addEventListener(Event.ADDED_TO_STAGE, init);
  100. }
  101. //--------------------------------------------------------------------------
  102. //
  103. // Init Method
  104. //
  105. //--------------------------------------------------------------------------
  106. /**
  107. * Initialises the synthesizer and draws the interface
  108. * @param e Added to stage event
  109. */
  110. private function init(e:Event = null):void
  111. {
  112. removeEventListener(Event.ADDED_TO_STAGE, init);
  113. _synth = new SfxrSynth();
  114. _synth.params.randomize();
  115. _propLookup = new Dictionary();
  116. _sliderLookup = {};
  117. _waveformLookup = [];
  118. _squareLookup = [];
  119. _history = new Vector.<SfxrParams>();
  120. _history.push(_synth.params);
  121. drawGraphics();
  122. drawButtons();
  123. drawSliders();
  124. drawCopyPaste();
  125. updateSliders();
  126. updateButtons();
  127. updateCopyPaste();
  128. }
  129. //--------------------------------------------------------------------------
  130. //
  131. // Button Methods
  132. //
  133. //--------------------------------------------------------------------------
  134. /**
  135. * Adds the buttons to the stage
  136. */
  137. private function drawButtons():void
  138. {
  139. // Generator
  140. addButton("PICKUP/COIN", clickPickupCoin, 4, 32);
  141. addButton("LASER/SHOOT", clickLaserShoot, 4, 62);
  142. addButton("EXPLOSION", clickExplosion, 4, 92);
  143. addButton("POWERUP", clickPowerup, 4, 122);
  144. addButton("HIT/HURT", clickHitHurt, 4, 152);
  145. addButton("JUMP", clickJump, 4, 182);
  146. addButton("BLIP/SELECT", clickBlipSelect, 4, 212);
  147. addButton("MUTATE", clickMutate, 4, 339);
  148. addButton("RANDOMIZE", clickRandomize, 4, 369, 2);
  149. // History
  150. _back = addButton("BACK", clickBack, 4, 399);
  151. _forward = addButton("FORWARD", clickForward, 4, 429);
  152. _back.enabled = false;
  153. _forward.enabled = false;
  154. // Waveform
  155. addButton("SQUAREWAVE", clickSquarewave, 130, 28, 1, true);
  156. addButton("SAWTOOTH", clickSawtooth, 250, 28, 1, true);
  157. addButton("SINEWAVE", clickSinewave, 370, 28, 1, true);
  158. addButton("NOISE", clickNoise, 490, 28, 1, true);
  159. // Play / save / export
  160. addButton("PLAY SOUND", clickPlaySound, 490, 271);
  161. addButton("LOAD SOUND", clickLoadSound, 490, 321);
  162. addButton("SAVE SOUND", clickSaveSound, 490, 351);
  163. addButton("EXPORT .WAV", clickExportWav, 490, 401, 3);
  164. addButton("44100 HZ", clickSampleRate, 490, 431);
  165. addButton("16-BIT", clickBitDepth, 490, 461);
  166. }
  167. /**
  168. * Adds a single button
  169. * @param label Text to display on the button
  170. * @param onClick Callback function called when the button is clicked
  171. * @param x X position of the button
  172. * @param y Y position of the button
  173. * @param border Thickness of the border in pixels
  174. * @param selectable If the button is selectable
  175. * @param selected If the button starts as selected
  176. */
  177. private function addButton(label:String, onClick:Function, x:Number, y:Number, border:Number = 1, selectable:Boolean = false):TinyButton
  178. {
  179. var button:TinyButton = new TinyButton(onClick, label, border, selectable);
  180. button.x = x;
  181. button.y = y;
  182. addChild(button);
  183. if(selectable) _waveformLookup.push(button);
  184. return button;
  185. }
  186. /**
  187. * Updates the buttons to reflect the synthesizer
  188. */
  189. private function updateButtons():void
  190. {
  191. _mutePlayOnChange = true;
  192. selectedSwitch(_waveformLookup[_synth.params.waveType]);
  193. _mutePlayOnChange = false;
  194. }
  195. //--------------------------------------------------------------------------
  196. //
  197. // Generator Methods
  198. //
  199. //--------------------------------------------------------------------------
  200. /**
  201. * Sets the synthesizer to generate a pickup/coin sound and previews it
  202. * @param button Button pressed
  203. */
  204. private function clickPickupCoin(button:TinyButton):void
  205. {
  206. addToHistory();
  207. _synth.params.generatePickupCoin();
  208. updateSliders();
  209. updateButtons();
  210. updateCopyPaste();
  211. _synth.play();
  212. }
  213. /**
  214. * Sets the synthesizer to generate a laser/shoot sound and previews it
  215. * @param button Button pressed
  216. */
  217. private function clickLaserShoot(button:TinyButton):void
  218. {
  219. addToHistory();
  220. _synth.params.generateLaserShoot();
  221. updateSliders();
  222. updateButtons();
  223. updateCopyPaste();
  224. _synth.play();
  225. }
  226. /**
  227. * Sets the synthesizer to generate an explosion sound and previews it
  228. * @param button Button pressed
  229. */
  230. private function clickExplosion(button:TinyButton):void
  231. {
  232. addToHistory();
  233. _synth.params.generateExplosion();
  234. updateSliders();
  235. updateButtons();
  236. updateCopyPaste();
  237. _synth.play();
  238. }
  239. /**
  240. * Sets the synthesizer to generate a powerup sound and previews it
  241. * @param button Button pressed
  242. */
  243. private function clickPowerup(button:TinyButton):void
  244. {
  245. addToHistory();
  246. _synth.params.generatePowerup();
  247. updateSliders();
  248. updateButtons();
  249. updateCopyPaste();
  250. _synth.play();
  251. }
  252. /**
  253. * Sets the synthesizer to generate a hit/hurt sound and previews it
  254. * @param button Button pressed
  255. */
  256. private function clickHitHurt(button:TinyButton):void
  257. {
  258. addToHistory();
  259. _synth.params.generateHitHurt();
  260. updateSliders();
  261. updateButtons();
  262. updateCopyPaste();
  263. _synth.play();
  264. }
  265. /**
  266. * Sets the synthesizer to generate a jump sound and previews it
  267. * @param button Button pressed
  268. */
  269. private function clickJump(button:TinyButton):void
  270. {
  271. addToHistory();
  272. _synth.params.generateJump();
  273. updateSliders();
  274. updateButtons();
  275. updateCopyPaste();
  276. _synth.play();
  277. }
  278. /**
  279. * Sets the synthesizer to generate a blip/select sound and previews it
  280. * @param button Button pressed
  281. */
  282. private function clickBlipSelect(button:TinyButton):void
  283. {
  284. addToHistory();
  285. _synth.params.generateBlipSelect();
  286. updateSliders();
  287. updateButtons();
  288. updateCopyPaste();
  289. _synth.play();
  290. }
  291. /**
  292. * Sets the synthesizer to mutate the sound and preview it
  293. * @param button Button pressed
  294. */
  295. private function clickMutate(button:TinyButton):void
  296. {
  297. addToHistory();
  298. _synth.params.mutate();
  299. updateSliders();
  300. updateButtons();
  301. updateCopyPaste();
  302. _synth.play();
  303. }
  304. /**
  305. * Sets the synthesizer to randomize the sound and preview it
  306. * @param button Button pressed
  307. */
  308. private function clickRandomize(button:TinyButton):void
  309. {
  310. addToHistory();
  311. _synth.params.randomize();
  312. updateSliders();
  313. updateButtons();
  314. updateCopyPaste();
  315. _synth.play();
  316. }
  317. //--------------------------------------------------------------------------
  318. //
  319. // History Methods
  320. //
  321. //--------------------------------------------------------------------------
  322. /**
  323. * When the back button is clicked, moves back through the history
  324. * @param button TinyButton clicked
  325. */
  326. private function clickBack(button:TinyButton):void
  327. {
  328. _historyPos--;
  329. if(_historyPos == 0) _back.enabled = false;
  330. if(_historyPos < _history.length - 1) _forward.enabled = true;
  331. _synth.stop();
  332. _synth.params = _history[_historyPos];
  333. updateSliders();
  334. updateButtons();
  335. updateCopyPaste();
  336. _synth.play();
  337. }
  338. /**
  339. * When the forward button is clicked, moves forward through the history
  340. * @param button TinyButton clicked
  341. */
  342. private function clickForward(button:TinyButton):void
  343. {
  344. _historyPos++;
  345. if(_historyPos > 0) _back.enabled = true;
  346. if(_historyPos == _history.length - 1) _forward.enabled = false;
  347. _synth.stop();
  348. _synth.params = _history[_historyPos];
  349. updateSliders();
  350. updateButtons();
  351. updateCopyPaste();
  352. _synth.play();
  353. }
  354. /**
  355. * Adds a new sound effect to the history.
  356. * Called just before a new sound effect is generated.
  357. */
  358. private function addToHistory():void
  359. {
  360. _historyPos++;
  361. _synth.params = _synth.params.clone();
  362. _history = _history.slice(0, _historyPos);
  363. _history.push(_synth.params);
  364. _back.enabled = true;
  365. _forward.enabled = false;
  366. }
  367. //--------------------------------------------------------------------------
  368. //
  369. // Waveform Methods
  370. //
  371. //--------------------------------------------------------------------------
  372. /**
  373. * Selects the squarewave waveform type
  374. * @param button Button pressed
  375. */
  376. private function clickSquarewave(button:TinyButton):void
  377. {
  378. _synth.params.waveType = 0;
  379. selectedSwitch(button);
  380. updateCopyPaste();
  381. if (_playOnChange && !_mutePlayOnChange) _synth.play();
  382. }
  383. /**
  384. * Selects the sawtooth waveform type
  385. * @param button Button pressed
  386. */
  387. private function clickSawtooth(button:TinyButton):void
  388. {
  389. _synth.params.waveType = 1;
  390. selectedSwitch(button);
  391. updateCopyPaste();
  392. if (_playOnChange && !_mutePlayOnChange) _synth.play();
  393. }
  394. /**
  395. * Selects the sinewave waveform type
  396. * @param button Button pressed
  397. */
  398. private function clickSinewave(button:TinyButton):void
  399. {
  400. _synth.params.waveType = 2;
  401. selectedSwitch(button);
  402. updateCopyPaste();
  403. if (_playOnChange && !_mutePlayOnChange) _synth.play();
  404. }
  405. /**
  406. * Selects the noise waveform type
  407. * @param button Button pressed
  408. */
  409. private function clickNoise(button:TinyButton):void
  410. {
  411. _synth.params.waveType = 3;
  412. selectedSwitch(button);
  413. updateCopyPaste();
  414. if (_playOnChange && !_mutePlayOnChange) _synth.play();
  415. }
  416. /**
  417. * Unselects all the waveform buttons and selects the one passed in
  418. * @param select Selects this button
  419. */
  420. private function selectedSwitch(select:TinyButton):void
  421. {
  422. for(var i:uint = 0, l:uint = _waveformLookup.length; i < l; i++)
  423. {
  424. if(_waveformLookup[i] != select) _waveformLookup[i].selected = false;
  425. }
  426. if(!select.selected) select.selected = true;
  427. for(i = 0; i < 2; i++)
  428. {
  429. _squareLookup[i].dimLabel = _synth.params.waveType != 0;
  430. }
  431. }
  432. //--------------------------------------------------------------------------
  433. //
  434. // Play/Save/Export Methods
  435. //
  436. //--------------------------------------------------------------------------
  437. /**
  438. * Previews the sound
  439. * @param button Button pressed
  440. */
  441. private function clickPlaySound(button:TinyButton):void
  442. {
  443. _synth.play();
  444. }
  445. /**
  446. * Opens a browse window to load a sound setting file
  447. * @param button Button pressed
  448. */
  449. private function clickLoadSound(button:TinyButton):void
  450. {
  451. _fileRef = new FileReference();
  452. _fileRef.addEventListener(Event.SELECT, onSelectSettings);
  453. _fileRef.browse([new FileFilter("SFX Sample Files (*.sfs)", "*.sfs")]);
  454. }
  455. /**
  456. * When the user selects a file, begins loading it
  457. * @param e Select event
  458. */
  459. private function onSelectSettings(e:Event):void
  460. {
  461. _fileRef.cancel();
  462. _fileRef.removeEventListener(Event.SELECT, onSelectSettings);
  463. _fileRef.addEventListener(Event.COMPLETE, onLoadSettings);
  464. _fileRef.load();
  465. }
  466. /**
  467. * Once loaded, passes the file to the synthesizer to parse
  468. * @param e Complete event
  469. */
  470. private function onLoadSettings(e:Event):void
  471. {
  472. _fileRef.removeEventListener(Event.COMPLETE, onLoadSettings);
  473. addToHistory();
  474. setSettingsFile(_fileRef.data);
  475. updateSliders();
  476. updateButtons();
  477. updateCopyPaste();
  478. _fileRef = null;
  479. }
  480. /**
  481. * Saves out a sound settings file
  482. * @param button Button pressed
  483. */
  484. private function clickSaveSound(button:TinyButton):void
  485. {
  486. var file:ByteArray = getSettingsFile();
  487. new FileReference().save(file, "sfx.sfs");
  488. }
  489. /**
  490. * Exports the sound as a .wav file
  491. * @param button Button pressed
  492. */
  493. private function clickExportWav(button:TinyButton):void
  494. {
  495. var file:ByteArray = _synth.getWavFile(_sampleRate, _bitDepth);
  496. new FileReference().save(file, "sfx.wav");
  497. }
  498. /**
  499. * Switches the sample rate between 44100Hz and 22050Hz
  500. * @param button Button pressed
  501. */
  502. private function clickSampleRate(button:TinyButton):void
  503. {
  504. if(_sampleRate == 44100) _sampleRate = 22050;
  505. else _sampleRate = 44100;
  506. button.label = _sampleRate + " HZ";
  507. }
  508. /**
  509. * Switches the bit depth between 16-bit and 8-bit
  510. * @param button Button pressed
  511. */
  512. private function clickBitDepth(button:TinyButton):void
  513. {
  514. if(_bitDepth == 16) _bitDepth = 8;
  515. else _bitDepth = 16;
  516. button.label = _bitDepth + "-BIT";
  517. }
  518. //--------------------------------------------------------------------------
  519. //
  520. // Settings File Methods
  521. //
  522. //--------------------------------------------------------------------------
  523. /**
  524. * Writes the current parameters to a ByteArray and returns it
  525. * Compatible with the original Sfxr files
  526. * @return ByteArray of settings data
  527. */
  528. public function getSettingsFile():ByteArray
  529. {
  530. var file:ByteArray = new ByteArray();
  531. file.endian = Endian.LITTLE_ENDIAN;
  532. file.writeInt(102);
  533. file.writeInt(_synth.params.waveType);
  534. file.writeFloat(_synth.params.masterVolume);
  535. file.writeFloat(_synth.params.startFrequency);
  536. file.writeFloat(_synth.params.minFrequency);
  537. file.writeFloat(_synth.params.slide);
  538. file.writeFloat(_synth.params.deltaSlide);
  539. file.writeFloat(_synth.params.squareDuty);
  540. file.writeFloat(_synth.params.dutySweep);
  541. file.writeFloat(_synth.params.vibratoDepth);
  542. file.writeFloat(_synth.params.vibratoSpeed);
  543. file.writeFloat(0);
  544. file.writeFloat(_synth.params.attackTime);
  545. file.writeFloat(_synth.params.sustainTime);
  546. file.writeFloat(_synth.params.decayTime);
  547. file.writeFloat(_synth.params.sustainPunch);
  548. file.writeBoolean(false);
  549. file.writeFloat(_synth.params.lpFilterResonance);
  550. file.writeFloat(_synth.params.lpFilterCutoff);
  551. file.writeFloat(_synth.params.lpFilterCutoffSweep);
  552. file.writeFloat(_synth.params.hpFilterCutoff);
  553. file.writeFloat(_synth.params.hpFilterCutoffSweep);
  554. file.writeFloat(_synth.params.phaserOffset);
  555. file.writeFloat(_synth.params.phaserSweep);
  556. file.writeFloat(_synth.params.repeatSpeed);
  557. file.writeFloat(_synth.params.changeSpeed);
  558. file.writeFloat(_synth.params.changeAmount);
  559. return file;
  560. }
  561. /**
  562. * Reads parameters from a ByteArray file
  563. * Compatible with the original Sfxr files
  564. * @param file ByteArray of settings data
  565. */
  566. public function setSettingsFile(file:ByteArray):void
  567. {
  568. file.position = 0;
  569. file.endian = Endian.LITTLE_ENDIAN;
  570. var version:int = file.readInt();
  571. if(version != 100 && version != 101 && version != 102) return;
  572. _synth.params.waveType = file.readInt();
  573. _synth.params.masterVolume = (version == 102) ? file.readFloat() : 0.5;
  574. _synth.params.startFrequency = file.readFloat();
  575. _synth.params.minFrequency = file.readFloat();
  576. _synth.params.slide = file.readFloat();
  577. _synth.params.deltaSlide = (version >= 101) ? file.readFloat() : 0.0;
  578. _synth.params.squareDuty = file.readFloat();
  579. _synth.params.dutySweep = file.readFloat();
  580. _synth.params.vibratoDepth = file.readFloat();
  581. _synth.params.vibratoSpeed = file.readFloat();
  582. var unusedVibratoDelay:Number = file.readFloat();
  583. _synth.params.attackTime = file.readFloat();
  584. _synth.params.sustainTime = file.readFloat();
  585. _synth.params.decayTime = file.readFloat();
  586. _synth.params.sustainPunch = file.readFloat();
  587. var unusedFilterOn:Boolean = file.readBoolean();
  588. _synth.params.lpFilterResonance = file.readFloat();
  589. _synth.params.lpFilterCutoff = file.readFloat();
  590. _synth.params.lpFilterCutoffSweep = file.readFloat();
  591. _synth.params.hpFilterCutoff = file.readFloat();
  592. _synth.params.hpFilterCutoffSweep = file.readFloat();
  593. _synth.params.phaserOffset = file.readFloat();
  594. _synth.params.phaserSweep = file.readFloat();
  595. _synth.params.repeatSpeed = file.readFloat();
  596. _synth.params.changeSpeed = (version >= 101) ? file.readFloat() : 0.0;
  597. _synth.params.changeAmount = (version >= 101) ? file.readFloat() : 0.0;
  598. }
  599. //--------------------------------------------------------------------------
  600. //
  601. // Slider Methods
  602. //
  603. //--------------------------------------------------------------------------
  604. /**
  605. * Adds the sliders to the stage, plus single checkbox
  606. */
  607. private function drawSliders():void
  608. {
  609. addSlider("ATTACK TIME", "attackTime", 350, 70);
  610. addSlider("SUSTAIN TIME", "sustainTime", 350, 88);
  611. addSlider("SUSTAIN PUNCH", "sustainPunch", 350, 106);
  612. addSlider("DECAY TIME", "decayTime", 350, 124);
  613. addSlider("START FREQUENCY", "startFrequency", 350, 142).defaultValue = 0.5;
  614. addSlider("MIN FREQUENCY", "minFrequency", 350, 160);
  615. addSlider("SLIDE", "slide", 350, 178, true);
  616. addSlider("DELTA SLIDE", "deltaSlide", 350, 196, true);
  617. addSlider("VIBRATO DEPTH", "vibratoDepth", 350, 214);
  618. addSlider("VIBRATO SPEED", "vibratoSpeed", 350, 232);
  619. addSlider("CHANGE AMOUNT", "changeAmount", 350, 250, true);
  620. addSlider("CHANGE SPEED", "changeSpeed", 350, 268);
  621. addSlider("SQUARE DUTY", "squareDuty", 350, 286, false, true);
  622. addSlider("DUTY SWEEP", "dutySweep", 350, 304, true, true);
  623. addSlider("REPEAT SPEED", "repeatSpeed", 350, 322);
  624. addSlider("PHASER OFFSET", "phaserOffset", 350, 340, true);
  625. addSlider("PHASER SWEEP", "phaserSweep", 350, 358, true);
  626. addSlider("LP FILTER CUTOFF", "lpFilterCutoff", 350, 376).defaultValue = 1.0;
  627. addSlider("LP FILTER CUTOFF SWEEP", "lpFilterCutoffSweep", 350, 394, true);
  628. addSlider("LP FILTER RESONANCE", "lpFilterResonance", 350, 412);
  629. addSlider("HP FILTER CUTOFF", "hpFilterCutoff", 350, 430);
  630. addSlider("HP FILTER CUTOFF SWEEP", "hpFilterCutoffSweep", 350, 448, true);
  631. addSlider("", "masterVolume", 492, 251);
  632. var checkbox:TinyCheckbox = new TinyCheckbox(onCheckboxChange, "PLAY ON CHANGE");
  633. checkbox.x = 350;
  634. checkbox.y = 466;
  635. addChild(checkbox);
  636. }
  637. /**
  638. * Adds a single slider
  639. * @param label Text label to display next to the slider
  640. * @param property Property name to link with the slider
  641. * @param x X position of slider
  642. * @param y Y Position of slider
  643. * @param plusMinus If the slider ranges from -1 to 1 (true) or 0 to 1 (false)
  644. * @param square If the slider is linked to the square duty properties
  645. */
  646. private function addSlider(label:String, property:String, x:Number, y:Number, plusMinus:Boolean = false, square:Boolean = false):TinySlider
  647. {
  648. var slider:TinySlider = new TinySlider(onSliderChange, label, plusMinus);
  649. slider.x = x;
  650. slider.y = y;
  651. addChild(slider);
  652. _propLookup[slider] = property;
  653. _sliderLookup[property] = slider;
  654. if (square) _squareLookup.push(slider);
  655. return slider;
  656. }
  657. /**
  658. * Updates the property on the synthesizer to the slider's value
  659. * @param slider
  660. */
  661. private function onSliderChange(slider:TinySlider):void
  662. {
  663. _synth.params[_propLookup[slider]] = slider.value;
  664. updateCopyPaste();
  665. if (_playOnChange && !_mutePlayOnChange) _synth.play();
  666. }
  667. /**
  668. * Updates the sliders to reflect the synthesizer
  669. */
  670. private function updateSliders():void
  671. {
  672. _mutePlayOnChange = true;
  673. for(var prop:String in _sliderLookup)
  674. {
  675. _sliderLookup[prop].value = _synth.params[prop];
  676. }
  677. _mutePlayOnChange = false;
  678. }
  679. /**
  680. * Changes if the sound should play on params change
  681. * @param checkbox Checbox clicked
  682. */
  683. private function onCheckboxChange(checkbox:TinyCheckbox):void
  684. {
  685. _playOnChange = checkbox.value;
  686. }
  687. //--------------------------------------------------------------------------
  688. //
  689. // Copy Paste Methods
  690. //
  691. //--------------------------------------------------------------------------
  692. /**
  693. * Adds a TextField over the whole app.
  694. * Allows for right-click copy/paste, as well as ctrl-c/ctrl-v
  695. */
  696. private function drawCopyPaste():void
  697. {
  698. _copyPaste = new TextField();
  699. _copyPaste.addEventListener(TextEvent.TEXT_INPUT, updateFromCopyPaste);
  700. _copyPaste.addEventListener(KeyboardEvent.KEY_DOWN, updateCopyPaste);
  701. _copyPaste.addEventListener(KeyboardEvent.KEY_UP, updateCopyPaste);
  702. _copyPaste.defaultTextFormat = new TextFormat("Amiga4Ever", 8, 0);
  703. _copyPaste.wordWrap = false;
  704. _copyPaste.multiline = false;
  705. _copyPaste.type = TextFieldType.INPUT;
  706. _copyPaste.embedFonts = true;
  707. _copyPaste.width = 640;
  708. _copyPaste.height = 580;
  709. _copyPaste.x = 0;
  710. _copyPaste.y = -20;
  711. addChild(_copyPaste);
  712. _copyPaste.contextMenu = new ContextMenu();
  713. _copyPaste.contextMenu.addEventListener(ContextMenuEvent.MENU_SELECT, updateCopyPaste);
  714. Mouse.cursor = MouseCursor.ARROW;
  715. }
  716. /**
  717. * Updates the contents of the textfield to a representation of the settings
  718. * @param e Optional event
  719. */
  720. private function updateCopyPaste(e:Event = null):void
  721. {
  722. _copyPaste.text = _synth.params.getSettingsString();
  723. _copyPaste.setSelection(0, _copyPaste.text.length);
  724. stage.focus = _copyPaste;
  725. }
  726. /**
  727. * When the textfield is pasted into, and the new info parses, updates the settings
  728. * @param e Text input event
  729. */
  730. private function updateFromCopyPaste(e:TextEvent):void
  731. {
  732. if (e.text.split(",").length == 24) addToHistory();
  733. if (!_synth.params.setSettingsString(e.text))
  734. {
  735. _copyPaste.setSelection(0, _copyPaste.text.length);
  736. stage.focus = _copyPaste;
  737. _copyPaste.text = _synth.params.getSettingsString();
  738. }
  739. _copyPaste.setSelection(0, _copyPaste.text.length);
  740. stage.focus = _copyPaste;
  741. updateSliders();
  742. updateButtons();
  743. }
  744. //--------------------------------------------------------------------------
  745. //
  746. // Graphics Methods
  747. //
  748. //--------------------------------------------------------------------------
  749. /**
  750. * Draws the extra labels, frames and lines to the stage
  751. */
  752. private function drawGraphics():void
  753. {
  754. var lines:Vector.<IGraphicsData> = new Vector.<IGraphicsData>();
  755. lines.push(new GraphicsStroke(2, false, LineScaleMode.NORMAL, CapsStyle.NONE, JointStyle.MITER, 3, new GraphicsSolidFill(0)));
  756. lines.push(new GraphicsPath(Vector.<int>([1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,2,2]),
  757. Vector.<Number>([ 114,0, 114,500,
  758. 160,66, 460,66,
  759. 160,138, 460,138,
  760. 160,246, 460,246,
  761. 160,282, 460,282,
  762. 160,318, 460,318,
  763. 160,336, 460,336,
  764. 160,372, 460,372,
  765. 160, 462, 460, 462,
  766. 160, 480, 460, 480,
  767. 590,255, 618,255, 618,411, 590,411])));
  768. lines.push(new GraphicsStroke(1, false, LineScaleMode.NORMAL, CapsStyle.NONE, JointStyle.MITER, 3, new GraphicsSolidFill(0)));
  769. lines.push(new GraphicsPath(Vector.<int>([1,2,1,2,1,2,1,2]),
  770. Vector.<Number>([ 160, 65, 160, 481,
  771. 460, 65, 460, 481])));
  772. graphics.drawGraphicsData(lines);
  773. graphics.lineStyle(2, 0xFF0000, 1, true, LineScaleMode.NORMAL, CapsStyle.SQUARE, JointStyle.MITER);
  774. graphics.drawRect(549.5, 250.5, 43, 10);
  775. addLabel("CLICK ON LABELS", 484, 118, 0x877569, 500);
  776. addLabel("TO RESET SLIDERS", 480, 132, 0x877569, 500);
  777. addLabel("COPY/PASTE SETTINGS", 470, 158, 0x877569, 500);
  778. addLabel("TO SHARE SOUNDS", 484, 172, 0x877569, 500);
  779. addLabel("BASED ON SFXR BY", 480, 198, 0x877569, 500);
  780. addLabel("TOMAS PETTERSSON", 480, 212, 0x877569, 500);
  781. addLabel("VOLUME", 516, 235, 0);
  782. addLabel("GENERATOR", 6, 8, 0x504030);
  783. addLabel("MANUAL SETTINGS", 122, 8, 0x504030);
  784. var as3sfxrLogo:DisplayObject = new As3sfxrLogo();
  785. as3sfxrLogo.x = 476;
  786. as3sfxrLogo.y = 62;
  787. addChild(as3sfxrLogo);
  788. var sfbtomLogo:DisplayObject = new SfbtomLogo();
  789. sfbtomLogo.x = 4;
  790. sfbtomLogo.y = 459;
  791. addChild(sfbtomLogo);
  792. _logoRect = sfbtomLogo.getBounds(stage);
  793. _sfxrRect = new Rectangle(480, 195, 100, 30);
  794. _volumeRect = new Rectangle(516, 235, 200, 15);
  795. stage.addEventListener(MouseEvent.MOUSE_DOWN, onClick);
  796. }
  797. /**
  798. * Handles clicking either
  799. * @param e Click event
  800. */
  801. private function onClick(e:MouseEvent):void
  802. {
  803. if (_logoRect.contains(stage.mouseX, stage.mouseY)) navigateToURL(new URLRequest("http://twitter.com/SFBTom"));
  804. if (_sfxrRect.contains(stage.mouseX, stage.mouseY)) navigateToURL(new URLRequest("http://www.drpetter.se/project_sfxr.html"));
  805. if (_volumeRect.contains(stage.mouseX, stage.mouseY)) _sliderLookup["masterVolume"].value = 0.5;
  806. }
  807. /**
  808. * Adds a label
  809. * @param label Text to display
  810. * @param x X position of the label
  811. * @param y Y position of the label
  812. * @param colour Colour of the text
  813. */
  814. private function addLabel(label:String, x:Number, y:Number, colour:uint, width:Number = 200):void
  815. {
  816. var txt:TextField = new TextField();
  817. txt.defaultTextFormat = new TextFormat("Amiga4Ever", 8, colour);
  818. txt.selectable = false;
  819. txt.embedFonts = true;
  820. txt.text = label;
  821. txt.width = width;
  822. txt.height = 15;
  823. txt.x = x;
  824. txt.y = y;
  825. addChild(txt);
  826. }
  827. }
  828. }