//||
//||	`rhybag' - Rhythmic BinAural Generator
//||
//||	(c) 1999 Jim Peters <jim@aguazul.demon.co.uk>.  All Rights Reserved.
//||	For latest version see <http://www.aguazul.demon.co.uk/bagen/>.
//||	Released under the GNU GPL.  Use at your own risk.
//||
//||	" 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, version 2.
//||
//||	  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. "
//||
//||	If you really don't have a copy of the GNU GPL, I'll send you one.
//||	

//
//	Playing routines
//
//	A play-list is maintained of all the currently playing notes.
//	As the start times of new Events are reached, they are added
//	to the play-list, and events drop off the play-list once their
//	end-time is reached.
//
//

void 
setupTables() {
  // Setup sin table
  if (!sin_table) {
    int len= 1<<(32-ST_FR);
    int a;
    sin_table= Alloc(len * sizeof(int));

    for (a= 0; a<len; a++)
      sin_table[a]= (int)(ST_AMP * sin((a * 3.14159265358979323846 * 2) / len));
  }

  // Setup exp table
  if (!exp_table) {
    int len= ET_PPO * ET_OCT + 1;
    int a;
    exp_table= Alloc(len * sizeof(double));
    
    for (a= 0; a<ET_PPO; a++) {
      double val= pow(2, a*1.0/ET_PPO);
      int b;
      for (b= a; b<ET_OCT*ET_PPO+1; b += ET_PPO) {
	exp_table[b]= val;
	val *= 2.0;
      }
    }
  }
}

//
//	Setup the buffer and internal lists ready to play the events
//	at `event', `bsiz' samples at a time, including `pre' samples
//	before time 0, and `len' samples after time 0.  Returns the
//	buffer address.
//

int * 
initPlay(int bsiz, int pre, int len) {
  // Clear lists
  while (playS) { PlayS *pl= playS; playS= pl->nxt; Free(pl); }
  while (playB) { PlayB *pl= playB; playB= pl->nxt; Free(pl); }

  pev= event;
  ptim= -pre;
  petim= len;
  if (pbuf) Free(pbuf);
  plen= bsiz;
  pbuf= Alloc(sizeof(int) * 2 * bsiz);
  return pbuf;
}

//
//	Play a chunk into the buffer.  Writes two 32-bit ints per
//	sample.  Returns the number of samples written, or 0 if the
//	end of the sequence has been reached.
//

int  
playChunk() {
  int cnt;

  fprintf(stderr, "  %d of %d  \r", ptim, petim); fflush(stderr);

  // Add new Events that start in this chunk
  while (pev && pev->stim < ptim + plen) {
    if (pev->etim >= ptim) playAdd(pev);
    pev= pev->nxt;
  }
  
  {	// Clear buffer
    int *p= pbuf; cnt= plen; 
    while (cnt-- > 0) { *p++= 0; *p++= 0; }
  }

  {	// Render binaural notes, deleting notes that have finished
    PlayB **prvp, *pl;
    for (prvp= &playB; pl= *prvp; )
      if (playNoteB(pl)) { *prvp= pl->nxt; Free(pl); }
      else { prvp= &pl->nxt; }
  }
  
  {	// Render sample-based notes, deleting notes that have finished
    PlayS **prvp, *pl;
    for (prvp= &playS; pl= *prvp; )
      if (pl->fun(pl)) { *prvp= pl->nxt; Free(pl); }
      else { prvp= &pl->nxt; }
  }

  cnt= petim - ptim;
  if (cnt > plen) cnt= plen;
  ptim += cnt;

  return cnt;
}

//
//	There are several different sample-playing routines used
//	depending up on certain characteristics of the sample to be
//	played.
//
//	- Oversampling level may be 1x, 2x or 4x
//	- Sample may be mono or stereo 16-bit
//

typedef int (*playS_fun)(PlayS*);
extern playS_fun playS_tbl[3][2];

//
//	Add a note to the play list
//

