code:
/*
    WDL - wavwrite.h
    Copyright (C) 2005 Cockos Incorporated

    WDL is dual-licensed. You may modify and/or distribute WDL under either of
    the following  licenses:

      This software is provided 'as-is', without any express or implied
      warranty.  In no event will the authors be held liable for any damages
      arising from the use of this software.

      Permission is granted to anyone to use this software for any purpose,
      including commercial applications, and to alter it and redistribute it
      freely, subject to the following restrictions:

      1. The origin of this software must not be misrepresented; you must not
         claim that you wrote the original software. If you use this software
         in a product, an acknowledgment in the product documentation would be
         appreciated but is not required.
      2. Altered source versions must be plainly marked as such, and must not be
         misrepresented as being the original software.
      3. This notice may not be removed or altered from any source distribution.

    or:

      WDL 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.

      WDL 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 WDL; if not, write to the Free Software
      Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

/*

  This file provides a simple class for writing PCM WAV files.

*/

#ifndef _WAVWRITE_H_
#define _WAVWRITE_H_

#include <stdio.h>
#include "pcmfmtcvt.h"

class WaveWriter
{
  public:
    // appending doesnt check sample types
    WaveWriter()
    {
      m_fp=0;
      m_bps=0;
      m_srate=0;
      m_nch=0;
    }

    WaveWriter(const char *filename, int bps, int nch, int srate, int allow_append=1)
    {
      m_fp=0;
      m_bps=0;
      m_srate=0;
      m_nch=0;
      Open(filename,bps,nch,srate,allow_append);

    }

    int Open(const char *filename, int bps, int nch, int srate, int allow_append=1)
    {
      m_fp=0;
      if (allow_append)
      {
        m_fp=fopen(filename,"r+b");
        if (m_fp)
        {
          fseek(m_fp,0,SEEK_END);
          int pos=ftell(m_fp);
          if (pos < 44)
          {
            char buf[44]={0,};
            fwrite(buf,1,44-pos,m_fp);
          }
        }
      }
      if (!m_fp)
      {
        m_fp=fopen(filename,"wb");
        char tbuf[44];
        fwrite(tbuf,1,44,m_fp); // room for header
      }
      m_bps=bps;
      m_nch=nch>1?2:1;
      m_srate=srate;

      return !!m_fp;
    }

    ~WaveWriter()
    {
      if (m_fp)
      {
        int bytelen=ftell(m_fp)-44;
        fseek(m_fp,0,SEEK_SET);

        // write header
        fwrite("RIFF",1,4,m_fp);
        int riff_size=bytelen+44-8;
        int x;
        for (x = 0; x < 32; x += 8)
        {
          unsigned char c=(riff_size>>x)&255;
          fwrite(&c,1,1,m_fp);
        }
        fwrite("WAVEfmt \x10\0\0\0",1,12,m_fp);
  			fwrite("\1\0",1,2,m_fp); // PCM

        for (x = 0; x < 16; x += 8) // nch
        {
          char c=(m_nch>>x)&255;
          fwrite(&c,1,1,m_fp);
        }
        for (x = 0; x < 32; x += 8) // srate
        {
          char c=(m_srate>>x)&255;
          fwrite(&c,1,1,m_fp);
        }
        for (x = 0; x < 32; x += 8) // bytes_per_sec
        {
          char c=((m_nch * (m_bps/8) * m_srate)>>x)&255;
          fwrite(&c,1,1,m_fp);
        }
        int blockalign=m_nch * (m_bps/8);
        for (x = 0; x < 16; x += 8) // block alignment
        {
          char c=(blockalign>>x)&255;
          fwrite(&c,1,1,m_fp);
        }
        for (x = 0; x < 16; x += 8) // bits/sample
        {
          char c=((m_bps&~7)>>x)&255;
          fwrite(&c,1,1,m_fp);
        }
        fwrite("data",1,4,m_fp);
        for (x = 0; x < 32; x += 8) // size
        {
          char c=((bytelen)>>x)&255;
          fwrite(&c,1,1,m_fp);
        }

        fclose(m_fp);
        m_fp=0;
      }
    }

    int Status() { return !!m_fp; }

    void WriteRaw(void *buf, int len)
    {
      if (m_fp) fwrite(buf,1,len,m_fp);
    }

    void WriteFloats(float *samples, int nsamples)
    {
      if (!m_fp) return;

      if (m_bps == 16)
      {
        while (nsamples-->0)
        {
          short a;
          float_TO_INT16(a,*samples);
          unsigned char c=a&0xff;
          fwrite(&c,1,1,m_fp);
          c=a>>8;
          fwrite(&c,1,1,m_fp);
          samples++;
        }
      }
      else if (m_bps == 24)
      {
        while (nsamples-->0)
        {
          unsigned char a[3];
          float_to_i24(samples,a);
          fwrite(a,1,3,m_fp);
          samples++;
        }
      }
    }

    void WriteFloatsNI(float **samples, int offs, int nsamples)
    {
      if (!m_fp) return;
      float *tmpptrs[2]={samples[0]+offs,m_nch>1?samples[1]+offs:NULL};

      if (m_bps == 16)
      {
        while (nsamples-->0)
        {
          int ch;
          for (ch = 0; ch < m_nch; ch ++)
          {
            short a;
            float_TO_INT16(a,tmpptrs[ch][0]);
            unsigned char c=a&0xff;
            fwrite(&c,1,1,m_fp);
            c=a>>8;
            fwrite(&c,1,1,m_fp);
            tmpptrs[ch]++;
          }
        }
      }
      else if (m_bps == 24)
      {
        while (nsamples-->0)
        {
          int ch;
          for (ch = 0; ch < m_nch; ch ++)
          {
            unsigned char a[3];
            float_to_i24(tmpptrs[ch],a);
            fwrite(a,1,3,m_fp);
            tmpptrs[ch]++;
          }
        }
      }
    }

    int get_nch() { return m_nch; }
    int get_srate() { return m_srate; }
    int get_bps() { return m_bps; }

  private:
    FILE *m_fp;
    int m_bps,m_nch,m_srate;
};

#endif//_WAVWRITE_H_


code:
/*
    NINJAM ClipLogCvt - cliplogcvt.cpp
    Copyright (C) 2005 Cockos Incorporated

    NINJAM 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.

    NINJAM 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 NINJAM; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

/*

  This command line tool parses a NINJAM session .log file (clipsort.log),
  and writes out a EDL text file that Vegas 4.0+ (and maybe others) can read,
  as well as a LOF file for Audacity.

  It can optionally do things like concatenate OGGs, or decompress OGGs to WAVs,
  or decompress OGGs to concatenated WAVs, too.

  */

#ifdef _WIN32
#include <windows.h>
#endif

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>

#ifndef _WIN32
#include <sys/types.h>
#include <sys/stat.h>
  #ifndef stricmp
    #define stricmp strcasecmp
  #endif
#endif

#include "../../WDL/string.h"
#include "../../WDL/ptrlist.h"
#include "../../WDL/lineparse.h"
#include "../../WDL/vorbisencdec.h"
#include "../../WDL/wavwrite.h"

class UserChannelValueRec
{
public:
  double position;
  double length;
  WDL_String guidstr;

};

class UserChannelList
{
  public:

   WDL_String user;
   int chidx;

   WDL_PtrList<UserChannelValueRec> items;
};

#ifdef _WIN32

const char *realpath(const char *path, char *resolved_path)
{
  char *p;
  if (GetFullPathName(path,1024,resolved_path,&p))
    return resolved_path;
  return NULL;
}

#define DIRCHAR '\\'
#define DIRCHAR_S "\\"

#else

#define DIRCHAR '/'
#define DIRCHAR_S "/"

#endif

int g_ogg_concatmode=0;
int g_write_wavs=0;
int g_write_wav_bits=16;
int g_maxsilence=0;

int resolveFile(char *name, WDL_String *outpath, char *path)
{
  char *p=name;
  while (*p && *p == '0') p++;
  if (!*p) return 0; // empty name

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wwrite-strings"
  char *exts[]={".wav",".ogg"};
#pragma GCC diagnostic pop
  WDL_String fnfind;
  int x;
  for (x = !!g_ogg_concatmode; x < (int)(sizeof(exts)/sizeof(exts[0])); x ++)
  {
    fnfind.Set(path);
    fnfind.Append(DIRCHAR_S);
    fnfind.Append(name);
    fnfind.Append(exts[x]);

    FILE *tmp=fopen(fnfind.Get(),"rb");
    if (!tmp)
    {
      fnfind.Set(path);
      fnfind.Append(DIRCHAR_S);
      // try adding guid subdir
      char t[3]={name[0],DIRCHAR,0};
      fnfind.Append(t);

      fnfind.Append(name);
      fnfind.Append(exts[x]);
      tmp=fopen(fnfind.Get(),"rb");
    }
    if (tmp)
    {
      fseek(tmp,0,SEEK_END);
      int l=ftell(tmp);
      fclose(tmp);
      if (l)
      {
        char buf[4096];
        if (realpath(fnfind.Get(),buf))
        {
          outpath->Set(buf);
        }
        return 1;
      }
    }
  }
  printf("Error resolving guid %s\n",name);
  return 0;

}

FILE *g_outfile_edl, *g_outfile_lof;

void WriteRec(char *name, int id, int trackid, double position, double len)
{
  if (g_outfile_edl)
  {
               //ID    Track    StartT  Length
    fprintf(g_outfile_edl,"%d;\t" "%d;\t" "%f;\t" "%f;\t",id,trackid,position,len);

    //          PlayRate   Locked  Normali SM Looped OnRuler  MediaType
    fprintf(g_outfile_edl,"1.000000;\tFALSE;\tFALSE;\t0;\tFALSE;\tFALSE;\tAUDIO;\t");

    //FileName
    fprintf(g_outfile_edl,"\"%s\";\t",name);

    //         Stream  StreamStart Len    FadeTimeIn   FadeTimeOut SustainGn  CurveInGainIn  CvO  GainOut  LayerColorCurveInRCurveOutR
    fprintf(g_outfile_edl,"0;\t" "0.0000;\t" "%f;\t" "0.0000;\t" "0.0000;\t" "1.000000;\t0;\t0.000000;\t0;\t0.000000;\t0;\t-1;\t0;\t0\n",
      (double)len);
  }
  if (g_outfile_lof)
  {
    fprintf(g_outfile_lof,"file \"%s\" offset %f\n",name,position/1000.0);
  }
}

void usage()
{
   printf("Usage: \n"
          "  cliplogcvt session_directory [options]\n"
          "\n"
          "Options:\n"
          "  -skip <intervals>\n"
          "  -maxlen <intervals>\n"
          "  -concat\n"
          "  -decode\n"
          "  -decodebits 16|24\n"
          "  -insertsilence maxseconds   -- valid only with -concat -decode\n"

      );
  exit(1);
}

WDL_String g_concatdir;

