Libmpg123: How to make MP3 Library

Share your advanced PureBasic knowledge/code with the community.
User avatar
oryaaaaa
Addict
Addict
Posts: 825
Joined: Mon Jan 12, 2004 11:40 pm
Location: Okazaki, JAPAN

Libmpg123: How to make MP3 Library

Post by oryaaaaa »

Hello

mpg123 - Fast console MPEG Audio Player and decoder library
http://www.mpg123.de/


Reason
  • ACM decorder is poor sound quality. mpg123 supported Float32.
    ACM decorder mix little noise for realtime decode.
    ACM decoder need MP3 Codec
    ACM decoder is very slow. mpg123 is very fast.
    License is gray. mpg123 is LGPL.
Distributed DLL don't run on PureBasic
Libmpg123 is MP3 libraly by LGPL. But DLL don't run on PB. Because no use _stdcall
This tips is making DLL and sample.pb.

I live in Japan, and I can't distribute libmpg123.dll by Japanese law. :wink:

GNU GCC use... this need
Installed MinGW32 and MinSYS

How to install MinGW32
http://sourceforge.net/project/showfile ... up_id=2435
MinGW-5.1.4.exe
->Download and install
->I Agree
->Current
->Full installation
->C:\MinGW
Downloading and install

How to install MinSYS
http://sourceforge.net/project/showfile ... up_id=2435
MSYS-1.0.11.exe
This will install "Minimal SYStem". Do you wish to continue? -> Yes
->C:\MSYS\1.0
->MinGW
->Install

This is a post install process that will try to normalize between
your MinGW install if any as well as your previous MSYS installs
if any. I don't have any traps as aborts will not hurt anything.
Do you wish to continue with the post install? [yn ] y

Do you have MinGW installed? [yn ] y

Please answer the following in the form of c:/foo/bar.
Where is your MinGW installation? C:/MinGW

How to setup MinGW
Run MinSys
$ echo $PATH
->Check OK?
$ cd /etc/
$ cp fstab.sample fstab
->Edit
#C:/ActiveState/perl /perl
->Exit minsys, Run minsys (reboot)
$ ls -l /mingw
->Check OK?

Download mpg123-1.9.1.tar.bz2
http://sourceforge.net/projects/mpg123/files/

use 7zip (windows)
Extract to
C:\msys\1.0\home\<user>\
Created directory "mpg123-1.9.1"
NEXT

You must change source file. Now, DLL's target is MinGW only.
Change or Add "_stdcall"

/configure
Change configure Lines::12997
ccalign="yes" to ccalign="no"

Code: Select all

ccalign="unknown"
if test x"$ccalign" = xunknown; then
	{ $as_echo "$as_me:${as_lineno-$LINENO}: checking __attribute__((aligned(16)))" >&5
$as_echo_n "checking __attribute__((aligned(16)))... " >&6; }
	ccalign="no"
	echo '__attribute__((aligned(16))) float var;' > conftest.c
	if $CC -c -o conftest.o conftest.c >/dev/null 2>&1; then
		ccalign="no"
		{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
	else
		{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
	fi
	rm -f conftest.o conftest.c
fi
Change directory
/src/libmpg123/

libmpg123.c :: All change
attribute_align_arg -> __stdcall

Code: Select all

int __stdcall mpg123_init(void)
{
	ALIGNCHECKK
	if((sizeof(short) != 2) || (sizeof(long) < 4)) return MPG123_BAD_TYPES;

	if(initialized) return MPG123_OK; /* no need to initialize twice */

#ifndef NO_LAYER12
	init_layer12(); /* inits also shared tables with layer1 */
#endif
#ifndef NO_LAYER3
	init_layer3();
#endif
	prepare_decode_tables();
	check_decoders();
	initialized = 1;
	return MPG123_OK;
}
mpg123.h.in:: All insert _stdcall

Code: Select all

/** Function to initialise the mpg123 library. 
 *	This function is not thread-safe. Call it exactly once per process, before any other (possibly threaded) work with the library.
 *
 *	\return MPG123_OK if successful, otherwise an error number.
 */
EXPORT int  __stdcall  mpg123_init(void);

/** Function to close down the mpg123 library. 
 *	This function is not thread-safe. Call it exactly once per process, before any other (possibly threaded) work with the library. */
EXPORT void  __stdcall mpg123_exit(void);
reader.h:: insert _stdcall

Code: Select all

struct reader_data
{
	off_t filelen; /* total file length or total buffer size */
	off_t filepos; /* position in file or position in buffer chain */
	int   filept;
	int   flags;
#ifndef WIN32
	long timeout_sec;
#endif
	ssize_t (*fdread) (mpg123_handle *, void *, size_t);
	/* User can replace the read and lseek functions. The r_* are the stored replacement functions or NULL,
	   The second two pointers are the actual workers (default map to POSIX read/lseek). */
	ssize_t  __stdcall (*r_read) (int fd, void *buf, size_t count);
	off_t    __stdcall (*r_lseek)(int fd, off_t offset, int whence);
	ssize_t  __stdcall (*read) (int fd, void *buf, size_t count);
	off_t    __stdcall (*lseek)(int fd, off_t offset, int whence);
	/* Buffered readers want that abstracted, set internally. */
	ssize_t (*fullread)(mpg123_handle *, unsigned char *, ssize_t);
	struct bufferchain buffer; /* Not dynamically allocated, these few struct bytes aren't worth the trouble. */
};
reader.c::Insert _stdcall

Code: Select all

static int default_init(mpg123_handle *fr);
static off_t get_fileinfo(mpg123_handle *);
static ssize_t  __stdcall posix_read(int fd, void *buf, size_t count){ return read(fd, buf, count); }
static off_t   __stdcall  posix_lseek(int fd, off_t offset, int whence){ return lseek(fd, offset, whence); }
parse.c::insert _stdcall

Code: Select all

double  __stdcall  mpg123_tpf(mpg123_handle *fr)
{
	static int bs[4] = { 0,384,1152,1152 };
	double tpf;
	if(fr == NULL) return -1;

	tpf = (double) bs[fr->lay];
	tpf /= freqs[fr->sampling_frequency] << (fr->lsf);
	return tpf;
}

int  __stdcall  mpg123_position(mpg123_handle *fr, off_t no, off_t buffsize,
	off_t  *current_frame,   off_t  *frames_left,
	double *current_seconds, double *seconds_left)
attribute_align_arg -> __stdcall
frame.c, format.c, optimize.c, libmpg123.c, stringbuf.c
$ makedll.sh

ReCompile
$ make clean
$ makedll.sh
If Error display, check code. example stringbuf.c ..... err, check stringbuf.c
Warning only is OK! DLL made Root Directory!!

"libmpg123-0.dll" 278KB

PureBasic Sample
This is mp3 to wav. If error, Registory EBP changed. Check debug assembly.

Code: Select all

; Author oryaaaaa / 2009.10.16
; 
; This example original is here
; http://www.mpg123.de/api/mpg123__to__wav_8c-source.shtml

#MPG123_ENC_16     = $040
#MPG123_ENC_SIGNED = $080
#MPG123_ENC_SIGNED_16   = #MPG123_ENC_16|#MPG123_ENC_SIGNED|$10
#MPG123_OK=0
#MPG123_ERR=-1
#MPG123_DONE=-12
#MPG123_NEW_FORMAT=-11
#MPG123_NEED_MORE=-10

Global mpg123_close.l, mpg123_delete.l, mpg123_exit.l, mpg123_init.l
Global mpg123_new.l, mpg123_init.l, mpg123_new.l, mpg123_open.l
Global mpg123_fmt_none.l, mpg123_getformat.l, mpg123_plain_strerror.l
Global mpg123_strerror.l, mpg123_format.l, mpg123_read, mpg123_outblock.l

Procedure libmpg123_Init()
  Shared DLL.l
  DLL = LoadLibraryM(?L1)
  If DLL
    mpg123_close = GetProcAddressM(DLL, "mpg123_close")
    mpg123_delete = GetProcAddressM(DLL, "mpg123_delete")
    mpg123_exit = GetProcAddressM(DLL, "mpg123_exit")
    ;
    mpg123_init = GetProcAddressM(DLL, "mpg123_init")
    ;
    mpg123_new = GetProcAddressM(DLL, "mpg123_new")
    mpg123_open = GetProcAddressM(DLL, "mpg123_open")
    mpg123_fmt_none = GetProcAddressM(DLL, "mpg123_fmt_none")
    mpg123_getformat = GetProcAddressM(DLL, "mpg123_getformat")
    mpg123_outblock = GetProcAddressM(DLL, "mpg123_outblock")
    ;
    mpg123_plain_strerror = GetProcAddressM(DLL, "mpg123_plain_strerror")
    mpg123_strerror = GetProcAddressM(DLL, "mpg123_strerror")
    ;
    mpg123_format = GetProcAddressM(DLL, "mpg123_format")
    ;
    mpg123_read = GetProcAddressM(DLL, "mpg123_read")
  EndIf
  DataSection
    L1: IncludeBinary "libmpg123-0.dll"
  EndDataSection
EndProcedure

Procedure libmpg123_End()
  Shared DLL.l
  FreeLibraryM(DLL)
EndProcedure

Procedure.l mpg123_close(mh.l)
  ProcedureReturn CallFunctionFast(mpg123_close, mh)
EndProcedure

Procedure.l mpg123_delete(mh.l)
  ProcedureReturn CallFunctionFast(mpg123_delete, mh)
EndProcedure

Procedure.l mpg123_exit()
  ProcedureReturn CallFunctionFast(mpg123_exit)
EndProcedure

Procedure.l mpg123_init()
  ProcedureReturn CallFunctionFast(mpg123_init)
EndProcedure

Procedure.l mpg123_new(decoder.l, mh.l)
  ProcedureReturn CallFunctionFast(mpg123_new, decoder, mh)
EndProcedure

Procedure.l mpg123_open(mh.l, Path.l)
  ProcedureReturn CallFunctionFast(mpg123_open, mh, Path)
EndProcedure

Procedure.l mpg123_fmt_none(mh.l)
  ProcedureReturn CallFunctionFast(mpg123_fmt_none, mh)
EndProcedure

Procedure.l mpg123_getformat(mh.l, rate.l, channels.l, encoding.l)
  ProcedureReturn CallFunctionFast(mpg123_getformat, mh, rate, channels, encoding)
EndProcedure

Procedure.s mpg123_plain_strerror(errcode.l)
  Protected Result.s
  Select errcode
    Case 0
      ProcedureReturn "No error... (code 0)"
    Case 1
      ProcedureReturn "Unable to set up output format! (code 1)"
    Case 2
      ProcedureReturn "Invalid channel number specified. (code 2)"
    Case 3
      ProcedureReturn "Invalid sample rate specified. (code 3)"
    Case 4
      ProcedureReturn "Unable to allocate memory for 16 to 8 converter table! (code 4)"
    Case 5
      ProcedureReturn "Bad parameter id! (code 5)"
    Case 6
      ProcedureReturn "Bad buffer given -- invalid pointer or too small size. (code 6)"
    Case 7
      ProcedureReturn "Out of memory -- some malloc() failed. (code 7)"
    Case 8
      ProcedureReturn "You didn't initialize the library! (code 8)"
    Case 9
      ProcedureReturn "Invalid decoder choice. (code 9)"
    Case 10
      ProcedureReturn "Invalid mpg123 handle. (code 10)"
    Case 11
      ProcedureReturn "Unable to initialize frame buffers (out of memory?)! (code 11)"
    Case 12
      ProcedureReturn "Invalid RVA mode. (code 12)"
    Case 13
      ProcedureReturn "This build doesn't support gapless decoding. (code 13)"
    Case 14
      ProcedureReturn "Not enough buffer space. (code 14)"
    Case 15
      ProcedureReturn "Incompatible numeric data types. (code 15)"
    Case 16
      ProcedureReturn "Bad equalizer band. (code 16)"
    Case 17
      ProcedureReturn "Null pointer given where valid storage address needed. (code 17)"
    Case 18
      ProcedureReturn "Error reading the stream. (code 18)"
    Case 19
      ProcedureReturn "Cannot seek from end (end is not known). (code 19)"
    Case 20
      ProcedureReturn "Invalid 'whence' for seek function. (code 20)"
    Case 21
      ProcedureReturn "Build does not support stream timeouts. (code 21)"
    Case 22
      ProcedureReturn "File access error. (code 22)"
    Case 23
      ProcedureReturn "Seek not supported by stream. (code 23)"
    Case 24
      ProcedureReturn "No stream opened. (code 24)"
    Case 25
      ProcedureReturn "Bad parameter handle. (code 25)"
    Case 26
      ProcedureReturn "Invalid parameter addresses for index retrieval. (code 26)"
    Case 27
      ProcedureReturn "Lost track in the bytestream and did not attempt resync. (code 27)"
    Case 28
      ProcedureReturn "Failed to find valid MPEG data within limit on resync. (code 28)"
    Case #MPG123_ERR
      ProcedureReturn "A generic mpg123 error."
    Case #MPG123_DONE
      ProcedureReturn "Message: I am done with this track."
    Case #MPG123_NEED_MORE
      ProcedureReturn "Message: Feed me more input data!"
    Case #MPG123_NEW_FORMAT
      ProcedureReturn "Message: Prepare for a changed audio format!"
    Default
      ProcedureReturn "I have no idea - an unknown error code!"
  EndSelect
EndProcedure

Procedure.s mpg123_strerror(mh.l)
  ProcedureReturn Str(CallFunctionFast(mpg123_strerror, mh))
EndProcedure

Procedure.l mpg123_format(mh.l, rate.l, channels.l, encoding.l)
  ProcedureReturn CallFunctionFast(mpg123_format, mh, rate, channels, encoding)
EndProcedure

Procedure.l mpg123_read(mh, outmemory.l, outmemsize.l, done.l)
  ProcedureReturn CallFunctionFast(mpg123_read, mh, outmemory, outmemsize, done)
EndProcedure

ProcedureDLL.l mpg123_outblock(mh.l)
  ProcedureReturn CallFunctionFast(mpg123_outblock, mh)
EndProcedure

; Program Parameter
Global FileInput.s, FileOutput.s
FileInput = ProgramParameter(0)
FileOutput = ProgramParameter(1)

Procedure usage()
  PrintN("Usage:mpg123_to_wav <input> <output>")
EndProcedure

Procedure mpg123_Cleanup(mpg123_handle.l)
  mpg123_close(mpg123_handle)
  mpg123_delete(mpg123_handle)
  mpg123_exit()
EndProcedure

Procedure.l Main()
  CallDebugger
  Protected sndfile.l, mpg123_handle.l, *Buffer, sample.l
  Protected buffer_size.l, done.l, channels.l, encoding.l, rate.l, err.l = #MPG123_OK
  
  If CountProgramParameters()<>2
    usage()
    PrintN("Input file:"+FileInput)
    PrintN("Output file:"+FileOutput)
  EndIf
  
  err = mpg123_init()
  Debug err
  If  err = #MPG123_OK
    mpg123_handle = mpg123_new(#Null, err)
    If mpg123_handle
      If mpg123_open(mpg123_handle, @FileInput)=#MPG123_OK
        If mpg123_getformat(mpg123_handle, @rate, @channels, @encoding)=#MPG123_OK
        Else
          PrintN("Trouble with mpg123::"+mpg123_plain_strerror(err)+" "+mpg123_strerror(mpg123_handle) )
          ProcedureReturn  
        EndIf
      Else
        PrintN("Trouble with mpg123::"+mpg123_plain_strerror(err)+" "+mpg123_strerror(mpg123_handle) )
        ProcedureReturn  
      EndIf
    Else
      PrintN("Trouble with mpg123:: Error mpg123_new")
      ProcedureReturn  
    EndIf
    
    If encoding<>#MPG123_ENC_SIGNED_16
      mpg123_Cleanup(mpg123_handle)
      PrintN("Bad encording:: "+Str(encoding))
      ProcedureReturn
    EndIf
    
    mpg123_fmt_none(mpg123_handle)
    mpg123_format(mpg123_handle, rate, channels, encoding)
    
    buffer_size = mpg123_outblock(mpg123_handle)
    *Buffer = AllocateMemory( buffer_size )
    
    Print("Creating 16 bit WAV with "+Str(channels)+" channels and "+Str(rate)+"Hz")
    
    sndfile = CreateFile(#PB_Any, FileOutput)
    If sndfile=#Null
      PrintN("Cannot open output file")
      mpg123_Cleanup(mpg123_handle)
      ProcedureReturn
    EndIf
    
    Repeat
      err = mpg123_read(mpg123_handle, *Buffer, buffer_size, @done)
      WriteData(sndfile, *Buffer, done)
    Until err <> #MPG123_OK
    
    If err<>#MPG123_DONE
      PrintN("Warning: Decording ended prematuely because: "+mpg123_strerror(mpg123_handle)+" "+mpg123_plain_strerror(err))
      mpg123_Cleanup(mpg123_handle)
    EndIf
    
    CloseFile(sndfile)
    FreeMemory(*Buffer)
    sample / channels
    PrintN( Str(sample) +" samples written")
    mpg123_Cleanup(mpg123_handle) 
  EndIf
  CallDebugger
EndProcedure
  
libmpg123_Init()
  
FileInput = OpenFileRequester("Audio MP3 File", "*.mp3", "MP3 file *.mp3 | *.mp3", 0)
FileOutput = ReplaceString(FileInput, ".mp3", "_raw.wav")
OpenConsole()
ConsoleTitle("PureBasic - mpglib123.dll example")
Main()
Inkey()
libmpg123_End()
End
How to check converted wav file
This Wav is Raw. You can use audacity.
Raw Import -> Signed 16bit PCM / none / streo / 100 / 44100
Play!!

Reading Thanks 8)

A free gift
Sample code binary (PB4.4 Beta5)
http://purebasic.coolverse.jp/_userdata/dec.zip


Tips Gapless decode
mpg123_param don't run on PB. Then you change source code.
1.ReSync Size 1024 to xxxxx
frame.c

Code: Select all

mp->resync_limit = 1024;
2.Gapless Enable
some source

Code: Select all

#ifdef GAPLESS
	fr->p.flags = MPG123_GAPLESS | fr->p.flags; //Add lines
	if(fr->p.flags & MPG123_GAPLESS)
	{
3.Stream End Disable :: libmpg123.c (how????)

Code: Select all

if(fr->lastoff && fr->num == fr->lastframe)
	{
		off_t byteoff = samples_to_bytes(fr, fr->lastoff);
		if((off_t)fr->buffer.fill > byteoff)
		{
			fr->buffer.fill = byteoff;
		}
		//<Dis> fr->lastoff = 0; /* Only enter here once... when you seek, lastoff should be reset. */
	}
Tips Stream Decode only
1.Prepare

Code: Select all

Procedure.b mpg123_Initialize()
  libmpg123_Init()
  If  mpg123_init() = #MPG123_OK
    mpg123_handle = mpg123_new(#Null, err)
    If mpg123_handle
      mpg123_open_feed(mpg123_handle)
      mpg123_format_none(mpg123_handle)
      mpg123_format(mpg123_handle, 44100, 2, #MPG123_ENC_SIGNED_16)
      NR_mpg123_BufferSize = mpg123_outblock(mpg123_handle)
      *NR_mpg123_Buffer = AllocateMemory( NR_mpg123_BufferSize )
      ProcedureReturn #True
      ;EndIf
    EndIf
  EndIf
  ProcedureReturn #False
EndProcedure

Procedure mpg123_Ending()
  FreeMemory(*NR_mpg123_Buffer)
  mpg123_close(mpg123_handle)
  mpg123_delete(mpg123_handle)
  mpg123_exit()
  libmpg123_End()
EndProcedure
2.Decode

Code: Select all

err = mpg123_decode(mpg123_handle, *Buf, Len, *mpg123_Buffer, mpg123_BufferSize, @done)
  
If err=#MPG123_NEW_FORMAT
  mpg123_getformat(mpg123_handle, @rate, @channels, @encoding)
EndIf
  
If err=#MPG123_OK
  SaveBuffer(*mpg123_Buffer, done)
EndIf
  
If err<>#MPG123_DONE
  Repeat
    err = mpg123_decode(mpg123_handle, #Null, 0, *mpg123_Buffer, NR_mpg123_BufferSize, @done)
    If done=0
      err=#MPG123_DONE
      Break
    EndIf
    SaveBuffer(*mpg123_Buffer, done)
  Until err<>#MPG123_OK And err<>#MPG123_NEED_MORE
EndIf
  
If err<>#MPG123_DONE And err<>#MPG123_OK
  Debug err
EndIf  
Last edited by oryaaaaa on Mon Oct 19, 2009 11:13 am, edited 3 times in total.
inc.
Enthusiast
Enthusiast
Posts: 406
Joined: Thu May 06, 2004 4:28 pm
Location: Cologne/GER

Re: Libmpg123: How to make MP3 Library

Post by inc. »

But DLL don't run on PB. Because no use _stdcall
Purebasic offers full support of the CDECL calling convention
CallfunctionC
PrototypeC
ImportC
Check out OOP support for PB here!
User avatar
oryaaaaa
Addict
Addict
Posts: 825
Joined: Mon Jan 12, 2004 11:40 pm
Location: Okazaki, JAPAN

Re: Libmpg123: How to make MP3 Library

Post by oryaaaaa »

CallfunctionC
In Reader Process, Invalid memory Access. Libmpg123 DLL is for MinGW.
Example
mpg123_read() call mpg123_decode() and call internal PosixRead(), ERROR
Post Reply