/*
  This program is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation; either version 2 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program; if not, write to the Free Software
  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/

/*
  This program reads a wavefile and prints out some sourcecode containing an
  initialized array of nSamples sampledata normalized to [-1.0 .. 1.0] floats.
  Use this program to embed sampledata in your program (C, Pascal)

  Needs libsndfile (http://www.mega-nerd.com/libsndfile)

  Source written with a little help of the libsndfile examples
  Rudolf Lindner, rlma-labs.de, 2005
*/

// compilation: gcc -I libsndfile wave2src.c libsndfile/libsndfile.lib -o wave2src.exe


#include <stdio.h>
#include <ctype.h>
#include <string.h>



// put libsoundfile's sndfile.h in your include path
#include <sndfile.h>

// input buffer length
#define  BUFFER_LEN    1024

// restrict to 9 channels (1 digit)
#define  MAX_CHANNELS    9

enum eLang {
    PAS  = 0,  // Pascal/Delphi
    C          // C, C++
};

void usage () {
    printf ( "\
    usage: wave2src [-llang] [-cchannel] wavefile [outfile]\n\
           wave2src -i wavefile\n\
\n\
    Read wavefiles and generate sourceode. samples are put into an initialized\n\
    float array normalized to [-1.0 .. 1.0].\n\
    Use this program to embed audio data in your sourcecode.\n\
    Rudolf Lindner, 2005, rlma-labs.de, uses libsndfile\n\
\n\
    Flags:\n\
    -l: lang:\n\
        p: Pascal\n\
        c: C (default)\n\
    -c: channel\n\
        (digit): channel number: extract this channel only\n\
        m:       mono: Mix all channels into one mono channel\n\
    -i: print info on wavefile and exit. Useful to retreive number of channels\n\
\n\
    Examples:\n\
    - print info on ding.wav:\n\
      wave2src -i ding.wav\n\
    - mix all channels of AcidTrax.wav into mono channel and create .c file:\n\
      wave2src -lc -cm AcidTrax.wav AcidTrax.c\n\
    - only use channel 2 (stereo right) of previous example:\n\
      wave2src -lc -c2 AcidTrax.wav AcidTrax.c\n\
    \n");
}


int  main ( int argc, char *argv[] ) {

    // read buffer
    static float data [BUFFER_LEN];

    SNDFILE      *infile;
    FILE         *fileout;

    SF_INFO       sfinfo;

    int           readcount;
    const char   *inFile = "input.wav";
    const char   *outFile = "output.txt";


    // commandline options
    enum eLang opt_lang = C;
    int  flagPrintInfo  = 0;
    int  flagMono       = 1;
    int  argChannel     = 1;
    int  haveOutFile    = 0;


    // scan commandline arguments
    int a;
    for ( a = 1; a < argc; a++ ) {


		if  ( *argv[a] != '-' ) break; // first non flag argument

		if      ( strncmp ( argv[a], "-i", strlen ("-i") ) == 0 )  {
			flagPrintInfo = 1;
			a++;
			break;
		}
		else if ( strncmp ( argv[a], "-l", strlen ("-l") ) == 0 )  {

			if      ( strncmp ( argv[a]+2, "p", 1 ) == 0 )  {
				opt_lang = PAS;
			}
			else if ( strncmp ( argv[a]+2, "c", 1 ) == 0 )  {
				opt_lang = C;
			}
			else {
				printf ( "unsupported Language: %s\n", argv[a]);
				usage  ();
				return 1;
			}
		}

		else if ( strncmp ( argv[a], "-c", strlen ("-c") ) == 0 )  {

			if  ( strncmp ( argv[a]+2, "m", 1 ) == 0 )  {
				flagMono = 1;
			}
			else {
				flagMono = 0;
				char cDigit = *(argv[a]+2);
				if  ( isdigit(cDigit) ) {
					argChannel = cDigit - '0';
				}
				else {
					printf ( "wrong channel: %s\n", argv[a]);
					usage  ();
					return 1;
				}
				if ( argChannel > MAX_CHANNELS ) {
					printf ( "only %d channels supported\n", MAX_CHANNELS );
					return 1;
				}
			}
		}
	}
	// wavefile argument
	if  ( a < argc ) {
		inFile = argv[a];
	}
	else {
		printf ( "missing inputfile\n" );
		usage  ();
		return 1;
	}
	// output file argument (optional)
	a++;
	if  ( a < argc ) {
		outFile = argv[a];
		haveOutFile = 1;
	}





    /* Here's where we open the input file. We pass sf_open the file name and
    ** a pointer to an SF_INFO struct.
    ** On successful open, sf_open returns a SNDFILE* pointer which is used
    ** for all subsequent operations on that file.
    ** If an error occurs during sf_open, the function returns a NULL pointer.
    **
    ** If you are trying to open a raw headerless file you will need to set the
    ** format and channels fields of sfinfo before calling sf_open(). For
    ** instance to open a raw 16 bit stereo PCM file you would need the following
    ** two lines:
    **
    **        sfinfo.format   = SF_FORMAT_RAW | SF_FORMAT_PCM_16;
    **        sfinfo.channels = 2;
    */
    if  ( ! (infile = sf_open (inFile, SFM_READ, &sfinfo)) ) {
        printf ("Not able to open input file %s.\n", inFile);
        /* Print the error message from libsndfile. */
        puts (sf_strerror (NULL));
        return  1;
    }

    if  ( flagPrintInfo ) {

		printf ( "Sample Rate : %d\n", sfinfo.samplerate );
		if  ( sfinfo.frames > 0x7FFFFFFF)
			printf ("Frames      : unknown\n");
		else
			printf ("Frames      : %ld\n", (long) sfinfo.frames);

		printf ( "Channels    : %d\n",     sfinfo.channels);
		printf ( "Format      : 0x%08X\n", sfinfo.format);
		printf ( "Sections    : %d\n",     sfinfo.sections);
		printf ( "Seekable    : %s\n",     (sfinfo.seekable ? "TRUE" : "FALSE"));

		sf_close ( infile );
		return 0;
	}

    if  ( sfinfo.channels > MAX_CHANNELS) {
        printf ("Not able to process more than %d channels\n", MAX_CHANNELS);
		sf_close ( infile );
        return  1;
    }

	if ( argChannel > sfinfo.channels ) {
		printf ( "wavefile only has %d channels\n", sfinfo.channels );
		sf_close ( infile );
		return 1;
	}

    if ( haveOutFile ) {
		fileout = fopen ( outFile, "w" );
		if (fileout == NULL ) {
			printf ("Not able to open output file %s.\n", outFile);
			sf_close ( infile );
			return  1;
		}
	}
	else {
		fileout = stdout;
	}



	// print file header


	char *headerText = "sampledata created by wave2src, Rudolf Lindner, 2005, rlma-labs.de";
	switch ( opt_lang ) {
	case PAS:
		fprintf ( fileout, "/*%s*/\n\n", headerText);
		fprintf ( fileout, "VAR samples : ARRAY[1..%d] OF FLOAT;\n\n", sfinfo.frames );
		break;
	case C:
		fprintf ( fileout, "//%s\n\n", headerText);
		fprintf ( fileout, "float samples[%d] = {\n", sfinfo.frames );
		break;
	}



    // print samples

    int bytenum = 0;
    int i;

    while ( (readcount = sf_read_float (infile, data, BUFFER_LEN)) )  {

        for ( i = 0; i < readcount; )  {

			float sample;
			if  ( flagMono )  {
				// mixdown channels to mono
				int c;
				for ( sample = 0, c = 0; c < sfinfo.channels; c++ ) {
					sample += data[i];
					i++;
				}
				sample = sample / sfinfo.channels;  // normalize back
			}
			else {
				// get channel data
				sample = data [i+argChannel-1];
				i     += sfinfo.channels;
			}


            switch ( opt_lang ) {
			case PAS:
				fprintf ( fileout, "samples [%d] := %.8f;\n", bytenum+1, sample);
				break;
			case C:
				fprintf ( fileout, "%.8f", sample);
				if  ( bytenum+1 < sfinfo.frames ) fprintf ( fileout, ",\n");
				break;
			}
			bytenum++;
        }
    }

    // print footer

	switch ( opt_lang ) {
	case PAS:
		break;
	case C:
		fprintf ( fileout, " };\n" );
		break;
	}



    /* Close input and output files. */
    sf_close (infile);
    fclose   (fileout);

    return 0;
} /* main */