void WriteOutTrack(char *chname, UserChannelList *list, int *track_id, int *id, char *path)
{
  int y;
  FILE *concatout=NULL;
  WaveWriter *concatout_wav=NULL;
  double last_pos=-1000.0, last_len=0.0;
  WDL_String concat_fn;
  int concat_filen=0;

  const double DELTA=0.0000001;
  for (y = 0; y < list->items.GetSize(); y ++)
  {
    WDL_String op;
    if (!resolveFile(list->items.Get(y)->guidstr.Get(),&op,path))
    {
      if (concatout || concatout_wav)
      {
        WriteRec(concat_fn.Get(), *id, *track_id, last_pos, last_len);
        (*id)++;
        if (concatout) fclose(concatout);
        delete concatout_wav;
      }
      concatout_wav=0;
      concatout=0;
      continue;
    }

    if (g_ogg_concatmode)
    {
      if ((concatout || concatout_wav) && fabs(last_pos+last_len - list->items.Get(y)->position) > DELTA)
      {
        if (concatout_wav && concatout_wav->Status() && list->items.Get(y)->position >= last_pos+last_len && list->items.Get(y)->position <= last_pos+last_len + g_maxsilence*1000.0)
        {
          double silen=list->items.Get(y)->position - (last_pos+last_len);
          last_len += silen;

          // fill in silence
          float buf[4096]={0,};
          int nsamples=(int) ((silen * concatout_wav->get_srate() * concatout_wav->get_nch())/1000.0);
          while (nsamples > 0)
          {
            int l=nsamples;
            if (l > 4096)l=4096;
            concatout_wav->WriteFloats(buf,l);
            nsamples-=l;
          }

        }
        else
        {
          WriteRec(concat_fn.Get(), *id, *track_id, last_pos, last_len);
          (*id)++;

          if (concatout) fclose(concatout);
          delete concatout_wav;
          concatout_wav=0;
          concatout=0;
        }
      }

      if (!concatout && !concatout_wav)
      {
        concat_fn.Set(g_concatdir.Get());
        char buf[4096];
        sprintf(buf,DIRCHAR_S "%s_%03d.%s",chname,concat_filen++,g_write_wavs?"wav":"ogg");
        concat_fn.Append(buf);

        if (realpath(concat_fn.Get(),buf))
        {
          concat_fn.Set(buf);
        }

        if (g_write_wavs)
        {

          concatout_wav = new WaveWriter;
        }
        else
        {

          concatout = fopen(concat_fn.Get(),"wb");
          if (!concatout)
          {
            printf("Warning: error opening %s. RESULTING TXT WILL LACK REFERENCE TO THIS FILE! ACK!\n",concat_fn.Get());
          }
        }
        last_pos = list->items.Get(y)->position;
        last_len = 0.0;
      }

      if (concatout || concatout_wav)
      {
        FILE *fp = fopen(op.Get(),"rb");
        if (fp)
        {
          if (concatout_wav)
          {
            // decode file
            VorbisDecoder decoder;
            int did_write=0;

            for (;;)
            {
              int l=fread(decoder.DecodeGetSrcBuffer(1024),1,1024,fp);
              decoder.DecodeWrote(l);

              if (decoder.m_samples_used>0)
              {

                if (concatout_wav->Status() && (decoder.GetNumChannels() != concatout_wav->get_nch() || decoder.GetSampleRate() != concatout_wav->get_srate()))
                {
                  // output parameter change
  //                printf("foo\n");

                  WriteRec(concat_fn.Get(), *id, *track_id, last_pos, last_len);
                  (*id)++;
                  delete concatout_wav;

                  concat_fn.Set(g_concatdir.Get());
                  char buf[4096];
                  sprintf(buf,DIRCHAR_S "%s_%03d.wav",chname,concat_filen++);
                  concat_fn.Append(buf);

                  if (realpath(concat_fn.Get(),buf))
                  {
                    concat_fn.Set(buf);
                  }

                  concatout_wav = new WaveWriter;
                  last_pos = list->items.Get(y)->position;
                  last_len = 0.0;

                }

                if (!concatout_wav->Status() && decoder.GetNumChannels() && decoder.GetSampleRate())
                {
//                  printf("opening new wav\n");
                  if (!concatout_wav->Open(concat_fn.Get(),g_write_wav_bits, decoder.GetNumChannels(), decoder.GetSampleRate(),0))
                  {
                    printf("Warning: error opening %s to write WAV\n",concat_fn.Get());
                    break;
                  }
                }

                if (concatout_wav->Status())
                {
                  concatout_wav->WriteFloats((float *)decoder.m_samples.Get(),decoder.m_samples_used);
                }
                did_write += decoder.m_samples_used;

                decoder.m_samples_used=0;
              }

              if (!l) break;
            }

            last_len += did_write * 1000.0 / (double)concatout_wav->get_srate() / (double)concatout_wav->get_nch();
            if (!did_write)
            {
              printf("Warning: error decoding %s to convert to WAV\n",op.Get());
            }

          }
          else
          {
            char buf[4096];
            for (;;)
            {
              int a=fread(buf,1,sizeof(buf),fp);
              if (!a) break;
              fwrite(buf,1,a,concatout);
            }
            last_len += list->items.Get(y)->length;
          }
          fclose(fp);
        }

      }
    }
    else
    {

      char *fn=op.Get();
      int fn_l=strlen(fn);

      if (g_write_wavs && fn_l > 3 && !stricmp(fn+fn_l-4,".ogg"))
      {
        // decode OGG file to WAV, set the output file name to that
        FILE *fp=fopen(fn,"rb");
        if (fp)
        {
          WDL_String newfn(fn);
          strcpy(newfn.Get()+fn_l-4,".wav");

          VorbisDecoder decoder;
          WaveWriter *wr=NULL;

          for (;;)
          {
            int l=fread(decoder.DecodeGetSrcBuffer(1024),1,1024,fp);
            decoder.DecodeWrote(l);

            if (decoder.m_samples_used>0)
            {
              if (!wr && decoder.GetNumChannels() && decoder.GetSampleRate())
              {
                wr = new WaveWriter(newfn.Get(), g_write_wav_bits, decoder.GetNumChannels(), decoder.GetSampleRate(),0);
              }

              if (wr)
              {
                wr->WriteFloats((float *)decoder.m_samples.Get(),decoder.m_samples_used);
              }

              decoder.m_samples_used=0;
            }

            if (!l) break;
          }

          if (wr)
          {
            if (wr->Status())
            {
              op.Set(newfn.Get());
            }
            else
            {
              printf("Warning: error opening %s to write WAV\n",newfn.Get());
            }
            delete wr;
          }
          else
          {
            printf("Warning: error decoding %s to convert to WAV\n",op.Get());
          }

          fclose(fp);
        }
        else
        {
          printf("Warning: error opening %s to convert to WAV\n",op.Get());
        }
      }

      WriteRec(op.Get(), *id, *track_id,
                   list->items.Get(y)->position, list->items.Get(y)->length);
      (*id)++;
    }
  }
  if (concatout || concatout_wav)
  {
    WriteRec(concat_fn.Get(), *id, *track_id, last_pos, last_len);
    (*id)++;
    if (concatout) fclose(concatout);
    delete concatout_wav;

    concatout=0;
    concatout_wav=0;
  }
  if (y) (*track_id)++;
}

int main(int argc, char **argv)
{
  printf("ClipLogCvt v0.02 - Copyright (C) 2005, Cockos, Inc.\n"
         "(Converts NINJAM sessions to EDL/LOF,\n"
         " optionally writing uncompressed WAVs etc)\n\n");
  if (argc <  2 || argv[1][0] == '-')
  {
    usage();
  }
  int start_interval=1;
  int end_interval=0x40000000;

  int p;
  for (p = 2; p < argc; p++)
  {
    if (!stricmp(argv[p],"-skip"))
    {
      if (++p >= argc) usage();
      start_interval = atoi(argv[p])+1;
    }
    else if (!stricmp(argv[p],"-maxlen"))
    {
      if (++p >= argc) usage();
      end_interval = atoi(argv[p]);
    }
    else if (!stricmp(argv[p],"-concat"))
    {
      g_ogg_concatmode=1;
    }
    else if (!stricmp(argv[p],"-decodebits"))
    {
      if (++p >= argc) usage();
      g_write_wav_bits=atoi(argv[p]);
      if (g_write_wav_bits != 24 && g_write_wav_bits != 16) usage();
    }
    else if (!stricmp(argv[p],"-decode"))
    {
      g_write_wavs=1;
    }
    else if (!stricmp(argv[p],"-insertsilence"))
    {
      if (++p >= argc) usage();
      g_maxsilence=atoi(argv[p]);
    }
    else usage();
  }
  end_interval += start_interval;

  WDL_String logfn(argv[1]);
  logfn.Append(DIRCHAR_S "clipsort.log");
  FILE *logfile=fopen(logfn.Get(),"rt");
  if (!logfile)
  {
    printf("Error opening logfile\n");
    return -1;
  }

  if (g_ogg_concatmode)
  {
    g_concatdir.Set(argv[1]);
    g_concatdir.Append(DIRCHAR_S "concat");
#ifdef _WIN32
    CreateDirectory(g_concatdir.Get(),NULL);
#else
    mkdir(g_concatdir.Get(),0755);
#endif
  }

  double m_cur_bpm=-1.0;
  int m_cur_bpi=-1;
  int m_interval=0;

  double m_cur_position=0.0;
  double m_cur_lenblock=0.0;

  UserChannelList localrecs[32];
  WDL_PtrList<UserChannelList> curintrecs;

int line_n = 0 ; // TODO: (issue #2)
  // go through the log file
  for (;;)
  {
++line_n ; // TODO: (issue #2)
    char buf[4096];
    buf[0]=0;
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-result"
    fgets(buf,sizeof(buf),logfile);
#pragma GCC diagnostic pop
    if (!buf[0]) break;
    if (buf[strlen(buf)-1]=='\n') buf[strlen(buf)-1]=0;
    if (!buf[0]) continue;

    LineParser lp(0);

    int res=lp.parse(buf);

    if (res)
    {
      printf("Error parsing log line!\n");
      return -1;
    }
    else
    {
      if (lp.getnumtokens()>0)
      {
        int w=lp.gettoken_enum(0,"interval\0local\0user\0end\0");
        if (w < 0)
        {
          printf("unknown token %s\n",lp.gettoken_str(0));
          return -1;
        }
        switch (w)
        {
          case 0: // interval
            {
              if (lp.getnumtokens() != 4)
              {
                printf("interval line has wrong number of tokens\n");
                return -2;
              }

              m_cur_position+=m_cur_lenblock;

// idx -Wunused-but-set-variable
//              int idx=0;
              double bpm=0.0;
              int bpi=0;
// idx -Wunused-but-set-variable
//              idx=lp.gettoken_int(1);
              bpm=lp.gettoken_float(2);
              bpi=lp.gettoken_int(3);

              if ((m_cur_bpi >= 0 && m_cur_bpi != bpi) ||
                  (m_cur_bpm >= 0 && m_cur_bpm != bpm))
              {
//                printf("BPI/BPM changed from %d/%.2f to %d/%.2f\n",m_cur_bpi,m_cur_bpm,bpi,bpm);
              }

              m_cur_bpi=bpi;
              m_cur_bpm=bpm;

              m_cur_lenblock=((double)m_cur_bpi * 60000.0 / m_cur_bpm);
              m_interval++;

            }
          break;
          case 1: // local
            if (m_interval >= start_interval && m_interval < end_interval)
            {
              if (lp.getnumtokens() != 3)
              {
                printf("local line has wrong number of tokens\n");
                return -2;
              }
              UserChannelValueRec *p=new UserChannelValueRec;
              p->position=m_cur_position;
              p->length=m_cur_lenblock;
              p->guidstr.Set(lp.gettoken_str(1));
              localrecs[(lp.gettoken_int(2))&31].items.Add(p);
            }
          break;
          case 2: // user
            if (m_interval >= start_interval && m_interval < end_interval)
            {
#define NIX_KLUDGE
// KLUDGE: LineParser::getnumtokens() is buggy on linux
//             always fails on first accepted 'user' line (issue #2)
#ifdef NIX_KLUDGE
if (!lp.gettoken_str(0) || !lp.gettoken_str(1) || !lp.gettoken_str(2) ||
    !lp.gettoken_str(3) || !lp.gettoken_str(4)                         )
#endif // NIX_KLUDGE
              if (lp.getnumtokens() != 5)
              {
#ifdef NIX_KLUDGE
printf("parse error on line: %d\n" , line_n) ;
if (lp.gettoken_str(0)) printf("0=%s\n" , lp.gettoken_str(0)) ;
if (lp.gettoken_str(1)) printf("1=%s\n" , lp.gettoken_str(1)) ;
if (lp.gettoken_str(2)) printf("2=%s\n" , lp.gettoken_str(2)) ;
if (lp.gettoken_str(3)) printf("3=%s\n" , lp.gettoken_str(3)) ;
if (lp.gettoken_str(4)) printf("4=%s\n" , lp.gettoken_str(4)) ;
#endif // NIX_KLUDGE
                printf("user line has wrong number of tokens\n");
                return -2;
              }

              char *guidtmp=lp.gettoken_str(1);
              char *username=lp.gettoken_str(2);
              int chidx=lp.gettoken_int(3);
//              char *channelname=lp.gettoken_str(4);

              //printf("Got user '%s' channel %d '%s' guid %s\n",username,chidx,channelname,guidtmp);

              UserChannelValueRec *ucvr=new UserChannelValueRec;
              ucvr->guidstr.Set(guidtmp);
              ucvr->position=m_cur_position;
              ucvr->length=m_cur_lenblock;

              int x;
              for (x = 0; x < curintrecs.GetSize(); x ++)
              {
                if (!stricmp(curintrecs.Get(x)->user.Get(),username) && curintrecs.Get(x)->chidx == chidx)
                {
                  break;
                }
              }
              if (x == curintrecs.GetSize())
              {
                // add the rec
                UserChannelList *t=new UserChannelList;
                t->user.Set(username);
                t->chidx=chidx;

                curintrecs.Add(t);
              }
              if (curintrecs.Get(x)->items.GetSize())
              {
                UserChannelValueRec *lastitem=curintrecs.Get(x)->items.Get(curintrecs.Get(x)->items.GetSize()-1); // this is for when the server sometimes groups them in the wrong interval
                double last_end=lastitem->position + lastitem->length;
                if (ucvr->position < last_end)
                {
                  ucvr->position = last_end;
                }
              }
              curintrecs.Get(x)->items.Add(ucvr);
              // add this record to it
            }

          break;
          case 3: // end
          break;
        }

      }
    }

  }
  fclose(logfile);

  printf("Done analyzing log, building output...\n");

  WDL_String outfn(argv[1]);
  outfn.Append(DIRCHAR_S "clipsort.txt");
  g_outfile_edl=fopen(outfn.Get(),"wt");
  if (!g_outfile_edl)
  {
    printf("Error opening EDL outfile\n");
  }
  outfn.Set(argv[1]);
  outfn.Append(DIRCHAR_S "clipsort.lof");
  g_outfile_lof=fopen(outfn.Get(),"wt");
  if (!g_outfile_lof)
  {
    printf("Error opening LOF outfile\n");
  }

  if (g_outfile_edl)
  {
    fprintf(g_outfile_edl,"%s",
      "\"ID\";\"Track\";\"StartTime\";\"Length\";\"PlayRate\";\"Locked\";\"Normalized\";\"StretchMethod\";\"Looped\";\"OnRuler\";\"MediaType\";\"FileName\";\"Stream\";\"StreamStart\";\"StreamLength\";\"FadeTimeIn\";\"FadeTimeOut\";\"SustainGain\";\"CurveIn\";\"GainIn\";\"CurveOut\";\"GainOut\";\"Layer\";\"Color\";\"CurveInR\";\"CurveOutR\"\n");
  }
  if (g_outfile_lof)
  {
    fprintf(g_outfile_lof,"window\n");
  }

  if (!g_outfile_edl && !g_outfile_lof)
  {
    printf("Was unable to open any outputs\n");
    return  0;
  }

  int id=1;
  int track_id=0;
  int x;
  for (x= 0; x < (int)(sizeof(localrecs)/sizeof(localrecs[0])); x ++)
  {
    char chname[512];
    sprintf(chname,"local_%02d",x);
    WriteOutTrack(chname,localrecs+x, &track_id, &id, argv[1]);

  }
  for (x= 0; x < curintrecs.GetSize(); x ++)
  {
    char chname[4096];
    sprintf(chname,"%s_%02d",curintrecs.Get(x)->user.Get(),x);
    char *p=chname;
    while (*p)
    {
      if (*p == '/'||*p == '\\' || *p == '?' || *p == '*' || *p == ':' || *p == '\'' || *p == '\"' || *p == '|' || *p == '<' || *p == '>') *p='_';
      p++;
    }

    WriteOutTrack(chname, curintrecs.Get(x), &track_id, &id, argv[1]);
  }
  printf("wrote %d records, %d tracks\n",id-1,track_id);

  if (g_outfile_edl) fclose(g_outfile_edl);
  if (g_outfile_lof) fclose(g_outfile_lof);

  return 0;

}

