12-28-2011 11:46 AM
Hi everybody,
I am experimenting for a new app, some sort of dj app. However it seems that the performance of the Sound and SoundChannel classes on the PlayBook is extremely poor.
I am using the following code by Andre Michelle:
package components
{
import flash.events.Event;
import flash.events.SampleDataEvent;
import flash.media.Sound;
import flash.net.URLRequest;
import flash.utils.ByteArray;
/**
* @author Andre Michelle (andre.michelle@gmail.com)
*/
public class MP3Pitch
{
private const BLOCK_SIZE: int = 3072;
private var _mp3: Sound;
private var _sound: Sound;
private var _target: ByteArray;
private var _position: Number;
private var _rate: Number;
public function MP3Pitch( url: String )
{
_target = new ByteArray();
_mp3 = new Sound();
_mp3.addEventListener( Event.COMPLETE, complete );
_mp3.load( new URLRequest( url ) );
_position = 0.0;
_rate = 1.0;
_sound = new Sound();
_sound.addEventListener( SampleDataEvent.SAMPLE_DATA, sampleData );
}
public function get rate(): Number
{
return _rate;
}
public function set rate( value: Number ): void
{
if( value < 0.0 )
value = 0;
_rate = value;
}
private function complete( event: Event ): void
{
_sound.play();
}
private function sampleData( event: SampleDataEvent ): void
{
//-- REUSE INSTEAD OF RECREATION
_target.position = 0;
//-- SHORTCUT
var data: ByteArray = event.data;
var scaledBlockSize: Number = BLOCK_SIZE * _rate;
var positionInt: int = _position;
var alpha: Number = _position - positionInt;
var positionTargetNum: Number = alpha;
var positionTargetInt: int = -1;
//-- COMPUTE NUMBER OF SAMPLES NEED TO PROCESS BLOCK (+2 FOR INTERPOLATION)
var need: int = Math.ceil( scaledBlockSize ) + 2;
//-- EXTRACT SAMPLES
var read: int = _mp3.extract( _target, need, positionInt );
var n: int = read == need ? BLOCK_SIZE : read / _rate;
var l0: Number;
var r0: Number;
var l1: Number;
var r1: Number;
for( var i: int = 0 ; i < n ; ++i )
{
//-- AVOID READING EQUAL SAMPLES, IF RATE < 1.0
if( int( positionTargetNum ) != positionTargetInt )
{
positionTargetInt = positionTargetNum;
//-- SET TARGET READ POSITION
_target.position = positionTargetInt << 3;
//-- READ TWO STEREO SAMPLES FOR LINEAR INTERPOLATION
l0 = _target.readFloat();
r0 = _target.readFloat();
l1 = _target.readFloat();
r1 = _target.readFloat();
}
//-- WRITE INTERPOLATED AMPLITUDES INTO STREAM
data.writeFloat( l0 + alpha * ( l1 - l0 ) );
data.writeFloat( r0 + alpha * ( r1 - r0 ) );
//-- INCREASE TARGET POSITION
positionTargetNum += _rate;
//-- INCREASE FRACTION AND CLAMP BETWEEN 0 AND 1
alpha += _rate;
while( alpha >= 1.0 ) --alpha;
}
//-- FILL REST OF STREAM WITH ZEROs
if( i < BLOCK_SIZE )
{
while( i < BLOCK_SIZE )
{
data.writeFloat( 0.0 );
data.writeFloat( 0.0 );
++i;
}
}
//-- INCREASE SOUND POSITION
_position += scaledBlockSize;
}
}
}
When I play a Sound using this method it is extremely choppy. This class however is as basic as it can get, so am I missing anything? Or should I just give up on making this work on the PlayBook?
Best regards
Solved! Go to Solution.
12-28-2011 03:00 PM
12-29-2011 02:10 AM
I do not have any problems if I run it as a desktop application. The problem also occurs on Android device, so maybe it a mobile AIR problem?
12-29-2011 09:19 AM
12-29-2011 10:04 AM
Thanks for the suggestion, unfortunately this doesn't matter. The hiccups are still unbearable. I'll try contacting peter though, thanks!
12-29-2011 11:49 AM
If you remove the interpolation stuff, does it still sound choppy?
I know I've had my own battles seamlessly looping sound with the AIR SDK and if not writing out samples properly, would definitely be choppy. But I haven't found any issues with choppy sound that haven't been with my own logic.
12-29-2011 01:59 PM
I see nothing explaining why the code is that complicated, instead of just letting the Sound object directly play the MP3 file. Why all the SampleData stuff?
Also, I've only skimmed it so far, but I see BLOCK_SIZE is hardcoded to 3072. Why that value? Have you tried others? Depending on how much other work the code is doing, it may simply not be generating as many samples per update (i.e. for each SampleDataEvent.SAMPLE_DATA) as you should try to do, so it "gets behind". Try at least 4096, or an even larger value if that's allowed... I vaguely recall 8192 is the max (though perhaps split between the two channels).
How long is the event handling itself? Try adding some timing measurement at start and end of that routine to see if it's taking much longer than you would expect... and longer than the time available to it.
12-30-2011 08:50 AM
I use linear interpolation as suggested by Andre Michelle to be able to control the pitch. It is indeed that code that is causing the problem, I'll guess I have to write a more optimised version.