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