code:
/*
    NINJAM Mac OS X Clients - audiostream_mac.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 file implements the AudioStreamer subclass for CoreAudio.
|*|   * audioStreamer_CoreAudio // audioStreamer subclass
|*| Instantiation is exposed only through class methods of the AudioStreamer superclass.
|*|   * audioStreamer::NewCA()
\*/

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>

#include <signal.h>
#include <unistd.h>
#include <fcntl.h>

#include "audiostream.h"

#include </System/Library/Frameworks/CoreAudio.framework/Headers/AudioHardware.h>

/* audioStreamer public class constants */

const std::string audioStreamer::DEFAULT_CA_INPUT_NAME  = "" ;
const std::string audioStreamer::DEFAULT_CA_OUTPUT_NAME = "" ;
const char*       audioStreamer::CA_ARGS_FMT            = "%s" ;

/* audioStreamer public class variables */

SPLPROC audioStreamer::OnSamples                    = NULL ;

class audioStreamer_CoreAudio  : public audioStreamer
{
	public:

		audioStreamer_CoreAudio();
		~audioStreamer_CoreAudio();
		int Open(char **dev, int srate, int nch, int bps);
		int Read(char *buf, int len); // returns 0 if blocked, < 0 if error, > 0 if data
		int Write(char *buf, int len); // returns 0 on success
    const char *GetChannelName(int idx)
	{
		if (idx < 0 || idx >= m_innch) return NULL;
		static char buf[128];
		sprintf(buf,"Channel %d",idx+1);
		return buf;
	}

	private:

	        AudioDeviceID m_myDev_i;
	        AudioDeviceID m_myDev_o;
 		int m_started;

};

#include <sys/stat.h>
#include <pthread.h>

#include <WDL/queue.h>

static int outchtab[2]={0,1};

static int g_srate;

// this is a total hack until I spend the time and make a good multichannel CoreAudio implementation.
static WDL_HeapBuf spltemp;

// this takes the interleaved samples in, and puts them in their own buffers,
// and processes, then reinterleaves
static void onsamples_old(float *inbuf, int innch, float *outbuf, int outnch, int nsamples, int srate)
{
  float **inptrs = (float **)alloca(sizeof(float *)*innch);
  int sz=nsamples*sizeof(float)*(innch+2);
  if (spltemp.GetSize() < sz) spltemp.Resize(sz);
  int x;
  float *t=(float*)spltemp.Get();
  for (x = 0; x < innch; x ++)
  {
    float *s=inbuf+x;
    inptrs[x]=t;
    int y=nsamples;
    while (y--)
    {
      *t++ = *s;
      s += innch;
    }
  }
  float *outptrs[2]={t,t+nsamples};

  if (!!audioStreamer::OnSamples) audioStreamer::OnSamples(inptrs , innch , outptrs , 2 , nsamples , srate) ;

  float *p1=outptrs[0];
  float *p2=outptrs[1];
  x=nsamples;
  if (outnch > 0)
  {
    while (x--)
    {
      outbuf[outchtab[0]]=*p1++;
      if (outnch > 1) outbuf[outchtab[1]]=*p2++;
      outbuf += outnch;
    }
  }
  else
  {
    outnch=-outnch;
    while (x--)
    {
      outbuf[0]=*p1++;
      if (outnch > 1) outbuf[1]=*p2++;
      outbuf += outnch;
    }
  }

}

static pthread_mutex_t m_mutex;
static WDL_Queue m_splbuf;
static int inchbuf=0;
static int outchbuf=0;
static float *ca_tmpbuf;
static int ca_tmpbuf_size;

OSStatus caIOproc(AudioDeviceID dev,
			const AudioTimeStamp* inNow,
			const AudioBufferList* inInputData,
			const AudioTimeStamp* inInputTime,
			AudioBufferList* outOutputData,
			const AudioTimeStamp* inOutputTime,
			void* inClientData)
{
  // process inInputData to outOutputData
  if (inInputData && outOutputData)
  {
     int in_size=inInputData->mBuffers[inchbuf].mDataByteSize;
     char *in=(char *)inInputData->mBuffers[inchbuf].mData;
     int in_nch = inInputData->mBuffers[inchbuf].mNumberChannels;

     int out_size=outOutputData->mBuffers[outchbuf].mDataByteSize;
     char *out=(char *)outOutputData->mBuffers[outchbuf].mData;
     int out_nch = outOutputData->mBuffers[outchbuf].mNumberChannels;

     if (in_size*out_nch == out_size*in_nch) // faster than a divide
     {
	int needsize=((in_size/in_nch) * 2);
        if (!ca_tmpbuf || ca_tmpbuf_size <  needsize) ca_tmpbuf=(float*)realloc(ca_tmpbuf,ca_tmpbuf_size=needsize);
	if (ca_tmpbuf)
	{
        	int c=in_size/(sizeof(float)*in_nch);
        	onsamples_old((float*)in,in_nch,(float *)out,out_nch,c,g_srate);
	}
     }
  }
  return 0;
}

OSStatus caInproc(AudioDeviceID dev,
			const AudioTimeStamp* inNow,
			const AudioBufferList* inInputData,
			const AudioTimeStamp* inInputTime,
			AudioBufferList* outOutputData,
			const AudioTimeStamp* inOutputTime,
			void* inClientData)
{
  // process inInputData to outOutputData
  if (inInputData)
  {
     int in_size=inInputData->mBuffers[inchbuf].mDataByteSize;
     char *in=(char *)inInputData->mBuffers[inchbuf].mData;
     int in_nch = inInputData->mBuffers[inchbuf].mNumberChannels;

     {
	int needsize=((in_size/in_nch) * 2);
        if (!ca_tmpbuf || ca_tmpbuf_size <  needsize) ca_tmpbuf=(float*)realloc(ca_tmpbuf,ca_tmpbuf_size=needsize);
	if (ca_tmpbuf)
	{
		if (m_splbuf.GetSize() < 48000*8)
                {
        	int c=in_size/(sizeof(float)*in_nch);
        	onsamples_old((float*)in,in_nch,(float *)ca_tmpbuf,-2,c,g_srate);

		pthread_mutex_lock(&m_mutex);

		m_splbuf.Add(ca_tmpbuf,needsize);

		pthread_mutex_unlock(&m_mutex);
		}
	}
     }
  }
  return 0;
}

OSStatus caOutproc(AudioDeviceID dev,
			const AudioTimeStamp* inNow,
			const AudioBufferList* inInputData,
			const AudioTimeStamp* inInputTime,
			AudioBufferList* outOutputData,
			const AudioTimeStamp* inOutputTime,
			void* inClientData)
{
  // process inInputData to outOutputData
  if (outOutputData)
  {
     int out_size=outOutputData->mBuffers[outchbuf].mDataByteSize;
     char *out=(char *)outOutputData->mBuffers[outchbuf].mData;
     int out_nch = outOutputData->mBuffers[outchbuf].mNumberChannels;

     pthread_mutex_lock(&m_mutex);
     if (out_size < m_splbuf.Available())
     {
		float *fin=(float *)m_splbuf.Get();
		float *fout=(float *)out;
        	int x,c=out_size/(sizeof(float)*out_nch);
		for (x = 0; x < c; x ++)
		{
			fout[outchtab[0]]=*fin++;
			fout[outchtab[1]]=*fin++;
			fout += out_nch;
		}
		m_splbuf.Advance(out_size);
		m_splbuf.Compact();
     }
     pthread_mutex_unlock(&m_mutex);
  }
  return 0;
}

audioStreamer_CoreAudio::audioStreamer_CoreAudio()
{
        m_myDev_i=0;
        m_myDev_o=0;
    	m_started=0;
}

audioStreamer_CoreAudio::~audioStreamer_CoreAudio()
{
 	if (m_started)
        {
		if (m_myDev_o != m_myDev_i)
		{
			AudioDeviceStop(m_myDev_i,caInproc);
	                AudioDeviceRemoveIOProc(m_myDev_i,caInproc);
			AudioDeviceStop(m_myDev_o,caOutproc);
                	AudioDeviceRemoveIOProc(m_myDev_o,caOutproc);
		}
		else
		{
			AudioDeviceStop(m_myDev_i,caIOproc);
	                AudioDeviceRemoveIOProc(m_myDev_i,caIOproc);
		}
        }

}

int matchlen(const char *sub, const char *pa)
{
  int l=0;
  while (*sub && *pa && toupper(*sub) == toupper(*pa)) { sub++; pa++; l++; }
  return l;
}

int audioStreamer_CoreAudio::Open(char **dev, int srate, int nch, int bps)
{
  pthread_mutex_init(&m_mutex,NULL);
  char *olddev= *dev;
	m_srate=g_srate=srate;
        m_bps=33;
        m_innch=m_outnch=2;
#ifndef AUDIOSTREAMER_NO_CONSOLEUI
  char user_buf[512];
#endif

	UInt32 theSize;
	int s = AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices, &theSize, NULL );
        int theNumberDevices = theSize / sizeof(AudioDeviceID);
        if (!theNumberDevices)
        {
          printf("No CoreAudio devices found!\n");
          return -1;
        }
        AudioDeviceID *list=(AudioDeviceID *)malloc(sizeof(AudioDeviceID)*theNumberDevices);
        AudioHardwareGetProperty(kAudioHardwarePropertyDevices,&theSize,list);

