LogoLogo
  • Home
  • Projects
  • About
  • Contact

Making Waves

Devon O. · April 17, 2010 · Actionscript · 9 comments
7

In a previous post, “Digging into the Microphone in Flash Player 10.1”, reader David Law asked in the comments how it would be possible to save .wav files to the server. I wasn’t sure right offhand, but thought I’d spend my lunch hour yesterday looking into it. Well, after reading this quick tutorial on Adobe Developer Connection, it turns out it’s actually quite simple. Almost embarrassingly simple, but still I thought I’d post it in case anyone else was looking to do the same thing.

The trick is to just convert your sound bytes into a wav formatted ByteArray object which can then be saved to the server with a quick bit of AMFPHP. You’ll notice in that Adobe article that there is included a WAVWriter class – that is all that’s necessary to convert your uncompressed ByteArray to a .wav file. Once you have that, the below AMFPHP class can easily save that .wav file to your server:

PHP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?php
 
class Wave {
 
    /**
     * save wav files to server
     */
    function saveWav($wavByteArray) {
        // create a writable 'wavs' directory in the same directory as this service - probably want to use a different path
        $dir = "wavs";
        
        $fileName = "/sound" . time() . ".wav";
        
        $data = $wavByteArray->data;
        $result = file_put_contents($dir . $fileName, $data);
        
        return $result;
    }
}
 
?>

You may notice that the above class just saves the file to a directory within your AMFPHP services directory. You’ll more than likely not want to do that, so you can just create a different path. Or you may want to save the .wav directly in a database as a blob, in which case this old post may help you out. You may also want to name the .wav file on the client side and pass that along as an additional parameter rather than giving it a random name on the server side. In any case, this was just a quick example. You can do what you want from here.

Below is a quick .swf example (requires the 10.1 Flash Player) that will allow you to save the .wav file directly to your local machine to try it out (once you begin recording, you only have 3 seconds to say something into your mic, so make it quick. Didn’t want to go crazy on bandwidth).

Get Adobe Flash player

