/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ /* Rosegarden A MIDI and audio sequencer and musical notation editor. Copyright 2000-2009 the Rosegarden development team. Other copyrights also apply to some parts of this work. Please see the AUTHORS file and individual file headers for details. 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. */ #include "JackCaptureClient.h" #include #define DEBUG_JACK_CAPTURE_CLIENT 0 namespace Rosegarden { /************************************* CONSTRUCTOR, DESTRUCTOR, AND INIT *************************************/ JackCaptureClient::JackCaptureClient( const char *captureClientName, int fs ) : m_isConnected(false), m_processing(false), m_jackRingBuffer(NULL), m_frameSize(fs) { // Try to connect to Jack server if ( (client = jack_client_new(captureClientName)) == 0 ) { return; } #if DEBUG_JACK_CAPTURE_CLIENT std::cout << "Registered as Jack client" << std::endl; #endif // get stream info m_jackSampleRate = jack_get_sample_rate( client ); m_jackBufferSize = jack_get_buffer_size( client ); m_jackSampleSize = sizeof(jack_default_audio_sample_t); #if DEBUG_JACK_CAPTURE_CLIENT std::cout << "Sample Rate " << m_jackSampleRate << std::endl; std::cout << "Max buffer size " << m_jackBufferSize << std::endl; std::cout << "Sample size (bytes) " << m_jackSampleSize << std::endl; #endif //setup ringbuffer setFrameSize(m_frameSize); //register process and shutdown calls jack_set_process_callback(client, &process, (void*)this); jack_on_shutdown(client, jackShutdown, (void*)this); #if DEBUG_JACK_CAPTURE_CLIENT std::cout << "Process and shutdown calls registered\n"; #endif // Activate if ( jack_activate(client) ) { std::cout << "Can't activate client\n"; throw("Cannot activate client"); } #if DEBUG_JACK_CAPTURE_CLIENT std::cout << "Activated\n"; #endif //set default port to the first available port const char **ports = getPorts(); setupPorts(ports[0], captureClientName); m_isConnected = true; } JackCaptureClient::~JackCaptureClient() { stopProcessing(); jack_client_close(client); if (m_jackRingBuffer) jack_ringbuffer_free(m_jackRingBuffer); } void JackCaptureClient::setFrameSize(int nextFrameSize) { m_frameSize = nextFrameSize + 1; #if DEBUG_JACK_CAPTURE_CLIENT std::cout << "CaptureClient: setting framesize to at least " << m_frameSize << std::endl; #endif if (m_processing) { std::cout << "CaptureClient: Procesing, can't change framesize" << std::endl; return; } if (m_jackRingBuffer) jack_ringbuffer_free(m_jackRingBuffer); // framesize must be larger than size of max buffer size (m_jackBufferSize) // else a complete jack frame can never be written to ringbuffer if ( m_frameSize < m_jackBufferSize ) m_frameSize = m_jackBufferSize+1; size_t m_jackRingBufferSize = m_jackSampleSize * m_frameSize; m_jackRingBuffer = jack_ringbuffer_create( m_jackRingBufferSize ); jack_ringbuffer_reset( m_jackRingBuffer ); #if DEBUG_JACK_CAPTURE_CLIENT std::cout << "Created ringbuffer, write space: " << jack_ringbuffer_write_space(m_jackRingBuffer) << "read space: " << jack_ringbuffer_read_space(m_jackRingBuffer) << " m_jackSampleSize " << m_jackSampleSize << std::endl;; #endif } const char **JackCaptureClient::getPorts() { return jack_get_ports( client, NULL, NULL, JackPortIsOutput ); } const char* JackCaptureClient::getCapturePortName() { return jack_port_name( m_capturePort); } void JackCaptureClient::setupPorts(const char *portName, const char *captureClientName) { #if DEBUG_JACK_CAPTURE_CLIENT std::cout << "Connecting ports...\n"; #endif // register port std::string inPortName = captureClientName; inPortName.append(" In"); #if DEBUG_JACK_CAPTURE_CLIENT std::cout << "Registering input port as:" << inPortName << std::endl; #endif inPort = jack_port_register(client, inPortName.c_str(), JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0 ); if (inPort == 0) { std::cout << "Cannot open Jack port"; } if ( jack_port_connected(inPort) ) { #if DEBUG_JACK_CAPTURE_CLIENT std::cout << "Disconnecting ports\n"; #endif const char **connectedPorts = jack_port_get_connections(inPort); int i=0; while (connectedPorts[i] != NULL) { #if DEBUG_JACK_CAPTURE_CLIENT std::cout << "disconnecting from " << connectedPorts[i] << std::endl; #endif jack_port_disconnect(client, inPort); i++; } } m_capturePort = jack_port_by_name(client, portName); #if DEBUG_JACK_CAPTURE_CLIENT std::cout << std::endl << "Recording from " << jack_port_name(m_capturePort) << std::endl; #endif // Connect ports if (jack_connect( client, portName, jack_port_name(inPort) ) < 0 ) { std::cout << "------------------------------" << std::endl << "Jack Client: cant connect port" << std::endl << "------------------------------" << std::endl << std::endl; } #if DEBUG_JACK_CAPTURE_CLIENT std::cout << "Port connected" << std::endl; #endif } /********************************* REALTIME OPERATIONS ********************************/ int JackCaptureClient::process(jack_nframes_t nframes, void *arg) { JackCaptureClient *jcc = (JackCaptureClient*)arg; if ( !jcc->m_processing ) { return 0; } jack_default_audio_sample_t *inSamp = (jack_default_audio_sample_t *) jack_port_get_buffer( jcc->m_capturePort, nframes); //check space to write uint writeSpace = jack_ringbuffer_write_space( jcc->m_jackRingBuffer ); int writeSamples = writeSpace / jcc->m_jackSampleSize; #if DEBUG_JACK_CAPTURE_CLIENT std::cout << "Want to write " << nframes << "frames\t" << writeSamples << " available\n"; #endif if ( writeSpace < (jcc->m_jackSampleSize * nframes) ) { unsigned int advance = nframes - writeSamples; unsigned int advanceBytes = advance * jcc->m_jackSampleSize; jack_ringbuffer_read_advance( jcc->m_jackRingBuffer, advanceBytes ); #if DEBUG_JACK_CAPTURE_CLIENT std::cout << "Advancing read pointer " << advance << "frames, " << advanceBytes << " bytes\n"; writeSpace = jack_ringbuffer_write_space( jcc->m_jackRingBuffer ); writeSamples = writeSpace / jcc->m_jackSampleSize; std::cout << writeSamples << " frames now available\n"; #endif } size_t written = jack_ringbuffer_write(jcc->m_jackRingBuffer, (char*)(inSamp), jcc->m_jackSampleSize * nframes); #if DEBUG_JACK_CAPTURE_CLIENT std::cout << "I've written " << written / jcc->m_jackSampleSize << " frames\n"; #else (void) written; // stops warning about unused variable #endif #if DEBUG_JACK_CAPTURE_CLIENT uint readSpace = jack_ringbuffer_read_space( jcc->m_jackRingBuffer ); uint readSamples = readSpace / jcc->m_jackSampleSize; std::cout << "Now " << readSamples << " samples available to read\n"; std::cout << std::endl; #endif return 0; } /********************************* CONTROL REALTIME OPERATIONS ********************************/ void JackCaptureClient::startProcessing() { if (m_isConnected) { m_processing = true; } } void JackCaptureClient::stopProcessing() { m_processing = false; } // Method will be called if Jack shuts process down void JackCaptureClient::jackShutdown(void *arg) { #if DEBUG_JACK_CAPTURE_CLIENT // JackCaptureClient *jcc = (JackCaptureClient*)arg; std::cout << "Shutdown by Jack!!!!!!!!!" << std::endl; #endif } /********************************* GET SAMPLE DATA ********************************/ bool JackCaptureClient::getFrame(float *frame, size_t captureSize) { size_t availableSize = jack_ringbuffer_read_space(m_jackRingBuffer) / m_jackSampleSize; // ensure a full chunk is available & processing is allowed if (captureSize <= availableSize) { size_t read = jack_ringbuffer_read(m_jackRingBuffer, (char*)(frame), (sizeof(float)*captureSize) ); (void) read; // stops warning about unused variable #if DEBUG_JACK_CAPTURE_CLIENT std::cout << "JackCaptureClient::getFrame - Got frame!\n"; #endif return true; } else { #if DEBUG_JACK_CAPTURE_CLIENT std::cout << "JackCaptureClient::getFrame - " << availableSize << " samples available. " << captureSize << " samples wanted" << std::endl; #endif return false; } } } // end namespace