Revert soxr adoption, does not meet real time safety guidelines.

ms-pipeline-optim-2
Mooneer Salem 2025-08-02 10:40:06 -07:00
parent 9dd7e10b90
commit eca4e9492e
18 changed files with 188 additions and 143 deletions

View File

@ -248,7 +248,7 @@ jobs:
- name: Install common packages
shell: bash
run: |
sudo apt-get install libpulse-dev libspeexdsp-dev libsoxr-dev sox git portaudio19-dev libhamlib-dev libasound2-dev libao-dev libgsm1-dev libsndfile-dev xvfb pipewire pulseaudio-utils pipewire-pulse wireplumber metacity at-spi2-core
sudo apt-get install libpulse-dev libspeexdsp-dev libsamplerate0-dev sox git portaudio19-dev libhamlib-dev libasound2-dev libao-dev libgsm1-dev libsndfile-dev xvfb pipewire pulseaudio-utils pipewire-pulse wireplumber metacity at-spi2-core
- name: Install version-specific packages
if: ${{ matrix.os == 'ubuntu-22.04' }}

View File

@ -38,7 +38,7 @@ jobs:
- uses: gerlero/brew-install@v1
with:
packages: automake libtool numpy sox wxwidgets speexdsp portaudio libsndfile libsoxr hamlib
packages: automake libtool numpy sox wxwidgets speexdsp portaudio libsndfile libsamplerate hamlib
- name: Install VB-Cable
shell: bash

View File

@ -130,8 +130,8 @@ set(USE_STATIC_PORTAUDIO FALSE CACHE BOOL
"Download and build static portaudio instead of the system library.")
set(USE_STATIC_SNDFILE FALSE CACHE BOOL
"Download and build static sndfile instead of the system library.")
set(USE_STATIC_SOXR FALSE CACHE BOOL
"Download and build static soxr instead of the system library.")
set(USE_STATIC_SAMPLERATE FALSE CACHE BOOL
"Download and build static samplerate instead of the system library.")
set(USE_STATIC_SPEEXDSP FALSE CACHE BOOL
"Download and build static speex instead of the system library.")
set(BOOTSTRAP_WXWIDGETS FALSE CACHE BOOL
@ -204,7 +204,7 @@ endif(SIGN_WINDOWS_BINARIES)
if(USE_STATIC_DEPS)
set(USE_STATIC_PORTAUDIO TRUE FORCE)
set(USE_STATIC_SNDFILE TRUE FORCE)
set(USE_STATIC_SOXR TRUE FORCE)
set(USE_STATIC_SAMPLERATE TRUE FORCE)
set(USE_STATIC_SPEEXDSP TRUE FORCE)
endif(USE_STATIC_DEPS)
@ -466,26 +466,25 @@ else(HAMLIB_LIBRARY AND HAMLIB_INCLUDE_DIR)
include(cmake/BuildHamlib.cmake)
endif(HAMLIB_LIBRARY AND HAMLIB_INCLUDE_DIR)
# Samplerate Library
#
# Soxr Library
#
if(NOT USE_STATIC_SOXR)
message(STATUS "Looking for soxr...")
find_library(LIBSOXR soxr)
find_path(LIBSOXR_INCLUDE_DIR soxr.h)
message(STATUS " soxr library: ${LIBSOXR}")
message(STATUS " soxr headers: ${LIBSOXR_INCLUDE_DIR}")
if(LIBSOXR AND LIBSOXR_INCLUDE_DIR)
list(APPEND FREEDV_LINK_LIBS ${LIBSOXR})
include_directories(${LIBSOXR_INCLUDE_DIR})
else(LIBSOXR AND LIBSOXR_INCLUDE_DIR)
message(STATUS "Will attempt static build of soxr.")
include(cmake/BuildSoxr.cmake)
endif(LIBSOXR AND LIBSOXR_INCLUDE_DIR)
else(NOT USE_STATIC_SOXR)
message(STATUS "Will attempt static build of soxr.")
include(cmake/BuildSoxr.cmake)
endif(NOT USE_STATIC_SOXR)
if(NOT USE_STATIC_SAMPLERATE)
message(STATUS "Looking for samplerate...")
find_library(LIBSAMPLERATE samplerate)
find_path(LIBSAMPLERATE_INCLUDE_DIR samplerate.h)
message(STATUS " samplerate library: ${LIBSAMPLERATE}")
message(STATUS " samplerate headers: ${LIBSAMPLERATE_INCLUDE_DIR}")
if(LIBSAMPLERATE AND LIBSAMPLERATE_INCLUDE_DIR)
list(APPEND FREEDV_LINK_LIBS ${LIBSAMPLERATE})
include_directories(${LIBSAMPLERATE_INCLUDE_DIR})
else(LIBSTAMPLERATE AND LIBSAMPLERATE_INCLUDE_DIR)
message(STATUS "Will attempt static build of samplerate.")
include(cmake/BuildSamplerate.cmake)
endif(LIBSAMPLERATE AND LIBSAMPLERATE_INCLUDE_DIR)
else(NOT USE_STATIC_SAMPLERATE)
message(STATUS "Will attempt static build of samplerate.")
include(cmake/BuildSamplerate.cmake)
endif(NOT USE_STATIC_SAMPLERATE)
#
# sndfile Library

View File

@ -10,7 +10,7 @@ This document describes how to build the FreeDV GUI program for various operatin
## Installing prerequisites on Ubuntu Linux
```
$ sudo apt install libspeexdsp-dev libsoxr-dev sox git \
$ sudo apt install libspeexdsp-dev libsamplerate0-dev sox git \
libwxgtk3.2-dev libhamlib-dev libasound2-dev libao-dev \
libgsm1-dev libsndfile1-dev cmake module-assistant build-essential
$ git clone https://github.com/drowe67/freedv-gui.git
@ -29,7 +29,7 @@ This document describes how to build the FreeDV GUI program for various operatin
```
$ sudo dnf groupinstall "Development Tools"
$ sudo dnf install cmake wxGTK3-devel soxr-devel \
$ sudo dnf install cmake wxGTK3-devel libsamplerate-devel \
libsndfile-devel speexdsp-devel hamlib-devel alsa-lib-devel libao-devel \
gsm-devel gcc-c++ sox
$ git clone https://github.com/drowe67/freedv-gui.git

View File

@ -0,0 +1,25 @@
set(SAMPLERATE_VERSION "0.2.2")
set(BUILD_TESTING OFF CACHE BOOL "Enable unit tests for libsamplerate")
include(FetchContent)
FetchContent_Declare(
samplerate
GIT_REPOSITORY https://github.com/libsndfile/libsamplerate.git
GIT_SHALLOW TRUE
GIT_PROGRESS TRUE
GIT_TAG master
)
FetchContent_GetProperties(samplerate)
if(NOT samplerate_POPULATED)
FetchContent_Populate(samplerate)
add_subdirectory(${samplerate_SOURCE_DIR} ${samplerate_BINARY_DIR} EXCLUDE_FROM_ALL)
list(APPEND FREEDV_PACKAGE_SEARCH_PATHS ${samplerate_BINARY_DIR}/src)
target_compile_options(samplerate PRIVATE -g -O3) # Ensure that samplerate is built with optimizations
endif()
list(APPEND FREEDV_LINK_LIBS samplerate)
target_include_directories(samplerate BEFORE PRIVATE ${samplerate_BINARY_DIR})
include_directories(${samplerate_SOURCE_DIR}/include)

View File

@ -1,25 +0,0 @@
set(SOXR_VERSION "0.1.3")
set(BUILD_TESTS OFF CACHE BOOL "Enable unit tests for libsoxr")
include(FetchContent)
FetchContent_Declare(
soxr
GIT_REPOSITORY https://github.com/chirlu/soxr.git
GIT_SHALLOW TRUE
GIT_PROGRESS TRUE
GIT_TAG ${SOXR_VERSION}
)
FetchContent_GetProperties(soxr)
if(NOT soxr_POPULATED)
FetchContent_Populate(soxr)
list(APPEND CMAKE_MODULE_PATH "${soxr_SOURCE_DIR}/cmake/Modules")
add_subdirectory(${soxr_SOURCE_DIR} ${soxr_BINARY_DIR} EXCLUDE_FROM_ALL)
list(APPEND FREEDV_PACKAGE_SEARCH_PATHS ${soxr_BINARY_DIR}/bin)
endif()
list(APPEND FREEDV_LINK_LIBS soxr)
target_include_directories(soxr BEFORE PRIVATE ${soxr_BINARY_DIR})
include_directories(${soxr_SOURCE_DIR}/src)

View File

@ -82,7 +82,7 @@ function(CheckGitSetup)
BYPRODUCTS ${post_configure_file}
)
add_library(git_version STATIC ${CMAKE_BINARY_DIR}/generated/git_version.cpp)
add_library(git_version ${CMAKE_BINARY_DIR}/generated/git_version.cpp)
target_include_directories(git_version PUBLIC ${CMAKE_BINARY_DIR}/generated)
add_dependencies(git_version AlwaysCheckGit)

