I'm working on a project where the user have to record his/her voice, and submit it to server. But before submitting the user might need to play the recorded sound.
The application has a recording and playing capabilities with SPEEX codec. But what i found
strange and difficult is when i the user plays back the recorded audio, the playing speed is faster or slower than normal that it cannot be understood. As if its fast forwarding.
Here is the sample code:
private var mic:Microphone;
private var rec:ByteArray;
private var snd:Sound;
private var channel:SoundChannel;
protected function recBtn_clickHandler(event:MouseEvent):void
{
rec = new ByteArray();
mic = Microphone.getMicrophone();
mic.setLoopBack(false);
mic.setUseEchoSuppression(true);
mic.gain = 50;
mic.setSilenceLevel(5, 1000);
mic.codec = SoundCodec.SPEEX;
mic.addEventListener(SampleDataEvent.SAMPLE_DATA, getMicAudio);
}
protected function plyBtn_clickHandler(event:MouseEvent):void
{
snd.addEventListener(SampleDataEvent.SAMPLE_DATA, playRecorded);
channel = snd.play();
}
private function getMicAudio(e:SampleDataEvent): void
{
rec.writeBytes(e.data);
}
private function playRecorded(e:SampleDataEvent): void
{
if (!rec.bytesAvailable > 0) return;
for (var i:int = 0; i < 2048; i++){
var sample:Number = 0;
if (rec.bytesAvailable > 0) sample = rec.readFloat();
for (var j:uint = 0; j < 6; j++) {
e.data.writeFloat(sample);
}
}
}
This scenario only happens when:
mic.codec = SoundCodec.SPEEX;
mic.rate = 16
I went through a lot of forums, but could not find any solution for Microphone playback with SPEEX codec or microphone.rate = 16;
In flash, a sound object plays at 44khz. Since you're sampling at 16khz, you're sending data through the SampleDataEvent Event handler 2.75 faster then you are getting that data.
That is, if you were sending it twice.
But you're actually attempting to solve this problem by writing 3 times faster than what you're recording. This is still not optimum, you'll get a slowed down version of the recording, just a bit, because you're now sending data as if it were recorded at 48 khz, yet you're sending it as 44khz.
There are only two things you can do, and I think you're doing them already.
either adjust how many writes you do per iteration in that for loop. or adjust the max increment(2048) to a higher number, but it can't exceed 8192, I believe.
I had the same problem when i recorded in speex.
e.data.writeFloat(sample);
e.data.writeFloat(sample);
e.data.writeFloat(sample);
e.data.writeFloat(sample);
if (i%3)
{
e.data.writeFloat(sample);
e.data.writeFloat(sample);
}
Related
So I'm creating a monopoly game, and I've got two functions which read/write information about the game to/from a database so it can be retrieved between postbacks. I also have a start game function which initialises everything to 0 and then calls the writeToDatabase(). createArrays() just creates a list of players/squares, squares have been removed as they'not relevant.
The problem I'm having is I believe it is only storing some of the values within the loop. When the start game function is called it correctly sets the players positions to 0, and their money to 1500, however for some reason when reading from the database again the 4th players details immediately go back to what they were during the previous session.
private void readFromDatabase()
{
createArrays();
for (int i = 0; i < players.Count(); i++)
{
players[i].SetID(Convert.ToInt32(players_table.GetRow(i)["ID"]));
players[i].SetPosition(Convert.ToInt32(players_table.GetRow(i)["position"]));
players[i].SetMoney(Convert.ToInt32(players_table.GetRow(i)["money"]));
players[i].SetInJail(Convert.ToBoolean(players_table.GetRow(i)["isInJail"]));
players[i].SetIsBankrupt(Convert.ToBoolean(players_table.GetRow(i)["isBankrupt"]));
}
currentPlayerID = Convert.ToInt32(gameinfo_table.GetRow(0)["CurrentPlayerID"]);
currentSquareID = Convert.ToInt32(gameinfo_table.GetRow(0)["CurrentSquareID"]);
freeParkingAmount = Convert.ToInt32(gameinfo_table.GetRow(0)["FreeParkingAmount"]);
currentPlayer = players[currentPlayerID];
CurrentSquare = squares[currentSquareID];
}
private void writeToDatabase()
{
CurrentSquare = squares[currentSquareID];
for (int i = 0; i < players.Count(); i++)
{
SQLDatabase.DatabaseRow prow = players_table.GetRow(players[i].GetID());
prow["ID"] = players[i].GetID().ToString();
prow["position"] = players[i].GetPosition().ToString();
prow["money"] = players[i].GetMoney().ToString();
prow["isInJail"] = players[i].IsInJail().ToString();
prow["isBankrupt"] = players[i].IsBankrupt().ToString();
players_table.Update(prow);
}
SQLDatabase.DatabaseRow girow = gameinfo_table.GetRow(0);
girow["CurrentPlayerID"] = currentPlayerID.ToString();
girow["CurrentSquareID"] = CurrentSquare.GetID().ToString();
girow["FreeParkingAmount"] = freeParkingAmount.ToString();
gameinfo_table.Update(girow);
}
private void startGame()
{
createArrays();
currentPlayerID = 0;
currentPlayer = players[0];
CurrentSquare = squares[0];
writeToDatabase();
}
private void createArrays()
{
for (int i = 0; i < 4; i++)
{
Player player = new Player(i);
players.Add(player);
}
}
I really can't work out why this is happening, as when it creates the player array it loops through 4 times, therefore creating 4 brand new players, and immediately overwriting the details in the database. It just seems odd it works perfectly fine for the first 3 players but the 4ths details don't seem to change. Any help would be greatly appreciated, I'm sure it something simple but I have spent hours on this and haven't got anywhere.
Generalities : explanations about my program and its functioning
I am working on a photo-retouching JavaFX application. The final user can load several images. When he clicks on the button REVERSE, a Task is launched for each image using an Executor. Each of these Task executes the reversal algorithm : it fills an ArrayBlockingQueue<Pixel> (using add method).
When the final user clicks on the button REVERSE, as I said, these Task are launched. But just after these statements, I tell the JavaFX Application Thread to draw the Pixel of the ArrayBlockingQueue<Pixel> (using remove method).
Thus, there are parallelism and concurrency (solved by the ArrayBlockingQueue<Pixel>) between the JavaFX Application Thread and the Task, and between the Task themselves.
To draw the Pixel of the ArrayBlockingQueue<Pixel>, the JavaFX Application Thread starts an AnimationTimer. The latter contains the previously-mentionned remove method. This AnimationTimer is started for each image.
I think you're wondering yourself how this AnimationTimer can know to what image belongs the Pixel it has removed ? In fact, each Pixel has an attribute writable_image that specifies the image to what it belongs.
My problems
Tell me if I'm wrong, but my program should work. Indeed :
My JavaFX Application Thread is the only thread that change the GUI (and it's required in JavaFX) : the Task just do the calculations.
There is not concurrency, thanks to the BlockingQueue I use (in particular, there isn't possibility of draining).
The AnimationTimer knows to what image belongs each Pixel.
However, it's (obviously !) not the case (otherwise I wouldn't have created this question haha !).
My problem is that my JavaFX Application freezes (first problem), after having drawn only some reversed pixels (not all the pixels). On the last loaded image moreover (third problem).
A detail that could be the problems' cause
But I would need your opinion.
The AnimationTimer of course doesn't draw the reversed pixels of each image directly : this is animated. The final user can see each pixel of an image being reversed, little by little. It's very practical in other algorithms as the creation of a circle, because the user can "look" how the algorithm works.
But to do that, the AnimationTimer needs to read a variable called max. This variable is modified (writen) in... each Task. But it's an AtomicLong. So IF I AM NOT WRONG, there isn't any problem of concurrency between the Task themselves, or between the JavaFX Application Thread and these Task.
However, it could be the problem : indeed, the max's value could be 2000 in Task n°1 (= in image n°1), and 59 in Task n°2 (= in image n°2). The problem is the AnimationTimer must use 2000 for the image n°1, and 59 for the n°2. But if the Task n°1 et n°2 have finished, the only value known by the AnimationTimer would be 59...
Sources
When the user clicks on the button REVERSE
We launch the several Task and start several times the AnimationTimer. CLASS : RightPane.java
WritableImage current_writable_image;
for(int i = 0; i < this.gui.getArrayListImageViewsImpacted().size(); i++) {
current_writable_image = (WritableImage) this.gui.getArrayListImageViewsImpacted().get(i).getImage();
this.gui.getGraphicEngine().executor.execute(this.gui.getGraphicEngine().createTask(current_writable_image));
}
for(int i = 0; i < this.gui.getArrayListImageViewsImpacted().size(); i++) {
current_writable_image = (WritableImage) this.gui.getArrayListImageViewsImpacted().get(i).getImage();
this.gui.getImageAnimation().setWritableImage(current_writable_image);
this.gui.getImageAnimation().startAnimation();
}
The Task are part of the CLASS GraphicEngine, which contains an Executor :
public final Executor executor = Executors.newCachedThreadPool(runnable -> {
Thread t = new Thread(runnable);
t.setDaemon(true);
return t ;
});
public Task createTask(WritableImage writable_image) {
int image_width = (int) writable_image.getWidth(), image_height = (int) writable_image.getHeight();
Task ret = new Task() {
protected Void call() {
switch(operation_to_do) {
case "reverse" :
gui.getImageAnimation().setMax(image_width*image_height); // USE OF "MAX" VARIABLE
reverseImg(writable_image);
break;
}
return null;
}
};
return ret;
}
The same CLASS, GraphicEngine, also contains the reversal algorithm :
private void reverseImg(WritableImage writable_image) {
int image_width = (int) writable_image.getWidth(), image_height = (int) writable_image.getHeight();
BlockingQueue<Pixel> updates = gui.getUpdates();
PixelReader pixel_reader = writable_image.getPixelReader();
double[] rgb_reversed;
for (int x = 0; x < image_width; x++) {
for (int y = 0; y < image_height; y++) {
rgb_reversed = PhotoRetouchingFormulas.reverse(pixel_reader.getColor(x, y).getRed(), pixel_reader.getColor(x, y).getGreen(), pixel_reader.getColor(x, y).getBlue());
updates.add(new Pixel(x, y, Color.color(rgb_reversed[0], rgb_reversed[1], rgb_reversed[2], pixel_reader.getColor(x, y).getOpacity()), writable_image));
}
}
}
Finally, here is the code of the CLASS AnimationTimer. There is nothing particular. Note the variable max is used here too (and in the CLASS GraphicEngine : setMax).
public class ImageAnimation extends AnimationTimer {
private Gui gui;
private AtomicLong max, speed, max_delay;
private long count, start;
private WritableImage writable_image;
ImageAnimation (Gui gui) {
this.gui = gui;
this.count = 0;
this.start = -1;
this.max = new AtomicLong(Long.MAX_VALUE);
this.max_delay = new AtomicLong(999_000_000);
this.speed = new AtomicLong(this.max_delay.get());
}
public void setMax(long max) {
this.max.set(max);
}
public void setSpeed(long speed) { this.speed.set(speed); }
public double getMaxDelay() { return this.max_delay.get(); }
#Override
public void handle(long timestamp) {
if (start < 0) {
start = timestamp ;
return ;
}
ArrayList<Pixel> list_sorted_pixels = new ArrayList<>();
BlockingQueue<Pixel> updates = this.gui.getUpdates();
for(Pixel new_pixel : updates) {
if(new_pixel.getWritableImage() == writable_image) {
list_sorted_pixels.add(new_pixel);
}
}
while (list_sorted_pixels.size() > 0 && timestamp - start > (count * this.speed.get()) / (writable_image.getWidth()) && !updates.isEmpty()) {
Pixel update = list_sorted_pixels.remove(0);
updates.remove(update);
count++;
if (update.getX() >= 0 && update.getY() >= 0) {
writable_image.getPixelWriter().setColor(update.getX(), update.getY(), update.getColor());
}
}
if (count >= max.get()) {
this.count = 0;
this.start = -1;
this.max.set(Long.MAX_VALUE);
stop();
}
}
public void setWritableImage(WritableImage writable_image) { this.writable_image = writable_image; }
public void startAnimation() {
this.start();
}
}
I have to query the TSDB with millisecond precision.By default when using the TDSBQuery (obtained via TSDB.newQuery()) this requirement is not fulfilled. I tried with TSQuery but it didn't work. The returned results are far more than the ones returned by HTTP query API (that brings the right data that I need):
http://localhost:9999/api/query?start=1197849600.001&end=1197849882.000&msResolution=true&m=sum:CP101_X&ms.
Can you please help on how to achieve the millisecond precision query by using Java API?
Thanks,
Regards,
Florin
I have used the following Java code:
TSQuery q = getMetricForValidate();
q.validateAndSetQuery();
Query[] queries = q.buildQueries(tsdb);
for (int i = 0; i < queries.length; i++)
{
DataPoints[] dps = queries[i].run();
if (dps.length > 0)
{
for (DataPoint dp : dps[0])
{
System.out.println(dp.timestamp());
}
}
}
private static TSQuery getMetricForValidate()
{
final TSQuery query = new TSQuery();
query.setStart("119784960.0001");
query.setEnd("1197849882.000");
query.setMsResolution(true);
final ArrayList<TSSubQuery> subs = new ArrayList<TSSubQuery>(1);
subs.add(getSubMetricForValidate());
query.setQueries(subs);
return query;
}
public static TSSubQuery getSubMetricForValidate()
{
final TSSubQuery sub = new TSSubQuery();
sub.setAggregator("sum");
sub.setMetric("01CP101_X");
sub.setRate(false);
return sub;
}
While debugging the TSDMain class, I found out that the filtering at millisecond is not performed at the database level, but in client code in HTTPJsonSerializer. I believe that this is the current supported implementation. Are any other solution at horizon?
Regards,
Florin
I am beginner in Windows phone. I created the recorder example and also executed successfully in windows phone 7. But I have to add the pause and resume functionality in my application.
Note: I used microphone for the recording.
How can I put push and resume functionality in microphone for recording?
OR give me any alternative solution for recording in windows phone.
here is my code..
Microphone mphone;
List<byte[]> memobuffercollection = new List<byte[]>();
DynamicSoundEffectInstance playback;
private void BtnRecords_Click(object sender, RoutedEventArgs e)
{
// Clear the collection for storing the buffers
memobuffercollection.Clear();
// Stop any playback in Progress
playback.Stop();
// Start Recording
mphone.Start();
BtnStop.Opacity = 1;
BtnRecords.Opacity = 0;
}
private void BtnStop_Click(object sender, RoutedEventArgs e)
{
StopRecording();
BtnStop.Opacity = 0;
BtnRecords.Opacity = 1;
}
void StopRecording()
{
// Get the last partial buffer
int sampleSize = mphone.GetSampleSizeInBytes(mphone.BufferDuration);
byte[] extraBuffer = new byte[sampleSize];
int extraBytes = mphone.GetData(extraBuffer);
// Stop Recording
mphone.Stop();
// Create MemoInfo object and add at top of collection
int totalSize = memobuffercollection.Count * sampleSize + extraBytes;
TimeSpan duration = mphone.GetSampleDuration(totalSize);
MemoInfo memoInfo = new MemoInfo(DateTime.UtcNow, totalSize, duration);
memofiles.Insert(0, memoInfo);
// Save Data in IsolatedStorage
using (IsolatedStorageFile storage = IsolatedStorageFile.GetUserStoreForApplication())
{
using (IsolatedStorageFileStream stream = storage.CreateFile(memoInfo.FileName))
{
// Write buffers from collection
foreach (byte[] buffer in memobuffercollection)
stream.Write(buffer, 0, buffer.Length);
// Write partial buffer
stream.Write(extraBuffer, 0, extraBytes);
}
}
memosListBox.UpdateLayout();
memosListBox.ScrollIntoView(memoInfo);
}
And memoinfo is my class which is used for giving the title to recorded audio.
If you look into the documentation of Microphone, you will find out that there is no Pause() or Resume() method in Microphone class. Only playback has Pause & Resume features ( read this ).
The only way to pause & resume, is to stop recording, save the audio file, and record a new one when you "resume". Last, combine the audio files together to one.
Related Question (though it's for Windows Phone 8): how to enable pause and resume in audio recorder in windows phone 8?(Details Insde)
I have an asp.net web site that serves sample MP3 files to client Flash Players (SWF).
These files are downloadable by tons of download tools.
Although only registered members can access the high quality mp3 samples, my client wants to prevent these low quality MP3 files to be downloaded by download tools.
So I thought about this solution:
Convert these MP3 files to bytearrays on server side (ASP.NET)
Do some bitwise XOR operations (Simple encryption)
Write this array to aspx' responsestream
Modify Flash (.fla) to request to this new file/page/aspx
Do some bitwise XOR operations on Flash and convert it to the original MP3 as byte array. (Simple decryption)
Play the MP3
I was able to succeed till step 6. I cannot convert this byte array to a Sound object that Flash can play. I did a bit by bit comparison of the resulting array on the flash and the source array on ASP.NET. They are equal.
I'm open to completely different approaches. But I cannot use Flash Media Server. I need to be using Flash as3 and ASP.NET.
Also very important! The .mp3 must be downloaded/decrypted and played asynchronously (which I coud not succeed in doing)
I agree with Peter Elliot that authentication probably is the easiest way to restrict access to the files. However, if you still need to explore the route of encrypting the files, I thought I'd expand a bit on Alex Vlad's answer.
What you need to do in order to be able to stream the audio file, decrypt it on the fly, and play it asynchronously is to use the URLStream class (docs) in conjunction with the Sound class (docs) and keeping a buffer of the partially downloaded content.
Some pseudocode to illustrate:
class AsyncEncryptedSoundPlayer extends Sound {
var buffer:ByteArray;
var stream:URLStream;
var currSoundPosition:uint = 0;
public function AsyncEncryptedSoundPlayer(url:String) {
buffer = new ByteArray();
stream = new URLStream();
stream.addEventListener(ProgressEvent.PROGRESS, onProgress);
stream.load(new URLRequest(url));
addEventListener(SampleDataEvent.SAMPLE_DATA, onSampleDataRequested);
}
function onProgress(e:ProgressEvent):void {
var tmpData:ByteArray;
stream.readBytes(tmpData, buffer.length, stream.bytesAvailable - buffer.length);
var decryptedData:ByteArray = decryptData(tmpData); // Decrypt loaded data
buffer.writeBytes(decryptedData, buffer.length, decryptedData.length); // Add decrypted data to buffer
}
function onSampleDataRequested(e:ProgressEvent):void {
// Feed samples from the buffer to the Sound instance
// You may have to pause the audio to increase the buffer it the download speed isn't high enough
event.data.writeBytes(buffer, currSoundPosition, 2048);
currSoundPosition += 2048;
}
function decryptedData(data:ByteArray):void {
// Returns decrypted data
}
}
This is obviously a very rough outline of a class, but I hope it will point you in the right direction.
#walkietokyo, thanks a lot for pointing me to the right direction. I succeeded in doing what I wanted. The keyword here was the loadCompressedDataFromByteArray function.
After tens of trial and errors I found out that loadCompressedDataFromByteArray was working in a differential manner.
It appends anything that it converts to the end of the sound object data.
Another issue: sound object doesn't continue playing the parts appended by loadCompressedDataFromByteArray after its play function is called.
So I implemented a sort of double buffering. Where I use 2 sound objects interchangeably.
My final (test) version is listed below. With the encryption (obfuscation) method I used (a simple XOR) no download manager or grabber or sniffer that I tested was able to play the Mp3s.
Flash (Client) side:
import flash.events.DataEvent;
import flash.events.Event;
import flash.events.EventDispatcher;
import flash.events.OutputProgressEvent;
import flash.events.ProgressEvent;
import flash.net.URLRequest;
import flash.net.URLStream;
import flash.utils.ByteArray;
import flashx.textLayout.formats.Float;
var buffer:ByteArray;
var stream:URLStream;
var bufferReadPosition:uint = 0;
var bufferWritePosition:uint = 0;
var url:String = "http://www.blablabla.com/MusicServer.aspx?" + (new Date());
buffer = new ByteArray();
stream = new URLStream();
stream.addEventListener(ProgressEvent.PROGRESS, onProgress);
stream.load(new URLRequest(url));
var s1:Sound = new Sound();
var s2:Sound = new Sound();
var channel1:SoundChannel;
var channel2:SoundChannel;
var pausePosition:int = 0;
var aSoundIsPlaying:Boolean = false;
var lastLoadedS1:Boolean = false;
var lastS1Length:int = 0;
var lastS2Length:int = 0;
function onProgress(e:ProgressEvent):void {
var tmpData:ByteArray = new ByteArray();
stream.readBytes(tmpData, 0, stream.bytesAvailable);
var decryptedData:ByteArray = decryptData(tmpData); // Decrypt loaded data
buffer.position = bufferWritePosition;
buffer.writeBytes(decryptedData, 0, decryptedData.length); // Add decrypted data to buffer
bufferWritePosition += decryptedData.length;
if(lastLoadedS1)
{
buffer.position = lastS2Length;
s2.loadCompressedDataFromByteArray(buffer, buffer.length - lastS2Length);
lastS2Length = buffer.length;
}
else
{
buffer.position = lastS1Length;
s1.loadCompressedDataFromByteArray(buffer, buffer.length - lastS1Length);
lastS1Length = buffer.length;
}
if(!aSoundIsPlaying)
{
DecidePlay();
}
}
function channel1Completed(e:Event):void
{
DecidePlay();
}
function channel2Completed(e:Event):void
{
DecidePlay();
}
function DecidePlay():void
{
aSoundIsPlaying = false;
if(lastLoadedS1)
{
channel1.stop();
if(s2.length - s1.length > 10000)
{
//At least a 10 second buffer
channel2 = s2.play(s1.length);
channel2.addEventListener(Event.SOUND_COMPLETE, channel2Completed);
lastLoadedS1 = false;
aSoundIsPlaying = true;
}
}
else
{
if(channel2 != null)
{
channel2.stop();
}
if(s1.length - s2.length > 10000)
{
//At least a 10 second buffer
channel1 = s1.play(s2.length);
channel1.addEventListener(Event.SOUND_COMPLETE, channel1Completed);
lastLoadedS1 = true;
aSoundIsPlaying = true;
}
}
}
function decryptData(data:ByteArray):ByteArray {
for(var i:int = 0;i<data.length;i++)
{
//Here put in your bitwise decryption code
}
return data;
}
ASP.NET server side (MusicServer.aspx):
protected void Page_Load(object sender, EventArgs e)
{
CopyStream(Mp3ToStream(Server.MapPath("blabla.mp3")), Response.OutputStream);
this.Response.AddHeader("Content-Disposition", "blabla.mp3");
this.Response.ContentType = "audio/mpeg";
this.Response.End();
}
public static void CopyStream(Stream input, Stream output)
{
byte[] buffer = new byte[32768];
int read;
while ((read = input.Read(buffer, 0, buffer.Length)) > 0)
{
for (int i = 0; i < read; i++)
{
//Here put in your bitwise encryption code
}
output.Write(buffer, 0, read);
}
}
public Stream Mp3ToStream(string filePath)
{
using (FileStream fileStream = File.OpenRead(filePath))
{
MemoryStream memStream = new MemoryStream();
memStream.SetLength(fileStream.Length);
fileStream.Read(memStream.GetBuffer(), 0, (int)fileStream.Length);
return memStream;
}
}
what might be simpler than encrypting the data coming back from your service is instead authenticating requests so that only your swf can request the files.
You can accomplish this in the same way that say, the Amazon APIs work: build a request that includes a number of parameters, including a timestamp. hash all of these arguments together in an HMAC (HMAC-SHA256 is available in the as3crypto library) along with a private key embedded in your swf. Your server end authenticates this request, ensuring that the hash is valid and that it is close enough to the timestamp. Any requests with a bad hash, or using a request with a timestamp too far in the past (replay attack) are denied.
This is certainly not perfect security. Any sufficiently motivated user could disassemble your swf and pull out your auth key, or grab the mp3 from their browser cache. But then again, any mechanism you are going to use will have those issues. This removes the overhead of having to encrypt and decrypt all of your files, instead moving the work over to the request generation phase.
Flash Sound supports only streaming mp3 playing that is you can play only mp3 by direct link. But you can send swf file with embeded mp3 withing it and this swf can be encrypted in the same way as you encrypt mp3.
as3 code for embedding and using mp3:
public class Sounds
{
[Embed(source="/../assets/sounds/sound1.mp3")]
private static const sound1:Class;
}
after loading this swf by the Loader you can access to the sound in this way:
var domain:ApplicationDomain = ApplicationDomain.currentDomain; // <-- ApplicationDomain where you load sounds.swf
var soundClass:Class = domain.getDefinition("Sounds_sound1");
var sound:Sound = new soundClass();
sound.play();
Be sure, that you do at least one of the follows:
give different names for sound class (sound1)
give different name for holder class (Sounds)
or load sound.swf into different application domains
to prevent class names overlapping.
Unfortunate this approach doesn't allow you to streaming play sound, you have to load whole swf, decrypt it and only after that you will be able to play sound.
please have a look here:
http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/events/SampleDataEvent.html
and here:
http://help.adobe.com/en_US/as3/dev/WSE523B839-C626-4983-B9C0-07CF1A087ED7.html