again:

        char *indev_ptr=olddev?olddev:(char *)"";
        char *outdev_ptr=strstr(indev_ptr,",");
        if (outdev_ptr)
        {
          *outdev_ptr++=0;
          while (*outdev_ptr == ' ') outdev_ptr++;
          if (!*outdev_ptr) outdev_ptr=indev_ptr;
        } else outdev_ptr=indev_ptr;

        int outm=0,inm=0;
        printf("CoreAudio device list:\n");
        for (s = 0; s < theNumberDevices; s ++)
        {
          AudioDeviceID myDev;
          myDev = list[s];
          UInt32 os=0;
          Boolean ow;
          AudioDeviceGetPropertyInfo(myDev,0,0,kAudioDevicePropertyDeviceName,&os,&ow);
          if (os > 0)
          {
            char *buf=(char *)malloc(os+1);

            AudioDeviceGetProperty(myDev,0,0,kAudioDevicePropertyDeviceName,&os,buf);
            if (os > 0)
		{
			int flags=0;
	    		int i;

			for (i = 0; i <2; i ++)
			{
  			    UInt32 nos=0; Boolean now;
		            AudioDeviceGetPropertyInfo(myDev,0,i,kAudioDevicePropertyStreamConfiguration,&nos,&now);
		            if (nos>=sizeof(AudioBufferList))
		            {
		               AudioBufferList *buf2=(AudioBufferList *)malloc(nos);
		               AudioDeviceGetProperty(myDev,0,i,kAudioDevicePropertyStreamConfiguration,&nos,buf2);
		               if (nos>=sizeof(AudioBufferList))
		               {
		                 flags |= 1<<i;
		 	       }
		             	free(buf2);
		             }
		          }
                          int ml=(flags & 2) ? matchlen(indev_ptr,buf) : 0;
                          if (ml > inm) { inm=ml; m_myDev_i = myDev; }
                          ml=(flags & 1) ? matchlen(outdev_ptr,buf) : 0;
                          if (ml > outm) { outm=ml; m_myDev_o = myDev; }

 			  printf("  '%s' %s%s%s",buf,flags&2?"Input":"",flags==3?"/":"",flags&1?"Output":"");

		}

            printf("\n");
            free(buf);
          }
        }

        if (!m_myDev_i || !m_myDev_o)
        {
    #ifndef AUDIOSTREAMER_NO_CONSOLEUI
        printf("Type in the beginning of the name of your sound hardware now (or leave blank for system defaults)\n");
        printf("Note: to specify different input/output hardware, use device1, device2\n");
        printf("Choice: ");
        fflush(stdout);
        user_buf[0]=0;
        fgets(user_buf,sizeof(user_buf),stdin);
        olddev=user_buf;
        if (user_buf[0] && user_buf[0] != '\r'  && user_buf[0] != '\n')
        {
		goto again;
        }
    #endif
        UInt32 theSize=sizeof(AudioDeviceID);
        if (!m_myDev_i) AudioHardwareGetProperty(kAudioHardwarePropertyDefaultInputDevice,&theSize,&m_myDev_i);
        theSize=sizeof(AudioDeviceID);
        if (!m_myDev_o) AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice,&theSize,&m_myDev_o);
	}

        free(list);

        int isinput;
        for (isinput=0;isinput<2;isinput++)
        {
          AudioDeviceID myDev = isinput ? m_myDev_i : m_myDev_o;

          UInt32 os=0;
          Boolean ow;
	  AudioStreamBasicDescription d={0,};
	  os=sizeof(d);
          AudioDeviceGetProperty(myDev,0,isinput,kAudioDevicePropertyStreamFormat,&os,&d);
	  if (os > 0)
  	  {
		d.mSampleRate=srate;
		os=sizeof(d);
//          	AudioDeviceSetProperty(myDev,NULL,0,isinput,kAudioDevicePropertyStreamFormat,os,&d);
          	AudioDeviceGetProperty(myDev,0,isinput,kAudioDevicePropertyStreamFormat,&os,&d);
		if (os>0) g_srate=m_srate=(int)d.mSampleRate;
 	 }
          AudioDeviceGetPropertyInfo(myDev,0,isinput,kAudioDevicePropertyStreamConfiguration,&os,&ow);
          if (os > 0)
          {
             AudioBufferList *buf=(AudioBufferList *)malloc(os);
             AudioDeviceGetProperty(myDev,0,isinput,kAudioDevicePropertyStreamConfiguration,&os,buf);
             int x;
             for (x = 0; x < (int)(os/sizeof(AudioBufferList)); x ++)
             {
                printf("  %s Channel %d: %d buffers\n",isinput?"Input":"Output",x,(int) buf[x].mNumberBuffers);
                int y;
                for (y = 0; y < (int)buf[x].mNumberBuffers; y++)
		{
                   if (buf[x].mBuffers[y].mNumberChannels)
                      printf("    buffer %d: %d channels\n",y,(int)buf[x].mBuffers[y].mNumberChannels);
		   if (y == inchbuf && !x && buf[x].mBuffers[y].mNumberChannels)
			m_innch = buf[x].mBuffers[y].mNumberChannels;
		}
		break;
             }

             free(buf);
          }
	  if (os < sizeof(AudioBufferList))
	  {
		printf("Device has no %s buffers! Invalid device?\n",isinput ? "Input" : "Output");
		return -1;
	  }
       }

       m_started=1;
       if (m_myDev_o != m_myDev_i)
 	{
       		AudioDeviceAddIOProc(m_myDev_i,caInproc,(void *)this);
       		AudioDeviceAddIOProc(m_myDev_o,caOutproc,(void *)this);

       		AudioDeviceStart(m_myDev_i,caInproc);
 	        AudioDeviceStart(m_myDev_o,caOutproc);
	}
	else
	{
       		AudioDeviceAddIOProc(m_myDev_i,caIOproc,(void *)this);
       		AudioDeviceStart(m_myDev_i,caIOproc);
	}

       return 0;

}

int audioStreamer_CoreAudio::Read(char *buf, int len) // returns 0 if blocked, < 0 if error, > 0 if data
{
   struct timespec s={0,1000*1000*10}; // sleep 10ms;
   nanosleep(&s,NULL);
//   memset(buf,0,len);
   return 0;//len;
}
int audioStreamer_CoreAudio::Write(char *buf, int len) // returns 0 on success
{
	return 0;
}

/* audioStreamer public constructors */

audioStreamer* audioStreamer::NewCA(SPLPROC     on_samples_cb                             ,
                                    std::string input_device  , std::string output_device ,
                                    int         n_channels    , int         sample_rate   ,
                                    int         bit_depth                                 )
{
// TODO: the CA "constructor" requires writable memory for device names
//         but we are ignoring the result for now
//       these should be instance vars with public accessors

  OnSamples = on_samples_cb ;

  const int n_bytes    = (NINJAM::MAX_DEVICE_NAME_LEN * 2) + 1 ; // 2 devs + comma
  char  cli_args_buffer[n_bytes] ;
  char* cli_args       = cli_args_buffer ;

  if (output_device.empty?) output_device = ',' + output_device ;
  snprintf(cli_args_buffer , n_bytes , CA_ARGS_FMT , input_device .c_str() ,
                                                     output_device.c_str() ) ;

  audioStreamer_CoreAudio* ca_streamer = new audioStreamer_CoreAudio() ;

  if (ca_streamer->Open(&cli_args , sample_rate , n_channels , bit_depth))
  {
    delete ca_streamer ; return NULL ;
  }

  return ca_streamer ;
}


code:
/*
  This file provides the basic code for loading a pre-compiled binary
      NINJAM<->ASIO interface 'NJASIODRV.DLL' if it is exists.
  The code for that interface is a separate project (in 'ninjam/njasiodrv')
      because it requires headers from the ASIO SDK which are not GPL'ed.
*/

#ifndef NO_SUPPORT_ASIO

#include "audiostream.h"

#include <strsafe.h>

/* audioStreamer private class constants */

const std::string audioStreamer::ASIO_DLL_FILENAME     = "njasiodrv.dll" ;
const LPCSTR      audioStreamer::ASIO_CONSTRUCTOR_NAME = "create_asio_streamer" ;
const LPCTSTR     audioStreamer::ASIO_ARGS_FMT         = "%d%c:%d,%d:%d,%d" ;

/* audioStreamer private class variables */

HINSTANCE   audioStreamer::NjasiodrvDll ;   // LoadNjasiodrvDll()
std::string audioStreamer::ASIODriverName ; // NewASIO()

/* audioStreamer public constructors */

audioStreamer* audioStreamer::NewASIO(SPLPROC on_samples_cb       , int asio_driver_n      ,
                                      int     input_channel_b_n   , int input_channel_e_n  ,
                                      int     output_channel_b_n  , int output_channel_e_n ,
                                      bool    should_show_asio_cp                          )
{
/* cli_args format - 'c' forces control panel open - the rest are ints like:
       [ driverN [ + 'c' ] [ + ':' + input_ch_begin_n  [ + ',' + input_ch_end_n  ] ]
                           [ + ':' + output_ch_begin_n [ + ',' + output_ch_end_n ] ] ]
   CreateASIO() modifies cli_args - on return cli_args will point to driver name */

  const int n_bytes    = NINJAM::MAX_DEVICE_NAME_LEN ;
  char  cli_args_buffer[n_bytes] ;
  char* cli_args       = cli_args_buffer ;
  char  should_show_cp = (should_show_asio_cp ) ? 'c' : ' ' ;

  if (!IsNjasiodrvAvailable()                                                        ||
      FAILED(StringCbPrintf(cli_args_buffer    , n_bytes            , ASIO_ARGS_FMT ,
                            asio_driver_n      , should_show_cp                     ,
                            input_channel_b_n  , input_channel_e_n                  ,
                            output_channel_b_n , output_channel_e_n                 )))
    return NULL ;

  audioStreamer* audio = CreateASIO(&cli_args , on_samples_cb) ;

  if (!!audio) ASIODriverName = cli_args ;

  return (!!audio) ? audio : NULL ;
}

/* audioStreamer public class methods */

bool audioStreamer::IsNjasiodrvAvailable() { return LoadNjasiodrvDll() ; }

std::string audioStreamer::GetASIODriverName() { return ASIODriverName ; }

/* audioStreamer private class methods */

audioStreamer* (*audioStreamer::CreateASIO)(char** cli_args , SPLPROC on_samples_cb) ;

bool audioStreamer::LoadNjasiodrvDll()
{
  if (!!NjasiodrvDll && !!CreateASIO) return true ;

  // get filepath of host binary - e.g. "c:\Program Files\aNinjam\aNinjam.exe"
  char  buffer[NINJAM::MAX_FILEPATH_LEN] ;
  GetModuleFileName(GetModuleHandle(NULL) , buffer , sizeof(buffer)) ;
  std::string filepath(buffer , 0 , NINJAM::MAX_FILEPATH_LEN) ;

  // replace binary filename with ASIO_DLL_FILENAME
  size_t filename_begin = filepath.find_last_of('\\') + 1 ;
  filepath.replace(filename_begin , std::string::npos , ASIO_DLL_FILENAME) ;

  // load NjasiodrvDll if present
  if (NjasiodrvDll = LoadLibrary(filepath.c_str()))
    *((void**)&CreateASIO) = (void*)GetProcAddress(NjasiodrvDll , ASIO_CONSTRUCTOR_NAME) ;

  return !!NjasiodrvDll && !!CreateASIO ;
}

#endif // NO_SUPPORT_ASIO

repo_name: ninjam/libninjam

license: mit


code:
/*
    NINJAM - audiostream_win32.cpp
    Copyright (C) 2004-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 file implements the AudioStreamer subclasses for DirectSound and WaveOut.
|*|   * audioStreamer_int           // internal          root class
|*|   * audioStreamer_ds            // audioStreamer_int subclass
|*|   * audioStreame _waveOut       // audioStreamer_int subclass
|*|   * audioStreamer_win32_asiosim // audioStreamer     subclass
|*| It also implements some relevant utility methods of the AudioStreamer superclass.
|*| Instantiation is exposed only through class methods of the AudioStreamer superclass.
|*|   * audioStreamer::NewDS()
|*|   * audioStreamer::NewWAVE()
\*/

#include <windows.h>
#include <mmsystem.h>

#include "../WDL/pcmfmtcvt.h"
#include "../WDL/ptrlist.h"
#include "audiostream.h"

#define DS_SLEEP 1
#define WO_SLEEP 1

/* audioStreamer public class constants */

#ifndef NO_SUPPORT_DS
const std::string audioStreamer::DEFAULT_DS_DEVICE_NAME = "Primary Sound Driver" ;
#endif // NO_SUPPORT_DS

/* audioStreamer private class variables */

#ifndef NO_SUPPORT_DS
std::vector<DsDevice> audioStreamer::DsDevices ;
#endif // NO_SUPPORT_DS

/* audioStreamer public class methods */

std::vector<std::string> audioStreamer::GetDsNames()
{
  LoadDsDevices() ; // modifies DsDevices

  std::vector<std::string> device_names ;

  for (unsigned int device_n = 0 ; device_n < DsDevices.size() ; ++device_n)
    device_names.push_back(DsDevices[device_n].deviceName) ;

  return device_names ;
}

std::string audioStreamer::GetDsNamesCSV(std::string separator)
{
  LoadDsDevices() ; // modifies DsDevices

  std::string device_names ;

  for (unsigned int device_n = 0 ; device_n < DsDevices.size() ; ++device_n)
    device_names += DsDevices[device_n].deviceName + separator ;

  if (!!DsDevices.size()) device_names.resize(device_names.length() - separator.length()) ;

  return device_names ;
}

