
/*
**  jazz - a midi sequencer for Linux
**
**  Copyright (C) 1994-1996 Andreas Voss (andreas@avix.rhein-neckar.de)
**
**  Copyright (C) 1995-1996 Per Sigmond (Per.Sigmond@hia.no)
**
**  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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

#include "wx.h"
#pragma hdrstop

#include "song.h"
#include "command.h"




tSong::~tSong()
{
  int i;
  Clear();
  for (i = 0; i < MaxTracks; i++)
    delete Tracks[i];
}

tSong::tSong()
{
  int i;
  nTracks = MaxTracks;
  TicksPerQuarter = 120;
  for (i = 0; i < MaxTracks; i++)
    Tracks[i] = new tTrack();
}

void tSong::Clear()
{
  for (int i = 0; i < nTracks; i++)
    Tracks[i]->Clear();
  nTracks = MaxTracks;
}

void tSong::Read(tReadBase &io, char *fname)
{
  wxBeginBusyCursor();
  int n = io.Open(fname);
  for (int i = 0; i < n && i < MaxTracks; i++)
    Tracks[i]->Read(io);
  io.Close();
  TicksPerQuarter = io.TicksPerQuarter;

  if (TicksPerQuarter < 48)
  {
    SetTicksPerQuarter(48);
  }
  else if (TicksPerQuarter > 192)
  {
    SetTicksPerQuarter(192);
  }

  wxEndBusyCursor();
}


void tSong::Write(tWriteBase &io, char *fname)
{
  int n = NumUsedTracks();
  if (!io.Open(fname, n, TicksPerQuarter))
  {
    Error("cant open %s", fname);
    return;
  }
  wxBeginBusyCursor();
  for (int i = 0; i < n; i++)
    Tracks[i]->Write(io);
  io.Close();
  wxEndBusyCursor();
}


tTrack *tSong::GetTrack(int Nr)
{
  if (Nr >= 0 && Nr < nTracks)
    return Tracks[Nr];
  return 0;
}


long tSong::GetLastClock()
{
  long max = 0;
  for (int i = 0; i < nTracks; i++)
  {
    long clk = Tracks[i]->GetLastClock();
    if (clk > max)
      max = clk;
  }
  return max;
}


void tSong::Clock2String(long clk, char *buf)
{
  tBarInfo b(this);
  b.SetClock(clk);
  clk -= b.Clock;
  long TicksPerCount = b.TicksPerBar / b.CountsPerBar;
  int Count = clk / TicksPerCount;
  sprintf(buf, "%d:%d:%ld", b.BarNr + 1, Count + 1, clk % TicksPerCount);
}

long tSong::String2Clock(char *buf)
{
  int bar = 1;
  long clk = 0;
  int  cnt = 1;
  sscanf(buf, "%d:%d:%ld", &bar, &cnt, &clk);
  -- bar;
  -- cnt;
  tBarInfo b(this);
  b.SetBar(bar);
  long TicksPerCount = b.TicksPerBar / b.CountsPerBar;
  return b.Clock + cnt * TicksPerCount + clk;
}


void tSong::MergeTracks(long FrClock, long ToClock, tEventArray *Destin, long delta)
{
  int i;

  // Find Solo-Tracks
  int solo = 0;
  for (i = 0; i < nTracks; i++)
  {
    if (Tracks[i]->State == tsSolo)
    {
      solo = 1;
      break;
    }
  }

  for (i = 0; i < nTracks; i++)
  {
    if (Tracks[i]->State == tsSolo || (!solo && Tracks[i]->State == tsPlay))
    {
      tEventIterator Iterator(Tracks[i]);
      tEvent *e = Iterator.Range(FrClock, ToClock);
      while (e)
      {
        tEvent *c = e->Copy();
        c->Clock += delta;
	Destin->Put(c);
	e = Iterator.Next();
      }
    }
  }
}

// ********************************************************************
// BarInfo
// *******************************************************************


tBarInfo::tBarInfo(tSong *Song)
  : Iterator(Song->Tracks[0])
{
  BarNr = 0;
  Clock = 0;
  TicksPerQuarter = Song->TicksPerQuarter;
  CountsPerBar = 4;
  TicksPerBar = TicksPerQuarter * 4;
}


void tBarInfo::SetBar(int barnr)
{
  BarNr = barnr;
  Clock = 0;
  TicksPerBar = TicksPerQuarter * 4;
  e = Iterator.First();
  while (1)
  {
    // Events bis Taktanfang nach MeterChange durchsuchen
    // Meter-Event vor oder genau auf Taktanfang stehen
    while (e && e->Clock <= Clock)
    {
      e->BarInfo(TicksPerBar, CountsPerBar, TicksPerQuarter);
      e = Iterator.Next();
    }

    if (!barnr)
      return;

    // Clock + Bar auf Anfang naechster Takt
    -- barnr;
    Clock += TicksPerBar;
  }
}


void tBarInfo::SetClock(long clock)
{
  BarNr = 0;
  Clock = 0;
  TicksPerBar = TicksPerQuarter * 4;
  e = Iterator.First();
  while (1)
  {
    while (e && e->Clock <= Clock)
    {
      e->BarInfo(TicksPerBar, CountsPerBar, TicksPerQuarter);
      e = Iterator.Next();
    }

    if (Clock + TicksPerBar > clock)
      return;

    // Clock + Bar auf Anfang naechster Takt
    Clock += TicksPerBar;
    ++ BarNr;
  }
}


void tBarInfo::Next()
{
  ++ BarNr;
  Clock += TicksPerBar;

  while (e && e->Clock <= Clock)
  {
    e->BarInfo(TicksPerBar, CountsPerBar, TicksPerQuarter);
    e = Iterator.Next();
  }
}


// ***********************************************************************
// Undo
// ***********************************************************************

void tSong::NewUndoBuffer()
{
  for (int i = 0; i < nTracks; i++)
    Tracks[i]->NewUndoBuffer();
}

void tSong::Undo()
{
  wxBeginBusyCursor();
  for (int i = 0; i < nTracks; i++)
    Tracks[i]->Undo();
  wxEndBusyCursor();
}

// ***********************************************************************
// SetTicksPerQuarter
// ***********************************************************************

void tSong::SetTicksPerQuarter(int NewTicks)
{
  int tt, ee;

  double f = (double)NewTicks / (double)TicksPerQuarter;
  for (tt = 0; tt < nTracks; tt++)
  {
    tTrack *t = Tracks[tt];
    for (ee = 0; ee < t->nEvents; ee++)
    {
      tEvent *e = t->Events[ee];
      e->Clock = (long)(f * e->Clock + 0.5);
      tKeyOn *k = e->IsKeyOn();
      if (k)
        k->Length = (long)(f * k->Length + 0.5);
    }
  }
  TicksPerQuarter = NewTicks;
}

// ***********************************************************************
// SetMeterChange
// ***********************************************************************

int tSong::SetMeterChange(int BarNr, int Numerator, int Denomiator)
{

  NewUndoBuffer();

  // Clock Taktanfang und -ende

  tBarInfo BarInfo(this);
  BarInfo.SetBar(BarNr - 1);
  long FrClock = BarInfo.Clock;
  BarInfo.Next();
  long ToClock = BarInfo.Clock;

  // evtl vorhandene TimeSignatures loeschen

  tTrack *t = Tracks[0];
  tEventIterator Iterator(t);
  tEvent *e = Iterator.Range(FrClock, ToClock);
  while (e)
  {
    if (e->IsTimeSignat())
      t->Kill(e);
    e = Iterator.Next();
  }

  // neues TimeSignature Event ablegen

  int Shift = 2;
  switch (Denomiator)
  {
    case 1:  Shift = 0; break;
    case 2:  Shift = 1; break;
    case 4:  Shift = 2; break;
    case 8:  Shift = 3; break;
    case 16: Shift = 4; break;
    case 32: Shift = 5; break;
  }

  e = new tTimeSignat(FrClock, Numerator, Shift);
  t->Put(e);
  t->Cleanup();
  return 0;
}


int tSong::NumUsedTracks()
{
  int n;
  for (n = nTracks; n > 1; n--)
    if (!Tracks[n-1]->IsEmpty())
      break;
  return n;
}

