RS-tracker/tools/pa-stdout.c

461 wiersze
13 KiB
C

/*
* - load portaudio.dll
* - output device n to stdout
*
* win:
* gcc -DWIN_DLL pa-stdout.c
* (cl /DWIN_DLL /DCYGWIN pa-stdout.c)
* gcc -DWIN_DLL -DPA_DLL=\"PortAudio.dll\" pa-stdout.c
*
* linux:
* gcc pa-stdout.c -lportaudio
*
* [select SDR Audio Output channel]
* ./pa-stdout.exe --list
* ./pa-stdout.exe [devNo] | ./rs41mod.exe -vx --ptu
* ./pa-stdout.exe --line [lineNo] | ./rs41mod.exe -vx --ptu
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef WIN_DLL
#include <windows.h>
#endif
#ifdef CYGWIN
#include <fcntl.h> // cygwin: _setmode()
#include <io.h>
#endif
#ifndef WIN_DLL
#include "portaudio.h"
#else
//
//------------------------------------------------------------------------------------------------------
#define paNoDevice ((PaDeviceIndex)-1)
#define paClipOff ((PaStreamFlags) 0x00000001)
#define paFloat32 ((PaSampleFormat) 0x00000001) /**< @see PaSampleFormat */
#define paInt32 ((PaSampleFormat) 0x00000002) /**< @see PaSampleFormat */
#define paInt24 ((PaSampleFormat) 0x00000004) /**< Packed 24 bit format. @see PaSampleFormat */
#define paInt16 ((PaSampleFormat) 0x00000008) /**< @see PaSampleFormat */
#define paInt8 ((PaSampleFormat) 0x00000010) /**< @see PaSampleFormat */
#define paUInt8 ((PaSampleFormat) 0x00000020) /**< @see PaSampleFormat */
#define paCustomFormat ((PaSampleFormat) 0x00010000) /**< @see PaSampleFormat */
typedef int PaError;
typedef enum PaErrorCode
{
paNoError = 0,
paNotInitialized = -10000,
paUnanticipatedHostError,
paInvalidChannelCount,
paInvalidSampleRate,
paInvalidDevice,
paInvalidFlag,
paSampleFormatNotSupported,
paBadIODeviceCombination,
paInsufficientMemory,
paBufferTooBig,
paBufferTooSmall,
paNullCallback,
paBadStreamPtr,
paTimedOut,
paInternalError,
paDeviceUnavailable,
paIncompatibleHostApiSpecificStreamInfo,
paStreamIsStopped,
paStreamIsNotStopped,
paInputOverflowed,
paOutputUnderflowed,
paHostApiNotFound,
paInvalidHostApi,
paCanNotReadFromACallbackStream,
paCanNotWriteToACallbackStream,
paCanNotReadFromAnOutputOnlyStream,
paCanNotWriteToAnInputOnlyStream,
paIncompatibleStreamHostApi,
paBadBufferPtr
} PaErrorCode;
typedef int PaDeviceIndex;
typedef int PaHostApiIndex;
typedef double PaTime;
typedef struct PaDeviceInfo
{
int structVersion; /* this is struct version 2 */
const char *name;
PaHostApiIndex hostApi; /**< note this is a host API index, not a type id*/
int maxInputChannels;
int maxOutputChannels;
/** Default latency values for interactive performance. */
PaTime defaultLowInputLatency;
PaTime defaultLowOutputLatency;
/** Default latency values for robust non-interactive applications (eg. playing sound files). */
PaTime defaultHighInputLatency;
PaTime defaultHighOutputLatency;
double defaultSampleRate;
} PaDeviceInfo;
typedef unsigned long PaStreamFlags;
typedef unsigned long PaSampleFormat;
typedef struct PaStreamCallbackTimeInfo{
PaTime inputBufferAdcTime; /**< The time when the first sample of the input buffer was captured at the ADC input */
PaTime currentTime; /**< The time when the stream callback was invoked */
PaTime outputBufferDacTime; /**< The time when the first sample of the output buffer will output the DAC */
} PaStreamCallbackTimeInfo;
typedef unsigned long PaStreamCallbackFlags;
typedef struct PaStreamParameters
{
PaDeviceIndex device;
int channelCount;
PaSampleFormat sampleFormat;
PaTime suggestedLatency;
void *hostApiSpecificStreamInfo;
} PaStreamParameters;
typedef void PaStream;
typedef int PaStreamCallback(
const void *input, void *output,
unsigned long frameCount,
const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags,
void *userData );
//
//------------------------------------------------------------------------------------------------------
// load WIN-DLL
// prototypes ( __cdecl -> WINAPIV , __stdcall -> WINAPI )
#define CALLCONV WINAPIV // __cdecl
typedef int (CALLCONV* t_Pa_Initialize)(void);
typedef int (CALLCONV* t_Pa_GetDefaultInputDevice)(void);
typedef int (CALLCONV* t_Pa_GetDeviceCount)(void);
typedef PaDeviceInfo* (CALLCONV* t_Pa_GetDeviceInfo)(PaDeviceIndex device);
t_Pa_Initialize Pa_Initialize;
t_Pa_GetDefaultInputDevice Pa_GetDefaultInputDevice;
t_Pa_GetDeviceCount Pa_GetDeviceCount;
t_Pa_GetDeviceInfo Pa_GetDeviceInfo;
typedef int (CALLCONV* t_Pa_OpenStream)(PaStream**,PaStreamParameters*,PaStreamParameters*,double,
unsigned long,PaStreamFlags,PaStreamCallback*,void*);
typedef int (CALLCONV* t_Pa_StartStream)(PaStream*);
typedef int (CALLCONV* t_Pa_ReadStream)(PaStream*,void*,unsigned long);
typedef int (CALLCONV* t_Pa_CloseStream)(PaStream*);
typedef int (CALLCONV* t_Pa_Terminate)(void);
typedef char* (CALLCONV* t_Pa_GetErrorText)(PaError);
t_Pa_OpenStream Pa_OpenStream;
t_Pa_StartStream Pa_StartStream;
t_Pa_ReadStream Pa_ReadStream;
t_Pa_CloseStream Pa_CloseStream;
t_Pa_Terminate Pa_Terminate;
t_Pa_GetErrorText Pa_GetErrorText;
HINSTANCE pa_handle;
int adr_functions() {
int ret = 0;
Pa_Initialize = (t_Pa_Initialize)GetProcAddress(pa_handle, "Pa_Initialize");
if (Pa_Initialize == NULL) {
fprintf(stderr, "ERROR: GetProcAddress\n");
ret = -2;
}
Pa_GetDefaultInputDevice = (t_Pa_GetDefaultInputDevice)GetProcAddress(pa_handle, "Pa_GetDefaultInputDevice");
if (Pa_GetDefaultInputDevice == NULL) {
fprintf(stderr, "ERROR: GetProcAddress\n");
ret = -2;
}
Pa_GetDeviceCount = (t_Pa_GetDeviceCount)GetProcAddress(pa_handle, "Pa_GetDeviceCount");
if (Pa_GetDeviceCount == NULL) {
fprintf(stderr, "ERROR: GetProcAddress\n");
ret = -2;
}
Pa_GetDeviceInfo = (t_Pa_GetDeviceInfo)GetProcAddress(pa_handle, "Pa_GetDeviceInfo");
if (Pa_GetDeviceInfo == NULL) {
fprintf(stderr, "ERROR: GetProcAddress\n");
ret = -2;
}
Pa_OpenStream = (t_Pa_OpenStream)GetProcAddress(pa_handle, "Pa_OpenStream");
if (Pa_OpenStream == NULL) {
fprintf(stderr, "ERROR: GetProcAddress\n");
ret = -2;
}
Pa_StartStream = (t_Pa_StartStream)GetProcAddress(pa_handle, "Pa_StartStream");
if (Pa_StartStream == NULL) {
fprintf(stderr, "ERROR: GetProcAddress\n");
ret = -2;
}
Pa_ReadStream = (t_Pa_ReadStream)GetProcAddress(pa_handle, "Pa_ReadStream");
if (Pa_ReadStream == NULL) {
fprintf(stderr, "ERROR: GetProcAddress\n");
ret = -2;
}
Pa_CloseStream = (t_Pa_CloseStream)GetProcAddress(pa_handle, "Pa_CloseStream");
if (Pa_CloseStream == NULL) {
fprintf(stderr, "ERROR: GetProcAddress\n");
ret = -2;
}
Pa_Terminate = (t_Pa_Terminate)GetProcAddress(pa_handle, "Pa_Terminate");
if (Pa_Terminate == NULL) {
fprintf(stderr, "ERROR: GetProcAddress\n");
ret = -2;
}
Pa_GetErrorText = (t_Pa_GetErrorText)GetProcAddress(pa_handle, "Pa_GetErrorText");
if (Pa_GetErrorText == NULL) {
fprintf(stderr, "ERROR: GetProcAddress\n");
ret = -2;
}
return ret;
}
#endif
#define SAMPLE_RATE (48000)
#define FRAMES_PER_BUFFER (1024)
#define N_CH (0x02)
// 16-bit int samples
#define PA_SAMPLE_TYPE paInt16
typedef short SAMPLE; // 16-bit
int get_Devices(int list) {
int i;
PaDeviceIndex numDevices = Pa_GetDeviceCount();
fprintf(stderr, "#devices: %d\n", numDevices);
if (list)
{
for (i = 0; i < numDevices; i++)
{
const PaDeviceInfo *devInfo = Pa_GetDeviceInfo(i);
fprintf(stderr, "[%2d] %s\n", i, devInfo->name);
}
}
return numDevices;
}
int get_Line(int lineNo, int *devNo) {
int i;
const PaDeviceIndex numDevices = Pa_GetDeviceCount();
const PaDeviceInfo *devInfo = NULL;
char strbuf[32];
sprintf(strbuf, "Line %d", lineNo % 100);
fprintf(stderr, "#devices: %d\n", numDevices);
for (i = 0; i < numDevices; i++)
{
devInfo = Pa_GetDeviceInfo(i);
if ( strstr(devInfo->name, strbuf) ) {
*devNo = i;
break;
}
}
return numDevices;
}
unsigned char wav_hdr[] = { // 0x80, 0xbb, 0x00, 0x00: sample_rate=48000
0x52, 0x49, 0x46, 0x46, 0x24, 0xf0, 0xff, 0x7f, 0x57, 0x41, 0x56, 0x45, 0x66, 0x6d, 0x74, 0x20,
0x10, 0x00, 0x00, 0x00, 0x01, 0x00, N_CH, 0x00, 0x80, 0xbb, 0x00, 0x00, 0x00, 0xee, 0x02, 0x00,
0x04, 0x00, 0x10, 0x00, 0x64, 0x61, 0x74, 0x61, 0x00, 0xf0, 0xff, 0x7f};
//0x10,0x00: bits/sample=16 // 0x0n, 0x00: channels=n
int main(int argc, char* argv[])
{
int ret = 0;
int devNo = -1,
numDevices = 0,
list = 0,
lineNo = -1;
PaError err = paNoError;
PaStreamParameters inputParameters;
PaStream *stream;
const PaDeviceInfo *devInfo = NULL;
SAMPLE *dataSamples = NULL;
int totalFrames;
int numSamples;
#ifdef CYGWIN
_setmode(fileno(stdout), _O_BINARY); // _setmode(_fileno(stdout), _O_BINARY);
#endif
//setvbuf(stdout, NULL, _IOFBF, 128); // buffered
//setvbuf(stdout, NULL, _IONBF, 0); // unbuffered
setbuf(stdout, NULL); // unbuffered
setbuf(stderr, NULL);
#ifdef WIN_DLL
{
char *dll_path = "PortAudio.dll"; // x86/x64 (same as gcc/cl)
// gcc -DPA_DLL=\"PortAudio_x64.dll\" ...
#ifdef PA_DLL
dll_path = PA_DLL;
#endif
pa_handle = LoadLibrary(dll_path);
if (pa_handle == NULL) {
fprintf(stderr, "ERROR: loadlibrary\n");
return -1;
}
ret = adr_functions();
if (ret != 0) goto error;
}
#endif
err = Pa_Initialize();
if ( err != paNoError ) goto done;
if (argv[1]) {
devNo = -1;
lineNo = -1;
if (strcmp(argv[1], "--list") == 0) {
list = 1;
devNo = -1;
}
else if (strcmp(argv[1], "--line") == 0) {
if (argv[2]) lineNo = atoi(argv[2]);
}
else {
devNo = atoi(argv[1]);
}
if (devNo == 0 && argv[1][0] != '0') devNo = -1;
}
if (lineNo >= 0) numDevices = get_Line(lineNo, &devNo);
else numDevices = get_Devices(list);
if ( list ) goto done;
if ( devNo >= numDevices ) devNo = -1;
totalFrames = FRAMES_PER_BUFFER;
numSamples = totalFrames * N_CH;
dataSamples = (SAMPLE *) calloc( numSamples, sizeof(SAMPLE) );
if ( dataSamples == NULL )
{
fprintf(stderr, "ERROR: malloc\n");
goto done;
}
if (devNo < 0) inputParameters.device = Pa_GetDefaultInputDevice();
else inputParameters.device = devNo;
fprintf(stderr, "Input Device:\n");
devInfo = Pa_GetDeviceInfo(inputParameters.device);
fprintf(stderr, "[%d] %s\n", inputParameters.device, devInfo->name);
if (inputParameters.device == paNoDevice) {
fprintf(stderr,"Error: No default input device.\n");
goto done;
}
inputParameters.channelCount = N_CH;
inputParameters.sampleFormat = PA_SAMPLE_TYPE;
inputParameters.suggestedLatency = Pa_GetDeviceInfo( inputParameters.device )->defaultLowInputLatency;
inputParameters.hostApiSpecificStreamInfo = NULL;
err = Pa_OpenStream(
&stream,
&inputParameters,
NULL, // &outputParameters,
SAMPLE_RATE,
FRAMES_PER_BUFFER,
paClipOff,
NULL,
NULL );
if ( err != paNoError ) goto done;
err = Pa_StartStream( stream );
if ( err != paNoError ) goto done;
fwrite( wav_hdr, 1, sizeof(wav_hdr)/*44*/, stdout );
while (1) {
err = Pa_ReadStream( stream, dataSamples, totalFrames );
if ( err != paNoError ) {
if ( err != paInputOverflowed) goto done;
//fprintf(stderr, "Error: %d (%s)\n", err, Pa_GetErrorText( err ) );
// // dataSamples...
}
fwrite( dataSamples, N_CH * sizeof(SAMPLE), totalFrames, stdout );
}
err = Pa_CloseStream( stream );
if ( err != paNoError ) goto done;
done:
Pa_Terminate();
if ( dataSamples ) free(dataSamples);
if ( err != paNoError )
{
fprintf( stderr, "Error number: %d\n", err );
fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
err = 1; /* Always return 0 or 1, but no other return codes. */
}
error:
#ifdef WIN_DLL
FreeLibrary(pa_handle);
#endif
if (ret) err = ret;
return err;
}