repo_name: ninjam/libninjam

license: mit


code:
<?xml version="1.0" encoding="Windows-1252"?>
<VisualStudioProject
	ProjectType="Visual C++"
	Version="9.00"
	Name="cliplogcvt"
	ProjectGUID="{95EE2676-7091-4236-8AC0-257057170B98}"
	TargetFrameworkVersion="0"
	>
	<Platforms>
		<Platform
			Name="Win32"
		/>
	</Platforms>
	<ToolFiles>
	</ToolFiles>
	<Configurations>
		<Configuration
			Name="Release|Win32"
			OutputDirectory=".\Release"
			IntermediateDirectory=".\Release"
			ConfigurationType="1"
			InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC60.vsprops"
			UseOfMFC="0"
			ATLMinimizesCRunTimeLibraryUsage="false"
			CharacterSet="2"
			>
			<Tool
				Name="VCPreBuildEventTool"
			/>
			<Tool
				Name="VCCustomBuildTool"
			/>
			<Tool
				Name="VCXMLDataGeneratorTool"
			/>
			<Tool
				Name="VCWebServiceProxyGeneratorTool"
			/>
			<Tool
				Name="VCMIDLTool"
				TypeLibraryName=".\Release/cliplogcvt.tlb"
				HeaderFileName=""
			/>
			<Tool
				Name="VCCLCompilerTool"
				Optimization="2"
				InlineFunctionExpansion="1"
				AdditionalIncludeDirectories="../../sdks/oggvorbis-win32sdk-1.0.1/include"
				PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE"
				StringPooling="true"
				RuntimeLibrary="2"
				EnableFunctionLevelLinking="true"
				PrecompiledHeaderFile=".\Release/cliplogcvt.pch"
				AssemblerListingLocation=".\Release/"
				ObjectFile=".\Release/"
				ProgramDataBaseFileName=".\Release/"
				WarningLevel="3"
				SuppressStartupBanner="true"
			/>
			<Tool
				Name="VCManagedResourceCompilerTool"
			/>
			<Tool
				Name="VCResourceCompilerTool"
				PreprocessorDefinitions="NDEBUG"
				Culture="1033"
			/>
			<Tool
				Name="VCPreLinkEventTool"
			/>
			<Tool
				Name="VCLinkerTool"
				OutputFile=".\Release/cliplogcvt.exe"
				LinkIncremental="1"
				SuppressStartupBanner="true"
				ProgramDatabaseFile=".\Release/cliplogcvt.pdb"
				SubSystem="1"
				OptimizeForWindows98="1"
				RandomizedBaseAddress="1"
				DataExecutionPrevention="0"
				TargetMachine="1"
			/>
			<Tool
				Name="VCALinkTool"
			/>
			<Tool
				Name="VCManifestTool"
			/>
			<Tool
				Name="VCXDCMakeTool"
			/>
			<Tool
				Name="VCBscMakeTool"
				SuppressStartupBanner="true"
				OutputFile=".\Release/cliplogcvt.bsc"
			/>
			<Tool
				Name="VCFxCopTool"
			/>
			<Tool
				Name="VCAppVerifierTool"
			/>
			<Tool
				Name="VCPostBuildEventTool"
			/>
		</Configuration>
		<Configuration
			Name="Debug|Win32"
			OutputDirectory=".\Debug"
			IntermediateDirectory=".\Debug"
			ConfigurationType="1"
			InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC60.vsprops"
			UseOfMFC="0"
			ATLMinimizesCRunTimeLibraryUsage="false"
			CharacterSet="2"
			>
			<Tool
				Name="VCPreBuildEventTool"
			/>
			<Tool
				Name="VCCustomBuildTool"
			/>
			<Tool
				Name="VCXMLDataGeneratorTool"
			/>
			<Tool
				Name="VCWebServiceProxyGeneratorTool"
			/>
			<Tool
				Name="VCMIDLTool"
				TypeLibraryName=".\Debug/cliplogcvt.tlb"
				HeaderFileName=""
			/>
			<Tool
				Name="VCCLCompilerTool"
				Optimization="0"
				AdditionalIncludeDirectories=""C:\src\libninjam-win\libvorbis-1.3.3\include";"C:\src\libninjam-win\libogg-1.3.0\include""
				PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE"
				MinimalRebuild="true"
				BasicRuntimeChecks="3"
				RuntimeLibrary="1"
				PrecompiledHeaderFile=".\Debug/cliplogcvt.pch"
				AssemblerListingLocation=".\Debug/"
				ObjectFile=".\Debug/"
				ProgramDataBaseFileName=".\Debug/"
				WarningLevel="3"
				SuppressStartupBanner="true"
				DebugInformationFormat="4"
			/>
			<Tool
				Name="VCManagedResourceCompilerTool"
			/>
			<Tool
				Name="VCResourceCompilerTool"
				PreprocessorDefinitions="_DEBUG"
				Culture="1033"
			/>
			<Tool
				Name="VCPreLinkEventTool"
			/>
			<Tool
				Name="VCLinkerTool"
				OutputFile=".\Debug/cliplogcvt.exe"
				LinkIncremental="2"
				SuppressStartupBanner="true"
				AdditionalLibraryDirectories=""
				GenerateDebugInformation="true"
				ProgramDatabaseFile=".\Debug/cliplogcvt.pdb"
				SubSystem="1"
				RandomizedBaseAddress="1"
				DataExecutionPrevention="0"
				TargetMachine="1"
			/>
			<Tool
				Name="VCALinkTool"
			/>
			<Tool
				Name="VCManifestTool"
			/>
			<Tool
				Name="VCXDCMakeTool"
			/>
			<Tool
				Name="VCBscMakeTool"
				SuppressStartupBanner="true"
				OutputFile=".\Debug/cliplogcvt.bsc"
			/>
			<Tool
				Name="VCFxCopTool"
			/>
			<Tool
				Name="VCAppVerifierTool"
			/>
			<Tool
				Name="VCPostBuildEventTool"
			/>
		</Configuration>
	</Configurations>
	<References>
	</References>
	<Files>
		<Filter
			Name="Source Files"
			Filter="cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
			>
			<File
				RelativePath="cliplogcvt.cpp"
				>
				<FileConfiguration
					Name="Release|Win32"
					>
					<Tool
						Name="VCCLCompilerTool"
						AdditionalIncludeDirectories=""
						PreprocessorDefinitions=""
					/>
				</FileConfiguration>
				<FileConfiguration
					Name="Debug|Win32"
					>
					<Tool
						Name="VCCLCompilerTool"
						PreprocessorDefinitions=""
					/>
				</FileConfiguration>
			</File>
		</Filter>
		<Filter
			Name="Header Files"
			Filter="h;hpp;hxx;hm;inl"
			>
			<File
				RelativePath="..\..\WDL\heapbuf.h"
				>
			</File>
			<File
				RelativePath="..\..\WDL\lineparse.h"
				>
			</File>
			<File
				RelativePath="..\..\WDL\ptrlist.h"
				>
			</File>
			<File
				RelativePath="..\..\WDL\string.h"
				>
			</File>
			<File
				RelativePath="..\..\WDL\vorbisencdec.h"
				>
			</File>
			<File
				RelativePath="..\..\WDL\wavwrite.h"
				>
			</File>
		</Filter>
	</Files>
	<Globals>
	</Globals>
</VisualStudioProject>


code:
#!/bin/bash

# temporary makefile - while this cliplogcvt project is still quite buggy

g++ -O2 -malign-double -s -Wall -Wextra -I../ -o ../../build/cliplogcvt cliplogcvt.cpp -logg -lvorbis

path: src/cliplogcvt/mk


code:
On linux, install libogg, libvorbis, libasound, then do

make

and hope things work.

On Mac OS X, once you have the dev tools installed, install libogg and
libvorbis, then do:

export MAC=1
make

good luck!

-Justin

path: src/cursesclient/COMPILING


code:
DESTDIR=
PREFIX=/usr
INSTALLDIR=$(DESTDIR)$(PREFIX)

