/***************************************************************************
 *             __________               __   ___.
 *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
 *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
 *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
 *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
 *                     \/            \/     \/    \/            \/
 *
 * Copyright (C) 2005 Karl Kurbjun based on midi2wav by Stepan Moskovchenko
 *
 * All files in this archive are subject to the GNU General Public License.
 * See the file COPYING in the source tree root for full license agreement.
 *
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
 * KIND, either express or implied.
 *
 ****************************************************************************/

#include "../../plugin.h"

PLUGIN_HEADER

#define SAMPLE_RATE 11025  // 44100 22050 11025 
#define MAX_VOICES 25  // I've found that setting this to 25 doesn't hurt the midi
                        // files I was testing and significantly reduced the buffer
                        // misses.
#define BUF_SIZE 512
#define NBUF   2

int numberOfSamples;
long bpm;

#include "midi/midiutil.c"
#include "midi/guspat.h"
#include "midi/guspat.c"
#include "midi/sequencer.c"
#include "midi/midifile.c"
#include "midi/synth.c"

int currentSample=0;
short *outptr;
int quit=0;

struct MIDIfile * mf; 

short gmbuf[BUF_SIZE*NBUF];

struct plugin_api * rb;

#ifdef USE_IRAM
extern char iramcopy[];
extern char iramstart[];
extern char iramend[];
extern char iedata[];
extern char iend[];
#endif

enum plugin_status plugin_start(struct plugin_api* api, void* parameter)
{
   rb = api;

   if(parameter == NULL)
   {
      rb->splash(HZ*2, true, " Play .MID file ");
      return PLUGIN_OK;
   }
	rb->lcd_setfont(0);

#ifdef USE_IRAM
   rb->memcpy(iramstart, iramcopy, iramend-iramstart);
   rb->memset(iedata, 0, iend - iedata);
#endif

#if !defined(SIMULATOR) && defined(HAVE_ADJUSTABLE_CPU_FREQ)
   rb->cpu_boost(true);
#endif

   rb->splash(HZ, true, parameter);

   if(midimain(parameter) == -1)
   {
      return PLUGIN_ERROR;
   }

   rb->pcm_play_stop();
   rb->pcm_set_frequency(44100); // 44100

#if !defined(SIMULATOR) && defined(HAVE_ADJUSTABLE_CPU_FREQ)
   rb->cpu_boost(false);
#endif

   rb->splash(HZ, true, "FINISHED PLAYING");

   return PLUGIN_OK;
}

bool swap=0;
bool lastswap=1;

inline void synthbuf(void)
{
   register int i;
   int synthtemp[2];
   
   if(lastswap==swap) return;
   lastswap=swap;

   outptr=(swap ? gmbuf : gmbuf+BUF_SIZE);

   for(i=0; i<BUF_SIZE/2; i++)
   {
      synthSample(&synthtemp[0], &synthtemp[1]);
      currentSample++;
      *outptr=synthtemp[0]&0xFFFF;
      outptr++;
      *outptr=synthtemp[1]&0xFFFF;
      outptr++;
      if(currentSample==numberOfSamples)
      {
         if( tick(mf) == 0 ) quit=1;
         currentSample=0;
      }
   }
}

void get_more(unsigned char** start, size_t* size)
{
   if(lastswap!=swap)
   {
      printf("Buffer miss!"); // Comment out the printf to make missses less noticable.
//      synthbuf();  // For some reason midiplayer crashes when an update is forced
   }
   *start = (unsigned char*)((swap ? gmbuf : gmbuf + BUF_SIZE));
   *size = BUF_SIZE*sizeof(short);
   swap=!swap;
}

int midimain(void * filename)
{
   int button;

   rb->splash(HZ/5, true, "LOADING MIDI");

   mf= loadFile(filename);

   rb->splash(HZ/5, true, "LOADING PATCHES");
   if (initSynth(mf, "/.rockbox/patchset/patchset.cfg", "/.rockbox/patchset/drums.cfg") == -1)
      return -1;

   rb->pcm_play_stop();
   
   rb->pcm_set_frequency(SAMPLE_RATE); // 44100 22050 11025

   /*
    * tick() will do one MIDI clock tick. Then, there's a loop here that
    * will generate the right number of samples per MIDI tick. The whole
    * MIDI playback is timed in terms of this value.. there are no forced
    * delays or anything. It just produces enough samples for each tick, and
    * the playback of these samples is what makes the timings right.
    *
    * This seems to work quite well.
    */

   printf("\nOkay, starting sequencing");

   bpm=mf->div*1000000/tempo;
   numberOfSamples=SAMPLE_RATE/bpm;

   tick(mf);

   synthbuf();
   rb->pcm_play_data(&get_more, NULL, 0);

   button=rb->button_status();

   while(!quit){
      synthbuf();
      rb->yield();
      if(rb->button_status()!=button) quit=1;
   }

   return 0;
}
