X-Git-Url: http://git.johnwright.org/?p=fmit.git;a=blobdiff_plain;f=src%2FCaptureThreadImplJACK.cpp;fp=src%2FCaptureThreadImplJACK.cpp;h=0e08ab9c7d2a6513a385bfd240e85849b47de5cb;hp=0000000000000000000000000000000000000000;hb=49947d1cd4506f5574b3be62ee90e9f00227d9fd;hpb=82c9faab9421b3d87a0faa84a73f55aaccbbb689 diff --git a/src/CaptureThreadImplJACK.cpp b/src/CaptureThreadImplJACK.cpp new file mode 100644 index 0000000..0e08ab9 --- /dev/null +++ b/src/CaptureThreadImplJACK.cpp @@ -0,0 +1,319 @@ +// Copyright 2004 "Gilles Degottex" + +// This file is part of "Music" + +// "Music" is free software; you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation; either version 2.1 of the License, or +// (at your option) any later version. +// +// "Music" is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +#include "CaptureThread.h" + +#include +#include +#include +#include +#include +#include +#include +#include +using namespace std; + +// ------------------------------ JACK implementation ---------------------------- +#ifdef CAPTURE_JACK + +#define JACK_BUFF_SIZE 1024 + +const size_t g_jack_sample_size = sizeof(jack_default_audio_sample_t); + +CaptureThreadImplJACK::CaptureThreadImplJACK(CaptureThread* capture_thread) +: CaptureThreadImpl(capture_thread, "JACK", "Jack Audio Connection Kit") +{ + m_jack_client = NULL; + m_jack_port = NULL; + m_ringbuffer = NULL; + + m_alive = true; + m_in_run = false; + m_loop = false; +} + +bool CaptureThreadImplJACK::is_available() +{ + if(m_jack_client==NULL) + { + try + { + //m_jack_client = jack_client_new((m_capture_thread->m_name+"_test").latin1()); + m_jack_client = jack_client_open((m_capture_thread->m_name+"_test").toAscii().constData(), JackNoStartServer, NULL); + if(m_jack_client==NULL) + throw QString("unknown reason"); + } + catch(QString error) + { + m_jack_client = NULL; + m_status = "N/A"; + return false; + } + capture_finished(); + } + + m_status = "available"; + + return true; +} + +void CaptureThreadImplJACK::setSamplingRate(int value) +{ + cerr << "CaptureThread: ERROR: JACK: setSamplingRate not available with JACK ! change the JACK server sampling rate instead" << endl; +} + +void CaptureThreadImplJACK::startCapture() +{ + if(!isRunning()) + start(); + + m_loop = true; + + m_wait_for_start = true; + while(m_wait_for_start) // some implementations take a long time to start + msleep(50); +} +void CaptureThreadImplJACK::stopCapture() +{ + m_loop = false; + + while(m_in_run) + msleep(50); +} + +void CaptureThreadImplJACK::JackShutdown(void* arg){((CaptureThreadImplJACK*)arg)->jackShutdown();} +void CaptureThreadImplJACK::jackShutdown() +{ +// cerr << "CaptureThreadImplJACK::jackShutdown" << endl; + + m_jack_client = NULL; + m_jack_port = NULL; + if(m_ringbuffer) + { + jack_ringbuffer_free(m_ringbuffer); + m_ringbuffer = NULL; + } + + m_capture_thread->emitError("JACK: server shutdown !"); +// cerr << "~CaptureThreadImplJACK::jackShutdown" << endl; +} + +int CaptureThreadImplJACK::JackSampleRate(jack_nframes_t nframes, void* arg){return ((CaptureThreadImplJACK*)arg)->jackSampleRate(nframes);} +int CaptureThreadImplJACK::jackSampleRate(jack_nframes_t nframes) +{ + if(m_sampling_rate!=int(nframes)) + { + if(m_ringbuffer) + jack_ringbuffer_free(m_ringbuffer); + m_ringbuffer = jack_ringbuffer_create(g_jack_sample_size*nframes); // one second buffer + + m_sampling_rate = nframes; + m_capture_thread->emitSamplingRateChanged(); + } + + return 0; +} + +int CaptureThreadImplJACK::JackProcess(jack_nframes_t nframes, void* arg){return ((CaptureThreadImplJACK*)arg)->jackProcess(nframes);} +int CaptureThreadImplJACK::jackProcess(jack_nframes_t nframes) +{ +// cerr << "CaptureThreadImplJACK::jackProcess '" << nframes << "'" << endl; + + if(!m_jack_client || !m_jack_port || !m_ringbuffer) return 0; + if(m_capture_thread->m_pause || !m_capture_thread->m_capturing || nframes<=0) return 0; + + void* pin = jack_port_get_buffer(m_jack_port, nframes); + + if(!pin) return 0; + + jack_default_audio_sample_t* in = (jack_default_audio_sample_t*)pin; + + int characters_written = jack_ringbuffer_write(m_ringbuffer, (const char *) ((void *) (in)), g_jack_sample_size*nframes); + if (characters_written < g_jack_sample_size*nframes) + cerr << "CaptureThreadImplJACK::jackProcess Can not write all frames: ringbuffer full?" << endl; + +// int toread = jack_ringbuffer_read_space(m_ringbuffer); +// cerr << "CaptureThreadImplJACK::jackProcess " << characters_written/g_jack_sample_size << " values written, " << toread/g_jack_sample_size << " to read" << endl; + +// cerr << "~CaptureThreadImplJACK::jackProcess" << endl; + + return 0; +} + +void CaptureThreadImplJACK::capture_init() +{ + m_jack_client = jack_client_open(m_capture_thread->m_name.toAscii().constData(), JackNoStartServer, NULL); + if(!m_jack_client) + throw QString("JACK: cannot create client, JACK deamon is running ?"); + + jack_set_process_callback(m_jack_client, JackProcess, (void*)this); + jack_on_shutdown(m_jack_client, JackShutdown, (void*)this); + jack_set_sample_rate_callback(m_jack_client, JackSampleRate, (void*)this); + + cerr << "CaptureThread: INFO: JACK: sampling rate=" << jack_get_sample_rate(m_jack_client) << endl; + m_ringbuffer = jack_ringbuffer_create(g_jack_sample_size*jack_get_sample_rate(m_jack_client)); // one second buffer + + int err=0; + if((err=jack_activate(m_jack_client))!=0) + throw QString("JACK: cannot activate client"); + + setFormatDescrsAndFns(g_jack_sample_size, true, true, 1); + + m_jack_port = jack_port_register(m_jack_client, "input", JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput,0); + + if(m_source!="") + { + QString dest = QString(jack_get_client_name(m_jack_client))+":input"; + cerr << "CaptureThread: INFO: JACK: auto-connect '" << m_source.toStdString() << "' with '" << dest.toStdString() << "'" << endl; + if((err=jack_connect(m_jack_client, m_source.toAscii().constData(), dest.toAscii().constData()))!=0) + m_capture_thread->emitError(QString("JACK: Invalid source '")+m_source+"'"); + } + + int old_sampling_rate = m_sampling_rate; + m_sampling_rate = jack_get_sample_rate(m_jack_client); + if(m_sampling_rate!=old_sampling_rate) + m_capture_thread->emitSamplingRateChanged(); + + m_capture_thread->m_capturing = true; + m_capture_thread->emitCaptureStarted(); + m_capture_thread->emitCaptureToggled(true); +} +void CaptureThreadImplJACK::capture_loop() +{ +// cerr << "CaptureThreadImplJACK::capture_loop " << g_jack_sample_size << endl; + + char dest[JACK_BUFF_SIZE*g_jack_sample_size]; + + m_wait_for_start = false; + while(m_loop && m_jack_client && m_jack_port && m_ringbuffer) + { +// cerr << "CaptureThreadImplJACK::capture_loop toread=" << toread/g_jack_sample_size << endl; + int read = jack_ringbuffer_read(m_ringbuffer, dest, JACK_BUFF_SIZE*g_jack_sample_size); + if(read>0) + { + if(!m_capture_thread->m_pause) + { +// cerr << "CaptureThreadImplJACK::capture_loop locking" << endl; + m_capture_thread->m_lock.lock(); + +// int toread = jack_ringbuffer_read_space(m_ringbuffer); +// cerr << "CaptureThreadImplJACK::capture_loop in deque='" << m_capture_thread->m_values.size() << "' still toread=" << toread/g_jack_sample_size << endl; + + if(g_jack_sample_size==4) + for(int i=0; i<(read/g_jack_sample_size); i++) + addValue(this, (double)(((float*)dest)[i]), i); + else if(g_jack_sample_size==8) + for(int i=0; i<(read/g_jack_sample_size); i++) + addValue(this, (double)(((double*)dest)[i]), i); + + m_capture_thread->m_packet_size = (read/g_jack_sample_size); + if(m_capture_thread->m_ext_lock) + { + m_capture_thread->m_packet_size_sll = 0; + m_capture_thread->m_ext_lock = false; + } + m_capture_thread->m_packet_size_sll += (read/g_jack_sample_size); + + m_capture_thread->m_lock.unlock(); +// cerr << "CaptureThreadImplJACK::capture_loop unlocked" << endl; + } + } + else + msleep(10); + } + +// cerr << "~CaptureThreadImplJACK::capture_loop" << endl; +} +void CaptureThreadImplJACK::capture_finished() +{ +// cerr << "CaptureThreadImplJACK::capture_finished" << endl; + + if(m_jack_client!=NULL) + { + jack_client_close(m_jack_client); + m_jack_client = NULL; + if(m_ringbuffer) + { + jack_ringbuffer_free(m_ringbuffer); + m_ringbuffer = NULL; + } + + m_capture_thread->m_capturing = false; + m_capture_thread->emitCaptureStoped(); + m_capture_thread->emitCaptureToggled(false); + } +} +void CaptureThreadImplJACK::run() +{ +// cerr << "CaptureThread: INFO: JACK: capture thread entered" << endl; + +// while(m_alive) // TODO need to keep alsa thread alive to let PortAudio working after ALSA !! TODO same for JACK ??? + { + while(m_alive && !m_loop) + msleep(50); + + m_in_run = true; + + try + { +// cerr << "CaptureThread: INFO: JACK: capture thread running" << endl; + + capture_init(); + + m_capture_thread->m_capturing = true; + m_capture_thread->emitCaptureStarted(); + m_capture_thread->emitCaptureToggled(true); + + capture_loop(); + + m_capture_thread->m_capturing = false; + m_capture_thread->emitCaptureStoped(); + m_capture_thread->emitCaptureToggled(false); + } + catch(QString error) + { + m_loop = false; + cerr << "CaptureThread: ERROR: " << error.toStdString() << endl; + m_capture_thread->emitError(error); + } + m_wait_for_start = false; + + capture_finished(); + + m_in_run = false; + +// cerr << "CaptureThread: INFO: JACK: capture thread stop running" << endl; + } + +// cerr << "CaptureThread: INFO: JACK: capture thread exited" << endl; +} +CaptureThreadImplJACK::~CaptureThreadImplJACK() +{ +// cerr << "CaptureThreadImplJACK::~CaptureThreadImplJACK" << endl; + + m_alive = false; + + stopCapture(); + + while(isRunning()) + msleep(50); + +// cerr << "~CaptureThreadImplJACK::~CaptureThreadImplJACK" << endl; +} +#endif