std::vector<GUID> audioStreamer::GetDsGuids()
{
  LoadDsDevices() ; // modifies DsDevices

  std::vector<GUID> device_guids ;

  for (unsigned int device_n = 0 ; device_n < DsDevices.size() ; ++device_n)
    device_guids.push_back(DsDevices[device_n].deviceGuid) ;

  return device_guids ;
}

std::string audioStreamer::GetDsGuidsCSV(std::string separator)
{
  LoadDsDevices() ; // modifies DsDevices

  std::string device_guids ;

  for (unsigned int device_n = 0 ; device_n < DsDevices.size() ; ++device_n)
    device_guids += DsGuidToString(&DsDevices[device_n].deviceGuid) + separator ;

  if (!!DsDevices.size()) device_guids.resize(device_guids.length() - separator.length()) ;

  return device_guids ;
}

void audioStreamer::GetDsGuidByName(std::string device_name , LPGUID guid)
{
  LoadDsDevices() ; // modifies DsDevices

  *guid = GUID_NULL ;

  for (unsigned int device_n = 0 ; device_n < DsDevices.size() ; ++device_n)
    if (!DsDevices[device_n].deviceName.compare(device_name))
      *guid = DsDevices[device_n].deviceGuid ;
}

int audioStreamer::GetDsGuidIdx(LPGUID guid)
{
  LoadDsDevices() ; // modifies DsDevices

  int device_n = (int)DsDevices.size() ;

  while (device_n-- && memcmp(&DsDevices[device_n].deviceGuid , guid , sizeof(GUID))) ;

  return device_n ;
}

std::string audioStreamer::DsGuidToString(LPGUID guid)
{
/* marshall guids - e.g. GUID: 6B29FC40-CA47-1067-B31D-00DD010662DA
  typedef struct _GUID {
    DWORD Data1    ; // first 8 hex digits
    WORD  Data2    ; // next  4 hex digits
    WORD  Data3    ; // next  4 hex digits
    BYTE  Data4[8] ; // next  4 hex digits and final 12 hex digits
  } GUID ; */
/*
#pragma comment(lib , "rpcrt4.lib") // for RpcStringFree()

  UUID* uuid = (!!guid) ? (UUID*)guid : (UUID*)&GUID_NULL ;
  BYTE* uuid_bytes ;
  std::string uuid_string ;

  UuidToString(uuid , &uuid_bytes) ;
  uuid_string.append((LPTSTR)uuid_bytes) ;
  RpcStringFree(&uuid_bytes) ;
*/
  LPOLESTR uuid_cstring ;
  StringFromCLSID((CLSID)*guid , &uuid_cstring) ;
  std::string uuid_string = (char*)uuid_cstring ;

  return uuid_string ;
}

GUID audioStreamer::DsGuidFromString(std::string guid_string)
{
/*
  UUID uuid ;

  if (UuidFromString((RPC_CSTR)guid_string.c_str() , &uuid) != RPC_S_OK) uuid = GUID_NULL ;

  return (GUID)uuid ;
*/
  LPCLSID clsid = NULL ; CLSIDFromString((LPOLESTR)guid_string.c_str() , clsid) ;

  return (GUID)*clsid ;
}

BOOL audioStreamer::DsEnumDo(LPDSENUMCALLBACK ds_enum_cb , LPVOID user32)
{
  return !FAILED(DirectSoundEnumerate(ds_enum_cb , &user32)) ; // see dsound.h
}

/* audioStreamer private class methods */

void audioStreamer::LoadDsDevices()
{
  DsDevices.clear() ;
  DsEnumDo((LPDSENUMCALLBACK)StoreDsDevice , NULL) ; // modifies DsDevices
}

BOOL CALLBACK audioStreamer::StoreDsDevice(LPGUID  lpGUID      , LPCTSTR lpszDesc ,
                                           LPCTSTR lpszDrvName , LPVOID  unused   )
{
  GUID     guid      = (!!lpGUID) ? *lpGUID : GUID_NULL ;
  DsDevice ds_device = { guid , lpszDesc , lpszDrvName } ;

  DsDevices.push_back(ds_device) ;

  return TRUE ; // == should_continue
}

/* audioStreamer_int class */

class audioStreamer_int
{
	public:
		audioStreamer_int()
    {
      m_nch   = audioStreamer::DEFAULT_N_INPUTS ;
      m_srate = audioStreamer::DEFAULT_SAMPLE_RATE ;
      m_bps   = audioStreamer::DEFAULT_BIT_DEPTH ;
    }
		virtual ~audioStreamer_int() { }

		virtual int Read( char* buf , int len) = 0 ; // returns 0 if blocked, < 0 if error, > 0 if data
		virtual int Write(char* buf , int len) = 0 ; // returns 0 on success

		int m_srate , m_nch , m_bps ;
} ;

/* audioStreamer_win32_asiosim class */

class audioStreamer_win32_asiosim : public audioStreamer
{
	public:
		audioStreamer_win32_asiosim(audioStreamer_int *i, audioStreamer_int *o, int bufsize, int srate, int bps, SPLPROC proc)
    {
      m_splproc=proc;
      in=i;
      out=o;
      DWORD id;
      m_bps=bps;
      m_innch=m_outnch=2;
      m_bps=bps;
      m_srate=srate;
      m_done=0;
      m_buf=(char *)malloc(bufsize);
      m_bufsize=bufsize;

      m_procbuf=(float *)malloc((bufsize*64)/bps);// allocated 2x, input and output
      hThread=CreateThread(NULL,0,threadProc,(LPVOID)this,0,&id);
      SetThreadPriority(hThread,THREAD_PRIORITY_HIGHEST);
    }
		~audioStreamer_win32_asiosim()
    {
      m_done=1;
      WaitForSingleObject(hThread,INFINITE);
      CloseHandle(hThread);
      delete in;
      delete out;
      free(m_buf);
      free(m_procbuf);
    }

    const char *GetChannelName(int idx)
    {
      if (idx == 0) return "Left";
      if (idx == 1) return "Right";
      return NULL;
    }

	private:
    void tp();
    static DWORD WINAPI threadProc(LPVOID p)
    {
      audioStreamer_win32_asiosim *t=(audioStreamer_win32_asiosim*)p;
      t->tp();
      return 0;
    }
    audioStreamer_int *in, *out;

    HANDLE hThread;
    int m_done,m_bufsize;
    char *m_buf;
    float *m_procbuf;

    SPLPROC m_splproc;
};

void audioStreamer_win32_asiosim::tp()
{
  while (!m_done)
  {
    int a=in->Read(m_buf,m_bufsize);
    if (a>0)
    {
      int spllen=a*4/(m_bps); // a*8/m_bps/nch
      float *inptrs[2], *outptrs[2];
      inptrs[0]=m_procbuf;
      inptrs[1]=m_procbuf+spllen;
      outptrs[0]=m_procbuf+spllen*2;
      outptrs[1]=m_procbuf+spllen*3;

      pcmToFloats(m_buf,spllen,m_bps,2,inptrs[0],1);
      pcmToFloats(m_buf+(m_bps/8),spllen,m_bps,2,inptrs[1],1);

      if (m_splproc) m_splproc(inptrs,2,outptrs,2,spllen,m_srate);

      floatsToPcm(outptrs[0],1,spllen,m_buf,m_bps,2);
      floatsToPcm(outptrs[1],1,spllen,m_buf+(m_bps/8),m_bps,2);

      out->Write(m_buf,a);
    }
    else
    {
      Sleep(1);
    }
  }
}

/* audioStreamer_ds class */

#ifndef NO_SUPPORT_DS
class audioStreamer_ds : public audioStreamer_int
{
	public:
		audioStreamer_ds();
		~audioStreamer_ds();
		int Open(int iswrite, int srate, int nch, int bps, int sleep, int nbufs, int bufsize, GUID *device=NULL);// guid anyway for device
		int Read(char *buf, int len); // returns 0 if blocked, < 0 if error, > 0 if data
		int Write(char *buf, int len); // returns 0 on success

	private:

    int m_bps,m_nch,m_srate;

    LPDIRECTSOUND m_lpds;
    LPDIRECTSOUNDCAPTURE m_lpcap;
    LPDIRECTSOUNDBUFFER m_outbuf;
    LPDIRECTSOUNDCAPTUREBUFFER m_inbuf;

		int m_has_started;
    int m_bufpos;
    int m_last_pos;
    unsigned int m_i_lw, m_i_dw;

    int m_totalbufsize;
		int m_sleep;
		int m_bufsize;

    // fucko: finish dsound implementation
};

audioStreamer_ds::audioStreamer_ds()
{
  m_lpds=0;
  m_outbuf=0;
  m_lpcap=0;
  m_has_started=0;
  m_inbuf=0;
  m_last_pos=0;
  m_bufpos=0;
  m_i_lw=0;
  m_i_dw=0;
}

audioStreamer_ds::~audioStreamer_ds()
{
  if (m_lpds) m_lpds->Release();
  if (m_lpcap) m_lpcap->Release();
  m_lpcap=0;
  m_lpds=0;
  m_outbuf=0;
  m_inbuf=0;

}

int audioStreamer_ds::Open(int iswrite, int srate, int nch, int bps, int sleep, int nbufs, int bufsize, GUID *device)
{
  // todo: use device
  m_sleep = sleep >= 0 ? sleep : 0;

  if (!memcmp(device , &GUID_NULL , sizeof(GUID))) device = NULL ;

  m_nch = nch;
  m_srate=srate;
  m_bps=bps;

  int fmt_align=(bps>>3)*nch;
  int fmt_mul=fmt_align*srate;
  WAVEFORMATEX wfx={
		WAVE_FORMAT_PCM,
		nch,
		srate,
		fmt_mul,
		fmt_align,
		bps,
		0
	};
  m_totalbufsize=nbufs*bufsize;

  if (iswrite)
  {
      DirectSoundCreate(device,&m_lpds,NULL);

      if (m_lpds)
      {
        HWND hWnd = GetForegroundWindow();
        if (hWnd == NULL) hWnd = GetDesktopWindow();
        m_lpds->SetCooperativeLevel(hWnd,DSSCL_PRIORITY);

        // create a secondary buffer for now
        DSBUFFERDESC ds={sizeof(ds),DSBCAPS_GETCURRENTPOSITION2|DSBCAPS_GLOBALFOCUS,m_totalbufsize,0,&wfx, };
        m_lpds->CreateSoundBuffer(&ds,&m_outbuf,NULL);

      }

  }
  else
  {
    DirectSoundCaptureCreate(device,&m_lpcap,NULL);
    if (m_lpcap)
    {
      DSCBUFFERDESC ds={sizeof(ds),0,m_totalbufsize,0,&wfx, };
      m_lpcap->CreateCaptureBuffer(&ds,&m_inbuf,NULL);
    }
  }

  m_bufsize=bufsize;

  return 0;
}

