Skip to content

Commit 3fcb0fa

Browse files
committed
Convert sounds to native Flash Sound objects and use them for playback.
1 parent 62febe9 commit 3fcb0fa

File tree

2 files changed

+59
-21
lines changed

2 files changed

+59
-21
lines changed

src/scratch/ScratchSound.as

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030

3131
package scratch {
3232
import by.blooddy.crypto.MD5;
33+
import flash.media.Sound;
3334
import flash.utils.*;
3435
import sound.*;
3536
import sound.mp3.MP3Loader;
@@ -44,10 +45,13 @@ public class ScratchSound {
4445
public var format:String = '';
4546
public var rate:int = 44100;
4647
public var sampleCount:int;
48+
public var sampleDataStart:int;
4749
public var bitsPerSample:int; // primarily used for compressed Squeak sounds; not saved
4850

4951
public var editorData:Object; // cache of data used by sound editor; not saved
52+
public var channels:uint = 1;
5053
private const WasEdited:int = -10; // special soundID used to indicate sounds that have been edited
54+
public var nativeSound:Sound;
5155

5256
// Undo support; not saved
5357
public var undoList:Array = [];
@@ -67,6 +71,8 @@ public class ScratchSound {
6771
rate = info.samplesPerSecond;
6872
sampleCount = info.sampleCount;
6973
bitsPerSample = info.bitsPerSample;
74+
channels = info.channels;
75+
sampleDataStart = info.sampleDataStart;
7076
reduceSizeIfNeeded(info.channels);
7177
} catch (e:*) {
7278
setSamples(new Vector.<int>(0), 22050);
@@ -147,12 +153,15 @@ Scratch.app.log('Converting MP3 to WAV: ' + soundName);
147153
if (soundData) MP3Loader.convertToScratchSound('', soundData, whenDone);
148154
else setSamples(new Vector.<int>, 22050);
149155
}
156+
157+
var ssp:ScratchSoundPlayer = sndplayer();
158+
ssp.createNative();
150159
}
151160

152161
public function sndplayer():ScratchSoundPlayer {
153-
var player:ScratchSoundPlayer
162+
var player:ScratchSoundPlayer;
154163
if (format == 'squeak') player = new SqueakSoundPlayer(soundData, bitsPerSample, rate);
155-
else if ((format == '') || (format == 'adpcm')) player = new ScratchSoundPlayer(soundData);
164+
else if (format == '' || format == 'adpcm' || format == 'float') player = new ScratchSoundPlayer(soundData);
156165
else player = new ScratchSoundPlayer(WAVFile.empty()); // player on empty sound
157166
player.scratchSound = this;
158167
return player;

src/sound/ScratchSoundPlayer.as

Lines changed: 48 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -70,43 +70,72 @@ public class ScratchSoundPlayer {
7070
public function ScratchSoundPlayer(wavFileData:ByteArray) {
7171
getSample = getSample16Uncompressed;
7272
if (wavFileData != null) {
73-
var info:* = WAVFile.decode(wavFileData);
74-
soundData = wavFileData;
75-
startOffset = info.sampleDataStart;
76-
endOffset = startOffset + info.sampleDataSize;
77-
stepSize = info.samplesPerSecond / 44100.0;
78-
if (info.encoding == 17) {
79-
adpcmBlockSize = info.adpcmBlockSize;
80-
getSample = getSampleADPCM;
81-
} else {
82-
if (info.bitsPerSample == 8) getSample = getSample8Uncompressed;
83-
if (info.bitsPerSample == 16) getSample = getSample16Uncompressed;
73+
try {
74+
var info:* = WAVFile.decode(wavFileData);
75+
soundData = wavFileData;
76+
startOffset = info.sampleDataStart;
77+
endOffset = startOffset + info.sampleDataSize;
78+
stepSize = info.samplesPerSecond / 44100.0;
79+
if (info.encoding == 17) {
80+
adpcmBlockSize = info.adpcmBlockSize;
81+
getSample = getSampleADPCM;
82+
} else {
83+
if (info.bitsPerSample == 8) getSample = getSample8Uncompressed;
84+
if (info.bitsPerSample == 16) getSample = getSample16Uncompressed;
85+
}
8486
}
87+
catch (e:*) {}
8588
}
8689
}
8790

88-
public function atEnd():Boolean { return soundChannel == null }
91+
public function isPlaying(snd:ByteArray = null):Boolean {
92+
return (activeSounds.indexOf(this) > -1 && (!snd || soundData == snd));
93+
}
94+
95+
public function atEnd():Boolean { return soundChannel == null; }
8996

9097
public function stopPlaying():void {
9198
if (soundChannel != null) {
92-
soundChannel.stop();
99+
var sc:SoundChannel = soundChannel;
93100
soundChannel = null;
101+
sc.stop();
102+
sc.dispatchEvent(new Event(Event.SOUND_COMPLETE));
94103
}
95104
var i:int = activeSounds.indexOf(this);
96105
if (i >= 0) activeSounds.splice(i, 1);
97106
}
98107

108+
public function createNative():void {
109+
if (!!scratchSound.nativeSound) return;
110+
111+
var flashSnd:Sound = scratchSound.nativeSound = new Sound();
112+
var convertedSamples:ByteArray = new ByteArray();
113+
convertedSamples.length = endOffset - startOffset;
114+
bytePosition = startOffset;
115+
var sampleCount:uint = 0;
116+
while (bytePosition < endOffset) {
117+
var n:Number = interpolatedSample();
118+
convertedSamples.writeFloat(n);
119+
convertedSamples.writeFloat(n);
120+
++sampleCount;
121+
}
122+
123+
convertedSamples.position = 0;
124+
flashSnd.loadPCMFromByteArray(convertedSamples, sampleCount);
125+
}
126+
99127
public function startPlaying(doneFunction:Function = null):void {
100128
stopIfAlreadyPlaying();
101129
activeSounds.push(this);
102-
bytePosition = startOffset;
103-
nextSample = getSample();
104130

105-
var flashSnd:Sound = new Sound();
106-
flashSnd.addEventListener(SampleDataEvent.SAMPLE_DATA, writeSampleData);
107-
soundChannel = flashSnd.play();
131+
createNative();
132+
133+
soundChannel = scratchSound.nativeSound.play();
108134
if (soundChannel) {
109-
if (doneFunction != null) soundChannel.addEventListener(Event.SOUND_COMPLETE, doneFunction);
135+
soundChannel.addEventListener(Event.SOUND_COMPLETE, function(e:Event):void {
136+
soundChannel = null;
137+
if (doneFunction != null) doneFunction();
138+
});
110139
} else {
111140
// User has no sound card or too many sounds already playing.
112141
stopPlaying();

0 commit comments

Comments
 (0)