
static float ampCh[24] = { 
   0.0f, 0.0f, // Analog 1&2 [1]
   1.0f, 1.0f, // Analog 3&4 [1] Korg Radias
   1.0f, 1.0f, // Analog 5&6 [1] Yamaha FS1R
   1.0f, 1.0f, // Analog 7&8 [1] Roland MKS 50
   0.0f, 0.0f, // SPDIF L/R  [1]
   0.0f, 0.0f, // Mixer L/R  [1]
   1.0f, 1.0f, // Analog 1&2 [2] Roland Juno-G
   0.0f, 0.0f, // Analog 3&4 [2] Yamaha a5000?? 
   1.0f, 1.0f, // Analog 5&6 [2] Waldorf Micro-Q
   1.0f, 1.0f, // Analog 7&8 [2] Roland Alpha Juno 2
   0.0f, 0.0f, // SPDIF L/R  [2]
   0.0f, 0.0f, // Mixer L/R  [2]
};

static float boostAmp = 0.6f;//0.75f;

/*
--------------------------------------- device #1
Name                        = M-Audio Delta ASIO
Host API                    = ASIO
Max inputs = 24, Max outputs = 20
Default low input latency   =    0.012
Default low output latency  =    0.012
Default high input latency  =    0.035
Default high output latency =    0.035
ASIO minimum buffer size    = 64
ASIO maximum buffer size    = 2048
ASIO preferred buffer size  = 512
ASIO buffer granularity     = 0
ASIO input ch[0] name="Analog In 1 Delta-1010LT [1]".
ASIO input ch[1] name="Analog In 2 Delta-1010LT [1]".
ASIO input ch[2] name="Analog In 3 Delta-1010LT [1]".
ASIO input ch[3] name="Analog In 4 Delta-1010LT [1]".
ASIO input ch[4] name="Analog In 5 Delta-1010LT [1]".
ASIO input ch[5] name="Analog In 6 Delta-1010LT [1]".
ASIO input ch[6] name="Analog In 7 Delta-1010LT [1]".
ASIO input ch[7] name="Analog In 8 Delta-1010LT [1]".
ASIO input ch[8] name="SPDIF In L Delta-1010LT [1]".
ASIO input ch[9] name="SPDIF In R Delta-1010LT [1]".
ASIO input ch[10] name="Mixer In L Delta-1010LT [1]".
ASIO input ch[11] name="Mixer In R Delta-1010LT [1]".
ASIO input ch[12] name="Analog In 1 Delta-1010LT [2]".
ASIO input ch[13] name="Analog In 2 Delta-1010LT [2]".
ASIO input ch[14] name="Analog In 3 Delta-1010LT [2]".
ASIO input ch[15] name="Analog In 4 Delta-1010LT [2]".
ASIO input ch[16] name="Analog In 5 Delta-1010LT [2]".
ASIO input ch[17] name="Analog In 6 Delta-1010LT [2]".
ASIO input ch[18] name="Analog In 7 Delta-1010LT [2]".
ASIO input ch[19] name="Analog In 8 Delta-1010LT [2]".
ASIO input ch[20] name="SPDIF In L Delta-1010LT [2]".
ASIO input ch[21] name="SPDIF In R Delta-1010LT [2]".
ASIO input ch[22] name="Mixer In L Delta-1010LT [2]".
ASIO input ch[23] name="Mixer In R Delta-1010LT [2]".

ASIO output ch[0] name="Analog Out 1/2 Delta-1010LT [1]".
ASIO output ch[1] name="Analog Out 1/2 Delta-1010LT [1]".
ASIO output ch[2] name="Analog Out 3/4 Delta-1010LT [1]".
ASIO output ch[3] name="Analog Out 3/4 Delta-1010LT [1]".
ASIO output ch[4] name="Analog Out 5/6 Delta-1010LT [1]".
ASIO output ch[5] name="Analog Out 5/6 Delta-1010LT [1]".
ASIO output ch[6] name="Analog Out 7/8 Delta-1010LT [1]".
ASIO output ch[7] name="Analog Out 7/8 Delta-1010LT [1]".
ASIO output ch[8] name="SPDIF Out L/R Delta-1010LT [1]".
ASIO output ch[9] name="SPDIF Out L/R Delta-1010LT [1]".
ASIO output ch[10] name="Analog Out 1/2 Delta-1010LT [2]".
ASIO output ch[11] name="Analog Out 1/2 Delta-1010LT [2]".
ASIO output ch[12] name="Analog Out 3/4 Delta-1010LT [2]".
ASIO output ch[13] name="Analog Out 3/4 Delta-1010LT [2]".
ASIO output ch[14] name="Analog Out 5/6 Delta-1010LT [2]".
ASIO output ch[15] name="Analog Out 5/6 Delta-1010LT [2]".
ASIO output ch[16] name="Analog Out 7/8 Delta-1010LT [2]".
ASIO output ch[17] name="Analog Out 7/8 Delta-1010LT [2]".
ASIO output ch[18] name="SPDIF Out L/R Delta-1010LT [2]".
ASIO output ch[19] name="SPDIF Out L/R Delta-1010LT [2]".
*/

#define NUM_INPUT_CHANNELS 20
//#define NUM_INPUT_CHANNELS 6 // XXXX
//#define NUM_OUTPUT_CHANNELS 18
#define NUM_OUTPUT_CHANNELS 2

#define OUTPUT_24BIT defined

//#define MASTER_OUT_L 6
//#define MASTER_OUT_R 7

#define MASTER_OUT_L 0
#define MASTER_OUT_R 1

#include <stdio.h>
#include <string.h>
#include <math.h>
#include "portaudio.h"

#ifdef WIN32
#ifndef PA_NO_ASIO
#include "pa_asio.h"
#endif
#endif

/*******************************************************************/
static void PrintSupportedStandardSampleRates(
        const PaStreamParameters *inputParameters,
        const PaStreamParameters *outputParameters )
{
    static double standardSampleRates[] = {
        8000.0, 9600.0, 11025.0, 12000.0, 16000.0, 22050.0, 24000.0, 32000.0,
        44100.0, 48000.0, 88200.0, 96000.0, 192000.0, -1 /* negative terminated  list */
    };
    int     i, printCount;
    PaError err;

    printCount = 0;
    for( i=0; standardSampleRates[i] > 0; i++ )
    {
        err = Pa_IsFormatSupported( inputParameters, outputParameters, standardSampleRates[i] );
        if( err == paFormatIsSupported )
        {
            if( printCount == 0 )
            {
                printf( "\t%8.2f", standardSampleRates[i] );
                printCount = 1;
            }
            else if( printCount == 4 )
            {
                printf( ",\n\t%8.2f", standardSampleRates[i] );
                printCount = 1;
            }
            else
            {
                printf( ", %8.2f", standardSampleRates[i] );
                ++printCount;
            }
        }
    }
    if( !printCount )
        printf( "None\n" );
    else
        printf( "\n" );
}
/*******************************************************************/
int list_portaudio_devices(void)
{
    int     i, numDevices, defaultDisplayed;
    const   PaDeviceInfo *deviceInfo;
    PaStreamParameters inputParameters, outputParameters;
    PaError err;

    
    Pa_Initialize();

    printf( "PortAudio version number = %d\nPortAudio version text = '%s'\n",
            Pa_GetVersion(), Pa_GetVersionText() );

            
    numDevices = Pa_GetDeviceCount();
    if( numDevices < 0 )
    {
        printf( "ERROR: Pa_CountDevices returned 0x%x\n", numDevices );
        err = numDevices;
        goto error;
    }
    
    printf( "Number of devices = %d\n", numDevices );
    for( i=0; i<numDevices; i++ )
    {
        deviceInfo = Pa_GetDeviceInfo( i );
        printf( "--------------------------------------- device #%d\n", i );
                
    /* Mark global and API specific default devices */
        defaultDisplayed = 0;
        if( i == Pa_GetDefaultInputDevice() )
        {
            printf( "[ Default Input" );
            defaultDisplayed = 1;
        }
        else if( i == Pa_GetHostApiInfo( deviceInfo->hostApi )->defaultInputDevice )
        {
            const PaHostApiInfo *hostInfo = Pa_GetHostApiInfo( deviceInfo->hostApi );
            printf( "[ Default %s Input", hostInfo->name );
            defaultDisplayed = 1;
        }
        
        if( i == Pa_GetDefaultOutputDevice() )
        {
            printf( (defaultDisplayed ? "," : "[") );
            printf( " Default Output" );
            defaultDisplayed = 1;
        }
        else if( i == Pa_GetHostApiInfo( deviceInfo->hostApi )->defaultOutputDevice )
        {
            const PaHostApiInfo *hostInfo = Pa_GetHostApiInfo( deviceInfo->hostApi );
            printf( (defaultDisplayed ? "," : "[") );                
            printf( " Default %s Output", hostInfo->name );
            defaultDisplayed = 1;
        }

        if( defaultDisplayed )
            printf( " ]\n" );

    /* print device info fields */
        printf( "Name                        = %s\n", deviceInfo->name );
        printf( "Host API                    = %s\n",  Pa_GetHostApiInfo( deviceInfo->hostApi )->name );
        printf( "Max inputs = %d", deviceInfo->maxInputChannels  );
        printf( ", Max outputs = %d\n", deviceInfo->maxOutputChannels  );

        printf( "Default low input latency   = %8.3f\n", deviceInfo->defaultLowInputLatency  );
        printf( "Default low output latency  = %8.3f\n", deviceInfo->defaultLowOutputLatency  );
        printf( "Default high input latency  = %8.3f\n", deviceInfo->defaultHighInputLatency  );
        printf( "Default high output latency = %8.3f\n", deviceInfo->defaultHighOutputLatency  );

#ifdef WIN32
#ifndef PA_NO_ASIO
/* ASIO specific latency information */
        if( Pa_GetHostApiInfo( deviceInfo->hostApi )->type == paASIO ){
            long minLatency, maxLatency, preferredLatency, granularity;
            const char *channel_name;
            int ch;

            err = PaAsio_GetAvailableLatencyValues( i,
		            &minLatency, &maxLatency, &preferredLatency, &granularity );

            printf( "ASIO minimum buffer size    = %ld\n", minLatency  );
            printf( "ASIO maximum buffer size    = %ld\n", maxLatency  );
            printf( "ASIO preferred buffer size  = %ld\n", preferredLatency  );

            if( granularity == -1 )
                printf( "ASIO buffer granularity     = power of 2\n" );
            else
                printf( "ASIO buffer granularity     = %ld\n", granularity  );

            for(ch=0; ch<deviceInfo->maxInputChannels; ch++)
            {
               PaAsio_GetInputChannelName(i, ch, &channel_name);
               printf("ASIO input ch[%i] name=\"%s\".\n", ch, channel_name);
            }
            printf("\n");
            for(ch=0; ch<deviceInfo->maxOutputChannels; ch++)
            {
               PaAsio_GetOutputChannelName(i, ch, &channel_name);
               printf("ASIO output ch[%i] name=\"%s\".\n", ch, channel_name);
            }
        }
#endif /* !PA_NO_ASIO */
#endif /* WIN32 */

        printf( "Default sample rate         = %8.2f\n", deviceInfo->defaultSampleRate );

    /* poll for standard sample rates */
        inputParameters.device = i;
        inputParameters.channelCount = deviceInfo->maxInputChannels;
        inputParameters.sampleFormat = paInt16;
        inputParameters.suggestedLatency = 0; /* ignored by Pa_IsFormatSupported() */
        inputParameters.hostApiSpecificStreamInfo = NULL;
        
        outputParameters.device = i;
        outputParameters.channelCount = deviceInfo->maxOutputChannels;
        outputParameters.sampleFormat = paInt16;
        outputParameters.suggestedLatency = 0; /* ignored by Pa_IsFormatSupported() */
        outputParameters.hostApiSpecificStreamInfo = NULL;
#if 0
        if( inputParameters.channelCount > 0 )
        {
            printf("Supported standard sample rates\n for half-duplex 16 bit %d channel input = \n",
                    inputParameters.channelCount );
            PrintSupportedStandardSampleRates( &inputParameters, NULL );
        }

        if( outputParameters.channelCount > 0 )
        {
            printf("Supported standard sample rates\n for half-duplex 16 bit %d channel output = \n",
                    outputParameters.channelCount );
            PrintSupportedStandardSampleRates( NULL, &outputParameters );
        }

        if( inputParameters.channelCount > 0 && outputParameters.channelCount > 0 )
        {
            printf("Supported standard sample rates\n for full-duplex 16 bit %d channel input, %d channel output = \n",
                    inputParameters.channelCount, outputParameters.channelCount );
            PrintSupportedStandardSampleRates( &inputParameters, &outputParameters );
        }
#endif
    }

    Pa_Terminate();

    printf("----------------------------------------------\n");
    return 0;

error:
    Pa_Terminate();
    fprintf( stderr, "An error occured while using the portaudio stream\n" );
    fprintf( stderr, "Error number: %d\n", err );
    fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
    return err;
}


/*
PaError Pa_OpenStream( PaStream** stream,
                       const PaStreamParameters *inputParameters,
                       const PaStreamParameters *outputParameters,
                       double sampleRate,
                       unsigned long framesPerBuffer,
                       PaStreamFlags streamFlags,
                       PaStreamCallback *streamCallback,
                       void *userData );


  */

static int cbk_count = 0;
static unsigned long maxFrameCount = 0;
#ifndef M_PI
#define M_PI            3.14159265358979323846
#endif
static double sinAng = 0.0;


int stream_callback(const void *input, void *output,
                    unsigned long frameCount,
                    const PaStreamCallbackTimeInfo* timeInfo,
                    PaStreamCallbackFlags statusFlags,
                    void *userData )
{
   cbk_count++;
   if(frameCount > maxFrameCount)
      maxFrameCount = frameCount;

#ifdef OUTPUT_24BIT
   memset(output, 0, 3*NUM_OUTPUT_CHANNELS);
#else
   memset(output, 0, 2*NUM_OUTPUT_CHANNELS);
#endif

   if(0)
   {
      // Write sinewave to master output
      unsigned long i;
#ifndef OUTPUT_24BIT
      short *d;

      d = &((short*)output)[MASTER_OUT_L];
      for(i=0; i<frameCount; i++)
      {
         short v = (short) (sin(sinAng)*32767.0f);
         d[0] = v;
         d[1] = v;
         sinAng += M_PI*2*(400.0f/44100.f);
         if(sinAng>=(M_PI*2))
            sinAng -= (M_PI*2);
         d += NUM_OUTPUT_CHANNELS;
      }
#else
      char *d;

      d = &((char*)output)[3*MASTER_OUT_L];
      for(i=0; i<frameCount; i++)
      {
         float v = (float) sin(sinAng);

         int outL = (int) (8388607.0f * v);
         int outR = (int) (8388607.0f * v);
         d[0+0] = outL&0xFF;
         d[0+1] = (outL&0xFF00)>>8;
         d[0+2] = (outL&0xFF0000)>>16;
         d[3+0] = outR&0xFF;
         d[3+1] = (outR&0xFF00)>>8;
         d[3+2] = (outR&0xFF0000)>>16;

         sinAng += M_PI*2*(400.0f/44100.f);
         if(sinAng>=(M_PI*2))
            sinAng -= (M_PI*2);
         d += 3*NUM_OUTPUT_CHANNELS;
      }

#endif // OUTPUT_24BIT
   }

   if(0)
   {
      // Write inputs 5&6 to outputs 1&2
      unsigned long i;
#ifndef OUTPUT_24BIT
      short *d;
      short *s;

      d = &((short*)output)[MASTER_OUT_L];
      s = &((short*)input)[4];
      for(i=0; i<frameCount; i++)
      {
         d[0] = s[0];
         d[1] = s[1];
         d += NUM_OUTPUT_CHANNELS;
         s += NUM_INPUT_CHANNELS;
      }
#else
      char *d;
      short *s;

      d = &((char*)output)[3*MASTER_OUT_L];
      s = &((short*)input)[4];
      for(i=0; i<frameCount; i++)
      {
         int outL = s[0] * 256;
         int outR = s[1] * 256;
         d[0+0] = outL&0xFF;
         d[0+1] = (outL&0xFF00)>>8;
         d[0+2] = (outL&0xFF0000)>>16;
         d[3+0] = outR&0xFF;
         d[3+1] = (outR&0xFF00)>>8;
         d[3+2] = (outR&0xFF0000)>>16;
         d += 3*NUM_OUTPUT_CHANNELS;
         s += NUM_INPUT_CHANNELS;
      }
#endif // OUTPUT_24BIT

   }

   if(1)
   {
      // Accumulate all input buffers and write 24bit output
      unsigned long i;
      int ch;
      short *s;
#ifdef OUTPUT_24BIT
      char *d;
      int outL;
      int outR;
#else
      short *d;
      short outL;
      short outR;
#endif
      float accumL;
      float accumR;

      s= &((short *)input)[0];
#ifdef OUTPUT_24BIT
      d = &((char *)output)[3*MASTER_OUT_L];
#else
      d = &((short*)output)[MASTER_OUT_L];
#endif

      for(i=0; i<frameCount; i++)
      {
         accumL = 0.0f;
         for(ch=0; ch<NUM_INPUT_CHANNELS; ch+=2)
         {
            if( (ch<8) || (ch>11) )
            {
               //if(ch==12)
               {
                  accumL += (s[ch])/32767.0f * ampCh[ch];
               }
            }
         }
         accumR = 0.0f;
         for(ch=1; ch<NUM_INPUT_CHANNELS; ch+=2)
         {
            if( (ch<8) || (ch>11) )
            {
               //if(ch==13)
               {
                  accumR += (s[ch])/32767.0f * ampCh[ch];
               }
            }
         }
         accumL *= boostAmp;
         accumR *= boostAmp;

#ifdef OUTPUT_24BIT
         outL = (int) (8388607.0f * accumL);
         outR = (int) (8388607.0f * accumR);
         d[0+0] = outL&0xFF;
         d[0+1] = (outL&0xFF00)>>8;
         d[0+2] = (outL&0xFF0000)>>16;
         d[3+0] = outR&0xFF;
         d[3+1] = (outR&0xFF00)>>8;
         d[3+2] = (outR&0xFF0000)>>16;
         d += 3*NUM_OUTPUT_CHANNELS;
#else
         outL = (short) (32767.0f * accumL);
         outR = (short) (32767.0f * accumR);
         d[0] = outL;
         d[1] = outR;
         d += NUM_OUTPUT_CHANNELS;
#endif
         s += NUM_INPUT_CHANNELS;
      }
   }


   return paContinue;
}


