192 lines
4.8 KiB
C++
192 lines
4.8 KiB
C++
/*
|
|
* Chromaprint -- Audio fingerprinting toolkit
|
|
* Copyright (C) 2010 Lukas Lalinsky <lalinsky@gmail.com>
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library 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
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
|
|
* USA
|
|
*/
|
|
|
|
#include <assert.h>
|
|
#include <algorithm>
|
|
#include <stdio.h>
|
|
extern "C" {
|
|
#include "avresample/avcodec.h"
|
|
}
|
|
#include "debug.h"
|
|
#include "audio_processor.h"
|
|
|
|
using namespace std;
|
|
using namespace Chromaprint;
|
|
|
|
static const int kMinSampleRate = 1000;
|
|
static const int kMaxBufferSize = 1024 * 16;
|
|
|
|
// Resampler configuration
|
|
static const int kResampleFilterLength = 16;
|
|
static const int kResamplePhaseCount = 10;
|
|
static const int kResampleLinear = 0;
|
|
static const double kResampleCutoff = 0.8;
|
|
|
|
AudioProcessor::AudioProcessor(int sample_rate, AudioConsumer *consumer)
|
|
: m_buffer_size(kMaxBufferSize),
|
|
m_target_sample_rate(sample_rate),
|
|
m_consumer(consumer),
|
|
m_resample_ctx(0)
|
|
{
|
|
m_buffer = new short[kMaxBufferSize];
|
|
m_buffer_offset = 0;
|
|
m_resample_buffer = new short[kMaxBufferSize];
|
|
}
|
|
|
|
AudioProcessor::~AudioProcessor()
|
|
{
|
|
if (m_resample_ctx) {
|
|
av_resample_close(m_resample_ctx);
|
|
}
|
|
delete[] m_resample_buffer;
|
|
delete[] m_buffer;
|
|
}
|
|
|
|
void AudioProcessor::LoadMono(short *input, int length)
|
|
{
|
|
short *output = m_buffer + m_buffer_offset;
|
|
while (length--) {
|
|
*output++ = input[0];
|
|
input++;
|
|
}
|
|
}
|
|
|
|
void AudioProcessor::LoadStereo(short *input, int length)
|
|
{
|
|
short *output = m_buffer + m_buffer_offset;
|
|
while (length--) {
|
|
*output++ = (input[0] + input[1]) / 2;
|
|
input += 2;
|
|
}
|
|
}
|
|
|
|
void AudioProcessor::LoadMultiChannel(short *input, int length)
|
|
{
|
|
short *output = m_buffer + m_buffer_offset;
|
|
while (length--) {
|
|
long sum = 0;
|
|
for (int i = 0; i < m_num_channels; i++) {
|
|
sum += *input++;
|
|
}
|
|
*output++ = (short)(sum / m_num_channels);
|
|
}
|
|
}
|
|
|
|
int AudioProcessor::Load(short *input, int length)
|
|
{
|
|
assert(length >= 0);
|
|
assert(m_buffer_offset <= m_buffer_size);
|
|
length = min(length, m_buffer_size - m_buffer_offset);
|
|
switch (m_num_channels) {
|
|
case 1:
|
|
LoadMono(input, length);
|
|
break;
|
|
case 2:
|
|
LoadStereo(input, length);
|
|
break;
|
|
default:
|
|
LoadMultiChannel(input, length);
|
|
break;
|
|
}
|
|
m_buffer_offset += length;
|
|
return length;
|
|
}
|
|
|
|
void AudioProcessor::Resample()
|
|
{
|
|
if (!m_resample_ctx) {
|
|
m_consumer->Consume(m_buffer, m_buffer_offset);
|
|
m_buffer_offset = 0;
|
|
return;
|
|
}
|
|
int consumed = 0;
|
|
int length = av_resample(m_resample_ctx, m_resample_buffer, m_buffer, &consumed, m_buffer_offset, kMaxBufferSize, 1);
|
|
if (length > kMaxBufferSize) {
|
|
DEBUG() << "Chromaprint::AudioProcessor::Resample() -- Resampling overwrote output buffer.\n";
|
|
length = kMaxBufferSize;
|
|
}
|
|
m_consumer->Consume(m_resample_buffer, length);
|
|
int remaining = m_buffer_offset - consumed;
|
|
if (remaining > 0) {
|
|
copy(m_buffer + consumed, m_buffer + m_buffer_offset, m_buffer);
|
|
}
|
|
else if (remaining < 0) {
|
|
DEBUG() << "Chromaprint::AudioProcessor::Resample() -- Resampling overread input buffer.\n";
|
|
remaining = 0;
|
|
}
|
|
m_buffer_offset = remaining;
|
|
}
|
|
|
|
|
|
bool AudioProcessor::Reset(int sample_rate, int num_channels)
|
|
{
|
|
if (num_channels <= 0) {
|
|
DEBUG() << "Chromaprint::AudioProcessor::Reset() -- No audio channels.\n";
|
|
return false;
|
|
}
|
|
if (sample_rate <= kMinSampleRate) {
|
|
DEBUG() << "Chromaprint::AudioProcessor::Reset() -- Sample rate less "
|
|
<< "than " << kMinSampleRate << " (" << sample_rate << ").\n";
|
|
return false;
|
|
}
|
|
m_buffer_offset = 0;
|
|
if (m_resample_ctx) {
|
|
av_resample_close(m_resample_ctx);
|
|
m_resample_ctx = 0;
|
|
}
|
|
if (sample_rate != m_target_sample_rate) {
|
|
m_resample_ctx = av_resample_init(
|
|
m_target_sample_rate, sample_rate,
|
|
kResampleFilterLength,
|
|
kResamplePhaseCount,
|
|
kResampleLinear,
|
|
kResampleCutoff);
|
|
}
|
|
m_num_channels = num_channels;
|
|
return true;
|
|
}
|
|
|
|
void AudioProcessor::Consume(short *input, int length)
|
|
{
|
|
assert(length >= 0);
|
|
assert(length % m_num_channels == 0);
|
|
length /= m_num_channels;
|
|
while (length > 0) {
|
|
int consumed = Load(input, length);
|
|
input += consumed * m_num_channels;
|
|
length -= consumed;
|
|
if (m_buffer_size == m_buffer_offset) {
|
|
Resample();
|
|
if (m_buffer_size == m_buffer_offset) {
|
|
DEBUG() << "Chromaprint::AudioProcessor::Consume() -- Resampling failed?\n";
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void AudioProcessor::Flush()
|
|
{
|
|
if (m_buffer_offset) {
|
|
Resample();
|
|
}
|
|
}
|
|
|