/*
** Example Winamp .RAW input plug-in
** Copyright (c) 1998, Justin Frankel/Nullsoft Inc.
**
** benski - Cinco de Mayo 2008
**  Updated for Winamp 5.5 const-correct In_Module definition 
**
*/

#include <windows.h>

#include "../Winamp/in2.h"

// avoid CRT. Evil. Big. Bloated. Only uncomment this code if you are using 
// 'ignore default libraries' in VC++. Keeps DLL size way down.
// /*
BOOL WINAPI _DllMainCRTStartup(HANDLE hInst, ULONG ul_reason_for_call, LPVOID lpReserved)
{
  return TRUE;
}
// */

// post this to the main window at end of file (after playback as stopped)
#define WM_WA_MPEG_EOF WM_USER+2


// raw configuration.
#define NCH 2
#define SAMPLERATE 44100
#define BPS 16


In_Module mod;      // the output module (filled in near the bottom of this file)

char lastfn[MAX_PATH];  // currently playing file (used for getting info on the current file)

int file_length;    // file length, in bytes
int decode_pos_ms;    // current decoding position, in milliseconds. 
            // Used for correcting DSP plug-in pitch changes
int paused;       // are we paused?
volatile int seek_needed; // if != -1, it is the point that the decode 
              // thread should seek to, in ms.

HANDLE input_file=INVALID_HANDLE_VALUE; // input file handle

volatile int killDecodeThread=0;      // the kill switch for the decode thread
HANDLE thread_handle=INVALID_HANDLE_VALUE;  // the handle to the decode thread


DWORD WINAPI DecodeThread(LPVOID b); // the decode thread procedure

void config(HWND hwndParent)
{
  MessageBox(hwndParent,
    "No configuration. .RAW files must be 44/16/2",
    "Configuration",MB_OK);
  // if we had a configuration box we'd want to write it here (using DialogBox, etc)
}
void about(HWND hwndParent)
{
  MessageBox(hwndParent,"Nullsoft RAW Player, by Justin Frankel",
    "About Nullsoft RAW Player",MB_OK);
}

void init() {
  /* any one-time initialization goes here (configuration reading, etc) */
}

void quit() {
  /* one-time deinit, such as memory freeing */
}

int isourfile(const char *fn) {
// used for detecting URL streams.. unused here. 
// return !strncmp(fn,"http://",7); to detect HTTP streams, etc
  return 0;
}


