Last active
June 13, 2019 09:47
-
-
Save nickbailey/aea9032cfdce75706bd80720372061aa to your computer and use it in GitHub Desktop.
An adapter to allow old-school data-push audio apps to work as JACK clients
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// A little bit of research to find out how to regress jack to ALSA :) | |
// compile with g++ push_to_jack.cpp -ljack | |
#include <jack/jack.h> | |
#include <thread> | |
#include <mutex> | |
#include <condition_variable> | |
#include <iostream> | |
class JackBlockingOutputAdaptor { | |
size_t bufsize; | |
jack_client_t* client; | |
jack_port_t* port; | |
jack_default_audio_sample_t* buffer; | |
jack_nframes_t sr; | |
std::mutex mtx; | |
std::condition_variable cv; | |
bool dav {false}; | |
JackThreadCallback process_dispatch; | |
public: | |
jack_default_audio_sample_t * const data(void) const { return buffer; } | |
const size_t size(void) const { return bufsize; }; | |
const jack_nframes_t rate(void) const { return sr; }; | |
JackBlockingOutputAdaptor(const char* client_name, | |
const char* server_name = nullptr) { | |
// Communicate with jack | |
jack_status_t status; | |
jack_options_t options = JackNullOption; | |
client = jack_client_open (client_name, options, &status, server_name); | |
if (client == nullptr) { | |
std::cerr << "jack_client_open() failed, " | |
"status = " << status << std::endl; | |
if (status & JackServerFailed) { | |
std::cerr << "Unable to connect to JACK server\n"; | |
} | |
exit (1); | |
} | |
// Initialise working parameters | |
bufsize = jack_get_buffer_size(client); | |
buffer = new jack_default_audio_sample_t[bufsize]; | |
std::fill(buffer, buffer + bufsize, 0); | |
sr = jack_get_sample_rate(client); | |
port = jack_port_register(client, "output", | |
JACK_DEFAULT_AUDIO_TYPE, | |
JackPortIsOutput, 0); | |
if (port == nullptr) { | |
std::cerr << "No more JACK ports available\n"; | |
exit (1); | |
} | |
jack_set_process_thread(client, | |
JackBlockingOutputAdaptor::process, this); | |
} | |
~JackBlockingOutputAdaptor() { | |
jack_deactivate(client); | |
jack_client_close(client); | |
delete[] buffer; | |
} | |
void commence() { | |
if (jack_activate(client)) { | |
std::cerr << "Cannot activate client\n"; | |
exit (1); | |
} | |
} | |
void dispatch(void) { | |
{ | |
std::unique_lock<std::mutex> lck(mtx); | |
dav = true; | |
cv.notify_one(); | |
} | |
while(dav) std::this_thread::yield(); | |
} | |
private: | |
static void* process(void* o) { | |
return static_cast<JackBlockingOutputAdaptor*>(o)->process_delegate(nullptr); | |
} | |
void* process_delegate(void* args) { | |
while(true) { | |
jack_default_audio_sample_t* in( | |
static_cast<jack_default_audio_sample_t*>( | |
jack_port_get_buffer(port, bufsize) | |
)); | |
std::unique_lock<std::mutex> lck(mtx); | |
cv.wait(lck, [this](){return dav;}); | |
std::copy(buffer, buffer + bufsize, in); | |
jack_cycle_signal(client, 0); | |
dav = false; | |
jack_cycle_wait(client); | |
} | |
jack_cycle_signal(client, -1); | |
return nullptr; | |
} | |
}; | |
#include <cmath> | |
int main() | |
{ | |
JackBlockingOutputAdaptor a("BEEEEP!"); | |
int writes(10*a.rate()/a.size()); | |
jack_default_audio_sample_t * const target{a.data()}; | |
a.commence(); | |
long s{0}; | |
while (writes--) { | |
for(int i = 0; i < a.size(); i++) { | |
s++; | |
target[i] = sin(s*2.0*M_PI*442.0/a.rate()); | |
} | |
a.dispatch(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment