<commit_before>/*
    rtpa.c:

    Copyright (C) 2004, 2005 <NAME>, <NAME>,
                             <NAME>, <NAME>

    This file is part of Csound.

    The Csound Library is free software; you can redistribute it
    and/or modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    Csound 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 Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with Csound; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
    02111-1307 USA
*/

/*                                              RTPA.C for PortAudio    */

#include "csdl.h"
#if !defined(WIN32)
#include "soundio.h"
#endif
#include <portaudio.h>

#define NO_FULLDUPLEX_PA_LOCK   0   

typedef struct PaAlsaStreamInfo {
    unsigned long   size;
    PaHostApiTypeId hostApiType;
    unsigned long   version;
    const char      *deviceString;
} PaAlsaStreamInfo;

typedef struct devparams_ {
    PaStream    *handle;        /* stream handle                    */
    float       *buf;           /* sample conversion buffer         */
    int         nchns;          /* number of channels               */
} DEVPARAMS;

typedef struct PA_BLOCKING_STREAM_ {
    CSOUND      *csound;
    PaStream    *paStream;
    int         mode;                   /* 1: rec, 2: play, 3: full-duplex  */
    int         noPaLock;
    int         inBufSamples;
    int         outBufSamples;
    int         currentInputIndex;
    int         currentOutputIndex;
    float       *inputBuffer;
    float       *outputBuffer;
    void        *paLock;                /* thread lock for stream callback  */
    void        *clientLock;            /* thread lock for rtplay/rtrecord  */
    csRtAudioParams inParm;
    csRtAudioParams outParm;
    PaStreamParameters inputPaParameters;
    PaStreamParameters outputPaParameters;
#ifdef WIN32
    int         paused;                 /* VL: to allow for smooth pausing  */
#endif
    int         paLockTimeout;
} PA_BLOCKING_STREAM;

static int pa_PrintErrMsg(CSOUND *csound, const char *fmt, ...)
{
    va_list args;
    va_start(args, fmt);
    csound->ErrMsgV(csound, Str(" *** PortAudio: error: "), fmt, args);
    va_end(args);
    return -1;
}

static int initPortAudio(CSOUND *csound)
{
    char  *s;
    int   err;
    /* initialise PortAudio */
    if (!csound->QueryGlobalVariable(csound, "::PortAudio::NeedsTerminate")) {
      if (csound->CreateGlobalVariable(csound,
                                       "::PortAudio::NeedsTerminate", 1) != 0)
        return -1;
      err = (int) Pa_Initialize();
      if (err != (int) paNoError) {
        return pa_PrintErrMsg(csound, "%d: %s",
                                      err, Pa_GetErrorText((PaError) err));
      }
      /* print PortAudio version */
      if ((s = (char*) Pa_GetVersionText()) != NULL)
        csound->Message(csound, "%s\n", s);
    }
    return 0;
}

/* list available input or output devices; returns the number of devices */

static int listPortAudioDevices_blocking(CSOUND *csound,
                                         int print_list, int play)
{
    PaDeviceInfo  *dev_info;
    int           i, j, ndev;

    ndev = (int) Pa_GetDeviceCount();
    for (i = j = 0; i < ndev; i++) {
      dev_info = (PaDeviceInfo*) Pa_GetDeviceInfo((PaDeviceIndex) i);
      if ((play && dev_info->maxOutputChannels > 0) ||
          (!play && dev_info->maxInputChannels > 0))
        j++;
    }
    if (!j) {
      pa_PrintErrMsg(csound, Str("No %s devices are available\n"),
                             (play ? Str("output") : Str("input")));
      return 0;
    }
    if (!print_list)
      return j;
    csound->Message(csound, Str("PortAudio: available %s devices:\n"),
                            (play ? Str("output") : Str("input")));
    for (i = j = 0; i < ndev; i++) {
      dev_info = (PaDeviceInfo*) Pa_GetDeviceInfo((PaDeviceIndex) i);
      if ((play && dev_info->maxOutputChannels > 0) ||
          (!play && dev_info->maxInputChannels > 0)) {
        csound->Message(csound, " %3d: %s\n", j, dev_info->name);
        j++;
      }
    }
    return j;
}

/* select PortAudio device; returns the actual device number */

static int selectPortAudioDevice(CSOUND *csound, int devNum, int play)
{
    PaDeviceInfo  *dev_info;
    int           i, j, maxNum;

    maxNum = listPortAudioDevices_blocking(csound, 1, play) - 1;
    if (maxNum < 0)
      return -1;
    if (devNum == 1024) {
      if (play)
        devNum = (int) Pa_GetDefaultOutputDevice();
      else
        devNum = (int) Pa_GetDefaultInputDevice();
    }
    else {
      if (devNum < 0 || devNum > maxNum) {
        /* listPortAudioDevices_blocking(csound, 1, play); */
        pa_PrintErrMsg(csound, Str("%s device number %d is out of range"),
                               (play ? Str("output") : Str("input")), devNum);
        return -1;
      }
      for (i = j = 0; j <= maxNum; i++) {
        dev_info = (PaDeviceInfo*) Pa_GetDeviceInfo((PaDeviceIndex) i);
        if ((play && dev_info->maxOutputChannels > 0) ||
            (!play && dev_info->maxInputChannels > 0)) {
          if (j == devNum)
            break;
          j++;
        }
      }
      devNum = i;
    }
    dev_info = (PaDeviceInfo*) Pa_GetDeviceInfo((PaDeviceIndex) devNum);
    if (dev_info)
      csound->Message(csound, Str("PortAudio: selected %s device '%s'\n"),
                      (play ? Str("output") : Str("input")),
                      dev_info->name);
    else
      csound->Message(csound, Str("PortAudio: failed to obtain device info.\n"));
    return devNum;
}