#############################################################
# CPU optimization section
#############################################################

OPTFLAGS = -O2
LFLAGS = -lm -L../../build/lib -lninjam-client -lninjam-common
ifdef MAC
OPTFLAGS += -D_MAC -mcpu=7450
LFLAGS += -framework coreaudio -lncurses.5
else
MACH=$(shell uname -m)
ifeq ($(MACH), x86_64)
OPTFLAGS += -fPIC
else
OPTFLAGS += -malign-double
endif
LFLAGS += -lncurses
endif

#############################################################
# Basic Configuration
#############################################################

# we MUST have -fomit-frame-pointer and -lm, otherwise we hate life
CFLAGS = $(OPTFLAGS) -s -Wall -Wextra -I../
# CFLAGS += -Wshadow
CC=gcc
CXX=g++
CXXFLAGS = $(CFLAGS)

OBJS = ../../build/obj/cursesclient/cursesclient.o

../../build/obj/cursesclient/%.o: %.cpp
	mkdir -p $(@D)
	$(CXX) $(CXXFLAGS) -c $< -o $@

default: cninjam

cninjam: $(OBJS)
	$(CXX) $(CXXFLAGS) -o ../../build/cninjam $(OBJS) -lpthread $(LFLAGS)

clean:
	-rm $(OBJS) ../../build/cninjam

install: default
	mkdir -p                    $(INSTALLDIR)/bin
	install ../../build/cninjam $(INSTALLDIR)/bin

path: src/cursesclient/Makefile


code:
/*
    NINJAM Curses Client - cursesclient.cpp
    Copyright (C) 2005 Cockos Incorporated

    NINJAM 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.

    NINJAM 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 NINJAM; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

/*

  Curses (text mode) client code. On Windows this requires the (included)
  win32 curses emulation layer.

  */

#ifdef _WIN32
#define CURSES_INSTANCE (&m_cursinst)
#include <windows.h>
#include "curses.h"
#include "cursesclientinst.h"
#define strncasecmp strnicmp
#else
#include <stdlib.h>
#include <memory.h>
#include <curses.h>
#endif

#include <stdio.h>
#include <ctype.h>
#include <math.h>
#include <signal.h>
#include <float.h>

#include <ninjam/audiostream.h>
#include <ninjam/njclient.h>
#include <WDL/dirscan.h>
#include <WDL/lineparse.h>

#include <ninjam/njmisc.h>

#define VALIDATE_TEXT_CHAR(thischar) ((isspace(thischar) || isgraph(thischar)) && (thischar) < 256)
#ifdef _WIN32
#define getch() curses_getch(CURSES_INSTANCE)
#define erase() curses_erase(CURSES_INSTANCE)

void ninjamCursesClientInstance::Run()
{
}

ninjamCursesClientInstance m_cursinst;

jesusonicAPI *JesusonicAPI;
WDL_String jesusdir;
#endif

int g_chat_scroll=0;
int curs_ypos,curs_xpos;
int color_map[8];
int g_ui_inchat=0;
int g_done=0;
int g_ui_state=0;
int g_ui_locrename_ch;
int g_ui_voltweakstate_channel;
int g_need_disp_update;
char m_lineinput_str[120];
char m_chatinput_str[120];

WDL_PtrList<char> g_chat_buffers;

void addChatLine(const char *src, const char *text)
{
  while (g_chat_buffers.GetSize() > 256)
  {
    free(g_chat_buffers.Get(0));
    g_chat_buffers.Delete(0);
  }
  WDL_String tmp;
  if (src && *src && !strncmp(text,"/me ",4))
  {
    tmp.Set("* ");
    tmp.Append(src);
    tmp.Append(" ");
    const char *p=text+3;
    while (*p == ' ') p++;
    tmp.Append(p);
  }
  else
  {
   if (src&&*src)
   {
     tmp.Set("<");
     tmp.Append(src);
     tmp.Append("> ");
   }
   else if (src)
   {
     tmp.Set("*** ");
   }
   tmp.Append(text);
  }
  g_chat_buffers.Add(strdup(tmp.Get()));
  g_chat_scroll=0;
}

WDL_String g_topic;

void chatmsg_cb(int user32, NJClient *inst, const char **parms, int nparms)
{
  if (!parms[0]) return;

  if (!strcmp(parms[0],"TOPIC"))
  {
    if (parms[2])
    {
      WDL_String tmp;
      if (parms[1] && *parms[1])
      {
        tmp.Set(parms[1]);
        tmp.Append(" sets topic to: ");
      }
      else tmp.Set("Topic is: ");
      tmp.Append(parms[2]);

      g_topic.Set(parms[2]);
      addChatLine("",tmp.Get());

      g_need_disp_update=1;
    }
  }
  else if (!strcmp(parms[0],"MSG"))
  {
    if (parms[1] && parms[2])
      addChatLine(parms[1],parms[2]);
    g_need_disp_update=1;
  }
  else if (!strcmp(parms[0],"PRIVMSG"))
  {
    if (parms[1] && parms[2])
    {
      WDL_String tmp;
      tmp.Set("*");
      tmp.Append(parms[1]);
      tmp.Append("* ");
      tmp.Append(parms[2]);
      addChatLine(NULL,tmp.Get());
    }
    g_need_disp_update=1;
  }
  else if (!strcmp(parms[0],"JOIN") || !strcmp(parms[0],"PART"))
  {
    if (parms[1] && *parms[1])
    {
      WDL_String tmp(parms[1]);
      tmp.Append(" has ");
      tmp.Append(parms[0][0]=='P' ? "left" : "joined");
      tmp.Append(" the server");
      addChatLine("",tmp.Get());
    }
    g_need_disp_update=1;
  }
}

#ifdef _WIN32
audioStreamer *CreateConfiguredStreamer(char *inifile, int showcfg, HWND hwndParent);
#endif
audioStreamer *g_audio;
NJClient *g_client;

void audiostream_onunder() { }
void audiostream_onover() { }

int g_audio_enable=0;

void audiostream_onsamples(float **inbuf, int innch, float **outbuf, int outnch, int len, int srate)
{
  if (!g_audio_enable)
  {
    int x;
    // clear all output buffers
    for (x = 0; x < outnch; x ++) memset(outbuf[x],0,sizeof(float)*len);
    return;
  }
  g_client->AudioProc(inbuf,innch, outbuf, outnch, len,srate);
}

int g_sel_x, g_sel_ypos,g_sel_ycat;

#define COLORMAP(x) color_map[x]

// highlights shit in []
void highlightoutline(int line, char *str, int attrnorm, int bknorm, int attrhi, int bkhi, int attrsel, int bksel, int whl)
{
  int state=0;
  int l=COLS-1;
  int lcol=0;
  move(line,0);
	attrset(attrnorm);
	bkgdset(bknorm);

  while (*str && l-- > 0)
  {
    if (*str == ']')
    {
      if (state)
      {
	      attrset(attrnorm);
    	  bkgdset(bknorm);
        state=0;
      }
    }
    addch(*str);
    if (*str == '[')
    {
      if (whl > 0)
      {
        char *tmp=strstr(str,"]");
        if (tmp && !strstr(tmp,"["))
        {
          whl=0;
          g_sel_x=lcol;
        }
      }
      if (!state)
      {
        lcol++;
        if (!whl--)
        {
          attrset(attrsel);
          bkgdset(bksel);
        }
        else
        {
          attrset(attrhi);
          bkgdset(bkhi);
        }
        state=1;
      }
    }
    str++;

  }
  if (state)
  {
	  attrset(attrnorm);
	  bkgdset(bknorm);
  }

}

void drawstatusbar()
{
  if (g_ui_state) return;
  int l,p;
  g_client->GetPosition(&p,&l);
  if (!l) return;

	bkgdset(COLORMAP(6));
	attrset(COLORMAP(6));

  move(LINES-2,0);
  p*=(COLS);
  p/=l;
  int x;
  for (x = 0; x < COLS; x ++) addch(x <= p ? '#' : ' ');

	bkgdset(COLORMAP(0));
	attrset(COLORMAP(0));

  move(curs_ypos,curs_xpos);
}

