InDashPC.org Forum Index InDashPC.org
In Dash Car PC forum
 
 FAQFAQ   SearchSearch   MemberlistMemberlist   UsergroupsUsergroups   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

problems parsing MP3s

 
Post new topic   Reply to topic    InDashPC.org Forum Index -> Software
View previous topic :: View next topic  
Author Message
jboyd99



Joined: 12 Sep 2010
Posts: 4

PostPosted: Sun Sep 12, 2010 6:45 am    Post subject: problems parsing MP3s Reply with quote

I'm looking for libraries to read multiple audio formats, including MP3s, into raw PCM. Ability to play to soundcard asyncronously is a plus. PyMedia looks good, but I'm running into some snags. I'm posting a few questions at once, thanks for any help!

1) What is the status of PyMedia? The news section on pymedia dot org was last updated nearly 4 years ago. This forum seems active, but is development continuing?

2) PyMedia dot org says for Windows to install from the binaries. Which don't seem to exist. After lengthy failed attempts to install from source, I found an installer here: (dubya-dubya-dubya).lfd.uci.edu/~gohlke/pythonlibs . This is for v.1.3.7.3, about same as version from 2006...

3) My code to read MP3s is getting weird noise in some frames, and many frames end abruptly and/or begin with silence. Visualizing the raw data using matplotlib, I can see the sections of noise, which look as if they are not audio data, but are within frames that also contain audio. Am I doing something wrong, or are these bugs?

Here's some code:

Code:

import struct
import time

import pymedia.audio.sound as sound
import pymedia.audio.acodec as acodec
import pymedia.muxer as muxer

import pylab

filename = r'C:\Documents and Settings\KSLB\My Documents\My Music\Corel Sample Music\Classical Interlude 1.mp3'

dm = muxer.Demuxer('mp3')

f = open(filename, 'rb')
s = f.read()
frames = dm.parse(s)

print dm.hasHeader()
print dm.getHeaderInfo()

samples = []
alldata = []
for frame in frames:
    dec = acodec.Decoder(dm.streams[0])
    r = dec.decode(frame[1])
    data = str(r.data)
   
    alldata.append(data)
   
    print r.sample_rate, r.bitrate, r.sample_length, r.channels, len(data)
    unpacked = struct.unpack('{0}h'.format(len(data)/2), data)
    samples.extend(unpacked)

pylab.figure()
pylab.plot(samples)
pylab.show()

snd = sound.Output(r.sample_rate, r.channels, sound.AFMT_S16_LE)
snd.play(''.join(alldata))

while snd.isPlaying():
    time.sleep(1)
Back to top
View user's profile Send private message
jboyd99



Joined: 12 Sep 2010
Posts: 4

PostPosted: Sun Sep 12, 2010 6:56 am    Post subject: Reply with quote

Incedentally, I'm running Python 2.6.5 on WinXP, PyMedia 1.3.7.3.
Back to top
View user's profile Send private message
jboyd99



Joined: 12 Sep 2010
Posts: 4

PostPosted: Wed Sep 15, 2010 3:08 am    Post subject: Reply with quote

So I guess I'll answer my own questions.

I discovered the (somewhat obvious) bug in my above code was creating a new Decoder every pass through the loop, which was therefore discarding large chunks of compressed audio and losing the correct place in the input.

With that fixed, everything worked much better except for one issue I've seen a couple other posts about, with no great answer, which is that the API somehow appeared to be neither blocking nor non-blocking. For short sounds, it would return from calls to Output.play() instantly, but for longer chunks of sound, it would mysteriously block for a while and then return before the sound completed playing. I tried creating a separate thread to handle playback, but this had its own problems (very short sounds for some reason played silently).

Since others have had similar issues -- the explanation is that Windows (and I imagine many platforms) has a buffer limit to how much data you can send to the soundcard without blocking. PyMedia does the right thing in that you can apparently send it much more data and it will queue it up and send as the Win32 API is able to accept it, but once the buffer is filled, PyMedia (or a library it wraps) will block until it can send everything passed to Output.play(). The solution is to either do your own thread management with this in mind, or probably better is to call Output.getSpace(), which will tell you how many more bytes can be sent to the sound buffer right now. If you send less than this, you will never be blocked. On Windows, the buffer takes chunks of 10K at a time, so if getSpace() returns 10000 bytes, and you call play() passing, say, 512 bytes, an immediate second call to getSpace() should return 0. YMMV.

With these problems addressed, the library is working beautifully. I am able to load multiple MP3 files, mix them and add various simple DSP effects and then send them to the sound card in real-time!

I still am curious about the state of PyMedia, and why the website is so out of date. Also I cleaned up the aplayer() tutorial so that it works correctly and is a bit more readable, following PEP-8 conventions. Where might I post my edit?
Back to top
View user's profile Send private message
jboyd99



Joined: 12 Sep 2010
Posts: 4

PostPosted: Wed Sep 15, 2010 3:24 am    Post subject: Reply with quote

Quote:
I tried creating a separate thread to handle playback, but this had its own problems (very short sounds for some reason played silently).


And as I wrote this I realized what the problem was with that, in case anyone runs into the same thing:

Code:

thread.start_new_thread(snd.play, (data,))


Duh. Though this gaurantees asynchrony, it also means that as soon as snd.play() stops blocking, which will be immediately for sounds small enough to go completely to the sound card driver (35K bytes on my machine), the anonymous thread will finish. Apparently this is causing some object to go out of scope, because doing this:

Code:

while snd.isPlaying():
  time.sleep(.5)


in the main program thread returns False also as soon as the above spawned thread is destroyed, even if sound has been sent to the buffer and not yet played. My guess is something like this would work for trivial apps:

Code:

def playSound(data):
  snd.play(data) # assumes we've set up a global sound Output
  while snd.isPlaying():
    time.sleep(.5)

thread.start_new_thread(playSound, (data,))


Note that on my machine at least, creating new Output objects works to have multiple channels to the sound card, but its important to maintain references to all of them, since once they go out of scope and are garbage collected, any sound currently playing will be cut off.
Back to top
View user's profile Send private message
Display posts from previous:   
Post new topic   Reply to topic    InDashPC.org Forum Index -> Software All times are GMT - 8 Hours
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum
You can attach files in this forum
You can download files in this forum


Powered by phpBB © 2001, 2005 phpBB Group