int audioStreamer_ds::Read(char *buf, int len) // returns 0 if blocked, < 0 if error, > 0 if data
{
  if (!m_inbuf) return -1;
  if (!m_has_started)
  {
    m_inbuf->Start(DSCBSTART_LOOPING);
    m_has_started=1;
  }

  int cappos;
  m_inbuf->GetCurrentPosition(NULL,(DWORD *)&cappos);
  if (cappos < m_last_pos) m_i_dw++;
  m_last_pos=cappos;

  if ((m_i_dw - m_i_lw) * m_totalbufsize + cappos - m_bufpos >= (unsigned int)m_totalbufsize/2) // detect overrun, set to cappos
  {
    m_i_lw=m_i_dw;
    m_bufpos=0;
    while (m_bufpos < cappos-m_bufsize) m_bufpos += m_bufsize;
    if (m_bufpos >= m_totalbufsize)
    {
      m_i_lw++;
      m_bufpos -= m_totalbufsize;
    }
//    audiostream_onover();
  }

  for (;;)
  {
    if (m_i_lw < m_i_dw || m_bufpos + m_bufsize < cappos) break;

    Sleep(DS_SLEEP);
    m_inbuf->GetCurrentPosition(NULL,(DWORD*)&cappos);

    if (cappos < m_last_pos) m_i_dw++;
    m_last_pos=cappos;
  }

  //audiostream_instance->g_sound_in_overruns = (m_i_lw < m_i_dw ? (m_totalbufsize+cappos) : cappos ) -m_bufpos;

  void *v1=0, *v2=0;
  DWORD lv1=0, lv2=0;

  if (m_inbuf->Lock(m_bufpos,len,&v1,&lv1,&v2,&lv2,FALSE) == DS_OK)
  {
    int l1=min((int)lv1,len);
    memcpy(buf,v1,l1);
    if (l1 < len && v2 && lv2) memcpy(buf+l1,v2,min((int)lv2,len-l1));
    m_inbuf->Unlock(v1,lv1,v2,lv2);

    m_bufpos += len;
    if (m_bufpos >= m_totalbufsize)
    {
      m_i_lw++;
      m_bufpos -= m_totalbufsize;
    }
  }
  else
  {
    return -1;
  }

  return len;
}

int audioStreamer_ds::Write(char *buf, int len) // returns 0 on success
{
  if (len<1) return 0;
  if (!m_outbuf) return -1;

  if (!m_has_started)
  {
    m_outbuf->Play(0,0,DSBPLAY_LOOPING);
    m_has_started=1;
  }

  int thispos=0,ppos=0;
  m_outbuf->GetCurrentPosition((DWORD*)&ppos,(DWORD*)&thispos);

//  thispos=ppos; // let's use the write cursor, not the play position

  if (thispos < m_last_pos)
  {
    m_i_dw++;
  }
  m_last_pos = thispos;

  if (m_i_lw < m_i_dw || (m_i_lw == m_i_dw && m_bufpos < thispos )) // detect if we fall too far behind
  {
//    audiostream_onunder();
    m_i_lw=m_i_dw;
    m_bufpos=0;
    while (m_bufpos <= thispos+m_bufsize) m_bufpos+=m_bufsize;
    if (m_bufpos >= m_totalbufsize)
    {
      m_bufpos -= m_totalbufsize;
      m_i_lw++;
    }
  }

  if (m_bufpos + len >= ppos && m_i_lw > m_i_dw)
  {
    return 0;
  }

//    printf("%d,%d,%d\n",m_totalbufsize, tp2, m_bufpos+len);

  if (1)//tp2 >= m_bufpos + len)
  {
    void *v1=0, *v2=0;
    DWORD lv1=0, lv2=0;
    if (m_outbuf->Lock(m_bufpos,len,&v1,&lv1,&v2,&lv2,0) == DS_OK)
    {
      memcpy(v1,buf,min((int)lv1,len));
      if ((int)lv1 < len && v2 && lv2) memcpy(v2,buf+lv1,min((int)lv2,(int)(len-lv1)));

      m_outbuf->Unlock(v1,lv1,v2,lv2);

      m_bufpos += len;
      if (m_bufpos >= m_totalbufsize)
      {
        m_bufpos -= m_totalbufsize;
        m_i_lw++;
      }
    }
    else
    {
      return -1;
    }

  }
  else
  {
//  	audiostream_onunder(); // g_sound_in_overruns?
  }

  return 0;
}
#endif // NO_SUPPORT_DS

/* audioStreamer_waveOut class */

#ifndef NO_SUPPORT_WAVE
class audioStreamer_waveOut : public audioStreamer_int
{
	public:
		audioStreamer_waveOut();
		~audioStreamer_waveOut();
		int Open(int iswrite, int srate, int nch, int bps, int sleep, int nbufs, int bufsize, int device=-1);
		int Read(char *buf, int len); // returns 0 if blocked, < 0 if error, > 0 if data
		int Write(char *buf, int len); // returns 0 on success

	private:

		int m_sleep;
		int m_bufsize;

		HWAVEOUT m_hwo;
		HWAVEIN m_hwi;

		WDL_PtrList<WDL_HeapBuf> m_bufs; // includes WAVEHDR and buffer each

		int m_whichbuf; // used only for read mode
};

audioStreamer_waveOut::audioStreamer_waveOut()
{
	m_hwi=0;
	m_hwo=0;
}

audioStreamer_waveOut::~audioStreamer_waveOut()
{
   int x;
   if (m_hwo) waveOutReset(m_hwo);
   if (m_hwi) waveInReset(m_hwi);
   for (x = 0; x < m_bufs.GetSize(); x ++)
   {
	   WAVEHDR *p = (WAVEHDR *)m_bufs.Get(x)->Get();
	   if (m_hwi && p)
		   if (p->dwFlags & WHDR_PREPARED) waveInUnprepareHeader(m_hwi,p,sizeof(WAVEHDR));

	   if (m_hwo && p)
     {
		   if (p->dwFlags & WHDR_PREPARED) waveOutUnprepareHeader(m_hwo,p,sizeof(WAVEHDR));
     }
	   delete m_bufs.Get(x);
   }
   m_bufs.Empty();

   if (m_hwo)
   {
	   waveOutClose(m_hwo);
	   m_hwo=0;
   }
   if (m_hwi)
   {
	   waveInClose(m_hwi);
	   m_hwi=0;
   }

}

int audioStreamer_waveOut::Open(int iswrite, int srate, int nch, int bps, int sleep, int nbufs, int bufsize, int device)
{
  m_sleep =   WO_SLEEP;

  m_nch = nch;
  m_srate=srate;
  m_bps=bps;

  int fmt_align=(bps>>3)*nch;
  int fmt_mul=fmt_align*srate;
  WAVEFORMATEX wfx={
		WAVE_FORMAT_PCM,
		nch,
		srate,
		fmt_mul,
		fmt_align,
		bps,
		0
	};

  m_bufsize=bufsize;
  m_whichbuf=0;

  if (iswrite)
  {
	  if(waveOutOpen(&m_hwo,device,&wfx,(DWORD)0,0,WAVE_FORMAT_DIRECT)!=MMSYSERR_NOERROR) return -1;
  }
  else
  {
  	if(waveInOpen(&m_hwi,device,&wfx,0,0,WAVE_FORMAT_DIRECT)!=MMSYSERR_NOERROR) return -1;
  }

  int x;
  for (x = 0; x < nbufs; x ++)
  {
	  WDL_HeapBuf *p=new WDL_HeapBuf;
	  p->Resize(sizeof(WAVEHDR) + m_bufsize);

	  WAVEHDR *h = (WAVEHDR *)p->Get();

	  memset(h,0,p->GetSize());

	  h->lpData=(char *)h + sizeof(WAVEHDR);
	  h->dwBufferLength = m_bufsize;

	  if (!iswrite)
	  {
		  waveInPrepareHeader(m_hwi,h,sizeof(WAVEHDR));
		  waveInAddBuffer(m_hwi,h,sizeof(WAVEHDR));
	  }
	  else
	  {
      waveOutPrepareHeader(m_hwo,h,sizeof(WAVEHDR));
	  }

	  m_bufs.Add(p);
  }

  if (iswrite) waveOutRestart(m_hwo);
  else waveInStart(m_hwi);

  return 0;
}

int audioStreamer_waveOut::Read(char *buf, int len) // returns 0 if blocked, < 0 if error, > 0 if data
{
  if (!m_hwi) return -1;

#if 0 // lame, this doesnt really do what we want it to
  // check to see if all are full, and if so, kill a lot of em
  {
    int x;
    int cnt=0;
    for (x = 0; x < m_bufs.GetSize(); x ++)
    {
      WAVEHDR *th = (WAVEHDR *) m_bufs.Get(x)->Get();
      if (th->dwFlags & WHDR_DONE) cnt++;
    }
    if (cnt >= m_bufs.GetSize()-1)
    {
//      audiostream_onover();
      for (x = 0; x < m_bufs.GetSize(); x ++)
      {
        if (x != m_whichbuf)
        {
          WAVEHDR *th = (WAVEHDR *) m_bufs.Get(x)->Get();
          if (th->dwFlags & WHDR_DONE)
          {
            th->dwBytesRecorded=0;
            th->dwFlags = WHDR_PREPARED;
            waveInAddBuffer(m_hwi,th,sizeof(WAVEHDR));
          }
        }
      }
    }
  }
#endif

  WAVEHDR *th = (WAVEHDR *) m_bufs.Get(m_whichbuf)->Get();
  while (!(th->dwFlags & WHDR_DONE))
  {
    Sleep(WO_SLEEP);
  }
  len=min(len,(int)th->dwBytesRecorded);

  memcpy(buf,th->lpData,len);

  th->dwBytesRecorded=0;
  th->dwFlags = WHDR_PREPARED;
  waveInAddBuffer(m_hwi,th,sizeof(WAVEHDR));

  if (++m_whichbuf >= m_bufs.GetSize()) m_whichbuf=0;

  return len;
}

int audioStreamer_waveOut::Write(char *buf, int len) // returns 0 on success
{
  if (!m_hwo) return -1;
  if (len<1) return 0;

  int use_addr=-1;

  int cnt;
  do
  {
    int x;
    cnt=0;
    for (x = 0; x < m_bufs.GetSize(); x ++)
    {
        WAVEHDR *h=(WAVEHDR *)m_bufs.Get(x)->Get();
        if (h->dwFlags & WHDR_DONE) h->dwFlags &= ~(WHDR_INQUEUE|WHDR_DONE); // remove done and in queue

        if (!(h->dwFlags & WHDR_INQUEUE))
        {
          cnt++;
          use_addr=x;
        }
    }
    if (use_addr < 0)
    {
#if 1
      Sleep(WO_SLEEP);
#else
//      audiostream_onover();
      return 0;
#endif
    }
  } while (use_addr < 0);

  WAVEHDR *h=(WAVEHDR *)m_bufs.Get(use_addr)->Get();

  if (len > m_bufsize) len=m_bufsize;

  h->dwBufferLength=len;
  memcpy(h->lpData,buf,len);
  waveOutWrite(m_hwo,h,sizeof(WAVEHDR));

  if (!cnt)
  {
//    audiostream_onunder();

    int x;
    for (x = 0; x < m_bufs.GetSize(); x ++)
    {
      if (x != use_addr)
      {
        h=(WAVEHDR *) m_bufs.Get(x)->Get();
        h->dwBufferLength=len;
        waveOutWrite(m_hwo,h,sizeof(WAVEHDR));
      }
    }
  }

  return 0;
}
#endif // NO_SUPPORT_WAVE

/* audioStreamer public constructors */

#ifndef NO_SUPPORT_DS
audioStreamer* audioStreamer::NewDS(SPLPROC on_samples_proc                             ,
                                    GUID    input_device_guid , GUID output_device_guid ,
                                    int     sample_rate       , int  bit_depth          ,
                                    int     n_buffers         , int  buffer_size        )
{
  audioStreamer_ds* input_streamer  = new audioStreamer_ds() ;
  audioStreamer_ds* output_streamer = new audioStreamer_ds() ;

  if (input_streamer ->Open(0 , sample_rate , 2 , bit_depth , 0          ,
                            n_buffers , buffer_size , &input_device_guid ) ||
      output_streamer->Open(1 , sample_rate , 2 , bit_depth , 0          ,
                            n_buffers , buffer_size , &output_device_guid)  )
  {
    delete input_streamer ; delete output_streamer ; return 0 ;
  }

  return new audioStreamer_win32_asiosim(input_streamer , output_streamer ,
                                         buffer_size    , sample_rate     ,
                                         bit_depth      , on_samples_proc ) ;
}
#endif // NO_SUPPORT_DS
#ifndef NO_SUPPORT_WAVE
audioStreamer* audioStreamer::NewWAVE(SPLPROC on_samples_cb                         ,
                                      int     input_device_n  , int output_device_n ,
                                      int     sample_rate     , int bit_depth       ,
                                      int     n_buffers       , int buffer_size     )
{
  audioStreamer_waveOut* input_streamer  = new audioStreamer_waveOut() ;
  audioStreamer_waveOut* output_streamer = new audioStreamer_waveOut() ;

  if (input_streamer ->Open(0 , sample_rate , 2 , bit_depth , 0      ,
                            n_buffers , buffer_size , input_device_n ) ||
      output_streamer->Open(1 , sample_rate , 2 , bit_depth , 0      ,
                            n_buffers , buffer_size , output_device_n)  )
  {
    delete input_streamer ; delete output_streamer ; return 0 ;
  }

  return new audioStreamer_win32_asiosim(input_streamer , output_streamer ,
                                         buffer_size    , sample_rate     ,
                                         bit_depth      , on_samples_cb   ) ;
}
#endif // NO_SUPPORT_WAVE


code:
#include <string>

namespace NINJAM
{
  // NOTE: most of libninjam does not yet respect these constants explicitly
  // TODO: have yet to locate the code that handles host and username validation
  //           but experiments have shown usernames are trunctuated to 16 chars
  //           and valid username chars include [0-9] , [a-z] , '-' , '_'
  //           (njclient.h states that usernames are case insensitive)
  const  std::string HOST_CHARS           = "0123456789abcdefghijklmnopqrstuvwxyz-." ;
  const  std::string NICK_CHARS           = "0123456789abcdefghijklmnopqrstuvwxyz-_" ;
  // TODO: have yet to locate the code that handles channelname validation
  //           but experiments have shown channelnames may be absurdly long (> 2000 chars)
  static const int   MAX_USER_NAME_LEN    = 16 ;
  static const int   MAX_CHANNEL_NAME_LEN = 256 ;
  static const int   MAX_DEVICE_NAME_LEN  = 256 ;
  static const int   MAX_FILEPATH_LEN     = 1024 ;
}


code:
/*
    NINJAM - mpb.cpp
    Copyright (C) 2005-2007 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 file provides implementation of Message Parser and Builder (mpb_)
  classes for constructing and parsing Net_Messages.

*/

#ifdef _WIN32
#include <windows.h>
#else
#include <stdlib.h>
#include <memory.h>
#endif

#include "mpb.h"

// MESSAGE_SERVER_AUTH_CHALLENGE
int mpb_server_auth_challenge::parse(Net_Message *msg) // return 0 on success
{
  if (msg->get_type() != MESSAGE_SERVER_AUTH_CHALLENGE) return -1;
  if (msg->get_size() < 4+4+(int)sizeof(challenge)) return 1;
  unsigned char *p=(unsigned char *)msg->get_data();
  if (!p) return 2;

  memcpy(challenge,p,sizeof(challenge));
  p+=sizeof(challenge);

  server_caps = ((int)*p++);
  server_caps |= ((int)*p++)<<8;
  server_caps |= ((int)*p++)<<16;
  server_caps |= ((int)*p++)<<24;

  protocol_version = ((int)*p++);
  protocol_version |= ((int)*p++)<<8;
  protocol_version |= ((int)*p++)<<16;
  protocol_version |= ((int)*p++)<<24;

  if (server_caps&1)
  {
    char *s=(char*)p;
    while (p-(unsigned char *)msg->get_data() < msg->get_size())
    {
      if (!*p)
      {
        license_agreement=s;
        break;
      }
      p++;
    }
  }

  return 0;
}

Net_Message *mpb_server_auth_challenge::build()
{
  Net_Message *nm=new Net_Message;
  nm->set_type(MESSAGE_SERVER_AUTH_CHALLENGE);

  nm->set_size(sizeof(challenge) + 8 + (license_agreement?strlen(license_agreement)+1:0));

  unsigned char *p=(unsigned char *)nm->get_data();

  if (!p)
  {
    delete nm;
    return 0;
  }

  memcpy(p,challenge,sizeof(challenge));
  p+=sizeof(challenge);

  int sc=server_caps;
  if (license_agreement) sc|=1;
  else sc&=~1;

  *p++ = sc&0xff;
  *p++ = (sc>>8)&0xff;
  *p++ = (sc>>16)&0xff;
  *p++ = (sc>>24)&0xff;

  *p++ = protocol_version&0xff;
  *p++ = (protocol_version>>8)&0xff;
  *p++ = (protocol_version>>16)&0xff;
  *p++ = (protocol_version>>24)&0xff;

  if (license_agreement)
  {
    strcpy((char*)p,license_agreement);
    p+=strlen(license_agreement);
    *p++=0;
  }

  return nm;
}

// MESSAGE_SERVER_AUTH_REPLY
int mpb_server_auth_reply::parse(Net_Message *msg) // return 0 on success
{
  if (msg->get_type() != MESSAGE_SERVER_AUTH_REPLY) return -1;
  if (msg->get_size() < 1) return 1;
  unsigned char *p=(unsigned char *)msg->get_data();
  if (!p) return 2;

  flag=*p++;
  if (msg->get_size()>1)
  {
    char *t=(char*)p;
    while (p-(unsigned char *)msg->get_data() < msg->get_size() && *p) p++;

    if (p-(unsigned char *)msg->get_data() < msg->get_size())
    {
      errmsg=t;

      p++;
      if (p-(unsigned char *)msg->get_data() < msg->get_size())
      {
        maxchan=*p++;
      }
    }
  }

  return 0;
}

Net_Message *mpb_server_auth_reply::build()
{
  Net_Message *nm=new Net_Message;
  nm->set_type(MESSAGE_SERVER_AUTH_REPLY);

  nm->set_size(errmsg?strlen(errmsg)+1+1+1:1);

  unsigned char *p=(unsigned char *)nm->get_data();

  if (!p)
  {
    delete nm;
    return 0;
  }

  *p++=flag;
  if (errmsg)
  {
    strcpy((char*)p,errmsg);
    p+=strlen(errmsg)+1;
    *p++ = maxchan;
  }

  return nm;
}

// MESSAGE_SERVER_CONFIG_CHANGE_NOTIFY
int mpb_server_config_change_notify::parse(Net_Message *msg) // return 0 on success
{
  if (msg->get_type() != MESSAGE_SERVER_CONFIG_CHANGE_NOTIFY) return -1;
  if (msg->get_size() < 4) return 1;
  unsigned char *p=(unsigned char *)msg->get_data();
  if (!p) return 2;

  beats_minute = *p++;
  beats_minute |= ((int)*p++)<<8;
  beats_interval = *p++;
  beats_interval |= ((int)*p++)<<8;

  return 0;
}

Net_Message *mpb_server_config_change_notify::build()
{
  Net_Message *nm=new Net_Message;
  nm->set_type(MESSAGE_SERVER_CONFIG_CHANGE_NOTIFY);

  nm->set_size(4);

  unsigned char *p=(unsigned char *)nm->get_data();

  if (!p)
  {
    delete nm;
    return 0;
  }

  *p++=beats_minute&0xff;
  *p++=(beats_minute>>8)&0xff;
  *p++=beats_interval&0xff;
  *p++=(beats_interval>>8)&0xff;

  return nm;
}

// MESSAGE_SERVER_USERINFO_CHANGE_NOTIFY
int mpb_server_userinfo_change_notify::parse(Net_Message *msg) // return 0 on success
{
  if (msg->get_type() != MESSAGE_SERVER_USERINFO_CHANGE_NOTIFY) return -1;
  if (msg->get_size() < 1) return 1;

  m_intmsg = msg;
  return 0;
}

Net_Message *mpb_server_userinfo_change_notify::build()
{
  if (m_intmsg)
  {
    Net_Message *n=m_intmsg;
    m_intmsg=0;
    return n;
  }

  Net_Message *nm=new Net_Message;
  nm->set_type(MESSAGE_SERVER_USERINFO_CHANGE_NOTIFY);
  nm->set_size(0);

  return nm;
}

void mpb_server_userinfo_change_notify::build_add_rec(int isActive, int channelid,
                                                      short volume, int pan, int flags, char *username, char *chname)
{
  int size=1+ // is remove
           1+ // channel index
           2+ // volume
           1+ // pan
           1+ // flags
           strlen(username?username:"")+1+strlen(chname?chname:"")+1;

  if (!m_intmsg)
  {
    m_intmsg = new Net_Message;
    m_intmsg->set_type(MESSAGE_SERVER_USERINFO_CHANGE_NOTIFY);
  }
  int oldsize=m_intmsg->get_size();
  m_intmsg->set_size(size+oldsize);
  unsigned char *p=(unsigned char *)m_intmsg->get_data();
  if (p)
  {
    p+=oldsize;
    *p++=!!isActive;

    if (channelid < 0) channelid=0;
    else if (channelid>255)channelid=255;
    *p++=channelid;

    *p++=volume&0xff;
    *p++=(volume>>8)&0xff;

    if (pan<-128) pan=-128;
    else if (pan>127)pan=127;
    *p++=(unsigned char)pan;

    *p++=(unsigned char)flags;

    strcpy((char*)p,username);
    p+=strlen(username)+1;
    strcpy((char*)p,chname);
    p+=strlen(chname)+1;
  }
}

// returns offset of next item on success, or <= 0 if out of items
int mpb_server_userinfo_change_notify::parse_get_rec(int offs, int *isActive, int *channelid, short *volume,
                                                     int *pan, int *flags, char **username, char **chname)
{
  int hdrsize=1+ // is remove
           1+ // channel index
           2+ // volume
           1+ // pan
           1; // flags

  if (!m_intmsg) return 0;
  unsigned char *p=(unsigned char *)m_intmsg->get_data();
  int len=m_intmsg->get_size()-offs;
  if (!p || len < hdrsize+2) return 0;
  p+=offs;

  unsigned char *hdrbuf=p;
  char *unp;
  char *cnp;

  if (len < hdrsize+2) return 0;
  hdrbuf=p;
  len -= hdrsize;
  unp=(char *)hdrbuf+hdrsize;
  cnp=unp;
  while (*cnp)
  {
    cnp++;
    if (!len--) return 0;
  }
  cnp++;
  if (!len--) return 0;

  p=(unsigned char *)cnp;
  while (*p)
  {
    p++;
    if (!len--) return 0;
  }
  p++;
  if (!len--) return 0;

  *isActive=(int)*hdrbuf++;
  *channelid=(int)*hdrbuf++;
  *volume=(int)*hdrbuf++;
  *volume |= ((int)*hdrbuf++)<<8;
  *pan = (int) *hdrbuf++;
  *flags = (int) *hdrbuf++;

  *username = unp;
  *chname = cnp;

  return p - (unsigned char *)m_intmsg->get_data();
}

