/* * Audio Library for Linux and Windows * =================================== * Author: DJ0ABR * * Author: Kurt Moraw, Ham radio: DJ0ABR, github: dj0abr * License: GPL-3 * * compilation: * Windows ... Visual Studio * Linux ... make * * Documentation see: libkmaudio.h * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * libkmaudio_getDevices.cpp * read audio device list via portaudio (which is only used for Windows) * prepare a device name string which can be read by another program * in order to present the devices to use user for selection * */ #include "libkmaudio.h" void io_buildAudioDevString(); // number of detected devices, updated after a call to kmaudio_getDeviceList() int devanz = 0; // devlist contains all information for all detected devices // the list is filled by a call to kmaudio_getDeviceList() DEVLIST devlist[MAXDEVICES]; double latency = 0.2; // WASAPI latency in seconds #ifdef WIN32 /*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 };*/ static double standardSampleRates[] = {44100.0, 48000.0, -1}; int getDevlistIndex(const char* name, int ichans, int ochans) { for (int i = 0; i < devanz; i++) { // check if already exists if (!strcmp(devlist[i].name, name) && devlist[i].inputParameters.channelCount == ichans && devlist[i].outputParameters.channelCount == ochans) return i; } int newidx = devanz; devanz++; //printf("New Dev:%s Idx:%d\n", name, newidx); return newidx; } int kmaudio_getDeviceList() { int numDevices = Pa_GetDeviceCount(); if (numDevices < 0) { printf("ERROR: Pa_GetDeviceCount returned 0x%x\n", numDevices); return -1; } //printf("%d Devices found\n", numDevices); for (int i = 0; i < devanz; i++) devlist[i].active = 0; int didx; for (int i = 0; i < numDevices; i++) { const PaDeviceInfo* deviceInfo = Pa_GetDeviceInfo(i); const PaHostApiInfo *ai = Pa_GetHostApiInfo(deviceInfo->hostApi); // Windows: use WASAPI devices only if (strstr(ai->name, "WASAPI") != NULL) { didx = getDevlistIndex(deviceInfo->name, deviceInfo->maxInputChannels, deviceInfo->maxOutputChannels); devlist[didx].devnum = i; snprintf(devlist[didx].name, MAXDEVNAMELENGTH - 1, "%s", deviceInfo->name); //printf("------%s-------\n", deviceInfo->name); devlist[didx].inputParameters.device = i; devlist[didx].inputParameters.channelCount = deviceInfo->maxInputChannels; devlist[didx].inputParameters.sampleFormat = paInt16; devlist[didx].inputParameters.suggestedLatency = latency; devlist[didx].inputParameters.hostApiSpecificStreamInfo = NULL; devlist[didx].outputParameters.device = i; devlist[didx].outputParameters.channelCount = deviceInfo->maxOutputChannels; devlist[didx].outputParameters.sampleFormat = paInt16; devlist[didx].outputParameters.suggestedLatency = latency; devlist[didx].outputParameters.hostApiSpecificStreamInfo = NULL; if (devlist[didx].inputParameters.channelCount > 0 && devlist[devanz].outputParameters.channelCount > 0) devlist[didx].in_out = 2; else if (devlist[didx].inputParameters.channelCount > 0) devlist[didx].in_out = 0; else if (devlist[didx].outputParameters.channelCount > 0) devlist[didx].in_out = 1; devlist[didx].index = didx; devlist[didx].active = 1; for (int j = 0; standardSampleRates[j] > 0; j++) { PaError err = 0; //if (devlist[didx].inputParameters.channelCount > 0 && devlist[didx].outputParameters.channelCount > 0) // err = Pa_IsFormatSupported(&devlist[didx].inputParameters, &devlist[didx].outputParameters, standardSampleRates[j]); if (devlist[didx].inputParameters.channelCount > 0) err = Pa_IsFormatSupported(&devlist[didx].inputParameters, NULL, standardSampleRates[j]); if (devlist[didx].outputParameters.channelCount > 0) err = Pa_IsFormatSupported(NULL, &devlist[didx].outputParameters, standardSampleRates[j]); // portaudio cannot detect if a device was removed, instead it delivers errors if (err == paInvalidDevice) devlist[didx].active = 0; else if (err == paFormatIsSupported) { if (j == 0) devlist[didx].supports_44100 = 1; if (j == 1) devlist[didx].supports_48000 = 1; } } } } io_buildAudioDevString(); // close stream if a device does not exist any more for (int i = 0; i < devanz; i++) { if (devlist[i].active == 0) { if (devlist[i].capStream != NULL) { printf("capture device %s disconnected, stop stream\n", devlist[i].name); Pa_CloseStream(devlist[i].capStream); devlist[i].capStream = NULL; devlist[i].working = 0; } if (devlist[i].pbStream != NULL) { printf("playback device %s disconnected, stop stream\n", devlist[i].name); Pa_CloseStream(devlist[i].pbStream); devlist[i].pbStream = NULL; devlist[i].working = 0; } } } static int csum = 0; int sum = 0; uint8_t* p = (uint8_t*)&(devlist[0].index); for (int i = 0; i < (int)sizeof(devlist); i++) sum += *p++; if (csum != sum) { csum = sum; printf("Windows Devices found:\n"); for (int i = 0; i < devanz; i++) { printf("Portaudio ID: %d\n", devlist[i].index); printf("Name: %s\n", devlist[i].name); printf("Cap/PB: %d\n", devlist[i].in_out); printf("Channels: i:%d o:%d\n", devlist[i].inputParameters.channelCount, devlist[i].outputParameters.channelCount); printf("SR 44100: %d\n", devlist[i].supports_44100); printf("SR 48000: %d\n", devlist[i].supports_48000); printf("is active: %s\n", devlist[i].active ? "yes" : "no"); } } return 0; } #endif //WIN32 // find a device in devlist // returns: list index or -1 if error int searchDevice(char* devname, int io) { for (int i = 0; i < devanz; i++) { if (strcmp(devname, devlist[i].name) == 0 && (devlist[i].in_out == io || devlist[i].in_out == 2)) return i; } return -1; } // choose physical, real sample rate for a device // returns: 0=ok, -1=error: no sample rate supported int getRealSamprate(int idx) { if (devlist[idx].requested_samprate == 44100) { if (devlist[idx].supports_44100) devlist[idx].real_samprate = 44100; else if (devlist[idx].supports_48000) devlist[idx].real_samprate = 48000; else return -1; } else if (devlist[idx].requested_samprate == 48000) { if (devlist[idx].supports_48000) devlist[idx].real_samprate = 48000; else if (devlist[idx].supports_44100) devlist[idx].real_samprate = 44100; else return -1; } return 0; } // build string of audio device name, to be sent to application as response to Broadcast search // starting with PB devices, sperarator ^, capture devices // separator between devices: ~ #define MAXDEVSTRLEN (MAXDEVICES * (MAXDEVNAMELENGTH + 2) + 10) uint8_t io_devstring[MAXDEVSTRLEN]; void io_buildAudioDevString() { memset(io_devstring, 0, sizeof(io_devstring)); io_devstring[0] = ' '; // placeholder for other data io_devstring[1] = ' '; io_devstring[2] = ' '; io_devstring[3] = ' '; io_devstring[4] = ' '; // playback devices for (int i = 0; i < devanz; i++) { if (devlist[i].active == 0) continue; if (strlen((char *)io_devstring) > MAXDEVSTRLEN) { printf("io_devstring too small:%d / %d. Serious error, abort program\n", MAXDEVSTRLEN, (int)strlen((char*)io_devstring)); exit(0); } if (devlist[i].in_out == 1) { strcat((char*)io_devstring, devlist[i].name); strcat((char*)io_devstring, "~"); // audio device separator } } strcat((char*)(io_devstring + 1), "^"); // PB, CAP separator // capture devices for (int i = 0; i < devanz; i++) { if (devlist[i].active == 0) continue; if (strlen((char*)io_devstring) > MAXDEVSTRLEN) { printf("io_devstring too small:%d / %d. Serious error, abort program\n", MAXDEVSTRLEN, (int)strlen((char*)io_devstring)); exit(0); } if (devlist[i].in_out == 0) { strcat((char*)io_devstring, devlist[i].name); strcat((char*)io_devstring, "~"); // audio device separator } } //printf("<%s>\n", (char *)io_devstring); } uint8_t* io_getAudioDevicelist(int* len) { *len = strlen((char*)(io_devstring + 1)) + 1; return io_devstring; }