Again, this saves the file locally rather than to a server, but if you look through the code, you’ll see a commented area that would send the file to the AMFPHP class above rather than to the user’s machine via FileReference. Also, if you want to compile this yourself, you’ll need the WAVWriter class from the Adobe tutorial as well as the great MinimalComps from Bit-101 (of course you could just use your own buttons easily enough, but those components are freakin’ awesome for quick prototyping and if you don’t already have them, I recommend you get them).

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
package {
    
    import com.bit101.components.PushButton;
    import com.bit101.components.Style;
    import flash.display.Sprite;
    import flash.events.Event;
    import com.adobe.audio.format.WAVWriter;
    import flash.events.MouseEvent;
    import flash.events.StatusEvent;
    import flash.events.TimerEvent;
    import flash.net.FileReference;
    import flash.net.NetConnection;
    import flash.net.Responder;
    import flash.text.AntiAliasType;
    import flash.text.TextField;
    import flash.text.TextFieldAutoSize;
    import flash.text.TextFormat;
    import flash.utils.Timer;
            
    import flash.events.SampleDataEvent;
    import flash.media.Microphone;
    import flash.utils.ByteArray;
    
    /**
     * Record .wav file through mic then send it to users machine or server
     * @author Devon O.
     */
    [SWF(width='400', height='200', backgroundColor='#FFFFFF', frameRate='31')]
    public class Main extends Sprite {
        
        public static const GATEWAY_URL:String = "path/to/gateway.php";
        
        private var _startButton:PushButton;
        private var _stopButton:PushButton;
        private var _saveButton:PushButton;
        
        private var _recordingMessage:TextField;
        
        private var _timer:Timer;
        private var _mic:Microphone;
        private var _recordingComplete:Boolean = false;
        private var _soundRecording:ByteArray;
        
        private var _fileRef:FileReference = new FileReference();
        
        public function Main():void {
            if (stage) init();
            else addEventListener(Event.ADDED_TO_STAGE, init);
        }
        
        private function init(e:Event = null):void {
            removeEventListener(Event.ADDED_TO_STAGE, init);
            // entry point
            
            initMicrophone();
            initMessage();
            initUI();
        }
        
        private function initMicrophone():void {
            _mic = Microphone.getMicrophone();
            _mic.rate = 44;
            _mic.gain = 60;
            _mic.addEventListener(StatusEvent.STATUS, onmicStatus);
        }
        
        private function onmicStatus(event:StatusEvent):void {
            _mic.removeEventListener(StatusEvent.STATUS, onmicStatus);
            
            if (event.code == "Microphone.Muted") {
                _recordingMessage.text = "You must allow access\nto Mic to record.";
                _mic.removeEventListener(SampleDataEvent.SAMPLE_DATA, gotMicData);
            } else {
                _recordingMessage.text = "Recording...";
                _timer = new Timer(3000, 1);
                _timer.addEventListener(TimerEvent.TIMER_COMPLETE, timesUp);
                _timer.start();
            }
        }
        
        private function timesUp(event:TimerEvent):void {
            _timer.removeEventListener(TimerEvent.TIMER_COMPLETE, timesUp);
            stopRecording();
        }
        
        private function startRecording(event:MouseEvent):void {            
            _startButton.enabled = false;
            _recordingComplete = true;
            _soundRecording = new ByteArray();
            _mic.addEventListener(SampleDataEvent.SAMPLE_DATA, gotMicData);
        }
            
        private function stopRecording():void{
            _recordingMessage.text = "Recording complete.\nClick 'Save' to export .wav.";
            _saveButton.enabled = true;
            _mic.removeEventListener(SampleDataEvent.SAMPLE_DATA, gotMicData);
        }
            
        private function gotMicData(micData:SampleDataEvent):void {
            _soundRecording.writeBytes(micData.data);
        }
        
        private function saveRecording(event:MouseEvent):void {
            if (_soundRecording == null || _soundRecording.length <= 0) return;
            
            _saveButton.enabled = false;
            
            var wavWriter:WAVWriter = new WAVWriter();
                
            _soundRecording.position = 0;
                
            wavWriter.numOfChannels = 1;
            wavWriter.sampleBitRate = 16;
            wavWriter.samplingRate = 44100;
            
            var wavBytes:ByteArray = new ByteArray();
                
            wavWriter.processSamples(wavBytes, _soundRecording, 44100, 1); // convert our ByteArray to a WAV file.
            
            /* Use something like this to send the .wav to the server rather than the local machine:
 
            var resp:Responder = new Responder(successHandler, errorHandler);
            var gateway:NetConnection = new NetConnection();
            gateway.connect(GATEWAY_URL);
            gateway.call("Wave.saveWav", resp, wavBytes);
            
            */
            
            // or use this to save it locally
            _fileRef.save(wavBytes, "sound.wav");
        }
        
        private function successHandler(o:Object):void {
            trace(o);
            if (!o) trace("wav file not saved");
        }
        
        private function errorHandler(o:Object):void {
            for (var prop:String in o) trace(prop + " = " + o[prop]);
        }
        
        private function initMessage():void {
            _recordingMessage = new TextField();
            _recordingMessage.selectable = false;
            _recordingMessage.mouseEnabled = false;
            _recordingMessage.autoSize = TextFieldAutoSize.LEFT;
            _recordingMessage.antiAliasType = AntiAliasType.ADVANCED;
            _recordingMessage.defaultTextFormat = new TextFormat("_sans", 24, 0x660000);
            _recordingMessage.text = "Click 'Record' to begin...";
            _recordingMessage.x = 10;
            _recordingMessage.y = 100;
            _recordingMessage.multiline = true;
            addChild(_recordingMessage);
        }
        
        private function initUI():void {
            Style.BUTTON_FACE = 0x000000;
            _startButton = new PushButton(this, 10, 10, "Record", startRecording);
            _saveButton = new PushButton(this, _startButton.x + _startButton.width + 5, 10, "Save", saveRecording);
            _saveButton.enabled = false;
        }
    }
}

Of course, saving .wav files is all good and well, but you’ll probably want to actually be able to play them back. As you know (or should), Flash doesn’t natively allow for dynamically loading and playing .wav files. Needless to say, there is a way around that though. The general idea is this: the .wav file must be loaded in as a stream of bytes. This byte stream is then added to the library of a dynamically generated .swf file. The .wav can then be extracted from the library of that .swf and played as a Sound object using ApplicationDomain. Thankfully, I didn’t have to bother doing all this myself. A bit of Googling turned up this perfectly working example. I rearranged the code a little bit (added a real package, added some events, and added a playWav() method) which you can download in zipped format here. All credit goes to the original author though. Here’s a quick usage example:

C#
1
2
3
4
5
6
7
8
9
10
11
12
wp = new WavPlayer();
wp.addEventListener(ProgressEvent.PROGRESS, onProgress);
wp.addEventListener(Event.COMPLETE, onComplete);
wp.Load("mysound.wav");
        
private function onProgress(event:ProgressEvent):void {
    trace((event.bytesLoaded / event.bytesTotal) * 100);
}
        
private function onComplete(event:Event):void {
    wp.playWav();
}

Bear in mind that this WavPlayer works by loading in raw ones and zeroes which can be extremely hazardous if you don’t know exactly where those ones and zeroes came from. I strongly suggest you use this only on .wav files you’ve created yourself or know for a fact don’t have any monkey business going on inside.

Hope all that helps some folks out. Have fun…

  Facebook   Pinterest   Twitter   Google+
microphonewav
  • Learning AGAL with AGALMacroAssembler and OpenGL Examples
    June 03, 2012 · 16 comments
    6958
    8
    Read more
  • It’s Beginning to Look a Lot Like Unity…
    December 24, 2010 · 0 comments
    1610
    8
    Read more
  • FOTB in Review – Day 3
    September 23, 2009 · 0 comments
    1619
    3
    Read more