// MESSAGE_SERVER_DOWNLOAD_INTERVAL_BEGIN
int mpb_server_download_interval_begin::parse(Net_Message *msg) // return 0 on success
{
  if (msg->get_type() != MESSAGE_SERVER_DOWNLOAD_INTERVAL_BEGIN) return -1;
  if (msg->get_size() < 25+1) return 1;
  unsigned char *p=(unsigned char *)msg->get_data();
  if (!p) return 2;

  memcpy(guid,p,sizeof(guid));
  p+=sizeof(guid);
  estsize = (int)*p++;
  estsize |= ((int)*p++)<<8;
  estsize |= ((int)*p++)<<16;
  estsize |= ((int)*p++)<<24;
  fourcc = (unsigned int)*p++;
  fourcc |= ((unsigned int)*p++)<<8;
  fourcc |= ((unsigned int)*p++)<<16;
  fourcc |= ((unsigned int)*p++)<<24;
  chidx = (int)*p++;
  int len=msg->get_size()-25;

  username=(char *)p;

  // validate null termination for now
  while (len)
  {
    if (!*p) break;
    p++;
    len--;
  }
  if (!len) return -1;

  return 0;
}

Net_Message *mpb_server_download_interval_begin::build()
{
  Net_Message *nm=new Net_Message;
  nm->set_type(MESSAGE_SERVER_DOWNLOAD_INTERVAL_BEGIN);

  nm->set_size(25+strlen(username?username:"")+1);

  unsigned char *p=(unsigned char *)nm->get_data();

  if (!p)
  {
    delete nm;
    return 0;
  }

  memcpy(p,guid,sizeof(guid));
  p+=sizeof(guid);
  *p++=(unsigned char)((estsize)&0xff);
  *p++=(unsigned char)((estsize>>8)&0xff);
  *p++=(unsigned char)((estsize>>16)&0xff);
  *p++=(unsigned char)((estsize>>24)&0xff);
  *p++=(unsigned char)((fourcc)&0xff);
  *p++=(unsigned char)((fourcc>>8)&0xff);
  *p++=(unsigned char)((fourcc>>16)&0xff);
  *p++=(unsigned char)((fourcc>>24)&0xff);
  *p++=(unsigned char)((chidx)&0xff);

  strcpy((char *)p,username?username:"");

  return nm;
}

// MESSAGE_SERVER_DOWNLOAD_INTERVAL_WRITE
int mpb_server_download_interval_write::parse(Net_Message *msg) // return 0 on success
{
  if (msg->get_type() != MESSAGE_SERVER_DOWNLOAD_INTERVAL_WRITE) return -1;
  if (msg->get_size() < 17) return 1;
  unsigned char *p=(unsigned char *)msg->get_data();
  if (!p) return 2;

  memcpy(guid,p,sizeof(guid));
  p+=sizeof(guid);
  flags = (char)*p++;

  audio_data = p;
  audio_data_len = msg->get_size()-17;

  return 0;
}

Net_Message *mpb_server_download_interval_write::build()
{
  Net_Message *nm=new Net_Message;
  nm->set_type(MESSAGE_SERVER_DOWNLOAD_INTERVAL_WRITE);

  nm->set_size(17+(audio_data?audio_data_len:0));

  unsigned char *p=(unsigned char *)nm->get_data();

  if (!p)
  {
    delete nm;
    return 0;
  }
  memcpy(p,guid,sizeof(guid));
  p+=sizeof(guid);
  *p++=(unsigned char) flags;

  if (audio_data&&audio_data_len) memcpy(p,audio_data,audio_data_len);

  return nm;
}

/////////////////////////////////////////////////////////////////////////
//////////// client to server messages
/////////////////////////////////////////////////////////////////////////

// MESSAGE_CLIENT_AUTH_USER
int mpb_client_auth_user::parse(Net_Message *msg) // return 0 on success
{
  if (msg->get_type() != MESSAGE_CLIENT_AUTH_USER) return -1;
  if (msg->get_size() < (int)sizeof(passhash)+1) return 1;
  unsigned char *p=(unsigned char *)msg->get_data();
  if (!p) return 2;
  int len=msg->get_size();

  memcpy(passhash,p,sizeof(passhash));
  p+=sizeof(passhash);
  len -= sizeof(passhash);

  username=(char *)p;
  while (*p && len>0)
  {
    p++;
    len--;
  }
  if (!len) return 3;
  p++;
  len--;

  if (len < 8) return 3;

  client_caps = ((int)*p++);
  client_caps |= ((int)*p++)<<8;
  client_caps |= ((int)*p++)<<16;
  client_caps |= ((int)*p++)<<24;

  client_version = ((int)*p++);
  client_version |= ((int)*p++)<<8;
  client_version |= ((int)*p++)<<16;
  client_version |= ((int)*p++)<<24;

  //printf("bla (len=%d, caps=%d) decoded client version %08x\n",len,client_caps,client_version);

  return 0;
}

Net_Message *mpb_client_auth_user::build()
{
  Net_Message *nm=new Net_Message;
  nm->set_type(MESSAGE_CLIENT_AUTH_USER);

  nm->set_size(sizeof(passhash) + (username?strlen(username):0) + 1 + 4 + 4);

  unsigned char *p=(unsigned char *)nm->get_data();

  if (!p)
  {
    delete nm;
    return 0;
  }

  memcpy(p,passhash,sizeof(passhash));
  p+=sizeof(passhash);

  strcpy((char*)p,username?username:"");
  p+=strlen(username?username:"")+1;

  *p++=(client_caps&0xff);
  *p++=(client_caps&0xff00)>>8;
  *p++=(client_caps&0xff0000)>>16;
  *p++=(client_caps&0xff000000)>>24;

  *p++=(client_version&0xff);
  *p++=(client_version&0xff00)>>8;
  *p++=(client_version&0xff0000)>>16;
  *p++=(client_version&0xff000000)>>24;

  return nm;
}

// MESSAGE_CLIENT_SET_USERMASK
int mpb_client_set_usermask::parse(Net_Message *msg) // return 0 on success
{
  if (msg->get_type() != MESSAGE_CLIENT_SET_USERMASK) return -1;
  if (msg->get_size() < 1) return 1;

  m_intmsg = msg;
  return 0;
}

Net_Message *mpb_client_set_usermask::build()
{
  if (m_intmsg)
  {
    Net_Message *n=m_intmsg;
    m_intmsg=0;
    return n;
  }

  Net_Message *nm=new Net_Message;
  nm->set_type(MESSAGE_CLIENT_SET_USERMASK);
  nm->set_size(0);

  return nm;
}

void mpb_client_set_usermask::build_add_rec(char *username, unsigned int chflags)
{
  int size=4+strlen(username?username:"")+1;

  if (!m_intmsg)
  {
    m_intmsg = new Net_Message;
    m_intmsg->set_type(MESSAGE_CLIENT_SET_USERMASK);
  }
  int oldsize=m_intmsg->get_size();
  m_intmsg->set_size(size+oldsize);
  unsigned char *p=(unsigned char *)m_intmsg->get_data();
  if (p)
  {
    p+=oldsize;

    strcpy((char*)p,username);
    p+=strlen(username)+1;

    *p++=chflags&0xff;
    *p++=(chflags>>8)&0xff;
    *p++=(chflags>>16)&0xff;
    *p++=(chflags>>24)&0xff;
  }
}

// returns offset of next item on success, or <= 0 if out of items
int mpb_client_set_usermask::parse_get_rec(int offs, char **username, unsigned int *chflags)
{
  if (!m_intmsg) return 0;
  unsigned char *p=(unsigned char *)m_intmsg->get_data();
  int len=m_intmsg->get_size()-offs;
  if (!p || len < 5) return 0;
  p+=offs;

  *username=(char*)p;
  while (*p && len > 0)
  {
    len--;
    p++;
  }
  p++;
  len--;

  if (len<4) return -1;

  *chflags = ((int)*p++);
  *chflags |= ((int)*p++)<<8;
  *chflags |= ((int)*p++)<<16;
  *chflags |= ((int)*p++)<<24;

  return p - (unsigned char *)m_intmsg->get_data();
}

// MESSAGE_CLIENT_SET_CHANNEL_INFO
int mpb_client_set_channel_info::parse(Net_Message *msg) // return 0 on success
{
  if (msg->get_type() != MESSAGE_CLIENT_SET_CHANNEL_INFO) return -1;

  m_intmsg = msg;

  return 0;
}

Net_Message *mpb_client_set_channel_info::build()
{
  if (m_intmsg)
  {
    Net_Message *n=m_intmsg;
    m_intmsg=0;
    return n;
  }

  Net_Message *nm=new Net_Message;
  nm->set_type(MESSAGE_CLIENT_SET_CHANNEL_INFO);
  nm->set_size(0);

  return nm;
}

void mpb_client_set_channel_info::build_add_rec(char *chname, short volume, int pan, int flags)
{
  int size=mpisize+strlen(chname?chname:"")+1;

  if (!m_intmsg)
  {
    m_intmsg = new Net_Message;
    m_intmsg->set_type(MESSAGE_CLIENT_SET_CHANNEL_INFO);
    m_intmsg->set_size(2);
    unsigned char *p=(unsigned char*)m_intmsg->get_data();
    if (!p) return;
    *p++ = mpisize&0xff;
    *p++ = (mpisize>>8)&0xff;
  }
  int oldsize=m_intmsg->get_size();
  m_intmsg->set_size(size+oldsize);
  unsigned char *p=(unsigned char *)m_intmsg->get_data();
  if (p)
  {
    p+=oldsize;

    strcpy((char*)p,chname);
    p+=strlen(chname)+1;
    if (pan < -128) pan=-128;
    else if (pan > 127) pan=127;
    if (mpisize>0) *p++=(volume)&0xff;
    if (mpisize>1) *p++=(volume>>8)&0xff;
    if (mpisize>2) *p++=(unsigned char)pan;
    if (mpisize>3) *p++=(unsigned char)flags;
    if (mpisize>4)
      memset(p,0,mpisize-4);

  }
}

// returns offset of next item on success, or <= 0 if out of items
int mpb_client_set_channel_info::parse_get_rec(int offs, char **chname, short *volume, int *pan, int *flags)
{
  if (!m_intmsg) return 0;
  unsigned char *p=(unsigned char *)m_intmsg->get_data();
  if (!p || m_intmsg->get_size() <= 2) return 0;
  int len=m_intmsg->get_size()-offs;

  mpisize=(int)p[0] | (((int)p[1])<<8);
  if (len < mpisize) return 0;

  p+=offs+2;

  *chname=(char*)p;
  while (*p && len > 0)
  {
    len--;
    p++;
  }
  p++;
  len--;

  if (len<mpisize) return -1;

  if (mpisize>1)
  {
    *volume=(int)p[0];
    *volume|=((int)p[1])<<8;
  }
  else *volume=0;
  if (mpisize>2) *pan=(int)p[2];
  else *pan=0;
  if (mpisize>3) *flags=(int)p[3];
  else *flags=0;

  return (p+mpisize) - ((unsigned char *)m_intmsg->get_data()+2);
}

// MESSAGE_CLIENT_UPLOAD_INTERVAL_BEGIN

