Implemented output audio stream driver for linux.
Implement outputStream backend on linux using Pulseaudio simple API. TG-250
This commit is contained in:
parent
b3a861a47c
commit
081b19e52c
10
meson.build
10
meson.build
|
|
@ -278,7 +278,8 @@ linux_inc = inc + ['platform/targets/linux',
|
||||||
if not meson.is_cross_build()
|
if not meson.is_cross_build()
|
||||||
sdl_dep = dependency('SDL2')
|
sdl_dep = dependency('SDL2')
|
||||||
threads_dep = dependency('threads')
|
threads_dep = dependency('threads')
|
||||||
linux_dep = [sdl_dep, threads_dep, codec2_dep]
|
pulse_dep = dependency('libpulse')
|
||||||
|
linux_dep = [sdl_dep, threads_dep, pulse_dep, codec2_dep]
|
||||||
else
|
else
|
||||||
linux_dep = [ ]
|
linux_dep = [ ]
|
||||||
endif
|
endif
|
||||||
|
|
@ -371,7 +372,7 @@ mod17_def = def + stm32f405_def + {'PLATFORM_MOD17': ''}
|
||||||
|
|
||||||
linux_c_args = ['-DPLATFORM_LINUX']
|
linux_c_args = ['-DPLATFORM_LINUX']
|
||||||
linux_cpp_args = ['-std=c++14', '-DPLATFORM_LINUX']
|
linux_cpp_args = ['-std=c++14', '-DPLATFORM_LINUX']
|
||||||
linux_l_args = ['-lm', '-lreadline']
|
linux_l_args = ['-lm', '-lreadline', '-lpulse-simple']
|
||||||
|
|
||||||
# Add AddressSanitizer if required
|
# Add AddressSanitizer if required
|
||||||
if get_option('asan')
|
if get_option('asan')
|
||||||
|
|
@ -689,9 +690,14 @@ linux_inputStream_test = executable('linux_inputStream_test',
|
||||||
sources : unit_test_src + ['tests/unit/linux_inputStream_test.cpp'],
|
sources : unit_test_src + ['tests/unit/linux_inputStream_test.cpp'],
|
||||||
kwargs : unit_test_opts)
|
kwargs : unit_test_opts)
|
||||||
|
|
||||||
|
sine_test = executable('sine_test',
|
||||||
|
sources : unit_test_src + ['tests/unit/play_sine.c'],
|
||||||
|
kwargs : unit_test_opts)
|
||||||
|
|
||||||
test('M17 Golay Unit Test', m17_golay_test)
|
test('M17 Golay Unit Test', m17_golay_test)
|
||||||
test('M17 Viterbi Unit Test', m17_viterbi_test)
|
test('M17 Viterbi Unit Test', m17_viterbi_test)
|
||||||
test('M17 Demodulator Test', m17_demodulator_test)
|
test('M17 Demodulator Test', m17_demodulator_test)
|
||||||
test('M17 RRC Test', m17_rrc_test)
|
test('M17 RRC Test', m17_rrc_test)
|
||||||
test('Codeplug Test', cps_test)
|
test('Codeplug Test', cps_test)
|
||||||
test('Linux InputStream Test', linux_inputStream_test)
|
test('Linux InputStream Test', linux_inputStream_test)
|
||||||
|
test('Sine Test', sine_test)
|
||||||
|
|
|
||||||
|
|
@ -18,8 +18,17 @@
|
||||||
* along with this program; if not, see <http://www.gnu.org/licenses/> *
|
* along with this program; if not, see <http://www.gnu.org/licenses/> *
|
||||||
***************************************************************************/
|
***************************************************************************/
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
#include <interfaces/audio_stream.h>
|
#include <interfaces/audio_stream.h>
|
||||||
|
#include <pulse/error.h>
|
||||||
|
#include <pulse/simple.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
static int priority = PRIO_BEEP; // Current priority
|
||||||
|
static bool running = false; // Stream is running
|
||||||
|
pa_simple *s = NULL; // Pulseaudio instance
|
||||||
|
int error; // Error code
|
||||||
|
|
||||||
streamId outputStream_start(const enum AudioSink destination,
|
streamId outputStream_start(const enum AudioSink destination,
|
||||||
const enum AudioPriority prio,
|
const enum AudioPriority prio,
|
||||||
|
|
@ -28,14 +37,53 @@ streamId outputStream_start(const enum AudioSink destination,
|
||||||
const enum BufMode mode,
|
const enum BufMode mode,
|
||||||
const uint32_t sampleRate)
|
const uint32_t sampleRate)
|
||||||
{
|
{
|
||||||
(void) destination;
|
assert(destination == SINK_SPK && "Only speaker sink was implemented!\n");
|
||||||
(void) prio;
|
assert(mode == BUF_LINEAR && "Only linear buffering was implemented!\n");
|
||||||
(void) buf;
|
|
||||||
(void) length;
|
|
||||||
(void) mode;
|
|
||||||
(void) sampleRate;
|
|
||||||
|
|
||||||
return -1;
|
/* The Sample format to use */
|
||||||
|
static pa_sample_spec ss = {
|
||||||
|
.format = PA_SAMPLE_S16LE,
|
||||||
|
.rate = 0,
|
||||||
|
.channels = 1
|
||||||
|
};
|
||||||
|
|
||||||
|
ss.rate = sampleRate;
|
||||||
|
|
||||||
|
// Check if an output stream is already opened and, in case, handle priority.
|
||||||
|
if(running)
|
||||||
|
{
|
||||||
|
if((int) prio < priority) return -1; // Lower priority, reject.
|
||||||
|
if((int) prio > priority) outputStream_stop(0); // Higher priority, takes over.
|
||||||
|
outputStream_sync(0, false); // Same priority, wait.
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assign priority and set stream as running
|
||||||
|
priority = prio;
|
||||||
|
running = true;
|
||||||
|
|
||||||
|
if (!s)
|
||||||
|
{
|
||||||
|
if (!(s = pa_simple_new(NULL,
|
||||||
|
"OpenRTX",
|
||||||
|
PA_STREAM_PLAYBACK,
|
||||||
|
NULL,
|
||||||
|
"playback",
|
||||||
|
&ss,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
&error)))
|
||||||
|
{
|
||||||
|
fprintf(stderr, __FILE__": pa_simple_new() failed: %s\n", pa_strerror(error));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pa_simple_write(s, buf, length, &error) < 0) {
|
||||||
|
fprintf(stderr, __FILE__": pa_simple_write() failed: %s\n", pa_strerror(error));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
stream_sample_t *outputStream_getIdleBuffer(const streamId id)
|
stream_sample_t *outputStream_getIdleBuffer(const streamId id)
|
||||||
|
|
@ -50,15 +98,33 @@ bool outputStream_sync(const streamId id, const bool bufChanged)
|
||||||
(void) id;
|
(void) id;
|
||||||
(void) bufChanged;
|
(void) bufChanged;
|
||||||
|
|
||||||
return false;
|
/* Make sure that every single sample was played */
|
||||||
|
if (pa_simple_drain(s, &error) < 0) {
|
||||||
|
fprintf(stderr, __FILE__": pa_simple_drain() failed: %s\n", pa_strerror(error));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
running = false;
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void outputStream_stop(const streamId id)
|
void outputStream_stop(const streamId id)
|
||||||
{
|
{
|
||||||
(void) id;
|
(void) id;
|
||||||
|
|
||||||
|
/* Make sure that every single sample was played */
|
||||||
|
if (pa_simple_flush(s, &error) < 0) {
|
||||||
|
fprintf(stderr, __FILE__": pa_simple_drain() failed: %s\n", pa_strerror(error));
|
||||||
|
}
|
||||||
|
|
||||||
|
running = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void outputStream_terminate(const streamId id)
|
void outputStream_terminate(const streamId id)
|
||||||
{
|
{
|
||||||
(void) id;
|
(void) id;
|
||||||
|
|
||||||
|
if (s)
|
||||||
|
pa_simple_free(s);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,66 @@
|
||||||
|
/***************************************************************************
|
||||||
|
* Copyright (C) 2021 by Federico Amedeo Izzo IU2NUO, *
|
||||||
|
* Niccolò Izzo IU2KIN *
|
||||||
|
* Frederik Saraci IU2NRO *
|
||||||
|
* Silvano Seva IU2KWO *
|
||||||
|
* *
|
||||||
|
* *
|
||||||
|
* 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 3 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, see <http://www.gnu.org/licenses/> *
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
// Test private methods
|
||||||
|
#define private public
|
||||||
|
|
||||||
|
#include <interfaces/audio_stream.h>
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
#define BUF_LEN 4096 * 10
|
||||||
|
#define AMPLITUDE 20000
|
||||||
|
#define PI 3.14159
|
||||||
|
#define SAMPLE_RATE 48000
|
||||||
|
#define FREQUENCY 440
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test sine playback
|
||||||
|
*/
|
||||||
|
|
||||||
|
int16_t buffer[BUF_LEN] = { 0 };
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
int id = 0;
|
||||||
|
|
||||||
|
// Create 440 Hz sine wave
|
||||||
|
for(int i = 0; i < BUF_LEN; i++)
|
||||||
|
{
|
||||||
|
buffer[i] = AMPLITUDE * sin(2 * PI * i *
|
||||||
|
((float) FREQUENCY / SAMPLE_RATE));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Play back sine wave
|
||||||
|
for(int i = 0; i < 10; i++)
|
||||||
|
{
|
||||||
|
id = outputStream_start(SINK_SPK,
|
||||||
|
PRIO_PROMPT,
|
||||||
|
buffer,
|
||||||
|
BUF_LEN,
|
||||||
|
BUF_LINEAR,
|
||||||
|
SAMPLE_RATE);
|
||||||
|
};
|
||||||
|
outputStream_sync(id, false);
|
||||||
|
|
||||||
|
// Sync, flush, terminate
|
||||||
|
outputStream_stop(id);
|
||||||
|
outputStream_terminate(id);
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue