Record, Visualize & Save Microphone Input

Source Available Code Here

A few weeks ago I received an e-mail from a reader asking about how one might go about visualizing microphone input in real-time. It turns out that, in part because of the super simple Sound API in AS3, that it is not very different from visualizing other audio sources. Back in July, I wrote an article on slicing and looping mp3 files. The demo rendered the audio waveform data much like that which you would see in any audio recording software. The same basic approach can be taken when rendering microphone input. The most significant difference being that the data is produced in real-time.

The source code for the demo above contains four classes. Here is a brief description of them:

MicrophoneCapture:
This is the document class. It basically initializes the recording and rendering classes, draws the user interface components to the screen and handles user interface events

MicrophoneInput
Connects to the Microphone, adds a listener for SampleDataEvent and stores the event’s data if the recording flag is set to true

InputRenderer
Draws the input signal and provides an API for viewing the waveform in more or less detail

WavEncoder
Creates a ByteArray from the recorded audio that can be saved as a .wav file

The Flash Player requires that a user allow access to the computer’s Microphone. After this happens any input will dispatch a SampleDataEvent that contains the input as a ByteArray. The record() method of the MicrophoneInput class, sets the _recording flag to true and enables recording. To record the input we just cache it in another ByteArray. Here is a snippet from the MicrophoneInput class:

protected function onSampleData( event:SampleDataEvent ):void 
{	
	if( recording )
	{
		var n:Number;
		while( event.data.bytesAvailable )
		{
			n = event.data.readFloat();
			_buffer.writeFloat( n );
			_buffer.writeFloat( n );
		}
	}
	dispatchEvent( event );
}

Note that writeFloat() is called twice, passing in the same value. This is because the microphone signal is in mono. If the microphone were stereo we would would call readFloat() on the event.data property twice, storing it each time. The handler also dispatches the SampleDataEvent regardless of whether or not the _recording flag is set. By listening to for this event, an outside class can handle it and use the event’s data.

Another thing to mention about the MicrophoneInput class is that it provides a public getter for the actual Microphone object. This allows you to set up the microphone, adjust it’s gain and add any other listeners to it, outside of the actual MicrophoneInput class.

The rendering is fairly straight forward and is handled by the InputRenderer class. In the case of this example, I’ve set up a parameter called resolution on the InputRenderer. This allows you to fine tune the detail in the visualization. Because audio data is quite dense it can be pretty heavy on a processor to render all of the information. By setting the resolution to a higher value you are basically telling the computer to only render every nth sample. It is a little counter-intuitive that setting the value higher actually gives you a lower resolution, but this could be corrected.

Here is a what the render function looks like:

public function render( data:ByteArray = null ):void
{
	var i:int = 0;
	var l:int = samples.length;
			
	if( data )
	{
		data.position = 0;
		while( data.bytesAvailable )
		{
			samples[l+i] = data.readFloat();
			i++;
		}
	}
			
	var xpos:Number = 0;
	var g:Graphics = waveform.graphics;
	g.clear();
	g.lineStyle( 1, 0xf05151 );
	g.moveTo( 0, viewport.height * .5 + 10 );
	g.lineTo( viewport.width, viewport.height * .5 + 10 );
	g.moveTo( 0, viewport.height * .5 + 10 );
			
	if( samples.length > startSample )
	{
		var n:Number;
		for( i = startSample; i < endSample; i+= _resolution )
		{
			if( i < samples.length )
			{
				n = ( samples[i] * viewport.height + 5 ) + ( viewport.height * .5 + 5 );
				g.lineTo( xpos, n );
				xpos += xIncrement;
				continue
			}
			break;
		}
	}
}

You can see that the first thing that happens is that the data passed to the function is cached. This may not be ideal in a larger context, because the amount of data could become quite large and drawing such a large data set might cause performance issues. However, it facilitates changing the resolution of the render and works well for this example. The Input Renderer class also provides hooks for resizing and resetting.

If you press the "Save" button the recorded audio can be saved as a .wav file. The data needs to be converted to usable values for the .wav format. This is handled by the WavEncoder class. This class is rather slim in it's implementation. It's pretty much meant to be used only in this example. There are more much more robust encoders available. However, if you really want to get into it, writing your own from scratch will be a fun adventure for you. Be sure to check out the .wav format spec.

This experiment turned out to be a little more involved than my others. It has multiple classes and requires that you link to both the minimalcomps user interface library and the makemachine.utils lib. It is not checked into gist or github. Instead I've just made a .zip file for your downloading convenience.

Happy Hacking!

Source Code Here

Posted in Actionscript, Audio, Visualizations | Tagged , , , , , , , , , | 11 Comments

11 Responses to Record, Visualize & Save Microphone Input

  1. Sathyan says:

    I’m new to action script and flash. Can you pls suggest me some links which will help me to embed this player in a localhost website setup using wamp..any article or links will be of great help..

  2. Jeremy says:

    For embedding, try swfObject

  3. Sathyan says:

    Thanks for the reply. Is it possible to save the audio clip automatically to a location in my server instead of manually saving it to my computer. I’m using a WAMP server. Can u give please me any leads?

  4. Konstantin says:

    I would like to try out your code. However in the InputRenderer you used a function roundToInt with two parameters that doesn’t compile. What’s up with that? Is that function supposed to be in one of those other classes?
    Thanks.

  5. Jeremy says:

    Hi Konstantin,

    This is a function in my utils lib you can find it here: http://bit.ly/x5LsxR

    Thanks,
    ~Jeremy

  6. alexej says:

    hey there.. thanks for putting all this here.
    i get an error because of an undefined Method constrain and map. any ideas??

    thanks, alexej

  7. alexej says:

    thank you. didnt know that collection. Great work you do

  8. alexej says:

    hey there, i have another question: how can i make this playing only the part of the sound which is within the visible range? i tried understanding what you did in the “slicing and looping” example, but i dont know how to add this feature to the recorded sound. would be cool if you can help me with that.

    thanks, alexej

  9. Tâm says:

    Thanks Jeremy. excelent post!
    I just begin learing flash. I download source code but I don’t know run it. Please help me… thanks so much :-)

  10. Jeremy says:

    This question is a little broad. There are many books that can help you learn how to use ActionScript. I’d recommend getting into HTML5 WebAudio with JavaScript though. The Flash platform is becoming less and less relevant as JavaScript is now capable of most things Flash was once the only solution for.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>