int mpb_client_upload_interval_begin::parse(Net_Message *msg) // return 0 on success
{
  if (msg->get_type() != MESSAGE_CLIENT_UPLOAD_INTERVAL_BEGIN) return -1;
  if (msg->get_size() < 25) return 1;
  unsigned char *p=(unsigned char *)msg->get_data();
  if (!p) return 2;

  memcpy(guid,p,sizeof(guid));
  p+=sizeof(guid);
  estsize = (int)*p++;
  estsize |= ((int)*p++)<<8;
  estsize |= ((int)*p++)<<16;
  estsize |= ((int)*p++)<<24;
  fourcc = (unsigned int)*p++;
  fourcc |= ((unsigned int)*p++)<<8;
  fourcc |= ((unsigned int)*p++)<<16;
  fourcc |= ((unsigned int)*p++)<<24;
  chidx = (int)*p++;

  return 0;
}

Net_Message *mpb_client_upload_interval_begin::build()
{
  Net_Message *nm=new Net_Message;
  nm->set_type(MESSAGE_CLIENT_UPLOAD_INTERVAL_BEGIN);

  nm->set_size(25);

  unsigned char *p=(unsigned char *)nm->get_data();

  if (!p)
  {
    delete nm;
    return 0;
  }

  memcpy(p,guid,sizeof(guid));
  p+=sizeof(guid);
  *p++=(unsigned char)((estsize)&0xff);
  *p++=(unsigned char)((estsize>>8)&0xff);
  *p++=(unsigned char)((estsize>>16)&0xff);
  *p++=(unsigned char)((estsize>>24)&0xff);
  *p++=(unsigned char)((fourcc)&0xff);
  *p++=(unsigned char)((fourcc>>8)&0xff);
  *p++=(unsigned char)((fourcc>>16)&0xff);
  *p++=(unsigned char)((fourcc>>24)&0xff);
  *p++=(unsigned char)((chidx)&0xff);

  return nm;
}

// MESSAGE_CLIENT_UPLOAD_INTERVAL_WRITE
int mpb_client_upload_interval_write::parse(Net_Message *msg) // return 0 on success
{
  if (msg->get_type() != MESSAGE_CLIENT_UPLOAD_INTERVAL_WRITE) return -1;
  if (msg->get_size() < 17) return 1;
  unsigned char *p=(unsigned char *)msg->get_data();
  if (!p) return 2;

  memcpy(guid,p,sizeof(guid));
  p+=sizeof(guid);
  flags = (char)*p++;

  audio_data = p;
  audio_data_len = msg->get_size()-17;

  return 0;
}

Net_Message *mpb_client_upload_interval_write::build()
{
  Net_Message *nm=new Net_Message;
  nm->set_type(MESSAGE_CLIENT_UPLOAD_INTERVAL_WRITE);

  nm->set_size(17+(audio_data?audio_data_len:0));

  unsigned char *p=(unsigned char *)nm->get_data();

  if (!p)
  {
    delete nm;
    return 0;
  }
  memcpy(p,guid,sizeof(guid));
  p+=sizeof(guid);
  *p++=(unsigned char) flags;

  if (audio_data&&audio_data_len) memcpy(p,audio_data,audio_data_len);

  return nm;
}

/////////////////////////////////////////////////////////////////////////
//////////// bidirectional generic  messages
/////////////////////////////////////////////////////////////////////////

// MESSAGE_CHAT_MESSAGE

int mpb_chat_message::parse(Net_Message *msg) // return 0 on success
{
  if (msg->get_type() != MESSAGE_CHAT_MESSAGE) return -1;
  if (msg->get_size() < 1) return 1;
  char *p=(char *)msg->get_data();
  if (!p) return 2;

  char *endp=(char*)msg->get_data()+msg->get_size();

  unsigned int x;
  memset(parms,0,sizeof(parms));
  for (x = 0; x < sizeof(parms)/sizeof(parms[0]); x ++)
  {
    parms[x]=p;
    while (p < endp && *p) p++;
    p++;
    if (p >= endp) break;
  }
  return x?0:3;
}

Net_Message *mpb_chat_message::build()
{
  Net_Message *nm=new Net_Message;
  nm->set_type(MESSAGE_CHAT_MESSAGE);

  unsigned int x;
  int sz=0;
  for (x = 0; x < sizeof(parms)/sizeof(parms[0]); x ++)
  {
    sz+=(parms[x]?strlen(parms[x]):0)+1;
  }

  nm->set_size(sz);

  char *p=(char *)nm->get_data();

  if (!p)
  {
    delete nm;
    return 0;
  }

  for (x = 0; x < sizeof(parms)/sizeof(parms[0]); x ++)
  {
    const char *sp=parms[x];
    if (!sp) sp="";
    strcpy(p,sp);
    p+=strlen(sp)+1;
  }

  return nm;
}


code:
/*
    NINJAM - mpb.h
    Copyright (C) 2005-2007 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 header provides message-type defines, as well as Message Parser and Builder (mpb_)
  classes for constructing and parsing Net_Messages.

*/

#ifndef _MPB_H_
#define _MPB_H_ // mpb.h, message parsing and building

#include "netmsg.h"

#define PROTO_VER_MIN 0x00020000
#define PROTO_VER_MAX 0x0002ffff
#define PROTO_VER_CUR 0x00020000

#define MESSAGE_SERVER_AUTH_CHALLENGE 0x00

class mpb_server_auth_challenge
{
  public:
    mpb_server_auth_challenge() : server_caps(0), license_agreement(0), protocol_version(0) { memset(challenge,0,sizeof(challenge)); }
    ~mpb_server_auth_challenge() { }

    int parse(Net_Message *msg); // return 0 on success
    Net_Message *build();

    // public data
    unsigned char challenge[8];
    int server_caps; // low bit is license agreement, bits 8-16 are keepalive
    char *license_agreement;
    int protocol_version; // version should be 1 to start.
};

#define MESSAGE_SERVER_AUTH_REPLY 0x01

class mpb_server_auth_reply
{
  public:
    mpb_server_auth_reply() : flag(0), errmsg(0), maxchan(32) { }
    ~mpb_server_auth_reply() { }

    int parse(Net_Message *msg); // return 0 on success
    Net_Message *build();

    // public data
    char flag;  // low bit is success bit
    char *errmsg; // if success bit is set, and this is also set, then it is the effective username of the client
    char maxchan;
};

#define MESSAGE_SERVER_CONFIG_CHANGE_NOTIFY 0x02

class mpb_server_config_change_notify
{
  public:
    mpb_server_config_change_notify() : beats_minute(120), beats_interval(32) { }
    ~mpb_server_config_change_notify() { }

    int parse(Net_Message *msg); // return 0 on success
    Net_Message *build();

    // public data
    int beats_minute;  //bpm
    int beats_interval;  // beats/interval
};

#define MESSAGE_SERVER_USERINFO_CHANGE_NOTIFY 0x03

class mpb_server_userinfo_change_notify
{
  public:
    mpb_server_userinfo_change_notify() : m_intmsg(0) { }
    ~mpb_server_userinfo_change_notify() { }

    int parse(Net_Message *msg); // return 0 on success
    Net_Message *build(); // if you call build_add_rec at all, you must do delete x->build(); to avoid a mem leak.

    // public accessors
    // pan is -128..127
    // volume is dB gain, so 0=0dB, 10=1dB, -30=-3 dB, etc
    // flags, &1 = no default subscribe, &2=instamode
    void build_add_rec(int isActive, int channelid, short volume, int pan, int flags, char *username, char *chname);
    int parse_get_rec(int offs, int *isActive, int *channelid, short *volume, int *pan, int *flags, char **username, char **chname); // returns offset of next item on success, or <0 if out of items

   private:

     Net_Message *m_intmsg;
};

#define MESSAGE_SERVER_DOWNLOAD_INTERVAL_BEGIN 0x04
class mpb_server_download_interval_begin
{
  public:
    mpb_server_download_interval_begin() : estsize(0), fourcc(0), chidx(0), username(0) { memset(guid,0,sizeof(guid)); }
    ~mpb_server_download_interval_begin() { }

    int parse(Net_Message *msg); // return 0 on success
    Net_Message *build();

    // public data
    unsigned char guid[16];
    int estsize;
    unsigned int fourcc;
    int chidx;       // only 1 byte
    char *username;
};

#define MESSAGE_SERVER_DOWNLOAD_INTERVAL_WRITE 0x05
class mpb_server_download_interval_write
{
  public:
    mpb_server_download_interval_write() : flags(0), audio_data(0), audio_data_len(0) { memset(guid,0,sizeof(guid)); }
    ~mpb_server_download_interval_write() { }

    int parse(Net_Message *msg); // return 0 on success
    Net_Message *build();

    // public data
    unsigned char guid[16]; // transfer id
    char flags; // & 1 = end

    void *audio_data;
    int audio_data_len; // not encoded in, just used internally
};

#define MESSAGE_CLIENT_AUTH_USER 0x80
class mpb_client_auth_user
{
  public:
    mpb_client_auth_user() : client_caps(0), client_version(0), username(0) { memset(passhash,0,sizeof(passhash)); }
    ~mpb_client_auth_user() { }

    int parse(Net_Message *msg); // return 0 on success
    Net_Message *build();

    // public data
    unsigned char passhash[20];
    int client_caps; // low bit is agreeing to license
    int client_version; // client version, only present if second bit of caps is there
                     // second bit should be set, otherwise server will disconnect anyway.
    char *username;
};

#define MESSAGE_CLIENT_SET_USERMASK 0x81
class mpb_client_set_usermask
{
  public:
    mpb_client_set_usermask() : m_intmsg(0) { }
    ~mpb_client_set_usermask() { }

    int parse(Net_Message *msg); // return 0 on success
    Net_Message *build();

    void build_add_rec(char *username, unsigned int chflags);
    int parse_get_rec(int offs, char **username, unsigned int *chflags); // returns offset of next item on success, or <0 if out of items

   private:

     Net_Message *m_intmsg;
};

#define MESSAGE_CLIENT_SET_CHANNEL_INFO 0x82
class mpb_client_set_channel_info
{
  public:
    mpb_client_set_channel_info() : mpisize(4), m_intmsg(0) { }
    ~mpb_client_set_channel_info() { }

    int parse(Net_Message *msg); // return 0 on success
    Net_Message *build();

    // pan is -128..127
    // volume is dB gain, so 0=0dB, 10=1dB, -30=-3 dB, etc
    // flags, &1 = no default subscribe, &2=instamode, &4=session mode, 0x80=filler (inactive)
    void build_add_rec(char *chname, short volume, int pan, int flags);
    int parse_get_rec(int offs, char **chname, short *volume, int *pan, int *flags); // returns offset of next item on success, or <0 if out of items

    int mpisize;

   private:

     Net_Message *m_intmsg;
};

#define MESSAGE_CLIENT_UPLOAD_INTERVAL_BEGIN 0x83
class mpb_client_upload_interval_begin
{
  public:
    mpb_client_upload_interval_begin() : estsize(0), fourcc(0), chidx(0){ memset(guid,0,sizeof(guid)); }
    ~mpb_client_upload_interval_begin() { }

    int parse(Net_Message *msg); // return 0 on success
    Net_Message *build();

    // public data
    unsigned char guid[16];
    int estsize;
    unsigned int fourcc;
    int chidx;       // only 1 byte
};

// this uses the exact same message format as the server version
#define MESSAGE_CLIENT_UPLOAD_INTERVAL_WRITE 0x84
class mpb_client_upload_interval_write
{
  public:
    mpb_client_upload_interval_write() : flags(0), audio_data(0), audio_data_len(0) { memset(guid,0,sizeof(guid)); }
    ~mpb_client_upload_interval_write() { }

