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

jack tests

parent fc14ecdd
......@@ -24,18 +24,10 @@
#include <string>
#include <vector>
#include <queue>
#include <jack/jack.h>
#ifndef JACK_MAX_CHANNELS
#define JACK_MAX_CHANNELS 16
#endif
#include <functional>
#include <jack/jack.h>
// Forward declaration of ITADatasource
class ITADatasource;
......@@ -77,9 +69,8 @@ public:
};
//! If no blockSize given the interface will use blockSize of JACK server
ITAJACKInterface(int blockSize = -1);
//! Destructor
~ITAJACKInterface();
......@@ -87,79 +78,39 @@ public:
ITA_JACK_ERRORCODE Initialize(const std::string& clientName);
//! Returns true if playback is enabled, false otherwise
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
//! Finalize JACK
/**
* This also deletes the record datasource.
*/
ITA_JACK_ERRORCODE Finalize();
//! Opens a Portaudio stream
//! Opens a JACK client
ITA_JACK_ERRORCODE Open();
//! Closes the Portaudio stream
//! Closes the JACK client
ITA_JACK_ERRORCODE Close();
//! Start Portaudio streaming
//! Start JACK client
ITA_JACK_ERRORCODE Start();
//! Stop Portaudio streaming
//! Stop JACK client
ITA_JACK_ERRORCODE Stop();
//! Returns the number of drivers found by Portaudio
int GetNumDevices() const;
//! Returns the name of the driver avaiable in Portaudio
std::string GetDeviceName( int iDriverID ) const;
//! 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;
inline int GetNumInputChannels() const {
return m_bInitialized ? m_iNumInputChannels : 0;
}
inline int GetNumOutputChannels() const {
return m_bInitialized ? m_iNumOutputChannels : 0;
}
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
int GetInputDevice() const;
inline int SetBlockSize(int blockSize);
//! 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
/**
......@@ -182,41 +133,22 @@ public:
inline static jack_client_t *GetJackClient() { return s_jackClient; }
void printInfo();
struct ITAJackUserData {
ITADatasource* pdsPlaybackDatasource; //!< ITADatasource playback datasource
ITADatasource* pdsRecordDatasource; //!< ITADatasource record datasource
bool bPlayback; //!< Playback enabled
bool bRecord; //!< Record enabled
jack_port_t *input_ports[JACK_MAX_CHANNELS];
jack_port_t *output_ports[JACK_MAX_CHANNELS];
ITADatasource* pdsRecordDatasource; //!< ITADatasource record datasource
std::vector<jack_port_t *> input_ports, output_ports;
uint64_t num_xruns;
uint64_t num_samples;
ITAJackUserData()
ITAJackUserData() : num_xruns(0), num_samples(0), pdsPlaybackDatasource(nullptr), pdsRecordDatasource(nullptr)
{
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:
static jack_client_t *s_jackClient;
jack_client_t *m_jackClient;
......@@ -230,8 +162,6 @@ private:
bool m_bOpen; //!< Portaudio open 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_iNumOutputChannels; //!< Number of output channels
......
......@@ -8,11 +8,11 @@
#include <signal.h>
#include <future>
#include <ITADataSource.h>
#include <ITADataSourceRealization.h>
#include <ITAStreamInfo.h>
#include <iostream>
static int ITAJackProcess (jack_nframes_t nframes, void *arg);
void connectNewPortToInput(jack_port_t * port, ITAJACKInterface::ITAJackUserData* userData);
......@@ -49,9 +49,6 @@ ITAJACKInterface::ITAJACKInterface(int blockSize)
m_bInitialized = false;
m_bOpen = false;
m_bStreaming = false;
m_bRecord = false;
m_bPlayback = false;
m_iError = ITA_JACK_NO_ERROR;
}
......@@ -122,33 +119,12 @@ ITAJACKInterface::ITA_JACK_ERRORCODE ITAJACKInterface::Initialize(const std::str
m_iNumInputChannels = 0;
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(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 */
......@@ -168,21 +144,7 @@ ITAJACKInterface::ITA_JACK_ERRORCODE ITAJACKInterface::Initialize(const std::str
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() {
if (!m_bInitialized)
......@@ -216,24 +178,6 @@ ITAJACKInterface::ITA_JACK_ERRORCODE ITAJACKInterface::Finalize() {
ITAJACKInterface::ITA_JACK_ERRORCODE ITAJACKInterface::Open() {
if (!m_bInitialized) return ITA_JACK_NOT_INITIALIZED;
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;
......@@ -258,22 +202,21 @@ bool ITAJACKInterface::connectJackPorts(bool connect)
{
char portname[64]; // enough to hold all numbers up to 64-bits
auto func = connect ? &jack_connect : &jack_disconnect;
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);
if (connect ? jack_connect(m_jackClient, portname, jack_port_name(m_oUserData.input_ports[i]))
: jack_disconnect(m_jackClient, portname, jack_port_name(m_oUserData.input_ports[i]))) {
if (func(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]));
//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);
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)) {
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;
......@@ -293,6 +236,7 @@ ITAJACKInterface::ITA_JACK_ERRORCODE ITAJACKInterface::Start() {
/* Tell the JACK server that we are ready to roll. Our
* process() callback will start running now. */
if (jack_activate (m_jackClient)) {
std::cerr << "Failed to activate jack client!" << std::endl;
return ITA_JACK_INTERNAL_ERROR;
}
......@@ -319,42 +263,6 @@ ITAJACKInterface::ITA_JACK_ERRORCODE ITAJACKInterface::Stop() {
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) {
if (!m_bInitialized)
return ITA_JACK_NOT_INITIALIZED;
......@@ -368,9 +276,24 @@ ITAJACKInterface::ITA_JACK_ERRORCODE ITAJACKInterface::SetPlaybackDatasource(ITA
if (pidsDatasource->GetSampleRate() != m_dSampleRate)
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;
}
......@@ -382,11 +305,23 @@ ITADatasource* ITAJACKInterface::GetRecordDatasource() {
if (m_bOpen)
return NULL;
if (m_oUserData.pdsRecordDatasource == NULL)
m_oUserData.pdsRecordDatasource = new ITAJackSource(m_iNumInputChannels, m_dSampleRate, m_iBufferSize);
m_bRecord = true;
if (m_oUserData.pdsRecordDatasource == NULL) {
// create input ports
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;
}
......@@ -404,6 +339,14 @@ std::string ITAJACKInterface::GetErrorCodeString(const ITA_JACK_ERRORCODE err) {
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)
{
#ifdef WITH_PROFILER
......@@ -418,12 +361,8 @@ static int ITAJackProcess (jack_nframes_t nframes, void *arg)
ITAJACKInterface::ITAJackUserData* userData = (ITAJACKInterface::ITAJackUserData*)arg;
ITAJackSource* pipdsRecordDatasource = (ITAJackSource*) userData->pdsRecordDatasource;
ITADatasource* pipdsPlaybackDatasource = userData->pdsPlaybackDatasource;
bool bRecord = userData->bRecord;
bool bPlayback = userData->bPlayback;
ITAStreamInfo oStreamInfo;
......@@ -431,19 +370,7 @@ static int ITAJackProcess (jack_nframes_t nframes, void *arg)
oStreamInfo.nSamples = userData->num_samples;
// TODO: swap playback and record block here to avoid extra latency
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) {
if (pipdsRecordDatasource) {
int iNumInputChannels = pipdsRecordDatasource->GetNumberOfChannels();
float* datablock;
for (int j=0; j<iNumInputChannels; j++) {
......@@ -454,17 +381,28 @@ static int ITAJackProcess (jack_nframes_t nframes, void *arg)
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;
return 0;
}
// Callbacks
/*
this function handles new port registrations from other clients
*/
void ITAJackPortRegistered(jack_port_id_t port_id, int reg, void *arg)
{
ITAJACKInterface::ITAJackUserData* userData = (ITAJACKInterface::ITAJackUserData*)arg;
......@@ -532,8 +470,8 @@ void connectNewPortToInput(jack_port_t * port, ITAJACKInterface::ITAJackUserData
// get channel number by trailing number (-1 because 0 indexing)
int ch = atoi(&portNameA[strlen(portNameA) - 1]) - 1;
if (ch >= 0 && ch < JACK_MAX_CHANNELS) {
if (userData->input_ports[ch] && !jack_port_connected_to(userData->input_ports[ch], portNameA)) {
if (ch >= 0 && ch < userData->input_ports.size()) {
if (!jack_port_connected_to(userData->input_ports[ch], portNameA)) {
printf("Connecting port %s -> %s\n", portNameA, jack_port_name(userData->input_ports[ch]));
if (jack_connect(ITAJACKInterface::GetJackClient(), portNameA, jack_port_name(userData->input_ports[ch])) != 0) {
printf("\t... connection failed!\n");
......@@ -541,9 +479,9 @@ void connectNewPortToInput(jack_port_t * port, ITAJACKInterface::ITAJackUserData
}
}
// if mono, connect to all ports!
// if mono (e.g. no numeric port suffix), connect to all ports!
if (ch == -1) {
for (ch = 0; ch < JACK_MAX_CHANNELS; ch++) {
for (ch = 0; ch < userData->input_ports.size(); ch++) {
if (userData->input_ports[ch] && !jack_port_connected_to(userData->input_ports[ch], portNameA)) {
printf("Connecting port %s -> %s\n", portNameA, jack_port_name(userData->input_ports[ch]));
if (jack_connect(ITAJACKInterface::GetJackClient(), portNameA, jack_port_name(userData->input_ports[ch])) != 0)
......
......@@ -12,7 +12,7 @@ vista_use_package( ITADataSources REQUIRED FIND_DEPENDENCIES )
add_definitions( -DITA_DATA_SOURCES_DLL )
# Loopback
add_executable( ITAJackLoopback ITAJackLoopback.cpp )
target_link_libraries( ITAJackLoopback ${VISTA_USE_PACKAGE_LIBRARIES} )
......@@ -22,3 +22,16 @@ vista_create_default_info_file( ITAJackLoopback )
set_property( TARGET ITAJackLoopback PROPERTY FOLDER "ITACoreLibs/Tests/ITADataSources" )
# Playback
add_executable( ITAJackPlayback ITAJackPlayback.cpp )
target_link_libraries( ITAJackPlayback ${VISTA_USE_PACKAGE_LIBRARIES} )
vista_configure_app( ITAJackPlayback )
vista_install( ITAJackPlayback )
vista_create_default_info_file( ITAJackPlayback )
set_property( TARGET ITAJackPlayback PROPERTY FOLDER "ITACoreLibs/Tests/ITADataSources" )
......@@ -6,11 +6,12 @@
#include <ITAJACKInterface.h>
#include <ITADataSourceRealization.h>
#include <ITAFileDatasource.h>
#include <ITAFileDataSource.h>
#include <ITAStreamFunctionGenerator.h>
static double dSampleRate = 44.1e3;
static int iBlockSize = 512;
#include <ITAException.h>
static std::string sOutputFileName = "ITAPA_Record.wav";
float fRecordingTime = 5; // Seconds
......@@ -50,46 +51,37 @@ private:
};
void loopback() {
ITAJACKInterface jack(iBlockSize);
jack.SetPlaybackEnabled(true);
jack.SetRecordEnabled(true);
ITAJACKInterface jack;
auto err = jack.Initialize("jack-test");
auto err = jack.Initialize("jack-loopback");
if (err != ITAJACKInterface::ITA_JACK_NO_ERROR) {
std::cerr << "Failed to init jack!" << std::endl;
std::cerr << "Failed to init jack:" << jack.GetErrorCodeString(err) << std::endl;
return;
}
jack.printInfo();
std::cout << "GetRecordDatasource..." << std::endl;
auto dsIn = jack.GetRecordDatasource();
int blockSize = dsIn->GetBlocklength();
// TODO
ITADatasource* pSource = NULL;
try
{
pSource = new ITAFileDatasource("../ITAAsioTests/Trompete.wav", blockSize, true);
}
catch (ITAException& e)
{
std::cerr << "Could open audio file, error = " << e << std::endl;
pSource = new ITAStreamFunctionGenerator(1, dSampleRate, blockSize, ITAStreamFunctionGenerator::SINE, 300, 0.9f, true);
}
int blockSize = jack.GetBlockSize();
Loopback dsLoop(pSource, jack.GetNumInputChannels(), jack.GetSampleRate(), dsIn->GetBlocklength());
std::cout << "creating loopback" << std::endl;
Loopback dsLoop(dsIn, jack.GetNumInputChannels(), jack.GetSampleRate(), blockSize);
jack.SetPlaybackDatasource(&dsLoop);
std::cout << "settings playback datasource" << std::endl;
jack.SetPlaybackDatasource(dsIn);
std::cout << "open..." << std::endl;
jack.Open();
std::cout << "start..." << std::endl;
jack.Start();
std::cout << "Started. Press any key to quit." << std::endl;
getchar();
jack.Stop();
jack.Close();
jack.Finalize();