void 
playAdd(Event *ev) {
  if (ev->not0) {		// Sample-note
    PlayS *pl= CAlloc(sizeof(*pl));
    NoteS *nn= ev->not0;

    // Open sample file if not already open
    if (!nn->fd) {
      nn->fd= open(nn->fnam, O_RDONLY);
      if (nn->fd < 0) 
	error("Can't open sample file `%s' for note `%s'", nn->fnam, nn->nam);
      if (-1 == (int)(nn->mmap= mmap(0, (nn->pre + nn->len) << (1 + nn->styp), 
				     PROT_READ, MAP_SHARED, nn->fd, 0)))
	errorE("Can't map sample file `%s' into memory for note `%s'", nn->fnam, nn->nam);
    }

    pl->nxt= playS; playS= pl;
    pl->vol= ev->vol * mvol;
    pl->tim= ev->tim;
    pl->env= nn->env;
    pl->frb= 32 - nn->bits;
    pl->off= nn->off0;
    pl->max= (nn->pre + nn->len) << pl->frb;
    pl->dec= nn->len << pl->frb;
    pl->loop= nn->loop;
    pl->dat= nn->mmap;
    pl->len= 0;		// Everything else will be setup as the first envelope chunk is loaded
    pl->fun= playS_tbl[0][nn->styp];	// @@@ Assume 1x oversampling only for now

    // Repeat loop before 0 if there is envelope there, but no `pre' section
    if (pl->env && pl->env->tim < 0 && nn->loop && nn->pre == 0) {
      double adj= pl->env->adj[0];	// Use initial adjust to calculate timing - assume 
      int tim= pl->env->tim;		//  that this will be constant up to 0-point; if not, the 
      int slen= (int)(nn->len / adj);	//  beginning of the sample won't match with time zero
      int cnt= (-tim + slen - 1) / slen;
      tim += cnt * slen;
      pl->off += (int)(ldexp(tim,16) / slen);
    }
  }
  else {			// Binaural note
    PlayB *pl= CAlloc(sizeof(*pl));
    NoteB *nn= ev->not1;
    pl->nxt= playB; playB= pl;
    pl->vol= ev->vol * mvol;
    pl->ba= nn->bin;
    pl->off1= 0;			// Start off out of phase for binaurals, in phase for pure sine
    pl->off2= (pl->ba->freq == 0) ? 0 : 0x80000000;
    pl->len= pl->cnt= 0;		// Leave everything to be set up when it starts playing
    pl->tim= ev->tim;
  }
}

//
//	Play a binaural note into buffer pbuf, starting at time ptim,
//	maximum duration plen.  Rets: delete me ?
//

int  
playNoteB(PlayB *pl) {
  int *bp;
  int tim, bcnt;
  double tmp;

  while (1) {
    if (pl->len) {		// We're in the middle of something
      bp= pbuf; bcnt= plen;
    }
    else {			// Get next part set up
      BaList *ba= pl->ba;
      if (!ba) return 1;		// Done - so delete me

      pl->ba= ba->nxt;
      pl->carr[0]= ba->carr[0] / rate * 4294967296.0;
      pl->carr[1]= ba->carr[1];
      pl->carr[2]= ba->carr[2];
      pl->freq[0]= ba->freq[0] / rate / 2 * 4294967296.0;   // half as we add to one, sub from other
      pl->freq[1]= ba->freq[1];
      pl->freq[2]= ba->freq[2];
      pl->amp[0]= ba->vol[0] * pl->vol * AMP_MAX;
      pl->amp[1]= ba->vol[1];
      pl->amp[2]= ba->vol[2];
      pl->ampi= ba->voli;
      pl->d_cnt= ba->cnt;
      pl->cnt= 0;
      pl->len= ba->len;

      tim= pl->tim + ba->tim - ptim;
      // Skip forward, emulating exactly the effect on off[12] as if real data was written
      while (tim < 0 && pl->len > 0) {
	int cnt, cnt2;
	uint carr, freq, inc1, inc2;
	
	carr= (uint)(pl->carr[0] * exp_table[(int)pl->carr[1]]);
	freq= (uint)(pl->freq[0] * exp_table[(int)pl->freq[1]]);
	inc1= carr + freq;
	inc2= carr - freq;
	
	cnt= -tim < pl->len ? -tim : pl->len;
	cnt2= pl->d_cnt - pl->cnt;
	if (cnt >= cnt2) {
	  cnt= cnt2;
	  pl->carr[1] += pl->carr[2];
	  pl->freq[1] += pl->freq[2];
	  pl->amp[1] += pl->amp[2];
	  pl->cnt= 0;
	}
	else pl->cnt += cnt;
	
	pl->len -= cnt;
	tim += cnt;
	
	pl->off1 += cnt * inc1;		// Overflows, but we're working mod 2^32 anyway
	pl->off2 += cnt * inc2;
      }
	
      bp= pbuf + 2*tim;
      bcnt= plen - tim;
      if (bcnt <= 0) return 0;		// Not yet - leave it until next chunk
    }

    // Write as much as we can to the buffer
    while (pl->len && bcnt) {		// Efficiency required within this loop
      int cnt, cnt2;
      uint carr, freq, inc1, inc2, amp, ampi, off1, off2;

      carr= (uint)(pl->carr[0] * exp_table[(int)pl->carr[1]]);
      freq= (uint)(pl->freq[0] * exp_table[(int)pl->freq[1]]);
      inc1= carr + freq;
      inc2= carr - freq;
      amp= (uint)(tmp= pl->amp[0] * exp_table[(int)pl->amp[1]]);
      ampi= (uint)(int)(tmp * pl->ampi + (tmp < 0 ? 0.49 : -0.49));	// Round towards zero
      if (pl->cnt) amp += ampi * pl->cnt;

      cnt= bcnt < pl->len ? bcnt : pl->len;
      cnt2= pl->d_cnt - pl->cnt;
      if (cnt >= cnt2) {
	cnt= cnt2;
	pl->carr[1] += pl->carr[2];
	pl->freq[1] += pl->freq[2];
	pl->amp[1] += pl->amp[2];
	pl->cnt= 0;
      }
      else pl->cnt += cnt;

      pl->len -= cnt;
      bcnt -= cnt;

      off1= pl->off1; off2= pl->off2;
      while (cnt-- > 0) {
	*bp++ += sin_table[(off1 += inc1) >> 18] * amp;
	*bp++ += sin_table[(off2 += inc2) >> 18] * amp;
	amp += ampi;
      }
      pl->off1= off1; pl->off2= off2;
    }
    if (!bcnt) break;
  }
  return 0;
}