static int pa_SetStreamParameters(CSOUND *csound, PaStreamParameters *sp,
                                                  csRtAudioParams *parm,
                                                  int is_playback)
{
    int dev;

    memset(sp, 0, sizeof(PaStreamParameters));
    if (parm->devName != NULL && parm->devName[0] != '\0') {
      return pa_PrintErrMsg(csound,
                            Str("Must specify a device number, not a name"));
    }
    dev = selectPortAudioDevice(csound, parm->devNum, is_playback);
    if (dev < 0)
      return -1;
    sp->device = (PaDeviceIndex) dev;
    sp->channelCount = (parm->nChannels < 2 ? 2 : parm->nChannels);
    sp->sampleFormat = (PaSampleFormat) paFloat32;
    sp->suggestedLatency = (PaTime) ((double) parm->bufSamp_HW
                                     / (double) parm->sampleRate);
    sp->hostApiSpecificStreamInfo = NULL;
    return 0;
}

static void rtclose_(CSOUND *csound);
static int  paBlockingReadWriteStreamCallback(const void *, void *,
                                              unsigned long,
                                              const PaStreamCallbackTimeInfo *,
                                              PaStreamCallbackFlags,
                                              void *);

static int paBlockingReadWriteOpen(CSOUND *csound)
{
    PA_BLOCKING_STREAM  *pabs;
    PaStream            *stream = NULL;
    PaError             err;

    pabs = (PA_BLOCKING_STREAM*) csound->QueryGlobalVariable(csound,
                                                             "_rtpaGlobals");
    if (pabs == NULL)
      return -1;
    if (initPortAudio(csound) != 0)
      goto err_return;

    if ((int) Pa_GetDeviceCount() <= 0) {
      pa_PrintErrMsg(csound, Str("No sound device is available"));
      goto err_return;
    }

    if (pabs->mode & 1) {
      if (pa_SetStreamParameters(csound, &(pabs->inputPaParameters),
                                         &(pabs->inParm), 0) != 0)
        goto err_return;
      pabs->inBufSamples = pabs->inParm.bufSamp_SW
                           * (int) pabs->inputPaParameters.channelCount;
      pabs->inputBuffer = (float*) calloc((size_t) pabs->inBufSamples,
                                          sizeof(float));
      if (pabs->inputBuffer == NULL) {
        pa_PrintErrMsg(csound, Str("Memory allocation failure"));
        goto err_return;
      }
    }
    if (pabs->mode & 2) {
      if (pa_SetStreamParameters(csound, &(pabs->outputPaParameters),
                                         &(pabs->outParm), 1) != 0)
        goto err_return;
      pabs->outBufSamples = pabs->outParm.bufSamp_SW
                            * (int) pabs->outputPaParameters.channelCount;
      pabs->outputBuffer = (float*) calloc((size_t) pabs->outBufSamples,
                                           sizeof(float));
      if (pabs->outputBuffer == NULL) {
        pa_PrintErrMsg(csound, Str("Memory allocation failure"));
        goto err_return;
      }
    }
    if ((pabs->mode & 3) == 3) {
      if (pabs->inParm.bufSamp_SW != pabs->outParm.bufSamp_SW) {
        pa_PrintErrMsg(csound, Str("Inconsistent full-duplex buffer sizes"));
        goto err_return;
      }
      if (pabs->inParm.sampleRate != pabs->outParm.sampleRate) {
        pa_PrintErrMsg(csound, Str("Inconsistent full-duplex sample rates"));
        goto err_return;
      }
      if (((pabs->inParm.bufSamp_SW / csound->ksmps) * csound->ksmps)
          != pabs->inParm.bufSamp_SW)
        csound->MessageS(csound,
                         CSOUNDMSG_WARNING,
                         Str("WARNING: buffer size should be an integer "
                             "multiple of ksmps in full-duplex mode\n"));
#if NO_FULLDUPLEX_PA_LOCK
      pabs->noPaLock = 1;
#endif
    }

    pabs->paLock = csound->CreateThreadLock();
    if (pabs->paLock == NULL)
      goto err_return;
    pabs->clientLock = csound->CreateThreadLock();
    if (pabs->clientLock == NULL)
      goto err_return;
#if NO_FULLDUPLEX_PA_LOCK
    if (!pabs->noPaLock)
#endif
      csound->WaitThreadLock(pabs->paLock, (size_t) 500);
    csound->WaitThreadLock(pabs->clientLock, (size_t) 500);

    pabs->paLockTimeout =
        (int) (1.33 * (pabs->outputPaParameters.suggestedLatency
                         >= pabs->inputPaParameters.suggestedLatency ?
                       pabs->outputPaParameters.suggestedLatency
                       : pabs->inputPaParameters.suggestedLatency));
    if (pabs->paLockTimeout < 25)
      pabs->paLockTimeout = 25;
    else if (pabs->paLockTimeout > 1000)
      pabs->paLockTimeout = 1000;


    err = Pa_OpenStream(&stream,
                        (pabs->mode & 1 ? &(pabs->inputPaParameters)
                                          : (PaStreamParameters*) NULL),
                        (pabs->mode & 2 ? &(pabs->outputPaParameters)
                                          : (PaStreamParameters*) NULL),
                        (double) (pabs->mode & 2 ? pabs->outParm.sampleRate
                                                   : pabs->inParm.sampleRate),
                        (unsigned long) (pabs->mode & 2 ?
                                         pabs->outParm.bufSamp_SW
                                         : pabs->inParm.bufSamp_SW),
                        (csound->dither_output ?
                         (PaStreamFlags) paNoFlag : (PaStreamFlags) paDitherOff),
                        paBlockingReadWriteStreamCallback,
                        (void*) pabs);
    if (err != paNoError) {
      pa_PrintErrMsg(csound, "%d: %s", (int) err, Pa_GetErrorText(err));
      goto err_return;
    }

    err = Pa_StartStream(stream);
    if (err != paNoError) {
      Pa_CloseStream(stream);
      pa_PrintErrMsg(csound, "%d: %s", (int) err, Pa_GetErrorText(err));
      goto err_return;
    }

    pabs->paStream = stream;
    return 0;

    /* clean up and report error */
 err_return:
    rtclose_(csound);
    return -1;
}

static CS_NOINLINE void paClearOutputBuffer(PA_BLOCKING_STREAM *pabs,
                                            float *buf)
{
    int   nsmps = pabs->outBufSamples;
    memset(buf,0, nsmps*sizeof(float));
    /* int   i = 0; */
    /* do { */
    /*   buf[i] = 0.0f; */
    /* } while (++i < nsmps); */
}

static int paBlockingReadWriteStreamCallback(const void *input,
                                             void *output,
                                             unsigned long frameCount,
                                             const PaStreamCallbackTimeInfo
                                                 *timeInfo,
                                             PaStreamCallbackFlags statusFlags,
                                             void *userData)
{
    int     err;
    PA_BLOCKING_STREAM *pabs = (PA_BLOCKING_STREAM*) userData;
    CSOUND  *csound = pabs->csound;
    float   *paInput = (float*) input;
    float   *paOutput = (float*) output;

   
#ifdef WIN32    
if (pabs->paStream == NULL
      || pabs->paused
) {
if (pabs->mode & 2)
        paClearOutputBuffer(pabs, paOutput);
return paContinue;
}
#endif

#if NO_FULLDUPLEX_PA_LOCK
    err = 0;
    if (!pabs->noPaLock)
#endif
      /*#ifndef __MACH__*/
      /*#  ifdef WIN32 */
      err = csound->WaitThreadLock(pabs->paLock, (size_t) pabs->paLockTimeout);
    /*#  else
    err = csound->WaitThreadLock(pabs->paLock, (size_t) 500); 
#  endif
    #else
      csound->WaitThreadLock(pabs->paLock, (size_t) 500);
      err = 0;
     #endif*/
 
    if (pabs->mode & 1) {
      int n = pabs->inBufSamples;
      int i = 0;
      do {
        pabs->inputBuffer[i] = paInput[i];
      } while (++i < n);
    }
    if (pabs->mode & 2) {
      if (!err) {
        int n = pabs->outBufSamples;
        int i = 0;
        do {
          paOutput[i] = pabs->outputBuffer[i];
        } while (++i < n);
      }
      else {
#ifdef WIN32
        pabs->paused = err;
#endif
        paClearOutputBuffer(pabs, paOutput);
      }
    }
   
   paClearOutputBuffer(pabs, pabs->outputBuffer);
   csound->NotifyThreadLock(pabs->clientLock);

    return paContinue;
}

/* get samples from ADC */

static int rtrecord_(CSOUND *csound, MYFLT *buffer, int nbytes)
{
    PA_BLOCKING_STREAM  *pabs;
    int     i = 0, samples = nbytes / (int) sizeof(MYFLT);

    pabs = (PA_BLOCKING_STREAM*) *(csound->GetRtRecordUserData(csound));
    if (pabs == NULL) {
      memset(buffer, 0, nbytes);
      return nbytes;
    }
    if (pabs->paStream == NULL) {
      if (paBlockingReadWriteOpen(csound) != 0)
        csound->Die(csound, Str("Failed to initialise real time audio input"));
    }

    do {
      buffer[i] = (MYFLT) pabs->inputBuffer[pabs->currentInputIndex++];
      if (pabs->inParm.nChannels == 1)
        pabs->currentInputIndex++;
      if (pabs->currentInputIndex >= pabs->inBufSamples) {
        if (pabs->mode == 1) {
#if NO_FULLDUPLEX_PA_LOCK
          if (!pabs->noPaLock)
#endif
            csound->NotifyThreadLock(pabs->paLock);
          csound->WaitThreadLock(pabs->clientLock, (size_t) 500);
        }
        pabs->currentInputIndex = 0;
      }
    } while (++i < samples);

    return nbytes;
}

