Frank B's MP3 lib - changing sample rate

Status
Not open for further replies.

TMcGahee

Member
Hey, I'm working on a project where I'd like to be able to speed up and down the playback of a mp3 file - I don't care about the audio getting warped. I'm using Frank B's great MP3 library with the Teensy Audio lib (thanks to Frank B and Paul and everyone else who contributed! This is a really impressive community project!). I've successfully played MP3 files from winbond serial flash. I know there are a few ways to change the audio speed, and I've (possibly wrongly) concluded that changing the sample rate as the file is being decoded would be the most straightforward.

I dove into the MP3 library, but I'm not getting very far with modifications that aren't breaking the code (Teensy blocks or hangs).

Here's how I understand it now (as a summary for myself + check my understanding against reality):
Main sketch calls play(*filename), which calls play(void) after opening the file (play_sd_mp3.h)
play(void) (play_sd_mp3.cpp) (only describing relevant parts):
mp3objptr = this;
calls decodeMp3(mp3objptr, ...):
AudioPlaySdMp3 *o = mp3objptr;
MP3Decode(o->hMP3Decoder, ...); (mp3dec.c)
this calls UnpackFrameHeader which fills samprate from the header​
initSwi();
sets isr flag: playing = codec_playing;​
The ISR calls update(), but that doesn't seem to affect samprate.
MP3GetNextFrameInfo() and MP3GetLastFrameInfo() do affect samplerate, and the latter is called by decodeMp3, but I can't find where that gets called. From _vectorsRam[IRQ_AUDIOCODEC + 16]?​



What I tried:
changing the initial samprate (overwriting it in play(void) after decodeMp3 returns) - no effect
overriding the samprate in MP3GetNextFrameInfo() and MP3GetLastFrameInfo() and printing the value in update(). Causes hangs/code blocking so I can't see the serial data and no audio plays. I suspect there are calculations somewhere that depend on the sample rate value.


So my questions:
  1. Is changing sample rate the best way to speed up/down playback of the mp3 from serial flash? If not, what is?
  2. If it is, how can I change the sample rate* and any supporting values (bitsPerSample?) so that this works? *either while the file is playing or by smoothly stopping the file and starting it from that position at the changed speed
  3. What is the proper term for the playback "speed" of the song?


Thanks in advance for your time!
 
Last edited:
You have to change the sampling-rate of the Audio-library, first.
Then, the codecs work _only_ when the sampling-rate of the encoded data matches the one of the library. There is no samplerate-conversion anywhere in the code.

For the codecs, there is only a simple "if (samplerate==44100...) play it or play it not" somewhere.. if you don't find it, i'll look.

Edit:
I'll publish a new library, the next days. It will be more flexible, and less hardware-dependend.
The old one is deprecated, from now on (i've not the time to support both versions).
 
Last edited:
Thanks, I found:
Code:
if((mp3FrameInfo.samprate != AUDIOCODECS_SAMPLE_RATE ) || (mp3FrameInfo.bitsPerSample != 16) || (mp3FrameInfo.nChans > 2)) {
    //Serial.println("incompatible MP3 file.");
    lastError = ERR_CODEC_FORMAT;
    stop();
    return lastError;
}
(line 142 of play_sd_mp3.cpp, in void play())
where (codecs.h)
Code:
#define AUDIOCODECS_SAMPLE_RATE            (((int)(AUDIO_SAMPLE_RATE / 100)) * 100) //44100
and (Teensy cores/AudioStream.h)
Code:
#if defined(KINETISK)
#define AUDIO_SAMPLE_RATE    44117.64706 // 48 MHz / 1088, or 96 MHz * 2 / 17 / 256
#elif defined(KINETISL)
#define AUDIO_SAMPLE_RATE    22058.82353 // 48 MHz / 2176, or 96 MHz * 1 / 17 / 256
#endif


I'll try it out with that removed, but it looks like the mp3 library only checks the samprate during play (which makes sense), and that I'm not sure it'd explain why changing the samprate after play begins throws things for a loop/has no effect on playback. Are the buffers calculated based on the samprate or on the bitrate or something else?

I suppose the existence of that if statement begs the question - will the library support playback at other speeds (assuming the file was encoded at 44100)?
Edit: ignore (strikethrough not supported?)


I'm looking forward to seeing the new library! Let me know if there's something I can help test related to these things.

Thanks again for the great work and support you put into these! Edit: Seriously, everything is elegantly handled, appropriately encapsulated, clearly commented and structured. No one could reasonably say there's any spaghetti code here; it's just my understanding of how the two libraries fit together that's keeping me from adding the function I want.



Edit: According to this other sample rates can work if everything is adjusted correctly. I now understand your comment much better - thanks! I'll let you know how it goes!
 
Last edited:
@TMcGahee,
downsampling 48KHz->44.1KHz (or, better the exact samplerate) would be a great addition..
 
@Michael, what do you think about using malloc() on a Teensy ?
mp3 needs about ~36KB, acc about the same, flac much more..(~100KB)

i think, i will drop the support for flac - nobody seems to use it (never got any feedback)
i'm a bit undecided about aac. The quality is much better with the same bits per second, but, as above, no feedback..
 
@Michael, what do you think about using malloc() on a Teensy ?
mp3 needs about ~36KB, acc about the same, flac much more..(~100KB)

Well the Teensys have more memory than say the ATtiny85 (which only had 512 bytes of SDRAM).

However, ~36KB instantly eliminates the Teensy 3.0/LC which have 16K/8K respectively. And it is over 1/2 of the Teensy 3.1/3.2's memory (64K). If you need that much SDRAM, I would say allocate it in the constructor and/or begin function, rather than later in the program, and perhaps print to the USB console if you can't get the memory. If you don't allocate it early, you run the risk that there won't be enough continuous memory when you do need it.

FLAC obviously won't run on the 3.1/3.2 either, and it is over 1/2 of the 3.5's memory (192K).

I suspect in terms of quality, you have some users that want the best sound, and some that just want to play a particular sound and aren't as picky about perfect playback. I tend to be in that later category.
 
Last edited:
Well, the old library uses malloc(), but i'm not too glad about that.

The new - at the moment - uses static allocated memory. So you see the RAM-usage after compiling. Do you think, this is better ?
(btw, flac was only possible with a reduced blocksize - it works now with std-blocksizes on T3.5/3.6 )

I'm asking, because i found that malloc() does not work reliable when not enough memory is available. It allocates the memory, regardless of having enough free ram...

I've never found the reason.

p.s.: the ~36KB are needed by the codecs - no way to save ram. it's due to the decoding-process, that relies on that memory.
 
Well, the old library uses malloc(), but i'm not too glad about that.

The new - at the moment - uses static allocated memory. So you see the RAM-usage after compiling. Do you think, this is better ?
(btw, flac was only possible with a reduced blocksize - it works now with std-blocksizes on T3.5/3.6 )

I'm asking, because i found that malloc() does not work reliable when not enough memory is available. It allocates the memory, regardless of having enough free ram...

I've never found the reason.

p.s.: the ~36KB are needed by the codecs - no way to save ram. it's due to the decoding-process, that relies on that memory.

If you can allocate the memory statically, then I suspect that is best.

That is a serious bug in malloc (probably comes from the newlib implementation).
 
An other problem might be, when the stack grows into the allocated area - it don't remember the exact reason.

Ok, i think, i don't use malloc for the new lib. Or i make a #define switch - perhaps it is still useful, if using more than one instance or switching between aac and mp3 at runtime.. (hmm aac-support yes or no....?....)
 
Just speeding up and slowing down a song according to a sensor input. Silly little tool to help people keep a particular tempo
 
i think, i will drop the support for flac - nobody seems to use it (never got any feedback)
i'm a bit undecided about aac. The quality is much better with the same bits per second, but, as above, no feedback.

Frank, just saw this, although it´s an old thread . . .

Is AAC the Apple way with .m4a ?). If yes, please support AAC in your new lib !

