2010! A new year, a new decennium even! This year I want to be more consistent in publishing articles on this weblog. That’s my new year’s resolution.
The first one of the new year is more of a utility than a complete application. It’s a wave display; It loads an MP3 file and displays the sound as a graph of the waveform. It makes no sound. A simple waveform display like this might be a useful component in a larger application where audio needs to be edited. Here it’s on its own because I was figuring out how to best program such a component. So this application is more experimental than useful.
Click here to download all source files in a ZIP.
How to use it
A default MP3 file is loaded and displayed at startup. But you can load your own MP3. Click the ‘load mp3’ button and browse to an MP3 file on your computer. Opening a large MP3 file might take a long time however. Best to try with short files. Five minutes or less for example.
Once the MP3 is loaded and displayed there’s several controls to zoom in closer on details of the waveform:
- Zoom in to a detail of the waveform with the ‘zoom horizontal’ slider.
- When zoomed in you can move through the waveform with the scrollbar directly under the waveform.
- For more precise movement once zoomed in drag the mouse left or right on the waveform.
- Magnify the waveform vertically with the ‘zoom vertical’ slider. This is useful to inspect very quiet parts of the sound.
Some information is shown above the display:
- In Flash the sample rate is 44100 samples per second. When the waveform is at the highest zoom level, it shows one sample per pixel. (So one second of audio would take up 44100 pixels!)
- At lower zoom levels each pixel displays the average amplitude of several pixels. The amount is displayed at ‘samples per pixel’.
- The first and last sample in view are shown at ‘first sample’ and ‘last sample’.
Some notes on the ActionScript
The biggest problem with the display was to get it to respond quick to zoom and position changes. It should use as little calculations as possible to refresh the display. Three things reduce the number of calculations (and so the processor load) here:
Each change in zoom level or position results in a changed portion of the waveform that is actually shown in the display. When the display needs updating, it’s first determined what the first and last sample are that will be displayed. Then only that portion of the file is processed. That means that especially at high zoom levels only a tiny fraction of the file needs to be calculated.
But at low zoom levels still a lot of samples need to be averaged: To show one minute of waveform 44100 x 60 = 2,646,000 samples must be processed. More than two and a half million! To reduce this number samples are skipped. The more samples there are, the more are skipped.
This bit of ActionScript shows how these two reductions are done:
/**
* Sets the zoom level of the display. Measured in how many samples one horizontal pixel of the display represents.
* @param inSamplesPerPixel : How many samples one horizontal pixel of the display represents.
* @param inCenterSamplePosition : The center or focus of the zoom. 0.5 is the horizontal center.
*/
private function setCurrentSamplesPerPixel(inSamplesPerPixel : Number, inCenterSamplePosition : Number = 0.5) : void
{
if(inSamplesPerPixel == mCurrentSamplesPerPixel) return;
// get the sample that is the center of the zoom
var centerSample : int = mFirstSampleInView + (mNumSamplesInView * inCenterSamplePosition);
mCurrentSamplesPerPixel = Math.max(mMinSamplesPerPixel, Math.min(inSamplesPerPixel, mMaxSamplesPerPixel));
mNumSamplesInView = mCurrentSamplesPerPixel * mDisplayWidth;
mFirstSampleInView = centerSample - (mNumSamplesInView * inCenterSamplePosition);
// polling stuff
mPollingFrequency = Math.ceil(mCurrentSamplesPerPixel / 20);
mNumPolls = int(mCurrentSamplesPerPixel / mPollingFrequency);
mBytesToSkip = (mPollingFrequency - 1) * SoundProxy.BYTES_PER_SAMPLE;
if(mFirstSampleInView < 0) mFirstSampleInView = 0;
if((mFirstSampleInView + mNumSamplesInView) > mNumSamplesTotal) mFirstSampleInView = mNumSamplesTotal - mNumSamplesInView;
updateGraphic();
}
And the third thing to reduce the processor load is a simple timer. There’s a maximum of how often the display can update. In this case a maximum of once every 50 milliSeconds was fine. The timer script was added at the start of the update function:
private var mUpdateDelayTimer : Timer = new Timer(50, 1);
/**
* Redraws the display.
*/
private function updateGraphic() : void
{
// delay between updates
if(mUpdateDelayTimer.running) return;
mUpdateDelayTimer.start();
// etcetera ...
That’s it for now. Download the source files to have a closer look at the code or to use it in your own projects.
This project is build with PureMVC as framework again. I like it more and more.
The UI components I used here are very much based on Keith Peters‘ Minimal Comps. To load an MP3 file and use it in Flash as a Sound object I used FlexibleFactory‘s MP3FileReferenceLoader library.
For debugging I used the Yalog logging tool by Stephan Bezoen. Very nice Air application.
Hi!
For some reason, I can not open the application. I have the file, but I can not find the app.
Excellent post. Thanks. Just perfect.
@Lee: Strange, the source files ZIP should download fine! I’ll send them to you via email.
The link to the source files is sadly 404
This looks superb. I have converted WAV to PCM wave graphs in C, never in Flash – I have never touched Flash. Now I find I need a way to do what you have done, but to allow an external process to control the loading and positioning of the file – I’m going to try to write an API for your code, if I may, but since this is my first Flash, this may take some time….!
Hi Sam,
I don’t do freelance projects. At least not at the moment. But the source code for the display is free to download and use, so I hope that’s of help to you.
I have an idea how I can use the component in my application. Are you available for a paid project?
Hi JC, thanks for noticing. The download URL was wrong. I just corrected it and it should work now. Cheers.
Thanks! Yes, subscribing to the RSS feed should work. But I didn’t post anything for some time.
Hey there,
Thanks for posting this!
This looks great and I’m working on the same kind of project.
Did your source code change location? Couldn’t access it…
Fantastic blog! I truly love how it is easy on my eyes and the information are well written. I am wondering how I can be notified whenever a new post has been made. I have subscribed to your rss feed which really should do the trick! Have a nice day!
cheers,:)