/* put samples to DAC */

static void rtplay_(CSOUND *csound, const MYFLT *buffer, int nbytes)
{
    PA_BLOCKING_STREAM  *pabs;
    int     i = 0, samples = nbytes / (int) sizeof(MYFLT);

    pabs = (PA_BLOCKING_STREAM*) *(csound->GetRtPlayUserData(csound));
    if (pabs == NULL)
      return;
#ifdef WIN32
    pabs->paused = 0;
#endif

    do {
      pabs->outputBuffer[pabs->currentOutputIndex++] = (float) buffer[i];
      if (pabs->outParm.nChannels == 1)
        pabs->outputBuffer[pabs->currentOutputIndex++] = (float) buffer[i];
      if (pabs->currentOutputIndex >= pabs->outBufSamples) {
#if NO_FULLDUPLEX_PA_LOCK
        if (!pabs->noPaLock)
#endif
          csound->NotifyThreadLock(pabs->paLock);
        csound->WaitThreadLock(pabs->clientLock, (size_t) 500);
        pabs->currentOutputIndex = 0;
      }
    } while (++i < samples);
}

/* open for audio input */

static int recopen_(CSOUND *csound, const csRtAudioParams *parm)
{
    CSOUND *p = csound;
    PA_BLOCKING_STREAM *pabs;

    pabs = (PA_BLOCKING_STREAM*) p->QueryGlobalVariable(p, "_rtpaGlobals");
    if (pabs == NULL) {
      if (p->CreateGlobalVariable(p, "_rtpaGlobals", sizeof(PA_BLOCKING_STREAM))
          != 0)
        return -1;
      pabs = (PA_BLOCKING_STREAM*) p->QueryGlobalVariable(p, "_rtpaGlobals");
      pabs->csound = p;
    }
    pabs->mode |= 1;
    memcpy(&(pabs->inParm), parm, sizeof(csRtAudioParams));
    *(p->GetRtRecordUserData(p)) = (void*) pabs;

    return 0;
}

/* open for audio output */

static int playopen_(CSOUND *csound, const csRtAudioParams *parm)
{
    CSOUND *p = csound;
    PA_BLOCKING_STREAM *pabs;

    pabs = (PA_BLOCKING_STREAM*) p->QueryGlobalVariable(p, "_rtpaGlobals");
    if (pabs == NULL) {
      if (p->CreateGlobalVariable(p, "_rtpaGlobals", sizeof(PA_BLOCKING_STREAM))
          != 0)
        return -1;
      pabs = (PA_BLOCKING_STREAM*) p->QueryGlobalVariable(p, "_rtpaGlobals");
      pabs->csound = p;
    }
    pabs->mode |= 2;
    memcpy(&(pabs->outParm), parm, sizeof(csRtAudioParams));
    *(p->GetRtPlayUserData(p)) = (void*) pabs;

    return (paBlockingReadWriteOpen(p));
}

/* close the I/O device entirely */

static void rtclose_(CSOUND *csound)
{
    PA_BLOCKING_STREAM *pabs;
    pabs = (PA_BLOCKING_STREAM*) csound->QueryGlobalVariable(csound,
                                                             "_rtpaGlobals");
    if (pabs == NULL)
      return;
   
    

    if (pabs->paStream != NULL) {
      PaStream  *stream = pabs->paStream;
      int       i;
      pabs->paStream = NULL;
      for (i = 0; i < 4; i++) {
#if NO_FULLDUPLEX_PA_LOCK
        if (!pabs->noPaLock)
#endif
          csound->NotifyThreadLock(pabs->paLock);
        csound->NotifyThreadLock(pabs->clientLock);
        Pa_Sleep(80);
      }
      
      Pa_AbortStream(stream);
      Pa_CloseStream(stream);
      Pa_Sleep(80);
    }

    if (pabs->clientLock != NULL) {
      csound->NotifyThreadLock(pabs->clientLock);
      csound->DestroyThreadLock(pabs->clientLock);
      pabs->clientLock = NULL;
    }
    if (pabs->paLock != NULL) {
      csound->NotifyThreadLock(pabs->paLock);
      csound->DestroyThreadLock(pabs->paLock);
      pabs->paLock = NULL;
    }

    if (pabs->outputBuffer != NULL) {
      free(pabs->outputBuffer);
      pabs->outputBuffer = NULL;
    }
    if (pabs->inputBuffer != NULL) {
      free(pabs->inputBuffer);
      pabs->inputBuffer = NULL;
    }

    *(csound->GetRtRecordUserData(csound)) = NULL;
    *(csound->GetRtPlayUserData(csound)) = NULL;
    csound->DestroyGlobalVariable(csound, "_rtpaGlobals");
}

/* set up audio device */

static int set_device_params(CSOUND *csound, DEVPARAMS *dev,
                             const csRtAudioParams *parm, int play)<commit_msg>modified port audio to use Pa_StopStrem() instead of Pa_AbortStream(), removed calls to Pa_Sleep(), added complete flag so that callback can return PaComplete when Csound is shutting down<commit_after>/*
    rtpa.c:

    Copyright (C) 2004, 2005 <NAME>, <NAME>,
                             <NAME>, <NAME>

    This file is part of Csound.

    The Csound Library is free software; you can redistribute it
    and/or modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    Csound 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 Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with Csound; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
    02111-1307 USA
*/

/*                                              RTPA.C for PortAudio    */

#include "csdl.h"
#if !defined(WIN32)
#include "soundio.h"
#endif
#include <portaudio.h>

#define NO_FULLDUPLEX_PA_LOCK   0   

typedef struct PaAlsaStreamInfo {
    unsigned long   size;
    PaHostApiTypeId hostApiType;
    unsigned long   version;
    const char      *deviceString;
} PaAlsaStreamInfo;

typedef struct devparams_ {
    PaStream    *handle;        /* stream handle                    */
    float       *buf;           /* sample conversion buffer         */
    int         nchns;          /* number of channels               */
} DEVPARAMS;

typedef struct PA_BLOCKING_STREAM_ {
    CSOUND      *csound;
    PaStream    *paStream;
    int         mode;                   /* 1: rec, 2: play, 3: full-duplex  */
    int         noPaLock;
    int         inBufSamples;
    int         outBufSamples;
    int         currentInputIndex;
    int         currentOutputIndex;
    float       *inputBuffer;
    float       *outputBuffer;
    void        *paLock;                /* thread lock for stream callback  */
    void        *clientLock;            /* thread lock for rtplay/rtrecord  */
    csRtAudioParams inParm;
    csRtAudioParams outParm;
    PaStreamParameters inputPaParameters;
    PaStreamParameters outputPaParameters;
#ifdef WIN32
    int         paused;                 /* VL: to allow for smooth pausing  */
#endif
    int         paLockTimeout;
    int         complete;
} PA_BLOCKING_STREAM;

static int pa_PrintErrMsg(CSOUND *csound, const char *fmt, ...)
{
    va_list args;
    va_start(args, fmt);
    csound->ErrMsgV(csound, Str(" *** PortAudio: error: "), fmt, args);
    va_end(args);
    return -1;
}

static int initPortAudio(CSOUND *csound)
{
    char  *s;
    int   err;
    /* initialise PortAudio */
    if (!csound->QueryGlobalVariable(csound, "::PortAudio::NeedsTerminate")) {
      if (csound->CreateGlobalVariable(csound,
                                       "::PortAudio::NeedsTerminate", 1) != 0)
        return -1;
      err = (int) Pa_Initialize();
      if (err != (int) paNoError) {
        return pa_PrintErrMsg(csound, "%d: %s",
                                      err, Pa_GetErrorText((PaError) err));
      }
      /* print PortAudio version */
      if ((s = (char*) Pa_GetVersionText()) != NULL)
        csound->Message(csound, "%s\n", s);
    }
    return 0;
}

/* list available input or output devices; returns the number of devices */

static int listPortAudioDevices_blocking(CSOUND *csound,
                                         int print_list, int play)
{
    PaDeviceInfo  *dev_info;
    int           i, j, ndev;

    ndev = (int) Pa_GetDeviceCount();
    for (i = j = 0; i < ndev; i++) {
      dev_info = (PaDeviceInfo*) Pa_GetDeviceInfo((PaDeviceIndex) i);
      if ((play && dev_info->maxOutputChannels > 0) ||
          (!play && dev_info->maxInputChannels > 0))
        j++;
    }
    if (!j) {
      pa_PrintErrMsg(csound, Str("No %s devices are available\n"),
                             (play ? Str("output") : Str("input")));
      return 0;
    }
    if (!print_list)
      return j;
    csound->Message(csound, Str("PortAudio: available %s devices:\n"),
                            (play ? Str("output") : Str("input")));
    for (i = j = 0; i < ndev; i++) {
      dev_info = (PaDeviceInfo*) Pa_GetDeviceInfo((PaDeviceIndex) i);
      if ((play && dev_info->maxOutputChannels > 0) ||
          (!play && dev_info->maxInputChannels > 0)) {
        csound->Message(csound, " %3d: %s\n", j, dev_info->name);
        j++;
      }
    }
    return j;
}

/* select PortAudio device; returns the actual device number */

static int selectPortAudioDevice(CSOUND *csound, int devNum, int play)
{
    PaDeviceInfo  *dev_info;
    int           i, j, maxNum;

    maxNum = listPortAudioDevices_blocking(csound, 1, play) - 1;
    if (maxNum < 0)
      return -1;
    if (devNum == 1024) {
      if (play)
        devNum = (int) Pa_GetDefaultOutputDevice();
      else
        devNum = (int) Pa_GetDefaultInputDevice();
    }
    else {
      if (devNum < 0 || devNum > maxNum) {
        /* listPortAudioDevices_blocking(csound, 1, play); */
        pa_PrintErrMsg(csound, Str("%s device number %d is out of range"),
                               (play ? Str("output") : Str("input")), devNum);
        return -1;
      }
      for (i = j = 0; j <= maxNum; i++) {
        dev_info = (PaDeviceInfo*) Pa_GetDeviceInfo((PaDeviceIndex) i);
        if ((play && dev_info->maxOutputChannels > 0) ||
            (!play && dev_info->maxInputChannels > 0)) {
          if (j == devNum)
            break;
          j++;
        }
      }
      devNum = i;
    }
    dev_info = (PaDeviceInfo*) Pa_GetDeviceInfo((PaDeviceIndex) devNum);
    if (dev_info)
      csound->Message(csound, Str("PortAudio: selected %s device '%s'\n"),
                      (play ? Str("output") : Str("input")),
                      dev_info->name);
    else
      csound->Message(csound, Str("PortAudio: failed to obtain device info.\n"));
    return devNum;
}

static int pa_SetStreamParameters(CSOUND *csound, PaStreamParameters *sp,
                                                  csRtAudioParams *parm,
                                                  int is_playback)
{
    int dev;

    memset(sp, 0, sizeof(PaStreamParameters));
    if (parm->devName != NULL && parm->devName[0] != '\0') {
      return pa_PrintErrMsg(csound,
                            Str("Must specify a device number, not a name"));
    }
    dev = selectPortAudioDevice(csound, parm->devNum, is_playback);
    if (dev < 0)
      return -1;
    sp->device = (PaDeviceIndex) dev;
    sp->channelCount = (parm->nChannels < 2 ? 2 : parm->nChannels);
    sp->sampleFormat = (PaSampleFormat) paFloat32;
    sp->suggestedLatency = (PaTime) ((double) parm->bufSamp_HW
                                     / (double) parm->sampleRate);
    sp->hostApiSpecificStreamInfo = NULL;
    return 0;
}

static void rtclose_(CSOUND *csound);
static int  paBlockingReadWriteStreamCallback(const void *, void *,
                                              unsigned long,
                                              const PaStreamCallbackTimeInfo *,
                                              PaStreamCallbackFlags,
                                              void *);

static int paBlockingReadWriteOpen(CSOUND *csound)
{
    PA_BLOCKING_STREAM  *pabs;
    PaStream            *stream = NULL;
    PaError             err;

    pabs = (PA_BLOCKING_STREAM*) csound->QueryGlobalVariable(csound,
                                                             "_rtpaGlobals");
    if (pabs == NULL)
      return -1;
    if (initPortAudio(csound) != 0)
      goto err_return;

    if ((int) Pa_GetDeviceCount() <= 0) {
      pa_PrintErrMsg(csound, Str("No sound device is available"));
      goto err_return;
    }

    if (pabs->mode & 1) {
      if (pa_SetStreamParameters(csound, &(pabs->inputPaParameters),
                                         &(pabs->inParm), 0) != 0)
        goto err_return;
      pabs->inBufSamples = pabs->inParm.bufSamp_SW
                           * (int) pabs->inputPaParameters.channelCount;
      pabs->inputBuffer = (float*) calloc((size_t) pabs->inBufSamples,
                                          sizeof(float));
      if (pabs->inputBuffer == NULL) {
        pa_PrintErrMsg(csound, Str("Memory allocation failure"));
        goto err_return;
      }
    }
    if (pabs->mode & 2) {
      if (pa_SetStreamParameters(csound, &(pabs->outputPaParameters),
                                         &(pabs->outParm), 1) != 0)
        goto err_return;
      pabs->outBufSamples = pabs->outParm.bufSamp_SW
                            * (int) pabs->outputPaParameters.channelCount;
      pabs->outputBuffer = (float*) calloc((size_t) pabs->outBufSamples,
                                           sizeof(float));
      if (pabs->outputBuffer == NULL) {
        pa_PrintErrMsg(csound, Str("Memory allocation failure"));
        goto err_return;
      }
    }
    if ((pabs->mode & 3) == 3) {
      if (pabs->inParm.bufSamp_SW != pabs->outParm.bufSamp_SW) {
        pa_PrintErrMsg(csound, Str("Inconsistent full-duplex buffer sizes"));
        goto err_return;
      }
      if (pabs->inParm.sampleRate != pabs->outParm.sampleRate) {
        pa_PrintErrMsg(csound, Str("Inconsistent full-duplex sample rates"));
        goto err_return;
      }
      if (((pabs->inParm.bufSamp_SW / csound->ksmps) * csound->ksmps)
          != pabs->inParm.bufSamp_SW)
        csound->MessageS(csound,
                         CSOUNDMSG_WARNING,
                         Str("WARNING: buffer size should be an integer "
                             "multiple of ksmps in full-duplex mode\n"));
#if NO_FULLDUPLEX_PA_LOCK
      pabs->noPaLock = 1;
#endif
    }

    pabs->paLock = csound->CreateThreadLock();
    if (pabs->paLock == NULL)
      goto err_return;
    pabs->clientLock = csound->CreateThreadLock();
    if (pabs->clientLock == NULL)
      goto err_return;
#if NO_FULLDUPLEX_PA_LOCK
    if (!pabs->noPaLock)
#endif
      csound->WaitThreadLock(pabs->paLock, (size_t) 500);
    csound->WaitThreadLock(pabs->clientLock, (size_t) 500);

    pabs->paLockTimeout =
        (int) (1.33 * (pabs->outputPaParameters.suggestedLatency
                         >= pabs->inputPaParameters.suggestedLatency ?
                       pabs->outputPaParameters.suggestedLatency
                       : pabs->inputPaParameters.suggestedLatency));
    if (pabs->paLockTimeout < 25)
      pabs->paLockTimeout = 25;
    else if (pabs->paLockTimeout > 1000)
      pabs->paLockTimeout = 1000;


    err = Pa_OpenStream(&stream,
                        (pabs->mode & 1 ? &(pabs->inputPaParameters)
                                          : (PaStreamParameters*) NULL),
                        (pabs->mode & 2 ? &(pabs->outputPaParameters)
                                          : (PaStreamParameters*) NULL),
                        (double) (pabs->mode & 2 ? pabs->outParm.sampleRate
                                                   : pabs->inParm.sampleRate),
                        (unsigned long) (pabs->mode & 2 ?
                                         pabs->outParm.bufSamp_SW
                                         : pabs->inParm.bufSamp_SW),
                        (csound->dither_output ?
                         (PaStreamFlags) paNoFlag : (PaStreamFlags) paDitherOff),
                        paBlockingReadWriteStreamCallback,
                        (void*) pabs);
    if (err != paNoError) {
      pa_PrintErrMsg(csound, "%d: %s", (int) err, Pa_GetErrorText(err));
      goto err_return;
    }

    err = Pa_StartStream(stream);
    if (err != paNoError) {
      Pa_CloseStream(stream);
      pa_PrintErrMsg(csound, "%d: %s", (int) err, Pa_GetErrorText(err));
      goto err_return;
    }

    pabs->paStream = stream;
    return 0;

    /* clean up and report error */
 err_return:
    rtclose_(csound);
    return -1;
}

