mirror of
https://github.com/dj0abr/SSB_HighSpeed_Modem.git
synced 2024-10-31 15:37:12 -04:00
292 lines
10 KiB
C++
Executable File
292 lines
10 KiB
C++
Executable File
/*
|
|
* 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;
|
|
}
|