    int parse(Net_Message *msg); // return 0 on success
    Net_Message *build();

    // public data
    unsigned char guid[16];
    char flags; // & 1 = end

    void *audio_data;
    int audio_data_len; // not encoded in, just used internally
};

#define MESSAGE_CHAT_MESSAGE 0xC0
class mpb_chat_message
{
  public:
    mpb_chat_message() { memset(parms,0,sizeof(parms)); }
    ~mpb_chat_message() { }

    int parse(Net_Message *msg); // return 0 on success
    Net_Message *build();

    const char *parms[5];

    // currently defined client->server commands:
    // MSG <text>   - sends a message to everybody
    // PRIVMSG username <text>   - sends a private message to username
    // TOPIC <topic>   - set server topic (need permissions)

    // and server->client commands:
    // MSG <username> <text>   - a message from username
    // PRIVMSG username <text>   - a private message from username
    // TOPIC <topic>   - server topic change
};

#endif//_MPB_H_


code:
/*
    NINJAM - netmsg.cpp
    Copyright (C) 2005-2007 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 file provides the implementations of the Net_Messsage class, and
  Net_Connection class (handles sending and receiving Net_Messages to
  a JNetLib JNL_Connection).

*/

#ifdef _WIN32
#include <windows.h>
#else
#include <stdlib.h>
#include <memory.h>
#endif

#include "netmsg.h"

int Net_Message::parseBytesNeeded()
{
  return get_size()-m_parsepos;
}

int Net_Message::parseAddBytes(void *data, int len)
{
  char *p=(char*)get_data();
  if (!p) return 0;
  if (len > parseBytesNeeded()) len = parseBytesNeeded();
  memcpy(p+m_parsepos,data,len);
  m_parsepos+=len;
  return len;
}

int Net_Message::parseMessageHeader(void *data, int len) // returns bytes used, if any (or 0 if more data needed) or -1 if invalid
{
	unsigned char *dp=(unsigned char *)data;
  if (len < 5) return 0;

  int type=*dp++;

  int size = *dp++;
  size |= ((int)*dp++)<<8;
  size |= ((int)*dp++)<<16;
  size |= ((int)*dp++)<<24;
  len -= 5;
  if (type == MESSAGE_INVALID || size < 0 || size > NET_MESSAGE_MAX_SIZE) return -1;

  m_type=type;
  set_size(size);

  m_parsepos=0;

  return 5;
}

int Net_Message::makeMessageHeader(void *data) // makes message header, data should be at least 16 bytes to be safe
{
	if (!data) return 0;

	unsigned char *dp=(unsigned char *)data;
  *dp++ = (unsigned char) m_type;
  int size=get_size();
  *dp++=size&0xff; size>>=8;
  *dp++=size&0xff; size>>=8;
  *dp++=size&0xff; size>>=8;
  *dp++=size&0xff;

  return (dp-(unsigned char *)data);
}

Net_Message *Net_Connection::Run(int *wantsleep)
{
  if (!m_con || m_error) return 0;

  {
    int s=0,r=0;
    m_con->run(-1,-1,&s,&r);
    if (wantsleep && (s||r)) *wantsleep=0;
  }

  time_t now=time(NULL);

  if (m_sendq.Available() > 0) m_last_send=now;
  else if (now > m_last_send + m_keepalive)
  {
    Net_Message *keepalive= new Net_Message;
    keepalive->set_type(MESSAGE_KEEPALIVE);
    keepalive->set_size(0);
    Send(keepalive);
    m_last_send=now;
  }

  // handle sending
  while (m_con->send_bytes_available()>64 && m_sendq.Available()>0)
  {
    Net_Message **topofq = (Net_Message **)m_sendq.Get();

    if (!topofq) break;
    Net_Message *sendm=*topofq;
    if (sendm)
    {
      if (wantsleep) *wantsleep=0;
      if (m_msgsendpos<0) // send header
      {
        char buf[32];
        int hdrlen=sendm->makeMessageHeader(buf);
        m_con->send_bytes(buf,hdrlen);

        m_msgsendpos=0;
      }

      int sz=sendm->get_size()-m_msgsendpos;
      if (sz < 1) // end of message, discard and move to next
      {
        sendm->releaseRef();
        m_sendq.Advance(sizeof(Net_Message*));
        m_msgsendpos=-1;
      }
      else
      {
        int avail=m_con->send_bytes_available();
        if (sz > avail) sz=avail;
        if (sz>0)
        {
          m_con->send_bytes((char*)sendm->get_data()+m_msgsendpos,sz);
          m_msgsendpos+=sz;
        }
      }
    }
    else
    {
      m_sendq.Advance(sizeof(Net_Message*));
      m_msgsendpos=-1;
    }
    {
      int s=0,r=0;
      m_con->run(-1,-1,&s,&r);
      if (wantsleep && (s||r)) *wantsleep=0;
    }
  }

  m_sendq.Compact();

  Net_Message *retv=0;

  // handle receive now
  if (!m_recvmsg)
  {
    m_recvmsg=new Net_Message;
    m_recvstate=0;
  }

  while (!retv && m_con->recv_bytes_available()>0)
  {
    char buf[8192];
    int bufl=m_con->peek_bytes(buf,sizeof(buf));
    int a=0;

    if (!m_recvstate)
    {
      a=m_recvmsg->parseMessageHeader(buf,bufl);
      if (a<0)
      {
        m_error=-1;
        break;
      }
      if (a==0) break;
      m_recvstate=1;
    }
    int b2=m_recvmsg->parseAddBytes(buf+a,bufl-a);

    m_con->recv_bytes(buf,b2+a); // dump our bytes that we used

    if (m_recvmsg->parseBytesNeeded()<1)
    {
      retv=m_recvmsg;
      m_recvmsg=0;
      m_recvstate=0;
    }
    if (wantsleep) *wantsleep=0;
  }

  {
    int s=0,r=0;
    m_con->run(-1,-1,&s,&r);
    if (wantsleep && (s||r)) *wantsleep=0;
  }

  if (retv)
  {
    m_last_recv=now;
  }
  else if (now > m_last_recv + m_keepalive*3)
  {
    m_error=-3;
  }

  return retv;
}

int Net_Connection::Send(Net_Message *msg)
{
  if (msg)
  {
    msg->addRef();
    if (m_sendq.GetSize() < NET_CON_MAX_MESSAGES*(int)sizeof(Net_Message *))
      m_sendq.Add(&msg,sizeof(Net_Message *));
    else
    {
      m_error=-2;
      msg->releaseRef(); // todo: debug message to log overrun error
      return -1;
    }

#if 0
    if (m_con)
    {
      m_con->run();

      while (m_con->send_bytes_available()>64 && m_sendq.Available()>0)
      {
        Net_Message **topofq = (Net_Message **)m_sendq.Get();

        if (!topofq) break;
        Net_Message *sendm=*topofq;
        if (sendm)
        {
          if (m_msgsendpos<0) // send header
          {
            char buf[32];
            int hdrlen=sendm->makeMessageHeader(buf);
            m_con->send_bytes(buf,hdrlen);

            m_msgsendpos=0;
          }

          int sz=sendm->get_size()-m_msgsendpos;
          if (sz < 1) // end of message, discard and move to next
          {
            sendm->releaseRef();
            m_sendq.Advance(sizeof(Net_Message*));
            m_msgsendpos=-1;
          }
          else
          {
            int avail=m_con->send_bytes_available();
            if (sz > avail) sz=avail;
            if (sz>0)
            {
              m_con->send_bytes((char*)sendm->get_data()+m_msgsendpos,sz);
              m_msgsendpos+=sz;
            }
          }
        }
        else
        {
          m_sendq.Advance(sizeof(Net_Message*));
          m_msgsendpos=-1;
        }
        m_con->run();
      }

      m_sendq.Compact();
    }
  #endif

  }
  return 0;
}

int Net_Connection::GetStatus()
{
  if (m_error) return m_error;
  return !m_con || m_con->get_state()<JNL_Connection::STATE_RESOLVING || m_con->get_state()>=JNL_Connection::STATE_CLOSING; // 1 if disconnected somehow
}

Net_Connection::~Net_Connection()
{
  Net_Message **p=(Net_Message **)m_sendq.Get();
  if (p)
  {
    int n=m_sendq.Available()/sizeof(Net_Message *);
    while (n-->0)
    {
      (*p)->releaseRef();
      p++;
    }
    m_sendq.Advance(m_sendq.Available());

  }

  delete m_con;
  delete m_recvmsg;

}

void Net_Connection::Kill(int quick)
{
  m_con->close();
}


code:
/*
    NINJAM - netmsg.h
    Copyright (C) 2005-2007 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 header provides the declarations for the Net_Messsage class, and
  Net_Connection class (handles sending and receiving Net_Messages to
  a JNetLib JNL_Connection).
*/

#ifndef _NETMSG_H_
#define _NETMSG_H_

#include <WDL/queue.h>
#include <WDL/jnetlib/jnetlib.h>

#define NET_MESSAGE_MAX_SIZE 16384

#define NET_CON_MAX_MESSAGES 512

#define MESSAGE_KEEPALIVE 0xfd
#define MESSAGE_EXTENDED 0xfe
#define MESSAGE_INVALID 0xff

#define NET_CON_KEEPALIVE_RATE 3

class Net_Message
{
	public:
		Net_Message() : m_parsepos(0), m_refcnt(0), m_type(MESSAGE_INVALID)
		{
		}
		~Net_Message()
		{
		}

		void set_type(int type)	{ m_type=type; }
		int  get_type() { return m_type; }

		void set_size(int newsize) { m_hb.Resize(newsize); }
		int get_size() { return m_hb.GetSize(); }

		void *get_data() { return m_hb.Get(); }

		int parseMessageHeader(void *data, int len); // returns bytes used, if any (or 0 if more data needed), or -1 if invalid
    int parseBytesNeeded();
    int parseAddBytes(void *data, int len); // returns bytes actually added

		int makeMessageHeader(void *data); // makes message header, returns length. data should be at least 16 bytes to be safe

		void addRef() { ++m_refcnt; }
		void releaseRef() { if (--m_refcnt < 1) delete this; }

	private:
    		int m_parsepos;
		int m_refcnt;
		int m_type;
		WDL_HeapBuf m_hb;
};

class Net_Connection
{
  public:
    Net_Connection() : m_error(0),m_msgsendpos(-1), m_recvstate(0),m_recvmsg(0),m_con(0)
    {
      SetKeepAlive(0);
    }
    ~Net_Connection();

    void attach(JNL_Connection *con)
    {
      m_con=con;
    }

    Net_Message *Run(int *wantsleep=0);
    int Send(Net_Message *msg); // -1 on error, i.e. queue full
    int GetStatus(); // returns <0 on error, 0 on normal, 1 on disconnect
    JNL_Connection *GetConnection() { return m_con; }

    void SetKeepAlive(int interval)
    {
      m_keepalive=interval?interval:NET_CON_KEEPALIVE_RATE;
      m_last_send=m_last_recv=time(NULL);
    }

    void Kill(int quick=0);

  private:
    int m_error;

    int m_keepalive;
    int m_msgsendpos;

    time_t m_last_send, m_last_recv;

    int m_recvstate;
    Net_Message *m_recvmsg;

    JNL_Connection *m_con;
    WDL_Queue m_sendq;

};

#endif