Skip to main content
Known Participant
June 3, 2011
Question

Saving Microphone Recording IOS/Android

  • June 3, 2011
  • 3 replies
  • 17001 views

I'm currently capable of capturing audio using the microphone, but I would like some tips on saving it to the mobile device.

I'm being using the techniques described here to record and play audio through the microphone:

http://suzhiyam.wordpress.com/tag/using-microphone-in-as3/

The save technique used is this:

function onSave(evt:MouseEvent=null):void

{

     hidePlayUI()

     soundO.position=0

     var o:ByteArray=enco.encode(soundO,1)

     file.save(o,"sample.wav")

     showPlayUI()

}

What is the best method to save an audio file from a ByteArray? Where does it actually save? How can I reference/load the saved audio file later in my application? Is FileReference the way to go or should I be using mediaPromise?

Thanks,

Tegan Snyder

This topic has been closed for replies.

3 replies

Known Participant
June 6, 2011

What amazing responses! I appreciate all the suggestions and pointers. I plan on tackling this later on in the week and will post back an example if I succede. Thanks again!

relaxatraja
Inspiring
June 6, 2011

http://blancer.com/tutorials/105750/create-a-useful-audio-recorder-app-in-actionscript-3/

June 4, 2011

Hi

In my app (Android only, presumably would work the same on iOS) I save the raw bytearray data to the SD card.

Calling documentsDirectory I'm targeting the SD card's filesystem:

// soundrecording is your recorded bytearray

private var soundrecording:ByteArray;

private var myfilename:String = "myfile";

public function saveFile():void

     {         

     var filename:String = "myfolder/recordings/"+myfilename;          

     var outputFile:File = File.documentsDirectory.resolvePath(filename);

     var outputStream:FileStream = new FileStream();

     outputStream.open(outputFile,FileMode.WRITE);

     outputStream.writeBytes(soundrecording,0,soundrecording.length);

     outputStream.close();

     }

I can then reload it back into the app later.

To get an array of File objects in my folder:

private function openFolder(e:MouseEvent):Array

     {

     var folder:File = File.documentsDirectory.resolvePath("myfolder/recordings");

     filelist = folder.getDirectoryListing();

         

     return filelist;

     }

Then once a file has been selected:

private function getFile(_filename:String):ByteArray

     {

     var bytes:ByteArray = new ByteArray();

     var inputFile:File = File.documentsDirectory.resolvePath("myfolder/recordings/"+_filename);

     var inputStream:FileStream = new FileStream();

     inputStream.open(inputFile,FileMode.READ);

     inputStream.readBytes(bytes,0,inputStream.bytesAvailable);

     inputStream.close();

         

     return bytes;

     }

