Flash for the masses – AS, JSFL, XUL, etc.

2008/12/31

Working with the Microphone in AS3.0

Filed under: AS3.0 — delfeld @ 12:01 am
Tags: , , , , , ,

For the code below, much code and concepts are from Adobe’s Working With Sound webpages, which is a great introductory resource to microphone audio. However, there is nothing about saving audio to a file. They delegate that to a media server, and seem to imply that there is no other way to save audio.

However, there is the flash.media.SoundMixer.computeSpectrum() method that delivers the byte data of a sound, and this works for both microphone and input audio file. This byte data can be saved to file, potentially, and I will update this post if I ever get around to doing that.

Anyway, for simple screwing around with audio, the following class is a starter. All it does is send the audio from your mic to your speakers – your own public address system!

Setup: It requires a Flash MovieClip in the library that inherits this class. Also, this object has to have some child MovieClip named activityMeter1, and a child TextField named activityLevel1.

package SoundRecorder
{
	import flash.display.MovieClip;
	import flash.display.Sprite;
	import flash.events.*;
	import flash.media.Microphone;
	import flash.system.Security;
	import flash.system.SecurityPanel;

	/**
	* ...
	* @author Delfeld, copyright 2008
	*/
	public class SoundRecorder extends MovieClip {

		private static const SR_AUDIO_ALLOWED:String = "SRAudioAllowed";

		private var recordingAllowed:Boolean;
		private var mic:Microphone;

		public function SoundRecorder()
		{
			try
			{
				recordingAllowed = false;

				// setup mic
				mic = Microphone.getMicrophone();

				// confirm the mic settings first
				Security.showSettings(SecurityPanel.MICROPHONE);

				// listen for mic becoming active
				this.addEventListener(SR_AUDIO_ALLOWED, allowAudioHandler, false, 0, true);

				// call the mic popup, and confirm with user that they should use the mic.
				if (mic != null) {
					// kill (some) feedback
					mic.setUseEchoSuppression(true);
					// send ALL mic input to the speaker
					mic.setLoopBack(true);
					// listen for events
					mic.addEventListener(ActivityEvent.ACTIVITY, activityHandler, false, 0, true);
					mic.addEventListener(StatusEvent.STATUS, statusHandler, false, 0, true);
				}
			}
			catch (e:Error)
			{
				trace("(caught error) SoundRecorder.SoundRecorder: " + e.toString() + " :\n.\n" + e.getStackTrace());
			}
		}

		// method is run when mic is activated
		private function allowAudioHandler(e:Event):void
		{
			e.target.removeEventListener(e.type, allowAudioHandler);

			setAudio();
		}

		// initial audio setup when the microphone is active
		private function setAudio():void
		{
			// this is in case of repeated calls to this method
			if (hasEventListener(Event.ENTER_FRAME))
			{
				removeEventListener(Event.ENTER_FRAME, changeDisplay);
			}

			mic.gain = 40;
			mic.rate = 16;
			mic.setSilenceLevel(5, 1000);

			// start displaying visuals on every frame
			addEventListener(Event.ENTER_FRAME, changeDisplay, false, 0, true);
		}

		private function changeDisplay(e:Event):void
		{
			if (recordingAllowed)
			{
				// resize the meter (really simple feedback)
				activityMeter1.height = 3 * mic.activityLevel;
				// note the mic level
				activityLevel1.text = mic.activityLevel.toFixed(0);

				// simple (and not great) control of mic level
				if (mic.activityLevel >= 99 && mic.gain > 25 )
				{
					mic.gain -= 1;
				}
				else if (mic.activityLevel <= 0 && mic.gain < 75)
				{
					mic.gain += 1;
				}
			}
		}

		private function activityHandler(event:ActivityEvent):void {
			//trace("activityHandler: " + event);
		}

		private function statusHandler(event:StatusEvent):void {
			if (event.code == "Microphone.Unmuted")
			{
				//trace("Microphone access was allowed.");
				recordingAllowed = true;
				dispatchEvent(new Event(SR_AUDIO_ALLOWED));
			}
			else if (event.code == "Microphone.Muted")
			{
				//trace("Microphone access was denied.");
				recordingAllowed = false;
			}
		}
	}
}

8 Comments »

  1. Hmm,
    “Because sound data from a microphone or from RTMP streams do not pass through the global SoundMixer object, the SoundMixer.computeSpectrum() method will not return data from those sources.” – Flash CS3 Documentation
    Or do you have some kind of workaround for this?

    Comment by Malte — 2009/02/15 @ 10:34 pm | Reply

    • I’m not sure that that is true in reality. David Stiller wrote a comment that in his tests, SoundMixer responds to dynamic sounds (Adobe AS3 SoundMixer reference). It was a while ago, but I think I tested that as well, and got the same results. Best thing to do is try it! Once I start working on this problem some more, I will post results concerning SoundMixer; but that may be a while.

      Comment by delfeld — 2009/02/17 @ 3:58 pm | Reply

  2. I would definitely be interested in any kind of work around. I want to get audio data from the mic and then send the raw data (or even better ready compressed) to the server – I know I could use FMS or red5 but I’d rather avoid all the extra overheads and general hassle with that. Unfortunately AIR/standalone is not an option.

    Fingers crossed ;)

    Comment by Oli — 2009/03/17 @ 4:19 pm | Reply

  3. I would like to see that work around too! Got the same problem with rtmp and the mic. ^^

    Comment by Steffen — 2009/06/08 @ 6:21 am | Reply

  4. You would not believe the bind I have gotten myself into. I have created a simple oscilloscope simulation, which allows you to visualize waveform data and manipulate sampling frequency. I showed this to my colleagues and they of course want the next logical step. Visualizing the waveform of speech from an attached microphone. Much to my regret I said.. that should be easy, I’ll just use the computeSpectrum() method on the mic input and present it as a graph on the screen… wow, problem solved right??? It would appear that I have opened up a can of computational worms and am doomed to failure in my attempts to fulfill on this wistful promise. It is truly baffling that I could analyze sound in machine language on my apple (!) computer from an analog joystick input and put a graph of the raw waveform on my screen in 1978, but CS3 will not allow me to get sound input from a microphone and do things with it in 2009. Some progress. I generally am impressed with flash, but what a blind spot this one is.

    Comment by Robert — 2009/07/29 @ 9:24 pm | Reply

    • You should be able to do your oscilloscope by working with the example I posted. If this doesn’t work out, then read the Sound class (and related classes) at Adobe docs, and see if you can piece together what you need.

      Comment by delfeld — 2009/07/31 @ 6:51 pm | Reply

  5. I’m very much interested to learn if this is possible yes or not:
    Just a simple soundlevel indication while talking into the mic?

    it would be great feedback while recording audio from the microphone.

    Comment by rdeman — 2009/08/13 @ 2:18 pm | Reply

    • Yes, that is simple to set up. Look on ActionScript.org or Kirupa’s forum if you can’t figure it out. This is a just buidling block toward that end.

      Comment by delfeld — 2009/08/19 @ 3:54 pm | Reply


RSS feed for comments on this post. TrackBack URI

Leave a comment

Blog at WordPress.com.