diff options
author | Matthias P. Braendli <matthias.braendli@mpb.li> | 2014-06-26 20:16:37 +0200 |
---|---|---|
committer | Matthias P. Braendli <matthias.braendli@mpb.li> | 2014-06-26 20:16:37 +0200 |
commit | 45613a2ac14272c3e4779dd9aa971b5160bb060e (patch) | |
tree | ffec3e9f2e213415a3755849f0feaa53ea5101ad /src | |
parent | c0ec1e01c7dd84ce694bc5268916cc43870f00cf (diff) | |
download | ODR-AudioEnc-45613a2ac14272c3e4779dd9aa971b5160bb060e.tar.gz ODR-AudioEnc-45613a2ac14272c3e4779dd9aa971b5160bb060e.tar.bz2 ODR-AudioEnc-45613a2ac14272c3e4779dd9aa971b5160bb060e.zip |
Add JACK input support
Diffstat (limited to 'src')
-rw-r--r-- | src/JackInput.cpp | 139 | ||||
-rw-r--r-- | src/JackInput.h | 101 | ||||
-rw-r--r-- | src/dabplus-enc.cpp | 47 |
3 files changed, 279 insertions, 8 deletions
diff --git a/src/JackInput.cpp b/src/JackInput.cpp new file mode 100644 index 0000000..f39bf14 --- /dev/null +++ b/src/JackInput.cpp @@ -0,0 +1,139 @@ +/* ------------------------------------------------------------------ + * Copyright (C) 2011 Martin Storsjo + * Copyright (C) 2013,2014 Matthias P. Braendli + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. + * See the License for the specific language governing permissions + * and limitations under the License. + * ------------------------------------------------------------------- + */ + +#include <cstdio> +#include <string> +#include "config.h" + +#if HAVE_JACK + +extern "C" { +#include <jack/jack.h> +} + +#include "JackInput.h" +#include <sys/time.h> + +using namespace std; + +int JackInput::prepare() +{ + jack_options_t options = JackNullOption; + jack_status_t status; + const char *server_name = NULL; + + m_client = jack_client_open(m_jack_name.c_str(), options, &status, server_name); + if (m_client == NULL) { + fprintf(stderr, "jack_client_open() failed, " + "status = 0x%2.0x\n", status); + if (status & JackServerFailed) { + fprintf(stderr, "Unable to connect to JACK server\n"); + } + return -1; + } + + if (status & JackServerStarted) { + fprintf(stderr, "JACK server started\n"); + } + + if (status & JackNameNotUnique) { + fprintf(stderr, "JACK name '%s' not unique!\n", m_jack_name.c_str()); + return -1; + } + + /* Set up real-time process callback */ + jack_set_process_callback(m_client, process_cb, this); + + /* tell the JACK server to call `shutdown_cb' if + it ever shuts down, either entirely, or if it + just decides to stop calling us. */ + jack_on_shutdown(m_client, shutdown_cb, this); + + if (m_rate != jack_get_sample_rate(m_client)) { + fprintf(stderr, "JACK uses different sample_rate %d " + "than requested (%d)!\n", + jack_get_sample_rate(m_client), + m_rate); + return -1; + } + + /* create ports */ + for (int i = 0; i < m_channels; i++) { + std::stringstream port_name; + port_name << "input" << i; + + jack_port_t* input_port = jack_port_register(m_client, + port_name.str().c_str(), + JACK_DEFAULT_AUDIO_TYPE, + JackPortIsInput, + 0); + + if (input_port == NULL) { + fprintf(stderr, "no more JACK ports available\n"); + return -1; + } + + m_input_ports.push_back(input_port); + } + + /* Tell the JACK server that we are ready to roll. Our + * process() callback will start running now. */ + if (jack_activate(m_client)) { + fprintf (stderr, "JACK: cannot activate client"); + return -1; + } +} + +void JackInput::jack_process(jack_nframes_t nframes) +{ + // Convert samples to shorts + std::vector<int16_t> buffer(m_channels * nframes); + + for (int chan = 0; chan < m_channels; chan++) { + // start offset interleaving + int i = chan; + + const int dst_skip = m_channels; + + jack_default_audio_sample_t* src = + (jack_default_audio_sample_t*)jack_port_get_buffer(m_input_ports[chan], nframes); + + jack_nframes_t n = nframes; + while (n--) { + if (*src <= -1.0f) { + buffer[i] = 32767; + } + else if (*src >= 1.0f) { + buffer[i] = -32768; + } + else { + buffer[i] = + (int16_t)lrintf(*src * 32768.0f); + } + + i += dst_skip; + src++; + } + } + + m_queue.push((uint8_t*)&buffer.front(), buffer.size() * sizeof(uint16_t)); +} + +#endif // HAVE_JACK + diff --git a/src/JackInput.h b/src/JackInput.h new file mode 100644 index 0000000..d5832c7 --- /dev/null +++ b/src/JackInput.h @@ -0,0 +1,101 @@ +/* ------------------------------------------------------------------ + * Copyright (C) 2011 Martin Storsjo + * Copyright (C) 2013,2014 Matthias P. Braendli + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. + * See the License for the specific language governing permissions + * and limitations under the License. + * ------------------------------------------------------------------- + */ + +#ifndef __JACK_INPUT_H +#define __JACK_INPUT_H +#include "config.h" +#include <cstdio> +#include <string> + +#if HAVE_JACK + +extern "C" { +#include <jack/jack.h> +} + +#include "SampleQueue.h" + +// 16 bits per sample is fine for now +#define BYTES_PER_SAMPLE 2 + +class JackInput +{ + public: + JackInput(const std::string& jack_name, + unsigned int channels, + unsigned int samplerate, + SampleQueue<uint8_t>& queue) : + m_client(NULL), + m_jack_name(jack_name), + m_channels(channels), + m_rate(samplerate), + m_queue(queue) { } + + ~JackInput() { + if (m_client) { + jack_client_close(m_client); + } + } + + /* Prepare the audio input */ + int prepare(); + + private: + JackInput(const JackInput& other); + + jack_client_t* m_client; + + std::vector<jack_port_t*> m_input_ports; + + std::string m_jack_name; + unsigned int m_channels; + unsigned int m_rate; + + // Callback for real-time JACK process + void jack_process(jack_nframes_t nframes); + + // Callback when JACK shuts down + void jack_shutdown() + { + m_fault = true; + } + + bool m_fault; + bool m_running; + + SampleQueue<uint8_t>& m_queue; + + // Static functions for JACK callbacks + static int process_cb(jack_nframes_t nframes, void* arg) + { + ((JackInput*)arg)->jack_process(nframes); + return 0; + } + + static void shutdown_cb(void* arg) + { + ((JackInput*)arg)->jack_shutdown(); + } + +}; + +#endif // HAVE_JACK + +#endif + diff --git a/src/dabplus-enc.cpp b/src/dabplus-enc.cpp index 2822f94..6470e97 100644 --- a/src/dabplus-enc.cpp +++ b/src/dabplus-enc.cpp @@ -19,6 +19,7 @@ #include "AlsaInput.h" #include "FileInput.h" +#include "JackInput.h" #include "SampleQueue.h" #include "zmq.hpp" @@ -49,7 +50,12 @@ using namespace std; void usage(const char* name) { fprintf(stderr, "dabplus-enc %s is a HE-AACv2 encoder for DAB+\n" - "based on fdk-aac-dabplus that can read from a ALSA or file source\n" + "based on fdk-aac-dabplus that can read from" +#if HAVE_JACK + " JACK, ALSA or a file source\n" +#else + " a ALSA or file source\n" +#endif "and encode to a ZeroMQ output for ODR-DabMux.\n" "\n" "The -D option enables experimental sound card clock drift compensation.\n" @@ -85,6 +91,10 @@ void usage(const char* name) { " -i, --input=FILENAME Input filename (default: stdin).\n" " -f, --format={ wav, raw } Set input file format (default: wav).\n" " --fifo-silence Input file is fifo and encoder generates silence when fifo is empty. Ignore EOF.\n" +#if HAVE_JACK + " For the JACK input:\n" + " -j, --jack=name Enable JACK input, and define our name\n" +#endif " Encoder parameters:\n" " -b, --bitrate={ 8, 16, ..., 192 } Output bitrate in kbps. Must be 8 multiple.\n" " -a, --afterburner Turn on AAC encoder quality increaser.\n" @@ -94,7 +104,7 @@ void usage(const char* name) { " --sbr Force the usage of SBR\n" " --ps Force the usage of PS\n" " Output and pad parameters:\n" - " -o, --output=URI Output zmq uri. (e.g. 'tcp://*:9000')\n" + " -o, --output=URI Output zmq uri. (e.g. 'tcp://localhost:9000')\n" " -or- Output file uri. (e.g. 'file.dab')\n" " -or- a single dash '-' to denote stdout\n" " -k, --secret-key=FILE Enable ZMQ encryption with the given secret key.\n" @@ -227,6 +237,8 @@ int main(int argc, char *argv[]) // For the file output FILE *out_fh = NULL; + const char *jack_name = NULL; + const char *outuri = NULL; int sample_rate=48000, channels=2; const int bytes_per_sample = 2; @@ -263,6 +275,9 @@ int main(int argc, char *argv[]) {"device", required_argument, 0, 'd'}, {"format", required_argument, 0, 'f'}, {"input", required_argument, 0, 'i'}, +#if HAVE_JACK + {"jack", required_argument, 0, 'j'}, +#endif {"output", required_argument, 0, 'o'}, {"pad", required_argument, 0, 'p'}, {"pad-fifo", required_argument, 0, 'P'}, @@ -286,7 +301,7 @@ int main(int argc, char *argv[]) int index; while(ch != -1) { - ch = getopt_long(argc, argv, "ahDlb:c:f:i:k:o:r:d:p:P:", longopts, &index); + ch = getopt_long(argc, argv, "ahDlb:c:f:i:j:k:o:r:d:p:P:", longopts, &index); switch (ch) { case 0: // AAC-LC aot = AOT_DABPLUS_AAC_LC; @@ -324,6 +339,11 @@ int main(int argc, char *argv[]) case 'i': infile = optarg; break; +#if HAVE_JACK + case 'j': + jack_name = optarg; + break; +#endif case 'k': keyfile = optarg; break; @@ -349,8 +369,8 @@ int main(int argc, char *argv[]) } } - if (alsa_device && infile) { - fprintf(stderr, "You must define either alsa or file input, not both\n"); + if (alsa_device && infile && jack_name) { + fprintf(stderr, "You must define only one possible input, not several!\n"); return 1; } @@ -478,6 +498,9 @@ int main(int argc, char *argv[]) AlsaInputThreaded alsa_in_threaded(alsa_device, channels, sample_rate, queue); AlsaInputDirect alsa_in_direct(alsa_device, channels, sample_rate); FileInput file_in(infile, raw_input, sample_rate); +#if HAVE_JACK + JackInput jack_in(jack_name, channels, sample_rate, queue); +#endif if (infile) { if (file_in.prepare() != 0) { @@ -485,6 +508,14 @@ int main(int argc, char *argv[]) return 1; } } +#if HAVE_JACK + else if (jack_name) { + if (jack_in.prepare() != 0) { + fprintf(stderr, "JACK preparation failed\n"); + return 1; + } + } +#endif else if (drift_compensation) { if (alsa_in_threaded.prepare() != 0) { fprintf(stderr, "Alsa preparation failed\n"); @@ -531,7 +562,7 @@ int main(int argc, char *argv[]) // -------------- wait the right amount of time - if (drift_compensation) { + if (drift_compensation || jack_name) { struct timespec tp_now; clock_gettime(CLOCK_MONOTONIC, &tp_now); @@ -609,8 +640,8 @@ int main(int argc, char *argv[]) } } } - else if (drift_compensation) { - if (alsa_in_threaded.fault_detected()) { + else if (drift_compensation || jack_name) { + if (drift_compensation && alsa_in_threaded.fault_detected()) { fprintf(stderr, "Detected fault in alsa input!\n"); break; } |