void showmainview(bool action=false, int ymove=0)
{
  int chat_lines=LINES/4;
  if (chat_lines<4) chat_lines=4;

  int sec1lines=2;
  int x;
  for (x=0;x<MAX_LOCAL_CHANNELS;x++)
  {
    if (g_client->EnumLocalChannels(x)<0) break;
    sec1lines++;
  }

  int sec2lines=0;

  x=0;
  for (;;)
  {
    if (!g_client->GetUserState(x)) break;

    int y=0;
    for (;;)
    {
      if (g_client->EnumUserChannels(x,y) < 0) break;
      sec2lines++;
      y++;
    }
    x++;
  }
  if (!sec2lines) sec2lines=1;

  if (ymove < 0)
  {
    if (g_sel_ypos-- <= 0)
    {
      if (g_sel_ycat == 1) g_sel_ypos=sec1lines-1;
      else if (g_sel_ycat == 2) g_sel_ypos=sec2lines-1;
      else g_sel_ypos=0;

      if (g_sel_ycat>0) g_sel_ycat--;
    }
  }
  else if (ymove > 0)
  {
    g_sel_ypos++;
  }

  if (!ymove && g_sel_ycat == 1 && g_sel_ypos >= sec2lines)
  {
    g_sel_ypos=sec2lines-1;
  }

  int selpos=0;
  int selcat=0;
	bkgdset(COLORMAP(0));
	attrset(COLORMAP(0));

  erase();
	bkgdset(COLORMAP(1));
	attrset(COLORMAP(1));
	mvaddstr(0,0,"LOCAL");
  clrtoeol();
	bkgdset(COLORMAP(0));
	attrset(COLORMAP(0));
  char linebuf[1024];
  int linemax=LINES-2-chat_lines;
  {
    if (action && g_sel_ycat == selcat && g_sel_ypos == selpos)
    {
      if (g_sel_x == 1 || g_sel_x == 3)
      {
        g_ui_state=1;
        g_ui_voltweakstate_channel=g_sel_x == 1 ? -2 : -1;
      }
      else
      {
        if (g_sel_x == 0)
        {
          g_client->config_mastermute=!g_client->config_mastermute;
        }
        else
        {
          g_client->config_metronome_mute=!g_client->config_metronome_mute;
        }
      }
    }
    sprintf(linebuf,"  master: [%c]mute [",g_client->config_mastermute?'X':' ');
    mkvolpanstr(linebuf+strlen(linebuf),g_client->config_mastervolume,g_client->config_masterpan);

    sprintf(linebuf+strlen(linebuf),"]    |    metronome: [%c]mute [",g_client->config_metronome_mute?'X':' ');
    mkvolpanstr(linebuf+strlen(linebuf),g_client->config_metronome,g_client->config_metronome_pan);
    sprintf(linebuf+strlen(linebuf),"]");

    highlightoutline(1,linebuf,COLORMAP(0),COLORMAP(0),
                               COLORMAP(0)|A_BOLD,COLORMAP(0),
                               COLORMAP(5),COLORMAP(5),(g_sel_ypos != selpos++ || g_sel_ycat != selcat) ? -1 : g_sel_x);
//	  mvaddnstr(1,0,linebuf,COLS-1);
  }
  int ypos=2;
  for (x=0;ypos < linemax;x++)
  {
    int a=g_client->EnumLocalChannels(x);
    if (a<0) break;
    int sch;
    bool bc,mute;
    float vol,pan;
    char *name=g_client->GetLocalChannelInfo(a,&sch,NULL,&bc);
    g_client->GetLocalChannelMonitoring(a,&vol,&pan,&mute,NULL);

    if (action && g_sel_ycat == selcat && g_sel_ypos == selpos)
    {
      if (g_sel_x == 0)
      {
        g_ui_state=2;
        g_ui_locrename_ch=a;
        strncpy(m_lineinput_str,name,sizeof(m_lineinput_str)-1);
      }
      else if (g_sel_x == 1)
      {
        // toggle active
        g_client->SetLocalChannelInfo(a,NULL,false,0,false,0,true,bc=!bc);
        g_client->NotifyServerOfChannelChange();
      }
      else if (g_sel_x == 2)
      {
        g_ui_state=3;
        g_ui_locrename_ch=a;
      }
      else if (g_sel_x == 3)
      {
        // mute
        g_client->SetLocalChannelMonitoring(a,false,0.0f,false,0.0f,true,mute=!mute,false,false);
      }
      else if (g_sel_x == 4)
      {
        //volume
        g_ui_state=1;
        g_ui_voltweakstate_channel=a;
      }
#ifdef _WIN32
      else if (g_sel_x >= 6 && JesusonicAPI)
      {
        void *i=0;
        g_client->GetLocalChannelProcessor(a,NULL,&i);
        if (!i)
        {
          // start it up
          void *p=CreateJesusInstance(a,"",g_audio->getSampleRate());
          if (p) g_client->SetLocalChannelProcessor(a,jesusonic_processor,p);
        }
        else
        {
          if (g_sel_x >= 7)  // kill
          {
            g_client->SetLocalChannelProcessor(a,NULL,NULL);
            deleteJesusonicProc(i,a);
          }
          else
          {
           // JesusonicAPI->ui_wnd_destroy(i);
            HWND h=JesusonicAPI->ui_wnd_gethwnd(i);
            if (h && IsWindow(h))
            {
              ShowWindow(h,SW_SHOWNA);
              SetForegroundWindow(h);
            }
            else
            {
              HWND h=JesusonicAPI->ui_wnd_create(i);
              ShowWindow(h,SW_SHOWNA);
              SetTimer(h,1,40,NULL);
              SetForegroundWindow(h);
            }

            // show
          }
        }
      }
#endif
      else if (g_sel_x >= 5)
      {
#ifdef _WIN32
        void *i=0;
        g_client->GetLocalChannelProcessor(a,NULL,&i);
        if (i) deleteJesusonicProc(i,a);
#endif
        g_client->DeleteLocalChannel(a);
        g_client->NotifyServerOfChannelChange();
        x--;
        action=0;
        continue;
        // delete
      }
    }

#ifdef _WIN32
    void *tmp;
    g_client->GetLocalChannelProcessor(a,NULL,&tmp);
#endif
    char volstr[256];
    mkvolpanstr(volstr,vol,pan);
    const char *sname=g_audio->GetChannelName(sch);
    if (!sname) sname="Silence";

    char snamebuf[32];
    if (strlen(sname)>16)
    {
      strcpy(snamebuf,"...");
      strcat(snamebuf,sname+strlen(sname)-13);
      sname=snamebuf;
    }
    sprintf(linebuf,"  [%s] [%c]xmit [%s] [%c]mute [%s] [del] ",name,bc?'X':' ',sname,mute?'X':' ',volstr);

#ifdef _WIN32
    if (JesusonicAPI)
    {
      sprintf(linebuf+strlen(linebuf),"[js][%c] ", tmp?'x': ' ');
    }
#endif

    sprintf(linebuf+strlen(linebuf),
      "<%2.1fdB>",
      VAL2DB(g_client->GetLocalChannelPeak(a)));

    highlightoutline(ypos++,linebuf,COLORMAP(0),COLORMAP(0),
                               COLORMAP(0)|A_BOLD,COLORMAP(0),
                               COLORMAP(5),COLORMAP(5),(g_sel_ypos != selpos++ || g_sel_ycat != selcat) ? -1 : g_sel_x);
  }
  if (ypos < LINES-3)
  {
    if (action && g_sel_ycat == selcat && g_sel_ypos == selpos)
    {
      int x;
      for (x = 0; x < g_client->GetMaxLocalChannels(); x ++)
      {
        if (!g_client->GetLocalChannelInfo(x,NULL,NULL,NULL)) break;
      }
      if (x < g_client->GetMaxLocalChannels())
      {
        g_client->SetLocalChannelInfo(x,"channel",true,0,false,0,true,false);
        g_client->NotifyServerOfChannelChange();

        const char *sname=g_audio->GetChannelName(0);
        if (!sname) sname="Silence";

        char snamebuf[32];
        if (strlen(sname)>16)
        {
          strcpy(snamebuf,"...");
          strcat(snamebuf,sname+strlen(sname)-13);
          sname=snamebuf;
        }

        char volstr[256];
        mkvolpanstr(volstr,1.0f,0.0f);
        sprintf(linebuf,"  [channel] [ ]xmit [%s] [ ]mute [%s] [del] %s<-120dB>",
          sname,
          volstr,
#ifdef _WIN32
          JesusonicAPI?"[js][ ] " :
#endif
           ""

          );

        action=false;
        selpos++;

        highlightoutline(ypos++,linebuf,COLORMAP(0),COLORMAP(0),
                                   COLORMAP(0)|A_BOLD,COLORMAP(0),
                                   COLORMAP(5),COLORMAP(5),g_sel_x);

      }
    }
    if (ypos < LINES-3)
    {
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wwrite-strings"
      highlightoutline(ypos++,"  [new channel]",COLORMAP(0),COLORMAP(0),
                                 COLORMAP(0)|A_BOLD,COLORMAP(0),
                                 COLORMAP(5),COLORMAP(5),(g_sel_ypos != selpos++ || g_sel_ycat != selcat) ? -1 : g_sel_x);
#pragma GCC diagnostic pop
    }
  }

  int wasadv=0;
  if (g_sel_ycat == selcat && g_sel_ypos >= selpos)
  {
    wasadv=1;
    g_sel_ycat++;
    g_sel_ypos=0;
  }

  selpos=0;
  selcat=1;

  if (ypos < LINES-3)
  {
	  bkgdset(COLORMAP(6));
	  attrset(COLORMAP(6));
	  mvaddnstr(ypos++,0,"REMOTE",COLS-1);
    clrtoeol();
	  bkgdset(COLORMAP(0));
	  attrset(COLORMAP(0));
  }
  int user=0;
  x=0;
  while (ypos < linemax)
  {
    if (!x) // show user info
    {
      char *name=g_client->GetUserState(user);
      if (!name) break;

	    bkgdset(COLORMAP(4));
	    attrset(COLORMAP(4));
	    mvaddnstr(ypos++,0,name,COLS-1);

      clrtoeol();
	    bkgdset(COLORMAP(0));
	    attrset(COLORMAP(0));

    }

    if (ypos >= linemax) break;

    int a=g_client->EnumUserChannels(user,x);
    if (a < 0)
    {
      x=0;
      user++;
      continue;
    }

    float vol,pan;
    bool sub,mute;
    char *name=g_client->GetUserChannelState(user,a,&sub,&vol,&pan,&mute);
    // show channel info

    if (action && g_sel_ycat == selcat && g_sel_ypos == selpos)
    {
      if (g_sel_x == 0)
      {
        // toggle subscribe
        g_client->SetUserChannelState(user,a,true,sub=!sub,false,0.0f,false,0.0f,false,false,false,false,false,0,false,false);
      }
      else if (g_sel_x == 1)
      {
        // toggle mute
        g_client->SetUserChannelState(user,a,false,false,false,0.0f,false,0.0f,true,mute=!mute,false,false,false,0,false,false);
      }
      else if (g_sel_x >= 2)
      {
        // volume
        g_ui_state=1;
        g_ui_voltweakstate_channel=1024+64*user+a;
      }
    }

    char volstr[256];
    mkvolpanstr(volstr,vol,pan);
    sprintf(linebuf,"  \"%s\" [%c]recv [%c]mute [%s] <%2.1fdB>",name,sub?'X':' ',mute?'X':' ',volstr,VAL2DB(g_client->GetUserChannelPeak(user,a)));

    highlightoutline(ypos++,linebuf,COLORMAP(0),COLORMAP(0),
                               COLORMAP(0)|A_BOLD,COLORMAP(0),
                               COLORMAP(5),COLORMAP(5),(g_sel_ypos != selpos++ || g_sel_ycat != selcat) ? -1 : g_sel_x);

    x++;

  }

  if (!selpos && ypos < linemax)
  {
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wwrite-strings"
    highlightoutline(ypos++,"[no remote users]",COLORMAP(0),COLORMAP(0),
                               COLORMAP(0)|A_BOLD,COLORMAP(0),
                               COLORMAP(5),COLORMAP(5),(g_sel_ypos != selpos++ || g_sel_ycat != selcat) ? -1 : g_sel_x);
#pragma GCC diagnostic pop
  }

  curs_ypos=LINES-1;
  curs_xpos=0;

  if (!selpos && wasadv) g_sel_ycat++;
  if (selpos > 0 && g_sel_ycat == selcat && g_sel_ypos >= selpos)
  {
    g_sel_ycat++;
    g_sel_ypos=0;
  }

  selcat=2;
  selpos=0;

  g_ui_inchat=0;
  if (chat_lines>=4)
  {
	  bkgdset(COLORMAP(1));
	  attrset(COLORMAP(1));
    mvaddnstr(LINES-2-chat_lines,0,g_topic.Get()[0]?g_topic.Get():"CHAT",COLS-1);
    clrtoeol();
	  bkgdset(COLORMAP(0));
	  attrset(COLORMAP(0));

    int x;
    if (g_chat_scroll > g_chat_buffers.GetSize()-(chat_lines-2)) g_chat_scroll=g_chat_buffers.GetSize()-(chat_lines-2);
    int pos=g_chat_buffers.GetSize()-g_chat_scroll;
    if (pos < 0) pos=0;
    else if (pos > g_chat_buffers.GetSize()) pos=g_chat_buffers.GetSize();

    for (x = 0; x < chat_lines-2; )
    {
      char *p;
      if (--pos < 0 || !(p=g_chat_buffers.Get(pos))) break;

      char *np=p;
      int maxw=COLS-1;
      while ((int)strlen(np) > maxw) np+=maxw;

      while (np >= p && x < chat_lines-2)
      {
        mvaddnstr(LINES-2-2-x,0,np,maxw);
        x++;
        np-=maxw;
      }

    }

    if (g_sel_ycat == selcat && g_sel_ypos == selpos++)
    {
      g_sel_x=0;
      g_ui_inchat=1;
	    bkgdset(COLORMAP(2));
	    attrset(COLORMAP(2));
      curs_ypos=LINES-2-1;
      curs_xpos=strlen(m_chatinput_str);
    }
    else
    {
	    bkgdset(COLORMAP(3));
	    attrset(COLORMAP(3));
    }
	  mvaddstr(LINES-2-1,0,m_chatinput_str);
    clrtoeol();
	  bkgdset(COLORMAP(0));
	  attrset(COLORMAP(0));
  }

  if (g_sel_ycat == selcat && g_sel_ypos > selpos) g_sel_ypos=selpos;

  if (g_ui_state==1)
  {
	  bkgdset(COLORMAP(2));
	  attrset(COLORMAP(2));
	  mvaddnstr(LINES-2,0,"USE UP AND DOWN FOR VOLUME, LEFT AND RIGHT FOR PANNING, ENTER WHEN DONE",COLS-1);
    clrtoeol();
	  bkgdset(COLORMAP(0));
	  attrset(COLORMAP(0));
  }
  else if (g_ui_state == 3)
  {
	  bkgdset(COLORMAP(2));
	  attrset(COLORMAP(2));
	  mvaddnstr(LINES-2,0,"USE ARROW KEYS TO SELECT THE INPUT CHANNEL, ENTER WHEN DONE",COLS-1);
    clrtoeol();
	  bkgdset(COLORMAP(0));
	  attrset(COLORMAP(0));
  }
  else drawstatusbar();

  ypos=LINES-1;
  sprintf(linebuf,"[QUIT NINJAM] : %s : %.1fBPM %dBPI : %dHz %dch->%dch %dbps%s",
    g_client->GetHostName(),g_client->GetActualBPM(),g_client->GetBPI(),g_audio->getSampleRate(),g_audio->getNInputChannels(),g_audio->getNOutputChannels(),g_audio->getBitDepth()&~7,g_audio->getBitDepth()&1 ? "(f)":"");
  highlightoutline(ypos++,linebuf,COLORMAP(1),COLORMAP(1),COLORMAP(1),COLORMAP(1),COLORMAP(5),COLORMAP(5),(g_sel_ypos != selpos || g_sel_ycat != selcat) ? -1 : g_sel_x);
  attrset(COLORMAP(1));
  bkgdset(COLORMAP(1));
  clrtoeol();
  if (action && g_sel_ycat == selcat && g_sel_ypos == selpos)
  {
    g_done++;
  }

  if (g_ui_state == 2 || g_ui_state == 4)
  {
	  bkgdset(COLORMAP(2));
	  attrset(COLORMAP(2));
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wwrite-strings"
    char *p1="RENAME CHANNEL:";
#pragma GCC diagnostic pop
	  mvaddnstr(LINES-2,0,p1,COLS-1);
	  bkgdset(COLORMAP(0));
	  attrset(COLORMAP(0));

    if ((int)strlen(p1) < COLS-2) { addch(' '); addnstr(m_lineinput_str,COLS-2-strlen(p1)); }

    clrtoeol();
  }
  else
  {
    move(curs_ypos,curs_xpos);
  }

}

