#include"node.h" #include #include"img.h" #include #include #include #include #include #include"microphone.h" #define pabufsize (48000*5) typedef struct CHiMicrophoneNode { CHiPubNode pub; PaStream *paStream; int16_t paBuffer[pabufsize]; atomic_size_t paBufferWriteIdx; atomic_size_t paBufferReadIdx; } CHiMicrophoneNode; static int pacallback(const void *input_, void *output, unsigned long samples, const PaStreamCallbackTimeInfo *timeInfo, PaStreamCallbackFlags flags, void *ud) { CHiMicrophoneNode *node = ud; const int16_t *input = input_; size_t writeidx = node->paBufferWriteIdx; while(writeidx + samples >= pabufsize) { memcpy(node->paBuffer + writeidx, input, (pabufsize - writeidx) * sizeof(*node->paBuffer)); samples -= pabufsize - writeidx; input += pabufsize - writeidx; writeidx = 0; } memcpy(node->paBuffer + writeidx, input, samples * sizeof(*node->paBuffer)); writeidx = (writeidx + samples) % pabufsize; node->paBufferWriteIdx = writeidx; /*static size_t total = 0; for(size_t i = 0; i < pabufsize; i++) { paBuffer[paBufferWriteIdx] = sin(total++ * 440.0 / 24000 * 3.141592653) * 0.1; paBufferWriteIdx = (paBufferWriteIdx + 1) % pabufsize; }*/ return paContinue; } static int microphone_start(CHiPubNode *pubn) { CHiMicrophoneNode *node = (void*) pubn; PaStreamParameters params = { .device = pubn->sinks[0].data.vec4[0], .channelCount = 1, .sampleFormat = paInt16, .suggestedLatency = Pa_GetDeviceInfo(pubn->sinks[0].data.vec4[0])->defaultLowInputLatency, }; Pa_OpenStream(&node->paStream, ¶ms, NULL, 48000, 0, paNoFlag, pacallback, pubn); Pa_StartStream(node->paStream); return 1; } static int microphone_stop(CHiPubNode *pubn) { CHiMicrophoneNode *node = (void*) pubn; Pa_StopStream(node->paStream); Pa_CloseStream(node->paStream); return 1; } static int microphone_perform(CHiPubNode *pubn) { CHiMicrophoneNode *node = (void*) pubn; if(pubn->sources[0].data.sample) { CHi_Image_Free(pubn->sources[0].data.sample); } size_t width = roundf(CHi_Time_GetDelta(pubn->ng) * 48000.f); do { }while((node->paBufferWriteIdx - node->paBufferReadIdx + pabufsize) % pabufsize < width); // Wait until available CHiImage *ret = CHi_Image_New(2, 1, 2 * width, width, 1, NULL); if(node->paBufferReadIdx + width >= pabufsize) { memcpy(ret->data16, node->paBuffer + node->paBufferReadIdx, sizeof(*node->paBuffer) * (pabufsize - node->paBufferReadIdx)); memcpy(ret->data16 + pabufsize - node->paBufferReadIdx, node->paBuffer, sizeof(*node->paBuffer) * (width - pabufsize + node->paBufferReadIdx)); node->paBufferReadIdx = node->paBufferReadIdx + width - pabufsize; } else { memcpy(ret->data16, node->paBuffer + node->paBufferReadIdx, sizeof(*node->paBuffer) * width); node->paBufferReadIdx = (node->paBufferReadIdx + width) % pabufsize; } pubn->sources[0].type = CUTIHI_VAL_SAMPLE; pubn->sources[0].data.sample = ret; pubn->clean = 0; return 1; } CUTIVIS CHiPubNode *CHi_Microphone() { static int inited = 0; if(!inited) { Pa_Initialize(); inited = 1; } CHiPubNode *n = calloc(1, sizeof(CHiMicrophoneNode)); n->type = CUTIHI_T('CInA','udio'); n->Start = microphone_start; n->Perform = microphone_perform; n->Stop = microphone_stop; n->clean = 0; n->sinkCount = 1; n->sinks = calloc(sizeof(*n->sinks), n->sinkCount); n->sourceCount = 1; n->sources = calloc(sizeof(*n->sources), n->sourceCount); return n; } struct CHiExportWavNode { CHiPubNode pubn; FILE *output; }; CUTIVIS int CHi_ExportWav_Start(CHiPubNode *pubn) { struct CHiExportWavNode *n = (struct CHiExportWavNode*) pubn; n->output = fopen(CHi_Crawl(&pubn->sinks[0])->data.text, "wb"); struct __attribute__((packed)) { uint32_t ckID; uint32_t ckSize; uint32_t waveID; } header = {.ckID = 'FFIR', .ckSize = 0, .waveID = 'EVAW'}; fwrite(&header, sizeof(header), 1, n->output); struct __attribute__((packed)) { uint32_t ckID; uint32_t ckSize; uint16_t wFormatTag; uint16_t nChannels; uint32_t nSamplesPerSec; uint32_t nAvgBytesPerSec; uint16_t nBlockAlign; uint16_t wBitsPerSample; } chunk0 = {.ckID = ' tmf', .ckSize = 16, .wFormatTag = 1 /* float */, .nChannels = 1, .nSamplesPerSec = 48000, .nAvgBytesPerSec = 48000 * 2, .nBlockAlign = 4, .wBitsPerSample = 16}; fwrite(&chunk0, sizeof(chunk0), 1, n->output); struct __attribute__((packed)) { uint32_t ckID; uint32_t ckSize; } chunk1 = {.ckID = 'atad', .ckSize = 0}; fwrite(&chunk1, sizeof(chunk1), 1, n->output); return 1; } static int exportwav_perform(CHiPubNode *pubn) { struct CHiExportWavNode *n = (struct CHiExportWavNode*) pubn; CHiImage *buf = CHi_Crawl(&pubn->sinks[1])->data.sample; fwrite(buf->data16, 2, buf->width, n->output); pubn->clean = 0; return 1; } CUTIVIS int CHi_ExportWav_Stop(CHiPubNode *pubn) { struct CHiExportWavNode *n = (struct CHiExportWavNode*) pubn; /* Fill size info in headers. */ uint32_t sz = ftell(n->output) - 8; fseek(n->output, 4, SEEK_SET); fwrite(&sz, sizeof(sz), 1, n->output); sz -= 36; fseek(n->output, 32, SEEK_CUR); fwrite(&sz, sizeof(sz), 1, n->output); fclose(n->output); return 1; } CUTIVIS CHiPubNode *CHi_ExportWav() { struct CHiExportWavNode *n = malloc(sizeof(*n)); n->pubn.type = CUTIHI_T('CExp','Wave'); n->pubn.Start = CHi_ExportWav_Start; n->pubn.Perform = exportwav_perform; n->pubn.Stop = CHi_ExportWav_Stop; n->pubn.clean = 0; n->pubn.sinkCount = 2; n->pubn.sinks = calloc(sizeof(*n->pubn.sinks), n->pubn.sinkCount); n->pubn.sourceCount = 0; n->pubn.sources = NULL; return &n->pubn; } size_t CHi_Microphone_GetSourceCount() { return Pa_GetDeviceCount(); } const char *CHi_Microphone_GetSourceName(size_t i) { return Pa_GetDeviceInfo(i)->name; } size_t CHi_Microphone_GetNextSource(size_t i) { i++; while(i < CHi_Microphone_GetSourceCount()) { if(Pa_GetDeviceInfo(i)->maxInputChannels > 0) break; i++; } return i; }