diff --git a/libs/qm-dsp/README.txt b/libs/qm-dsp/README.txt index 6927c7a1b8..03d8b04316 100644 --- a/libs/qm-dsp/README.txt +++ b/libs/qm-dsp/README.txt @@ -17,9 +17,6 @@ maths/pca.c -- Fionn Murtagh, from StatLib; with permission maths/Polyfit.h -- Allen Miller, David J Taylor and others; also for Delphi in the the JEDI Math Library, under the Mozilla Public License -thread/BlockAllocator.h -- derived from FSB Allocator by Juha Nieminen, -under BSD-style license - See individual files for further authorship details. diff --git a/libs/qm-dsp/dsp/rateconversion/Resampler.cpp b/libs/qm-dsp/dsp/rateconversion/Resampler.cpp deleted file mode 100644 index f0598cab2c..0000000000 --- a/libs/qm-dsp/dsp/rateconversion/Resampler.cpp +++ /dev/null @@ -1,416 +0,0 @@ -/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ -/* - QM DSP Library - - Centre for Digital Music, Queen Mary, University of London. - This file by Chris Cannam. - - 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 2 of the - License, or (at your option) any later version. See the file - COPYING included with this distribution for more information. -*/ - -#include "Resampler.h" - -#include "maths/MathUtilities.h" -#include "base/KaiserWindow.h" -#include "base/SincWindow.h" -#include "thread/Thread.h" - -#include -#include -#include -#include - -using std::vector; -using std::map; -using std::cerr; -using std::endl; - -//#define DEBUG_RESAMPLER 1 -//#define DEBUG_RESAMPLER_VERBOSE 1 - -Resampler::Resampler(int sourceRate, int targetRate) : - m_sourceRate(sourceRate), - m_targetRate(targetRate) -{ - initialise(100, 0.02); -} - -Resampler::Resampler(int sourceRate, int targetRate, - double snr, double bandwidth) : - m_sourceRate(sourceRate), - m_targetRate(targetRate) -{ - initialise(snr, bandwidth); -} - -Resampler::~Resampler() -{ - delete[] m_phaseData; -} - -// peakToPole -> length -> beta -> window -static map > > > -knownFilters; - -static Mutex -knownFilterMutex; - -void -Resampler::initialise(double snr, double bandwidth) -{ - int higher = std::max(m_sourceRate, m_targetRate); - int lower = std::min(m_sourceRate, m_targetRate); - - m_gcd = MathUtilities::gcd(lower, higher); - m_peakToPole = higher / m_gcd; - - if (m_targetRate < m_sourceRate) { - // antialiasing filter, should be slightly below nyquist - m_peakToPole = m_peakToPole / (1.0 - bandwidth/2.0); - } - - KaiserWindow::Parameters params = - KaiserWindow::parametersForBandwidth(snr, bandwidth, higher / m_gcd); - - params.length = - (params.length % 2 == 0 ? params.length + 1 : params.length); - - params.length = - (params.length > 200001 ? 200001 : params.length); - - m_filterLength = params.length; - - vector filter; - knownFilterMutex.lock(); - - if (knownFilters[m_peakToPole][m_filterLength].find(params.beta) == - knownFilters[m_peakToPole][m_filterLength].end()) { - - KaiserWindow kw(params); - SincWindow sw(m_filterLength, m_peakToPole * 2); - - filter = vector(m_filterLength, 0.0); - for (int i = 0; i < m_filterLength; ++i) filter[i] = 1.0; - sw.cut(filter.data()); - kw.cut(filter.data()); - - knownFilters[m_peakToPole][m_filterLength][params.beta] = filter; - } - - filter = knownFilters[m_peakToPole][m_filterLength][params.beta]; - knownFilterMutex.unlock(); - - int inputSpacing = m_targetRate / m_gcd; - int outputSpacing = m_sourceRate / m_gcd; - -#ifdef DEBUG_RESAMPLER - cerr << "resample " << m_sourceRate << " -> " << m_targetRate - << ": inputSpacing " << inputSpacing << ", outputSpacing " - << outputSpacing << ": filter length " << m_filterLength - << endl; -#endif - - // Now we have a filter of (odd) length flen in which the lower - // sample rate corresponds to every n'th point and the higher rate - // to every m'th where n and m are higher and lower rates divided - // by their gcd respectively. So if x coordinates are on the same - // scale as our filter resolution, then source sample i is at i * - // (targetRate / gcd) and target sample j is at j * (sourceRate / - // gcd). - - // To reconstruct a single target sample, we want a buffer (real - // or virtual) of flen values formed of source samples spaced at - // intervals of (targetRate / gcd), in our example case 3. This - // is initially formed with the first sample at the filter peak. - // - // 0 0 0 0 a 0 0 b 0 - // - // and of course we have our filter - // - // f1 f2 f3 f4 f5 f6 f7 f8 f9 - // - // We take the sum of products of non-zero values from this buffer - // with corresponding values in the filter - // - // a * f5 + b * f8 - // - // Then we drop (sourceRate / gcd) values, in our example case 4, - // from the start of the buffer and fill until it has flen values - // again - // - // a 0 0 b 0 0 c 0 0 - // - // repeat to reconstruct the next target sample - // - // a * f1 + b * f4 + c * f7 - // - // and so on. - // - // Above I said the buffer could be "real or virtual" -- ours is - // virtual. We don't actually store all the zero spacing values, - // except for padding at the start; normally we store only the - // values that actually came from the source stream, along with a - // phase value that tells us how many virtual zeroes there are at - // the start of the virtual buffer. So the two examples above are - // - // 0 a b [ with phase 1 ] - // a b c [ with phase 0 ] - // - // Having thus broken down the buffer so that only the elements we - // need to multiply are present, we can also unzip the filter into - // every-nth-element subsets at each phase, allowing us to do the - // filter multiplication as a simply vector multiply. That is, rather - // than store - // - // f1 f2 f3 f4 f5 f6 f7 f8 f9 - // - // we store separately - // - // f1 f4 f7 - // f2 f5 f8 - // f3 f6 f9 - // - // Each time we complete a multiply-and-sum, we need to work out - // how many (real) samples to drop from the start of our buffer, - // and how many to add at the end of it for the next multiply. We - // know we want to drop enough real samples to move along by one - // computed output sample, which is our outputSpacing number of - // virtual buffer samples. Depending on the relationship between - // input and output spacings, this may mean dropping several real - // samples, one real sample, or none at all (and simply moving to - // a different "phase"). - - m_phaseData = new Phase[inputSpacing]; - - for (int phase = 0; phase < inputSpacing; ++phase) { - - Phase p; - - p.nextPhase = phase - outputSpacing; - while (p.nextPhase < 0) p.nextPhase += inputSpacing; - p.nextPhase %= inputSpacing; - - p.drop = int(ceil(std::max(0.0, double(outputSpacing - phase)) - / inputSpacing)); - - int filtZipLength = int(ceil(double(m_filterLength - phase) - / inputSpacing)); - - for (int i = 0; i < filtZipLength; ++i) { - p.filter.push_back(filter[i * inputSpacing + phase]); - } - - m_phaseData[phase] = p; - } - -#ifdef DEBUG_RESAMPLER - int cp = 0; - int totDrop = 0; - for (int i = 0; i < inputSpacing; ++i) { - cerr << "phase = " << cp << ", drop = " << m_phaseData[cp].drop - << ", filter length = " << m_phaseData[cp].filter.size() - << ", next phase = " << m_phaseData[cp].nextPhase << endl; - totDrop += m_phaseData[cp].drop; - cp = m_phaseData[cp].nextPhase; - } - cerr << "total drop = " << totDrop << endl; -#endif - - // The May implementation of this uses a pull model -- we ask the - // resampler for a certain number of output samples, and it asks - // its source stream for as many as it needs to calculate - // those. This means (among other things) that the source stream - // can be asked for enough samples up-front to fill the buffer - // before the first output sample is generated. - // - // In this implementation we're using a push model in which a - // certain number of source samples is provided and we're asked - // for as many output samples as that makes available. But we - // can't return any samples from the beginning until half the - // filter length has been provided as input. This means we must - // either return a very variable number of samples (none at all - // until the filter fills, then half the filter length at once) or - // else have a lengthy declared latency on the output. We do the - // latter. (What do other implementations do?) - // - // We want to make sure the first "real" sample will eventually be - // aligned with the centre sample in the filter (it's tidier, and - // easier to do diagnostic calculations that way). So we need to - // pick the initial phase and buffer fill accordingly. - // - // Example: if the inputSpacing is 2, outputSpacing is 3, and - // filter length is 7, - // - // x x x x a b c ... input samples - // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 ... - // i j k l ... output samples - // [--------|--------] <- filter with centre mark - // - // Let h be the index of the centre mark, here 3 (generally - // int(filterLength/2) for odd-length filters). - // - // The smallest n such that h + n * outputSpacing > filterLength - // is 2 (that is, ceil((filterLength - h) / outputSpacing)), and - // (h + 2 * outputSpacing) % inputSpacing == 1, so the initial - // phase is 1. - // - // To achieve our n, we need to pre-fill the "virtual" buffer with - // 4 zero samples: the x's above. This is int((h + n * - // outputSpacing) / inputSpacing). It's the phase that makes this - // buffer get dealt with in such a way as to give us an effective - // index for sample a of 9 rather than 8 or 10 or whatever. - // - // This gives us output latency of 2 (== n), i.e. output samples i - // and j will appear before the one in which input sample a is at - // the centre of the filter. - - int h = int(m_filterLength / 2); - int n = ceil(double(m_filterLength - h) / outputSpacing); - - m_phase = (h + n * outputSpacing) % inputSpacing; - - int fill = (h + n * outputSpacing) / inputSpacing; - - m_latency = n; - - m_buffer = vector(fill, 0); - m_bufferOrigin = 0; - -#ifdef DEBUG_RESAMPLER - cerr << "initial phase " << m_phase << " (as " << (m_filterLength/2) << " % " << inputSpacing << ")" - << ", latency " << m_latency << endl; -#endif -} - -double -Resampler::reconstructOne() -{ - Phase &pd = m_phaseData[m_phase]; - double v = 0.0; - int n = pd.filter.size(); - - assert(n + m_bufferOrigin <= (int)m_buffer.size()); - - const double *const __restrict__ buf = m_buffer.data() + m_bufferOrigin; - const double *const __restrict__ filt = pd.filter.data(); - - for (int i = 0; i < n; ++i) { - // NB gcc can only vectorize this with -ffast-math - v += buf[i] * filt[i]; - } - - m_bufferOrigin += pd.drop; - m_phase = pd.nextPhase; - return v; -} - -int -Resampler::process(const double *src, double *dst, int n) -{ - for (int i = 0; i < n; ++i) { - m_buffer.push_back(src[i]); - } - - int maxout = int(ceil(double(n) * m_targetRate / m_sourceRate)); - int outidx = 0; - -#ifdef DEBUG_RESAMPLER - cerr << "process: buf siz " << m_buffer.size() << " filt siz for phase " << m_phase << " " << m_phaseData[m_phase].filter.size() << endl; -#endif - - double scaleFactor = (double(m_targetRate) / m_gcd) / m_peakToPole; - - while (outidx < maxout && - m_buffer.size() >= m_phaseData[m_phase].filter.size() + m_bufferOrigin) { - dst[outidx] = scaleFactor * reconstructOne(); - outidx++; - } - - m_buffer = vector(m_buffer.begin() + m_bufferOrigin, m_buffer.end()); - m_bufferOrigin = 0; - - return outidx; -} - -vector -Resampler::process(const double *src, int n) -{ - int maxout = int(ceil(double(n) * m_targetRate / m_sourceRate)); - vector out(maxout, 0.0); - int got = process(src, out.data(), n); - assert(got <= maxout); - if (got < maxout) out.resize(got); - return out; -} - -vector -Resampler::resample(int sourceRate, int targetRate, const double *data, int n) -{ - Resampler r(sourceRate, targetRate); - - int latency = r.getLatency(); - - // latency is the output latency. We need to provide enough - // padding input samples at the end of input to guarantee at - // *least* the latency's worth of output samples. that is, - - int inputPad = int(ceil((double(latency) * sourceRate) / targetRate)); - - // that means we are providing this much input in total: - - int n1 = n + inputPad; - - // and obtaining this much output in total: - - int m1 = int(ceil((double(n1) * targetRate) / sourceRate)); - - // in order to return this much output to the user: - - int m = int(ceil((double(n) * targetRate) / sourceRate)); - -#ifdef DEBUG_RESAMPLER - cerr << "n = " << n << ", sourceRate = " << sourceRate << ", targetRate = " << targetRate << ", m = " << m << ", latency = " << latency << ", inputPad = " << inputPad << ", m1 = " << m1 << ", n1 = " << n1 << ", n1 - n = " << n1 - n << endl; -#endif - - vector pad(n1 - n, 0.0); - vector out(m1 + 1, 0.0); - - int gotData = r.process(data, out.data(), n); - int gotPad = r.process(pad.data(), out.data() + gotData, pad.size()); - int got = gotData + gotPad; - -#ifdef DEBUG_RESAMPLER - cerr << "resample: " << n << " in, " << pad.size() << " padding, " << got << " out (" << gotData << " data, " << gotPad << " padding, latency = " << latency << ")" << endl; -#endif -#ifdef DEBUG_RESAMPLER_VERBOSE - int printN = 50; - cerr << "first " << printN << " in:" << endl; - for (int i = 0; i < printN && i < n; ++i) { - if (i % 5 == 0) cerr << endl << i << "... "; - cerr << data[i] << " "; - } - cerr << endl; -#endif - - int toReturn = got - latency; - if (toReturn > m) toReturn = m; - - vector sliced(out.begin() + latency, - out.begin() + latency + toReturn); - -#ifdef DEBUG_RESAMPLER_VERBOSE - cerr << "first " << printN << " out (after latency compensation), length " << sliced.size() << ":"; - for (int i = 0; i < printN && i < sliced.size(); ++i) { - if (i % 5 == 0) cerr << endl << i << "... "; - cerr << sliced[i] << " "; - } - cerr << endl; -#endif - - return sliced; -} - diff --git a/libs/qm-dsp/dsp/rateconversion/Resampler.h b/libs/qm-dsp/dsp/rateconversion/Resampler.h deleted file mode 100644 index 92c0169ba0..0000000000 --- a/libs/qm-dsp/dsp/rateconversion/Resampler.h +++ /dev/null @@ -1,102 +0,0 @@ -/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ -/* - QM DSP Library - - Centre for Digital Music, Queen Mary, University of London. - This file by Chris Cannam. - - 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 2 of the - License, or (at your option) any later version. See the file - COPYING included with this distribution for more information. -*/ - -#ifndef RESAMPLER_H -#define RESAMPLER_H - -#include - -/** - * Resampler resamples a stream from one integer sample rate to - * another (arbitrary) rate, using a kaiser-windowed sinc filter. The - * results and performance are pretty similar to libraries such as - * libsamplerate, though this implementation does not support - * time-varying ratios (the ratio is fixed on construction). - * - * See also Decimator, which is faster and rougher but supports only - * power-of-two downsampling factors. - */ -class Resampler -{ -public: - /** - * Construct a Resampler to resample from sourceRate to - * targetRate. - */ - Resampler(int sourceRate, int targetRate); - - /** - * Construct a Resampler to resample from sourceRate to - * targetRate, using the given filter parameters. - */ - Resampler(int sourceRate, int targetRate, - double snr, double bandwidth); - - virtual ~Resampler(); - - /** - * Read n input samples from src and write resampled data to - * dst. The return value is the number of samples written, which - * will be no more than ceil((n * targetRate) / sourceRate). The - * caller must ensure the dst buffer has enough space for the - * samples returned. - */ - int process(const double *src, double *dst, int n); - - /** - * Read n input samples from src and return resampled data by - * value. - */ - std::vector process(const double *src, int n); - - /** - * Return the number of samples of latency at the output due by - * the filter. (That is, the output will be delayed by this number - * of samples relative to the input.) - */ - int getLatency() const { return m_latency; } - - /** - * Carry out a one-off resample of a single block of n - * samples. The output is latency-compensated. - */ - static std::vector resample - (int sourceRate, int targetRate, const double *data, int n); - -private: - int m_sourceRate; - int m_targetRate; - int m_gcd; - int m_filterLength; - int m_bufferLength; - int m_latency; - double m_peakToPole; - - struct Phase { - int nextPhase; - std::vector filter; - int drop; - }; - - Phase *m_phaseData; - int m_phase; - std::vector m_buffer; - int m_bufferOrigin; - - void initialise(double, double); - double reconstructOne(); -}; - -#endif - diff --git a/libs/qm-dsp/thread/AsynchronousTask.h b/libs/qm-dsp/thread/AsynchronousTask.h deleted file mode 100644 index d9d7d872c9..0000000000 --- a/libs/qm-dsp/thread/AsynchronousTask.h +++ /dev/null @@ -1,110 +0,0 @@ -/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ - -/* - QM DSP Library - - Centre for Digital Music, Queen Mary, University of London. - This file Copyright 2009 QMUL. -*/ - -#ifndef _ASYNCHRONOUS_TASK_H_ -#define _ASYNCHRONOUS_TASK_H_ - -#include "Thread.h" - -#include - -/** - * AsynchronousTask provides a thread pattern implementation for - * threads which are used to perform a series of similar operations in - * parallel with other threads of the same type. - * - * For example, a thread used to calculate FFTs of a particular block - * size in the context of a class that needs to calculate many block - * sizes of FFT at once may be a candidate for an AsynchronousTask. - * - * The general use pattern is: - * - * caller -> request thread A calculate something - * caller -> request thread B calculate something - * caller -> request thread C calculate something - * caller -> wait for threads A, B, and C - * - * Here threads A, B, and C may be AsynchronousTasks. An important - * point is that the caller must be prepared to block when waiting for - * these threads to complete (i.e. they are started asynchronously, - * but testing for completion is synchronous). - */ -class AsynchronousTask : public Thread -{ -public: - AsynchronousTask() : - m_todo("AsynchronousTask: task to perform"), - m_done("AsynchronousTask: task complete"), - m_inTask(false), - m_finishing(false) - { - start(); - } - virtual ~AsynchronousTask() - { - m_todo.lock(); - m_finishing = true; - m_todo.signal(); - m_todo.unlock(); - wait(); - } - - // Subclass must provide methods to request task and obtain - // results, which the caller calls. The method that requests a - // new task should set up any internal state and call startTask(), - // which then calls back on the subclass implementation of - // performTask from within its work thread. The method that - // obtains results should call awaitTask() and then return any - // results from internal state. - -protected: - void startTask() { - m_done.lock(); - m_todo.lock(); - m_inTask = true; - m_todo.signal(); - m_todo.unlock(); - } - void awaitTask() { - m_done.wait(); - m_done.unlock(); - } - - virtual void performTask() = 0; - -private: - virtual void run() { - m_todo.lock(); - while (1) { - while (!m_inTask && !m_finishing) { - m_todo.wait(); - } - if (m_finishing) { - m_done.lock(); - m_inTask = false; - m_done.signal(); - m_done.unlock(); - break; - } - performTask(); - m_done.lock(); - m_inTask = false; - m_done.signal(); - m_done.unlock(); - } - m_todo.unlock(); - } - - Condition m_todo; - Condition m_done; - bool m_inTask; - bool m_finishing; -}; - -#endif diff --git a/libs/qm-dsp/thread/BlockAllocator.h b/libs/qm-dsp/thread/BlockAllocator.h deleted file mode 100644 index 5c311d615d..0000000000 --- a/libs/qm-dsp/thread/BlockAllocator.h +++ /dev/null @@ -1,189 +0,0 @@ -/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ - -/* - QM DSP Library - - Centre for Digital Music, Queen Mary, University of London. - - This file is derived from the FSB Allocator by Juha Nieminen. The - underlying method is unchanged, but the class has been refactored - to permit multiple separate allocators (e.g. one per thread) - rather than use a single global one (and to fit house style). - -Copyright (c) 2008 Juha Nieminen - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -*/ - -#ifndef _BLOCK_ALLOCATOR_H_ -#define _BLOCK_ALLOCATOR_H_ - -#include - -/** - * BlockAllocator is a simple allocator for fixed-size (usually small) - * chunks of memory. The size of an element is specified in the - * BlockAllocator constructor, and the functions allocate() and - * deallocate() are used to obtain and release a single element at a - * time. - * - * BlockAllocator may be an appropriate class to use in situations - * involving a very large number of allocations and deallocations of - * simple, identical objects across multiple threads (a hard situation - * for a generic system malloc implementation to handle well). Retain - * one BlockAllocator per thread (the class itself is not - * thread-safe), and ensure that each thread uses its own allocator - * exclusively. - * - * BlockAllocator is based on Juha Nieminen's more general - * FSBAllocator. - */ -class BlockAllocator -{ -public: - typedef std::size_t data_t; - - BlockAllocator(int elementSize) : m_sz(elementSize) { } - - void * - allocate() - { - if (m_freelist.empty()) { - m_freelist.push_back(m_blocks.data.size()); - m_blocks.data.push_back(Block(this)); - } - - const data_t index = m_freelist.back(); - Block &block = m_blocks.data[index]; - void *retval = block.allocate(index); - if (block.isFull()) m_freelist.pop_back(); - - return retval; - } - - void - deallocate(void *ptr) - { - if (!ptr) return; - - data_t *unitPtr = (data_t *)ptr; - const data_t blockIndex = unitPtr[elementSizeInDataUnits()]; - Block& block = m_blocks.data[blockIndex]; - - if (block.isFull()) m_freelist.push_back(blockIndex); - block.deallocate(unitPtr); - } - -private: - inline data_t elementsPerBlock() const { - return 512; - } - inline data_t dataSize() const { - return sizeof(data_t); - } - inline data_t elementSizeInDataUnits() const { - return (m_sz + (dataSize() - 1)) / dataSize(); - } - inline data_t unitSizeInDataUnits() const { - return elementSizeInDataUnits() + 1; - } - inline data_t blockSizeInDataUnits() const { - return elementsPerBlock() * unitSizeInDataUnits(); - } - - class Block - { - public: - Block(BlockAllocator *a) : - m_a(a), - m_block(0), - m_firstFreeUnit(data_t(-1)), - m_allocated(0), - m_end(0) - {} - - ~Block() { - delete[] m_block; - } - - bool isFull() const { - return m_allocated == m_a->elementsPerBlock(); - } - - void clear() { - delete[] m_block; - m_block = 0; - m_firstFreeUnit = data_t(-1); - } - - void *allocate(data_t index) { - - if (m_firstFreeUnit == data_t(-1)) { - - if (!m_block) { - m_block = new data_t[m_a->blockSizeInDataUnits()]; - m_end = 0; - } - - data_t *retval = m_block + m_end; - m_end += m_a->unitSizeInDataUnits(); - retval[m_a->elementSizeInDataUnits()] = index; - ++m_allocated; - return retval; - - } else { - - data_t *retval = m_block + m_firstFreeUnit; - m_firstFreeUnit = *retval; - ++m_allocated; - return retval; - } - } - - void deallocate(data_t *ptr) { - - *ptr = m_firstFreeUnit; - m_firstFreeUnit = ptr - m_block; - - if (--m_allocated == 0) clear(); - } - - private: - const BlockAllocator *m_a; - data_t *m_block; - data_t m_firstFreeUnit; - data_t m_allocated; - data_t m_end; - }; - - struct Blocks - { - std::vector data; - - Blocks() { - data.reserve(1024); - } - }; - - const int m_sz; - Blocks m_blocks; - std::vector m_freelist; -}; - -#endif diff --git a/libs/qm-dsp/thread/Thread.cpp b/libs/qm-dsp/thread/Thread.cpp deleted file mode 100644 index 85dcec3155..0000000000 --- a/libs/qm-dsp/thread/Thread.cpp +++ /dev/null @@ -1,643 +0,0 @@ -/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ - -/* - QM DSP Library - - Centre for Digital Music, Queen Mary, University of London. - This file copyright Chris Cannam, used with permission. -*/ - -#include "Thread.h" - -#include -#include - -#ifdef USE_PTHREADS -#include -#include -#endif - -using std::cerr; -using std::endl; -using std::string; - -#ifdef _WIN32 - -Thread::Thread() : - m_id(0), - m_extant(false) -{ -#ifdef DEBUG_THREAD - cerr << "THREAD DEBUG: Created thread object " << this << endl; -#endif -} - -Thread::~Thread() -{ -#ifdef DEBUG_THREAD - cerr << "THREAD DEBUG: Destroying thread object " << this << ", id " << m_id << endl; -#endif - if (m_extant) { - WaitForSingleObject(m_id, INFINITE); - } -#ifdef DEBUG_THREAD - cerr << "THREAD DEBUG: Destroyed thread object " << this << endl; -#endif -} - -void -Thread::start() -{ - m_id = CreateThread(NULL, 0, staticRun, this, 0, 0); - if (!m_id) { - cerr << "ERROR: thread creation failed" << endl; - exit(1); - } else { -#ifdef DEBUG_THREAD - cerr << "THREAD DEBUG: Created thread " << m_id << " for thread object " << this << endl; -#endif - m_extant = true; - } -} - -void -Thread::wait() -{ - if (m_extant) { -#ifdef DEBUG_THREAD - cerr << "THREAD DEBUG: Waiting on thread " << m_id << " for thread object " << this << endl; -#endif - WaitForSingleObject(m_id, INFINITE); -#ifdef DEBUG_THREAD - cerr << "THREAD DEBUG: Waited on thread " << m_id << " for thread object " << this << endl; -#endif - m_extant = false; - } -} - -Thread::Id -Thread::id() -{ - return m_id; -} - -bool -Thread::threadingAvailable() -{ - return true; -} - -DWORD -Thread::staticRun(LPVOID arg) -{ - Thread *thread = static_cast(arg); -#ifdef DEBUG_THREAD - cerr << "THREAD DEBUG: " << (void *)GetCurrentThreadId() << ": Running thread " << thread->m_id << " for thread object " << thread << endl; -#endif - thread->run(); - return 0; -} - -Mutex::Mutex() -#ifndef NO_THREAD_CHECKS - : - m_lockedBy(-1) -#endif -{ - m_mutex = CreateMutex(NULL, FALSE, NULL); -#ifdef DEBUG_MUTEX - cerr << "MUTEX DEBUG: " << (void *)GetCurrentThreadId() << ": Initialised mutex " << &m_mutex << endl; -#endif -} - -Mutex::~Mutex() -{ -#ifdef DEBUG_MUTEX - cerr << "MUTEX DEBUG: " << (void *)GetCurrentThreadId() << ": Destroying mutex " << &m_mutex << endl; -#endif - CloseHandle(m_mutex); -} - -void -Mutex::lock() -{ -#ifndef NO_THREAD_CHECKS - DWORD tid = GetCurrentThreadId(); - if (m_lockedBy == tid) { - cerr << "ERROR: Deadlock on mutex " << &m_mutex << endl; - } -#endif -#ifdef DEBUG_MUTEX - cerr << "MUTEX DEBUG: " << (void *)tid << ": Want to lock mutex " << &m_mutex << endl; -#endif - WaitForSingleObject(m_mutex, INFINITE); -#ifndef NO_THREAD_CHECKS - m_lockedBy = tid; -#endif -#ifdef DEBUG_MUTEX - cerr << "MUTEX DEBUG: " << (void *)tid << ": Locked mutex " << &m_mutex << endl; -#endif -} - -void -Mutex::unlock() -{ -#ifndef NO_THREAD_CHECKS - DWORD tid = GetCurrentThreadId(); - if (m_lockedBy != tid) { - cerr << "ERROR: Mutex " << &m_mutex << " not owned by unlocking thread" << endl; - return; - } -#endif -#ifdef DEBUG_MUTEX - cerr << "MUTEX DEBUG: " << (void *)tid << ": Unlocking mutex " << &m_mutex << endl; -#endif -#ifndef NO_THREAD_CHECKS - m_lockedBy = -1; -#endif - ReleaseMutex(m_mutex); -} - -bool -Mutex::trylock() -{ -#ifndef NO_THREAD_CHECKS - DWORD tid = GetCurrentThreadId(); -#endif - DWORD result = WaitForSingleObject(m_mutex, 0); - if (result == WAIT_TIMEOUT || result == WAIT_FAILED) { -#ifdef DEBUG_MUTEX - cerr << "MUTEX DEBUG: " << (void *)tid << ": Mutex " << &m_mutex << " unavailable" << endl; -#endif - return false; - } else { -#ifndef NO_THREAD_CHECKS - m_lockedBy = tid; -#endif -#ifdef DEBUG_MUTEX - cerr << "MUTEX DEBUG: " << (void *)tid << ": Locked mutex " << &m_mutex << " (from trylock)" << endl; -#endif - return true; - } -} - -Condition::Condition(string name) : - m_locked(false) -#ifdef DEBUG_CONDITION - , m_name(name) -#endif -{ - m_mutex = CreateMutex(NULL, FALSE, NULL); - m_condition = CreateEvent(NULL, FALSE, FALSE, NULL); -#ifdef DEBUG_CONDITION - cerr << "CONDITION DEBUG: " << (void *)GetCurrentThreadId() << ": Initialised condition " << &m_condition << " \"" << m_name << "\"" << endl; -#endif -} - -Condition::~Condition() -{ -#ifdef DEBUG_CONDITION - cerr << "CONDITION DEBUG: " << (void *)GetCurrentThreadId() << ": Destroying condition " << &m_condition << " \"" << m_name << "\"" << endl; -#endif - if (m_locked) ReleaseMutex(m_mutex); - CloseHandle(m_condition); - CloseHandle(m_mutex); -} - -void -Condition::lock() -{ -#ifdef DEBUG_CONDITION - cerr << "CONDITION DEBUG: " << (void *)GetCurrentThreadId() << ": Want to lock " << &m_condition << " \"" << m_name << "\"" << endl; -#endif - WaitForSingleObject(m_mutex, INFINITE); - m_locked = true; -#ifdef DEBUG_CONDITION - cerr << "CONDITION DEBUG: " << (void *)GetCurrentThreadId() << ": Locked " << &m_condition << " \"" << m_name << "\"" << endl; -#endif -} - -void -Condition::unlock() -{ - if (!m_locked) { -#ifdef DEBUG_CONDITION - cerr << "CONDITION DEBUG: " << (void *)GetCurrentThreadId() << ": Not locked " << &m_condition << " \"" << m_name << "\"" << endl; -#endif - return; - } -#ifdef DEBUG_CONDITION - cerr << "CONDITION DEBUG: " << (void *)GetCurrentThreadId() << ": Unlocking " << &m_condition << " \"" << m_name << "\"" << endl; -#endif - m_locked = false; - ReleaseMutex(m_mutex); -} - -void -Condition::wait(int us) -{ - if (us == 0) { - -#ifdef DEBUG_CONDITION - cerr << "CONDITION DEBUG: " << (void *)GetCurrentThreadId() << ": Waiting on " << &m_condition << " \"" << m_name << "\"" << endl; -#endif - SignalObjectAndWait(m_mutex, m_condition, INFINITE, FALSE); - WaitForSingleObject(m_mutex, INFINITE); - - } else { - - DWORD ms = us / 1000; - if (us > 0 && ms == 0) ms = 1; - -#ifdef DEBUG_CONDITION - cerr << "CONDITION DEBUG: " << (void *)GetCurrentThreadId() << ": Timed waiting on " << &m_condition << " \"" << m_name << "\"" << endl; -#endif - SignalObjectAndWait(m_mutex, m_condition, ms, FALSE); - WaitForSingleObject(m_mutex, INFINITE); - } - -#ifdef DEBUG_CONDITION - cerr << "CONDITION DEBUG: " << (void *)GetCurrentThreadId() << ": Wait done on " << &m_condition << " \"" << m_name << "\"" << endl; -#endif - - m_locked = true; -} - -void -Condition::signal() -{ -#ifdef DEBUG_CONDITION - cerr << "CONDITION DEBUG: " << (void *)GetCurrentThreadId() << ": Signalling " << &m_condition << " \"" << m_name << "\"" << endl; -#endif - SetEvent(m_condition); -} - -#else /* !_WIN32 */ - -#ifdef USE_PTHREADS - -Thread::Thread() : - m_id(0), - m_extant(false) -{ -#ifdef DEBUG_THREAD - cerr << "THREAD DEBUG: Created thread object " << this << endl; -#endif -} - -Thread::~Thread() -{ -#ifdef DEBUG_THREAD - cerr << "THREAD DEBUG: Destroying thread object " << this << ", id " << m_id << endl; -#endif - if (m_extant) { - pthread_join(m_id, 0); - } -#ifdef DEBUG_THREAD - cerr << "THREAD DEBUG: Destroyed thread object " << this << endl; -#endif -} - -void -Thread::start() -{ - if (pthread_create(&m_id, 0, staticRun, this)) { - cerr << "ERROR: thread creation failed" << endl; - exit(1); - } else { -#ifdef DEBUG_THREAD - cerr << "THREAD DEBUG: Created thread " << m_id << " for thread object " << this << endl; -#endif - m_extant = true; - } -} - -void -Thread::wait() -{ - if (m_extant) { -#ifdef DEBUG_THREAD - cerr << "THREAD DEBUG: Waiting on thread " << m_id << " for thread object " << this << endl; -#endif - pthread_join(m_id, 0); -#ifdef DEBUG_THREAD - cerr << "THREAD DEBUG: Waited on thread " << m_id << " for thread object " << this << endl; -#endif - m_extant = false; - } -} - -Thread::Id -Thread::id() -{ - return m_id; -} - -bool -Thread::threadingAvailable() -{ - return true; -} - -void * -Thread::staticRun(void *arg) -{ - Thread *thread = static_cast(arg); -#ifdef DEBUG_THREAD - cerr << "THREAD DEBUG: " << (void *)pthread_self() << ": Running thread " << thread->m_id << " for thread object " << thread << endl; -#endif - thread->run(); - return 0; -} - -Mutex::Mutex() -#ifndef NO_THREAD_CHECKS - : - m_lockedBy(0), - m_locked(false) -#endif -{ - pthread_mutex_init(&m_mutex, 0); -#ifdef DEBUG_MUTEX - cerr << "MUTEX DEBUG: " << (void *)pthread_self() << ": Initialised mutex " << &m_mutex << endl; -#endif -} - -Mutex::~Mutex() -{ -#ifdef DEBUG_MUTEX - cerr << "MUTEX DEBUG: " << (void *)pthread_self() << ": Destroying mutex " << &m_mutex << endl; -#endif - pthread_mutex_destroy(&m_mutex); -} - -void -Mutex::lock() -{ -#ifndef NO_THREAD_CHECKS - pthread_t tid = pthread_self(); - if (m_locked && m_lockedBy == tid) { - cerr << "ERROR: Deadlock on mutex " << &m_mutex << endl; - } -#endif -#ifdef DEBUG_MUTEX - cerr << "MUTEX DEBUG: " << (void *)tid << ": Want to lock mutex " << &m_mutex << endl; -#endif - pthread_mutex_lock(&m_mutex); -#ifndef NO_THREAD_CHECKS - m_lockedBy = tid; - m_locked = true; -#endif -#ifdef DEBUG_MUTEX - cerr << "MUTEX DEBUG: " << (void *)tid << ": Locked mutex " << &m_mutex << endl; -#endif -} - -void -Mutex::unlock() -{ -#ifndef NO_THREAD_CHECKS - pthread_t tid = pthread_self(); - if (!m_locked) { - cerr << "ERROR: Mutex " << &m_mutex << " not locked in unlock" << endl; - return; - } else if (m_lockedBy != tid) { - cerr << "ERROR: Mutex " << &m_mutex << " not owned by unlocking thread" << endl; - return; - } -#endif -#ifdef DEBUG_MUTEX - cerr << "MUTEX DEBUG: " << (void *)tid << ": Unlocking mutex " << &m_mutex << endl; -#endif -#ifndef NO_THREAD_CHECKS - m_locked = false; -#endif - pthread_mutex_unlock(&m_mutex); -} - -bool -Mutex::trylock() -{ -#ifndef NO_THREAD_CHECKS - pthread_t tid = pthread_self(); -#endif - if (pthread_mutex_trylock(&m_mutex)) { -#ifdef DEBUG_MUTEX - cerr << "MUTEX DEBUG: " << (void *)tid << ": Mutex " << &m_mutex << " unavailable" << endl; -#endif - return false; - } else { -#ifndef NO_THREAD_CHECKS - m_lockedBy = tid; - m_locked = true; -#endif -#ifdef DEBUG_MUTEX - cerr << "MUTEX DEBUG: " << (void *)tid << ": Locked mutex " << &m_mutex << " (from trylock)" << endl; -#endif - return true; - } -} - -Condition::Condition(string name) : - m_locked(false) -#ifdef DEBUG_CONDITION - , m_name(name) -#endif -{ - pthread_mutex_init(&m_mutex, 0); - pthread_cond_init(&m_condition, 0); -#ifdef DEBUG_CONDITION - cerr << "CONDITION DEBUG: " << (void *)pthread_self() << ": Initialised condition " << &m_condition << " \"" << m_name << "\"" << endl; -#endif -} - -Condition::~Condition() -{ -#ifdef DEBUG_CONDITION - cerr << "CONDITION DEBUG: " << (void *)pthread_self() << ": Destroying condition " << &m_condition << " \"" << m_name << "\"" << endl; -#endif - if (m_locked) pthread_mutex_unlock(&m_mutex); - pthread_cond_destroy(&m_condition); - pthread_mutex_destroy(&m_mutex); -} - -void -Condition::lock() -{ -#ifdef DEBUG_CONDITION - cerr << "CONDITION DEBUG: " << (void *)pthread_self() << ": Want to lock " << &m_condition << " \"" << m_name << "\"" << endl; -#endif - pthread_mutex_lock(&m_mutex); - m_locked = true; -#ifdef DEBUG_CONDITION - cerr << "CONDITION DEBUG: " << (void *)pthread_self() << ": Locked " << &m_condition << " \"" << m_name << "\"" << endl; -#endif -} - -void -Condition::unlock() -{ - if (!m_locked) { -#ifdef DEBUG_CONDITION - cerr << "CONDITION DEBUG: " << (void *)pthread_self() << ": Not locked " << &m_condition << " \"" << m_name << "\"" << endl; -#endif - return; - } -#ifdef DEBUG_CONDITION - cerr << "CONDITION DEBUG: " << (void *)pthread_self() << ": Unlocking " << &m_condition << " \"" << m_name << "\"" << endl; -#endif - m_locked = false; - pthread_mutex_unlock(&m_mutex); -} - -void -Condition::wait(int us) -{ - if (us == 0) { - -#ifdef DEBUG_CONDITION - cerr << "CONDITION DEBUG: " << (void *)pthread_self() << ": Waiting on " << &m_condition << " \"" << m_name << "\"" << endl; -#endif - pthread_cond_wait(&m_condition, &m_mutex); - - } else { - - struct timeval now; - gettimeofday(&now, 0); - - now.tv_usec += us; - while (now.tv_usec > 1000000) { - now.tv_usec -= 1000000; - ++now.tv_sec; - } - - struct timespec timeout; - timeout.tv_sec = now.tv_sec; - timeout.tv_nsec = now.tv_usec * 1000; - -#ifdef DEBUG_CONDITION - cerr << "CONDITION DEBUG: " << (void *)pthread_self() << ": Timed waiting on " << &m_condition << " \"" << m_name << "\"" << endl; -#endif - pthread_cond_timedwait(&m_condition, &m_mutex, &timeout); - } - -#ifdef DEBUG_CONDITION - cerr << "CONDITION DEBUG: " << (void *)pthread_self() << ": Wait done on " << &m_condition << " \"" << m_name << "\"" << endl; -#endif - - m_locked = true; -} - -void -Condition::signal() -{ -#ifdef DEBUG_CONDITION - cerr << "CONDITION DEBUG: " << (void *)pthread_self() << ": Signalling " << &m_condition << " \"" << m_name << "\"" << endl; -#endif - pthread_cond_signal(&m_condition); -} - -#else /* !USE_PTHREADS */ - -Thread::Thread() -{ -} - -Thread::~Thread() -{ -} - -void -Thread::start() -{ - abort(); -} - -void -Thread::wait() -{ - abort(); -} - -Thread::Id -Thread::id() -{ - abort(); -} - -bool -Thread::threadingAvailable() -{ - return false; -} - -Mutex::Mutex() -{ -} - -Mutex::~Mutex() -{ -} - -void -Mutex::lock() -{ - abort(); -} - -void -Mutex::unlock() -{ - abort(); -} - -bool -Mutex::trylock() -{ - abort(); -} - -Condition::Condition(const char *) -{ -} - -Condition::~Condition() -{ -} - -void -Condition::lock() -{ - abort(); -} - -void -Condition::wait(int us) -{ - abort(); -} - -void -Condition::signal() -{ - abort(); -} - -#endif /* !USE_PTHREADS */ -#endif /* !_WIN32 */ - -MutexLocker::MutexLocker(Mutex *mutex) : - m_mutex(mutex) -{ - if (m_mutex) { - m_mutex->lock(); - } -} - -MutexLocker::~MutexLocker() -{ - if (m_mutex) { - m_mutex->unlock(); - } -} - diff --git a/libs/qm-dsp/thread/Thread.h b/libs/qm-dsp/thread/Thread.h deleted file mode 100644 index cd5489d379..0000000000 --- a/libs/qm-dsp/thread/Thread.h +++ /dev/null @@ -1,152 +0,0 @@ -/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ - -/* - QM DSP Library - - Centre for Digital Music, Queen Mary, University of London. - This file copyright Chris Cannam, used with permission. -*/ - -#ifndef _THREAD_H_ -#define _THREAD_H_ - -#ifdef _WIN32 -#include -#else /* !_WIN32 */ -#define USE_PTHREADS -#ifdef USE_PTHREADS -#include -#else -#error Must have either _WIN32 or USE_PTHREADS defined -#endif /* USE_PTHREADS */ -#endif /* !_WIN32 */ - -#include - -//#define DEBUG_THREAD 1 -//#define DEBUG_MUTEX 1 -//#define DEBUG_CONDITION 1 - -class Thread -{ -public: -#ifdef _WIN32 - typedef HANDLE Id; -#else -#ifdef USE_PTHREADS - typedef pthread_t Id; -#endif -#endif - - Thread(); - virtual ~Thread(); - - Id id(); - - void start(); - void wait(); - - static bool threadingAvailable(); - -protected: - virtual void run() = 0; - -private: -#ifdef _WIN32 - HANDLE m_id; - bool m_extant; - static DWORD WINAPI staticRun(LPVOID lpParam); -#else -#ifdef USE_PTHREADS - pthread_t m_id; - bool m_extant; - static void *staticRun(void *); -#endif -#endif -}; - -class Mutex -{ -public: - Mutex(); - ~Mutex(); - - void lock(); - void unlock(); - bool trylock(); - -private: -#ifdef _WIN32 - HANDLE m_mutex; -#ifndef NO_THREAD_CHECKS - DWORD m_lockedBy; -#endif -#else -#ifdef USE_PTHREADS - pthread_mutex_t m_mutex; -#ifndef NO_THREAD_CHECKS - pthread_t m_lockedBy; - bool m_locked; -#endif -#endif -#endif -}; - -class MutexLocker -{ -public: - MutexLocker(Mutex *); - ~MutexLocker(); - -private: - Mutex *m_mutex; -}; - -class Condition -{ -public: - Condition(std::string name); - ~Condition(); - - // Condition bundles a pthread-style condition variable and mutex - // into one class. - - // To wait on a condition, call lock(), test termination variables - // as appropriate, and then wait(). The condition will be - // unlocked for the duration of the wait() call, which will end - // when the condition is signalled. The condition will be locked - // again when wait() returns. - // - // To signal a condition, call signal(). If the waiting thread - // will be performing tests between its own lock() and wait(), - // then the signalling thread should also lock() before it signals - // (and then unlock afterwards). If the signalling thread always - // locks the mutex during signalling, then the waiting thread - // knows that signals will only happen during wait() and not be - // missed at other times. - - void lock(); - void unlock(); - void wait(int us = 0); - - void signal(); - -private: - -#ifdef _WIN32 - HANDLE m_mutex; - HANDLE m_condition; - bool m_locked; -#else -#ifdef USE_PTHREADS - pthread_mutex_t m_mutex; - pthread_cond_t m_condition; - bool m_locked; -#endif -#endif -#ifdef DEBUG_CONDITION - std::string m_name; -#endif -}; - -#endif diff --git a/libs/qm-dsp/wscript b/libs/qm-dsp/wscript index c4592750b2..11ea9c8011 100644 --- a/libs/qm-dsp/wscript +++ b/libs/qm-dsp/wscript @@ -49,7 +49,6 @@ def build(bld): dsp/phasevocoder/PhaseVocoder.cpp dsp/rateconversion/Decimator.cpp dsp/rateconversion/DecimatorB.cpp - dsp/rateconversion/Resampler.cpp dsp/rhythm/BeatSpectrum.cpp dsp/segmentation/cluster_melt.c dsp/segmentation/ClusterMeltSegmenter.cpp @@ -73,7 +72,6 @@ def build(bld): maths/KLDivergence.cpp maths/MathUtilities.cpp maths/pca/pca.c - thread/Thread.cpp ext/kissfft/kiss_fft.c ext/kissfft/tools/kiss_fftr.c '''