Commit 8e6935a3 authored by Fabian Schlieper's avatar Fabian Schlieper
Browse files

jack tests

parent fc14ecdd
...@@ -24,18 +24,10 @@ ...@@ -24,18 +24,10 @@
#include <string> #include <string>
#include <vector> #include <vector>
#include <queue> #include <queue>
#include <jack/jack.h>
#ifndef JACK_MAX_CHANNELS
#define JACK_MAX_CHANNELS 16
#endif
#include <functional> #include <functional>
#include <jack/jack.h>
// Forward declaration of ITADatasource // Forward declaration of ITADatasource
class ITADatasource; class ITADatasource;
...@@ -77,9 +69,8 @@ public: ...@@ -77,9 +69,8 @@ public:
}; };
//! If no blockSize given the interface will use blockSize of JACK server
ITAJACKInterface(int blockSize = -1); ITAJACKInterface(int blockSize = -1);
//! Destructor
~ITAJACKInterface(); ~ITAJACKInterface();
...@@ -87,79 +78,39 @@ public: ...@@ -87,79 +78,39 @@ public:
ITA_JACK_ERRORCODE Initialize(const std::string& clientName); ITA_JACK_ERRORCODE Initialize(const std::string& clientName);
//! Returns true if playback is enabled, false otherwise //! Finalize JACK
bool IsPlaybackEnabled() const;
//! Set playback enabled/disabled
void SetPlaybackEnabled( bool bEnabled);
//! Returns true if record is enabled, false otherwise
bool IsRecordEnabled() const;
//! Set record enabled/disabled
void SetRecordEnabled( bool bEnabled);
//! Finalize Portaudio
/** /**
* This also deletes the record datasource. * This also deletes the record datasource.
*/ */
ITA_JACK_ERRORCODE Finalize(); ITA_JACK_ERRORCODE Finalize();
//! Opens a Portaudio stream //! Opens a JACK client
ITA_JACK_ERRORCODE Open(); ITA_JACK_ERRORCODE Open();
//! Closes the Portaudio stream //! Closes the JACK client
ITA_JACK_ERRORCODE Close(); ITA_JACK_ERRORCODE Close();
//! Start Portaudio streaming //! Start JACK client
ITA_JACK_ERRORCODE Start(); ITA_JACK_ERRORCODE Start();
//! Stop Portaudio streaming //! Stop JACK client
ITA_JACK_ERRORCODE Stop(); ITA_JACK_ERRORCODE Stop();
//! Returns the number of drivers found by Portaudio inline int GetNumInputChannels() const {
int GetNumDevices() const; return m_bInitialized ? m_iNumInputChannels : 0;
}
//! Returns the name of the driver avaiable in Portaudio inline int GetNumOutputChannels() const {
std::string GetDeviceName( int iDriverID ) const; return m_bInitialized ? m_iNumOutputChannels : 0;
}
//! Returns the interactive low latency capability of the driver
/**
* \param iDriverID Identifier of driver
* \return Latency in seconds, -1 if any error with the driver occurs
*/
float GetDeviceLatency() const;
ITA_JACK_ERRORCODE GetDriverSampleRate(int iDeviceID, double& dSampleRate) const; inline int GetSampleRate() const {
return m_dSampleRate;
}
inline int GetBlockSize() const { return m_iBufferSize; }
//! Get current input device index inline int SetBlockSize(int blockSize);
int GetInputDevice() const;
//! Get current output device index
int GetOutputDevice() const;
//! Returns the number of input and output channels
void GetNumChannels(int iDeviceID, int& iNumInputChannels, int& iNumOutputChannels) const;
//! Returns the number of input channels
/**
* \return Number of input channels (>=0) or #ITA_JACK_ERRORCODE (<0)
*/
int GetNumInputChannels(int iDeviceID=0) const;
//! Returns the number of output channels
/**
* \return Number of output channels (>=0) or #ITA_JACK_ERRORCODE (<0)
*/
int GetNumOutputChannels(int iDeviceID) const;
//! Returns the sample rate
double GetSampleRate() const;
//! Sets the sample rate
ITA_JACK_ERRORCODE SetSampleRate(double dSampleRate);
//! Set the playback data source //! Set the playback data source
/** /**
...@@ -182,41 +133,22 @@ public: ...@@ -182,41 +133,22 @@ public:
inline static jack_client_t *GetJackClient() { return s_jackClient; } inline static jack_client_t *GetJackClient() { return s_jackClient; }
void printInfo();
struct ITAJackUserData { struct ITAJackUserData {
ITADatasource* pdsPlaybackDatasource; //!< ITADatasource playback datasource ITADatasource* pdsPlaybackDatasource; //!< ITADatasource playback datasource
ITADatasource* pdsRecordDatasource; //!< ITADatasource record datasource ITADatasource* pdsRecordDatasource; //!< ITADatasource record datasource
std::vector<jack_port_t *> input_ports, output_ports;
bool bPlayback; //!< Playback enabled
bool bRecord; //!< Record enabled
jack_port_t *input_ports[JACK_MAX_CHANNELS];
jack_port_t *output_ports[JACK_MAX_CHANNELS];
uint64_t num_xruns; uint64_t num_xruns;
uint64_t num_samples; uint64_t num_samples;
ITAJackUserData() : num_xruns(0), num_samples(0), pdsPlaybackDatasource(nullptr), pdsRecordDatasource(nullptr)
ITAJackUserData()
{ {
pdsPlaybackDatasource = NULL;
pdsRecordDatasource = NULL;
bPlayback = false;
bRecord = false;
memset(input_ports, 0, sizeof(input_ports));
memset(output_ports, 0, sizeof(output_ports));
num_xruns = 0;
num_samples = 0;
} }
}; };
private: private:
static jack_client_t *s_jackClient; static jack_client_t *s_jackClient;
jack_client_t *m_jackClient; jack_client_t *m_jackClient;
...@@ -230,8 +162,6 @@ private: ...@@ -230,8 +162,6 @@ private:
bool m_bOpen; //!< Portaudio open status bool m_bOpen; //!< Portaudio open status
bool m_bStreaming; //!< Portaudio streaming status bool m_bStreaming; //!< Portaudio streaming status
bool m_bRecord; //!< Portaudio recording mode
bool m_bPlayback; //!< Portaudio playback mode
int m_iNumInputChannels; //!< Number of input channels int m_iNumInputChannels; //!< Number of input channels
int m_iNumOutputChannels; //!< Number of output channels int m_iNumOutputChannels; //!< Number of output channels
......
...@@ -8,11 +8,11 @@ ...@@ -8,11 +8,11 @@
#include <signal.h> #include <signal.h>
#include <future> #include <future>
#include <ITADataSource.h> #include <ITADataSource.h>
#include <ITADataSourceRealization.h> #include <ITADataSourceRealization.h>
#include <ITAStreamInfo.h> #include <ITAStreamInfo.h>
#include <iostream>
static int ITAJackProcess (jack_nframes_t nframes, void *arg); static int ITAJackProcess (jack_nframes_t nframes, void *arg);
void connectNewPortToInput(jack_port_t * port, ITAJACKInterface::ITAJackUserData* userData); void connectNewPortToInput(jack_port_t * port, ITAJACKInterface::ITAJackUserData* userData);
...@@ -49,9 +49,6 @@ ITAJACKInterface::ITAJACKInterface(int blockSize) ...@@ -49,9 +49,6 @@ ITAJACKInterface::ITAJACKInterface(int blockSize)
m_bInitialized = false; m_bInitialized = false;
m_bOpen = false; m_bOpen = false;
m_bStreaming = false; m_bStreaming = false;
m_bRecord = false;
m_bPlayback = false;
m_iError = ITA_JACK_NO_ERROR; m_iError = ITA_JACK_NO_ERROR;
} }
...@@ -122,33 +119,12 @@ ITAJACKInterface::ITA_JACK_ERRORCODE ITAJACKInterface::Initialize(const std::str ...@@ -122,33 +119,12 @@ ITAJACKInterface::ITA_JACK_ERRORCODE ITAJACKInterface::Initialize(const std::str
m_iNumInputChannels = 0; m_iNumInputChannels = 0;
while (out_ports[m_iNumOutputChannels]) m_iNumOutputChannels++; while (out_ports[m_iNumOutputChannels]) m_iNumOutputChannels++;
while (in_ports[m_iNumInputChannels++]) m_iNumInputChannels++; while (in_ports[m_iNumInputChannels]) m_iNumInputChannels++;
jack_free(out_ports); jack_free(out_ports);
jack_free(in_ports); jack_free(in_ports);
char port_name[12];
for(i = 0; i < m_iNumOutputChannels; i++) {
sprintf(port_name, "out%00d", i);
m_oUserData.output_ports[i] = jack_port_register (m_jackClient, port_name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0);
if (m_oUserData.output_ports[i] == NULL) {
fprintf(stderr, "no more JACK ports available\n");
return ITA_JACK_ERROR;
}
}
for(i = 0; i < m_iNumInputChannels; i++)
{
sprintf(port_name, "in%00d", i);
m_oUserData.input_ports[i] = jack_port_register (m_jackClient, port_name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0);
if (m_oUserData.input_ports[i] == NULL) {
fprintf(stderr, "no more JACK ports available\n");
return ITA_JACK_ERROR;
}
}
//jack_set_sample_rate(m_jackClient, (jack_nframes_t)m_dSampleRate);
/* install a signal handler to properly quits jack client */ /* install a signal handler to properly quits jack client */
...@@ -168,21 +144,7 @@ ITAJACKInterface::ITA_JACK_ERRORCODE ITAJACKInterface::Initialize(const std::str ...@@ -168,21 +144,7 @@ ITAJACKInterface::ITA_JACK_ERRORCODE ITAJACKInterface::Initialize(const std::str
return ITA_JACK_NO_ERROR; return ITA_JACK_NO_ERROR;
} }
bool ITAJACKInterface::IsPlaybackEnabled() const {
return m_bPlayback;
}
void ITAJACKInterface::SetPlaybackEnabled(const bool bEnabled) {
m_bPlayback = bEnabled;
}
bool ITAJACKInterface::IsRecordEnabled() const {
return m_bRecord;
}
void ITAJACKInterface::SetRecordEnabled(const bool bEnabled) {
m_bRecord = bEnabled;
}
ITAJACKInterface::ITA_JACK_ERRORCODE ITAJACKInterface::Finalize() { ITAJACKInterface::ITA_JACK_ERRORCODE ITAJACKInterface::Finalize() {
if (!m_bInitialized) if (!m_bInitialized)
...@@ -216,24 +178,6 @@ ITAJACKInterface::ITA_JACK_ERRORCODE ITAJACKInterface::Finalize() { ...@@ -216,24 +178,6 @@ ITAJACKInterface::ITA_JACK_ERRORCODE ITAJACKInterface::Finalize() {
ITAJACKInterface::ITA_JACK_ERRORCODE ITAJACKInterface::Open() { ITAJACKInterface::ITA_JACK_ERRORCODE ITAJACKInterface::Open() {
if (!m_bInitialized) return ITA_JACK_NOT_INITIALIZED; if (!m_bInitialized) return ITA_JACK_NOT_INITIALIZED;
if (m_bOpen) return ITA_JACK_IS_OPEN; if (m_bOpen) return ITA_JACK_IS_OPEN;
if (m_oUserData.pdsPlaybackDatasource == NULL && m_bPlayback) return ITA_JACK_NO_PLAYBACK_DATASOURCE;
if (m_oUserData.pdsRecordDatasource == NULL && m_bRecord) return ITA_JACK_NO_RECORD_DATASOURCE;
if (m_bRecord) {
if ((int) m_oUserData.pdsRecordDatasource->GetNumberOfChannels() > m_iNumInputChannels)
return ITA_JACK_UNMATCHED_CHANNELS;
m_oUserData.bRecord = true;
}
if (m_bPlayback) {
if ((int) m_oUserData.pdsPlaybackDatasource->GetNumberOfChannels() > m_iNumOutputChannels)
return ITA_JACK_UNMATCHED_CHANNELS;
m_oUserData.bPlayback = true;
}
m_bOpen = true; m_bOpen = true;
...@@ -258,22 +202,21 @@ bool ITAJACKInterface::connectJackPorts(bool connect) ...@@ -258,22 +202,21 @@ bool ITAJACKInterface::connectJackPorts(bool connect)
{ {
char portname[64]; // enough to hold all numbers up to 64-bits char portname[64]; // enough to hold all numbers up to 64-bits
auto func = connect ? &jack_connect : &jack_disconnect;
int i; int i;
for (i=0; i < m_iNumInputChannels; i++) { for (i=0; i < (std::min)((size_t)m_iNumInputChannels, m_oUserData.input_ports.size()); i++) {
sprintf(portname, "system:capture_%d", i+1); sprintf(portname, "system:capture_%d", i+1);
if (connect ? jack_connect(m_jackClient, portname, jack_port_name(m_oUserData.input_ports[i])) if (func(m_jackClient, portname, jack_port_name(m_oUserData.input_ports[i]))) {
: jack_disconnect(m_jackClient, portname, jack_port_name(m_oUserData.input_ports[i]))) {
fprintf(stderr,"Could not %s input ports %s and %s!\n", connect ? "connect" : "disconnect", portname, jack_port_name(m_oUserData.input_ports[i])); fprintf(stderr,"Could not %s input ports %s and %s!\n", connect ? "connect" : "disconnect", portname, jack_port_name(m_oUserData.input_ports[i]));
//return false;
} }
} }
for (i=0; i < m_iNumOutputChannels; i++) { for (i=0; i < (std::min)((size_t)m_iNumOutputChannels, m_oUserData.output_ports.size()); i++) {
sprintf(portname, "system:playback_%d", i+1); sprintf(portname, "system:playback_%d", i+1);
if (connect ? jack_connect(m_jackClient, jack_port_name(m_oUserData.output_ports[i]), portname) if (connect ? jack_connect(m_jackClient, jack_port_name(m_oUserData.output_ports[i]), portname)
: jack_disconnect(m_jackClient, jack_port_name(m_oUserData.output_ports[i]), portname)) { : jack_disconnect(m_jackClient, jack_port_name(m_oUserData.output_ports[i]), portname)) {
fprintf(stderr,"Could not %s output ports %s and %s!\n", connect ? "connect" : "disconnect", jack_port_name(m_oUserData.output_ports[i]), portname); fprintf(stderr,"Could not %s output ports %s and %s!\n", connect ? "connect" : "disconnect", jack_port_name(m_oUserData.output_ports[i]), portname);
//return false;
} }
} }
return true; return true;
...@@ -293,6 +236,7 @@ ITAJACKInterface::ITA_JACK_ERRORCODE ITAJACKInterface::Start() { ...@@ -293,6 +236,7 @@ ITAJACKInterface::ITA_JACK_ERRORCODE ITAJACKInterface::Start() {
/* Tell the JACK server that we are ready to roll. Our /* Tell the JACK server that we are ready to roll. Our
* process() callback will start running now. */ * process() callback will start running now. */
if (jack_activate (m_jackClient)) { if (jack_activate (m_jackClient)) {
std::cerr << "Failed to activate jack client!" << std::endl;
return ITA_JACK_INTERNAL_ERROR; return ITA_JACK_INTERNAL_ERROR;
} }
...@@ -319,42 +263,6 @@ ITAJACKInterface::ITA_JACK_ERRORCODE ITAJACKInterface::Stop() { ...@@ -319,42 +263,6 @@ ITAJACKInterface::ITA_JACK_ERRORCODE ITAJACKInterface::Stop() {
return ITA_JACK_NO_ERROR; return ITA_JACK_NO_ERROR;
} }
int ITAJACKInterface::GetNumDevices() const {
if (!m_bInitialized)
return -1;
return 1;
}
int ITAJACKInterface::GetNumInputChannels(const int iDeviceID) const {
if (!m_bInitialized)
return ITA_JACK_NOT_INITIALIZED;
return m_iNumInputChannels;
}
int ITAJACKInterface::GetNumOutputChannels(const int iDeviceID) const {
if (!m_bInitialized)
return ITA_JACK_NOT_INITIALIZED;
return m_iNumOutputChannels;
}
double ITAJACKInterface::GetSampleRate() const {
return m_dSampleRate;
}
void ITAJACKInterface::GetNumChannels(const int iDeviceID, int& iNumInputChannels, int& iNumOutputChannels) const {
iNumInputChannels = GetNumInputChannels(iDeviceID);
iNumOutputChannels = GetNumOutputChannels(iDeviceID);
}
ITAJACKInterface::ITA_JACK_ERRORCODE ITAJACKInterface::GetDriverSampleRate(const int iDeviceID, double& dSampleRate) const {
if (!m_bInitialized)
return ITA_JACK_NOT_INITIALIZED;
dSampleRate = jack_get_sample_rate(m_jackClient);
return ITA_JACK_NO_ERROR;
}
ITAJACKInterface::ITA_JACK_ERRORCODE ITAJACKInterface::SetPlaybackDatasource(ITADatasource* pidsDatasource) { ITAJACKInterface::ITA_JACK_ERRORCODE ITAJACKInterface::SetPlaybackDatasource(ITADatasource* pidsDatasource) {
if (!m_bInitialized) if (!m_bInitialized)
return ITA_JACK_NOT_INITIALIZED; return ITA_JACK_NOT_INITIALIZED;
...@@ -368,9 +276,24 @@ ITAJACKInterface::ITA_JACK_ERRORCODE ITAJACKInterface::SetPlaybackDatasource(ITA ...@@ -368,9 +276,24 @@ ITAJACKInterface::ITA_JACK_ERRORCODE ITAJACKInterface::SetPlaybackDatasource(ITA
if (pidsDatasource->GetSampleRate() != m_dSampleRate) if (pidsDatasource->GetSampleRate() != m_dSampleRate)
return ITA_JACK_UNMATCHED_SAMPLE_RATE; return ITA_JACK_UNMATCHED_SAMPLE_RATE;
m_oUserData.pdsPlaybackDatasource = pidsDatasource; if (static_cast<int>(pidsDatasource->GetNumberOfChannels()) > m_iNumInputChannels)
return ITA_JACK_UNMATCHED_CHANNELS;
// register ports with first playback datasource
if (!m_oUserData.pdsPlaybackDatasource) {
char port_name[20];
for (int i = 0; i < m_iNumOutputChannels; i++) {
sprintf(port_name, "out%00d", i);
auto port = jack_port_register(m_jackClient, port_name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0);
if (!port) {
std::cerr << "no more JACK ports available" << std::endl;
return ITA_JACK_ERROR;
}
m_oUserData.output_ports.push_back(port);
}
}
m_bPlayback = true; m_oUserData.pdsPlaybackDatasource = pidsDatasource;
return ITA_JACK_NO_ERROR; return ITA_JACK_NO_ERROR;
} }
...@@ -382,11 +305,23 @@ ITADatasource* ITAJACKInterface::GetRecordDatasource() { ...@@ -382,11 +305,23 @@ ITADatasource* ITAJACKInterface::GetRecordDatasource() {
if (m_bOpen) if (m_bOpen)
return NULL; return NULL;
if (m_oUserData.pdsRecordDatasource == NULL) if (m_oUserData.pdsRecordDatasource == NULL) {
m_oUserData.pdsRecordDatasource = new ITAJackSource(m_iNumInputChannels, m_dSampleRate, m_iBufferSize);
// create input ports
m_bRecord = true; for (int i = 0; i < m_iNumInputChannels; i++)
{
char port_name[20];
sprintf(port_name, "in%00d", i);
auto port = jack_port_register(m_jackClient, port_name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0);
m_oUserData.input_ports.push_back(port);
if (port == NULL) {
fprintf(stderr, "no more JACK ports available\n");
return NULL;
}
}
m_oUserData.pdsRecordDatasource = new ITAJackSource(m_iNumInputChannels, m_dSampleRate, m_iBufferSize);
}
return m_oUserData.pdsRecordDatasource; return m_oUserData.pdsRecordDatasource;
} }
...@@ -404,6 +339,14 @@ std::string ITAJACKInterface::GetErrorCodeString(const ITA_JACK_ERRORCODE err) { ...@@ -404,6 +339,14 @@ std::string ITAJACKInterface::GetErrorCodeString(const ITA_JACK_ERRORCODE err) {
return "Unkown error code"; return "Unkown error code";
} }
void ITAJACKInterface::printInfo() {
std::cout << "JACK initialized" << std::endl;
std::cout << " sampling rate: " << GetSampleRate() << std::endl;
std::cout << " block size: " << GetBlockSize() << std::endl;
std::cout << " input ch#: " << m_iNumInputChannels << std::endl;
std::cout << " output ch#: " << m_iNumOutputChannels << std::endl;
}
static int ITAJackProcess (jack_nframes_t nframes, void *arg) static int ITAJackProcess (jack_nframes_t nframes, void *arg)
{ {
#ifdef WITH_PROFILER #ifdef WITH_PROFILER
...@@ -418,12 +361,8 @@ static int ITAJackProcess (jack_nframes_t nframes, void *arg) ...@@ -418,12 +361,8 @@ static int ITAJackProcess (jack_nframes_t nframes, void *arg)
ITAJACKInterface::ITAJackUserData* userData = (ITAJACKInterface::ITAJackUserData*)arg; ITAJACKInterface::ITAJackUserData* userData = (ITAJACKInterface::ITAJackUserData*)arg;
ITAJackSource* pipdsRecordDatasource = (ITAJackSource*) userData->pdsRecordDatasource; ITAJackSource* pipdsRecordDatasource = (ITAJackSource*) userData->pdsRecordDatasource;
ITADatasource* pipdsPlaybackDatasource = userData->pdsPlaybackDatasource; ITADatasource* pipdsPlaybackDatasource = userData->pdsPlaybackDatasource;
bool bRecord = userData->bRecord;
bool bPlayback = userData->bPlayback;
ITAStreamInfo oStreamInfo; ITAStreamInfo oStreamInfo;
...@@ -431,19 +370,7 @@ static int ITAJackProcess (jack_nframes_t nframes, void *arg) ...@@ -431,19 +370,7 @@ static int ITAJackProcess (jack_nframes_t nframes, void *arg)
oStreamInfo.nSamples = userData->num_samples; oStreamInfo.nSamples = userData->num_samples;
// TODO: swap playback and record block here to avoid extra latency // TODO: swap playback and record block here to avoid extra latency
if (pipdsRecordDatasource) {
if (bPlayback) {
int iNumOutputChannels = pipdsPlaybackDatasource->GetNumberOfChannels();
const float* datablock;
for (int j=0; j<iNumOutputChannels; j++) {
jack_default_audio_sample_t *out = (jack_default_audio_sample_t *)jack_port_get_buffer (userData->output_ports[j], nframes);
datablock = pipdsPlaybackDatasource->GetBlockPointer(j, &oStreamInfo);
memcpy (out, datablock, sizeof (jack_default_audio_sample_t) * nframes);
}
pipdsPlaybackDatasource->IncrementBlockPointer();
}
if (bRecord) {
int iNumInputChannels = pipdsRecordDatasource->GetNumberOfChannels(); int iNumInputChannels = pipdsRecordDatasource->GetNumberOfChannels();
float* datablock; float* datablock;
for (int j=0; j<iNumInputChannels; j++) { for (int j=0; j<iNumInputChannels; j++) {
...@@ -454,17 +381,28 @@ static int ITAJackProcess (jack_nframes_t nframes, void *arg) ...@@ -454,17 +381,28 @@ static int ITAJackProcess (jack_nframes_t nframes, void *arg)
pipdsRecordDatasource->IncrementWritePointer(); pipdsRecordDatasource->IncrementWritePointer();
} }
if (pipdsPlaybackDatasource) {
int iNumOutputChannels = pipdsPlaybackDatasource->GetNumberOfChannels();
const float* datablock;
for (int j = 0; j<iNumOutputChannels; j++) {
jack_default_audio_sample_t *out = (jack_default_audio_sample_t *)jack_port_get_buffer(userData->output_ports[j], nframes);
datablock = pipdsPlaybackDatasource->GetBlockPointer(j, &oStreamInfo);
memcpy(out, datablock, sizeof(jack_default_audio_sample_t) * nframes);
}
pipdsPlaybackDatasource->IncrementBlockPointer();
}
userData->num_samples += nframes; userData->num_samples += nframes;
return 0; return 0;
} }
// Callbacks // Callbacks
/*
this function handles new port registrations from other clients
*/
void ITAJackPortRegistered(jack_port_id_t port_id, int reg, void *arg) void ITAJackPortRegistered(jack_port_id_t port_id, int reg, void *arg)