/* The original version of this program can be found at http://damb.dk */
#include <windows.h>
#include <iostream>
#include <iomanip>
#include <fstream>

typedef struct
{
   WAVEFORMATEX wfx;
   WORD wID;
   DWORD fdwFlags;
   WORD nBlockSize;
   WORD nFramesPerBlock;
   WORD nCodecDelay;
}MPEGLAYER3WAVEFORMAT;
#define MPEGLAYER3_WFX_EXTRA_BYTES 12
#define  WAVE_FORMAT_MPEGLAYER3 0x0055

#define SAMPLE_RATE 22050
#define MP3_BLOCK_SIZE 522
#define MPEGLAYER3_FLAG_PADDING_OFF 0x00000002

void CALLBACK CallBackProc(HWAVEOUT hwo, UINT uMsg, DWORD, DWORD dwParam1, DWORD)
{
   if(uMsg == WOM_DONE)
   {
      MMRESULT MMResult;
      WAVEHDR *WaveHeader = (WAVEHDR *)dwParam1;

      if((MMResult = waveOutUnprepareHeader(hwo, WaveHeader, sizeof(WAVEHDR))) != MMSYSERR_NOERROR)
      {
         char Text[256];
         waveOutGetErrorText(MMResult, Text, sizeof(Text));
         std::cout << "Failed to unprepare: " << Text << std::endl;
      }
      free(WaveHeader->lpData);
   }
}

bool PlayMP3(const char* aBuffer, int aSize)
{
   HWAVEOUT WaveHandle;
   WAVEHDR WaveHeader;
   MMRESULT MMResult;

   MPEGLAYER3WAVEFORMAT Mp3Format;
   Mp3Format.wfx.cbSize = MPEGLAYER3_WFX_EXTRA_BYTES;
   Mp3Format.wfx.wFormatTag = WAVE_FORMAT_MPEGLAYER3;
   Mp3Format.wfx.nChannels = 1;
   Mp3Format.wfx.nAvgBytesPerSec = 24 * 1024/8;
   Mp3Format.wfx.wBitsPerSample = 0;
   Mp3Format.wfx.nBlockAlign = 1;
   Mp3Format.wfx.nSamplesPerSec = SAMPLE_RATE;
   Mp3Format.fdwFlags = MPEGLAYER3_FLAG_PADDING_OFF;
   Mp3Format.nBlockSize = 144 * 24*1024/SAMPLE_RATE; //MP3_BLOCK_SIZE;
   Mp3Format.nFramesPerBlock = 1;
   Mp3Format.nCodecDelay = 1393;
   Mp3Format.wID = 1;

   if(waveOutOpen(&WaveHandle, WAVE_MAPPER, (WAVEFORMATEX *)&Mp3Format, (DWORD )CallBackProc, 0, CALLBACK_FUNCTION) != MMSYSERR_NOERROR)
   {
      std::cerr << "Failed to open Wave!" << std::endl;
      return false;
   }
   memset(&WaveHeader, 0, sizeof(WaveHeader));
   aBuffer += 4; // Skip header for now
   aSize -= 4;
   WaveHeader.lpData = (char *)aBuffer;
   WaveHeader.dwBufferLength = aSize;
   WaveHeader.dwBytesRecorded = aSize;
   WaveHeader.dwLoops = 1;
   WaveHeader.dwFlags = WHDR_BEGINLOOP | WHDR_ENDLOOP;

   if(waveOutPrepareHeader(WaveHandle, &WaveHeader, sizeof(WaveHeader)) != MMSYSERR_NOERROR)
   {
      std::cout << "Failed to prepare header!" << std::endl;
      return false;
   }

   if((MMResult = waveOutWrite(WaveHandle, &WaveHeader, sizeof(WaveHeader))) != MMSYSERR_NOERROR)
   {
      char Text[256];
      waveOutGetErrorText(MMResult, Text, sizeof(Text));
      std::cout << "Failed to write: " <<  Text << std::endl;
      return false;
   }
   while(!(WaveHeader.dwFlags & WHDR_DONE))
   Sleep(100);
   if((MMResult = waveOutClose(WaveHandle)) != MMSYSERR_NOERROR)
   {
      char Text[256];
      waveOutGetErrorText(MMResult, Text, sizeof(Text));
      std::cout << "Failed to close: " << Text << std::endl;
      return false;
   }
   return true;
}

int main()
{
   std::ifstream inFile("test.mp3", std::ios::binary);
   if(inFile)
   {
      // Find the size of the file:
      int size;
      inFile.seekg(0, std::ios::end);
      size = inFile.tellg();
      inFile.seekg(0, std::ios::beg);

      // read the data from the file
      char *buffer = new char[size];
      inFile.read(buffer, size);
      PlayMP3(buffer, size);
      delete [] buffer;
   }
   else
   {
      std::cout << "failed to open the file" << std::endl;
   }
}