After a bit of discussion with Michael off-list, it seems that it would
be easier to split up into separate patches.
PATCH 1:
+ src/sound/PitchTracker .h .cpp
+ src/sound/JackCaptureClient .h .cpp
// no other files modified
Since we haven't modified any GUI files (or indeed, any current
rosegarden files), you won't see anything different when you compile
Rosegarden. However, getting these files merged now will make the later
diffs easier to understand.
No time pressure, so please feel free to take your time, and feel free
to nitpick the style.
I have svn access, so I can commit these changes myself; I just need
people to say that it's ok to do so.
Cheers,
- Graham
Index: src/sound/PitchDetector.h
===================================================================
--- src/sound/PitchDetector.h (revision 0)
+++ src/sound/PitchDetector.h (revision 0)
@@ -0,0 +1,148 @@
+/* -*- 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.
+*/
+
+#ifndef PITCHTRACKER_H
+#define PITCHTRACKER_H
+
+
+#include <math.h>
+#include <complex>
+#include <fftw3.h>
+#include <fstream>
+
+#include <QString>
+#include <QVector>
+
+#define MIN_THRESHOLD 1
+
+namespace Rosegarden
+{
+
+
+//!!! I don't understand much of this class, so I've only updated the
+//!!! member variables. I don't vouch for it meeting your code standards. -gp
+
+// We have made a design decision to use QString/QVector now rather than
+// the equivalent types in std::. Originally it was a pain to use the Qt
+// types; rather we'd link a library of microtonal stuff, and it would
+// mean that our library had to depend on Qt classes. But RG seems to be
+// moving towards a minimum dependence culture, and there are no libraries
+// built from the source tree, so let's go with the Qt classes now. - njb
+
+
+/**
+* \addtogroup Codicil
+* \@{
+* \brief Stores audio data from jack into a ring buffer
+ *
+ * This is part of the Glasgow Center for Music Technology's
+ * "Rosegarden Codicil" project.
+ * http://www.n-ism.org/Projects/microtonalism.php
+ *
+ * The most recent audio samples are read into a buffer. Unlike most audio
+ * applications, it is more important to reduce latency by dropping samples
+ * than to eventually process all of them. This means the estimated pitch
+ * is as up-to-date as possible.
+ *
+ * The reference to "frame" in the cotext of a PitchDector
+ * means an analysis frame (e.g. 1024 samples)
+ * and not the more usual usage of the number of samples presented
+ * simultaneously (1=mono, 2=stereo etc).
+ *
+ * \author Doug McGilvray (original author)
+ * \author Graham Percival (slightly rewritten to match Rosegarden standards)
+ * \author Nick Bailey (changed to use QSting, QVector instead of std::...)
+ * \date 2004, rewrite 2009, 2010
+ *
+ */
+class PitchDetector {
+
+public:
+
+ typedef QString Method; // was std::string.
+
+ static const double NOSIGNAL; /**< No input signal available */
+ static const double NONE; /**< No pitch (e.g. in a rest ) */
+ static const Method PARTIAL; /**< Use lowest freq max */
+ static const Method AUTOCORRELATION; /**< Use autocorrelation delay */
+ static const Method HPS; /**< Use Harmonic Prd Spectrum */
+ // The following not yet implemented
+ //static const Method AMDF;
+ //static const Method CEPSTRUM;
+
+ /**
+ * Analysis frame size
+ * A sensible default size of analysis buffer for all methods
+ */
+ static const int defaultFrameSize;
+
+ /**
+ * Analysis buffer overlap
+ * Frequency resolution may be increased by considering the phase
+ * drift between overlapping analysis frames. This is a sensible
+ * default size for the overlap for all methods
+ */
+ static const int defaultStepSize;
+
+ PitchDetector( int frameSize, int stepSize, int sampleRate );
+ virtual ~PitchDetector();
+
+ float *getInBuffer(); /**< Get audio data buffer ref */
+ double getPitch(); /**< Get pitch; use current method */
+
+ int getFrameSize() const; /**< Get current audio buf size */
+ void setFrameSize( int frameSize ); /**< Set current audio buf size */
+ int getStepSize() const; /**< Get no. samples between anals */
+ void setStepSize( int stepSize ); /**< Set no, samples between anals */
+ int getBufferSize() const; /**< Get size of audio buffer */
+
+ void setMethod( Method Method );
+ Method getCurrentMethod() {
+ return m_method;
+ }
+ static const QVector<Method> *getMethods(); // was std::vector
+
+private:
+
+ float *m_frame;
+ double partial();
+ double amdf();
+ double autocorrelation();
+ double hps();
+ double unwrapPhase( int fBin );
+
+ class MethodVector : public QVector<Method> {
+ public:
+ MethodVector();
+ };
+
+ static const MethodVector m_methods; // was std::vector
+
+ float *m_cepstralIn, *m_in1, *m_in2;
+ int m_frameSize;
+ int m_stepSize;
+ int m_sampleRate;
+
+ Method m_method;
+ fftwf_complex *m_ft1, *m_ft2, *m_cepstralOut;
+ fftwf_plan m_p1, m_p2, m_pc;
+
+};
+
+/** \@} */
+}
+#endif
Index: src/sound/JackCaptureClient.h
===================================================================
--- src/sound/JackCaptureClient.h (revision 0)
+++ src/sound/JackCaptureClient.h (revision 0)
@@ -0,0 +1,89 @@
+/* -*- 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.
+*/
+
+#ifndef JACKTRACKER_H
+#define JACKTRACKER_H
+
+#include <jack/jack.h>
+#include <jack/ringbuffer.h>
+
+namespace Rosegarden
+{
+
+
+/**
+ * \brief Stores audio data from jack into a ring buffer
+ *
+ * This is part of the Glasgow Center for Music Technology's
+ * "Rosegarden Codicil" project.
+ * http://www.n-ism.org/Projects/microtonalism.php
+ *
+ * \author Doug McGilvray (original author)
+ * \author Graham Percival (extensively rewritten to match Rosegarden standards)
+ * \date 2004, rewrite 2009
+ *
+ */
+class JackCaptureClient {
+public:
+ JackCaptureClient(const char *captureClientName, int fs);
+ ~JackCaptureClient();
+
+ // control processing
+ void startProcessing();
+ void stopProcessing();
+
+ // getting info
+ bool getFrame( float *frame, size_t captureSize );
+ bool isConnected() {
+ return m_isConnected;
+ }
+ jack_nframes_t getSampleRate() {
+ return m_jackSampleRate;
+ }
+
+protected:
+ // basic setup + init
+ void setFrameSize(int nextFrameSize);
+ const char **getPorts();
+ const char* getCapturePortName();
+ void setupPorts(const char *portName, const char *captureClientName);
+
+ // control processing
+ static void jackShutdown(void *arg);
+
+ // actual realtime portion
+ static int process(jack_nframes_t nframes, void *arg);
+
+private:
+ bool m_isConnected;
+ volatile bool m_processing;
+
+ jack_client_t *client;
+ jack_port_t *inPort;
+ jack_port_t *m_capturePort;
+
+ size_t m_jackBufferSize;
+ size_t m_jackSampleSize;
+ jack_nframes_t m_jackSampleRate;
+ jack_ringbuffer_t *m_jackRingBuffer;
+
+ size_t m_frameSize;
+};
+
+
+}
+#endif
Index: src/sound/PitchDetector.cpp
===================================================================
--- src/sound/PitchDetector.cpp (revision 0)
+++ src/sound/PitchDetector.cpp (revision 0)
@@ -0,0 +1,413 @@
+/* -*- 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 <stdio.h>
+#include <stdlib.h>
+#include <iostream>
+#include <fstream>
+
+#include <QObject>
+#include <QVector>
+
+#include "PitchDetector.h"
+
+#define DEBUG_PT 0
+
+namespace Rosegarden
+{
+
+const PitchDetector::Method PitchDetector::PARTIAL =
+ QObject::tr("Partial", "Frequency Component (DSP)");
+const PitchDetector::Method PitchDetector::AUTOCORRELATION =
+ QObject::tr("Autocorrelation", "DSP operation");
+const PitchDetector::Method PitchDetector::HPS =
+ QObject::tr("Harmonic Product Spectrum", "Pitch determination (DSP)");
+const double PitchDetector::NOSIGNAL = -1;
+const double PitchDetector::NONE = -2;
+const int PitchDetector::defaultFrameSize = 1024;
+const int PitchDetector::defaultStepSize = 256;
+
+const PitchDetector::MethodVector PitchDetector::m_methods;
+
+PitchDetector::MethodVector::MethodVector() {
+ append( AUTOCORRELATION );
+ append( HPS );
+ append( PARTIAL );
+}
+
+PitchDetector::PitchDetector( int fs, int ss, int sr ) {
+
+ m_frameSize = fs;
+ m_stepSize = ss;
+ m_sampleRate = sr;
+
+ m_frame = (float *)malloc( sizeof(float) * (m_frameSize+m_stepSize) );
+
+ // allocate fft buffers
+ m_in1 = (float *)fftwf_malloc(sizeof(float) * (m_frameSize) );
+ m_in2 = (float *)fftwf_malloc(sizeof(float) * (m_frameSize) );
+ m_ft1 = (fftwf_complex *)fftwf_malloc(sizeof(fftwf_complex) * m_frameSize );
+ m_ft2 = (fftwf_complex *)fftwf_malloc(sizeof(fftwf_complex) * m_frameSize );
+
+ //for cepstrum
+ m_cepstralIn = (float *)fftwf_malloc(sizeof(float) * m_frameSize );
+ m_cepstralOut = (fftwf_complex *)fftwf_malloc(sizeof(fftwf_complex) * m_frameSize );
+
+ // create fft plans
+ m_p1= fftwf_plan_dft_r2c_1d( m_frameSize, m_in1, m_ft1, FFTW_MEASURE );
+ m_p2= fftwf_plan_dft_r2c_1d( m_frameSize, m_in2, m_ft2, FFTW_MEASURE );
+
+ //for autocorrelation
+ m_pc= fftwf_plan_dft_r2c_1d( m_frameSize, m_cepstralIn, m_cepstralOut, FFTW_MEASURE );
+
+ //set default method
+ m_method = AUTOCORRELATION;
+}
+
+const QVector<PitchDetector::Method>* PitchDetector::getMethods() {
+ return &m_methods;
+}
+
+void PitchDetector::setMethod( PitchDetector::Method method ) {
+#if DEBUG_PT
+ std::cout << "PitchDetector::setMethod " << method << std::endl;
+#endif
+
+ if (m_methods.contains(method)) {
+ m_method = method;
+ }
+ else {
+#if DEBUG_PT
+ std::cout << "PitchDetector::setMethod Not a method!\n";
+#endif
+ }
+}
+
+double PitchDetector::getPitch() {
+// return 446.0;
+ double freq = 0;
+// double f2 = 0; // not used?
+
+ // Fill input buffers with data for two overlapping frames.
+ for ( int c=0; c<m_frameSize; c++ ) {
+ double window = 0.5 - 0.5*( cos(2*M_PI*c/m_frameSize) );
+ m_in1[c] = m_frame[c] * window;
+ m_in2[c] = m_frame[c+m_stepSize] *window ;
+ }
+ // Perform DFT
+ fftwf_execute( m_p1 );
+ fftwf_execute( m_p2 );
+ if ( m_method == AUTOCORRELATION )
+ freq = autocorrelation();
+ else if ( m_method == HPS )
+ freq = hps();
+ else if ( m_method == PARTIAL )
+ freq = partial();
+ else {
+ return 0;
+ }
+
+#if DEBUG_PT
+ std::cout << "Freq " << freq << std::endl;
+#endif
+ return freq;
+}
+
+float *PitchDetector::getInBuffer() {
+ return m_frame;
+}
+
+PitchDetector::~PitchDetector() {
+ fftwf_free(m_in1);
+ fftwf_free(m_in2);
+ fftwf_free(m_ft1);
+ fftwf_free(m_ft2);
+ fftwf_free(m_cepstralIn);
+ fftwf_free(m_cepstralOut);
+ fftwf_destroy_plan(m_p1);
+ fftwf_destroy_plan(m_p2);
+ fftwf_destroy_plan(m_pc);
+}
+
+/**
+ Calculates autocorrelation from FFT using Wiener-Khinchin Theorem.
+*/
+
+double PitchDetector::autocorrelation() {
+ double value = 0;
+ int bin = 0;
+
+ /*
+ Do 2nd FT on abs of original FT. Cepstrum would use Log
+ instead of square
+ */
+ for ( int c=0; c<m_frameSize/2; c++ ) {
+ value = abs(std::complex<double>(m_ft1[c][0], m_ft1[c][1]))/m_frameSize; // normalise
+ m_cepstralIn[c] = value;
+ m_cepstralIn[(m_frameSize - 1)-c] = 0;//value; //fills second half of fft
+ }
+ fftwf_execute( m_pc );
+
+ // search for peak after first trough
+// double oldValue = 0; // not used?
+ value = 0;
+ double max = 0;
+
+ int c=0;
+
+ double buff[m_frameSize/2];
+ //fill buffer with magnitudes
+ for ( int i=0; i<m_frameSize/2; i++) {
+ buff[i] = abs( std::complex<double>(m_cepstralOut[i][0],m_cepstralOut[i][1]) );
+ }
+
+
+ double smoothed[m_frameSize/2];
+ for ( int i=0; i<10; i++) smoothed[i]=0;
+ for ( int i=m_frameSize/2-10; i<m_frameSize/2; i++) smoothed[i]=0;
+
+ for (int i=10; i<(m_frameSize/2)-10; i++ ) {
+ smoothed[i] = 0;
+ for ( int x=-10; x<=10; x++ )
+ smoothed[i] += buff[i+x];
+ smoothed[i] /=21;
+ }
+
+ // find end of peak in smoothed buffer (c must atart after smoothing)
+ // starts at 50
+ for ( c=30; c<(m_frameSize/4) && smoothed[c] >= smoothed[c+1]; c++ ) {
+ }
+ if ( c >= m_frameSize/4 ) {
+#if DEBUG_PT
+ std::cout << "error: no end to first peak\n";
+#endif
+ return NOSIGNAL;
+ }
+
+ max=0;
+ //find next peak from bin 30 (1500Hz) to 588 (75Hz)
+ for ( int i=0; i<m_frameSize/2; i++ ) {
+ value = smoothed[i];
+ if ( i>c && i<588 && value > max ) {
+ max = value;
+ bin = i;
+ }
+ }
+
+ //std::cout << "max " << max << std::endl;
+
+ if ( bin == 0 ) { // avoids occaisional exception when bin==0
+#if DEBUG_PT
+ std::cout << "bin = 0???\n";
+#endif
+ return NOSIGNAL;
+ }
+
+ int FTbin = round((double)m_frameSize/bin);
+// double fpb = (double)m_sampleRate/(double)m_frameSize; // no used?
+
+
+ int fBin=0;
+ std::complex<double> cValue = 0;
+ double fMag = 0;
+
+ fMag = 0;
+ //find localised partial
+ for ( int c=FTbin-2; c<FTbin+2 && c<m_frameSize/2; c++ ) {
+ cValue = std::complex<double>(m_ft1[c][0], m_ft1[c][1]);
+ if ( fMag < abs(cValue ) ) {
+ fBin = c;
+ fMag = abs(cValue);
+ }
+ }
+
+#if DEBUG_PT
+ std::cout << "ACbin " << bin
+ << "\tFTbin " << FTbin
+ << "\tPeak FTBin " << fBin
+ << std::endl;
+#endif
+
+ return unwrapPhase( FTbin );
+}
+
+
+/**
+ Performs Harmonic Product Spectrum frequency estimation
+*/
+double PitchDetector::hps() {
+ double max = 0;
+ int fBin = 0;
+
+ //calculate max HPS - only covering 1/6 of framesize
+ //downsampling by factor of 3 * 1/2 of framesize
+ for ( int i=0; i<m_frameSize/6; i++ ) {
+ int i2 = 2*i;
+ int i3 = 3*i;
+ double hps =
+ abs( std::complex<double>(m_ft1[i][0], m_ft1[i][1]) ) +
+ 0.8*abs( std::complex<double>(m_ft1[i2][0], m_ft1[i2][1]) ) +
+ 0.6*abs( std::complex<double>(m_ft1[i3][0], m_ft1[i3][1]) );
+
+ if ( max < hps ) {
+ max = hps;
+ fBin = i;
+ }
+ }
+
+ //std::cout << "bin = " << fBin << std::endl;
+
+ return unwrapPhase( fBin );
+}
+
+
+/**
+ Calculates exact frequency of component in fBin.
+ Assumes FFT has already been applied to both
+*/
+double PitchDetector::unwrapPhase( int fBin ) {
+
+ double oldPhase, fPhase;
+ if ( abs( std::complex<double>(m_ft1[fBin][0], m_ft1[fBin][1]) ) < MIN_THRESHOLD )
+ return NOSIGNAL;
+
+ std::complex<double> cVal = std::complex<double>(m_ft1[fBin][0], m_ft1[fBin][1]);
+ oldPhase = arg(cVal);
+
+ cVal = std::complex<double>(m_ft2[fBin][0], m_ft2[fBin][1]);
+ fPhase = arg(cVal);
+
+
+ double freqPerBin = (double)m_sampleRate/(double)m_frameSize;
+ double cf = fBin*freqPerBin;
+ double phaseChange = fPhase-oldPhase;
+ double expected = cf*(double)m_stepSize/(double)m_sampleRate;
+
+ double phaseDiff = phaseChange/(2.0*M_PI) - expected;
+ phaseDiff -= floor(phaseDiff);
+
+ if ( (phaseDiff -= floor(phaseDiff)) > 0.5 )
+ phaseDiff -= 1;
+
+ phaseDiff *= 2*M_PI;
+
+ double freqDiff = phaseDiff*freqPerBin*((double)m_frameSize/(double)m_stepSize)/(2*M_PI);
+
+ double freq = cf + freqDiff;
+
+#if DEBUG_PT
+ std::cout << "Bin=" << fBin
+ << "\tFPB=" << freqPerBin
+ << "\tcf=" << cf
+ << "\tfreq=" << freq
+ << "\toPh=" << oldPhase
+ << "\tnPh=" << fPhase
+ << "\tphdf=" <<phaseChange
+ << "\texpc=" << expected
+ << std::endl;
+#endif
+
+ return freq;
+}
+
+/**
+ Searches for highest component in frequency domain.
+*/
+
+double PitchDetector::partial() {
+ int oldBin=0, fBin=0;
+ std::complex<double> value = 0;
+ double fMag = 0;
+ double fPhase = 0;
+ double oldPhase = 0;
+
+ fMag = 0;
+ // find maximum input for first fft (in range)
+ for ( int c=4; c<200; c++ ) {
+ value = std::complex<double>(m_ft1[c][0], m_ft1[c][1]);
+ if ( fMag < abs(value ) ) {
+ oldBin = c;
+ fMag = abs(value);
+ oldPhase = arg(value);
+ }
+
+ }
+
+ fMag = 0;
+
+ for ( int c=4; c<m_frameSize/2; c++ ) {
+ value = std::complex<double>(m_ft2[c][0], m_ft2[c][1]);
+ if ( fMag < abs(value) ) {
+ fBin = c;
+ fMag = abs(value);
+ fPhase = arg(value);
+ }
+ }
+ // If magnitude below threshold return -1
+ if ( fMag < MIN_THRESHOLD )
+ return NOSIGNAL;
+
+ double freqPerBin = (double)m_sampleRate/(double)m_frameSize;
+ double cf = (double)fBin*freqPerBin;
+ double phaseChange = fPhase-oldPhase;
+ double expected = cf*(double)m_stepSize/(double)m_sampleRate;
+
+ double phaseDiff = phaseChange/(2.0*M_PI) - expected;
+ phaseDiff -= floor(phaseDiff);
+
+ if ( (phaseDiff -= floor(phaseDiff)) > 0.5 )
+ phaseDiff -= 1;
+
+ phaseDiff *= 2*M_PI;
+
+ double freqDiff = phaseDiff*freqPerBin*((double)m_frameSize/(double)m_stepSize)/(2.0*M_PI);
+
+ double freq = cf + freqDiff;
+
+#if DEBUG_PT
+ std::cout << "P bin=" << oldBin
+ << "\tFPB=" << freqPerBin
+ << "\tcf=" << cf
+ << "\tfreq=" << freq
+ << "\tphdf=" << phaseChange
+ << "\texpc=" << expected
+ << "\tur " << unwrapPhase(oldBin)
+ << std::endl;
+#endif
+ return freq;
+
+}
+
+int PitchDetector::getFrameSize() const {
+ return m_frameSize;
+}
+void PitchDetector::setFrameSize( int nextFrameSize ) {
+ m_frameSize = nextFrameSize;
+}
+
+int PitchDetector::getStepSize() const {
+ return m_stepSize;
+}
+void PitchDetector::setStepSize( int nextStepSize ) {
+ m_stepSize = nextStepSize;
+}
+
+int PitchDetector::getBufferSize() const {
+ return m_frameSize + m_stepSize;
+}
+
+}
Index: src/sound/JackCaptureClient.cpp
===================================================================
--- src/sound/JackCaptureClient.cpp (revision 0)
+++ src/sound/JackCaptureClient.cpp (revision 0)
@@ -0,0 +1,317 @@
+/* -*- 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 <iostream>
+
+#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
+
------------------------------------------------------------------------------
Free Software Download: Index, Search & Analyze Logs and other IT data in
Real-Time with Splunk. Collect, index and harness all the fast moving IT data
generated by your applications, servers and devices whether physical, virtual
or in the cloud. Deliver compliance at lower cost and gain new business
insights. http://p.sf.net/sfu/splunk-dev2dev
_______________________________________________
Rosegarden-devel mailing list
[email protected] - use the link below to unsubscribe
https://lists.sourceforge.net/lists/listinfo/rosegarden-devel