Bare in mind that these ByteArrays are not WAV files (though I think they're not massively different), so if you want to be able to play the files in an external application you will need to convert them to WAVs.  I have an 'export' function in my app where I convert the selected byteArray file to WAV using the WAVwriter.as class you can find here:

http://code.google.com/p/ghostcat/source/browse/trunk/ghostcatfp10/src/ghostcat/media/WAVWriter.as?spec=svn424&r=424

Hope that helps

Inspiring
June 4, 2011

Tegan,

I have a whole chapter on Audio in my book which I recommend reading:

http://oreilly.com/catalog/0636920013884

There are various options in Audio (all below tested on Android but not on iOS)

+ You can save the audio as bytes as Slopester mentioned (note that if you need to manage the saved ByteArray, you can do so in a SQLite Database using the BLOB type).

Instead of saving uncompressed bytes, I would recommend applying a compression otherwise you will quickly fill up the user SD Card. The downside of it is that compressing is a little slow:

import flash.utils.CompressionAlgorithm;

// compress the bytes

bytes.position = 0;

bytes.compress(CompressionAlgorithm.ZLIB);

To play the audio back, you will need to decompress the data using the same compression:

bytes.uncompress(CompressionAlgorithm.ZLIB);

bytes.position = 0;

// now play audio

+ You can your recording as a WAV file. Download the Adobe.audio.format.WAVWriter class from the audio_sampler.zip file located at:

http://www.adobe.com/devnet/air/flex/articles/using_mic_api.html

Import it to your project.

Here is an example saving the file on the SD Card in a directory called "my sounds":

import com.adobe.audio.format.WAVWriter;

import flash.filesystem.File;

import flash.filesystem.FileStream;

import flash.filesystem.FileMode;

function saveWav(bytes:ByteArray):void {

     // point to mySounds directory on the SD card.

     var directory:File = File.documentsDirectory.resolvePath("mySounds");

     // if directory does not exist yet, create it

     if (!directory.exists) {

          directory.createDirectory();

     }

     // create name of a new wav file

     var file:File = directory.resolvePath("mySound.wav");

     // create an instance of the WAVWriter class and set properties

     var wav:WAVWriter = new WAVWriter();

     wav.numOfChannels = 1; // mono

     wav.sampleBitRate = 16; // or 8

     wav.samplingRate = 44100; // or 22000

     // rewind to the beginning of the ByteArray

     // create stream as conduit to copy data and write file

     var stream:FileStream = new FileStream();

     stream.open(file, FileMode.WRITE);

     // convert byteArray to WAV format and close stream

     wav.processSamples(stream, bytes, 44100, 1);

     stream.close();

}

There are currrently no native AIR libraries to load a WAV file or encode a ByteArray as an MP3 file.

To read the sound back into AIR, you need to use an open source libraries (I use this one: http://code.google.com/p/as3wavsound/)

Here are a few which provide a WAV decoder:

AS3WavSound (http://www.ohloh.net/p/as3wavsound)

standingwave3 (http://maxl0rd.github.com/standingwave3/)

Ogg/Vorbis (http://vorbis.com/software/)

Tonfall (http://code.google.com/p/tonfall/)  this is also an encoder

+ You can also choose to encode as an MP3 using open source libraries.

Shine, written by Gabriel Bouvigne, is an Alchemy/Flash MP3 encoder

https://github.com/kikko/Shine-MP3-Encoder-on-AS3-Alchemy

or

http://code.google.com/p/flash-kikko/

import fr.kikko.lab.ShineMP3Encoder;

encoder = new ShineMP3Encoder(bytes);

encoder.addEventListener(Event.COMPLETE, onEncoding);

encoder.addEventListener(ProgressEvent.PROGRESS, onProgress);

encoder.addEventListener(ErrorEvent.ERRROR, onError);

encoder.start();

file.save(mp3Encoder.mp3Data, "recording.mp3");

Let me know if any of this is not clear.

Message was edited by: Véronique Brossier

Participant
July 13, 2011

Hi!

I'm trying to write my application using your excample and I have following problems: in Android I dont' recieve the SampleDataEvent.SAMPLE_DATA

        public function startRecording():void {
            if (Microphone.isSupported) {
                _isRecording = true;
                _microphone = Microphone.getMicrophone(Microphone.names[0]);
                _microphone.rate = 22;
                _microphone.gain = 100;
                _microphone.setSilenceLevel(0, 4000);
                _soundData = new ByteArray();
                _microphone.addEventListener(SampleDataEvent.SAMPLE_DATA, onSampleDataReceived);
            } else {
                trace("No microphone support!");
            }
        }
        protected function onSampleDataReceived(event:SampleDataEvent):void
        {
            while(event.data.bytesAvailable)
            {
                var sample:Number = event.data.readFloat();
                _soundData.writeFloat(sample);
            }
        }

And in iOS I can record the sound from the microphone, but I dont' here it when I try to play the recorded sound:

        public function playRecording():void {
            if (_soundData != null) {
                _soundData.position = 0;
                var sound:Sound= new Sound();
                sound.addEventListener(SampleDataEvent.SAMPLE_DATA, sound_sampleDataHandler);
                var channel:SoundChannel = sound.play();
                channel.addEventListener(Event.SOUND_COMPLETE, soundCompleteHandler);
            }
        }
        protected function sound_sampleDataHandler(event:SampleDataEvent):void
        {
            if (!_soundData.bytesAvailable > 0)
            {
                return;
            }
           
            for (var i:int = 0; i < 8192; i++)
            {
                var sample:Number = 0;
                if (_soundData.bytesAvailable > 0)
                {
                    sample = _soundData.readFloat();
                }
                event.data.writeFloat(sample);
                event.data.writeFloat(sample); 
            }
           
        }

I thought that the problem in ANdroid could be in the application configuration, here's my:

<android>
        <manifestAdditions><![CDATA[
            <manifest android:installLocation="auto">
                <!--See the Adobe AIR documentation for more information about setting Google Android permissions-->
                <!--Removing the permission android.permission.INTERNET will have the side effect
        of preventing you from debugging your application on your device-->
                <uses-permission android:name="android.permission.INTERNET"/>
               <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
                <!--<uses-permission android:name="android.permission.READ_PHONE_STATE"/>-->
                <!--<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>-->
                <!--The DISABLE_KEYGUARD and WAKE_LOCK permissions should be toggled together
        in order to access AIR's SystemIdleMode APIs-->
                <!--<uses-permission android:name="android.permission.DISABLE_KEYGUARD"/>-->
                <!--<uses-permission android:name="android.permission.WAKE_LOCK"/>-->
                <!--<uses-permission android:name="android.permission.CAMERA"/>-->
                <uses-permission android:name="android.permission.RECORD_AUDIO"/>
                <!--The ACCESS_NETWORK_STATE and ACCESS_WIFI_STATE permissions should be toggled
        together in order to use AIR's NetworkInfo APIs-->
                <!--<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>-->
                <!--<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>-->
            </manifest>
           
        ]]></manifestAdditions>
    </android>