static CS_NOINLINE void paClearOutputBuffer(PA_BLOCKING_STREAM *pabs,
                                            float *buf)
{
    int   nsmps = pabs->outBufSamples;
    memset(buf,0, nsmps*sizeof(float));
    /* int   i = 0; */
    /* do { */
    /*   buf[i] = 0.0f; */
    /* } while (++i < nsmps); */
}

static int paBlockingReadWriteStreamCallback(const void *input,
                                             void *output,
                                             unsigned long frameCount,
                                             const PaStreamCallbackTimeInfo
                                                 *timeInfo,
                                             PaStreamCallbackFlags statusFlags,
                                             void *userData)
{
    int     err;
    PA_BLOCKING_STREAM *pabs = (PA_BLOCKING_STREAM*) userData;
    CSOUND  *csound = pabs->csound;
    float   *paInput = (float*) input;
    float   *paOutput = (float*) output;

   
    if (pabs->complete == 1) {
        return paComplete;
    }
    
#ifdef WIN32    
if (pabs->paStream == NULL
      || pabs->paused
) {
if (pabs->mode & 2)
        paClearOutputBuffer(pabs, paOutput);
return paContinue;
}
#endif

#if NO_FULLDUPLEX_PA_LOCK
    err = 0;
    if (!pabs->noPaLock)
#endif
      /*#ifndef __MACH__*/
      /*#  ifdef WIN32 */
      err = csound->WaitThreadLock(pabs->paLock, (size_t) pabs->paLockTimeout);
    /*#  else
    err = csound->WaitThreadLock(pabs->paLock, (size_t) 500); 
#  endif
    #else
      csound->WaitThreadLock(pabs->paLock, (size_t) 500);
      err = 0;
     #endif*/
 
    if (pabs->mode & 1) {
      int n = pabs->inBufSamples;
      int i = 0;
      do {
        pabs->inputBuffer[i] = paInput[i];
      } while (++i < n);
    }
    if (pabs->mode & 2) {
      if (!err) {
        int n = pabs->outBufSamples;
        int i = 0;
        do {
          paOutput[i] = pabs->outputBuffer[i];
        } while (++i < n);
      }
      else {
#ifdef WIN32
        pabs->paused = err;
#endif
        paClearOutputBuffer(pabs, paOutput);
      }
    }
   
   paClearOutputBuffer(pabs, pabs->outputBuffer);
   csound->NotifyThreadLock(pabs->clientLock);

    return paContinue;
}

/* get samples from ADC */

static int rtrecord_(CSOUND *csound, MYFLT *buffer, int nbytes)
{
    PA_BLOCKING_STREAM  *pabs;
    int     i = 0, samples = nbytes / (int) sizeof(MYFLT);

    pabs = (PA_BLOCKING_STREAM*) *(csound->GetRtRecordUserData(csound));
    if (pabs == NULL) {
      memset(buffer, 0, nbytes);
      return nbytes;
    }
    if (pabs->paStream == NULL) {
      if (paBlockingReadWriteOpen(csound) != 0)
        csound->Die(csound, Str("Failed to initialise real time audio input"));
    }

    do {
      buffer[i] = (MYFLT) pabs->inputBuffer[pabs->currentInputIndex++];
      if (pabs->inParm.nChannels == 1)
        pabs->currentInputIndex++;
      if (pabs->currentInputIndex >= pabs->inBufSamples) {
        if (pabs->mode == 1) {
#if NO_FULLDUPLEX_PA_LOCK
          if (!pabs->noPaLock)
#endif
            csound->NotifyThreadLock(pabs->paLock);
          csound->WaitThreadLock(pabs->clientLock, (size_t) 500);
        }
        pabs->currentInputIndex = 0;
      }
    } while (++i < samples);

    return nbytes;
}

/* put samples to DAC */

