Commit e3022741 authored by Philippe Gorley's avatar Philippe Gorley Committed by Adrien Béraud

audio: remove libsamplerate dependency

Rewrites the Resampler class to use MediaFilter.
Adds a unit test for the new Resampler.

Change-Id: I8ed78d2e1d82df81bbaf46a9719c682c825ee245
parent 834e835d
......@@ -351,10 +351,6 @@ AM_CONDITIONAL(HAVE_PORTAUDIO, test "x$with_portaudio" = "xyes")
AC_DEFINE_UNQUOTED([HAVE_JACK], `if test "x$have_jack" = "xyes"; then echo 1; else echo 0; fi`, [Define if you have jack])
AM_CONDITIONAL(BUILD_JACK, test "x$have_jack" = "xyes")
dnl Check for the samplerate development package - name: libsamplerate0-dev
LIBSAMPLERATE_MIN_VERSION=0.1.2
PKG_CHECK_MODULES(SAMPLERATE, samplerate >= ${LIBSAMPLERATE_MIN_VERSION},, AC_MSG_ERROR([Missing libsamplerate development files]))
dnl Coverage is default-disabled
AC_ARG_ENABLE([coverage], AS_HELP_STRING([--enable-coverage], [Enable coverage]))
......
85d93df24d9d62e7803a5d0ac5d268b2085214adcb160e32fac316b12ee8a0ce36ccfb433a3c0a08f6e3ec418a5962bdb84f8a11262286a9b347436983029a7d libsamplerate-0.1.8.tar.gz
--- a/examples/audio_out.c.orig 2014-06-26 21:09:44.000000000 -0400
+++ b/examples/audio_out.c 2014-06-26 21:09:58.000000000 -0400
@@ -172,7 +172,6 @@
#if (defined (__MACH__) && defined (__APPLE__)) /* MacOSX */
-#include <Carbon.h>
#include <CoreAudio/AudioHardware.h>
#define MACOSX_MAGIC MAKE_MAGIC ('M', 'a', 'c', ' ', 'O', 'S', ' ', 'X')
--- a/src/float_cast.h 2011-01-19 05:39:36.000000000 -0500
+++ b/src/float_cast.h 2016-05-30 17:09:20.000000000 -0400
@@ -230,38 +230,12 @@
#undef lrint
#undef lrintf
- #define lrint double2int
- #define lrintf float2int
+ #warning "Don't have the functions lrint() and lrintf()."
+ #warning "Replacing these functions with a standard C cast."
- inline static long
- float2int (register float in)
- { int res [2] ;
-
- __asm__ __volatile__
- ( "fctiw %1, %1\n\t"
- "stfd %1, %0"
- : "=m" (res) /* Output */
- : "f" (in) /* Input */
- : "memory"
- ) ;
-
- return res [1] ;
- } /* lrintf */
-
- inline static long
- double2int (register double in)
- { int res [2] ;
-
- __asm__ __volatile__
- ( "fctiw %1, %1\n\t"
- "stfd %1, %0"
- : "=m" (res) /* Output */
- : "f" (in) /* Input */
- : "memory"
- ) ;
+ #define lrint(dbl) ((long) (dbl))
+ #define lrintf(flt) ((long) (flt))
- return res [1] ;
- } /* lrint */
#else
#ifndef __sgi
@@ -278,4 +252,3 @@
#endif /* FLOAT_CAST_HEADER */
-
From 4188b5b9e553911f562e2ae147c8d2ae17bc2500 Mon Sep 17 00:00:00 2001
From: Adrien Beraud <adrien.beraud@savoirfairelinux.com>
Date: Wed, 2 Aug 2017 16:42:36 -0400
Subject: [PATCH] don't build example, doc, tests
---
Makefile.am | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Makefile.am b/Makefile.am
index 1295c92..47bd97d 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -2,7 +2,7 @@
DISTCHECK_CONFIGURE_FLAGS = --enable-gcc-werror
-SUBDIRS = M4 src doc examples tests
+SUBDIRS = M4 src
DIST_SUBDIRS = Win32 $(SUBDIRS)
EXTRA_DIST = autogen.sh libsamplerate.spec.in samplerate.pc.in Make.bat
--
2.11.0
set BUILD=%SRC%..\build
set SAMPLERATE_VERSION=0.1.8
set SAMPLERATE_URL="http://www.mega-nerd.com/SRC/libsamplerate-%SAMPLERATE_VERSION%.tar.gz"
mkdir %BUILD%
if %USE_CACHE%==1 (
copy %CACHE_DIR%\libsamplerate-%SAMPLERATE_VERSION%.tar.gz %cd%
) else (
%WGET_CMD% %SAMPLERATE_URL%
)
7z -y e libsamplerate-%SAMPLERATE_VERSION%.tar.gz && 7z -y x libsamplerate-%SAMPLERATE_VERSION%.tar -o%BUILD%
del libsamplerate-%SAMPLERATE_VERSION%.tar && del libsamplerate-%SAMPLERATE_VERSION%.tar.gz
rename %BUILD%\libsamplerate-%SAMPLERATE_VERSION% libsamplerate
cd %BUILD%\libsamplerate
%APPLY_CMD% %SRC%\samplerate\samplerate-vs2017.patch
cd %SRC%
\ No newline at end of file
# SAMPLERATE
SAMPLERATE_VERSION := 0.1.8
SAMPLERATE_URL := http://www.mega-nerd.com/SRC/libsamplerate-$(SAMPLERATE_VERSION).tar.gz
PKGS += samplerate
ifeq ($(call need_pkg,"samplerate"),)
PKGS_FOUND += samplerate
endif
$(TARBALLS)/libsamplerate-$(SAMPLERATE_VERSION).tar.gz:
$(call download,$(SAMPLERATE_URL))
.sum-samplerate: libsamplerate-$(SAMPLERATE_VERSION).tar.gz
samplerate: libsamplerate-$(SAMPLERATE_VERSION).tar.gz .sum-samplerate
$(UNPACK)
$(APPLY) $(SRC)/samplerate/disable_tests.patch
$(APPLY) $(SRC)/samplerate/soundcard.patch
$(APPLY) $(SRC)/samplerate/carbon.patch
ifdef HAVE_IOS
ifeq ($(IOS_TARGET_PLATFORM),iPhoneSimulator)
#warning assembler double / int conversion disabled
$(APPLY) $(SRC)/samplerate/disable_assembler.patch
endif
endif
$(UPDATE_AUTOCONFIG) && cd $(UNPACK_DIR) && mv config.guess config.sub Cfg
$(MOVE)
.samplerate: samplerate
cd $< && $(HOSTVARS) ./configure $(HOSTCONF)
cd $< && $(MAKE) install
touch $@
This diff is collapsed.
--- a/examples/audio_out.c.orig 2014-06-18 16:52:04.269479958 -0400
+++ b/examples/audio_out.c 2014-06-18 16:52:36.789478998 -0400
@@ -44,7 +44,11 @@
#include <fcntl.h>
#include <sys/ioctl.h>
+#if defined (__ANDROID__)
+#include <linux/soundcard.h>
+#else
#include <sys/soundcard.h>
+#endif
#define LINUX_MAGIC MAKE_MAGIC ('L', 'i', 'n', 'u', 'x', 'O', 'S', 'S')
......@@ -26,7 +26,6 @@ RUN apt-get update && \
autotools-dev \
gettext \
libpulse-dev \
libsamplerate0-dev \
libasound2-dev \
libexpat1-dev \
libpcre3-dev \
......
......@@ -49,7 +49,6 @@ libring_la_LDFLAGS = \
@PJPROJECT_LIBS@ \
@ALSA_LIBS@ \
@PULSEAUDIO_LIBS@ \
@SAMPLERATE_LIBS@ \
@YAMLCPP_LIBS@ \
@JSONCPP_LIBS@ \
@SPEEXDSP_LIBS@ \
......@@ -77,7 +76,6 @@ libring_la_CFLAGS = \
@PJPROJECT_CFLAGS@ \
@ALSA_CFLAGS@ \
@PULSEAUDIO_CFLAGS@ \
@SAMPLERATE_CFLAGS@ \
@LIBUPNP_CFLAGS@ \
@SPEEXDSP_CFLAGS@ \
@PORTAUDIO_CFLAGS@ \
......
......@@ -19,109 +19,82 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "resampler.h"
#include "logger.h"
#include "media_filter.h"
#include "media_stream.h"
#include "resampler.h"
#include "ring_types.h"
#include <samplerate.h>
namespace ring {
class SrcState {
public:
SrcState(int nb_channels, bool high_quality = false)
{
int err;
state_ = src_new(high_quality ? SRC_SINC_BEST_QUALITY : SRC_LINEAR, nb_channels, &err);
}
~SrcState()
{
src_delete(state_);
}
void process(SRC_DATA *src_data)
{
src_process(state_, src_data);
}
private:
SRC_STATE *state_ {nullptr};
};
Resampler::Resampler(AudioFormat format, bool quality) : floatBufferIn_(),
floatBufferOut_(), scratchBuffer_(), samples_(0), format_(format), high_quality_(quality), src_state_()
Resampler::Resampler(AudioFormat format)
: format_(format)
{
setFormat(format, quality);
setFormat(format);
}
Resampler::Resampler(unsigned sample_rate, unsigned channels, bool quality) : floatBufferIn_(),
floatBufferOut_(), scratchBuffer_(), samples_(0), format_(sample_rate, channels), high_quality_(quality), src_state_()
Resampler::Resampler(unsigned sample_rate, unsigned channels)
: format_(sample_rate, channels)
{
setFormat(format_, quality);
setFormat(format_);
}
Resampler::~Resampler() = default;
void
Resampler::setFormat(AudioFormat format, bool quality)
Resampler::reinitFilter(const MediaStream& inputParams)
{
format_ = format;
samples_ = (format.nb_channels * format.sample_rate * 20) / 1000; // start with 20 ms buffers
floatBufferIn_.resize(samples_);
floatBufferOut_.resize(samples_);
scratchBuffer_.resize(samples_);
filter_.reset(new MediaFilter());
std::stringstream aformat;
aformat << "aformat=sample_fmts=s16:channel_layouts="
<< av_get_default_channel_layout(format_.nb_channels)
<< ":sample_rates=" << format_.sample_rate;
if (filter_->initialize(aformat.str(), inputParams) < 0) {
RING_ERR() << "Failed to initialize resampler";
filter_.reset();
}
}
src_state_.reset(new SrcState(format.nb_channels, quality));
void
Resampler::setFormat(AudioFormat format)
{
format_ = format;
if (filter_)
reinitFilter(filter_->getInputParams());
}
void Resampler::resample(const AudioBuffer &dataIn, AudioBuffer &dataOut)
void
Resampler::resample(const AudioBuffer& dataIn, AudioBuffer& dataOut)
{
const double inputFreq = dataIn.getSampleRate();
const double outputFreq = dataOut.getSampleRate();
const double sampleFactor = outputFreq / inputFreq;
auto input = dataIn.toAVFrame();
MediaStream currentParams("resampler", static_cast<AVSampleFormat>(input->format),
0, input->sample_rate, input->channels);
if (filter_) {
const auto& ms = filter_->getInputParams();
if (ms.sampleRate != input->sample_rate || ms.nbChannels != input->channels) {
RING_WARN() << "Resampler settings changed, reinitializing";
reinitFilter(currentParams);
}
} else {
reinitFilter(currentParams);
}
if (sampleFactor == 1.0)
auto frame = filter_->apply(input);
av_frame_free(&input);
if (!frame) {
RING_ERR() << "Resampling failed, this may produce a glitch in the audio";
return;
const size_t nbFrames = dataIn.frames();
const size_t nbChans = dataIn.channels();
if (nbChans != format_.nb_channels) {
// change channel num if needed
src_state_.reset(new SrcState(nbChans, high_quality_));
format_.nb_channels = nbChans;
RING_DBG("SRC channel number changed.");
}
if (nbChans != dataOut.channels()) {
RING_DBG("Output buffer had the wrong number of channels (in: %zu, out: %u).", nbChans, dataOut.channels());
dataOut.setChannelNum(nbChans);
}
size_t inSamples = nbChans * nbFrames;
size_t outSamples = inSamples * sampleFactor;
// grow buffer if needed
floatBufferIn_.resize(inSamples);
floatBufferOut_.resize(outSamples);
scratchBuffer_.resize(outSamples);
SRC_DATA src_data;
src_data.data_in = floatBufferIn_.data();
src_data.data_out = floatBufferOut_.data();
src_data.input_frames = nbFrames;
src_data.output_frames = nbFrames * sampleFactor;
src_data.src_ratio = sampleFactor;
src_data.end_of_input = 0; // More data will come
dataIn.interleaveFloat(floatBufferIn_.data());
src_state_->process(&src_data);
/*
TODO: one-shot deinterleave and float-to-short conversion
*/
src_float_to_short_array(floatBufferOut_.data(), scratchBuffer_.data(), outSamples);
dataOut.deinterleave(scratchBuffer_.data(), src_data.output_frames, nbChans);
dataOut.setFormat(format_);
dataOut.resize(frame->nb_samples);
if (static_cast<AVSampleFormat>(frame->format) == AV_SAMPLE_FMT_FLTP)
dataOut.convertFloatPlanarToSigned16(frame->extended_data,
frame->nb_samples, frame->channels);
else if (static_cast<AVSampleFormat>(frame->format) == AV_SAMPLE_FMT_S16)
dataOut.deinterleave(reinterpret_cast<const AudioSample*>(frame->extended_data[0]),
frame->nb_samples, frame->channels);
av_frame_free(&frame);
}
} // namespace ring
......@@ -19,20 +19,18 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef _SAMPLE_RATE_H
#define _SAMPLE_RATE_H
#pragma once
#include <cmath>
#include <cstring>
#include <memory>
#include "audiobuffer.h"
#include "ring_types.h"
#include "noncopyable.h"
#include "ring_types.h"
namespace ring {
class SrcState;
class MediaFilter;
struct MediaStream;
class Resampler {
public:
......@@ -43,8 +41,8 @@ class Resampler {
* internal buffer size. Resampler must be reinitialized
* every time these parameters change
*/
Resampler(AudioFormat outFormat, bool quality = false);
Resampler(unsigned sample_rate, unsigned channels=1, bool quality = false);
Resampler(AudioFormat outFormat);
Resampler(unsigned sample_rate, unsigned channels=1);
// empty dtor, needed for unique_ptr
~Resampler();
......@@ -52,31 +50,22 @@ class Resampler {
* Change the converter sample rate and channel number.
* Internal state is lost.
*/
void setFormat(AudioFormat format, bool quality = false);
void setFormat(AudioFormat format);
/**
* resample from the samplerate1 to the samplerate2
* @param dataIn Input buffer
* @param dataIn Input buffer
* @param dataOut Output buffer
* @param nbSamples The number of samples to process
*/
void resample(const AudioBuffer& dataIn, AudioBuffer& dataOut);
private:
NON_COPYABLE(Resampler);
/* temporary buffers */
std::vector<float> floatBufferIn_;
std::vector<float> floatBufferOut_;
std::vector<AudioSample> scratchBuffer_;
void reinitFilter(const MediaStream& inputParams);
size_t samples_; // size in samples of temporary buffers
AudioFormat format_; // number of channels and max output frequency
bool high_quality_;
std::unique_ptr<SrcState> src_state_;
std::unique_ptr<MediaFilter> filter_;
};
} // namespace ring
#endif //_SAMPLE_RATE_H
......@@ -22,7 +22,6 @@
*/
#include <fstream>
#include <cmath>
#include <samplerate.h>
#include <cstring>
#include <vector>
#include <climits>
......
......@@ -70,7 +70,7 @@ ut_media_encoder_SOURCES = media/test_media_encoder.cpp
#
# media_decoder
#
check_PROGRAMS += ut_media_decoder # no reliable way to get a file
check_PROGRAMS += ut_media_decoder
ut_media_decoder_SOURCES = media/test_media_decoder.cpp
#
......@@ -79,4 +79,10 @@ ut_media_decoder_SOURCES = media/test_media_decoder.cpp
check_PROGRAMS += ut_media_filter
ut_media_filter_SOURCES = media/test_media_filter.cpp
#
# resampler
#
check_PROGRAMS += ut_resampler
ut_resampler_SOURCES = media/audio/test_resampler.cpp
TESTS = $(check_PROGRAMS)
/*
* Copyright (C) 2018 Savoir-faire Linux Inc.
*
* Author: Philippe Gorley <philippe.gorley@savoirfairelinux.com>
*
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <cppunit/TestAssert.h>
#include <cppunit/TestFixture.h>
#include <cppunit/extensions/HelperMacros.h>
#include "dring.h"
#include "libav_deps.h"
#include "audio/resampler.h"
#include "../test_runner.h"
namespace ring { namespace test {
class ResamplerTest : public CppUnit::TestFixture {
public:
static std::string name() { return "resampler"; }
void setUp();
void tearDown();
private:
void testResample();
CPPUNIT_TEST_SUITE(ResamplerTest);
CPPUNIT_TEST(testResample);
CPPUNIT_TEST_SUITE_END();
void writeWav(); // writes a minimal wav file to test decoding
std::unique_ptr<Resampler> resampler_;
};
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(ResamplerTest, ResamplerTest::name());
void
ResamplerTest::setUp()
{
DRing::init(DRing::InitFlag(DRing::DRING_FLAG_DEBUG | DRing::DRING_FLAG_CONSOLE_LOG));
libav_utils::ring_avcodec_init();
}
void
ResamplerTest::tearDown()
{
DRing::fini();
}
void
ResamplerTest::testResample()
{
const constexpr AudioFormat none(0, 0);
const constexpr AudioFormat infmt(44100, 1);
const constexpr AudioFormat outfmt(48000, 2);
resampler_.reset(new Resampler(none));
resampler_->setFormat(outfmt);
AudioBuffer inbuf(1024, infmt);
AudioBuffer outbuf;
resampler_->resample(inbuf, outbuf);
CPPUNIT_ASSERT(outbuf.getFormat().sample_rate == 48000);
CPPUNIT_ASSERT(outbuf.getFormat().nb_channels == 2);
}
}} // namespace ring::test
RING_TEST_RUNNER(ring::test::ResamplerTest::name());
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment