170 lines
4.8 KiB
C
170 lines
4.8 KiB
C
|
/*
|
||
|
mpg123_to_wav.c
|
||
|
|
||
|
This is example code only sensible to be considered in the public domain.
|
||
|
Initially written by Nicholas Humfrey.
|
||
|
|
||
|
The most complicated part is about the choices to make about output format,
|
||
|
and prepare for the unlikely case a bastard mp3 might file change it.
|
||
|
*/
|
||
|
|
||
|
#include <out123.h>
|
||
|
#include <mpg123.h>
|
||
|
#include <stdio.h>
|
||
|
#include <strings.h>
|
||
|
|
||
|
/** Print usage info. */
|
||
|
void usage(const char *cmd)
|
||
|
{
|
||
|
printf("Usage: %s <input> [<driver> [<output> [encoding [buffersize]]]]\n"
|
||
|
, cmd);
|
||
|
printf( "\nPlay MPEG audio from intput file to output file/device using\n"
|
||
|
"specified out123 driver, sample encoding and buffer size optional.\n\n" );
|
||
|
exit(99);
|
||
|
}
|
||
|
|
||
|
/** Free handles. */
|
||
|
void cleanup(mpg123_handle *mh, out123_handle *ao)
|
||
|
{
|
||
|
out123_del(ao);
|
||
|
/* It's really to late for error checks here;-) */
|
||
|
mpg123_close(mh);
|
||
|
mpg123_delete(mh);
|
||
|
}
|
||
|
|
||
|
/** The whole operation. */
|
||
|
int main(int argc, char *argv[])
|
||
|
{
|
||
|
mpg123_handle *mh = NULL;
|
||
|
out123_handle *ao = NULL;
|
||
|
char *infile = NULL;
|
||
|
char *driver = NULL;
|
||
|
char *outfile = NULL;
|
||
|
unsigned char* buffer = NULL;
|
||
|
const char *encname;
|
||
|
size_t buffer_size = 0;
|
||
|
size_t done = 0;
|
||
|
int channels = 0;
|
||
|
int encoding = 0;
|
||
|
int framesize = 1;
|
||
|
long rate = 0;
|
||
|
int err = MPG123_OK;
|
||
|
off_t samples = 0;
|
||
|
|
||
|
if(argc<2)
|
||
|
usage(argv[0]);
|
||
|
|
||
|
infile = argv[1];
|
||
|
if(argc >= 3)
|
||
|
driver = argv[2];
|
||
|
if(argc >= 4)
|
||
|
outfile = argv[3];
|
||
|
printf("Input file: %s\n", infile);
|
||
|
printf("Output driver: %s\n", driver ? driver : "<nil> (default)");
|
||
|
printf("Output file: %s\n", outfile ? outfile : "<nil> (default)");
|
||
|
|
||
|
#if MPG123_API_VERSION < 46
|
||
|
// Newer versions of the library don't need that anymore, but it is safe
|
||
|
// to have the no-op call present for compatibility with old versions.
|
||
|
err = mpg123_init();
|
||
|
#endif
|
||
|
if(err != MPG123_OK || (mh = mpg123_new(NULL, &err)) == NULL)
|
||
|
{
|
||
|
fprintf(stderr, "Basic setup goes wrong: %s", mpg123_plain_strerror(err));
|
||
|
cleanup(mh, ao);
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
ao = out123_new();
|
||
|
if(!ao)
|
||
|
{
|
||
|
fprintf(stderr, "Cannot create output handle.\n");
|
||
|
cleanup(mh, ao);
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
if(argc >= 5)
|
||
|
{ /* Make mpg123 support the desired encoding only for all rates. */
|
||
|
const long *rates;
|
||
|
size_t rate_count;
|
||
|
size_t i;
|
||
|
int enc;
|
||
|
/* If that is zero, you'll get the error soon enough from mpg123. */
|
||
|
enc = out123_enc_byname(argv[4]);
|
||
|
mpg123_format_none(mh);
|
||
|
mpg123_rates(&rates, &rate_count);
|
||
|
for(i=0; i<rate_count; ++i)
|
||
|
mpg123_format(mh, rates[i], MPG123_MONO|MPG123_STEREO, enc);
|
||
|
}
|
||
|
|
||
|
/* Let mpg123 work with the file, that excludes MPG123_NEED_MORE messages. */
|
||
|
if( mpg123_open(mh, infile) != MPG123_OK
|
||
|
/* Peek into track and get first output format. */
|
||
|
|| mpg123_getformat(mh, &rate, &channels, &encoding) != MPG123_OK )
|
||
|
{
|
||
|
fprintf( stderr, "Trouble with mpg123: %s\n", mpg123_strerror(mh) );
|
||
|
cleanup(mh, ao);
|
||
|
return -1;
|
||
|
}
|
||
|
if(out123_open(ao, driver, outfile) != OUT123_OK)
|
||
|
{
|
||
|
fprintf(stderr, "Trouble with out123: %s\n", out123_strerror(ao));
|
||
|
cleanup(mh, ao);
|
||
|
return -1;
|
||
|
}
|
||
|
/* It makes no sense for that to give an error now. */
|
||
|
out123_driver_info(ao, &driver, &outfile);
|
||
|
printf("Effective output driver: %s\n", driver ? driver : "<nil> (default)");
|
||
|
printf("Effective output file: %s\n", outfile ? outfile : "<nil> (default)");
|
||
|
|
||
|
/* Ensure that this output format will not change
|
||
|
(it might, when we allow it). */
|
||
|
mpg123_format_none(mh);
|
||
|
mpg123_format(mh, rate, channels, encoding);
|
||
|
|
||
|
encname = out123_enc_name(encoding);
|
||
|
printf( "Playing with %i channels and %li Hz, encoding %s.\n"
|
||
|
, channels, rate, encname ? encname : "???" );
|
||
|
if( out123_start(ao, rate, channels, encoding)
|
||
|
|| out123_getformat(ao, NULL, NULL, NULL, &framesize) )
|
||
|
{
|
||
|
fprintf(stderr, "Cannot start output / get framesize: %s\n"
|
||
|
, out123_strerror(ao));
|
||
|
cleanup(mh, ao);
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
/* Buffer could be almost any size here, mpg123_outblock() is just some
|
||
|
recommendation. The size should be a multiple of the PCM frame size. */
|
||
|
buffer_size = argc >= 6 ? atol(argv[5]) : mpg123_outblock(mh);
|
||
|
buffer = malloc( buffer_size );
|
||
|
|
||
|
do
|
||
|
{
|
||
|
size_t played;
|
||
|
err = mpg123_read( mh, buffer, buffer_size, &done );
|
||
|
played = out123_play(ao, buffer, done);
|
||
|
if(played != done)
|
||
|
{
|
||
|
fprintf(stderr
|
||
|
, "Warning: written less than gotten from libmpg123: %li != %li\n"
|
||
|
, (long)played, (long)done);
|
||
|
}
|
||
|
samples += played/framesize;
|
||
|
/* We are not in feeder mode, so MPG123_OK, MPG123_ERR and
|
||
|
MPG123_NEW_FORMAT are the only possibilities.
|
||
|
We do not handle a new format, MPG123_DONE is the end... so
|
||
|
abort on anything not MPG123_OK. */
|
||
|
} while (done && err==MPG123_OK);
|
||
|
|
||
|
free(buffer);
|
||
|
|
||
|
if(err != MPG123_DONE)
|
||
|
fprintf( stderr, "Warning: Decoding ended prematurely because: %s\n",
|
||
|
err == MPG123_ERR ? mpg123_strerror(mh) : mpg123_plain_strerror(err) );
|
||
|
|
||
|
printf("%li samples written.\n", (long)samples);
|
||
|
cleanup(mh, ao);
|
||
|
return 0;
|
||
|
}
|