static void rtplay_(CSOUND *csound, const MYFLT *buffer, int nbytes)
{
    PA_BLOCKING_STREAM  *pabs;
    int     i = 0, samples = nbytes / (int) sizeof(MYFLT);

    pabs = (PA_BLOCKING_STREAM*) *(csound->GetRtPlayUserData(csound));
    if (pabs == NULL)
      return;
#ifdef WIN32
    pabs->paused = 0;
#endif

    do {
      pabs->outputBuffer[pabs->currentOutputIndex++] = (float) buffer[i];
      if (pabs->outParm.nChannels == 1)
        pabs->outputBuffer[pabs->currentOutputIndex++] = (float) buffer[i];
      if (pabs->currentOutputIndex >= pabs->outBufSamples) {
#if NO_FULLDUPLEX_PA_LOCK
        if (!pabs->noPaLock)
#endif
          csound->NotifyThreadLock(pabs->paLock);
        csound->WaitThreadLock(pabs->clientLock, (size_t) 500);
        pabs->currentOutputIndex = 0;
      }
    } while (++i < samples);
}

/* open for audio input */

static int recopen_(CSOUND *csound, const csRtAudioParams *parm)
{
    CSOUND *p = csound;
    PA_BLOCKING_STREAM *pabs;

    pabs = (PA_BLOCKING_STREAM*) p->QueryGlobalVariable(p, "_rtpaGlobals");
    if (pabs == NULL) {
      if (p->CreateGlobalVariable(p, "_rtpaGlobals", sizeof(PA_BLOCKING_STREAM))
          != 0)
        return -1;
      pabs = (PA_BLOCKING_STREAM*) p->QueryGlobalVariable(p, "_rtpaGlobals");
      pabs->csound = p;
    }
    pabs->mode |= 1;
    memcpy(&(pabs->inParm), parm, sizeof(csRtAudioParams));
    *(p->GetRtRecordUserData(p)) = (void*) pabs;

    pabs->complete = 0;
    
    return 0;
}

/* open for audio output */

static int playopen_(CSOUND *csound, const csRtAudioParams *parm)
{
    CSOUND *p = csound;
    PA_BLOCKING_STREAM *pabs;

    pabs = (PA_BLOCKING_STREAM*) p->QueryGlobalVariable(p, "_rtpaGlobals");
    if (pabs == NULL) {
      if (p->CreateGlobalVariable(p, "_rtpaGlobals", sizeof(PA_BLOCKING_STREAM))
          != 0)
        return -1;
      pabs = (PA_BLOCKING_STREAM*) p->QueryGlobalVariable(p, "_rtpaGlobals");
      pabs->csound = p;
    }
    pabs->mode |= 2;
    memcpy(&(pabs->outParm), parm, sizeof(csRtAudioParams));
    *(p->GetRtPlayUserData(p)) = (void*) pabs;

    pabs->complete = 0;
    
    return (paBlockingReadWriteOpen(p));
}

/* close the I/O device entirely */

static void rtclose_(CSOUND *csound)
{
    PA_BLOCKING_STREAM *pabs;
    pabs = (PA_BLOCKING_STREAM*) csound->QueryGlobalVariable(csound,
                                                             "_rtpaGlobals");
    if (pabs == NULL)
      return;
   
    pabs->complete = 1;

    if (pabs->paStream != NULL) {
      PaStream  *stream = pabs->paStream;
      int       i;
      pabs->paStream = NULL;
      for (i = 0; i < 4; i++) {
#if NO_FULLDUPLEX_PA_LOCK
        if (!pabs->noPaLock)
#endif
          csound->NotifyThreadLock(pabs->paLock);
        csound->NotifyThreadLock(pabs->clientLock);
//        Pa_Sleep(80);
      }
      
      Pa_StopStream(stream);
      Pa_CloseStream(stream);
//      Pa_Sleep(80);
    }

    if (pabs->clientLock != NULL) {
      csound->NotifyThreadLock(pabs->clientLock);
      csound->DestroyThreadLock(pabs->clientLock);
      pabs->clientLock = NULL;
    }
    if (pabs->paLock != NULL) {
      csound->NotifyThreadLock(pabs->paLock);
      csound->DestroyThreadLock(pabs->paLock);
      pabs->paLock = NULL;
    }

    if (pabs->outputBuffer != NULL) {
      free(pabs->outputBuffer);
      pabs->outputBuffer = NULL;
    }
    if (pabs->inputBuffer != NULL) {
      free(pabs->inputBuffer);
      pabs->inputBuffer = NULL;
    }

    *(csound->GetRtRecordUserData(csound)) = NULL;
    *(csound->GetRtPlayUserData(csound)) = NULL;
    csound->DestroyGlobalVariable(csound, "_rtpaGlobals");
}

/* set up audio device */

static int set_device_params(CSOUND *csound, DEVPARAMS *dev,
                             const csRtAudioParams *parm, int play)
{'fraction_non_alphanumeric': 0.12207880970531795, 'fraction_numerical': 0.008622275965405747, 'mean_word_length': 1.9673946957878314, 'pattern_counts': {'":': 4, '<': 43, '<?xml version=': 0, '>': 415, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 112, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7095256', 'n_tokens_mistral': 12309, 'n_tokens_neox': 11735, 'n_words': 2682}