// minimp3 example player application for Win32
// this file is public domain -- do with it whatever you want!
#define MAIN_PROGRAM

#include "libc.h"
#include "minimp3.h"

#define BUFFER_COUNT 8

static WAVEFORMATEX wf = {
    1,  // wFormatTag
    0,  // nChannels
    0,  // nSamplesPerSec
    0,  // nAvgBytesPerSec
    4,  // nBlockAlign
    16, // wBitsPerSample
    sizeof(WAVEFORMATEX) // cbSize
};

static const WAVEHDR wh_template = {
    NULL, // lpData
    0, // dwBufferLength
    0, // dwBytesRecorded
    0, // dwUser
    0, // dwFlags
    1, // dwLoops
    NULL, // lpNext
    0 // reserved
};


static mp3_decoder_t mp3;
static mp3_info_t info;
static unsigned char *stream_pos;
static int bytes_left;
static int byte_count;
static WAVEHDR wh[BUFFER_COUNT];
static signed short sample_buffer[MP3_MAX_SAMPLES_PER_FRAME * BUFFER_COUNT];

static HANDLE local_stdout;
#define out(text) WriteFile(local_stdout, (LPCVOID) text, strlen(text), NULL, NULL)


void CALLBACK AudioCallback(
  HWAVEOUT hwo,      
  UINT uMsg,         
  DWORD_PTR dwInstance,  
  DWORD dwParam1,    
  DWORD dwParam2     
) {
    LPWAVEHDR wh = (LPWAVEHDR) dwParam1;
    if (!wh) return;
    if (byte_count) {
        stream_pos += byte_count;
        bytes_left -= byte_count;
        waveOutUnprepareHeader(hwo, wh, sizeof(WAVEHDR));
        waveOutPrepareHeader(hwo, wh, sizeof(WAVEHDR));
        waveOutWrite(hwo, wh, sizeof(WAVEHDR));
    }
    byte_count = mp3_decode(mp3, stream_pos, bytes_left, (signed short *) wh->lpData, &info);
}


void ShowTag(const char *caption, const unsigned char *src, int max_length) {
    static char tagbuf[32];
    char *tagpos = tagbuf;
    tagbuf[max_length] = '\0';
    __asm {
        cld
        mov esi, src
        mov edi, tagpos
        mov ecx, max_length
        rep movsb
    }
    if (!*tagbuf)
        return;
    out(caption);
    out(tagbuf);
}


int main(void) {
    char input_file_name[256];
    char *inptr, *outptr = input_file_name;
    HANDLE hFile, hMap;
    HWAVEOUT hwo;
    int i;
    
    // init stdout and write banner
    local_stdout = GetStdHandle(STD_OUTPUT_HANDLE);
    out("minimp3 -- a small MPEG Audio Layer III player based on ffmpeg\n\n");
    
    // read arguments, but skip the program name
    for (inptr = GetCommandLine();  (*inptr) && (*inptr != ' ');  ++inptr) {
        if (*inptr == '"')  // skip "quoted arguments"
            do { ++inptr; } while (*inptr != '"');
    }
    // skip whitespace
    while (*inptr == ' ')  ++inptr;
    // check for a parameter
    if (!*inptr) {
        // no parameter -> quit
        out("Error: no input file specified!\n");
        return 1;
    } else if (*inptr == '"') {
        // "quoted parameter"
        ++inptr;
        while (*inptr != '"')
            *outptr++ = *inptr++;
    } else {
        // unquoted parameter
        do {
            *outptr++ = *inptr++;
        } while(*inptr);
    }
    *outptr = '\0';
    
    // open and mmap() the file
    hFile = CreateFile(input_file_name, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL);
    bytes_left = GetFileSize(hFile, NULL) - 128;
    hMap = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL);
    stream_pos = (unsigned char*) MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, 0);
    
    // check if the result is valid
    if (!stream_pos) {
        out("Error: cannot open `");
        out(input_file_name);
        out("'!\n");
        return 1;
    } else {
        out("Now Playing: ");
        out(input_file_name);
    }
    
    // check for a ID3 tag
    inptr = stream_pos + bytes_left;
    if (((*(unsigned long *)inptr) & 0xFFFFFF) == 0x474154) {
        ShowTag("\nTitle: ",   inptr +  3, 30);
        ShowTag("\nArtist: ",  inptr + 33, 30);
        ShowTag("\nAlbum: ",   inptr + 63, 30);
        ShowTag("\nYear: ",    inptr + 93,  4);
        ShowTag("\nComment: ", inptr + 97, 30);
    }
    
    // set up minimp3 and decode the first frame
    mp3 = mp3_create();
    byte_count = mp3_decode(mp3, stream_pos, bytes_left, sample_buffer, &info);
    if (!byte_count) {
        out("\nError: not a valid MP2 audio file!\n");
        return 1;
    }
    
    // set up wave output
    wf.nSamplesPerSec = info.sample_rate;
    wf.nChannels = info.channels;
    if(waveOutOpen(&hwo, WAVE_MAPPER, &wf, (INT_PTR) AudioCallback, (INT_PTR) NULL, CALLBACK_FUNCTION)
       != MMSYSERR_NOERROR) {
        out("\nError: cannot open wave output!\n");
        return 1;
    }

    // allocate buffers
    out("\n\nPress Ctrl+C or close the console window to stop playback.\n");
    inptr = (char*) sample_buffer;
    for (i = 0;  i < BUFFER_COUNT;  ++i) {
        wh[i] = wh_template;
        wh[i].lpData = inptr;
        wh[i].dwBufferLength = info.audio_bytes;
        AudioCallback(hwo, 0, 0, (DWORD) &wh[i], 0);
        inptr += MP3_MAX_SAMPLES_PER_FRAME * 2;
    }

    // endless loop
    while (1) Sleep(10);

    return 0;    
}