View File

@ -91,7 +91,6 @@ set( _windlls
AVRT.dll
gdiplus.dll
MSIMG32.dll
PSAPI.DLL
# The below are additional DLLs required when compiled
# using the LLVM version of MinGW.

View File

@ -138,7 +138,7 @@ if(APPLE)
TARGET FreeDV
POST_BUILD
COMMAND rm -rf dist_tmp FreeDV.dmg || true
COMMAND DYLD_LIBRARY_PATH=${CODEC2_BUILD_DIR}/src:${portaudio_BINARY_DIR}:${soxr_BINARY_DIR}/src:${Python3_ROOT_DIR}:${DYLD_LIBRARY_PATH} ${CMAKE_SOURCE_DIR}/macdylibbundler/dylibbundler ARGS -od -b -x FreeDV.app/Contents/MacOS/FreeDV -d FreeDV.app/Contents/libs -p @loader_path/../libs/ -i /usr/lib -s ${CODEC2_BUILD_DIR}/src -s ${CMAKE_BINARY_DIR}/codec2_build/src ${PORTAUDIO_BUNDLE_ARG} -s ${soxr_BINARY_DIR}/src -s ${rade_BINARY_DIR}/src -s ${Python3_ROOT_DIR}
COMMAND DYLD_LIBRARY_PATH=${CODEC2_BUILD_DIR}/src:${portaudio_BINARY_DIR}:${samplerate_BINARY_DIR}/src:${Python3_ROOT_DIR}:${DYLD_LIBRARY_PATH} ${CMAKE_SOURCE_DIR}/macdylibbundler/dylibbundler ARGS -od -b -x FreeDV.app/Contents/MacOS/FreeDV -d FreeDV.app/Contents/libs -p @loader_path/../libs/ -i /usr/lib -s ${CODEC2_BUILD_DIR}/src -s ${CMAKE_BINARY_DIR}/codec2_build/src ${PORTAUDIO_BUNDLE_ARG} -s ${samplerate_BINARY_DIR}/src -s ${rade_BINARY_DIR}/src -s ${Python3_ROOT_DIR}
COMMAND cp ARGS ${CMAKE_CURRENT_SOURCE_DIR}/freedv.icns FreeDV.app/Contents/Resources
COMMAND rm ARGS -rf FreeDV.app/Contents/Frameworks
COMMAND mkdir ARGS FreeDV.app/Contents/Frameworks

View File

@ -54,6 +54,8 @@ extern "C"
#include "util/IRealtimeHelper.h"
#include <samplerate.h>
class IPipelineStep;
class ParallelStep;
class RADETransmitStep;

View File

@ -25,8 +25,6 @@
#include <wx/app.h>
#include <wx/confbase.h>
#include <soxr.h>
#include "dlg_audiooptions.h"
#include "audio/AudioEngineFactory.h"
@ -841,8 +839,9 @@ void AudioOptsDialog::plotDeviceInputForAFewSecs(wxString devName, PlotScalar *p
m_audioPlotThread = new std::thread([&](wxString devName, PlotScalar* ps) {
std::mutex callbackFifoMutex;
std::condition_variable callbackFifoCV;
SRC_STATE *src;
FIFO *fifo, *callbackFifo;
soxr_t src = nullptr;
int src_error;
// Reset plot before starting.
CallAfter([&]() {
@ -851,6 +850,7 @@ void AudioOptsDialog::plotDeviceInputForAFewSecs(wxString devName, PlotScalar *p
});
fifo = codec2_fifo_create((int)(DT*TEST_WAVEFORM_PLOT_FS*2)); assert(fifo != NULL);
src = src_new(SRC_SINC_FASTEST, 1, &src_error); assert(src != NULL);
auto engine = AudioEngineFactory::GetAudioEngine();
auto devList = engine->getAudioDeviceList(IAudioEngine::AUDIO_ENGINE_IN);
@ -860,19 +860,6 @@ void AudioOptsDialog::plotDeviceInputForAFewSecs(wxString devName, PlotScalar *p
{
int sampleCount = 0;
int sampleRate = wxAtoi(m_cbSampleRateRxIn->GetValue());
soxr_io_spec_t ioSpec = soxr_io_spec(SOXR_INT16_I, SOXR_INT16_I);
src = soxr_create(
sampleRate,
8000,
1,
nullptr,
&ioSpec,
nullptr, // soxr_quality_spec(SOXR_HQ, 0)
nullptr // soxr_runtime_spec(1)
);
assert(src != NULL);
auto device = engine->getAudioDevice(
devInfo.name,
IAudioEngine::AUDIO_ENGINE_IN,
@ -914,12 +901,7 @@ void AudioOptsDialog::plotDeviceInputForAFewSecs(wxString devName, PlotScalar *p
}
}
size_t inputUsed = 0;
size_t n8k = 0;
soxr_process(
src, in48k_short, TEST_BUF_SIZE, &inputUsed,
in8k_short, TEST_BUF_SIZE, &n8k);
int n8k = resample(src, in8k_short, in48k_short, 8000, sampleRate, TEST_BUF_SIZE, TEST_BUF_SIZE);
short* tmp = new short[n8k];
assert(tmp != nullptr);
resample_for_plot(fifo, in8k_short, tmp, n8k, FS);
@ -957,10 +939,7 @@ void AudioOptsDialog::plotDeviceInputForAFewSecs(wxString devName, PlotScalar *p
}
codec2_fifo_destroy(fifo);
if (src != nullptr)
{
soxr_delete(src);
}
src_delete(src);
CallAfter([&]() {
m_audioPlotThread->join();
@ -990,9 +969,9 @@ void AudioOptsDialog::plotDeviceOutputForAFewSecs(wxString devName, PlotScalar *
m_btnTxOutTest->Enable(false);
m_audioPlotThread = new std::thread([&](wxString devName, PlotScalar* ps) {
SRC_STATE *src;
FIFO *fifo, *callbackFifo;
int n = 0;
soxr_t src = nullptr;
int src_error, n = 0;
// Reset plot before starting.
CallAfter([&]() {
@ -1001,6 +980,7 @@ void AudioOptsDialog::plotDeviceOutputForAFewSecs(wxString devName, PlotScalar *
});
fifo = codec2_fifo_create((int)(DT*TEST_WAVEFORM_PLOT_FS*2)); assert(fifo != NULL);
src = src_new(SRC_SINC_FASTEST, 1, &src_error); assert(src != NULL);
auto engine = AudioEngineFactory::GetAudioEngine();
auto devList = engine->getAudioDeviceList(IAudioEngine::AUDIO_ENGINE_OUT);
@ -1010,19 +990,6 @@ void AudioOptsDialog::plotDeviceOutputForAFewSecs(wxString devName, PlotScalar *
{
int sampleCount = 0;
int sampleRate = wxAtoi(m_cbSampleRateRxIn->GetValue());
soxr_io_spec_t ioSpec = soxr_io_spec(SOXR_INT16_I, SOXR_INT16_I);
src = soxr_create(
sampleRate,
8000,
1,
nullptr,
&ioSpec,
nullptr, // soxr_quality_spec(SOXR_HQ, 0)
nullptr // soxr_runtime_spec(1)
);
assert(src != NULL);
auto device = engine->getAudioDevice(
devInfo.name,
IAudioEngine::AUDIO_ENGINE_OUT,
@ -1072,12 +1039,7 @@ void AudioOptsDialog::plotDeviceOutputForAFewSecs(wxString devName, PlotScalar *
}
}
size_t inputUsed = 0;
size_t n8k = 0;
soxr_process(
src, out48k_short, TEST_BUF_SIZE, &inputUsed,
out8k_short, TEST_BUF_SIZE, &n8k);
int n8k = resample(src, out8k_short, out48k_short, 8000, sampleRate, TEST_BUF_SIZE, TEST_BUF_SIZE);
short* tmp = new short[n8k];
assert(tmp != nullptr);
resample_for_plot(fifo, out8k_short, tmp, n8k, FS);
@ -1115,10 +1077,7 @@ void AudioOptsDialog::plotDeviceOutputForAFewSecs(wxString devName, PlotScalar *
}
codec2_fifo_destroy(fifo);
if (src != nullptr)
{
soxr_delete(src);
}
src_delete(src);
CallAfter([&]() {
m_audioPlotThread->join();

View File

@ -107,6 +107,7 @@ std::atomic<int> g_tx;
float g_snr;
std::atomic<bool> g_half_duplex;
std::atomic<bool> g_voice_keyer_tx;
SRC_STATE *g_spec_src; // sample rate converter for spectrum
// sending and receiving Call Sign data
struct FIFO *g_txDataInFifo;
@ -571,14 +572,6 @@ bool MainApp::OnInit()
#endif // _WIN32 || __APPLE__
#if __aarch64__
// For 64-bit ARM, it's safe to assume some form of SIMD is available,
// so make sure we enable it for soxr. (soxr will auto-enable on x86
// as appropriate, so no need to explicitly force on that platform).
wxSetEnv("SOXR_USE_SIMD32", "1");
wxSetEnv("SOXR_USE_SIMD64", "1");
#endif // __aarch64__
#if defined(UNOFFICIAL_RELEASE)
// Terminate the application if the current date > expiration date
wxDateTime buildDate;

View File

@ -47,6 +47,8 @@
#include <wx/regex.h>
#include <wx/socket.h>
#include <samplerate.h>
#include <stdint.h>
#include <speex/speex_preprocess.h>
#if defined(__x86_64__) || defined(_M_X64) || defined(__i386) || defined(_M_IX86)
@ -518,6 +520,14 @@ class MainFrame : public TopFrame
void resample_for_plot(struct FIFO *plotFifo, short buf[], short* dec_samples, int length, int fs);
int resample(SRC_STATE *src,
short output_short[],
short input_short[],
int output_sample_rate,
int input_sample_rate,
int length_output_short, // maximum output array length in samples
int length_input_short
);
void txRxProcessing();
// FreeDv API calls this when there is a test frame that needs a-plottin'

View File

@ -28,25 +28,53 @@
#include "../util/logging/ulog.h"
// returns number of output samples generated by resampling
// TBD -- remove identical version from util.cpp.
static int resample_step(SRC_STATE *src,
short output_short[],
short input_short[],
int output_sample_rate,
int input_sample_rate,
int length_output_short, // maximum output array length in samples
int length_input_short,
float *tmpInput,
float *tmpOutput
)
{
SRC_DATA src_data;
int ret;
assert(src != NULL);
src_short_to_float_array(input_short, tmpInput, length_input_short);
src_data.data_in = tmpInput;
src_data.data_out = tmpOutput;
src_data.input_frames = length_input_short;
src_data.output_frames = length_output_short;
src_data.end_of_input = 0;
src_data.src_ratio = (float)output_sample_rate/input_sample_rate;
ret = src_process(src, &src_data);
if (ret != 0)
{
log_warn("Resampling failed: %s", src_strerror(ret));
}
assert(ret == 0);
assert(src_data.output_frames_gen <= length_output_short);
src_float_to_short_array(tmpOutput, output_short, src_data.output_frames_gen);
return src_data.output_frames_gen;
}
ResampleStep::ResampleStep(int inputSampleRate, int outputSampleRate, bool forPlotsOnly)
: inputSampleRate_(inputSampleRate)
, outputSampleRate_(outputSampleRate)
{
soxr_io_spec_t ioSpec = soxr_io_spec(SOXR_INT16_I, SOXR_INT16_I);
soxr_quality_spec_t qualSpec = soxr_quality_spec(SOXR_HQ, SOXR_HI_PREC_CLOCK | SOXR_DOUBLE_PRECISION);
soxr_runtime_spec_t runtimeSpec = soxr_runtime_spec(1);
qualSpec.passband_end = 0.8; // experimentally determined to reduce latency to acceptable levels (default 0.913)
resampleState_ = soxr_create(
inputSampleRate_,
outputSampleRate_,
1,
nullptr,
&ioSpec,
&qualSpec,
&runtimeSpec
);
int src_error;
resampleState_ = src_new(forPlotsOnly ? SRC_LINEAR : SRC_SINC_MEDIUM_QUALITY, 1, &src_error);
assert(resampleState_ != nullptr);
// Pre-allocate buffers so we don't have to do so during real-time operation.
@ -62,7 +90,7 @@ ResampleStep::ResampleStep(int inputSampleRate, int outputSampleRate, bool forPl
ResampleStep::~ResampleStep()
{
soxr_delete(resampleState_);
src_delete(resampleState_);
delete[] tempInput_;
delete[] tempOutput_;
@ -100,19 +128,17 @@ short* ResampleStep::execute(short* inputSamples, int numInputSamples, int* numO
auto outputPtr = outputSamples_.get();
while (numInputSamples > 0)
{
size_t inputSize = std::min(numInputSamples, inputSampleRate_ * 10 / 1000);
size_t outputSize = ((float)outputSampleRate_ / inputSampleRate_) * inputSize;
int inputSize = std::min(numInputSamples, inputSampleRate_ * 10 / 1000);
int outputSize = ((float)outputSampleRate_ / inputSampleRate_) * inputSize;
size_t inputUsed = 0;
size_t outputUsed = 0;
soxr_process(
resampleState_, inputPtr, inputSize, &inputUsed,
outputPtr, outputSize, &outputUsed);
outputPtr += outputUsed;
inputPtr += inputUsed;
numInputSamples -= inputUsed;
*numOutputSamples += outputUsed;
auto numSamples = resample_step(
resampleState_, outputPtr, inputPtr, outputSampleRate_,
inputSampleRate_, outputSize, inputSize,
tempInput_, tempOutput_);
outputPtr += numSamples;
inputPtr += inputSize;
numInputSamples -= inputSize;
*numOutputSamples += numSamples;
}
return outputSamples_.get();

View File

@ -26,7 +26,7 @@
#include "IPipelineStep.h"
#include <memory>
#include <soxr.h>
#include <samplerate.h>
class ResampleStep : public IPipelineStep
{
@ -41,7 +41,7 @@ public:
private:
int inputSampleRate_;
int outputSampleRate_;
soxr_t resampleState_;
SRC_STATE* resampleState_;
float* tempInput_;
float* tempOutput_;

View File

@ -121,6 +121,15 @@ extern SNDFILE* g_sfPlayFileFromRadio;
extern bool g_recFileFromMic;
extern bool g_recVoiceKeyerFile;
// TBD -- shouldn't be needed once we've fully converted over
extern int resample(SRC_STATE *src,
short output_short[],
short input_short[],
int output_sample_rate,
int input_sample_rate,
int length_output_short, // maximum output array length in samples
int length_input_short
);
#include "sox_biquad.h"
void TxRxThread::initializePipeline_()

View File

@ -1,6 +1,7 @@
#ifndef AUDIO_PIPELINE_PA_CALLBACK_DATA_H
#define AUDIO_PIPELINE_PA_CALLBACK_DATA_H
#include <samplerate.h>
#include "../util/GenericFIFO.h"
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-=

View File

@ -248,6 +248,53 @@ void freq_shift_coh(COMP rx_fdm_fcorr[], COMP rx_fdm[], float foff, float Fs, CO
foff_phase_rect->imag /= mag;
}
// returns number of output samples generated by resampling
int resample(SRC_STATE *src,
short output_short[],
short input_short[],
int output_sample_rate,
int input_sample_rate,
int length_output_short, // maximum output array length in samples
int length_input_short
)
{
SRC_DATA src_data;
float* input = new float[length_input_short];
assert(input != nullptr);
float* output = new float[length_output_short];
assert(output != nullptr);
int ret;
assert(src != NULL);
src_short_to_float_array(input_short, input, length_input_short);
src_data.data_in = input;
src_data.data_out = output;
src_data.input_frames = length_input_short;
src_data.output_frames = length_output_short;
src_data.end_of_input = 0;
src_data.src_ratio = (float)output_sample_rate/input_sample_rate;
ret = src_process(src, &src_data);
if (ret != 0)
{
log_warn("Resampling failed: %s", src_strerror(ret));
}
assert(ret == 0);
assert(src_data.output_frames_gen <= length_output_short);
src_float_to_short_array(output, output_short, src_data.output_frames_gen);
delete[] input;
delete[] output;
return src_data.output_frames_gen;
}
// Decimates samples using an algorithm that produces nice plots of
// speech signals at a low sample rate. We want a low sample rate so
// we don't hammer the graphics system too hard. Saves decimated data