/***************************************************************************
 *   Copyright (C) 2004 by Predrag Viceic                                  *
 *   viceic@net2000.ch                                             *
 *                                                                         *
 *   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.             *
 ***************************************************************************/

 /*
    Midi file generation taken from Waon project
    Copyright (C) 1998 Kengo ICHIKI (ichiki@geocities.com)
*/

#include "midimanager.h"

MidiManager::MidiManager(QObject *parent, const char *name)
 : QObject(parent, name)
{
}


MidiManager::~MidiManager()
{
}




/*!
    \fn MidiManager::writeMidiFile(QString filename,QValueList<beatline_data> bld,
                                   long start, long end,double bpm)
 */
void MidiManager::writeMidiFile(QString filename,QValueList<beatline_data> bld,double /*bpm*/)
{
    
    filename.replace(".mid","");
    filename.append(".mid");
    //long dtime_sum=0;
    qHeapSort(bld);
    QBuffer buffer;
    buffer.open( IO_WriteOnly );
   
    //int count=0;
    long dtime=0;
    uint i;
    int lastNoteOn=-1;
    int lastPosOn=-1;
    int lastNoteOff=-1;
    int lastPosOff=bld[0].position;
    for (i=0;i<bld.count();i++){
        if(i>0){
            if(bld[i-1].midiNote!=-1){
                dtime=bld[i].position-bld[i-1].position;
                smf_note_off (&buffer,dtime, bld[i-1].midiNote,120,0); 
                lastNoteOff=bld[i-1].midiNote;
                lastPosOff=bld[i].position;
            }
            
        }
        if(bld[i].midiNote!=-1){
            dtime=bld[i].position-lastPosOff;
            smf_note_on (&buffer,dtime, bld[i].midiNote,120,0);
            lastNoteOn=bld[i].midiNote;
            lastPosOn=bld[i].position;
        }
        /*
        if(i==bld.count()-1){
            if(lastNoteOn!=-1 && lastPosOn!=-1){
                dtime=bld.last().position-lastPosOn;
                smf_note_off (&buffer,dtime, lastNoteOn,120,0);
            }
        }
        */
        //dtime_sum+=dtime;
    }
    //int track_end_size=smf_track_end(&buffer);
    buffer.close();
    
    QBuffer headerBuffer;
    headerBuffer.open( IO_WriteOnly );
    //int div=(int)((60.0/bpm)*44100.0);
    //int hd_fmt_size=smf_header_fmt(&headerBuffer,0,1,div);
    smf_track_head(&headerBuffer,buffer.buffer().count());
    headerBuffer.close();
    
    QFile file(filename);
    file.open(IO_Raw | IO_ReadWrite);
    file.writeBlock(headerBuffer.buffer());
    file.writeBlock(buffer.buffer());
    file.close();
    //std::cout<<"dtime sum: "<<dtime_sum<<"\n";

}

int MidiManager::smf_header_fmt (QBuffer*  buffer,
		unsigned short format,
		unsigned short tracks,
		unsigned short divisions)
{
  int num;

  //num = write (fd, "MThd", 4);
  num=buffer->writeBlock("MThd",4);
  num += wblong (buffer, 6); /* head data size (= 6)  */
  num += wbshort (buffer, format);
  num += wbshort (buffer, tracks);
  num += wbshort (buffer, divisions);
  return num;
  /* num = 14  */
}

int MidiManager::smf_track_head (QBuffer*  buffer, unsigned long size)
{
  int num;
  //num = write (fd, "MTrk", 4);
  num = buffer->writeBlock("MTrk",4);
  num += wblong (buffer, size);
  return num;
  /* num = 8  */
}

int MidiManager::smf_track_end (QBuffer*  buffer)
{
  char data[4];
  data[0] = 0x00; /* delta time  */
  data[1] = 0xff;
  data[2] = 0x2f;
  data[3] = 0x00;
  //write (fd, data, 4);
  return buffer->writeBlock((const char*)data,4);
}

int MidiManager::smf_prog_change (QBuffer*  buffer, char channel, char prog)
{
  char data[3];

  data[0] = 0x00; /* delta time  */
  data[1] = 0xC0 + channel;
  data[2] = prog;
  //return write (fd, data, 3);
  return buffer->writeBlock((const char*)data,3);
}

int MidiManager::smf_note_on (QBuffer*  buffer, long dtime, char note, char vel, char channel)
{
  char data[3];
  int num;

  num = write_var_len (buffer, dtime);
  data[0] = 0x90 + channel;
  data[1] = note;
  data[2] = vel;
  num += buffer->writeBlock((const char*)data,3);
  //std::cout<<"note on: "<<dtime<<" note: "<<(short)note<<endl;
  return num;
}

int MidiManager::smf_note_off (QBuffer*  buffer, long dtime, char note, char vel, char channel)
{
  char data[3];
  int num;

  num = write_var_len (buffer, dtime);
  data[0] = 0x80 + channel;
  data[1] = note;
  data[2] = vel;
  num += buffer->writeBlock((const char*)data,3);
  //std::cout<<"note off: "<<dtime<<" note: "<<(short)note<<endl;
  return num;
}



int MidiManager::write_var_len (QBuffer*  buffer, long value)
{
  char rep[4];
  int bytes;

  bytes = 1;
  rep[3] = value & 0x7f;
  value >>= 7;
  while (value >0)
    {
      rep[3 - bytes] = (value & 0x7f) | 0x80;
      bytes ++;
      value >>= 7;
    }

  //return (write (fd, &rep[4-bytes], bytes));
    return buffer->writeBlock((const char*)(&rep[4-bytes]),bytes);
}

int MidiManager::wblong (QBuffer*  buffer, unsigned long ul)
{
  char data[4];
  data[0] = (char) (ul >> 24) & 0xff;
  data[1] = (char) (ul >> 16) & 0xff;
  data[2] = (char) (ul >> 8) & 0xff;
  data[3] = (char) (ul) & 0xff;
  //return write (fd, data, 4);
  return buffer->writeBlock((const char*)data,4);
}

int MidiManager::wbshort (QBuffer*  buffer, unsigned short us)
{
  char data[2];
  data[0] = (char) (us >> 8) & 0xff;
  data[1] = (char) (us) & 0xff;
  //return write (fd, data, 2);
  return buffer->writeBlock((const char*)data,2);
}