9 Comments:
  1. Hi,
    An other way here : http://jeanphiblog.media-box.net/dotclear/index.php?2010/04/13/342-flash-cs5-recording-microphone-and-save-it-to-desktop-with-the-flash-player-101

    jeanphilippe · April 18, 2010
  2. thanks for the great article,

    i modified ur code and uploaded the byteArray of recorded sound data from microphone to J2EE server and saved in wave file.
    i used blazeDS’s remote object to communicate flex and java

    thanks a lot once again.

    Ganesh Tiwari (gt_ebuddy) · August 22, 2010
  3. Sounds like a great modification, Ganesh. I’m glad the post could help out.

    Devon O. · August 22, 2010
  4. U can give me th source please :) ??????

    Foufou · November 22, 2010
  5. Hey Foufou – the entire source is actually there in the post (no hidden files anywhere :) )

    Devon O. · November 23, 2010
  6. Great post! I’m wondering if you would absolutely suggest using AMFPHP to send the wav file to the server. Can this be done just using the HTTPService or the URLRequest classes and a simple PHP script? Any thoughts on how to do this? I’ve seen examples for .jpegs, but none for wavs and I’m very confused by the base64 encoding/decoding process. Thanks for your time!!!

    Rob · December 16, 2010
  7. I answered my own question. If you’re like me, and don’t want to set up AMFPHP just yet, you can send your bytearray to the server using a URLRequest and a couple lines of PHP code. See: http://blog.joa-ebert.com/2006/05/01/save-bytearray-to-file-with-php/

    Rob · December 17, 2010
  8. Cool, Rob. Glad you got it sorted..

    Devon O. · December 17, 2010
  9. hye can you give more details on load wav in flash
    i still cant get it.
    I able to record to server.. but i cant see where to start to load back the wav file into flas

    ADZMIR · May 12, 2011

Leave a Comment! Cancel reply

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

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Devon O. Wolfgang

AIR | Unity3D | AR/VR

Unity Certified Developer

Technical Reviewer of “The Essential Guide to Flash CS4 AIR Development” and “Starling Game Development Essentials”

Reviewer of “The Starling Handbook”

Unity Engineer at Touch Press.

Categories
  • Actionscript (95)
  • AIR (16)
  • Flash (99)
  • Games (7)
  • Liberty (13)
  • Life (53)
  • Shaders (20)
  • Unity3D (21)
Recent Comments
  • Devon O. on Unity Ripple or Shock Wave Effect
  • Feral_Pug on Unity Ripple or Shock Wave Effect
  • bavvireal on Unity3D Endless Runner Part I – Curved Worlds
  • Danielius Vargonas on Custom Post Processing with the LWRP
  • Luca G on Unity Ripple or Shock Wave Effect
Archives
  • December 2020 (1)
  • December 2019 (1)
  • September 2019 (1)
  • February 2019 (2)
  • December 2018 (1)
  • July 2018 (1)
  • June 2018 (1)
  • May 2018 (2)
  • January 2018 (1)
  • December 2017 (2)
  • October 2017 (1)
  • September 2017 (2)
  • January 2017 (1)
  • July 2016 (1)
  • December 2015 (2)
  • March 2015 (1)
  • September 2014 (1)
  • January 2014 (1)
  • August 2013 (1)
  • July 2013 (1)
  • May 2013 (1)
  • March 2013 (2)
  • December 2012 (1)
  • November 2012 (1)
  • September 2012 (3)
  • June 2012 (2)
  • May 2012 (1)
  • April 2012 (1)
  • December 2011 (2)
  • October 2011 (3)
  • September 2011 (1)
  • August 2011 (1)
  • July 2011 (1)
  • May 2011 (2)
  • April 2011 (2)
  • March 2011 (1)
  • February 2011 (1)
  • January 2011 (2)
  • December 2010 (3)
  • October 2010 (5)
  • September 2010 (1)
  • July 2010 (2)
  • May 2010 (5)
  • April 2010 (2)
  • March 2010 (7)
  • February 2010 (5)
  • January 2010 (5)
  • December 2009 (3)
  • November 2009 (1)
  • October 2009 (5)
  • September 2009 (5)
  • August 2009 (1)
  • July 2009 (1)
  • June 2009 (2)
  • May 2009 (6)
  • April 2009 (4)
  • March 2009 (2)
  • February 2009 (4)
  • January 2009 (1)
  • December 2008 (5)
  • November 2008 (2)
  • September 2008 (1)
  • August 2008 (6)
  • July 2008 (6)
  • June 2008 (9)
  • May 2008 (4)
  • April 2008 (3)
  • March 2008 (4)
  • February 2008 (9)
  • January 2008 (7)
  • December 2007 (6)
Copyright © 2017 Devon O. Wolfgang