192 lines
5.2 KiB
C++
Executable File
192 lines
5.2 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_playback.cpp ...
|
||
* starts a portaudio playback stream and a callback routine. Plays the
|
||
* audio samples coming via the fifo (Windows only)
|
||
*/
|
||
|
||
#include "libkmaudio.h"
|
||
|
||
#define FRAMES_PER_BUFFER 512
|
||
|
||
int playCallback(const void* inputBuffer,
|
||
void* outputBuffer,
|
||
unsigned long framesPerBuffer,
|
||
const PaStreamCallbackTimeInfo* timeInfo,
|
||
PaStreamCallbackFlags statusFlags,
|
||
void* userData);
|
||
|
||
void close_playback_stream(int idx)
|
||
{
|
||
if (devlist[idx].pbStream != NULL)
|
||
{
|
||
Pa_CloseStream(devlist[idx].pbStream);
|
||
devlist[idx].pbStream = NULL;
|
||
}
|
||
}
|
||
|
||
int kmaudio_startPlayback(char* devname, int samprate)
|
||
{
|
||
printf("Start request for PB stream:%s\n", devname);
|
||
|
||
if (devname == NULL || strlen(devname) < 3) // no devices defined yet
|
||
{
|
||
printf("no PB devices specified\n");
|
||
return -1;
|
||
}
|
||
|
||
int idx = searchDevice(devname, 1);
|
||
if (idx == -1)
|
||
{
|
||
printf("Playback Device:<%s> not found\n", devname);
|
||
return -1;
|
||
}
|
||
|
||
devlist[idx].working = 0;
|
||
|
||
close_playback_stream(idx);
|
||
|
||
printf("Starting PB stream:%s [%d]\n", devname, idx);
|
||
|
||
io_fifo_clear(idx);
|
||
|
||
devlist[idx].requested_samprate = samprate;
|
||
if (getRealSamprate(idx) == -1)
|
||
{
|
||
printf("Samplerate %d not supported by device:<%s>\n", samprate, devname);
|
||
return -1;
|
||
}
|
||
|
||
if (devlist[idx].requested_samprate != devlist[idx].real_samprate)
|
||
resampler_create(idx);
|
||
|
||
struct PaWasapiStreamInfo wasapiInfo;
|
||
memset(&wasapiInfo, 0, sizeof(PaWasapiStreamInfo));
|
||
wasapiInfo.size = sizeof(PaWasapiStreamInfo);
|
||
wasapiInfo.hostApiType = paWASAPI;
|
||
wasapiInfo.version = 1;
|
||
wasapiInfo.flags = (paWinWasapiExclusive | paWinWasapiThreadPriority);
|
||
wasapiInfo.threadPriority = eThreadPriorityProAudio;
|
||
|
||
devlist[idx].outputParameters.hostApiSpecificStreamInfo = (&wasapiInfo);
|
||
|
||
PaError e = Pa_IsFormatSupported(&devlist[idx].outputParameters, NULL, (double)devlist[idx].real_samprate);
|
||
printf("Playback : err:%d device:%d PAdev:%d samprate: %f chans:%d\n", e, idx, devlist[idx].devnum, (double)devlist[idx].real_samprate, devlist[idx].outputParameters.channelCount);
|
||
|
||
devlist[idx].index = idx;
|
||
|
||
int err = Pa_OpenStream(
|
||
&devlist[idx].pbStream,
|
||
NULL,
|
||
&devlist[idx].outputParameters,
|
||
(double)devlist[idx].real_samprate,
|
||
FRAMES_PER_BUFFER,
|
||
paClipOff,
|
||
playCallback,
|
||
&(devlist[idx].index));
|
||
|
||
if (err != paNoError)
|
||
{
|
||
printf("cannot open playback stream for device:<%s> %d\n", devname, err);
|
||
return -1;
|
||
}
|
||
|
||
err = Pa_StartStream(devlist[idx].pbStream);
|
||
if (err != paNoError)
|
||
{
|
||
printf("cannot start playback stream for device:<%s>\n", devname);
|
||
return -1;
|
||
}
|
||
|
||
printf("Playback started sucessfully\n");
|
||
devlist[idx].working = 1;
|
||
return idx;
|
||
}
|
||
|
||
int playCallback( const void* inputBuffer,
|
||
void* outputBuffer,
|
||
unsigned long framesPerBuffer,
|
||
const PaStreamCallbackTimeInfo* timeInfo,
|
||
PaStreamCallbackFlags statusFlags,
|
||
void* userData)
|
||
{
|
||
int16_t* rptr = (int16_t*)outputBuffer;
|
||
int devidx = *((int*)userData);
|
||
int chans = devlist[devidx].outputParameters.channelCount;
|
||
|
||
//measure_speed_bps(framesPerBuffer);
|
||
|
||
/* das hier ging
|
||
int16_t f[FRAMES_PER_BUFFER];
|
||
memset(f, 0, sizeof(int16_t) * FRAMES_PER_BUFFER);
|
||
unsigned int num = io_read_fifo_num_short(devidx, f, framesPerBuffer);
|
||
if (num < framesPerBuffer)
|
||
{
|
||
//printf("got %d from fifo, requested %d\n", num, framesPerBuffer);
|
||
}
|
||
int av = io_fifo_elems_avail(devidx);
|
||
das n<>chste ist neu
|
||
*/
|
||
|
||
int16_t f[FRAMES_PER_BUFFER];
|
||
// if fifo does not have enough data, just send 0.. (silence)
|
||
// this gives the fifo a chance to fill up a bit
|
||
memset(f, 0, sizeof(int16_t) * FRAMES_PER_BUFFER);
|
||
if ((unsigned long)io_fifo_elems_avail(devidx) >= framesPerBuffer)
|
||
{
|
||
io_read_fifo_num_short(devidx, f, framesPerBuffer);
|
||
devlist[devidx].audio_playing = 1;
|
||
}
|
||
else
|
||
{
|
||
// nothing to send
|
||
devlist[devidx].audio_playing = 0;
|
||
}
|
||
|
||
for (unsigned int i = 0; i < framesPerBuffer; i++)
|
||
{
|
||
if (chans == 1) rptr[i] = f[i];
|
||
else
|
||
{
|
||
rptr[i * 2] = f[i];
|
||
rptr[i * 2 + 1] = f[i];
|
||
}
|
||
}
|
||
|
||
// Prevent unused variable warnings
|
||
(void)inputBuffer;
|
||
(void)timeInfo;
|
||
(void)statusFlags;
|
||
|
||
if (keepcallbacksrunning == 1)
|
||
return paContinue;
|
||
|
||
return paComplete;
|
||
}
|