Chris@0: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ Chris@0: Chris@0: /* Chris@0: Sonic Visualiser Chris@0: An audio file viewer and annotation editor. Chris@0: Centre for Digital Music, Queen Mary, University of London. Chris@0: This file copyright 2006 Chris Cannam. Chris@0: Chris@0: This program is free software; you can redistribute it and/or Chris@0: modify it under the terms of the GNU General Public License as Chris@0: published by the Free Software Foundation; either version 2 of the Chris@0: License, or (at your option) any later version. See the file Chris@0: COPYING included with this distribution for more information. Chris@0: */ Chris@0: Chris@0: #ifdef HAVE_JACK Chris@0: Chris@0: #include "AudioJACKTarget.h" Chris@0: #include "AudioCallbackPlaySource.h" Chris@0: Chris@0: #include Chris@0: #include Chris@0: Chris@0: //#define DEBUG_AUDIO_JACK_TARGET 1 Chris@0: Chris@0: AudioJACKTarget::AudioJACKTarget(AudioCallbackPlaySource *source) : Chris@0: AudioCallbackPlayTarget(source), Chris@0: m_client(0), Chris@0: m_bufferSize(0), Chris@0: m_sampleRate(0) Chris@0: { Chris@0: char name[20]; Chris@0: strcpy(name, "Sonic Visualiser"); Chris@0: m_client = jack_client_new(name); Chris@0: Chris@0: if (!m_client) { Chris@0: sprintf(name, "Sonic Visualiser (%d)", (int)getpid()); Chris@0: m_client = jack_client_new(name); Chris@0: if (!m_client) { Chris@0: std::cerr Chris@0: << "ERROR: AudioJACKTarget: Failed to connect to JACK server" Chris@0: << std::endl; Chris@0: } Chris@0: } Chris@0: Chris@0: if (!m_client) return; Chris@0: Chris@0: m_bufferSize = jack_get_buffer_size(m_client); Chris@0: m_sampleRate = jack_get_sample_rate(m_client); Chris@0: Chris@0: jack_set_process_callback(m_client, processStatic, this); Chris@0: Chris@0: if (jack_activate(m_client)) { Chris@0: std::cerr << "ERROR: AudioJACKTarget: Failed to activate JACK client" Chris@0: << std::endl; Chris@0: } Chris@0: Chris@0: if (m_source) { Chris@0: sourceModelReplaced(); Chris@0: } Chris@0: } Chris@0: Chris@0: AudioJACKTarget::~AudioJACKTarget() Chris@0: { Chris@0: if (m_client) { Chris@0: jack_deactivate(m_client); Chris@0: jack_client_close(m_client); Chris@0: } Chris@0: } Chris@0: Chris@0: bool Chris@0: AudioJACKTarget::isOK() const Chris@0: { Chris@0: return (m_client != 0); Chris@0: } Chris@0: Chris@0: int Chris@0: AudioJACKTarget::processStatic(jack_nframes_t nframes, void *arg) Chris@0: { Chris@0: return ((AudioJACKTarget *)arg)->process(nframes); Chris@0: } Chris@0: Chris@0: void Chris@0: AudioJACKTarget::sourceModelReplaced() Chris@0: { Chris@0: m_mutex.lock(); Chris@0: Chris@0: m_source->setTargetBlockSize(m_bufferSize); Chris@0: m_source->setTargetSampleRate(m_sampleRate); Chris@0: Chris@0: size_t channels = m_source->getSourceChannelCount(); Chris@0: Chris@0: // Because we offer pan, we always want at least 2 channels Chris@0: if (channels < 2) channels = 2; Chris@0: Chris@0: if (channels == m_outputs.size() || !m_client) { Chris@0: m_mutex.unlock(); Chris@0: return; Chris@0: } Chris@0: Chris@0: const char **ports = Chris@0: jack_get_ports(m_client, NULL, NULL, Chris@0: JackPortIsPhysical | JackPortIsInput); Chris@0: size_t physicalPortCount = 0; Chris@0: while (ports[physicalPortCount]) ++physicalPortCount; Chris@0: Chris@0: #ifdef DEBUG_AUDIO_JACK_TARGET Chris@0: std::cerr << "AudioJACKTarget::sourceModelReplaced: have " << channels << " channels and " << physicalPortCount << " physical ports" << std::endl; Chris@0: #endif Chris@0: Chris@0: while (m_outputs.size() < channels) { Chris@0: Chris@0: char name[20]; Chris@0: jack_port_t *port; Chris@0: Chris@0: sprintf(name, "out %d", m_outputs.size() + 1); Chris@0: Chris@0: port = jack_port_register(m_client, Chris@0: name, Chris@0: JACK_DEFAULT_AUDIO_TYPE, Chris@0: JackPortIsOutput, Chris@0: 0); Chris@0: Chris@0: if (!port) { Chris@0: std::cerr Chris@0: << "ERROR: AudioJACKTarget: Failed to create JACK output port " Chris@0: << m_outputs.size() << std::endl; Chris@0: } else { Chris@0: m_source->setTargetPlayLatency(jack_port_get_latency(port)); Chris@0: } Chris@0: Chris@0: if (m_outputs.size() < physicalPortCount) { Chris@0: jack_connect(m_client, jack_port_name(port), ports[m_outputs.size()]); Chris@0: } Chris@0: Chris@0: m_outputs.push_back(port); Chris@0: } Chris@0: Chris@0: while (m_outputs.size() > channels) { Chris@0: std::vector::iterator itr = m_outputs.end(); Chris@0: --itr; Chris@0: jack_port_t *port = *itr; Chris@0: if (port) jack_port_unregister(m_client, port); Chris@0: m_outputs.erase(itr); Chris@0: } Chris@0: Chris@0: m_mutex.unlock(); Chris@0: } Chris@0: Chris@0: int Chris@0: AudioJACKTarget::process(jack_nframes_t nframes) Chris@0: { Chris@0: if (!m_mutex.tryLock()) { Chris@0: return 0; Chris@0: } Chris@0: Chris@0: if (m_outputs.empty()) { Chris@0: m_mutex.unlock(); Chris@0: return 0; Chris@0: } Chris@0: Chris@0: #ifdef DEBUG_AUDIO_JACK_TARGET Chris@0: std::cout << "AudioJACKTarget::process(" << nframes << "): have a source" << std::endl; Chris@0: #endif Chris@0: Chris@0: #ifdef DEBUG_AUDIO_JACK_TARGET Chris@0: if (m_bufferSize != nframes) { Chris@0: std::cerr << "WARNING: m_bufferSize != nframes (" << m_bufferSize << " != " << nframes << ")" << std::endl; Chris@0: } Chris@0: #endif Chris@0: Chris@0: float **buffers = (float **)alloca(m_outputs.size() * sizeof(float *)); Chris@0: Chris@0: for (size_t ch = 0; ch < m_outputs.size(); ++ch) { Chris@0: buffers[ch] = (float *)jack_port_get_buffer(m_outputs[ch], nframes); Chris@0: } Chris@0: Chris@0: if (m_source) { Chris@0: m_source->getSourceSamples(nframes, buffers); Chris@0: } else { Chris@0: for (size_t ch = 0; ch < m_outputs.size(); ++ch) { Chris@0: for (size_t i = 0; i < nframes; ++i) { Chris@0: buffers[ch][i] = 0.0; Chris@0: } Chris@0: } Chris@0: } Chris@0: Chris@0: float peakLeft = 0.0, peakRight = 0.0; Chris@0: Chris@0: for (size_t ch = 0; ch < m_outputs.size(); ++ch) { Chris@0: Chris@0: float peak = 0.0; Chris@0: Chris@0: for (size_t i = 0; i < nframes; ++i) { Chris@0: buffers[ch][i] *= m_outputGain; Chris@0: float sample = fabsf(buffers[ch][i]); Chris@0: if (sample > peak) peak = sample; Chris@0: } Chris@0: Chris@0: if (ch == 0) peakLeft = peak; Chris@0: if (ch > 0 || m_outputs.size() == 1) peakRight = peak; Chris@0: } Chris@0: Chris@0: if (m_source) { Chris@0: m_source->setOutputLevels(peakLeft, peakRight); Chris@0: } Chris@0: Chris@0: m_mutex.unlock(); Chris@0: return 0; Chris@0: } Chris@0: Chris@0: Chris@0: #ifdef INCLUDE_MOCFILES Chris@0: #include "AudioJACKTarget.moc.cpp" Chris@0: #endif Chris@0: Chris@0: #endif /* HAVE_JACK */ Chris@0: