Revert soxr adoption, does not meet real time safety guidelines.
parent
9dd7e10b90
commit
eca4e9492e
|
|
@ -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' }}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
@ -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)
|
||||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -54,6 +54,8 @@ extern "C"
|
|||
|
||||
#include "util/IRealtimeHelper.h"
|
||||
|
||||
#include <samplerate.h>
|
||||
|
||||
class IPipelineStep;
|
||||
class ParallelStep;
|
||||
class RADETransmitStep;
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
10
src/main.h
10
src/main.h
|
|
@ -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'
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
int src_error;
|
||||
|
||||
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
|
||||
);
|
||||
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();
|
||||
|
|
|
|||
|
|
@ -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_;
|
||||
|
|
|
|||
|
|
@ -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_()
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
||||
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-=
|
||||
|
|
|
|||
47
src/util.cpp
47
src/util.cpp
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in New Issue