void sigfunc(int sig)
{
  printf("Got Ctrl+C\n");
  g_done++;
}

void usage(int noexit=0)
{

  printf("Usage: NINJAM hostname [options]\n"
    "Options:\n"
    "  -user <username>\n"
    "  -pass <password>\n"
#ifdef _WIN32
    "  -noaudiocfg\n"
    "  -jesusonic <path to jesusonic root dir>\n"
#else
#ifdef _MAC
    "  -audiostr device_name[,output_device_name]\n"
#else
    "  -audiostr \"option value [option value ...]\"\n"
    "     ALSA audio options are:\n"
    "       in hw:0,0    -- set input device\n"
    "       out hw:0,0   -- set output device\n"
    "       srate 48000  -- set samplerate\n"
    "       nch 2        -- set channels\n"
    "       bps 16       -- set bits/sample\n"
    "       bsize 2048   -- set blocksize (bytes)\n"
    "       nblock 16    -- set number of blocks\n"
#endif
#endif

    "  -sessiondir <path>   -- sets the session directory (default: auto)\n"
    "  -savelocalwavs       -- save full quality copies of recorded files\n"
    "  -nosavesourcefiles   -- don't save source files for remixing\n"

    "  -writewav            -- writes a .wav of the jam in the session directory\n"
    "  -writeogg <bitrate>  -- writes a .ogg of the jam (bitrate 64-256)..\n");

  if (!noexit) exit(1);
}

int licensecallback(int user32, char *licensetext)
{
  /* todo, curses shit */

  int isscrolled=0;
  int linepos=0;
  int needref=1;
  int retval=0;
  while (!retval)
  {
    if (needref)
    {
	    bkgdset(COLORMAP(0));
	    attrset(COLORMAP(0));

      erase();
      char *tp=licensetext;
      needref=0;
	    bkgdset(COLORMAP(6));
	    attrset(COLORMAP(6));
      mvaddnstr(0,0,"You must agree to this license by scrolling down",COLS-1); clrtoeol();
      mvaddnstr(1,0,"and hitting Y to connect to this server:",COLS-1); clrtoeol();
	    bkgdset(COLORMAP(0));
	    attrset(COLORMAP(0));

      int x;
      for (x = 0; x < linepos; x ++)
      {
        int yp=0;
        while (*tp && *tp != '\n' && x < linepos)
        {
          if (yp++ >= COLS-1)
          {
            x++;
            yp=0;
          }
          tp++;
        }
        if (*tp) tp++;
      }
      for (x = 2; x < LINES-1 && *tp; x ++)
      {
        move(x,0);
        int yp=0;
        while (*tp && *tp != '\n' && x < LINES-1)
        {
          if (yp++ >= COLS-1)
          {
            x++;
            yp=0;
            move(x,0);
          }
          addch(*tp);
          tp++;
        }
        if (*tp) tp++;
      }
	    bkgdset(COLORMAP(5));
	    attrset(COLORMAP(5));

      if (*tp)
      {
        mvaddstr(LINES-1,0,"Use the arrows or pagedown to scroll down");
        clrtoeol();
        isscrolled=0;
      }
      else
      {
        mvaddstr(LINES-1,0,"Hit Y to agree");
        clrtoeol();
        isscrolled=1;
      }
	    bkgdset(COLORMAP(0));
	    attrset(COLORMAP(0));
    }

    int a=getch();
    switch (a)
    {
      case KEY_UP:
      case KEY_PPAGE:
        needref=1;
        linepos -= a == KEY_UP ? 1 : 10;
        if (linepos <0) linepos=0;
      break;
      case KEY_DOWN:
      case KEY_NPAGE:
        if (!isscrolled) linepos += a == KEY_DOWN ? 1 : 10;
        needref=1;
      break;
      case 'y':
      case 'Y':
        if (isscrolled) retval=1;
      break;
      case 27:
        retval=-1;
      break;
    };

#ifdef _WIN32
      MSG msg;
      while (PeekMessage(&msg,NULL,0,0,PM_REMOVE))
      {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
      }
      Sleep(1);
#else
	    struct timespec ts={0,1000*1000};
	    nanosleep(&ts,NULL);
#endif

  }

  showmainview();
  return retval>0;
}