int main(int _argc, char **_argv) {
   PaStreamParameters inputParams;
   PaStreamParameters outputParams;
   const PaDeviceInfo *deviceInfo;
   PaStream *stream;
   PaError err;

   list_portaudio_devices();

   // Open i/o streams

   Pa_Initialize();

   deviceInfo = Pa_GetDeviceInfo( 0 );

   inputParams.device = 0; // M-Audio Delta 1010 LT
   inputParams.channelCount = NUM_INPUT_CHANNELS; 
   inputParams.sampleFormat = paInt16;
   //inputParams.suggestedLatency = deviceInfo->defaultHighInputLatency;
   inputParams.suggestedLatency = deviceInfo->defaultLowInputLatency;
   printf("xxx suggestedLatency=%g\n", inputParams.suggestedLatency);
   inputParams.hostApiSpecificStreamInfo = NULL;

   outputParams.device = 0;
   outputParams.channelCount = NUM_OUTPUT_CHANNELS;
#ifdef OUTPUT_24BIT
   outputParams.sampleFormat = paInt24;
#else
   outputParams.sampleFormat = paInt16;
#endif
   //outputParams.suggestedLatency = deviceInfo->defaultHighOutputLatency;
   outputParams.suggestedLatency = deviceInfo->defaultLowOutputLatency;
   outputParams.hostApiSpecificStreamInfo = NULL;
   
   err = Pa_OpenStream(&stream, &inputParams, &outputParams, 48000.0, 
                       paFramesPerBufferUnspecified, 
                       0, // StreamFlags
                       stream_callback,
                       NULL // User data
                       );

   if(err == paNoError)
   {
      Pa_StartStream(stream);

      while(1)
      {
         printf("xxx cbk_count=%i maxFrameCount=%i\n", cbk_count, maxFrameCount);
         Pa_Sleep(1000);
      }

      Pa_AbortStream(stream);
      Pa_CloseStream(stream);
   }
   else
   {
      printf("[---] PA_OpenStream failed. err=%08x\n", err);
   }

   Pa_Terminate();


   return 0;
}

