248 lines
7.8 KiB
C++
Executable File
248 lines
7.8 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 libsoundio playback stream and a callback routine. Plays the
|
|
* audio samples coming via the fifo (Linux only)
|
|
*/
|
|
#ifndef WIN32
|
|
|
|
#include "libkmaudio.h"
|
|
|
|
// #define SINEWAVETEST
|
|
|
|
#ifdef SINEWAVETEST
|
|
static const double PI = 3.14159265358979323846264338328;
|
|
static double seconds_offset = 0.0;
|
|
#endif
|
|
|
|
void write_sample_float32ne(char* ptr, double sample)
|
|
{
|
|
float* buf = (float*)ptr;
|
|
*buf = (float)sample;
|
|
}
|
|
|
|
static void write_callback(struct SoundIoOutStream* outstream, int frame_count_min, int frame_count_max)
|
|
{
|
|
if (outstream == NULL || soundio == NULL) return;
|
|
//printf("pb: %d %d\n", frame_count_min, frame_count_max);
|
|
//printf("pb :%d\n", outstream->sample_rate);
|
|
int idx = *((int*)(outstream->userdata));
|
|
|
|
#ifdef SINEWAVETEST
|
|
double float_sample_rate = outstream->sample_rate;
|
|
double seconds_per_frame = 1.0 / float_sample_rate;
|
|
double pitch = 440.0;
|
|
double radians_per_second = pitch * 2.0 * PI;
|
|
#endif
|
|
struct SoundIoChannelArea* areas;
|
|
int err;
|
|
|
|
int frames_left = 4800;
|
|
if (frame_count_max < frames_left)
|
|
frames_left = frame_count_max;
|
|
|
|
for (;;) {
|
|
int frame_count = frames_left;
|
|
if ((err = soundio_outstream_begin_write(outstream, &areas, &frame_count))) {
|
|
fprintf(stderr, "write_callback unrecoverable soundio_outstream_begin_write error: %s\n", soundio_strerror(err));
|
|
return;
|
|
}
|
|
|
|
if (!frame_count)
|
|
break;
|
|
|
|
//printf("ck: %d read from fifo:%d\n", frame_count,idx);
|
|
if (frame_count >= 10000)
|
|
{
|
|
printf("frame count >= 1000: %d\n", frame_count);
|
|
exit(0);
|
|
}
|
|
|
|
float f[10000];
|
|
// 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(float) * frame_count);
|
|
if (io_fifo_elems_avail(idx) >= frame_count)
|
|
{
|
|
io_read_fifo_num(idx, f, frame_count);
|
|
//if (devlist[idx].audio_playing == 0) printf("NOW PLAYING\n");
|
|
devlist[idx].audio_playing = 1;
|
|
}
|
|
else
|
|
{
|
|
// nothing to send
|
|
//if (devlist[idx].audio_playing == 1) printf("STOPS playing\n");
|
|
devlist[idx].audio_playing = 0;
|
|
}
|
|
|
|
//measure_speed_bps(frame_count);
|
|
|
|
const struct SoundIoChannelLayout* layout = &outstream->layout;
|
|
|
|
for (int frame = 0; frame < frame_count; frame += 1)
|
|
{
|
|
#ifdef SINEWAVETEST
|
|
double sample = sin((seconds_offset + frame * seconds_per_frame) * radians_per_second);
|
|
#endif
|
|
for (int channel = 0; channel < layout->channel_count; channel += 1)
|
|
{
|
|
float ftx = f[frame];
|
|
//getTXMax(f[frame]);
|
|
#ifdef SINEWAVETEST
|
|
write_sample_float32ne(areas[channel].ptr, sample); // sine wave test tone
|
|
#endif
|
|
write_sample_float32ne(areas[channel].ptr, ftx);
|
|
areas[channel].ptr += areas[channel].step;
|
|
}
|
|
}
|
|
#ifdef SINEWAVETEST
|
|
seconds_offset = fmod(seconds_offset + seconds_per_frame * frame_count, 1.0);
|
|
#endif
|
|
|
|
if ((err = soundio_outstream_end_write(outstream))) {
|
|
if (err == SoundIoErrorUnderflow)
|
|
return;
|
|
fprintf(stderr, "unrecoverable stream error: %s\n", soundio_strerror(err));
|
|
return;
|
|
}
|
|
|
|
frames_left -= frame_count;
|
|
if (frames_left <= 0)
|
|
break;
|
|
}
|
|
}
|
|
|
|
void underflow_callback(struct SoundIoOutStream* outstream)
|
|
{
|
|
}
|
|
|
|
void close_playback_stream(int idx)
|
|
{
|
|
if (devlist[idx].outstream != NULL)
|
|
{
|
|
soundio_outstream_destroy(devlist[idx].outstream);
|
|
devlist[idx].outstream = 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 = 0; // index into devlist
|
|
char* pbdevid = getDevID(devname, 1, &idx);
|
|
printf("idx:%d\n", idx);
|
|
if (pbdevid == NULL) return -1;
|
|
|
|
close_playback_stream(idx);
|
|
|
|
printf("Starting PB stream:%s [%d]\n", devname, idx);
|
|
|
|
io_fifo_clear(idx);
|
|
|
|
devlist[idx].working = 0;
|
|
|
|
// define the capture device
|
|
soundio_flush_events(soundio);
|
|
|
|
for (int i = 0; i < soundio_output_device_count(soundio); i++)
|
|
{
|
|
devlist[idx].io_pb_device = NULL;
|
|
struct SoundIoDevice* device = soundio_get_output_device(soundio, i);
|
|
if (strcmp(device->id, pbdevid) == 0)
|
|
{
|
|
devlist[idx].io_pb_device = device;
|
|
break;
|
|
}
|
|
soundio_device_unref(device);
|
|
}
|
|
if (!devlist[idx].io_pb_device)
|
|
{
|
|
printf("Invalid device id: %s\n", pbdevid);
|
|
return -1;
|
|
}
|
|
|
|
if (devlist[idx].io_pb_device->probe_error)
|
|
{
|
|
printf("Unable to probe device: %s\n", soundio_strerror(devlist[idx].io_pb_device->probe_error));
|
|
return -1;
|
|
}
|
|
|
|
// create playback callback
|
|
devlist[idx].outstream = soundio_outstream_create(devlist[idx].io_pb_device);
|
|
if (!devlist[idx].outstream) {
|
|
printf("soundio_outstream_create: out of memory\n");
|
|
return 0;
|
|
}
|
|
|
|
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);
|
|
|
|
devlist[idx].outstream->format = SoundIoFormatFloat32NE;
|
|
devlist[idx].outstream->sample_rate = devlist[idx].real_samprate;
|
|
devlist[idx].outstream->software_latency = 0.1f;
|
|
devlist[idx].outstream->write_callback = write_callback;
|
|
devlist[idx].outstream->underflow_callback = underflow_callback;
|
|
devlist[idx].outstream->userdata = &(devlist[idx].index);
|
|
|
|
int err = 0;
|
|
if ((err = soundio_outstream_open(devlist[idx].outstream))) {
|
|
printf("unable to open output stream: %s", soundio_strerror(err));
|
|
return -1;
|
|
}
|
|
|
|
if ((err = soundio_outstream_start(devlist[idx].outstream))) {
|
|
printf("unable to start output device: %s", soundio_strerror(err));
|
|
return -1;
|
|
}
|
|
|
|
printf("selected PLAYBACK device:\nname:%s\nid :%s\n", devname, pbdevid);
|
|
printf("physical playback rate:%d, requested capture rate:%d\n", devlist[idx].real_samprate, devlist[idx].requested_samprate);
|
|
printf("format: %s\n\n", soundio_format_string(devlist[idx].outstream->format));
|
|
|
|
devlist[idx].working = 1;
|
|
|
|
return idx;
|
|
}
|
|
|
|
#endif // ndef WIN32
|