|
| 1 | +#include "portaudio.h" |
| 2 | +#include <pa_ringbuffer.h> |
| 3 | +#include <stdio.h> |
| 4 | +#include <unistd.h> |
| 5 | +#include <string.h> |
| 6 | + |
| 7 | +#define MIN(x, y) ((x) < (y) ? (x) : (y)) |
| 8 | + |
| 9 | +typedef enum { |
| 10 | + PA_SHIM_ERRMSG_OVERFLOW, // input overflow |
| 11 | + PA_SHIM_ERRMSG_UNDERFLOW, // output underflow |
| 12 | + PA_SHIM_ERRMSG_ERR_OVERFLOW, // error buffer overflowed |
| 13 | +} pa_shim_errmsg_t; |
| 14 | + |
| 15 | +// this callback type is used to notify the Julia side that the portaudio |
| 16 | +// callback has run |
| 17 | +typedef void (*pa_shim_notifycb_t)(void *userdata); |
| 18 | + |
| 19 | +// This struct is shared between the Julia side and C |
| 20 | +typedef struct { |
| 21 | + PaUtilRingBuffer *inputbuf; // ringbuffer for input |
| 22 | + PaUtilRingBuffer *outputbuf; // ringbuffer for output |
| 23 | + PaUtilRingBuffer *errorbuf; // ringbuffer to send error notifications |
| 24 | + int sync; // keep input/output ring buffers synchronized (0/1) |
| 25 | + pa_shim_notifycb_t notifycb; // Julia callback to notify conditions |
| 26 | + void *inputhandle; // condition to notify on new input |
| 27 | + void *outputhandle; // condition to notify when ready for output |
| 28 | + void *errorhandle; // condition to notify on new error |
| 29 | +} pa_shim_info_t; |
| 30 | + |
| 31 | +void senderr(pa_shim_info_t *info, pa_shim_errmsg_t msg) { |
| 32 | + if(PaUtil_GetRingBufferWriteAvailable(info->errorbuf) < 2) { |
| 33 | + // we've overflowed our error buffer! notify the host. |
| 34 | + msg = PA_SHIM_ERRMSG_ERR_OVERFLOW; |
| 35 | + } |
| 36 | + PaUtil_WriteRingBuffer(info->errorbuf, &msg, 1); |
| 37 | + if(info->notifycb) { |
| 38 | + info->notifycb(info->errorhandle); |
| 39 | + } |
| 40 | +} |
| 41 | + |
| 42 | +// return the sha256 hash of the shim source so we can make sure things are in sync |
| 43 | +const char *pa_shim_getsourcehash(void) |
| 44 | +{ |
| 45 | + // defined on the command-line at build-time |
| 46 | + return SOURCEHASH; |
| 47 | +} |
| 48 | + |
| 49 | +/* |
| 50 | + * This routine will be called by the PortAudio engine when audio is needed. |
| 51 | + * It may called at interrupt level on some machines so don't do anything that |
| 52 | + * could mess up the system like calling malloc() or free(). |
| 53 | + */ |
| 54 | +int pa_shim_processcb(const void *input, void *output, |
| 55 | + unsigned long frameCount, |
| 56 | + const PaStreamCallbackTimeInfo* timeInfo, |
| 57 | + PaStreamCallbackFlags statusFlags, |
| 58 | + void *userData) |
| 59 | +{ |
| 60 | + pa_shim_info_t *info = (pa_shim_info_t *)userData; |
| 61 | + if(info->notifycb == NULL) { |
| 62 | + fprintf(stderr, "pa_shim ERROR: notifycb is NULL\n"); |
| 63 | + } |
| 64 | + int nwrite; |
| 65 | + if(info->inputbuf) { |
| 66 | + nwrite = PaUtil_GetRingBufferWriteAvailable(info->inputbuf); |
| 67 | + nwrite = MIN(frameCount, nwrite); |
| 68 | + } |
| 69 | + int nread; |
| 70 | + if(info->outputbuf) { |
| 71 | + nread = PaUtil_GetRingBufferReadAvailable(info->outputbuf); |
| 72 | + nread = MIN(frameCount, nread); |
| 73 | + } |
| 74 | + if(info->inputbuf && info->outputbuf && info->sync) { |
| 75 | + // to keep the buffers synchronized, set readable and writable to |
| 76 | + // their minimum value |
| 77 | + nread = MIN(nread, nwrite); |
| 78 | + nwrite = nread; |
| 79 | + } |
| 80 | + // read/write from the ringbuffers |
| 81 | + if(info->inputbuf) { |
| 82 | + PaUtil_WriteRingBuffer(info->inputbuf, input, nwrite); |
| 83 | + if(info->notifycb) { |
| 84 | + info->notifycb(info->inputhandle); |
| 85 | + } |
| 86 | + if(nwrite < frameCount) { |
| 87 | + senderr(info, PA_SHIM_ERRMSG_OVERFLOW); |
| 88 | + } |
| 89 | + } |
| 90 | + if(info->outputbuf) { |
| 91 | + PaUtil_ReadRingBuffer(info->outputbuf, output, nread); |
| 92 | + if(info->notifycb) { |
| 93 | + info->notifycb(info->outputhandle); |
| 94 | + } |
| 95 | + if(nread < frameCount) { |
| 96 | + senderr(info, PA_SHIM_ERRMSG_UNDERFLOW); |
| 97 | + // we didn't fill the whole output buffer, so zero it out |
| 98 | + memset(output+nread*info->outputbuf->elementSizeBytes, 0, |
| 99 | + (frameCount - nread)*info->outputbuf->elementSizeBytes); |
| 100 | + } |
| 101 | + } |
| 102 | + |
| 103 | + return paContinue; |
| 104 | +} |
0 commit comments