Scripts, Tools & Methods Developed at Hook
Firstly, what you came here for:
Make sure you have a mic hooked up, and click record. Once you have recorded some sound, click, stop. At this point you can fill in some info for the file (these are used to set the metadata/comments/tags). Then click Encode to watch it encode the data. Progress of the encode and the time it took to encode is listed at the right. Finally you can click Save, and you can save your newly encoded .ogg file to your local machine. Please note that the performance tuning in this example is set to keep flash as responsive as possible while the encoding is taking place. This naturally slows the encoding performance. Please see below to learn how to adjust the tuning to your needs. Also, before we get started, I would like to mention, that we are planning on creating a series of posts about what we have learned working with Adobe Alchemy in the coming weeks. Now, with that out of the way, on with the show!
Quick props to all of the authors/sites that helped me get started with Alchemy:
- Forcing stdin/stdout to deal with ActionScript ByteArrays:
- flyield() example:
- Process for compiling multiple source files under gcc:
More on all of those things coming in the next few posts!
Currently this encoder takes 44.1kHz, 16bit, stereo wav formatted PCM data to do the encode (Adobe’s WAVWriter class is used to do the wav conversion in this example). This version allows for different quality settings, async performance tuning, and the recommended Ogg Comment Tags.
I’m hoping the next release of our encoder will handle more types of input (different bitrates, mono support, etc..) as well as the ability to cancel the encode at any time. I am also hoping to get a decoder working within this same package, for convenience. For now, however I would suggest using the very well done Ogg Vorbis decoder from Branden Hall at automatastudios.com. If you check out the sources for our Sequencer, you can see some examples of how to use the Decoder from Automata Studios.
Additionally in the coming weeks we will be posting about making use of Alchemy in general. From setup through all the steps we took creating this encoder. We will also be posting code snippets for commonly needed alchemy abilities, such as reading and writing to and from ByteArrays, calling ActionScript methods from Alchemy, and a slew of other tid-bits that were hard to find online.
During those posts I will be releasing the source code to the encoder, but I have to read through all of the licenses for the different pieces of the Xiph example code to be sure that I’m not violating any of those by redistributing modified versions of Xiph.org’s sources. Usually this just means that specific comments have to stay intact, and probably a readme file sent along with the sources. But until then, I figured the .swc could be useful to people as is
Or get updates from our Facebook page:
Wow, it felt dirty saying that, but this is where we will be posting about updated versions of the encoder.
Much of the encoder code was taken from the example C code on the Xiph.org website. It was then altered to expose parts to Flash as well as altered to allow for asynchronous operation. The Vorbis and Ogg C libraries were modified for compilation through Adobe Alchemy (FlaCC). All of this was linked and archived to a .swc for use within flash.
Be sure to add the swc to the library in your IDE. For Flash CS5, you will need to go to Publish Settings -> Flash Tab -> Script Settings -> Library Path Tab. In there you can either add the path to the folder containing the swc, or you can add the swc itself.
As part of the example code there is an OggEncoder actionscript class. This simplifies the use of the encoder swc.
Use of the encoder is fairly simple. First new up an instance of the OggEncoder, and listen for ENCODE_COMPLETE and ENCODE_PROGRESS events to keep track of the status.
//Create a new instance of the encoder _oggEncoder = new OggEncoder(); //Listen for progress and complete events from the encoder (encode happens async) _oggEncoder.addEventListener(OggEncoderEvent.ENCODE_COMPLETE, handleEncodeComplete, false, 0, true); _oggEncoder.addEventListener(OggEncoderEvent.ENCODE_PROGRESS, handleEncodeProgress, false, 0, true);
To start the encode progress, you will need to convert your sound data to a 44.1kHz, 16bit, 2Channel stereo wav file, with adobe’s WAVWriter Class.
//Setup up wav writer for flash sound -> wav conversion var wavWriter:WAVWriter = new WAVWriter(); wavWriter.numOfChannels = 2; wavWriter.sampleBitRate = 16; wavWriter.samplingRate = 44100; //Save converted data to _wavBytes (I would suggest modifying the // processSamples method to work in small chunks that you call from //a timer so you don't lock up the UI during the processing) _wavBytes.position = 0; _recData.position = 0; wavWriter.processSamples(_wavBytes, _recData, 44100, 2);
From there you can simply call:
//Start the encode _oggEncoder.encode(_wavBytes, oggComments);
While its encoding OggEncoderEvent.ENCODE_PROGRESS events are dispatched. The “data” property on the event contains a number 0 – 1 which represents the percent complete.
Lastly an OggEncoderEvent.ENCODE_COMPLETE event will be dispatched when the encoding has finished.
If you don’t want to go through the OggEncoder class, you can directly access the exposed functions in the swc:
//do encoding startAsyncEncode(onCompleteMethod:Function, clientObj:Object, wavBytes:ByteArray, oggBytes:ByteArray, quality:Number, numLoopsPerYield:int, commentObj:OggComments) //get version number as String: "0.5" getVersionNumber() //get version label as String: "beta" getVersionLabel()
First import the encoder classes
Then create an instance of the Alchemy Lib:
//Alchemy Loader object private var _loader:CLibInit;
//Setup the alchemy library _loader = new CLibInit; _lib = _loader.init();
Lastly do some encoding:
/*************** * This is the actual call the the Alchemy Ogg Encoder. * Parameters: * * encodeComplete: * method called when encoding is complete * * this: * the object passed in here is required to have a method called handleProgress. * when there is progress, the method is called with the number of bytes read from the wav * ByteArray, and the length of the wav ByteArray is passed to it. * (see below for implementation) * * _wavBytes: * the 44.1kHz, 16Bit, 2 Channel wav encoded bytes (ByteArray) * * _encodedBytes: * a ByteArray to store the Ogg encoded bytes in. * * 0.5: * quality setting for Ogg (valid range is 0.1 to 1) * * 1: * number of ogg pages before yielding back to flash player * (low numbers - flash player stays faster, high numbers - encoding faster) * I would really just stick with 1 here * * $oggComments: * this is the comment value object for filling in the comment tags for the Ogg file. ****************/ trace("Wav Bytes: " + _wavBytes.length + " / " + _wavBytes.position); trace("Enc Bytes: " + _encodedBytes.length + " / " + _encodedBytes.position); trace("Version Number: " + _lib.getVersionNumber()); trace("Version Label: " + _lib.getVersionLabel()); _lib.startAsyncEncode(encodeComplete, this, _wavBytes, _encodedBytes, 0.5, 1, $oggComments);
You can also call getVersionNumber() and getVersionLabel() on the library and it will return the current version info of the swc as an ActionScript String.
That about wraps up the technical portion. If you are interested in the story behind the project keep reading
A quick history of the project
Off and on for the last few months I’ve been playing around with various parts of the audio world in Flash and Air. Eventually each project hit the same roadblock, saving out the final audio performance. Its fairly trivial to save out the raw byte data to a file and load that back in. But of course the issue with that is the file size. As it is, well, gigantic. At first I started looking around for MP3 encoders already built for flash. I came across this port of the Shine mp3 encoder as basically the only viable option. While its pretty awesome in its own right, its still fairly limited in terms of sound quality, and the encoding process is rather slow. There were a couple of other things that made mp3 less of an attractive option as well. The first issue is the odd licensing scheme for the mp3 format. Since I’m no lawyer, it was a bit scary trying to build an encoder from the published spec. Lines like this “Implementers of the standard were supposed to devise their own algorithms suitable for removing parts of the information from the audio input.” made me nervous, probably without warrant. However, there was another reason that mp3 wasn’t a good fit for the projects, frame padding. As discovered while we were building our Sequencer mp3’s won’t loop smoothly. It seems that if a frame of the mp3 is not full, it gets padded. This padding causes a very noticeble gap in playback. Therefore smooth looping without some sort of gapless playback support from the encoder and decoder is impossible. Thus it was time to look for another format. Ogg Vorbis be thy name.
Ogg Vorbis is completely open, and is of very high quality. From the Xiph.org page:
“Ogg Vorbis is a fully open, non-proprietary, patent-and-royalty-free, general-purpose compressed audio format for mid to high quality (8kHz-48.0kHz, 16+ bit, polyphonic) audio and music at fixed and variable bitrates from 16 to 128 kbps/channel. This places Vorbis in the same competitive class as audio representations such as MPEG-4 (AAC), and similar to, but higher performance than MPEG-1/2 audio layer 3, MPEG-4 audio (TwinVQ), WMA and PAC.”
An Ogg Vorbis file doesn’t have the same issues with gapless playback as mp3, and seems to make smaller files, at least from my initial testing, which isn’t super extensive. Additionally encoding performance is pretty good.
The problem was, I could only find an Ogg Vorbis Decoder for flash. Mind you its an excellent decoder written by Branden Hall at automatastudios.com, but that only gets us half way. Some further looking around revealed that Andre Michelle and Joa Ebert had ported a java version of the encoder to AS3. So I started trying to do that, but stopped almost immediately when I came across the use of 64bit ints in the Java code. Flash’s ints are only 32bits wide, and since the codebase was so big, I didn’t want to try and merry two 32bit ints together everywhere a 64bit integer was needed. So began the trip down Alchemy Lane. We will be doing a series of posts about Alchemy in the coming weeks, so stay tuned!