//
//	Play a sample-based note into buffer pbuf, starting at time
//	ptim, maximum duration plen.  Rets: delete me ?
//
//	Sample-notes are played using a 32-bit offset that takes care
//	of both the position in the sample buffer and the fractional
//	part.  The split of the number of bits between the two parts
//	is variable and depends on the size of the sample file.  The
//	integer positional part must be able to contain a value 50%
//	greater than the maximum sample buffer position, to allow
//	play-back rates of up to one sample-buffer output per two
//	sample ticks.  Beyond this point, there will probably be
//	errors due to integer overflow, but this is an unrealistically
//	high rate to play the sample anyway, so no worries
//
//	For very large sample files, the number of fractional bits
//	will be small, meaning low accuracy if the pitch is varied,
//	but still this is acceptable.  For a 5min sample, played at
//	44.1kHz around its natural pitch, the unit of change is around
//	0.13 of a semitone.  If it was played four octaves below, it
//	would be limited to an accuracy of 2 semitones.  This is fine
//	if the pitch need not be varied, but otherwise, it is not a
//	good idea to try playing very long samples at very low
//	pitches.  For normal use, with a 12 sec sample played at
//	44.1kHz, there are 12 fractional bits, giving a maximum octave
//	drop of 12 octaves, and an accuracy at normal pitch of
//	4/1000ths of a semitone.  Reducing the play-back rate is the
//	only way to increase the accuracy if this really is a problem.
//
//	Sampling levels of quality:
//	- No interpolation/oversampling
//	- Using 2x oversampling to improve quality (take points at +/-25%)
//	- Use 4x or 8x oversampling as an option
//

//	Optimisations:
//	-------------
//
//	- Have a special routine to take care of case where no
//	pitch-shifting is going on at all.  This can just take care of
//	the volume envelope, skipping all oversampling and increments,
//	basically copying data.
//
//	- Take out sample data loaded check where this can be
//	predicted beforehand
//
//	- Take out (off>=max) check where this can be easily predicted
//	beforehand
//	

#define FUNC_NAM playS_1x_s16
#define AMP_MULT 65536
#define DAT_TO_BP 						\
  short *dp= (short*)(dat + ((off >> frb) << 2));		\
  *bp++ += amp * (int)*dp++; *bp++ += amp * (int)*dp;

#include "samp-play.c"

#define FUNC_NAM playS_1x_m16
#define AMP_MULT 65536
#define DAT_TO_BP 						\
  uint val= amp * (int)((short*)dat)[off >> frb];		\
  *bp++ += val; *bp++ += val;

#include "samp-play.c"


int playS_2x_m16(PlayS *pl) { error("Not yet implemented"); return 1; }
int playS_2x_s16(PlayS *pl) { error("Not yet implemented"); return 1; }
int playS_4x_m16(PlayS *pl) { error("Not yet implemented"); return 1; }
int playS_4x_s16(PlayS *pl) { error("Not yet implemented"); return 1; }

playS_fun playS_tbl[3][2]= {
  { playS_1x_m16, playS_1x_s16 },
  { playS_2x_m16, playS_2x_s16 },
  { playS_4x_m16, playS_4x_s16 }
};

//
//	Find the change in the sample offset represented by a
//	particular envelope segment.  This is returned as a double to
//	take care of overflow cases.  
//

double 
envLength(EnvList *en) {
  double off= 0;
  uint inc;
  double adj0, adj1, adj2;
  int d_cnt, len, cnt;
  
  adj0= en->adj[0];
  adj1= en->adj[1];
  adj2= en->adj[2];
  d_cnt= en->cnt;
  len= en->len;
  
  while (len > 0) {
    inc= (uint)(adj0 * exp_table[(int)adj1]);
    
    cnt= len;
    if (cnt >= d_cnt) { cnt= d_cnt; adj1 += adj2; }

    len -= cnt;
    off += inc * cnt;
  }
  return off;
}

// END //
