
Introduction
'madxlib' is a source code for creating a Windows DLL, that performs buffer-based MP3 decoding. It allows you to build interfaces that can process a given MP3 file in chunks at the intervals you specify, and returns the decoded 16-bit PCM samples in a buffer.
Background
madxlib effectively supersedes madlldlib, another MP3 decoding source I have released via The Code Project. The two sources differ in that, madlldlib took as parameters the location of the input MP3 and output WAV/PCM files and performed the conversion while passing status back to the calling code. This gave little control to the calling code to pause, restart, queue the samples, etc. madxlib rectifies this by relying on the calling code to fill the input buffer (which can come from a local file, the Internet, network share, etc). It then returns an output buffer of the converted PCM samples and waits for the next input.
This source, like madlldlib, was based on the madlld source, which was designed as a tutorial of the low-level functions of the libmad library.
Using the Code
Included in the source is the file test.cpp. This is a simple example demonstrating how to program the madxlib API. It consists of a single main() function.
Within main(), the variable declarations are first made:
unsigned char in_buffer[MADX_INPUT_BUFFER_SIZE + MAD_BUFFER_GUARD];
unsigned char out_buffer[MADX_OUTPUT_BUFFER_SIZE];
size_t a;
size_t in_size = MADX_INPUT_BUFFER_SIZE + MAD_BUFFER_GUARD;
madx_house mxhouse;
madx_stat mxstat;
madx_sig mxsig;
in_buffer and out_buffer will be used to send the MP3 data to madxlib and retrieve the PCM samples once they've been decoded, respectively. There are three datatypes specific to madxlib. madx_house "houses" the necessary data types that make madxlib (and the underlying library libmad) work. madx_stat is used to send status and information, such as how many bytes to read from the input, between the calling code and madxlib. madx_sig is an enum type that is returned by the madx_read() function (described below) to signal what action your code should take.
Next, madx_init() is executed.
madx_init(out_buffer, &mxhouse);
madx_init() takes as parameters a pointer to the output buffer, and the address of the madx_house structure, mxhouse. It then initializes the necessary libmad structures and assigns the out_buffer pointer to an internal variable.
Next test.cpp opens its input and output files with fopen(), erroring if there is a problem. Now we are ready to start the main loop (and most important logic) of the program.
do
{
mxsig = madx_read( in_buffer, out_buffer, &mxhouse, &mxstat );
if (strcmp(mxstat.msg, "")) printf("%s\n", mxstat.msg);
if (mxsig == ERROR_OCCURED)
{
printf("Unrecoverable error %s\n", mxstat.msg);
break;
}
else if (mxsig == MORE_INPUT)
{
if (mxstat.buffstart)
{
if ( (a = fread(mxstat.buffstart, 1,
mxstat.readsize, in_file))== mxstat.readsize)
{
printf("Filling buffer.\n");
}
else if (feof(in_file))
{
mxstat.is_eof = 1;
mxstat.readsize = a;
}
else
printf("Error! %d\n", ferror(in_file));
}
else
{
if ( fread(in_buffer, 1, mxstat.readsize, in_file) ==
mxstat.readsize)
{
printf("Fill buffer full.\n");
}
else if (feof(in_file))
{
mxstat.is_eof = 1;
mxstat.readsize = a;
}
else
printf("Error! %d\n", ferror(in_file));
}
}
else if (mxsig == FLUSH_BUFFER)
{
if (fwrite(out_buffer, 1, mxstat.write_size, out_file)
== mxstat.write_size)
printf("Writing buffer.\n");
else
printf("fwrite() error\n");
}
else if (mxsig == EOF_REACHED)
{
if ( (a = fwrite(out_buffer,1,mxstat.write_size,out_file))
!= mxstat.write_size)
{
printf("Error with final write! out_size:%d, fwrite: %d\n",
mxstat.write_size, a);
}
else
printf("Finished. out_size:%d, fwrite: %d\n",
mxstat.write_size, a);
break;
}
}
while(1);
The above do...while loop is the bulk of test.cpp. For every iteration, it makes a call to madx_read(). madx_read() takes as parameters the input buffer (initially empty), the output buffer, the address of mxhouse (the madx_house structure), and the address of mxstat (the madx_stat structure). It returns a madx_sig value, which is stored in the variable mxsig.
madx_read() essentially does the following. It decodes the MP3 samples in the input buffer to raw audio (PCM) and synthesizes them, then scales the samples down to 16-bit. Depending on what is encountered during the execution of the function (error, more input is needed, output buffer is full), the appropriate madx_sig signal is returned, and the calling code (i.e. this loop) must handle the signal. The trickiest part of this is handling the partial reads when the buffer fills up. This is where madx_stat comes in.
After madx_read() returns, mxsig is inspected to determine what action should be taken. If an ERROR_OCCURED, it prints an appropriate message and breaks out of the loop. If the instruction is FLUSH_BUFFER, then this means that the output buffer (out_buffer) is full and ready to be written, in this case, to disk. Note the use of mxstat.write_size in the fwrite() statement. mxstat.write_size will always contain the correct number of units to write. When a FLUSH_BUFFER has been returned, we do not exit the loop. We only exit if ERROR_OCCURED or EOF_REACHED. If the latter occured, then we must write the remaining bytes in out_buffer to the file and break the loop.
If MORE_INPUT is signaled, then the calling code must refill the input buffer. First, this code segment (else if mxsig == MORE_INPUT above) checks the mxstat.buffstart variable. If it is 0, then a full buffer read is done, which fills the buffer from the first position to mxstat.readsize. mxstat.readsize is always set by madx_read() before returning MORE_INPUT. If mxstat.buffstart is not 0, then it means you must fill the input buffer, but not from the first position. In this fread() statement, the input buffer in_buffer is substituted for mxstat.buffstart, which is set by madx_read() to some point between the starting position of in_buffer and the end.
Note that after both reads EOF is checked, and if reached the variable mxstat.is_eof is set to 1 (or "true"). This must be done so that madx_read() can pad the final bytes before processing them. This is a specific requirement of the underlying libmad library.
All that remains is to pass the &mxhouse to madx_deinit(), which takes care of freeing memory used by the libmad structures. We then close out_file and our task has been completed.
madx_deinit(&mxhouse);
fclose(out_file);
return;
Notes
- The source provided above depends on the libmad source. It must be downloaded and compiled first. See the notes in the file "Makefile" in madxlib for further instructions on compiling.
- This is a Beta release of madxlib. You should always check my website for the latest updates. They will be there first. In time I should have a forum dedicated to madxlib for answering questions.
- Also included in the demo is a C# sample that uses P/Invoke to access madxlib.dll.
| You must Sign In to use this message board. |
|
|
 |
|
 |
I am officially at my wits end trying to come up with a solution for Windows Mobile. There is a project out there called FSPlayer that uses the libmad for a windows mobile audio application. I don't want to rewrite my app in c++. So I ran across this code. It works great for a c# app written for win32. I'm recomplied libmad and re-linked it with madxlib using the FPM_DEFAULT pre-processor option (which is what the FSPlayer project uses). I was able to pinvoke into the DLL using C# but it bombs out during the read. Maybe I've not compiled libmad correctly or maybe I just don't know what the hell I'm doing. I've tried using the FPM_ARM pre-processor option but that throws a ton of errors. I'm pretty sure I've done everthing right up to the point of the error. Unfortunetly I can't debug into because I haven't figured out how to get visual studio to deply the madxlib.dll when I deploy to the emulator for debuging. I wouldn't go this route because I did port the back end code from FSPlayer into a .net c++ assembly and it works great in a win32 app as well but I can't figure out how to compile it into a .net compact framework c++ assembly. Apparenlty you can't write .net assemblys for the compact framework in c++. Anyone out there know how or what I'm doing wrong?
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
i have mp3 byte .i want to decode to pcm. but i dont know where can i give size of data and other . please write function for this such as this example . byte[] decode(byte[] data) {
return pcmdata; } thanks.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Runtime.InteropServices; using System.IO;
namespace ATRadio { public class MadXStream : Stream, IDisposable { public const int MADX_OUTPUT_BUFFER_SIZE = (1152 * 8); public const int MADX_INPUT_BUFFER_SIZE = (5 * 1152 * 8);
// Gotten from sizeof(madx_house): public const int MADX_HOUSE_SIZE = 22672;
// The easy one public enum madx_sig : int { ERROR_OCCURED, MORE_INPUT, FLUSH_BUFFER, EOF_REACHED, CALL_AGAIN }
[StructLayout(LayoutKind.Sequential, Size = 276), Serializable] public struct madx_stat { [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] public string msg; public uint write_size; public int is_eof; public uint readsize; public uint remaining; public IntPtr buffstart; }
[DllImport("madxlib.dll")] public static extern int madx_init(IntPtr out_buffer, IntPtr mx_house);
[DllImport("madxlib.dll")] public static extern madx_sig madx_read(IntPtr in_buffer, IntPtr out_buffer, IntPtr mx_house, ref madx_stat mxstat);
[DllImport("madxlib.dll")] public static extern void madx_deinit(IntPtr mx_house);
private byte[] mxHouse = null; private byte[] inBuffer = null; private byte[] outBuffer = null; private GCHandle handle; private IntPtr mxHousePtr = IntPtr.Zero; private GCHandle handleA; private IntPtr outBufferPtr = IntPtr.Zero; private GCHandle handleB; private IntPtr inBufferPtr = IntPtr.Zero; private Stream _source = null; private bool open = false;
public MadXStream(Stream source) { _source = source; // Trickery to get around limitation in .NET // with structs and arrays (couldn't map // madx_house struct directly). mxHouse = new byte[MADX_HOUSE_SIZE]; handle = GCHandle.Alloc(mxHouse, GCHandleType.Pinned); mxHousePtr = handle.AddrOfPinnedObject();
// Pin output buffer, assign to pointer outBuffer = new byte[MADX_OUTPUT_BUFFER_SIZE]; handleA = GCHandle.Alloc(outBuffer, GCHandleType.Pinned); outBufferPtr = handleA.AddrOfPinnedObject();
// Pin input buffer, assign to pointer inBuffer = new byte[MADX_INPUT_BUFFER_SIZE]; handleB = GCHandle.Alloc(inBuffer, GCHandleType.Pinned); inBufferPtr = handleB.AddrOfPinnedObject();
// Initialize madxlib if ((madx_init(outBufferPtr, mxHousePtr)) == 0) { throw new Exception("Error initializing madxlib"); } open = true; }
protected override void Dispose(bool disposing) { if (disposing) { if (mxHousePtr != IntPtr.Zero) { if (open) { madx_deinit(mxHousePtr); } mxHousePtr = IntPtr.Zero; } if (handle.IsAllocated) { handle.Free(); } if (handleA.IsAllocated) { handleA.Free(); } if (handleB.IsAllocated) { handleB.Free(); } open = false; } base.Dispose(disposing); }
/// <summary> /// Gets a value that indicates whether the ShoutcastStream supports reading. /// </summary> public override bool CanRead { get { return true; } }
/// <summary> /// Gets a value that indicates whether the ShoutcastStream supports seeking. /// This property will always be false. /// </summary> public override bool CanSeek { get { return false; } }
/// <summary> /// Gets a value that indicates whether the ShoutcastStream supports writing. /// This property will always be false. /// </summary> public override bool CanWrite { get { return false; } }
/// <summary> /// Flushes data from the stream. /// This method is currently not supported /// </summary> public override void Flush() { return; }
/// <summary> /// Gets the length of the data available on the Stream. /// This property is not currently supported and always thows a <see cref="NotSupportedException"/>. /// </summary> public override long Length { get { throw new NotSupportedException(); } }
/// <summary> /// Gets or sets the current position in the stream. /// This property is not currently supported and always thows a <see cref="NotSupportedException"/>. /// </summary> public override long Position { get { throw new NotSupportedException(); } set { throw new NotSupportedException(); } }
/// <summary> /// Sets the current position of the stream to the given value. /// This Method is not currently supported and always throws a <see cref="NotSupportedException"/>. /// </summary> /// <param name="offset"></param> /// <param name="origin"></param> /// <returns></returns> public override long Seek(long offset, SeekOrigin origin) { throw new NotSupportedException(); }
/// <summary> /// Sets the length of the stream. /// This Method always throws a <see cref="NotSupportedException"/>. /// </summary> /// <param name="value"></param> public override void SetLength(long value) { throw new NotSupportedException(); }
/// <summary> /// Writes data to the MadXStream. /// This method is not currently supported and always throws a <see cref="NotSupportedException"/>. /// </summary> /// <param name="buffer"></param> /// <param name="offset"></param> /// <param name="count"></param> public override void Write(byte[] buffer, int offset, int count) { throw new NotSupportedException(); }
/// <summary> /// Reads data from the MadXStream. /// </summary> /// <param name="buffer">An array of bytes to store the received data from the ShoutcastStream.</param> /// <param name="offset">The location in the buffer to begin storing the data to.</param> /// <param name="count">The number of bytes to read from the ShoutcastStream.</param> /// <returns>The number of bytes read from the ShoutcastStream.</returns> public override int Read(byte[] buffer, int offset, int count) { madx_sig mxSignal; madx_stat mxStat = new madx_stat(); if (open) { do { mxSignal = madx_read(inBufferPtr, outBufferPtr, mxHousePtr, ref mxStat); if (mxSignal == madx_sig.ERROR_OCCURED) // Error { System.Diagnostics.Debug.WriteLine("Unrecoverable error {0}", mxStat.msg); break; } else if (mxSignal == madx_sig.MORE_INPUT) // Input { // Partial read if ((int)mxStat.buffstart != 0) { int a; if ((a = _source.Read(inBuffer, (int)(MADX_INPUT_BUFFER_SIZE - mxStat.readsize), (int)mxStat.readsize)) == (int)mxStat.readsize) { System.Diagnostics.Debug.WriteLine(String.Format("Partial buffer {0}", a)); } else { if (a == -1) { mxStat.is_eof = 1; } mxStat.readsize = (uint)a; } } else // Full read { int a; if ((a = _source.Read(inBuffer, 0, MADX_INPUT_BUFFER_SIZE)) == (int)mxStat.readsize) { System.Diagnostics.Debug.WriteLine(String.Format("Full buffer {0}", a)); } else { if (a == -1) { mxStat.is_eof = 1; } mxStat.readsize = (uint)a; } } } else if (mxSignal == madx_sig.FLUSH_BUFFER) // Write { Buffer.BlockCopy(outBuffer, 0, buffer, 0, (int)mxStat.write_size); System.Diagnostics.Debug.WriteLine("Buffer written"); return (int)mxStat.write_size; } else if (mxSignal == madx_sig.EOF_REACHED) // End { Buffer.BlockCopy(outBuffer, 0, buffer, 0, (int)mxStat.write_size); System.Diagnostics.Debug.WriteLine(String.Format("Finished. {0}", (int)mxStat.write_size)); return (int)mxStat.write_size; } } while (open); } return -1; } } }
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
I just purchased the sample code and managed to convert it to C# for mp3 to wav conversion. Right now i see that it sets it to 16 bit audio. I need a 8 bit wav file. Is it possible ?
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Not with madxlib directly. I think you could do additional modification to the buffer if you incorporated something like libsndfile, though.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Hi,
I'd like to know under what licence this code may be used? With the given C# example madxlib seems to be very handy to use for our open source project "Pocket Scrobbler" (under GPL, see https://sourceforge.net/projects/pocketscrobbler).
Since madlib itself is also under GPL is it safe to assume the same for madxlib?
Thanks in advance!
Kind regards, Matthias Kahlow.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Hi I wanted to check each frame of the mp3 to read the frame header information. But in the code, mxHouse2 has no valid data ever. Does madx_read() not fill it in? Thanks
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Why is the out_buffer being passed at init and also at each read? Am I forced to use the same buffer all the time? Is there a way to simply pass a different output buffer each read and have the output written to that buffer?
This is very useful if you're doing heavy mixing and want to have a seperate thread doing the decoding of several frames ahead of time, so that your mixer callback can avoid doing the decoding.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
I got a dillema with this, in my api that i've been developing, it has a wav decoder and an mp3 decoder... of which it uses madxlib...
but the working tutorial... which if i might add, is highly accurate... when sent in chunks to openal's pcm buffer queue has a scratchy sound... i don't know why... maybe it's my openal code... but my custom wav decoder works perfectly
here is the code that may be erroneous:
case FLUSH_BUFFER: { #ifdef DEBUG printf("Writing buffer.\n"); #endif pcm.channels = internal->mxhouse.synth.pcm.channels; pcm.frequency = internal->mxhouse.synth.pcm.samplerate; pcm.bits = 16; pcm.rawpcm.write(internal->out_buffer, internal->mxstat.write_size);
position += internal->mxstat.write_size; length -= internal->mxstat.write_size; }
position being the next read position and length being what is left of the music file (i iterated through the mp3 - any pointers as to where i can find a better mp3 length calculator?)
rawpcm is my custom stream class that has a similar function to a FILE* but writes or reads from memory... might i add that it has been tested and therefore the internals of it are as error free as i could imagine...
but it leaves me clueless as to what could be the problem
sorry for the length-
thanks in advance
-- modified at 6:53 Friday 3rd August, 2007
SaGE SaneT
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Could it be a buffer underrun problem? Decoding MP3 data takes time, and maybe OpenAl reaches the end of the current buffer before you supply the next one. Just a thought.
|
| Sign In·View Thread·PermaLink | 1.00/5 |
|
|
|
 |
|
|
 |
|
|
 |
|
 |
Is there a function(get total size of mp3 file, get WAVEFORMATEX info) in madxlib SDK? (like ogg vorbis, m_dwSize = ov_pcm_total( &m_vf , -1 ) * 4; vorbis_info *vi = ov_info( &m_vf , -1 )
If there is this function, i will purchase madxlib SDK. Thank-you.
|
| Sign In·View Thread·PermaLink | 1.00/5 |
|
|
|
 |
|
 |
In this enum structure, enum madx_sig { ERROR_OCCURED, MORE_INPUT, FLUSH_BUFFER, EOF_REACHED, CALL_AGAIN };
I found you haven't given a sample on how to handle CALL_AGAIN. When I convert a mp3 file, it always came to this CALL_AGAIN.However I don't know how to use it.
Can you illustrate it? Thanks in advance..
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
The code in the article does actually illustrate how to handle CALL_AGAIN, although I suppose this isn't exactly intuitive. The CALL_AGAIN flag merely means that you need to make another call to madx_read(). Because the 'do/while' loop in the example ignores this in the 'if' statement, it goes the start and calls madx_read() again, as it should.
I suppose this could have been clearer. Usually CALL_AGAIN is thrown when a recoverable error occurs in the stream (which is generally cleared out by another read from the stream), or when more bytes need to be processed before FLUSH_BUFFER is sent.
Hope this helps.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
 |
|
 |
The only way to do it with this library is to iterate over the MP3 file and count the output bytes instead of writing to disk. I'm not sure how efficient this would be, but in the above loop, at
else if (mxsig == FLUSH_BUFFER) {
if ( fwrite(out_buffer, 1, mxstat.write_size, out_file) == mxstat.write_size ) printf("Writing buffer.\n"); else printf("fwrite() error\n");
}
You could simply add "mxstat.write_size" to a counter variable instead of performing the write to disk. Keep in mind that you are still doing the decompression, so there is that expense to think of.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
I want to decode the mp3 file to memory and play it with direct sound, but how can I get the information of the PCM data? In other words, how can I get the data which the WAVEFORMATEX struct needs? Thanks a lot
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
You can get the sample rate from the MP3 file. "BitsPerSample" will always be 16, and "Channels" will always be 2. You should then be able to calculate the rest of the header with these values.
You might also be interested in the madxlib SDK (available on my website for $10), which contains a VB.NET example that uses madxlib to play an MP3 to your speakers with DirectSound.
At http://www.arbingersys.com/madxlib.html#sdk.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
gilad-ap wrote: You can get the sample rate from the MP3 file.
But How can I get the sample rate from the MP3 file? Thanks a lot.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Cherish_He wrote: But How can I get the sample rate from the MP3 file? Thanks a lot.
You will have to parse the MP3 headers from the MP3 file. The madxlib SDK I mention above has examples of this.
Alternatively, I have a class library "Aumplib" (written in C#) that has a class in it called "MP3Check". This verifies a file is an MP3, and also returns details about the file (i.e. frequency, or sample rate).
http://www.arbingersys.com/concerns.html#aumplib
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
the program crashes in debug mode at line 338 in madxlib.cpp file provided in the source.
if( mxstat->is_eof ) { guard_ptr = in_buffer + mxstat->readsize; memset( guard_ptr, 0, MAD_BUFFER_GUARD ); mxstat->readsize += MAD_BUFFER_GUARD; }
the program crashes at this line memset( guard_ptr, 0, MAD_BUFFER_GUARD );
and the line before it, seems veryyyyy ambiguous guard_ptr = in_buffer + mxstat->readsize;
a size_t variable "mxstat->readsize" is added to the staring address of a character buffer and assigned to another character pointer    
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
First, you should make sure you have the latest version of the code. It can be gotten from my website.
Hannan Azam wrote: the program crashes at this line memset( guard_ptr, 0, MAD_BUFFER_GUARD );
What type of crash is occuring (e.g. memory access violation?). What is the value of "mxstat->readsize" when the error occurs? It should never exceed the size of "in_buffer", which is MADX_INPUT_BUFFER_SIZE + MAD_BUFFER_GUARD in size. If it were to do this, then a memory violation would have probably occurred.
Hannan Azam wrote: and the line before it, seems veryyyyy ambiguous guard_ptr = in_buffer + mxstat->readsize;
a size_t variable "mxstat->readsize" is added to the staring address of a character buffer and assigned to another character pointer
In this line we are only dealing with memory locations, since none of the pointers are dereferenced. We are merely setting the address of "guard_ptr" to the end of in_buffer using simple pointer arithmetic. The following memset() makes sure that the data from here on is set to 0. The buffer guard is a necessity for the underlying library, libmad.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
I also meet this exception in madx_read function line: 338 in madxlib.cpp, it is said "access violation", and the value of mxstat->readsize is 3435973836!!! It seems maxstat->readsize has not been initialized! Could you tell me how to init the maxstat? I'm sure I've downloaded the latest version of your source in your website(The date of madxlib.cpp is 2005/03/03 19:52:33, and the revision is 1.7). What happened on earth? Thanks a lot.
-- modified at 3:58 Friday 24th March, 2006
|
| Sign In·View Thread·PermaLink | 5.00/5 |
|
|
|
 |
|
|
 |
|
|