Mercurial > hg > sonic-visualiser
diff audioio/AudioJACKTarget.cpp @ 0:cd5d7ff8ef38
* Reorganising code base. This revision will not compile.
author | Chris Cannam |
---|---|
date | Mon, 31 Jul 2006 12:03:45 +0000 |
parents | |
children | 42a78f0e8fe2 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/audioio/AudioJACKTarget.cpp Mon Jul 31 12:03:45 2006 +0000 @@ -0,0 +1,218 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Sonic Visualiser + An audio file viewer and annotation editor. + Centre for Digital Music, Queen Mary, University of London. + This file copyright 2006 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. +*/ + +#ifdef HAVE_JACK + +#include "AudioJACKTarget.h" +#include "AudioCallbackPlaySource.h" + +#include <iostream> +#include <cmath> + +//#define DEBUG_AUDIO_JACK_TARGET 1 + +AudioJACKTarget::AudioJACKTarget(AudioCallbackPlaySource *source) : + AudioCallbackPlayTarget(source), + m_client(0), + m_bufferSize(0), + m_sampleRate(0) +{ + char name[20]; + strcpy(name, "Sonic Visualiser"); + m_client = jack_client_new(name); + + if (!m_client) { + sprintf(name, "Sonic Visualiser (%d)", (int)getpid()); + m_client = jack_client_new(name); + if (!m_client) { + std::cerr + << "ERROR: AudioJACKTarget: 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_process_callback(m_client, processStatic, this); + + if (jack_activate(m_client)) { + std::cerr << "ERROR: AudioJACKTarget: Failed to activate JACK client" + << std::endl; + } + + if (m_source) { + sourceModelReplaced(); + } +} + +AudioJACKTarget::~AudioJACKTarget() +{ + if (m_client) { + jack_deactivate(m_client); + jack_client_close(m_client); + } +} + +bool +AudioJACKTarget::isOK() const +{ + return (m_client != 0); +} + +int +AudioJACKTarget::processStatic(jack_nframes_t nframes, void *arg) +{ + return ((AudioJACKTarget *)arg)->process(nframes); +} + +void +AudioJACKTarget::sourceModelReplaced() +{ + m_mutex.lock(); + + m_source->setTargetBlockSize(m_bufferSize); + m_source->setTargetSampleRate(m_sampleRate); + + size_t channels = m_source->getSourceChannelCount(); + + // Because we offer pan, we always want at least 2 channels + if (channels < 2) channels = 2; + + if (channels == m_outputs.size() || !m_client) { + m_mutex.unlock(); + return; + } + + const char **ports = + jack_get_ports(m_client, NULL, NULL, + JackPortIsPhysical | JackPortIsInput); + size_t physicalPortCount = 0; + while (ports[physicalPortCount]) ++physicalPortCount; + +#ifdef DEBUG_AUDIO_JACK_TARGET + std::cerr << "AudioJACKTarget::sourceModelReplaced: have " << channels << " channels and " << physicalPortCount << " physical ports" << std::endl; +#endif + + while (m_outputs.size() < channels) { + + char name[20]; + jack_port_t *port; + + sprintf(name, "out %d", m_outputs.size() + 1); + + port = jack_port_register(m_client, + name, + JACK_DEFAULT_AUDIO_TYPE, + JackPortIsOutput, + 0); + + if (!port) { + std::cerr + << "ERROR: AudioJACKTarget: Failed to create JACK output port " + << m_outputs.size() << std::endl; + } else { + m_source->setTargetPlayLatency(jack_port_get_latency(port)); + } + + if (m_outputs.size() < physicalPortCount) { + jack_connect(m_client, jack_port_name(port), ports[m_outputs.size()]); + } + + m_outputs.push_back(port); + } + + while (m_outputs.size() > channels) { + std::vector<jack_port_t *>::iterator itr = m_outputs.end(); + --itr; + jack_port_t *port = *itr; + if (port) jack_port_unregister(m_client, port); + m_outputs.erase(itr); + } + + m_mutex.unlock(); +} + +int +AudioJACKTarget::process(jack_nframes_t nframes) +{ + if (!m_mutex.tryLock()) { + return 0; + } + + if (m_outputs.empty()) { + m_mutex.unlock(); + return 0; + } + +#ifdef DEBUG_AUDIO_JACK_TARGET + std::cout << "AudioJACKTarget::process(" << nframes << "): have a source" << std::endl; +#endif + +#ifdef DEBUG_AUDIO_JACK_TARGET + if (m_bufferSize != nframes) { + std::cerr << "WARNING: m_bufferSize != nframes (" << m_bufferSize << " != " << nframes << ")" << std::endl; + } +#endif + + float **buffers = (float **)alloca(m_outputs.size() * sizeof(float *)); + + for (size_t ch = 0; ch < m_outputs.size(); ++ch) { + buffers[ch] = (float *)jack_port_get_buffer(m_outputs[ch], nframes); + } + + if (m_source) { + m_source->getSourceSamples(nframes, buffers); + } else { + for (size_t ch = 0; ch < m_outputs.size(); ++ch) { + for (size_t i = 0; i < nframes; ++i) { + buffers[ch][i] = 0.0; + } + } + } + + float peakLeft = 0.0, peakRight = 0.0; + + for (size_t ch = 0; ch < m_outputs.size(); ++ch) { + + float peak = 0.0; + + for (size_t i = 0; i < nframes; ++i) { + buffers[ch][i] *= m_outputGain; + float sample = fabsf(buffers[ch][i]); + if (sample > peak) peak = sample; + } + + if (ch == 0) peakLeft = peak; + if (ch > 0 || m_outputs.size() == 1) peakRight = peak; + } + + if (m_source) { + m_source->setOutputLevels(peakLeft, peakRight); + } + + m_mutex.unlock(); + return 0; +} + + +#ifdef INCLUDE_MOCFILES +#include "AudioJACKTarget.moc.cpp" +#endif + +#endif /* HAVE_JACK */ +