// called when winamp wants to play a file
int play(const char *fn)
{
  int maxlatency;
  int thread_id;

  paused=0;
  decode_pos_ms=0;
  seek_needed=-1;



  // CHANGEME! Write your own file opening code here
  input_file = CreateFile(fn,GENERIC_READ,FILE_SHARE_READ|FILE_SHARE_WRITE,NULL,
    OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
  if (input_file == INVALID_HANDLE_VALUE) // error opening file
  {
    // we return error. 1 means to keep going in the playlist, -1
    // means to stop the playlist.
    return 1;
  }

  file_length=GetFileSize(input_file,NULL);



  strcpy(lastfn,fn);

  // -1 and -1 are to specify buffer and prebuffer lengths.
  // -1 means to use the default, which all input plug-ins should
  // really do.
  maxlatency = mod.outMod->Open(SAMPLERATE,NCH,BPS, -1,-1);

  // maxlatency is the maxium latency between a outMod->Write() call and
  // when you hear those samples. In ms. Used primarily by the visualization
  // system.

  if (maxlatency < 0) // error opening device
  {
    CloseHandle(input_file);
    input_file=INVALID_HANDLE_VALUE;
    return 1;
  }
  // dividing by 1000 for the first parameter of setinfo makes it
  // display 'H'... for hundred.. i.e. 14H Kbps.
  mod.SetInfo((SAMPLERATE*BPS*NCH)/1000,SAMPLERATE/1000,NCH,1);

  // initialize visualization stuff
  mod.SAVSAInit(maxlatency,SAMPLERATE);
  mod.VSASetInfo(SAMPLERATE,NCH);

  // set the output plug-ins default volume.
  // volume is 0-255, -666 is a token for
  // current volume.
  mod.outMod->SetVolume(-666);

  // launch decode thread
  killDecodeThread=0;
  thread_handle = (HANDLE)
    CreateThread(NULL,0,(LPTHREAD_START_ROUTINE) DecodeThread,NULL,0,&thread_id);

  return 0;
}

// standard pause implementation
void pause() { paused=1; mod.outMod->Pause(1); }
void unpause() { paused=0; mod.outMod->Pause(0); }
int ispaused() { return paused; }


// stop playing.
void stop() {
  if (thread_handle != INVALID_HANDLE_VALUE)
  {
    killDecodeThread=1;
    if (WaitForSingleObject(thread_handle,10000) == WAIT_TIMEOUT)
    {
      MessageBox(mod.hMainWindow,"error asking thread to die!\n",
        "error killing decode thread",0);
      TerminateThread(thread_handle,0);
    }
    CloseHandle(thread_handle);
    thread_handle = INVALID_HANDLE_VALUE;
  }

  // close output system
  mod.outMod->Close();

  // deinitialize visualization
  mod.SAVSADeInit();


  // CHANGEME! Write your own file closing code here
  if (input_file != INVALID_HANDLE_VALUE)
  {
    CloseHandle(input_file);
    input_file=INVALID_HANDLE_VALUE;
  }

}


// returns length of playing track
int getlength() {
  return (file_length*10)/(SAMPLERATE/100*NCH*(BPS/8));
}


// returns current output position, in ms.
// you could just use return mod.outMod->GetOutputTime(),
// but the dsp plug-ins that do tempo changing tend to make
// that wrong.
int getoutputtime() {
  return decode_pos_ms+
    (mod.outMod->GetOutputTime()-mod.outMod->GetWrittenTime());
}


// called when the user releases the seek scroll bar.
// usually we use it to set seek_needed to the seek
// point (seek_needed is -1 when no seek is needed)
// and the decode thread checks seek_needed.
void setoutputtime(int time_in_ms) {
  seek_needed=time_in_ms;
}


// standard volume/pan functions
void setvolume(int volume) { mod.outMod->SetVolume(volume); }
void setpan(int pan) { mod.outMod->SetPan(pan); }

// this gets called when the use hits Alt+3 to get the file info.
// if you need more info, ask me :)

int infoDlg(const char *fn, HWND hwnd)
{
  // CHANGEME! Write your own info dialog code here
  return 0;
}


// this is an odd function. it is used to get the title and/or
// length of a track.
// if filename is either NULL or of length 0, it means you should
// return the info of lastfn. Otherwise, return the information
// for the file in filename.
// if title is NULL, no title is copied into it.
// if length_in_ms is NULL, no length is copied into it.
void getfileinfo(const char *filename, char *title, int *length_in_ms)
{
  if (!filename || !*filename)  // currently playing file
  {
    if (length_in_ms) *length_in_ms=getlength();
    if (title) // get non-path portion.of filename
    {
      char *p=lastfn+strlen(lastfn);
      while (*p != '\\' && p >= lastfn) p--;
      strcpy(title,++p);
    }
  }
  else // some other file
  {
    if (length_in_ms) // calculate length
    {
      HANDLE hFile;
      hFile = CreateFile(filename,GENERIC_READ,FILE_SHARE_READ|FILE_SHARE_WRITE,NULL,
        OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
      if (hFile != INVALID_HANDLE_VALUE)
      {
        *length_in_ms = (GetFileSize(hFile,NULL)*10)/(SAMPLERATE/100*NCH*(BPS/8));
        CloseHandle(hFile);
      }
      else *length_in_ms=-1000; // the default is unknown file length (-1000).
    }
    if (title) // get non path portion of filename
    {
      const char *p=filename+strlen(filename);
      while (*p != '\\' && p >= filename) p--;
      strcpy(title,++p);
    }
  }
}

void eq_set(int on, char data[10], int preamp)
{
  // most plug-ins can't even do an EQ anyhow.. I'm working on writing
  // a generic PCM EQ, but it looks like it'll be a little too CPU 
  // consuming to be useful :)
  // if you _CAN_ do EQ with your format, each data byte is 0-63 (+20db <-> -20db)
  // and preamp is the same. 
}


// render 576 samples into buf. 
// this function is only used by DecodeThread. 

// note that if you adjust the size of sample_buffer, for say, 1024
// sample blocks, it will still work, but some of the visualization 
// might not look as good as it could. Stick with 576 sample blocks
// if you can, and have an additional auxiliary (overflow) buffer if 
// necessary.. 
int get_576_samples(char *buf)
{
  int l;
  // CHANGEME! Write your own sample getting code here
  ReadFile(input_file,buf,576*NCH*(BPS/8),&l,NULL);
  return l;
}



DWORD WINAPI DecodeThread(LPVOID b)
{
  int done=0; // set to TRUE if decoding has finished
  while (!killDecodeThread)
  {
    if (seek_needed != -1) // seek is needed.
    {
      int offs;
      decode_pos_ms = seek_needed;
      seek_needed=-1;
      done=0;
      mod.outMod->Flush(decode_pos_ms); // flush output device and set 
                        // output position to the seek position
      offs = MulDiv(decode_pos_ms,SAMPLERATE,1000); // decode_pos_ms*SAMPLERATE/1000
      SetFilePointer(input_file,offs*NCH*(BPS/8),NULL,FILE_BEGIN); // seek!
    }

    if (done) // done was set to TRUE during decoding, signaling eof
    {
      mod.outMod->CanWrite();   // some output drivers need CanWrite
                      // to be called on a regular basis.

      if (!mod.outMod->IsPlaying())
      {
        // we're done playing, so tell Winamp and quit the thread.
        PostMessage(mod.hMainWindow,WM_WA_MPEG_EOF,0,0);
        return 0; // quit thread
      }
      Sleep(10);    // give a little CPU time back to the system.
    }
    else if (mod.outMod->CanWrite() >= ((576*NCH*(BPS/8))*(mod.dsp_isactive()?2:1)))
      // CanWrite() returns the number of bytes you can write, so we check that
      // to the block size. the reason we multiply the block size by two if 
      // mod.dsp_isactive() is that DSP plug-ins can change it by up to a 
      // factor of two (for tempo adjustment).
    {
      int l=576*NCH*(BPS/8);             // block length in bytes
      static char sample_buffer[576*NCH*(BPS/8)*2];
                           // sample buffer. twice as 
                           // big as the blocksize

      l=get_576_samples(sample_buffer);    // retrieve samples
      if (!l)     // no samples means we're at eof
      {
        done=1;
      }
      else  // we got samples!
      {
        // give the samples to the vis subsystems
        mod.SAAddPCMData((char *)sample_buffer,NCH,BPS,decode_pos_ms);
        mod.VSAAddPCMData((char *)sample_buffer,NCH,BPS,decode_pos_ms);
        // adjust decode position variable
        decode_pos_ms+=(576*1000)/SAMPLERATE;

        // if we have a DSP plug-in, then call it on our samples
        if (mod.dsp_isactive())
          l=mod.dsp_dosamples(
            (short *)sample_buffer,l/NCH/(BPS/8),BPS,NCH,SAMPLERATE
            ) // dsp_dosamples
            *(NCH*(BPS/8));

        // write the pcm data to the output system
        mod.outMod->Write(sample_buffer,l);
      }
    }
    else  Sleep(20);
    // if we can't write data, wait a little bit. Otherwise, continue 
    // through the loop writing more data (without sleeping)
  }
  return 0;
}


// module definition.

In_Module mod =
{
  IN_VER, // defined in IN2.H
  "Nullsoft RAW Player v0.0 "
  // winamp runs on both alpha systems and x86 ones. :)
#ifdef __alpha
  "(AXP)"
#else
  "(x86)"
#endif
  ,
  0,  // hMainWindow (filled in by winamp)
  0,  // hDllInstance (filled in by winamp)
  "RAW\0RAW Audio File (*.RAW)\0"
  // this is a double-null limited list. "EXT\0Description\0EXT\0Description\0" etc.
  ,
  1,  // is_seekable
  1,  // uses output plug-in system
  config,
  about,
  init,
  quit,
  getfileinfo,
  infoDlg,
  isourfile,
  play,
  pause,
  unpause,
  ispaused,
  stop,

  getlength,
  getoutputtime,
  setoutputtime,

  setvolume,
  setpan,

  0,0,0,0,0,0,0,0,0, // visualization calls filled in by winamp

  0,0, // dsp calls filled in by winamp

  eq_set,

  NULL,   // setinfo call filled in by winamp

  0 // out_mod filled in by winamp

};

// exported symbol. Returns output module.

__declspec( dllexport ) In_Module * winampGetInModule2()
{
  return &mod;
}
































































//