I have been using the lib for quite a while now (and quite often!) for filter testing (playback of mostly M4A/sometimes MP3 from SD card and switching different filters into the audio path to test the effect). It runs very nicely!

I would also like to use your MP3/M4A lib in my new SDR, so I can switch between radio and MP3/AAC-playback from SD card (when I am bored of the shortwave sound quality). ;-)

Have fun,

Frank
 
Frank, just saw this, although it´s an old thread . . .

Is AAC the Apple way with .m4a ?). If yes, please support AAC in your new lib !

I have been using the lib for quite a while now (and quite often!) for filter testing (playback of mostly M4A/sometimes MP3 from SD card and switching different filters into the audio path to test the effect). It runs very nicely!

I would also like to use your MP3/M4A lib in my new SDR, so I can switch between radio and MP3/AAC-playback from SD card (when I am bored of the shortwave sound quality). ;-)

Have fun,

Frank

Hi,
m4a is just a container (and it is technically horrible!), and it can contain AAC-Data (= Advanced Audio Codec, not "Apple" :) ) If you can choose, use *.AAC. Much less useless overhead. The lib tries to parse m4a, but i'm not sure that every file gets parsed correctly..

Well, the new lib, again, seems to be not very interesing for people (no feedback).. so supporting aac will take a while.. sorry.
On the other hand, it would be interesting for my webradio.. there are some stations that send aac only.
I remember when i added flac - it took many hours to implement it - and nobody used it. don't know why. it is much better than wav, is lossless, you have smaller files..... but ok. the better thing is not always the one that poeple use..

the old libs play 44.1 kHz only - maybe time to change this ?
 
Last edited:
Hi Frank,

yes, that´s strange, because playing MP3 / other files was the first thing I wanted to try when I got the Teensy ;-).
so supporting aac will take a while.. sorry.
On the other hand, it would be interesting for my webradio.. there are some stations that send aac only.

But your old lib already played my .m4a-files that originated from putting CDs into Itunes (are these AAC-coded?) !? Or am I getting old and don´t remember that correctly? Ah, you probably mean implementing the old routines into the new lib will take some time? OK, I understand that ;-).
 
Ah, great to hear, that it works with ITunes-files ! I don't like itunes, and never tested this (and never heard "Yes, Frank, works!" :) ) . Yes, it is AAC.
Adding the codec to the new library is not so much work - but adding a parser for m4a is.
 
I had to verify that I remember correctly that your old lib really can play files from Itunes. Yes, it works! [and yes, I also hate Itunes ;-)]

Frank, if you want, I can post a script in your "MP3-Lib"-thread. It´s just a merge of the old MP3/AAC-player example and a fancy spectrum analyser (by Rheslip) based on an FFT1024. But could be useful for somebody who wants to play AAC files on the Teensy 3.5/3.6 and tries to use the new lib.[Just listening to an old John Scofield track ;-)]

P.S.: Teensy 3.6 Processor use 26%, mem 11 (playing MP3 & FFT1024 spectrum analyser)
 
Last edited:
Yes, would be great :)
Thanks in advance

One day, when i have time, i add a codec that capable of encoding, too..
 
Status
Not open for further replies.
Back
Top