Rather than just blog an experiment, and leave it at that, I thought I’d blog the story and thought behind the experiment. Hopefully it’s interesting to someone. π
The Idea
Friday morning I woke up with the random idea to try to simulate playing records in Flash. Don’t ask me where it came from, I just remember waking up and saying to my wife “I wonder if I could make MP3s sound like records with ActionScript?”. She smiled and nodded and said it sounded like a lovely idea (she’s used to humoring random thoughts first thing in the morning). If I were to take a guess, I’d guess the idea grew from a seed planted a month or so ago when my wife and I were discussing the idea of getting a new record player. Not sure why it took a month to turn into an idea, but apparently I think slowly.
I headed to the office and started thinking through the code. Beyond a cursory glance, I’ve never really played with any of the new sound APIs, so I managed to justify this experiment to my team as R&D for future projects.
Getting Started
I started by glancing through the API, and checking out a simple tutorial by Lee Brimelow on working with extractSound and the sampleData event. Then I switched over to designing a 2.5D vector record. I often start explorations this way – learn the core concepts I’ll need, then switch to design for a while. I’m not really a designer, but I think it helps turn on my creative facilities, and gives my subconscious a chance to fit the pieces together while I play with design.
Design
For anyone interested in the design side, here are the more interesting bits: the grooves are dynamically drawn circles, offset from the center randomly by a tiny amount so that you can see them spinning; the highlights are just two layers of vector wedges with blur applied to them, they are counter rotated as the record spins; the whole record is slightly rotated on the Y axis and the highlights are slightly offset to make it wobble a little bit when it spins. Nothing too exciting, but it was kind of fun getting it to look right.
Playback
Once I had a decent looking record, my next step was getting basic playback working (again, no sound API experience). I wanted to use FileReference, so that users could play back their own music from their harddrive. Unfortunately, there is no Sound.loadBytes() method (you can vote on this feature here), which makes it difficult to get a sound from the ByteArray data that FileReference returns. Fortunately, Chris at FlexibleFactory wrote a handy class that extracts the MP3 data, wraps it in a SWF, reads it into a Loader, and then pulls it back out as a Sound object.
Once I had the Sound object, it was easy to use extractSound and the sampleData event to get simple playback working (see Lee’s tutorial for help on this).
Scratching and Seeking
Next, I wanted to tie the on screen record to the music playback. I wanted the record to spin when the music was playing, and I wanted you to be able to spin the record to change the music’s playback speed. To make it more complicated, I wanted it to be as physically realistic as possible.
I quickly realized that to do this, I would have to make the record’s rotation subservient to the position of the playback. This is to prevent problems if my sampling events get out of synch with the rotation (you’ll notice even the tiniest audio glitch, but not slight variations in the rotation speed). As my sampling position changes, I update the rotation of the record by using the formula: position/44100/(60/rpm)*360, where 44100 is the sampling rate, 60 is the number of seconds in a minute, rpm is the revolutions per minute (33.3 for an LP), and 360 is the number of degrees in a full rotation. And yes, that formula can be optimized.
When you click and drag the record, it isn’t rotated directly. Instead I calculate a new sound position based on the rotation change implied by the mouse x/y, then update the rotation based on that position.
To generate the sound when dragging the record, I extract the samples between the last position and current position from the source music, then use simple interpolation to generate enough sample data to fill my standard sample size. If you rotate it backwards, it reverses the sampling direction to play backwards.
I’m going to try enhancing the interpolation algorithm in the future. I’m actually planning to try a method I used for my old pattern recognition system, which should hopefully increase the fidelity a bit.
Effects
I also wanted to introduce the hissing, pops, and skipping of a real record player.
Hissing was easy. I just add a percentage of random noise to the sample. I determine this percentage using a sine wave based on the current rotation of the record. This way the hissing fades in and out in conjunction with the records wobble, which is inline with how I remember records sounding. (mental note, I probably should have gone back and listened / looked at a physical record at some point during this experiment)
Skipping started easily. Using a derivative of the formula above, it was pretty simple to adjust the position by exactly one revolution of the record, and repeat that a random number of times at the same position. The hard bit was that I wanted it to pop a bit when it skipped, as opposed to just cleanly rewinding. I got this working by simply writing a block of “1”‘s at the point that it skips. I’m sure there’s a smarter way to do it, but I haven’t had a chance to find one yet.
To get popping working, I just applied what I had learned from skipping. I add a random offset to a randomly length block of samples. This results in quiet pops and louder ones.
Volume Monitor
I quickly added a retro style volume monitor behind the record. It just uses the drawing API to draw out the bars based on the output SoundChannel’s leftPeak and rightPeak values. No need to touch computeSpectrum for something this simple.
This was the last thing I did before heading home for the day, but hey, I had a whole weekend ahead of me.
ID3 Data
I didn’t have a lot of time to play on the weekend, but I did really want to get ID3 support in. Unfortunately, the Sound object that is generated from the FileReference data is stripped of ID3 tags, so I had to find another option.
After a bit of hunting, I found the Metaphile library which can read ID3 data from an MP3 in a ByteArray. It’s not well documented, and it has some minor dependencies on the Flex framework, but it was pretty easy to figure it out and get it working without Flex. This let me display the song title, artist, and album. More excitingly, I could pull out allbum art (usually), and display it on the record.
I also did a bit of clean up on the UI, and tweaked some of the effects a bit.
Here’s the current result. Fittingly, I think it sounds best with older tracks (the Beatles and Louis Armstrong sound pretty good). The effects get drowned out by louder tracks (this is true on real record players, too). Turn up your volume to hear the hissing and pops. If it skips, you can either wait for it to continue, or advance the record manually to stop the skipping.
Unfortunately, the ID3 library occasionally generates errors, and I haven’t got around to suppressing them yet. If an MP3 won’t play, try reloading and trying a different one.
AC_FL_RunContent(
'codebase', 'http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=10,0,0,0',
'width', '550',
'height', '450',
'src', '/blog/assets/record',
'quality', 'high',
'pluginspage', 'http://www.adobe.com/go/getflashplayer',
'align', 'middle',
'play', 'true',
'loop', 'true',
'scale', 'showall',
'wmode', 'window',
'devicefont', 'false',
'id', 'record',
'bgcolor', '#000000',
'name', 'record',
'menu', 'true',
'allowFullScreen', 'true',
'allowScriptAccess','sameDomain',
'movie', '/blog/assets/record',
'salign', ''
); //end AC code
I still plan on improving the interpolation, finessing the effects, and making the scratching sound more realistic (I think I need to introduce some random sawtooth wave forms or something). I’d also love to build a multi-touch version with two turntables and a cross fader. For now, I’m not going to release the source – I’m still playing with it and it’s a horrible, stinky mess. I might release it in the future. I thought I should blog it now though, in case it falls off my radar.
The result is amazing, excellent work Grant!!
Ahhh man I’ve been waiting for you to say something about this… It keeps getting better and better each time I refresh the URL.
+1 for source π
I have a few good ideas for an app that i’d like to use somethin like this in – I’ll have to read this over and over and check the resources you’ve posted.
Thanks!
Gotta say, reading the album art out of the ID3 tag was an excellent touch.
Alice in Chains works well too. π
Awesome work as always Grant. I do need to go back and clean up the Metaphile stuff one of these days, but probably won’t until I need it in a real project. π Feel free to send me any issues that are getting in your way though.
Awesome. Love it.
A tonearm would be a nice addition.
awesome! I created a similar effect with a scrub bar = ] I also couldn’t make the scratches sound truly realistic, and playing in reverse with sample data isn’t clean, but it’s awesome enough! I like what you did with the ID3 image.
Works really well. One thing that I always enjoy with record players is the sound of quickly spinning the record forward (while it’s already playing) and then hearing the ease of the music slowly going back to the proper play speed.
Could be cool to integrate some gTween into the playback π
Would be nice to see the needle
amazing work man – some type of physics/easing/throwing type behaviour for the platter itself would be cool, so I could dub-styles it up and rewind out of a track π
Grant, I’m not trying to put a “spin” on your post, but I was wondering if it was possible to “scratch” sound with ActionScript also this week too. The link you provide from the theflashblog proves it and your inspiring 3D turn table is very motivating.
I have only played with the physics, had no idea how to do the sound until your post…
http://keith-hair.net/blog/examples/turntables
Hi. It’s very interesting.
Did you try to record sound in flash with new sound api?
Suppose we have 2 mp3 files that playing simultaneously in flash player, and we want to record one mp3 that is mixing that 2 mp3s, how can we do that ?
For scratching you need to implement a decimation filter. The coefficients of the filter depend on the speed of your “scratch” so you can build the filter in memory and use a lookup table…
that’s just super!! awaiting for the source… π
Great write-up π As an avid vinyl collector myself I do miss the feeling of a real turntable. I think it’s due to the way the sound stops abruptly. If you’re near a 1200/1210 turntable you will immediately hear the way a record sounds like when you put your hand on the platter…or when you hit start/stop. I think implementing this will immediately make it feel more realistic.
Sorry, I meant to post a useful URL for decimation or “multirate resampling”…
http://www.dspguru.com/info/faqs/multrate/decim.htm
http://www.dspguru.com/sw/opendsp/alglib2.htm
Sorry, I meant to post a URL, but your spam filter sucked it up…
If you google “decimation filter” and read the “Multirate FAQ” about decimation, you can find some useful links to info and code.
You are looking for FIR decimation filters, but after seeing Andre’s talk at FOTB, there is probably a shortcut to this as AS3 might not be upto the job, unless you can write a PB kernel to do it…
Really cool… You should add a pickup that you can move for fast seeking, two turntables and a cross fade between so it becomes a retro DJ set… Then the ability to load a whole playlist as a LP and a box where you can flip through the LP:s to pick the ones you want to load up… And then… Well ok I’ll stop… π
oops sorry you had already thought of that.. I should have read before posting..
Great job ! very nice design to !
I also played with this kind of stuff a while ago to build a scratch tool.
http://demo.actiplay.com/scratch/
you can scratch the vinyls, play with the crossfader, the pitch and the volumes
Love it! Way better than I imagined! As already mentioned by Owen: A slow down when you click and then a speed up again when you let go the record, that would feel incredible.
Nice work Grant. Thx for the mention!
Hello
I like your brilliant idea about to simulate playing records in Flash.You have done a good job.Now I am also thinking about to try this idea.
cool idea Grant, is there too much skipping going on, or is that a bug?
been there, done that π in a not so dynamic way: http://krisrok.de/blok/?p=47
you’re lacking performance though. the sound isn’t running smoothly at all on my netbook.
but the mp3 upload is great of course, show us π
cheers
Wow, amazing posts as usual. I see a huge potential for these ideas! as a musician / programmer I can see amazing uses for an app like this..
If you get a chance check out DBlueGlitch, its a virtual fx plugin for modern music sequencers:
http://illformed.org/plugins/glitch/
very cool grant!