/*
    PsyTexx: sampler.h. Sample editor functions
    Copyright (C) 2004  Zolotov Alexandr

    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
*/
//*** Contact info: Zolotov Alexandr (NightRadio project)
//***               Ekaterinburg. Russia.
//***               Email: observer_page@mail.ru
//***                      warmplace@warmplace.ru
//***                      nightradio@knoppix.ru
//***               WWW: warmplace.ru

typedef struct {
  char *sampledata;      //sample data
  unsigned long rec_pos; //record position
} record_info;
record_info rec_info;

MemHandle smp,copy;
char *smpP,*copyP; //smpP - temp place for sample and for echo data
unsigned long copy_size=0;
unsigned long smp_size;
unsigned int smp_offset;
unsigned int smp_start; //loop start
unsigned int smp_end;   //loop end
unsigned int smp_sel1=0;  //Selection start
unsigned int smp_sel2=0;  //Selection end
unsigned long smp_zoom=1;
unsigned char maska[8]={128,64,32,16,8,4,2,1};
unsigned char maska2[20];
SndStreamRef rec_stream;
Err rec_callback(void*,SndStreamRef,void*,UInt32);

void Sampler_init()
{
  copy=MemHandleNew(61000);
  copyP=(char*)MemHandleLock(copy);
  smp=MemHandleNew(61000);
  smpP=(char*)MemHandleLock(smp);
}

void Sampler_close()
{
  MemHandleUnlock( copy );
  MemHandleFree( copy );
  MemHandleUnlock( smp );
  MemHandleFree( smp );
}

void Sampler_load(unsigned char *smp_data,
                  unsigned int s_size,
                  unsigned int s_start,
                  unsigned int s_len)
{
  MemMove(smpP,smp_data,s_size);
  smp_size=s_size;
  smp_start=s_start;
  smp_end=s_start+s_len;
}

void set_zoom(unsigned long window_size)
{
  if(!(smp_zoom=(window_size<<6)/160)) smp_zoom=1<<6;
}

char get_sample(unsigned long offset,
                unsigned long screen_x)
{
  unsigned long off;
  off=offset+((screen_x*smp_zoom)>>6);
  if(off<smp_size) return smpP[off]>>3;
  else return 0;
}

unsigned int get_sample_off(unsigned long offset,
                            unsigned long screen_x)
{
  unsigned long off;
  off=offset+((screen_x*smp_zoom)>>6);
  return off;
}

long get_screen_x(long off)
{
  long scr_x=((off-(long)smp_offset)<<6)/(long)smp_zoom;
  if(scr_x<0) return 0;
  if(scr_x>159) return 159;
  return scr_x;
}

void draw_line(unsigned char *screen,
               long x,
               long y,
               long y_size)
{
  long pnt=(x>>3)+(y<<4)+(y<<2);
  unsigned char mask=maska[x&7];
  if(y_size>=0){
    while(y_size!=0){
      screen[pnt]|=mask;
      pnt+=20;
      y_size--;
    }
  }else{
    while(y_size!=0){
      screen[pnt]|=mask;
      pnt-=20;
      y_size++;
    }
  }
}

void draw_point(long x,long y,unsigned char *screen)
{
  unsigned int x_off,xx;
  if(x>=0){
    if(x<160){
      y*=20;
      xx=x>>3;
      x_off=x-(xx<<3);
      screen[y+xx]|=maska[x_off];
    }
  }
}

void draw_loop(unsigned char *screen)
{
  int xl;
  unsigned int pp;
  for(pp=0;pp<240;pp++){screen[pp]=0;screen[pp+880]=0;}
  xl=get_screen_x(smp_start);
  draw_point(xl-1,5,screen);draw_point(xl,5,screen);draw_point(xl+1,5,screen);
  draw_point(xl,6,screen);
  xl=get_screen_x(smp_end);
  draw_point(xl-1,50,screen);draw_point(xl,50,screen);draw_point(xl+1,50,screen);
  draw_point(xl,49,screen);
}

void draw_block(unsigned char *screen) //Draw black box of the selected block
{
  unsigned int x1,x2,x3;
  long a,b,pnt1,pnt2,pnt;
  unsigned char m1,m2;
  x1=get_screen_x(smp_sel1); //start point
  x2=get_screen_x(smp_sel2); //end point
  if(x2<x1){x3=x2;x2=x1;x1=x3;}
  pnt1=x1>>3;
  pnt2=x2>>3;
  MemSet(maska2,20,0);
  for(a=pnt1;a<=pnt2;a++) maska2[a]=255;
  m1=255;m1>>=(x1&7);
  m2=255;m2<<=(7-(x2&7));
  maska2[pnt1]&=m1;
  maska2[pnt2]&=m2;

  pnt=240;
  for(b=12;b<44;b++){
    for(a=0;a<20;a++){
      screen[pnt]^=maska2[a];pnt++;
    }
  }
}

void draw_sample(unsigned char *screen,
                 unsigned int screen_size)
{
  int a;
  MemSet(screen,screen_size,0);
  for(a=0;a<160;a++){
    draw_line(screen,a,28,get_sample(smp_offset,a));
  }
  draw_block(screen);
  draw_loop(screen);
}

void correct_selection()
{
	long temp;
	if( smp_sel1 > smp_sel2 ) {
		temp = smp_sel1;
		smp_sel1 = smp_sel2;
		smp_sel2 = temp;
	}
}

void save_sample(int samp) //samp - current sample number;
{
  mod_info._status=0;
  MemHandleUnlock(modREC[samp+3]);                      //Close old sample record
  modREC[samp+3]=DmResizeRecord(modDB,samp+3,smp_size); //Resize sample record
  modH[samp+3]=MemHandleLock(modREC[samp+3]);           //Lock it
  sampledata[samp]=modH[samp+3];
  sampledata2[samp]=ByteSwap32(modH[samp+3]);
  song.samples[samp].length=(smp_size>>1)<<1;
  DmWrite(sampledata[samp],0,smpP,smp_size);            //Save sample data
  song.samples[samp].reppnt = smp_start;
  song.samples[samp].replen = smp_end-smp_start;
  //====================== Save MOD information ==========================
  for(aa=0;aa<31;aa++){
    song.samples[aa].length /= 2;
    song.samples[aa].reppnt /= 2;
    song.samples[aa].replen /= 2;
  }
  DmWrite(modH[1],0,&(song.title),1084);
  for(aa=0;aa<31;aa++){
    song.samples[aa].length *= 2;
    song.samples[aa].reppnt *= 2;
    song.samples[aa].replen *= 2;
  }
  //======================================================================
}

void rec_init(UInt32 rec_freq, void* info, void* callback) //Microphone recorder init
{
    rec_info.sampledata=smpP;
    rec_info.rec_pos=0;
    SndStreamCreate(&rec_stream,
                    sndInput,
                    rec_freq,
                    sndInt16Little,
                    sndMono,
                    callback,
                    info,
                    BUFSIZE,
                    1);

    SndStreamStart(rec_stream);
}

void rec_close() //Microphone recorder close
{
    SndStreamDelete(rec_stream);
}

Err rec_callback(void *userData,
                 SndStreamRef stream,
                 void *_buffer,
                 UInt32 frameCount)
{
  long l;
  record_info *ui;
  ui=userData;
  if((ui->rec_pos+frameCount)>=60000){l=60000-ui->rec_pos;}
  else{l=frameCount;}
  if(l>0){
    MemMove(ui->sampledata+ui->rec_pos,_buffer,l);
    ui->rec_pos+=l;
  }
}