int main(int argc, char **argv)
{
  char *parmuser=NULL;
  char *parmpass=NULL;
  WDL_String sessiondir;
  int sessionspec=0;
  int nolog=0,nowav=1,writeogg=0,g_nssf=0;

  printf("NINJAM v0.01a ALPHA curses client, compiled " __DATE__ " at " __TIME__ "\nCopyright (C) 2004-2005 Cockos, Inc.\n\n");
  char *audioconfigstr=NULL;
  g_client=new NJClient;
  g_client->config_savelocalaudio=1;
  g_client->LicenseAgreementCallback=licensecallback;
  g_client->ChatMessage_Callback=chatmsg_cb;

  char *hostname;

#if 1//def _MAC
  char hostbuf[512];
  if (argc < 2)
  {
    usage(1);
    printf("(no command line options specified, using interactive mode!)\n\n\nHost to connect to: ");
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-result"
    fgets(hostbuf,sizeof(hostbuf),stdin);
#pragma GCC diagnostic pop
    if (hostbuf[0] && hostbuf[strlen(hostbuf)-1] == '\n') hostbuf[strlen(hostbuf)-1]=0;
    hostname=hostbuf;
    if (!hostbuf[0]) return 0;
  }
#else
  if (argc < 2) usage();
#endif
  else hostname=argv[1];

  {
    int p;
    for (p = 2; p < argc; p++)
    {
      if (!stricmp(argv[p],"-savelocalwavs"))
      {
        g_client->config_savelocalaudio=2;
      }
      else if (!stricmp(argv[p],"-nosavelocal"))
      {
        g_client->config_savelocalaudio=0;
      }
      else if (!stricmp(argv[p],"-debuglevel"))
      {
        if (++p >= argc) usage();
        g_client->config_debug_level=atoi(argv[p]);
      }
      else if (!stricmp(argv[p],"-noaudiocfg"))
      {
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wwrite-strings"
        audioconfigstr="";
#pragma GCC diagnostic pop
      }
      else if (!stricmp(argv[p],"-audiostr"))
      {
        if (++p >= argc) usage();
        audioconfigstr=argv[p];
      }
      else if (!stricmp(argv[p],"-user"))
      {
        if (++p >= argc) usage();
        parmuser=argv[p];
      }
      else if (!stricmp(argv[p],"-pass"))
      {
        if (++p >= argc) usage();
        parmpass=argv[p];
      }
      else if (!stricmp(argv[p],"-writewav"))
      {
        nowav=0;
      }
      else if (!stricmp(argv[p],"-writeogg"))
      {
        if (++p >= argc) usage();
        writeogg=atoi(argv[p]);
      }
      else if (!stricmp(argv[p],"-nowritelog"))
      {
        nolog++;
      }
      else if (!stricmp(argv[p],"-nosavesourcefiles"))
      {
        g_nssf++;
      }
#ifdef _WIN32
      else if (!stricmp(argv[p],"-jesusonic"))
      {
        if (++p >= argc) usage();
        jesusdir.Set(argv[p]);
      }
#endif
      else if (!stricmp(argv[p],"-sessiondir"))
      {
        if (++p >= argc) usage();
        sessiondir.Set(argv[p]);
        sessionspec=1;
      }
      else usage();
    }
  }

  if (g_nssf)
  {
    g_client->config_savelocalaudio=0;
    nolog++;
  }

  char passbuf[512]="";
  char userbuf[512]="";
  if (!parmuser)
  {
    parmuser=userbuf;
    printf("Enter username: ");
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-result"
    fgets(userbuf,sizeof(userbuf),stdin);
#pragma GCC diagnostic pop
    if (userbuf[0] && userbuf[strlen(userbuf)-1] == '\n') userbuf[strlen(userbuf)-1]=0;
    if (!userbuf[0]) return 0;
  }
  if (!parmpass)
  {
    parmpass=passbuf;
    if (strncmp(parmuser,"anonymous",9) || (parmuser[9] && parmuser[9] != ':'))
    {
      printf("Enter password: ");
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-result"
      fgets(passbuf,sizeof(passbuf),stdin);
#pragma GCC diagnostic pop
      if (passbuf[0] && passbuf[strlen(passbuf)-1] == '\n') passbuf[strlen(passbuf)-1]=0;
    }
  }

#ifdef _WIN32
  g_audio=CreateConfiguredStreamer("ninjam.ini", !audioconfigstr, NULL);

#else
  {
    char *dev_name_in=audioconfigstr;
#ifdef _MAC
    g_audio=create_audioStreamer_CoreAudio(&dev_name_in,48000,2,16,audiostream_onsamples);
#else
    g_audio=create_audioStreamer_JACK("ninjam", 4, 4, audiostream_onsamples,g_client);
#endif
  }
#endif
  if (!g_audio)
  {
    printf("Error opening audio!\n");
    return 0;
  }
  printf("Opened at %dHz %d->%dch %dbps\n",
    g_audio->getSampleRate(), g_audio->getNInputChannels(), g_audio->getNOutputChannels(), g_audio->getBitDepth());

  signal(SIGINT,sigfunc);

  JNL::open_socketlib();

  // jesusonic init

#ifdef _WIN32
  HINSTANCE jesus_hDllInst;
  WDL_String jesusonic_configfile;
  if (jesusdir.Get()[0])
  {
    jesusonic_configfile.Set(jesusdir.Get());
    jesusonic_configfile.Append("\\cmdclient.jesusonicpreset");
    WDL_String dll;
    dll.Set(jesusdir.Get());
    dll.Append("\\jesus.dll");

    jesus_hDllInst = LoadLibrary(".\\jesus.dll"); // load from current dir
    if (!jesus_hDllInst) jesus_hDllInst = LoadLibrary(dll.Get());
    if (jesus_hDllInst)
    {
      *(void **)(&JesusonicAPI) = (void *)GetProcAddress(jesus_hDllInst,"JesusonicAPI");
      if (JesusonicAPI && JesusonicAPI->ver == JESUSONIC_API_VERSION_CURRENT)
      {
      }
      else JesusonicAPI = 0;
    }
  }

#endif
  // end jesusonic init

  {
    FILE *fp=fopen("ninjam.config","rt");
    int x=0;
    if (fp)
    {
      bool comment_state=false;
      while (!feof(fp))
      {
        char buf[4096];
        buf[0]=0;
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-result"
        fgets(buf,sizeof(buf),fp);
#pragma GCC diagnostic pop
        if (!buf[0]) continue;
        if (buf[strlen(buf)-1] == '\n')
          buf[strlen(buf)-1]=0;
        if (!buf[0]) continue;

        LineParser lp(comment_state);

        lp.parse(buf);

        switch (lp.gettoken_enum(0,"local\0master\0"))
        {
          case 0:
            // process local line
            if (lp.getnumtokens()>2)
            {
              int ch=lp.gettoken_int(1);
              int n;
              for (n = 2; n < lp.getnumtokens()-1; n += 2)
              {
                switch (lp.gettoken_enum(n,"source\0bc\0mute\0solo\0volume\0pan\0jesus\0name\0"))
                {
                  case 0: // source
                    g_client->SetLocalChannelInfo(ch,NULL,true,lp.gettoken_int(n+1),false,0,false,false);
                  break;
                  case 1: //broadcast
                    g_client->SetLocalChannelInfo(ch,NULL,false,false,false,0,true,!!lp.gettoken_int(n+1));
                  break;
                  case 2: //mute
                    g_client->SetLocalChannelMonitoring(ch,false,false,false,false,true,!!lp.gettoken_int(n+1),false,false);
                  break;
                  case 3: //solo
                    g_client->SetLocalChannelMonitoring(ch,false,false,false,false,false,false,true,!!lp.gettoken_int(n+1));
                  break;
                  case 4: //volume
                    g_client->SetLocalChannelMonitoring(ch,true,(float)lp.gettoken_float(n+1),false,false,false,false,false,false);
                  break;
                  case 5: //pan
                    g_client->SetLocalChannelMonitoring(ch,false,false,true,(float)lp.gettoken_float(n+1),false,false,false,false);
                  break;
                  case 6: //jesus
                    if (lp.gettoken_int(n+1))
                    {
#ifdef _WIN32
                      void *p=CreateJesusInstance(ch,"",g_audio->getSampleRate());
                      if (p) g_client->SetLocalChannelProcessor(ch,jesusonic_processor,p);
#endif
                    }
                  break;
                  case 7: //name
                    g_client->SetLocalChannelInfo(ch,lp.gettoken_str(n+1),false,false,false,0,false,false);
                  break;
                  default:
                  break;
                }
              }
            }

          break;
          case 1:
            if (lp.getnumtokens()>2)
            {
              int n;
              for (n = 1; n < lp.getnumtokens()-1; n += 2)
              {
                switch (lp.gettoken_enum(n,"mastervol\0masterpan\0metrovol\0metropan\0mastermute\0metromute\0"))
                {
                  case 0: // mastervol
                    g_client->config_mastervolume = (float)lp.gettoken_float(n+1);
                  break;
                  case 1: // masterpan
                    g_client->config_masterpan = (float)lp.gettoken_float(n+1);
                  break;
                  case 2:
                    g_client->config_metronome = (float)lp.gettoken_float(n+1);
                  break;
                  case 3:
                    g_client->config_metronome_pan = (float)lp.gettoken_float(n+1);
                  break;
                  case 4:
                    g_client->config_mastermute = !!lp.gettoken_int(n+1);
                  break;
                  case 5:
                    g_client->config_metronome_mute = !!lp.gettoken_int(n+1);
                  break;
                  default:
                  break;
                }
              }
            }
          break;
          default:
          break;
        }

      }
      fclose(fp);
    }
    else // set up defaults
    {
      g_client->SetLocalChannelInfo(0,"channel0",true,0,false,0,true,true);
      g_client->SetLocalChannelMonitoring(0,false,0.0f,false,0.0f,false,false,false,false);
    }
  }

  if (!sessiondir.Get()[0])
  {
    char buf[512];

    int cnt=0;
    while (cnt < 16)
    {
#if 0 // _WIN32
      SYSTEMTIME st;
      GetLocalTime(&st);
      wsprintf(buf,"%04d%02d%02d_%02d%02d",st.wYear,st.wMonth,st.wDay,st.wHour,st.wMinute);
#else
      time_t tv;
      time(&tv);
      struct tm *t=localtime(&tv);
      sprintf(buf,"%04d%02d%02d_%02d%02d",t->tm_year+1900,t->tm_mon+1,t->tm_mday,t->tm_hour,t->tm_min);
#endif
      if (cnt)
        wsprintf(buf+strlen(buf),"_%d",cnt);
      strcat(buf,".ninjam");

#ifdef _WIN32
      if (CreateDirectory(buf,NULL)) break;
#else
      if (!mkdir(buf,0700)) break;
#endif

      cnt++;
    }

    if (cnt >= 16)
    {
      printf("Error creating session directory\n");
      buf[0]=0;
      return 0;
    }

    sessiondir.Set(buf);
  }
  else
#ifdef _WIN32
    CreateDirectory(sessiondir.Get(),NULL);
#else
    mkdir(sessiondir.Get(),0700);
#endif
  if (sessiondir.Get()[0] && sessiondir.Get()[strlen(sessiondir.Get())-1]!='\\' && sessiondir.Get()[strlen(sessiondir.Get())-1]!='/')
#ifdef _WIN32
    sessiondir.Append("\\");
#else
    sessiondir.Append("/");
#endif

  g_client->SetWorkDir(sessiondir.Get());

  if (!nowav)
  {
    WDL_String wf;
    wf.Set(sessiondir.Get());
    wf.Append("output.wav");
    g_client->waveWrite = new WaveWriter(wf.Get(),24,g_audio->getNOutputChannels()>1?2:1,g_audio->getSampleRate());
  }
  if (writeogg)
  {
    WDL_String wf;
    wf.Set(sessiondir.Get());
    wf.Append("output.ogg");
    g_client->SetOggOutFile(fopen(wf.Get(),"ab"),g_audio->getSampleRate(),g_audio->getNOutputChannels()>1?2:1,writeogg);
  }
  if (!nolog)
  {
    WDL_String lf;
    lf.Set(sessiondir.Get());
    lf.Append("clipsort.log");
    g_client->SetLogFile(lf.Get());
  }

  printf("Connecting to %s...\n",hostname);
  g_client->Connect(hostname,parmuser,parmpass);
  g_audio_enable=1;

	// go into leet curses mode now
#ifdef _WIN32
	initscr(0);
#else
	initscr();
#endif
	cbreak();
	noecho();
	nonl();
	intrflush(stdscr,FALSE);
	keypad(stdscr,TRUE);
	nodelay(stdscr,TRUE);
	raw(); // disable ctrl+C etc. no way to kill if allow quit isn't defined, yay.

#ifndef _WIN32
	ESCDELAY=0; // dont wait--at least on the console this seems to work.
#endif

	if (has_colors()) // we don't use color yet, but we could
	{
		start_color();
		init_pair(1, COLOR_WHITE, COLOR_BLUE); // normal status lines
		init_pair(2, COLOR_BLACK, COLOR_CYAN); // value

#ifdef COLOR_BLUE_DIM
		init_pair(3, COLOR_WHITE, COLOR_BLUE_DIM); // alternating shit for the effect view
		init_pair(4, COLOR_WHITE, COLOR_RED_DIM);
#else

#if 0 // ok this aint gonna do shit for us :(
		if (can_change_color() && init_color(COLOR_YELLOW,0,0,150) && init_color(COLOR_MAGENTA,150,0,0))
		{
			init_pair(3, COLOR_WHITE, COLOR_YELLOW); // alternating shit for the effect view
			init_pair(4, COLOR_WHITE, COLOR_MAGENTA);
		}
		else
#endif

#ifdef VGA_CONSOLE
		char *term=getenv("TERM");
		if (term && !strcmp(term,"linux") && !ioperm(0x3C8,2,1))
		{
			init_pair(3, COLOR_WHITE, COLOR_YELLOW); // alternating shit for the effect view
			init_pair(4, COLOR_WHITE, COLOR_MAGENTA);
			set_pal(6,0,0,15);
			set_pal(5,15,0,0);
		}
		else
#endif
		{
			init_pair(3, COLOR_WHITE, COLOR_BLUE); // alternating shit for the effect view
			init_pair(4, COLOR_WHITE, COLOR_RED);
		}
#endif
		init_pair(5, COLOR_BLACK, COLOR_WHITE);
		init_pair(6, COLOR_WHITE, COLOR_RED);
		int x;
		for (x = 1; x < 8; x ++)
			color_map[x]=COLOR_PAIR(x);

	}
#ifndef _WIN32
	else
	{
//		color_map[1]=A_BOLD;
		color_map[2]=A_STANDOUT;
		color_map[5]=A_STANDOUT;
	}
#endif

  showmainview();
	refresh();

#ifdef _WIN32
  DWORD nextupd=GetTickCount()+250;
#else
  time_t nextupd=time(NULL)+1;
#endif

  while (g_client->GetStatus() >= 0 && !g_done
#ifdef _WIN32
  && IsWindow(CURSES_INSTANCE->cursesCtx.m_hwnd)
#endif

    )
  {
    if (g_client->Run())
    {
#ifdef _WIN32
      MSG msg;
      while (PeekMessage(&msg,NULL,0,0,PM_REMOVE))
      {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
      }
      Sleep(1);
#else
	struct timespec ts={0,1000*1000};
	nanosleep(&ts,NULL);
#endif

      int a=getch();
#ifdef _MAC
		{
			static timeval last_t;
			static int stage;
			timeval now;
			gettimeofday(&now,NULL);
			if (a != ERR || (stage &&
				  ((long long) (((now.tv_sec-last_t.tv_sec) * 1000) + ((now.tv_usec-last_t.tv_usec)/1000)))>333

				))
			{
				last_t = now;
				if (!stage && a == 27) { a=ERR; stage++; }
				else if (stage==1 && a == 79) { a=ERR; stage++; }
				else if (stage==2 && a >= 80 && a <= 91)
				{
					a = KEY_F(a-79);
					stage=0;
				}
				else if (stage) { a = 27; stage=0; }
			}
		}
		if (a == KEY_F(7)) a = KEY_F(11);
		if (a == KEY_F(8)) a = KEY_F(12);
#endif
		if (a == 127) a = KEY_BACKSPACE;
      if (a!=ERR)
      {
        if (!g_ui_state) switch (a)
        {
          case KEY_LEFT:
            if (g_sel_x > 0)
            {
              g_sel_x--;
              showmainview();
            }
          break;
          case KEY_RIGHT:
            {
              g_sel_x++;
              showmainview();
            }
          break;
          case KEY_UP:
            showmainview(false,-1);
          break;
          case KEY_DOWN:
            showmainview(false,1);
          break;
          case '\r': case ' ':
            if (!g_ui_inchat)
            {
              showmainview(true);
              break;
            }
          default:
            if (g_ui_inchat)
            {
              switch (a)
              {
                case KEY_PPAGE:
                  g_chat_scroll+=LINES/4-2;
                  showmainview();
                break;
                case KEY_NPAGE:
                  g_chat_scroll-=LINES/4-2;
                  if (g_chat_scroll<0)g_chat_scroll=0;
                  showmainview();
                break;
                case '\r':
                  if (m_chatinput_str[0])
                  {
                    if (m_chatinput_str[0] == '/')
                    {
                      if (!strncasecmp(m_chatinput_str,"/me ",4))
                      {
                        g_client->ChatMessage_Send("MSG",m_chatinput_str);
                      }
                      else if (!strncasecmp(m_chatinput_str,"/topic ",7)||
                               !strncasecmp(m_chatinput_str,"/kick ",6) ||
                               !strncasecmp(m_chatinput_str,"/bpm ",5) ||
                               !strncasecmp(m_chatinput_str,"/bpi ",5)
                        ) // alias to /admin *
                      {
                        g_client->ChatMessage_Send("ADMIN",m_chatinput_str+1);
                      }
                      else if (!strncasecmp(m_chatinput_str,"/admin ",7))
                      {
                        char *p=m_chatinput_str+7;
                        while (*p == ' ') p++;
                        g_client->ChatMessage_Send("ADMIN",p);
                      }
                      else if (!strncasecmp(m_chatinput_str,"/msg ",5))
                      {
                        char *p=m_chatinput_str+5;
                        while (*p == ' ') p++;
                        char *n=p;
                        while (*p && *p != ' ') p++;
                        if (*p == ' ') *p++=0;
                        while (*p == ' ') p++;
                        if (*p)
                        {
                          g_client->ChatMessage_Send("PRIVMSG",n,p);
                          WDL_String tmp;
                          tmp.Set("-> *");
                          tmp.Append(n);
                          tmp.Append("* ");
                          tmp.Append(p);
                          addChatLine(NULL,tmp.Get());
                        }
                        else
                        {
                          addChatLine("","error: /msg requires a username and a message.");
                        }
                      }
                      else
                      {
                        addChatLine("","error: unknown command.");
                      }
                    }
                    else
                    {
                      g_client->ChatMessage_Send("MSG",m_chatinput_str);
                    }

                    m_chatinput_str[0]=0;
                    showmainview();
                  }
                break;
                case 27:
                  {
                    m_chatinput_str[0]=0;
                    showmainview();
                  }
                break;
					      case KEY_BACKSPACE:
                  if (m_chatinput_str[0]) m_chatinput_str[strlen(m_chatinput_str)-1]=0;
                  showmainview();
					      break;
                default:
                  if (VALIDATE_TEXT_CHAR(a))
						      {
							      int l=strlen(m_chatinput_str);
							      if (l < (int)sizeof(m_chatinput_str)-1) { m_chatinput_str[l]=a; m_chatinput_str[l+1]=0; }
                    showmainview();
						      }
                break;
              }
            }
          break;
        }
        else if (g_ui_state == 1)
        {
          switch (a)
          {
            case KEY_LEFT:
            case KEY_RIGHT:
              {
                float pan;
                int ok=0;
                if (g_ui_voltweakstate_channel == -2) { ok=1; pan=(float)g_client->config_masterpan; }
                else if (g_ui_voltweakstate_channel == -1) { pan=(float)g_client->config_metronome_pan; ok=1; }
                else if (g_ui_voltweakstate_channel >= 1024)
                  ok=!!g_client->GetUserChannelState((g_ui_voltweakstate_channel-1024)/64,g_ui_voltweakstate_channel%64, NULL,NULL,&pan,NULL);
                else ok=!g_client->GetLocalChannelMonitoring(g_ui_voltweakstate_channel,NULL,&pan,NULL,NULL);

                if (ok)
                {
                  pan += a == KEY_LEFT ? -0.01f : 0.01f;
                  if (pan > 1.0f) pan=1.0f;
                  else if (pan < -1.0f) pan=-1.0f;
                  if (g_ui_voltweakstate_channel == -2) g_client->config_masterpan=pan;
                  else if (g_ui_voltweakstate_channel == -1) g_client->config_metronome_pan=pan;
                  else if (g_ui_voltweakstate_channel>=1024)
                    g_client->SetUserChannelState((g_ui_voltweakstate_channel-1024)/64,g_ui_voltweakstate_channel%64, false,false,false,0.0f,true,pan,false,false,false,false,false,0,false,false);
                  else
                    g_client->SetLocalChannelMonitoring(g_ui_voltweakstate_channel,false,0.0f,true,pan,false,false,false,false);
                  showmainview();
                }
              }
            break;
            case KEY_PPAGE:
            case KEY_UP:
            case KEY_NPAGE:
            case KEY_DOWN:
              {
                float vol;
                int ok=0;
                if (g_ui_voltweakstate_channel == -2) { ok=1; vol=(float)g_client->config_mastervolume; }
                else if (g_ui_voltweakstate_channel == -1) { vol=(float)g_client->config_metronome; ok=1; }
                else if (g_ui_voltweakstate_channel >= 1024)
                  ok=!!g_client->GetUserChannelState((g_ui_voltweakstate_channel-1024)/64,g_ui_voltweakstate_channel%64, NULL,&vol,NULL,NULL,NULL);
                else ok=!g_client->GetLocalChannelMonitoring(g_ui_voltweakstate_channel,&vol,NULL,NULL,NULL);

                if (ok)
                {
                  vol=(float) VAL2DB(vol);
                  float sc=a == KEY_PPAGE || a == KEY_NPAGE? 4.0f : 0.5f;
                  if (a == KEY_DOWN || a == KEY_NPAGE) sc=-sc;
                  vol += sc;
                  if (vol > 20.0f) vol=20.0f;
                  else if (vol < -120.0f) vol=-120.0f;
                  vol=(float) DB2VAL(vol);
                  if (g_ui_voltweakstate_channel == -2) g_client->config_mastervolume=vol;
                  else if (g_ui_voltweakstate_channel == -1) g_client->config_metronome=vol;
                  else if (g_ui_voltweakstate_channel>=1024)
                    g_client->SetUserChannelState((g_ui_voltweakstate_channel-1024)/64,g_ui_voltweakstate_channel%64, false,false,true,vol,false,0.0f,false,false,false,false,false,0,false,false);
                  else
                    g_client->SetLocalChannelMonitoring(g_ui_voltweakstate_channel,true,vol,false,0.0f,false,false,false,false);
                  showmainview();
                }
              }
            break;
            case 27: case '\r':
              {
                g_ui_state=0;
                showmainview();
              }
            break;
          }
        }
        else if (g_ui_state == 3)
        {
          switch (a)
          {
            case KEY_PPAGE:
            case KEY_UP:
            case KEY_LEFT:
              {
                int ch=0;
                g_client->GetLocalChannelInfo(g_ui_locrename_ch,&ch,NULL,NULL);
                if (ch > 0)
                {
                  ch--;
                  g_client->SetLocalChannelInfo(g_ui_locrename_ch,NULL,true,ch,false,0,false,false);
                  g_client->NotifyServerOfChannelChange();
                  showmainview();
                }
              }
            break;
            case KEY_NPAGE:
            case KEY_DOWN:
            case KEY_RIGHT:
              {
                int ch=0;
                g_client->GetLocalChannelInfo(g_ui_locrename_ch,&ch,NULL,NULL);
                if (ch < g_audio->getNInputChannels())
                {
                  ch++;
                  g_client->SetLocalChannelInfo(g_ui_locrename_ch,NULL,true,ch,false,0,false,false);
                  g_client->NotifyServerOfChannelChange();
                  showmainview();
                }
              }
            break;

            case 27: case '\r':
              {
                g_ui_state=0;
                showmainview();
              }
            break;
          }
        }
        else if (g_ui_state == 2 || g_ui_state == 4)
        {
          switch (a)
          {
            case '\r':
              if (m_lineinput_str[0])
              {
                if (g_ui_state == 4)
                {
                  g_client->SetLocalChannelInfo(g_ui_locrename_ch,m_lineinput_str,false,0,false,0,false,false);
                  g_client->NotifyServerOfChannelChange();
                }
              }
              g_ui_state=0;
              showmainview();
            break;
            case 27:
              {
                g_ui_state=0;
                showmainview();
              }
            break;
					  case KEY_BACKSPACE:
              if (m_lineinput_str[0]) m_lineinput_str[strlen(m_lineinput_str)-1]=0;
              showmainview();
              g_ui_state=4;
					  break;
            default:
              if (VALIDATE_TEXT_CHAR(a) && (g_ui_state != 3 || (a >= '0' && a <= '9'))) //fucko: 9 once we have > 2ch
						  {
							  int l=strlen(m_lineinput_str);
                if (g_ui_state == 2)
                {
                  l=0;
                  g_ui_state=4;
                }

							  if (l < (int)sizeof(m_lineinput_str)-1) { m_lineinput_str[l]=a; m_lineinput_str[l+1]=0; }
                showmainview();
						  }
            break;
          }
        }
      }

      if (g_ui_state < 2 && (g_need_disp_update||g_client->HasUserInfoChanged()||
#ifdef _WIN32
GetTickCount()>=nextupd
#else
time(NULL) >= nextupd
#endif

))
      {
#ifdef _WIN32
        nextupd=GetTickCount()+1000;
#else
        nextupd=time(NULL)+1;
#endif
        g_need_disp_update=0;
        showmainview();
      }
      else drawstatusbar();
    }

  }

	erase();
	refresh();

	// shut down curses
	endwin();

  switch (g_client->GetStatus())
  {
    case NJClient::NJC_STATUS_OK:
    break;
    case NJClient::NJC_STATUS_INVALIDAUTH:
      printf("ERROR: invalid login/password\n");
    break;
    case NJClient::NJC_STATUS_CANTCONNECT:
      printf("ERROR: failed connecting to host\n");
    break;
    case NJClient::NJC_STATUS_PRECONNECT:
      printf("ERROR: failed connect\n");
    break;
    case NJClient::NJC_STATUS_DISCONNECTED:
      printf("ERROR: disconnected from host\n");
    break;

    default:
      printf("exiting on status %d\n",g_client->GetStatus());
    break;
  }
  if (g_client->GetErrorStr()[0])
  {
    printf("Server gave explanation: %s\n",g_client->GetErrorStr());
  }

  printf("Shutting down\n");

  delete g_audio;

  delete g_client->waveWrite;
  g_client->waveWrite=0;

  // save local channel state
  {
    FILE *fp=fopen("ninjam.config","wt");
    int x=0;
    if (fp)
    {
      fprintf(fp,"master mastervol %f masterpan %f metrovol %f metropan %f mastermute %d metromute %d\n",
        g_client->config_mastervolume,g_client->config_masterpan,g_client->config_metronome,g_client->config_metronome_pan,
        g_client->config_mastermute,g_client->config_metronome_mute);

      for (x = 0;;x++)
      {
        int a=g_client->EnumLocalChannels(x);
        if (a<0) break;

        int sch=0;
        bool bc=0;
        void *has_jesus=0;
        char *lcn;
        float v=0.0f,p=0.0f;
        bool m=0,s=0;

        lcn=g_client->GetLocalChannelInfo(a,&sch,NULL,&bc);
        g_client->GetLocalChannelMonitoring(a,&v,&p,&m,&s);
        g_client->GetLocalChannelProcessor(a,NULL,&has_jesus);

        char *ptr=lcn;
        while (*ptr)
        {
          if (*ptr == '`') *ptr='\'';
          ptr++;
        }
        fprintf(fp,"local %d source %d bc %d mute %d solo %d volume %f pan %f jesus %d name `%s`\n",a,sch,bc,m,s,v,p,!!has_jesus,lcn);
      }
      fclose(fp);
    }
  }

  // delete all effects processors in g_client
  {
    int x=0;
    for (x = 0;;x++)
    {
      int a=g_client->EnumLocalChannels(x);
      if (a<0) break;
#ifdef _WIN32
      void *i=0;
      g_client->GetLocalChannelProcessor(a,NULL,&i);
      if (i) deleteJesusonicProc(i,a);
      g_client->SetLocalChannelProcessor(a,NULL,NULL);
#endif
    }
  }

  delete g_client;

#ifdef _WIN32
  ///// jesusonic stuff
  if (jesus_hDllInst) FreeLibrary(jesus_hDllInst);
  jesus_hDllInst=0;
  JesusonicAPI=0;

#endif

  if (g_nssf)
  {
    int n;
    for (n = 0; n < 16; n ++)
    {
      WDL_String s(sessiondir.Get());
      char buf[32];
      sprintf(buf,"%x",n);
      s.Append(buf);

      {
        WDL_DirScan ds;
        if (!ds.First(s.Get()))
        {
          do
          {
            if (ds.GetCurrentFN()[0] != '.')
            {
              WDL_String t;
              ds.GetCurrentFullFN(&t);
              unlink(t.Get());
            }
          }
          while (!ds.Next());
        }
      }
#ifdef _WIN32
      RemoveDirectory(s.Get());
#else
      rmdir(s.Get());
#endif
    }
  }
  if (!sessionspec)
  {
#ifdef _WIN32
      RemoveDirectory(sessiondir.Get());
#else
      rmdir(sessiondir.Get());
#endif

  }

  JNL::close_socketlib();
  return 0;
}


code:
master mastervol 1.000000 masterpan 0.000000 metrovol 0.070154 metropan 0.220000 mastermute 0 metromute 1
local 0 source 0 bc 1 mute 0 solo 0 volume 1.000001 pan -0.000001 jesus 0 name `channel`
local 1 source 1 bc 1 mute 0 solo 0 volume 1.000000 pan 0.000000 jesus 0 name `channel`