Skip to content

Instantly share code, notes, and snippets.

@arkadijs
Last active July 2, 2024 23:20
Show Gist options
  • Save arkadijs/7027bf8aad399f3420406600c73df9e4 to your computer and use it in GitHub Desktop.
Save arkadijs/7027bf8aad399f3420406600c73df9e4 to your computer and use it in GitHub Desktop.
PortAudio capture WASAPI synthetic [Loopback] device
#include <fcntl.h>
#include <io.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <windows.h>
#include "portaudio.h"
#define FRAMES_PER_BUFFER (512)
const int sampleRates[] = {8000, 16000, 32000, 44100, 48000};
volatile static int frame_counter = 0;
// int16_t buffer[10000000];
int callback(const void *input, void *output, unsigned long frameCount,
const PaStreamCallbackTimeInfo *timeInfo, PaStreamCallbackFlags statusFlags, void *userData) {
// printf("%s: pushed %lu frames\n", (char*)userData, frameCount);
// memcpy(buffer + frame_counter, input, frameCount*sizeof(int16_t));
frame_counter += frameCount;
return paContinue;
}
int main(void) {
SetConsoleOutputCP(65001);
PaError err = paNoError;
err = Pa_Initialize();
if (err != paNoError)
return err;
// FILE *capture_file = fopen("capture.pcm", "wb");
const int nDev = Pa_GetDeviceCount();
const PaDeviceInfo *devices[nDev] = {};
for (int i = 0; i < nDev; i++) {
// devices[i] = NULL;
const PaDeviceInfo *device;
if ((device = Pa_GetDeviceInfo(i)) != NULL) {
const char *host_api_name = Pa_GetHostApiInfo(device->hostApi)->name;
char *selected = "";
if (strstr(device->name, "[Loopback]") != NULL) {
devices[i] = device;
selected = " <=======";
}
printf("Device: %s > %s rate:%.0f%s\n", host_api_name,
device->name, device->defaultSampleRate, selected);
}
}
for (int i = 0; i < nDev; i++) {
const PaDeviceInfo *device = devices[i];
if (device == NULL) {
continue;
}
PaStreamParameters loopbackParameters = {0};
int loopbackSampleRate = 0;
loopbackParameters.channelCount = 1;
loopbackParameters.device = i;
loopbackParameters.sampleFormat = paInt16;
loopbackParameters.suggestedLatency = device->defaultLowInputLatency;
loopbackParameters.hostApiSpecificStreamInfo = NULL;
for (int i = 0; i < sizeof(sampleRates)/sizeof(sampleRates[0]); i++) {
// Check if the format is supported
err = Pa_IsFormatSupported(&loopbackParameters, NULL, sampleRates[i]);
if (err == paFormatIsSupported) {
loopbackSampleRate = sampleRates[i];
}
}
if (!loopbackSampleRate) {
printf("%s: not compatible\n", device->name);
continue;
}
PaStream *loopbackStream;
const char *result = "success";
printf("%s: trying callback API...\n", device->name);
err = Pa_OpenStream(&loopbackStream, &loopbackParameters, NULL,
loopbackSampleRate, paFramesPerBufferUnspecified, paClipOff, callback, (void*)device->name);
if (err != paNoError) {
result = Pa_GetErrorText(err);
} else {
int frame_counter_started_at = frame_counter;
err = Pa_StartStream(loopbackStream);
if (err != paNoError) {
result = Pa_GetErrorText(err);
} else {
Sleep(50);
PaError active = Pa_IsStreamActive(loopbackStream);
if (active < 0) {
printf("%s: %s\n", device->name, Pa_GetErrorText(active));
} else {
printf("%s: stream active: %d\n", device->name, active);
}
err = Pa_StopStream(loopbackStream);
if (err != paNoError) {
result = Pa_GetErrorText(err);
} else if (frame_counter_started_at == frame_counter) {
result = "no frames pushed";
}
// fwrite(buffer + frame_counter_started_at, sizeof(int16_t), frame_counter - frame_counter_started_at, capture_file);
}
Pa_CloseStream(loopbackStream);
}
printf("%s: %s\n", device->name, result);
if (!strcmp(result, "success")) {
printf("%s: trying blocking API...\n", device->name);
err = Pa_OpenStream(&loopbackStream, &loopbackParameters, NULL,
loopbackSampleRate, paFramesPerBufferUnspecified, paClipOff, NULL, NULL);
if (err != paNoError) {
result = Pa_GetErrorText(err);
} else {
err = Pa_StartStream(loopbackStream);
if (err != paNoError) {
result = Pa_GetErrorText(err);
} else {
int16_t buf[FRAMES_PER_BUFFER*2];
for (int i = 0; i < 5; i++) {
err = Pa_ReadStream(loopbackStream, buf, FRAMES_PER_BUFFER);
if (err != paNoError) {
result = Pa_GetErrorText(err);
break;
} else {
printf("%s: read %d frames\n", device->name, FRAMES_PER_BUFFER);
}
}
}
Pa_CloseStream(loopbackStream);
}
printf("%s: %s\n", device->name, result);
}
}
Pa_Terminate();
return err;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment