diff options
Diffstat (limited to 'akode/plugins/jack_sink/jack_sink.cpp')
-rw-r--r-- | akode/plugins/jack_sink/jack_sink.cpp | 227 |
1 files changed, 227 insertions, 0 deletions
diff --git a/akode/plugins/jack_sink/jack_sink.cpp b/akode/plugins/jack_sink/jack_sink.cpp new file mode 100644 index 0000000..4824165 --- /dev/null +++ b/akode/plugins/jack_sink/jack_sink.cpp @@ -0,0 +1,227 @@ +/* aKode: JACK-Sink + + Copyright (C) 2004 Allan Sandfeld Jensen <kde@carewolf.com> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "config.h" + +#ifdef HAVE_STDINT_H +#include <stdint.h> +#elif HAVE_INTTYPES_H +#include <inttypes.h> +#endif +#include <jack/jack.h> + +#include <audioframe.h> +#include <audiobuffer.h> +#include "jack_sink.h" + +#include <iostream> + +namespace aKode { + +extern "C" { JACKSinkPlugin jack_sink; } + +struct JACKSink::private_data +{ + private_data() : left_port(0), right_port(0), client(0), error(false), sample_rate(0), buffer(8),pos(0) {}; +// jack_port_t *input_port; + jack_port_t *left_port; + jack_port_t *right_port; + + jack_client_t *client; + + bool error; + unsigned int sample_rate; + + AudioConfiguration config; + AudioBuffer buffer; + AudioFrame current; + int pos; +}; + +static int process (jack_nframes_t nframes, void *arg) +{ + JACKSink::private_data *m_data = (JACKSink::private_data*)arg; + + jack_default_audio_sample_t *out1=0, *out2=0; + + if (m_data->left_port) + out1 = (jack_default_audio_sample_t *) jack_port_get_buffer (m_data->left_port, nframes); + if (m_data->right_port) + out2 = (jack_default_audio_sample_t *) jack_port_get_buffer (m_data->right_port, nframes); + + if (!out2 && !out1) return 0; + + unsigned int n = 0; + float** buffer = (float**)m_data->current.data; + while(n < nframes) { + if (m_data->pos >= m_data->current.length) { + if (!m_data->buffer.get(&m_data->current, false)) break; + m_data->pos = 0; + buffer = (float**)m_data->current.data; + } + + if (out1) out1[n] = buffer[0][m_data->pos]; + if (out2) out2[n] = buffer[1][m_data->pos]; + + n++; + m_data->pos++; + } + + return n; +} + +static void shutdown (void *arg) +{ + JACKSink::private_data *m_data = (JACKSink::private_data*)arg; + + // Jack has shut down, so the sink is in fatal error. + m_data->error = true; + m_data->buffer.release(); +} + +JACKSink::JACKSink() +{ + m_data = new private_data; +} + +bool JACKSink::open() { + m_data->client = jack_client_new("akode_client"); + if (!m_data->client) { + m_data->error = true; + return false; + } + jack_set_process_callback(m_data->client, process, (void*)m_data); + jack_on_shutdown(m_data->client, shutdown, (void*)m_data); + + m_data->sample_rate = jack_get_sample_rate(m_data->client); +// jack_set_sample_rate_callback(m_data->client, sample_rate, (void*)m_data); + + if (jack_activate(m_data->client)) { + m_data->error = true; + std::cout << "aKode::Jack: Activate failed\n"; + return false; + } + const char** names = jack_get_ports (m_data->client, NULL, NULL, JackPortIsPhysical|JackPortIsInput); + while (*names) { + std::cout << *names << std::endl; + names++; + } + + return true; +} + +JACKSink::~JACKSink() +{ + if (m_data->left_port) + jack_port_unregister(m_data->client, m_data->left_port); + if (m_data->right_port) + jack_port_unregister(m_data->client, m_data->right_port); + if (m_data->client) + jack_deactivate(m_data->client); + delete m_data; +} + +int JACKSink::setAudioConfiguration(const AudioConfiguration* config) +{ + if (m_data->error) return -1; + + int res = 0; + m_data->config = *config; + + if (config->channel_config != MonoStereo ) return -1; + m_data->left_port = jack_port_register (m_data->client, "left", JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0); + if (jack_connect (m_data->client, jack_port_name (m_data->left_port), "alsa_pcm:playback_1")) { + m_data->error = true; + return -1; + } + if (config->channels > 1) { + m_data->right_port = jack_port_register (m_data->client, "right", JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0); + if (jack_connect (m_data->client, jack_port_name (m_data->right_port), "alsa_pcm:playback_2")) { + m_data->config.channels = 1; + res = 1; + } + } + + if (config->sample_width != -32) { + res = 1; + m_data->config.sample_width = -32; + } + + if (config->sample_rate != m_data->sample_rate) { + res = 1; + m_data->config.sample_rate = m_data->sample_rate; + } + + return res; +} + +const AudioConfiguration* JACKSink::audioConfiguration() const +{ + return &m_data->config; +} + +template<typename S> +void JACKSink::convertFrame(AudioFrame *in, AudioFrame *out) +{ + float scale = (float)(1<<(in->sample_width-1)); + scale = 1.0/scale; +// float scale = 1; + + out->reserveSpace(&m_data->config, in->length); + + int channels = in->channels; + S** indata = (S**)in->data; + float** outdata = (float**)out->data; + for(int j=0; j<in->length; j++) { + for(int i=0; i<channels; i++) { + outdata[i][j] = scale*indata[i][j]; + } + } +} + +bool JACKSink::writeFrame(AudioFrame* frame) +{ + if ( m_data->error ) return false; + + if ( frame->channels != m_data->config.channels ) + { + if (setAudioConfiguration(frame)!=0) + return false; + } + if (frame->length == 0) return true; + + // this shouldn't really happen + if (frame->sample_width>0) { + AudioFrame out; + if (frame->sample_width<=8) + convertFrame<int8_t>(frame, &out); + else + if (frame->sample_width<=16) + convertFrame<int16_t>(frame, &out); + else + if (frame->sample_width<=32) + convertFrame<int32_t>(frame, &out); + return m_data->buffer.put(&out, true); + } else + return m_data->buffer.put(frame, true); + +} + +} // namespace |