You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
akode/akode/plugins/jack_sink/jack_sink.cpp

229 lines
6.2 KiB

/* 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_open("akode_client", JackNoStartServer, 0);
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