Mercurial > hg > vamp-live-host
changeset 0:a6020bf991cd
* Initial import of what may or may not become a simple live visual-response
host for causal Vamp plugins
author | cannam |
---|---|
date | Thu, 19 Oct 2006 16:53:48 +0000 |
parents | |
children | 4342352b8ef3 |
files | audioio/AudioCallbackRecordSource.cpp audioio/AudioCallbackRecordSource.h audioio/AudioCallbackRecordTarget.h audioio/AudioJACKSource.cpp audioio/AudioJACKSource.h audioio/AudioPortAudioSource.cpp audioio/AudioPortAudioSource.h audioio/AudioRecordSourceFactory.cpp audioio/AudioRecordSourceFactory.h audioio/BufferingAudioCallbackRecordTarget.cpp audioio/BufferingAudioCallbackRecordTarget.h host/Processor.cpp host/Processor.h host/host.cpp vamp-live-host.pro |
diffstat | 15 files changed, 1276 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/audioio/AudioCallbackRecordSource.cpp Thu Oct 19 16:53:48 2006 +0000 @@ -0,0 +1,16 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +#include "AudioCallbackRecordSource.h" +#include "AudioCallbackRecordTarget.h" + +#include <iostream> + +AudioCallbackRecordSource::AudioCallbackRecordSource(AudioCallbackRecordTarget *target) : + m_target(target) +{ +} + +AudioCallbackRecordSource::~AudioCallbackRecordSource() +{ +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/audioio/AudioCallbackRecordSource.h Thu Oct 19 16:53:48 2006 +0000 @@ -0,0 +1,23 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +#ifndef _AUDIO_CALLBACK_RECORD_SOURCE_H_ +#define _AUDIO_CALLBACK_RECORD_SOURCE_H_ + +#include <QObject> + +class AudioCallbackRecordTarget; + +class AudioCallbackRecordSource +{ +public: + AudioCallbackRecordSource(AudioCallbackRecordTarget *target); + virtual ~AudioCallbackRecordSource(); + + virtual bool isOK() const = 0; + +protected: + AudioCallbackRecordTarget *m_target; +}; + +#endif +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/audioio/AudioCallbackRecordTarget.h Thu Oct 19 16:53:48 2006 +0000 @@ -0,0 +1,28 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +#ifndef _AUDIO_CALLBACK_RECORD_TARGET_H_ +#define _AUDIO_CALLBACK_RECORD_TARGET_H_ + +#include <stdlib.h> + +class AudioCallbackRecordTarget +{ +public: + virtual ~AudioCallbackRecordTarget() { } + + virtual size_t getPreferredSampleRate() const { return 0; } + virtual size_t getChannelCount() const = 0; + + virtual void setSourceBlockSize(size_t) = 0; + virtual void setSourceSampleRate(size_t) = 0; + virtual void setSourceRecordLatency(size_t) = 0; + + virtual void putSamples(size_t nframes, float **samples) = 0; + + virtual void setInputLevels(float peakLeft, float peakRight) = 0; + + virtual void audioProcessingOverload() { } +}; + +#endif +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/audioio/AudioJACKSource.cpp Thu Oct 19 16:53:48 2006 +0000 @@ -0,0 +1,330 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +#ifdef HAVE_JACK + +#include "AudioJACKSource.h" +#include "AudioCallbackRecordTarget.h" + +#include <iostream> +#include <cmath> + +//#define DEBUG_AUDIO_JACK_SOURCE 1 + +#ifdef BUILD_STATIC +#ifdef Q_OS_LINUX + +// Some lunacy to enable JACK support in static builds. JACK isn't +// supposed to be linked statically, because it depends on a +// consistent shared memory layout between client library and daemon, +// so it's very fragile in the face of version mismatches. +// +// Therefore for static builds on Linux we avoid linking against JACK +// at all during the build, instead using dlopen and runtime symbol +// lookup to switch on JACK support at runtime. The following big +// mess (down to the #endifs) is the code that implements this. + +static void *symbol(const char *name) +{ + static bool attempted = false; + static void *library = 0; + static std::map<const char *, void *> symbols; + if (symbols.find(name) != symbols.end()) return symbols[name]; + if (!library) { + if (!attempted) { + library = ::dlopen("libjack.so.1", RTLD_NOW); + if (!library) library = ::dlopen("libjack.so.0", RTLD_NOW); + if (!library) library = ::dlopen("libjack.so", RTLD_NOW); + if (!library) { + std::cerr << "WARNING: AudioJACKSource: Failed to load JACK library: " + << ::dlerror() << " (tried .so, .so.0, .so.1)" + << std::endl; + } + attempted = true; + } + if (!library) return 0; + } + void *symbol = ::dlsym(library, name); + if (!symbol) { + std::cerr << "WARNING: AudioJACKSource: Failed to locate symbol " + << name << ": " << ::dlerror() << std::endl; + } + symbols[name] = symbol; + return symbol; +} + +static int dynamic_jack_set_process_callback(jack_client_t *client, + JackProcessCallback process_callback, + void *arg) +{ + typedef int (*func)(jack_client_t *client, + JackProcessCallback process_callback, + void *arg); + void *s = symbol("jack_set_process_callback"); + if (!s) return 1; + func f = (func)s; + return f(client, process_callback, arg); +} + +static int dynamic_jack_set_xrun_callback(jack_client_t *client, + JackXRunCallback xrun_callback, + void *arg) +{ + typedef int (*func)(jack_client_t *client, + JackXRunCallback xrun_callback, + void *arg); + void *s = symbol("jack_set_xrun_callback"); + if (!s) return 1; + func f = (func)s; + return f(client, xrun_callback, arg); +} + +static const char **dynamic_jack_get_ports(jack_client_t *client, + const char *port_name_pattern, + const char *type_name_pattern, + unsigned long flags) +{ + typedef const char **(*func)(jack_client_t *client, + const char *port_name_pattern, + const char *type_name_pattern, + unsigned long flags); + void *s = symbol("jack_get_ports"); + if (!s) return 0; + func f = (func)s; + return f(client, port_name_pattern, type_name_pattern, flags); +} + +static jack_port_t *dynamic_jack_port_register(jack_client_t *client, + const char *port_name, + const char *port_type, + unsigned long flags, + unsigned long buffer_size) +{ + typedef jack_port_t *(*func)(jack_client_t *client, + const char *port_name, + const char *port_type, + unsigned long flags, + unsigned long buffer_size); + void *s = symbol("jack_port_register"); + if (!s) return 0; + func f = (func)s; + return f(client, port_name, port_type, flags, buffer_size); +} + +static int dynamic_jack_connect(jack_client_t *client, + const char *source, + const char *dest) +{ + typedef int (*func)(jack_client_t *client, + const char *source, + const char *dest); + void *s = symbol("jack_connect"); + if (!s) return 1; + func f = (func)s; + return f(client, source, dest); +} + +static void *dynamic_jack_port_get_buffer(jack_port_t *port, + jack_nframes_t sz) +{ + typedef void *(*func)(jack_port_t *, jack_nframes_t); + void *s = symbol("jack_port_get_buffer"); + if (!s) return 0; + func f = (func)s; + return f(port, sz); +} + +static int dynamic_jack_port_unregister(jack_client_t *client, + jack_port_t *port) +{ + typedef int(*func)(jack_client_t *, jack_port_t *); + void *s = symbol("jack_port_unregister"); + if (!s) return 0; + func f = (func)s; + return f(client, port); +} + +#define dynamic1(rv, name, argtype, failval) \ + static rv dynamic_##name(argtype arg) { \ + typedef rv (*func) (argtype); \ + void *s = symbol(#name); \ + if (!s) return failval; \ + func f = (func) s; \ + return f(arg); \ + } + +dynamic1(jack_client_t *, jack_client_new, const char *, 0); +dynamic1(jack_nframes_t, jack_get_buffer_size, jack_client_t *, 0); +dynamic1(jack_nframes_t, jack_get_sample_rate, jack_client_t *, 0); +dynamic1(int, jack_activate, jack_client_t *, 1); +dynamic1(int, jack_deactivate, jack_client_t *, 1); +dynamic1(int, jack_client_close, jack_client_t *, 1); +dynamic1(jack_nframes_t, jack_port_get_latency, jack_port_t *, 0); +dynamic1(const char *, jack_port_name, const jack_port_t *, 0); + +#define jack_client_new dynamic_jack_client_new +#define jack_get_buffer_size dynamic_jack_get_buffer_size +#define jack_get_sample_rate dynamic_jack_get_sample_rate +#define jack_set_process_callback dynamic_jack_set_process_callback +#define jack_set_xrun_callback dynamic_jack_set_xrun_callback +#define jack_activate dynamic_jack_activate +#define jack_deactivate dynamic_jack_deactivate +#define jack_client_close dynamic_jack_client_close +#define jack_get_ports dynamic_jack_get_ports +#define jack_port_register dynamic_jack_port_register +#define jack_port_unregister dynamic_jack_port_unregister +#define jack_port_get_latency dynamic_jack_port_get_latency +#define jack_port_name dynamic_jack_port_name +#define jack_connect dynamic_jack_connect +#define jack_port_get_buffer dynamic_jack_port_get_buffer + +#endif +#endif + +AudioJACKSource::AudioJACKSource(AudioCallbackRecordTarget *target) : + AudioCallbackRecordSource(target), + m_client(0), + m_bufferSize(0), + m_sampleRate(0) +{ + char name[20]; + strcpy(name, "vamp-live-host"); + m_client = jack_client_new(name); + + if (!m_client) { + sprintf(name, "vamp-live-host (%d)", (int)getpid()); + m_client = jack_client_new(name); + if (!m_client) { + std::cerr + << "ERROR: AudioJACKSource: Failed to connect to JACK server" + << std::endl; + } + } + + if (!m_client) return; + + m_bufferSize = jack_get_buffer_size(m_client); + m_sampleRate = jack_get_sample_rate(m_client); + + jack_set_xrun_callback(m_client, xrunStatic, this); + jack_set_process_callback(m_client, processStatic, this); + + if (jack_activate(m_client)) { + std::cerr << "ERROR: AudioJACKSource: Failed to activate JACK client" + << std::endl; + } + + m_target->setSourceBlockSize(m_bufferSize); + m_target->setSourceSampleRate(m_sampleRate); + + size_t channels = m_target->getChannelCount(); + + while (m_inputs.size() < channels) { + + char name[20]; + jack_port_t *port; + + sprintf(name, "in %d", m_inputs.size() + 1); + + port = jack_port_register(m_client, + name, + JACK_DEFAULT_AUDIO_TYPE, + JackPortIsInput, + 0); + + if (!port) { + std::cerr + << "ERROR: AudioJACKSource: Failed to create JACK input port " + << m_inputs.size() << std::endl; + } else { + m_target->setSourceRecordLatency(jack_port_get_latency(port)); + } + + m_inputs.push_back(port); + } +} + +AudioJACKSource::~AudioJACKSource() +{ + if (m_client) { + jack_deactivate(m_client); + jack_client_close(m_client); + } +} + +bool +AudioJACKSource::isOK() const +{ + return (m_client != 0); +} + +int +AudioJACKSource::processStatic(jack_nframes_t nframes, void *arg) +{ + return ((AudioJACKSource *)arg)->process(nframes); +} + +int +AudioJACKSource::xrunStatic(void *arg) +{ + return ((AudioJACKSource *)arg)->xrun(); +} + +int +AudioJACKSource::process(jack_nframes_t nframes) +{ + if (m_inputs.size() < m_target->getChannelCount()) { + return 0; + } + +#ifdef DEBUG_AUDIO_JACK_SOURCE + std::cout << "AudioJACKSource::process(" << nframes << "), have " << m_inputs.size() << " inputs" << std::endl; +#endif + +#ifdef DEBUG_AUDIO_JACK_SOURCE + if (m_bufferSize != nframes) { + std::cerr << "WARNING: m_bufferSize != nframes (" << m_bufferSize << " != " << nframes << ")" << std::endl; + } +#endif + + float **buffers = (float **)alloca(m_inputs.size() * sizeof(float *)); + + for (size_t ch = 0; ch < m_inputs.size(); ++ch) { + buffers[ch] = (float *)jack_port_get_buffer(m_inputs[ch], nframes); + } + + if (m_target) { + m_target->putSamples(nframes, buffers); + } + + float peakLeft = 0.0, peakRight = 0.0; + + for (size_t ch = 0; ch < m_inputs.size(); ++ch) { + + float peak = 0.0; + + for (size_t i = 0; i < nframes; ++i) { + float sample = fabsf(buffers[ch][i]); + if (sample > peak) peak = sample; + } + + if (ch == 0) peakLeft = peak; + if (ch > 0 || m_inputs.size() == 1) peakRight = peak; + } + + if (m_target) { + m_target->setInputLevels(peakLeft, peakRight); + } + + return 0; +} + +int +AudioJACKSource::xrun() +{ + std::cerr << "AudioJACKSource: xrun!" << std::endl; + if (m_target) m_target->audioProcessingOverload(); + return 0; +} + +#endif /* HAVE_JACK */ +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/audioio/AudioJACKSource.h Thu Oct 19 16:53:48 2006 +0000 @@ -0,0 +1,42 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +#ifndef _AUDIO_JACK_SOURCE_H_ +#define _AUDIO_JACK_SOURCE_H_ + +#ifdef HAVE_JACK + +#include <jack/jack.h> +#include <vector> + +#include "AudioCallbackRecordSource.h" + +#include <QMutex> + +class AudioCallbackRecordTarget; + +class AudioJACKSource : public AudioCallbackRecordSource +{ +public: + AudioJACKSource(AudioCallbackRecordTarget *target); + virtual ~AudioJACKSource(); + + virtual bool isOK() const; + +protected: + int process(jack_nframes_t nframes); + int xrun(); + + static int processStatic(jack_nframes_t, void *); + static int xrunStatic(void *); + + jack_client_t *m_client; + std::vector<jack_port_t *> m_inputs; + jack_nframes_t m_bufferSize; + jack_nframes_t m_sampleRate; + QMutex m_mutex; +}; + +#endif /* HAVE_JACK */ + +#endif +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/audioio/AudioPortAudioSource.cpp Thu Oct 19 16:53:48 2006 +0000 @@ -0,0 +1,174 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +#ifdef HAVE_PORTAUDIO + +#include "AudioPortAudioSource.h" +#include "AudioCallbackRecordTarget.h" + +#include <iostream> +#include <cassert> +#include <cmath> + +//#define DEBUG_AUDIO_PORT_AUDIO_SOURCE 1 + +AudioPortAudioSource::AudioPortAudioSource(AudioCallbackRecordTarget *target) : + AudioCallbackRecordSource(target), + m_stream(0), + m_bufferSize(0), + m_sampleRate(0), + m_latency(0) +{ + PaError err; + + err = Pa_Initialize(); + if (err != paNoError) { + std::cerr << "ERROR: AudioPortAudioSource: Failed to initialize PortAudio" << std::endl; + return; + } + + m_bufferSize = 1024; + m_sampleRate = 44100; + if (m_target && (m_target->getPreferredSampleRate() != 0)) { + m_sampleRate = m_target->getPreferredSampleRate(); + } + + m_latency = Pa_GetMinNumBuffers(m_bufferSize, m_sampleRate) * m_bufferSize; + + err = Pa_OpenDefaultStream(&m_stream, m_target->getChannelCount(), + 0, paFloat32, + m_sampleRate, m_bufferSize, 0, + processStatic, this); + + if (err != paNoError) { + std::cerr << "ERROR: AudioPortAudioSource: Failed to open PortAudio stream" << std::endl; + m_stream = 0; + Pa_Terminate(); + return; + } + + err = Pa_StartStream(m_stream); + + if (err != paNoError) { + std::cerr << "ERROR: AudioPortAudioSource: Failed to start PortAudio stream" << std::endl; + Pa_CloseStream(m_stream); + m_stream = 0; + Pa_Terminate(); + return; + } + + if (m_target) { + std::cerr << "AudioPortAudioSource: block size " << m_bufferSize << std::endl; + m_target->setSourceBlockSize(m_bufferSize); + m_target->setSourceSampleRate(m_sampleRate); + m_target->setSourceRecordLatency(m_latency); + } +} + +AudioPortAudioSource::~AudioPortAudioSource() +{ + if (m_stream) { + PaError err; + err = Pa_CloseStream(m_stream); + if (err != paNoError) { + std::cerr << "ERROR: AudioPortAudioSource: Failed to close PortAudio stream" << std::endl; + } + Pa_Terminate(); + } +} + +bool +AudioPortAudioSource::isOK() const +{ + return (m_stream != 0); +} + +int +AudioPortAudioSource::processStatic(void *input, void *output, + unsigned long nframes, + PaTimestamp outTime, void *data) +{ + return ((AudioPortAudioSource *)data)->process(input, output, + nframes, outTime); +} + +int +AudioPortAudioSource::process(void *inputBuffer, + void *outputBuffer, + unsigned long nframes, + PaTimestamp) +{ +#ifdef DEBUG_AUDIO_PORT_AUDIO_SOURCE + std::cout << "AudioPortAudioSource::process(" << nframes << ")" << std::endl; +#endif + + if (!m_target) return 0; + + float *input = (float *)inputBuffer; + + assert(nframes <= m_bufferSize); + + static float **tmpbuf = 0; + static size_t tmpbufch = 0; + static size_t tmpbufsz = 0; + + size_t channels = m_target->getChannelCount(); + + if (!tmpbuf || tmpbufch != channels || tmpbufsz < m_bufferSize) { + + if (tmpbuf) { + for (size_t i = 0; i < tmpbufch; ++i) { + delete[] tmpbuf[i]; + } + delete[] tmpbuf; + } + + tmpbufch = channels; + tmpbufsz = m_bufferSize; + tmpbuf = new float *[tmpbufch]; + + for (size_t i = 0; i < tmpbufch; ++i) { + tmpbuf[i] = new float[tmpbufsz]; + } + } + + float peakLeft = 0.0, peakRight = 0.0; + + for (size_t ch = 0; ch < 2; ++ch) { + + float peak = 0.0; + + if (ch < channels) { + + // PortAudio samples are interleaved + for (size_t i = 0; i < nframes; ++i) { + tmpbuf[ch][i] = input[i * 2 + ch]; + float sample = fabsf(input[i * 2 + ch]); + if (sample > peak) peak = sample; + } + + } else if (ch == 1 && channels == 1) { + + for (size_t i = 0; i < nframes; ++i) { + tmpbuf[0][i] = input[i * 2 + ch]; + float sample = fabsf(input[i * 2 + ch]); + if (sample > peak) peak = sample; + } + + } else { + for (size_t i = 0; i < nframes; ++i) { + tmpbuf[ch][i] = 0; + } + } + + if (ch == 0) peakLeft = peak; + if (ch > 0 || channels == 1) peakRight = peak; + } + + m_target->putSamples(nframes, tmpbuf); + m_target->setInputLevels(peakLeft, peakRight); + + return 0; +} + +#endif /* HAVE_PORTAUDIO */ +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/audioio/AudioPortAudioSource.h Thu Oct 19 16:53:48 2006 +0000 @@ -0,0 +1,40 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +#ifndef _AUDIO_PORT_AUDIO_SOURCE_H_ +#define _AUDIO_PORT_AUDIO_SOURCE_H_ + +#ifdef HAVE_PORTAUDIO + +#include <portaudio.h> +#include <vector> + +#include "AudioCallbackRecordSource.h" + +class AudioCallbackRecordTarget; + +class AudioPortAudioSource : public AudioCallbackRecordSource +{ +public: + AudioPortAudioSource(AudioCallbackRecordTarget *source); + virtual ~AudioPortAudioSource(); + + virtual bool isOK() const; + +protected: + int process(void *input, void *output, unsigned long frames, + PaTimestamp outTime); + + static int processStatic(void *, void *, unsigned long, + PaTimestamp, void *); + + PortAudioStream *m_stream; + + int m_bufferSize; + int m_sampleRate; + int m_latency; +}; + +#endif /* HAVE_PORTAUDIO */ + +#endif +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/audioio/AudioRecordSourceFactory.cpp Thu Oct 19 16:53:48 2006 +0000 @@ -0,0 +1,37 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +#include "AudioRecordSourceFactory.h" + +#include "AudioJACKSource.h" +#include "AudioPortAudioSource.h" + +#include <iostream> + +AudioCallbackRecordSource * +AudioRecordSourceFactory::createCallbackRecordSource(AudioCallbackRecordTarget *target) +{ + AudioCallbackRecordSource *source = 0; + +#ifdef HAVE_JACK + source = new AudioJACKSource(target); + if (source->isOK()) return source; + else { + std::cerr << "WARNING: AudioRecordSourceFactory::createCallbackRecordSource: Failed to open JACK source" << std::endl; + delete source; + } +#endif + +#ifdef HAVE_PORTAUDIO + source = new AudioPortAudioSource(target); + if (source->isOK()) return source; + else { + std::cerr << "WARNING: AudioRecordSourceFactory::createCallbackRecordSource: Failed to open PortAudio source" << std::endl; + delete source; + } +#endif + + std::cerr << "WARNING: AudioRecordSourceFactory::createCallbackRecordSource: No suitable sources available" << std::endl; + return 0; +} + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/audioio/AudioRecordSourceFactory.h Thu Oct 19 16:53:48 2006 +0000 @@ -0,0 +1,16 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +#ifndef _AUDIO_RECORD_SOURCE_FACTORY_H_ +#define _AUDIO_RECORD_SOURCE_FACTORY_H_ + +class AudioCallbackRecordTarget; +class AudioCallbackRecordSource; + +class AudioRecordSourceFactory +{ +public: + static AudioCallbackRecordSource *createCallbackRecordSource(AudioCallbackRecordTarget *); +}; + +#endif +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/audioio/BufferingAudioCallbackRecordTarget.cpp Thu Oct 19 16:53:48 2006 +0000 @@ -0,0 +1,54 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +#include "BufferingAudioCallbackRecordTarget.h" + +#define RING_BUFFER_SIZE 4194303 // 2^22-1 frames, or about 87s at 48KHz + +BufferingAudioCallbackRecordTarget::BufferingAudioCallbackRecordTarget() : + m_sourceBlockSize(0), + m_sourceSampleRate(0), + m_sourceRecordLatency(0) +{ + for (size_t c = 0; c < getChannelCount(); ++c) { + m_buffers.push_back(new RingBuffer<float>(RING_BUFFER_SIZE)); + } +} + +BufferingAudioCallbackRecordTarget::~BufferingAudioCallbackRecordTarget() +{ + for (size_t c = 0; c < getChannelCount(); ++c) { + delete m_buffers[c]; + } +} + +void +BufferingAudioCallbackRecordTarget::putSamples(size_t nframes, float **samples) +{ + for (size_t c = 0; c < getChannelCount(); ++c) { + if (samples[c]) { + m_buffers[c]->write(samples[c], nframes); + } else { + m_buffers[c]->zero(nframes); + } + } +} + +size_t +BufferingAudioCallbackRecordTarget::samplesReady() const +{ + size_t ready = 0; + for (size_t c = 0; c < getChannelCount(); ++c) { + size_t readyHere = m_buffers[c]->getReadSpace(); + if (c == 0 || readyHere < ready) ready = readyHere; + } + return ready; +} + +size_t +BufferingAudioCallbackRecordTarget::getSamples(size_t channel, size_t nframes, + float *samples) +{ + return m_buffers[channel]->read(samples, nframes); +} + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/audioio/BufferingAudioCallbackRecordTarget.h Thu Oct 19 16:53:48 2006 +0000 @@ -0,0 +1,44 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +#ifndef _BUFFERING_AUDIO_CALLBACK_RECORD_TARGET_H_ +#define _BUFFERING_AUDIO_CALLBACK_RECORD_TARGET_H_ + +#include "AudioCallbackRecordTarget.h" +#include "base/RingBuffer.h" + +class BufferingAudioCallbackRecordTarget : public AudioCallbackRecordTarget +{ +public: + BufferingAudioCallbackRecordTarget(); + virtual ~BufferingAudioCallbackRecordTarget(); + + virtual size_t getPreferredSampleRate() const { return 0; } + virtual size_t getChannelCount() const { return 2; } + + virtual void setSourceBlockSize(size_t bs) { m_sourceBlockSize = bs; } + virtual void setSourceSampleRate(size_t sr) { m_sourceSampleRate = sr; } + virtual void setSourceRecordLatency(size_t rl) { m_sourceRecordLatency = rl; } + + size_t getSourceBlockSize() const { return m_sourceBlockSize; } + size_t getSourceSampleRate() const { return m_sourceSampleRate; } + size_t getSourceRecordLatency() const { return m_sourceRecordLatency; } + + virtual void putSamples(size_t nframes, float **samples); + + virtual size_t samplesReady() const; + virtual size_t getSamples(size_t channel, size_t nframes, float *samples); + + virtual void setInputLevels(float, float) { } + + virtual void audioProcessingOverload() { } + +protected: + std::vector<RingBuffer<float> *> m_buffers; + + size_t m_sourceBlockSize; + size_t m_sourceSampleRate; + size_t m_sourceRecordLatency; +}; + +#endif +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/host/Processor.cpp Thu Oct 19 16:53:48 2006 +0000 @@ -0,0 +1,337 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +#include "Processor.h" + +#include "plugin/FeatureExtractionPluginFactory.h" + +using std::cout; +using std::cerr; +using std::endl; + +void printFeatures(int, int, int, Vamp::Plugin::FeatureSet &); +void transformInput(float *, size_t); +void fft(unsigned int, bool, double *, double *, double *, double *); + +Processor::Processor(BufferingAudioCallbackRecordTarget *audioRecordTarget) : + m_exiting(false), + m_audioRecordTarget(audioRecordTarget), + m_nextNumber(1), + m_pluginBlockSize(0) +{ +} + +Processor::~Processor() +{ + m_exiting = true; + wait(); +} + +void +Processor::run() +{ + while (!m_exiting) { + + size_t bs = m_pluginBlockSize; + if (bs == 0) { + msleep(100); + continue; + } + + size_t nframes = m_audioRecordTarget->samplesReady(); + + cout << "Have " << nframes << endl; + + if (nframes > bs) { + + runPlugins(); + + nframes = m_audioRecordTarget->samplesReady(); + + if (nframes > 0) msleep(10); + else msleep(50); + + } else { + msleep(50); + } + } +} + +void +Processor::runPlugins() +{ + QMutexLocker locker(&m_mutex); + + static size_t frame = 0; + + size_t bs = m_pluginBlockSize; + + size_t sr = m_audioRecordTarget->getSourceSampleRate(); + + size_t ch = m_audioRecordTarget->getChannelCount(); + float **buffers = new float *[ch]; + for (size_t c = 0; c < ch; ++c) { + buffers[c] = new float[bs]; + if (m_audioRecordTarget->getSamples(c, bs, buffers[c]) < bs) { + cerr << "ERROR: not enough samples from record in" << endl; + } + } + + // run one plugin for now -- question -- can the plugin modify its input buffers? + + PluginMap::iterator i = m_plugins.begin(); + + if (i != m_plugins.end()) { + + for (size_t c = 0; c < ch; ++c) { + if (i->second->getInputDomain() == Vamp::Plugin::FrequencyDomain) { + transformInput(buffers[c], bs); + } + } + + Vamp::Plugin::FeatureSet fs = + i->second->process(buffers, Vamp::RealTime::frame2RealTime(frame, sr)); + + for (Vamp::Plugin::FeatureSet::iterator j = fs.begin(); + j != fs.end(); ++j) { + printFeatures(frame, sr, j->first, fs); + } + } + + frame += bs; + + for (size_t c = 0; c < ch; ++c) { + delete[] buffers[c]; + } + delete[] buffers; +} + +int +Processor::addPlugin(QString pluginId) +{ + QMutexLocker locker(&m_mutex); + + size_t sr = m_audioRecordTarget->getSourceSampleRate(); + + if (!sr) { + cerr << "ERROR: Processor::addPlugin: Source sample rate is not defined" << endl; + return 0; + } + + FeatureExtractionPluginFactory *factory = + FeatureExtractionPluginFactory::instanceFor(pluginId); + + if (!factory) { + cerr << "ERROR: Processor::addPlugin: No factory for plugin \"" << pluginId.toStdString() << "\"" << endl; + return 0; + } + + Vamp::Plugin *plugin = factory->instantiatePlugin(pluginId, sr); + + if (!plugin) { + cerr << "ERROR: Processor::addPlugin: Failed to instantiate plugin \"" << pluginId.toStdString() << "\"" << endl; + return 0; + } + + size_t bs = plugin->getPreferredBlockSize(); + + if (m_pluginBlockSize != 0) bs = m_pluginBlockSize; + + if (bs == 0) bs = m_audioRecordTarget->getSourceBlockSize(); + + size_t step = bs; //!!! fix -- separate peek and advance in bufferer + + int ch = m_audioRecordTarget->getChannelCount(); + if (ch > plugin->getMaxChannelCount()) ch = plugin->getMaxChannelCount(); + + if (!plugin->initialise(ch, bs, step)) { + cerr << "ERROR: Processor::addPlugin: Initialisation failed with step size " << step << ", block size " << bs << ", channels " << ch << endl; + delete plugin; + return 0; + } + + m_pluginBlockSize = bs; + + int number = m_nextNumber++; + + m_plugins[number] = plugin; + return number; +} + +void +Processor::removePlugin(int number) +{ + QMutexLocker locker(&m_mutex); + + if (m_plugins.find(number) == m_plugins.end()) { + cerr << "ERROR: Processor::removePlugin: No such plugin number " + << number << endl; + return; + } + + Vamp::Plugin *plugin = m_plugins[number]; + m_plugins.erase(number); + delete plugin; +} + + +void +printFeatures(int frame, int sr, int output, Vamp::Plugin::FeatureSet &features) +{ + for (unsigned int i = 0; i < features[output].size(); ++i) { + Vamp::RealTime rt = Vamp::RealTime::frame2RealTime(frame, sr); + if (features[output][i].hasTimestamp) { + rt = features[output][i].timestamp; + } + cout << rt.toString() << ":"; + for (unsigned int j = 0; j < features[output][i].values.size(); ++j) { + cout << " " << features[output][i].values[j]; + } + cout << endl; + } +} + +void +transformInput(float *buffer, size_t size) +{ + double *inbuf = new double[size * 2]; + double *outbuf = new double[size * 2]; + + // Copy across with Hanning window + for (size_t i = 0; i < size; ++i) { + inbuf[i] = double(buffer[i]) * (0.50 - 0.50 * cos(2 * M_PI * i / size)); + inbuf[i + size] = 0.0; + } + + for (size_t i = 0; i < size/2; ++i) { + double temp = inbuf[i]; + inbuf[i] = inbuf[i + size/2]; + inbuf[i + size/2] = temp; + } + + fft(size, false, inbuf, inbuf + size, outbuf, outbuf + size); + + for (size_t i = 0; i < size/2; ++i) { + buffer[i * 2] = outbuf[i]; + buffer[i * 2 + 1] = outbuf[i + size]; + } + + delete inbuf; + delete outbuf; +} + +void +fft(unsigned int n, bool inverse, double *ri, double *ii, double *ro, double *io) +{ + if (!ri || !ro || !io) return; + + unsigned int bits; + unsigned int i, j, k, m; + unsigned int blockSize, blockEnd; + + double tr, ti; + + if (n < 2) return; + if (n & (n-1)) return; + + double angle = 2.0 * M_PI; + if (inverse) angle = -angle; + + for (i = 0; ; ++i) { + if (n & (1 << i)) { + bits = i; + break; + } + } + + static unsigned int tableSize = 0; + static int *table = 0; + + if (tableSize != n) { + + delete[] table; + + table = new int[n]; + + for (i = 0; i < n; ++i) { + + m = i; + + for (j = k = 0; j < bits; ++j) { + k = (k << 1) | (m & 1); + m >>= 1; + } + + table[i] = k; + } + + tableSize = n; + } + + if (ii) { + for (i = 0; i < n; ++i) { + ro[table[i]] = ri[i]; + io[table[i]] = ii[i]; + } + } else { + for (i = 0; i < n; ++i) { + ro[table[i]] = ri[i]; + io[table[i]] = 0.0; + } + } + + blockEnd = 1; + + for (blockSize = 2; blockSize <= n; blockSize <<= 1) { + + double delta = angle / (double)blockSize; + double sm2 = -sin(-2 * delta); + double sm1 = -sin(-delta); + double cm2 = cos(-2 * delta); + double cm1 = cos(-delta); + double w = 2 * cm1; + double ar[3], ai[3]; + + for (i = 0; i < n; i += blockSize) { + + ar[2] = cm2; + ar[1] = cm1; + + ai[2] = sm2; + ai[1] = sm1; + + for (j = i, m = 0; m < blockEnd; j++, m++) { + + ar[0] = w * ar[1] - ar[2]; + ar[2] = ar[1]; + ar[1] = ar[0]; + + ai[0] = w * ai[1] - ai[2]; + ai[2] = ai[1]; + ai[1] = ai[0]; + + k = j + blockEnd; + tr = ar[0] * ro[k] - ai[0] * io[k]; + ti = ar[0] * io[k] + ai[0] * ro[k]; + + ro[k] = ro[j] - tr; + io[k] = io[j] - ti; + + ro[j] += tr; + io[j] += ti; + } + } + + blockEnd = blockSize; + } + + if (inverse) { + + double denom = (double)n; + + for (i = 0; i < n; i++) { + ro[i] /= denom; + io[i] /= denom; + } + } +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/host/Processor.h Thu Oct 19 16:53:48 2006 +0000 @@ -0,0 +1,41 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +#ifndef _PROCESSOR_H_ +#define _PROCESSOR_H_ + +#include <QThread> +#include <QString> +#include <QMutex> + +#include "audioio/BufferingAudioCallbackRecordTarget.h" + +#include <vamp-sdk/Plugin.h> + +class Processor : public QThread +{ +public: + Processor(BufferingAudioCallbackRecordTarget *audioRecordTarget); + virtual ~Processor(); + + int addPlugin(QString pluginId); // returns reference number, 0 for failure + void removePlugin(int number); + +protected: + virtual void run(); + void runPlugins(); + + bool m_exiting; + BufferingAudioCallbackRecordTarget *m_audioRecordTarget; + + typedef std::map<int, Vamp::Plugin *> PluginMap; + PluginMap m_plugins; + int m_nextNumber; + + //!!! sort out sensible use of block size and increment + size_t m_pluginBlockSize; + + QMutex m_mutex; +}; + +#endif +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/host/host.cpp Thu Oct 19 16:53:48 2006 +0000 @@ -0,0 +1,62 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +#include <vamp-sdk/PluginHostAdapter.h> + +#include "audioio/BufferingAudioCallbackRecordTarget.h" +#include "audioio/AudioRecordSourceFactory.h" + +#include <iostream> +#include <sndfile.h> + +#include "system/System.h" + +#include "Processor.h" + +#include <cmath> + +#include <QApplication> +#include <QFont> +#include <QPushButton> + +using std::cout; +using std::cerr; +using std::endl; +using std::string; +using std::vector; + + +int main(int argc, char **argv) +{ + QApplication application(argc, argv); + + QFont fn = application.font(); + fn.setPointSize(fn.pointSize() + 3); + application.setFont(fn); + + QPushButton *button = new QPushButton("Quit"); + QObject::connect(button, SIGNAL(clicked()), + &application, SLOT(closeAllWindows())); + + BufferingAudioCallbackRecordTarget *target = + new BufferingAudioCallbackRecordTarget(); + + AudioCallbackRecordSource *source = + AudioRecordSourceFactory::createCallbackRecordSource(target); + if (!source) { + std::cerr << "ERROR: Failed to create audio record source" << std::endl; + return 1; + } + + Processor processor(target); + processor.start(); + +// int n = processor.addPlugin("vamp:vamp-aubio:aubiotempo"); + int n = processor.addPlugin("vamp:vamp-example-plugins:percussiononsets"); +// int n = processor.addPlugin("vamp:vamp-example-plugins:spectralcentroid"); + std::cerr << "Plugin loaded successfully (number: " << n << ")" << std::endl; + + button->show(); + + application.exec(); +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vamp-live-host.pro Thu Oct 19 16:53:48 2006 +0000 @@ -0,0 +1,32 @@ + +TEMPLATE = app +TARGET = +DEPENDPATH += . + +INCLUDEPATH += . ../sonic-visualiser +LIBPATH += ../sonic-visualiser/plugin ../sonic-visualiser/base ../sonic-visualiser/system + +CONFIG += release + +DEFINES += HAVE_JACK HAVE_PORTAUDIO + +LIBS += -lsvplugin -lsvbase -lsvsystem -lportaudio -ljack -lasound -lvamp-sdk + +OBJECTS_DIR = tmp_obj +MOC_DIR = tmp_moc + +# Input +HEADERS += audioio/AudioCallbackRecordSource.h \ + audioio/AudioCallbackRecordTarget.h \ + audioio/AudioJACKSource.h \ + audioio/AudioPortAudioSource.h \ + audioio/AudioRecordSourceFactory.h \ + audioio/BufferingAudioCallbackRecordTarget.h \ + host/Processor.h +SOURCES += audioio/AudioCallbackRecordSource.cpp \ + audioio/AudioJACKSource.cpp \ + audioio/AudioPortAudioSource.cpp \ + audioio/AudioRecordSourceFactory.cpp \ + audioio/BufferingAudioCallbackRecordTarget.cpp \ + host/host.cpp \ + host/Processor.cpp