Browse Source

Merge branch 'netone-isolateion'

git-svn-id: http://subversion.jackaudio.org/jack/jack2/trunk/jackmp@3693 0c269be4-1314-0410-8aa9-9f06e86f4224
tags/v1.9.4
sletz 15 years ago
parent
commit
2d00ac0eaf
14 changed files with 6103 additions and 2 deletions
  1. +1125
    -0
      common/JackNetOneDriver.cpp
  2. +96
    -0
      common/JackNetOneDriver.h
  3. +2
    -1
      common/JackTransportEngine.cpp
  4. +689
    -0
      common/netjack.c
  5. +145
    -0
      common/netjack.h
  6. +1545
    -0
      common/netjack_packet.c
  7. +165
    -0
      common/netjack_packet.h
  8. +757
    -0
      example-clients/alsa_in.c
  9. +755
    -0
      example-clients/alsa_out.c
  10. +758
    -0
      example-clients/netsource.c
  11. +38
    -0
      example-clients/wscript
  12. +11
    -1
      linux/wscript
  13. +8
    -0
      macosx/wscript
  14. +9
    -0
      wscript

+ 1125
- 0
common/JackNetOneDriver.cpp
File diff suppressed because it is too large
View File


+ 96
- 0
common/JackNetOneDriver.h View File

@@ -0,0 +1,96 @@
/*
Copyright (C) 2001 Paul Davis
Copyright (C) 2008 Romain Moret at Grame

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program 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 General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/

#ifndef __JackNetDriver__
#define __JackNetDriver__

#include "JackAudioDriver.h"
#include "netjack.h"
#include "netjack_packet.h"

namespace Jack
{
/**
\Brief This class describes the Net Backend
*/

class JackNetOneDriver : public JackAudioDriver
{
private:
netjack_driver_state_t netj;

void
render_payload_to_jack_ports_float ( void *packet_payload, jack_nframes_t net_period_down, JSList *capture_ports, JSList *capture_srcs, jack_nframes_t nframes, int dont_htonl_floats);
void
render_jack_ports_to_payload_float (JSList *playback_ports, JSList *playback_srcs, jack_nframes_t nframes, void *packet_payload, jack_nframes_t net_period_up, int dont_htonl_floats );
#ifdef HAVE_CELT
void
render_payload_to_jack_ports_celt (void *packet_payload, jack_nframes_t net_period_down, JSList *capture_ports, JSList *capture_srcs, jack_nframes_t nframes);
void
render_jack_ports_to_payload_celt (JSList *playback_ports, JSList *playback_srcs, jack_nframes_t nframes, void *packet_payload, jack_nframes_t net_period_up);
#endif
void
render_payload_to_jack_ports (int bitdepth, void *packet_payload, jack_nframes_t net_period_down, JSList *capture_ports, JSList *capture_srcs, jack_nframes_t nframes, int dont_htonl_floats);
void
render_jack_ports_to_payload (int bitdepth, JSList *playback_ports, JSList *playback_srcs, jack_nframes_t nframes, void *packet_payload, jack_nframes_t net_period_up, int dont_htonl_floats);

public:
JackNetOneDriver ( const char* name, const char* alias, JackLockedEngine* engine, JackSynchro* table,
int port, int mtu, int capture_ports, int playback_ports, int midi_input_ports, int midi_output_ports,
int sample_rate, int period_size, int resample_factor,
char* net_name, uint transport_sync, int bitdepth, int use_autoconfig,
int latency, int redundancy, int dont_htonl_floats, int always_deadline );
~JackNetOneDriver();

int Open ( jack_nframes_t frames_per_cycle, jack_nframes_t rate, bool capturing, bool playing,
int inchannels, int outchannels, bool monitor, const char* capture_driver_name,
const char* playback_driver_name, jack_nframes_t capture_latency, jack_nframes_t playback_latency );

int Close();
int Attach();
int Detach();

int Read();
int Write();

bool Initialize();
int AllocPorts();
void FreePorts();

// BufferSize can't be changed
bool IsFixedBufferSize()
{
return true;
}

int SetBufferSize ( jack_nframes_t buffer_size )
{
return -1;
}

int SetSampleRate ( jack_nframes_t sample_rate )
{
return -1;
}

};
}

#endif

+ 2
- 1
common/JackTransportEngine.cpp View File

@@ -38,7 +38,8 @@ JackTransportEngine::JackTransportEngine(): JackAtomicArrayState<jack_position_t
{ {
fTransportState = JackTransportStopped; fTransportState = JackTransportStopped;
fTransportCmd = fPreviousCmd = TransportCommandStop; fTransportCmd = fPreviousCmd = TransportCommandStop;
fSyncTimeout = 2000000; /* 2 second default */
fSyncTimeout = 10000000; /* 10 seconds default...
in case of big netjack1 roundtrip */
fSyncTimeLeft = 0; fSyncTimeLeft = 0;
fTimeBaseMaster = -1; fTimeBaseMaster = -1;
fWriteCounter = 0; fWriteCounter = 0;


+ 689
- 0
common/netjack.c View File

@@ -0,0 +1,689 @@

/* -*- mode: c; c-file-style: "linux"; -*- */
/*
NetJack Abstraction.

Copyright (C) 2008 Pieter Palmers <pieterpalmers@users.sourceforge.net>
Copyright (C) 2006 Torben Hohn <torbenh@gmx.de>
Copyright (C) 2003 Robert Ham <rah@bash.sh>
Copyright (C) 2001 Paul Davis

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program 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 General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

$Id: net_driver.c,v 1.17 2006/04/16 20:16:10 torbenh Exp $
*/


#include <math.h>
#include <stdio.h>
#include <memory.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <stdarg.h>

#include <jack/types.h>
#include "jack/jslist.h"

#include <sys/types.h>

#ifdef WIN32
#include <winsock.h>
#include <malloc.h>
#else
#include <sys/socket.h>
#include <netinet/in.h>
#endif

#include "netjack.h"

//#include "config.h"
#if HAVE_SAMPLERATE
#include <samplerate.h>
#endif

#if HAVE_CELT
#include <celt/celt.h>
#endif

#include "netjack.h"
#include "netjack_packet.h"

// JACK2
#include "jack/control.h"

#define MIN(x,y) ((x)<(y) ? (x) : (y))

static int sync_state = 1;
static jack_transport_state_t last_transport_state;

static int
net_driver_sync_cb(jack_transport_state_t state, jack_position_t *pos, void *data)
{
int retval = sync_state;

if (state == JackTransportStarting && last_transport_state != JackTransportStarting) {
retval = 0;
}
// if (state == JackTransportStarting)
// jack_info("Starting sync_state = %d", sync_state);
last_transport_state = state;
return retval;
}

void netjack_wait( netjack_driver_state_t *netj )
{
int we_have_the_expected_frame = 0;
jack_nframes_t next_frame_avail;
jack_time_t packet_recv_time_stamp;
jacknet_packet_header *pkthdr;

if( !netj->next_deadline_valid ) {
netj->next_deadline = jack_get_time() + netj->deadline_offset;
netj->next_deadline_valid = 1;
}

// Increment expected frame here.

netj->expected_framecnt += 1;

//jack_log( "expect %d", netj->expected_framecnt );
// Now check if required packet is already in the cache.
// then poll (have deadline calculated)
// then drain socket, rinse and repeat.
while(1) {
if( packet_cache_get_next_available_framecnt( global_packcache, netj->expected_framecnt, &next_frame_avail) ) {
if( next_frame_avail == netj->expected_framecnt ) {
we_have_the_expected_frame = 1;
if( !netj->always_deadline )
break;
}
}
if( ! netjack_poll_deadline( netj->sockfd, netj->next_deadline ) ) {
break;
}

packet_cache_drain_socket( global_packcache, netj->sockfd );
}

// check if we know who to send our packets too.
if (!netj->srcaddress_valid)
if( global_packcache->master_address_valid ) {
memcpy (&(netj->syncsource_address), &(global_packcache->master_address), sizeof( struct sockaddr_in ) );
netj->srcaddress_valid = 1;
}

// XXX: switching mode unconditionally is stupid.
// if we were running free perhaps we like to behave differently
// ie. fastforward one packet etc.
// well... this is the first packet we see. hmm.... dunno ;S
// it works... so...
netj->running_free = 0;

if( !we_have_the_expected_frame )
jack_error( "netxrun... %d", netj->expected_framecnt );

if( we_have_the_expected_frame ) {
netj->time_to_deadline = netj->next_deadline - jack_get_time() - netj->period_usecs;
packet_cache_retreive_packet_pointer( global_packcache, netj->expected_framecnt, (char **) &(netj->rx_buf), netj->rx_bufsize , &packet_recv_time_stamp);
pkthdr = (jacknet_packet_header *) netj->rx_buf;
packet_header_ntoh(pkthdr);
netj->deadline_goodness = (int)pkthdr->sync_state;
netj->packet_data_valid = 1;

// TODO: Queue state could be taken into account.
// But needs more processing, cause, when we are running as
// fast as we can, recv_time_offset can be zero, which is
// good.
// need to add (now-deadline) and check that.
/*
if( recv_time_offset < netj->period_usecs )
//netj->next_deadline -= netj->period_usecs*netj->latency/100;
netj->next_deadline += netj->period_usecs/1000;
*/

if( netj->deadline_goodness != MASTER_FREEWHEELS ) {
if( netj->deadline_goodness < (netj->period_usecs/4+10*(int)netj->period_usecs*netj->latency/100) ) {
netj->deadline_offset -= netj->period_usecs/100;
//jack_log( "goodness: %d, Adjust deadline: --- %d\n", netj->deadline_goodness, (int) netj->period_usecs*netj->latency/100 );
}
if( netj->deadline_goodness > (netj->period_usecs/4+10*(int)netj->period_usecs*netj->latency/100) ) {
netj->deadline_offset += netj->period_usecs/100;
//jack_log( "goodness: %d, Adjust deadline: +++ %d\n", netj->deadline_goodness, (int) netj->period_usecs*netj->latency/100 );
}
}

netj->next_deadline = jack_get_time() + netj->deadline_offset;
} else {
netj->time_to_deadline = 0;
netj->next_deadline = jack_get_time() + netj->deadline_offset;
// bah... the packet is not there.
// either
// - it got lost.
// - its late
// - sync source is not sending anymore.

// lets check if we have the next packets, we will just run a cycle without data.
// in that case.

if( packet_cache_get_next_available_framecnt( global_packcache, netj->expected_framecnt, &next_frame_avail) )
{
jack_nframes_t offset = next_frame_avail - netj->expected_framecnt;

//XXX: hmm... i need to remember why resync_threshold wasnt right.
//if( offset < netj->resync_threshold )
if( offset < 10 ) {
// ok. dont do nothing. we will run without data.
// this seems to be one or 2 lost packets.
//
// this can also be reordered packet jitter.
// (maybe this is not happening in real live)
// but it happens in netem.

netj->packet_data_valid = 0;

// I also found this happening, when the packet queue, is too full.
// but wtf ? use a smaller latency. this link can handle that ;S
if( packet_cache_get_fill( global_packcache, netj->expected_framecnt ) > 80.0 )
netj->next_deadline -= netj->period_usecs/2;


} else {
// the diff is too high. but we have a packet in the future.
// lets resync.
netj->expected_framecnt = next_frame_avail;
packet_cache_retreive_packet_pointer( global_packcache, netj->expected_framecnt, (char **) &(netj->rx_buf), netj->rx_bufsize, NULL );
pkthdr = (jacknet_packet_header *) netj->rx_buf;
packet_header_ntoh(pkthdr);
//netj->deadline_goodness = 0;
netj->deadline_goodness = (int)pkthdr->sync_state - (int)netj->period_usecs * offset;
netj->next_deadline_valid = 0;
netj->packet_data_valid = 1;
}

} else {
// no packets in buffer.
netj->packet_data_valid = 0;

//printf( "frame %d No Packet in queue. num_lost_packets = %d \n", netj->expected_framecnt, netj->num_lost_packets );
if( netj->num_lost_packets < 5 ) {
// ok. No Packet in queue. The packet was either lost,
// or we are running too fast.
//
// Adjusting the deadline unconditionally resulted in
// too many xruns on master.
// But we need to adjust for the case we are running too fast.
// So lets check if the last packet is there now.
//
// It would not be in the queue anymore, if it had been
// retrieved. This might break for redundancy, but
// i will make the packet cache drop redundant packets,
// that have already been retreived.
//
if( packet_cache_get_highest_available_framecnt( global_packcache, &next_frame_avail) ) {
if( next_frame_avail == (netj->expected_framecnt - 1) ) {
// Ok. the last packet is there now.
// and it had not been retrieved.
//
// TODO: We are still dropping 2 packets.
// perhaps we can adjust the deadline
// when (num_packets lost == 0)

// This might still be too much.
netj->next_deadline += netj->period_usecs;
}
}
} else if( (netj->num_lost_packets <= 100) ) {
// lets try adjusting the deadline harder, for some packets, we might have just ran 2 fast.
netj->next_deadline += netj->period_usecs*netj->latency/8;
} else {

// But now we can check for any new frame available.
//
if( packet_cache_get_highest_available_framecnt( global_packcache, &next_frame_avail) ) {
netj->expected_framecnt = next_frame_avail;
packet_cache_retreive_packet_pointer( global_packcache, netj->expected_framecnt, (char **) &(netj->rx_buf), netj->rx_bufsize, NULL );
pkthdr = (jacknet_packet_header *) netj->rx_buf;
packet_header_ntoh(pkthdr);
netj->deadline_goodness = pkthdr->sync_state;
netj->next_deadline_valid = 0;
netj->packet_data_valid = 1;
netj->running_free = 0;
jack_info( "resync after freerun... %d\n", netj->expected_framecnt );
} else {
// give up. lets run freely.
// XXX: hmm...

netj->running_free = 1;

// when we really dont see packets.
// reset source address. and open possibility for new master.
// maybe dsl reconnect. Also restart of netsource without fix
// reply address changes port.
if (netj->num_lost_packets > 200 ) {
netj->srcaddress_valid = 0;
packet_cache_reset_master_address( global_packcache );
}
}
}
}
}

if( !netj->packet_data_valid )
netj->num_lost_packets += 1;
else {
netj->num_lost_packets = 0;
}
}

void netjack_send_silence( netjack_driver_state_t *netj, int syncstate )
{
int tx_size = get_sample_size(netj->bitdepth) * netj->playback_channels * netj->net_period_up + sizeof(jacknet_packet_header);
unsigned int *packet_buf, *packet_bufX;

packet_buf = alloca( tx_size);
jacknet_packet_header *tx_pkthdr = (jacknet_packet_header *)packet_buf;
jacknet_packet_header *rx_pkthdr = (jacknet_packet_header *)netj->rx_buf;

//framecnt = rx_pkthdr->framecnt;

netj->reply_port = rx_pkthdr->reply_port;

// offset packet_bufX by the packetheader.
packet_bufX = packet_buf + sizeof(jacknet_packet_header) / sizeof(jack_default_audio_sample_t);

tx_pkthdr->sync_state = syncstate;
tx_pkthdr->framecnt = netj->expected_framecnt;

// memset 0 the payload.
int payload_size = get_sample_size(netj->bitdepth) * netj->playback_channels * netj->net_period_up;
memset(packet_bufX, 0, payload_size);

packet_header_hton(tx_pkthdr);
if (netj->srcaddress_valid)
{
int r;
if (netj->reply_port)
netj->syncsource_address.sin_port = htons(netj->reply_port);

for( r=0; r<netj->redundancy; r++ )
netjack_sendto(netj->outsockfd, (char *)packet_buf, tx_size,
0, (struct sockaddr*)&(netj->syncsource_address), sizeof(struct sockaddr_in), netj->mtu);
}
}


void netjack_attach( netjack_driver_state_t *netj )
{
//puts ("net_driver_attach");
jack_port_t * port;
char buf[32];
unsigned int chn;
int port_flags;


if (netj->handle_transport_sync)
jack_set_sync_callback(netj->client, (JackSyncCallback) net_driver_sync_cb, NULL);

port_flags = JackPortIsOutput | JackPortIsPhysical | JackPortIsTerminal;

for (chn = 0; chn < netj->capture_channels_audio; chn++) {
snprintf (buf, sizeof(buf) - 1, "capture_%u", chn + 1);

port = jack_port_register (netj->client, buf,
JACK_DEFAULT_AUDIO_TYPE,
port_flags, 0);
if (!port) {
jack_error ("NET: cannot register port for %s", buf);
break;
}

netj->capture_ports =
jack_slist_append (netj->capture_ports, port);

if( netj->bitdepth == CELT_MODE ) {
#if HAVE_CELT
#if HAVE_CELT_API_0_7
celt_int32 lookahead;
CELTMode *celt_mode = celt_mode_create( netj->sample_rate, netj->period_size, NULL );
netj->capture_srcs = jack_slist_append(netj->capture_srcs, celt_decoder_create( celt_mode, 1, NULL ) );
#else
celt_int32_t lookahead;
CELTMode *celt_mode = celt_mode_create( netj->sample_rate, 1, netj->period_size, NULL );
netj->capture_srcs = jack_slist_append(netj->capture_srcs, celt_decoder_create( celt_mode ) );
#endif
celt_mode_info( celt_mode, CELT_GET_LOOKAHEAD, &lookahead );
netj->codec_latency = 2*lookahead;
#endif
} else {
#if HAVE_SAMPLERATE
netj->capture_srcs = jack_slist_append(netj->capture_srcs, src_new(SRC_LINEAR, 1, NULL));
#endif
}
}
for (chn = netj->capture_channels_audio; chn < netj->capture_channels; chn++) {
snprintf (buf, sizeof(buf) - 1, "capture_%u", chn + 1);

port = jack_port_register (netj->client, buf,
JACK_DEFAULT_MIDI_TYPE,
port_flags, 0);
if (!port) {
jack_error ("NET: cannot register port for %s", buf);
break;
}

netj->capture_ports =
jack_slist_append (netj->capture_ports, port);
}

port_flags = JackPortIsInput | JackPortIsPhysical | JackPortIsTerminal;

for (chn = 0; chn < netj->playback_channels_audio; chn++) {
snprintf (buf, sizeof(buf) - 1, "playback_%u", chn + 1);

port = jack_port_register (netj->client, buf,
JACK_DEFAULT_AUDIO_TYPE,
port_flags, 0);

if (!port) {
jack_error ("NET: cannot register port for %s", buf);
break;
}

netj->playback_ports =
jack_slist_append (netj->playback_ports, port);
if( netj->bitdepth == CELT_MODE ) {
#if HAVE_CELT
#if HAVE_CELT_API_0_7
CELTMode *celt_mode = celt_mode_create( netj->sample_rate, netj->period_size, NULL );
netj->playback_srcs = jack_slist_append(netj->playback_srcs, celt_encoder_create( celt_mode, 1, NULL ) );
#else
CELTMode *celt_mode = celt_mode_create( netj->sample_rate, 1, netj->period_size, NULL );
netj->playback_srcs = jack_slist_append(netj->playback_srcs, celt_encoder_create( celt_mode ) );
#endif
#endif
} else {
#if HAVE_SAMPLERATE
netj->playback_srcs = jack_slist_append(netj->playback_srcs, src_new(SRC_LINEAR, 1, NULL));
#endif
}
}
for (chn = netj->playback_channels_audio; chn < netj->playback_channels; chn++) {
snprintf (buf, sizeof(buf) - 1, "playback_%u", chn + 1);

port = jack_port_register (netj->client, buf,
JACK_DEFAULT_MIDI_TYPE,
port_flags, 0);

if (!port) {
jack_error ("NET: cannot register port for %s", buf);
break;
}

netj->playback_ports =
jack_slist_append (netj->playback_ports, port);
}

jack_activate (netj->client);
}


void netjack_detach( netjack_driver_state_t *netj )
{
JSList * node;


for (node = netj->capture_ports; node; node = jack_slist_next (node))
jack_port_unregister (netj->client,
((jack_port_t *) node->data));

jack_slist_free (netj->capture_ports);
netj->capture_ports = NULL;

for (node = netj->playback_ports; node; node = jack_slist_next (node))
jack_port_unregister (netj->client,
((jack_port_t *) node->data));

jack_slist_free (netj->playback_ports);
netj->playback_ports = NULL;
}


netjack_driver_state_t *netjack_init (netjack_driver_state_t *netj,
jack_client_t * client,
const char *name,
unsigned int capture_ports,
unsigned int playback_ports,
unsigned int capture_ports_midi,
unsigned int playback_ports_midi,
jack_nframes_t sample_rate,
jack_nframes_t period_size,
unsigned int listen_port,
unsigned int transport_sync,
unsigned int resample_factor,
unsigned int resample_factor_up,
unsigned int bitdepth,
unsigned int use_autoconfig,
unsigned int latency,
unsigned int redundancy,
int dont_htonl_floats,
int always_deadline)
{

// Fill in netj values.
// might be subject to autoconfig...
// so dont calculate anything with them...


netj->sample_rate = sample_rate;
netj->period_size = period_size;
netj->dont_htonl_floats = dont_htonl_floats;

netj->listen_port = listen_port;

netj->capture_channels = capture_ports + capture_ports_midi;
netj->capture_channels_audio = capture_ports;
netj->capture_channels_midi = capture_ports_midi;
netj->capture_ports = NULL;
netj->playback_channels = playback_ports + playback_ports_midi;
netj->playback_channels_audio = playback_ports;
netj->playback_channels_midi = playback_ports_midi;
netj->playback_ports = NULL;
netj->codec_latency = 0;

netj->handle_transport_sync = transport_sync;
netj->mtu = 1400;
netj->latency = latency;
netj->redundancy = redundancy;
netj->use_autoconfig = use_autoconfig;
netj->always_deadline = always_deadline;


netj->client = client;


if ((bitdepth != 0) && (bitdepth != 8) && (bitdepth != 16) && (bitdepth != CELT_MODE))
{
jack_info ("Invalid bitdepth: %d (8, 16 or 0 for float) !!!", bitdepth);
return NULL;
}
netj->bitdepth = bitdepth;


if (resample_factor_up == 0)
resample_factor_up = resample_factor;

netj->resample_factor = resample_factor;
netj->resample_factor_up = resample_factor_up;


return netj;
}

void netjack_release( netjack_driver_state_t *netj )
{
close( netj->sockfd );
close( netj->outsockfd );

packet_cache_free( global_packcache );
global_packcache = NULL;
}

int
netjack_startup( netjack_driver_state_t *netj )
{
int first_pack_len;
struct sockaddr_in address;
// Now open the socket, and wait for the first packet to arrive...
netj->sockfd = socket (AF_INET, SOCK_DGRAM, 0);
#ifdef WIN32
if (netj->sockfd == INVALID_SOCKET)
#else
if (netj->sockfd == -1)
#endif
{
jack_info ("socket error");
return -1;
}
address.sin_family = AF_INET;
address.sin_port = htons(netj->listen_port);
address.sin_addr.s_addr = htonl(INADDR_ANY);
if (bind (netj->sockfd, (struct sockaddr *) &address, sizeof (address)) < 0)
{
jack_info("bind error");
return -1;
}

netj->outsockfd = socket (AF_INET, SOCK_DGRAM, 0);
#ifdef WIN32
if (netj->outsockfd == INVALID_SOCKET)
#else
if (netj->outsockfd == -1)
#endif
{
jack_info ("socket error");
return -1;
}
netj->srcaddress_valid = 0;
if (netj->use_autoconfig)
{
jacknet_packet_header *first_packet = alloca (sizeof (jacknet_packet_header));
#ifdef WIN32
int address_size = sizeof( struct sockaddr_in );
#else
socklen_t address_size = sizeof (struct sockaddr_in);
#endif
//jack_info ("Waiting for an incoming packet !!!");
//jack_info ("*** IMPORTANT *** Dont connect a client to jackd until the driver is attached to a clock source !!!");

while(1) {
first_pack_len = recvfrom (netj->sockfd, (char *)first_packet, sizeof (jacknet_packet_header), 0, (struct sockaddr*) & netj->syncsource_address, &address_size);
#ifdef WIN32
if( first_pack_len == -1 ) {
first_pack_len = sizeof(jacknet_packet_header);
break;
}
#else
if (first_pack_len == sizeof (jacknet_packet_header))
break;
#endif
}
netj->srcaddress_valid = 1;

if (first_pack_len == sizeof (jacknet_packet_header))
{
packet_header_ntoh (first_packet);

jack_info ("AutoConfig Override !!!");
if (netj->sample_rate != first_packet->sample_rate)
{
jack_info ("AutoConfig Override: Master JACK sample rate = %d", first_packet->sample_rate);
netj->sample_rate = first_packet->sample_rate;
}

if (netj->period_size != first_packet->period_size)
{
jack_info ("AutoConfig Override: Master JACK period size is %d", first_packet->period_size);
netj->period_size = first_packet->period_size;
}
if (netj->capture_channels_audio != first_packet->capture_channels_audio)
{
jack_info ("AutoConfig Override: capture_channels_audio = %d", first_packet->capture_channels_audio);
netj->capture_channels_audio = first_packet->capture_channels_audio;
}
if (netj->capture_channels_midi != first_packet->capture_channels_midi)
{
jack_info ("AutoConfig Override: capture_channels_midi = %d", first_packet->capture_channels_midi);
netj->capture_channels_midi = first_packet->capture_channels_midi;
}
if (netj->playback_channels_audio != first_packet->playback_channels_audio)
{
jack_info ("AutoConfig Override: playback_channels_audio = %d", first_packet->playback_channels_audio);
netj->playback_channels_audio = first_packet->playback_channels_audio;
}
if (netj->playback_channels_midi != first_packet->playback_channels_midi)
{
jack_info ("AutoConfig Override: playback_channels_midi = %d", first_packet->playback_channels_midi);
netj->playback_channels_midi = first_packet->playback_channels_midi;
}

netj->mtu = first_packet->mtu;
jack_info ("MTU is set to %d bytes", first_packet->mtu);
netj->latency = first_packet->latency;
}
}
netj->capture_channels = netj->capture_channels_audio + netj->capture_channels_midi;
netj->playback_channels = netj->playback_channels_audio + netj->playback_channels_midi;

// After possible Autoconfig: do all calculations...
netj->period_usecs =
(jack_time_t) floor ((((float) netj->period_size) / (float)netj->sample_rate)
* 1000000.0f);
if( netj->latency == 0 )
netj->deadline_offset = 50*netj->period_usecs;
else
netj->deadline_offset = netj->period_usecs + 10*netj->latency*netj->period_usecs/100;

if( netj->bitdepth == CELT_MODE ) {
// celt mode.
// TODO: this is a hack. But i dont want to change the packet header.
netj->net_period_down = netj->resample_factor;
netj->net_period_up = netj->resample_factor_up;
} else {
netj->net_period_down = (float) netj->period_size / (float) netj->resample_factor;
netj->net_period_up = (float) netj->period_size / (float) netj->resample_factor_up;
}

netj->rx_bufsize = sizeof (jacknet_packet_header) + netj->net_period_down * netj->capture_channels * get_sample_size (netj->bitdepth);
netj->pkt_buf = malloc (netj->rx_bufsize);
global_packcache = packet_cache_new (netj->latency + 50, netj->rx_bufsize, netj->mtu);

netj->expected_framecnt_valid = 0;
netj->num_lost_packets = 0;
netj->next_deadline_valid = 0;
netj->deadline_goodness = 0;
netj->time_to_deadline = 0;

// Special handling for latency=0
if( netj->latency == 0 )
netj->resync_threshold = 0;
else
netj->resync_threshold = MIN( 15, netj->latency-1 );

netj->running_free = 0;

return 0;
}

+ 145
- 0
common/netjack.h View File

@@ -0,0 +1,145 @@

/*
Copyright (C) 2003 Robert Ham <rah@bash.sh>
Copyright (C) 2005 Torben Hohn <torbenh@gmx.de>

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program 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 General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

#ifndef __NETJACK_H__
#define __NETJACK_H__

#include <unistd.h>

#include <jack/types.h>
//#include <jack/driver.h>
#include <jack/jack.h>
#include <jack/transport.h>

#include "jack/jslist.h"

//#include <netinet/in.h>

#ifdef __cplusplus
extern "C"
{
#endif

typedef struct _netjack_driver_state netjack_driver_state_t;

struct _netjack_driver_state {
jack_nframes_t net_period_up;
jack_nframes_t net_period_down;

jack_nframes_t sample_rate;
jack_nframes_t bitdepth;
jack_nframes_t period_size;
jack_time_t period_usecs;
int dont_htonl_floats;
int always_deadline;

jack_nframes_t codec_latency;

unsigned int listen_port;

unsigned int capture_channels;
unsigned int playback_channels;
unsigned int capture_channels_audio;
unsigned int playback_channels_audio;
unsigned int capture_channels_midi;
unsigned int playback_channels_midi;

JSList *capture_ports;
JSList *playback_ports;
JSList *playback_srcs;
JSList *capture_srcs;

jack_client_t *client;

#ifdef WIN32
SOCKET sockfd;
SOCKET outsockfd;
#else
int sockfd;
int outsockfd;
#endif

struct sockaddr_in syncsource_address;

int reply_port;
int srcaddress_valid;

int sync_state;
unsigned int handle_transport_sync;

unsigned int *rx_buf;
unsigned int *pkt_buf;
unsigned int rx_bufsize;
//unsigned int tx_bufsize;
unsigned int mtu;
unsigned int latency;
unsigned int redundancy;

jack_nframes_t expected_framecnt;
int expected_framecnt_valid;
unsigned int num_lost_packets;
jack_time_t next_deadline;
jack_time_t deadline_offset;
int next_deadline_valid;
int packet_data_valid;
int resync_threshold;
int running_free;
int deadline_goodness;
jack_time_t time_to_deadline;
unsigned int use_autoconfig;
unsigned int resample_factor;
unsigned int resample_factor_up;
};

void netjack_wait( netjack_driver_state_t *netj );
void netjack_send_silence( netjack_driver_state_t *netj, int syncstate );
void netjack_read( netjack_driver_state_t *netj, jack_nframes_t nframes ) ;
void netjack_write( netjack_driver_state_t *netj, jack_nframes_t nframes, int syncstate );
void netjack_attach( netjack_driver_state_t *netj );
void netjack_detach( netjack_driver_state_t *netj );

netjack_driver_state_t *netjack_init (netjack_driver_state_t *netj,
jack_client_t * client,
const char *name,
unsigned int capture_ports,
unsigned int playback_ports,
unsigned int capture_ports_midi,
unsigned int playback_ports_midi,
jack_nframes_t sample_rate,
jack_nframes_t period_size,
unsigned int listen_port,
unsigned int transport_sync,
unsigned int resample_factor,
unsigned int resample_factor_up,
unsigned int bitdepth,
unsigned int use_autoconfig,
unsigned int latency,
unsigned int redundancy,
int dont_htonl_floats,
int always_deadline);

void netjack_release( netjack_driver_state_t *netj );
int netjack_startup( netjack_driver_state_t *netj );

#ifdef __cplusplus
}
#endif

#endif

+ 1545
- 0
common/netjack_packet.c
File diff suppressed because it is too large
View File


+ 165
- 0
common/netjack_packet.h View File

@@ -0,0 +1,165 @@

/*
* NetJack - Packet Handling functions
*
* used by the driver and the jacknet_client
*
* Copyright (C) 2006 Torben Hohn <torbenh@gmx.de>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Id: net_driver.c,v 1.16 2006/03/20 19:41:37 torbenh Exp $
*
*/

#ifndef __JACK_NET_PACKET_H__
#define __JACK_NET_PACKET_H__

#ifdef __cplusplus
extern "C"
{
#endif

#include <jack/jack.h>
#include <jack/types.h>
//#include <jack/engine.h>
#include <jack/jslist.h>

#include <jack/midiport.h>

//#include <netinet/in.h>
// The Packet Header.

#define CELT_MODE 1000 // Magic bitdepth value that indicates CELT compression
#define MASTER_FREEWHEELS 0x80000000

typedef struct _jacknet_packet_header jacknet_packet_header;

struct _jacknet_packet_header
{
// General AutoConf Data
jack_nframes_t capture_channels_audio;
jack_nframes_t playback_channels_audio;
jack_nframes_t capture_channels_midi;
jack_nframes_t playback_channels_midi;
jack_nframes_t period_size;
jack_nframes_t sample_rate;

// Transport Sync
jack_nframes_t sync_state;
jack_nframes_t transport_frame;
jack_nframes_t transport_state;

// Packet loss Detection, and latency reduction
jack_nframes_t framecnt;
jack_nframes_t latency;

jack_nframes_t reply_port;
jack_nframes_t mtu;
jack_nframes_t fragment_nr;
};

typedef union _int_float int_float_t;

union _int_float
{
uint32_t i;
float f;
};

// fragment reorder cache.
typedef struct _cache_packet cache_packet;

struct _cache_packet
{
int valid;
int num_fragments;
int packet_size;
int mtu;
jack_time_t recv_timestamp;
jack_nframes_t framecnt;
char * fragment_array;
char * packet_buf;
};

typedef struct _packet_cache packet_cache;

struct _packet_cache
{
int size;
cache_packet *packets;
int mtu;
struct sockaddr_in master_address;
int master_address_valid;
jack_nframes_t last_framecnt_retreived;
int last_framecnt_retreived_valid;
};

extern packet_cache *global_packcache;

// fragment cache function prototypes
// XXX: Some of these are private.
packet_cache *packet_cache_new(int num_packets, int pkt_size, int mtu);
void packet_cache_free(packet_cache *pkt_cache);

cache_packet *packet_cache_get_packet(packet_cache *pkt_cache, jack_nframes_t framecnt);
cache_packet *packet_cache_get_oldest_packet(packet_cache *pkt_cache);
cache_packet *packet_cache_get_free_packet(packet_cache *pkt_cache);

void cache_packet_reset(cache_packet *pack);
void cache_packet_set_framecnt(cache_packet *pack, jack_nframes_t framecnt);
void cache_packet_add_fragment(cache_packet *pack, char *packet_buf, int rcv_len);
int cache_packet_is_complete(cache_packet *pack);

void packet_cache_drain_socket( packet_cache *pcache, int sockfd );
void packet_cache_reset_master_address( packet_cache *pcache );
float packet_cache_get_fill( packet_cache *pcache, jack_nframes_t expected_framecnt );
int packet_cache_retreive_packet_pointer( packet_cache *pcache, jack_nframes_t framecnt, char **packet_buf, int pkt_size, jack_time_t *timestamp );
int packet_cache_release_packet( packet_cache *pcache, jack_nframes_t framecnt );
int packet_cache_get_next_available_framecnt( packet_cache *pcache, jack_nframes_t expected_framecnt, jack_nframes_t *framecnt );
int packet_cache_get_highest_available_framecnt( packet_cache *pcache, jack_nframes_t *framecnt );
int packet_cache_find_latency( packet_cache *pcache, jack_nframes_t expected_framecnt, jack_nframes_t *framecnt );
// Function Prototypes

int netjack_poll_deadline (int sockfd, jack_time_t deadline);

void netjack_sendto(int sockfd, char *packet_buf, int pkt_size, int flags, struct sockaddr *addr, int addr_size, int mtu);


int get_sample_size(int bitdepth);
void packet_header_hton(jacknet_packet_header *pkthdr);

void packet_header_ntoh(jacknet_packet_header *pkthdr);

void render_payload_to_jack_ports(int bitdepth, void *packet_payload, jack_nframes_t net_period_down, JSList *capture_ports, JSList *capture_srcs, jack_nframes_t nframes, int dont_htonl_floats );

void render_jack_ports_to_payload(int bitdepth, JSList *playback_ports, JSList *playback_srcs, jack_nframes_t nframes, void *packet_payload, jack_nframes_t net_period_up, int dont_htonl_floats );


// XXX: This is sort of deprecated:
// This one waits forever. an is not using ppoll
int netjack_poll(int sockfd, int timeout);

// TODO: these are deprecated.
//int netjack_recvfrom(int sockfd, char *packet_buf, int pkt_size, int flags, struct sockaddr *addr, socklen_t *addr_size, int mtu);
//int netjack_recv(int sockfd, char *packet_buf, int pkt_size, int flags, int mtu);

void decode_midi_buffer (uint32_t *buffer_uint32, unsigned int buffer_size_uint32, jack_default_audio_sample_t* buf);
void encode_midi_buffer (uint32_t *buffer_uint32, unsigned int buffer_size_uint32, jack_default_audio_sample_t* buf);
#ifdef __cplusplus
}
#endif
#endif


+ 757
- 0
example-clients/alsa_in.c View File

@@ -0,0 +1,757 @@
/** @file simple_client.c
*
* @brief This simple client demonstrates the basic features of JACK
* as they would be used by many applications.
*/

#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>

#include <alloca.h>
#include <math.h>

#include <jack/jack.h>
#include <jack/jslist.h>
#include <memops.h>

#include "alsa/asoundlib.h"

#include <samplerate.h>

// Here are the lists of the jack ports...

JSList *capture_ports = NULL;
JSList *capture_srcs = NULL;
JSList *playback_ports = NULL;
JSList *playback_srcs = NULL;
jack_client_t *client;

snd_pcm_t *alsa_handle;

int jack_sample_rate;
int jack_buffer_size;

int quit = 0;
double resample_mean = 1.0;
double static_resample_factor = 1.0;

double *offset_array;
double *window_array;
int offset_differential_index = 0;

double offset_integral = 0;

// ------------------------------------------------------ commandline parameters

int sample_rate = 0; /* stream rate */
int num_channels = 2; /* count of channels */
int period_size = 1024;
int num_periods = 2;

int target_delay = 0; /* the delay which the program should try to approach. */
int max_diff = 0; /* the diff value, when a hard readpointer skip should occur */
int catch_factor = 100000;
int catch_factor2 = 10000;
double pclamp = 15.0;
double controlquant = 10000.0;
int smooth_size = 256;
int good_window=0;
int verbose = 0;
int instrument = 0;
int samplerate_quality = 2;

// Debug stuff:

volatile float output_resampling_factor = 1.0;
volatile int output_new_delay = 0;
volatile float output_offset = 0.0;
volatile float output_integral = 0.0;
volatile float output_diff = 0.0;

snd_pcm_uframes_t real_buffer_size;
snd_pcm_uframes_t real_period_size;

// format selection, and corresponding functions from memops in a nice set of structs.

typedef struct alsa_format {
snd_pcm_format_t format_id;
size_t sample_size;
void (*jack_to_soundcard) (char *dst, jack_default_audio_sample_t *src, unsigned long nsamples, unsigned long dst_skip, dither_state_t *state);
void (*soundcard_to_jack) (jack_default_audio_sample_t *dst, char *src, unsigned long nsamples, unsigned long src_skip);
const char *name;
} alsa_format_t;

alsa_format_t formats[] = {
{ SND_PCM_FORMAT_S24_3LE, 3, sample_move_d24_sS, sample_move_dS_s24, "24bit - real" },
{ SND_PCM_FORMAT_FLOAT_LE, 4, sample_move_dS_floatLE, sample_move_floatLE_sSs, "float" },
{ SND_PCM_FORMAT_S32, 4, sample_move_d32u24_sS, sample_move_dS_s32u24, "32bit" },
{ SND_PCM_FORMAT_S24, 4, sample_move_d24_sS, sample_move_dS_s24, "24bit" },
{ SND_PCM_FORMAT_S16, 2, sample_move_d16_sS, sample_move_dS_s16, "16bit" }
};
#define NUMFORMATS (sizeof(formats)/sizeof(formats[0]))
int format=0;

// Alsa stuff... i dont want to touch this bullshit in the next years.... please...

static int xrun_recovery(snd_pcm_t *handle, int err) {
// printf( "xrun !!!.... %d\n", err );
if (err == -EPIPE) { /* under-run */
err = snd_pcm_prepare(handle);
if (err < 0)
printf("Can't recovery from underrun, prepare failed: %s\n", snd_strerror(err));
return 0;
} else if (err == -EAGAIN) {
while ((err = snd_pcm_resume(handle)) == -EAGAIN)
usleep(100); /* wait until the suspend flag is released */
if (err < 0) {
err = snd_pcm_prepare(handle);
if (err < 0)
printf("Can't recovery from suspend, prepare failed: %s\n", snd_strerror(err));
}
return 0;
}
return err;
}

static int set_hwformat( snd_pcm_t *handle, snd_pcm_hw_params_t *params )
{
int i;
int err;

for( i=0; i<NUMFORMATS; i++ ) {
/* set the sample format */
err = snd_pcm_hw_params_set_format(handle, params, formats[i].format_id);
if (err == 0) {
format = i;
return 0;
}
}

return err;
}

static int set_hwparams(snd_pcm_t *handle, snd_pcm_hw_params_t *params, snd_pcm_access_t access, int rate, int channels, int period, int nperiods ) {
int err, dir=0;
unsigned int buffer_time;
unsigned int period_time;
unsigned int rrate;
unsigned int rchannels;

/* choose all parameters */
err = snd_pcm_hw_params_any(handle, params);
if (err < 0) {
printf("Broken configuration for playback: no configurations available: %s\n", snd_strerror(err));
return err;
}
/* set the interleaved read/write format */
err = snd_pcm_hw_params_set_access(handle, params, access);
if (err < 0) {
printf("Access type not available for playback: %s\n", snd_strerror(err));
return err;
}

/* set the sample format */
err = set_hwformat(handle, params);
if (err < 0) {
printf("Sample format not available for playback: %s\n", snd_strerror(err));
return err;
}
/* set the count of channels */
rchannels = channels;
err = snd_pcm_hw_params_set_channels_near(handle, params, &rchannels);
if (err < 0) {
printf("Channels count (%i) not available for record: %s\n", channels, snd_strerror(err));
return err;
}
if (rchannels != channels) {
printf("WARNING: chennel count does not match (requested %d got %d)\n", channels, rchannels);
num_channels = rchannels;
}
/* set the stream rate */
rrate = rate;
err = snd_pcm_hw_params_set_rate_near(handle, params, &rrate, 0);
if (err < 0) {
printf("Rate %iHz not available for playback: %s\n", rate, snd_strerror(err));
return err;
}
if (rrate != rate) {
printf("WARNING: Rate doesn't match (requested %iHz, get %iHz)\n", rate, rrate);
sample_rate = rrate;
}
/* set the buffer time */

buffer_time = 1000000*period*nperiods/rate;
err = snd_pcm_hw_params_set_buffer_time_near(handle, params, &buffer_time, &dir);
if (err < 0) {
printf("Unable to set buffer time %i for playback: %s\n", 1000000*period*nperiods/rate, snd_strerror(err));
return err;
}
err = snd_pcm_hw_params_get_buffer_size( params, &real_buffer_size );
if (err < 0) {
printf("Unable to get buffer size back: %s\n", snd_strerror(err));
return err;
}
if( real_buffer_size != nperiods * period ) {
printf( "WARNING: buffer size does not match: (requested %d, got %d)\n", nperiods * period, (int) real_buffer_size );
}
/* set the period time */
period_time = 1000000*period/rate;
err = snd_pcm_hw_params_set_period_time_near(handle, params, &period_time, &dir);
if (err < 0) {
printf("Unable to set period time %i for playback: %s\n", 1000000*period/rate, snd_strerror(err));
return err;
}
err = snd_pcm_hw_params_get_period_size(params, &real_period_size, NULL );
if (err < 0) {
printf("Unable to get period size back: %s\n", snd_strerror(err));
return err;
}
if( real_period_size != period ) {
printf( "WARNING: period size does not match: (requested %i, got %i)\n", period, (int)real_period_size );
}
/* write the parameters to device */
err = snd_pcm_hw_params(handle, params);
if (err < 0) {
printf("Unable to set hw params for playback: %s\n", snd_strerror(err));
return err;
}
return 0;
}

static int set_swparams(snd_pcm_t *handle, snd_pcm_sw_params_t *swparams, int period) {
int err;

/* get the current swparams */
err = snd_pcm_sw_params_current(handle, swparams);
if (err < 0) {
printf("Unable to determine current swparams for capture: %s\n", snd_strerror(err));
return err;
}
/* start the transfer when the buffer is full */
err = snd_pcm_sw_params_set_start_threshold(handle, swparams, period );
if (err < 0) {
printf("Unable to set start threshold mode for capture: %s\n", snd_strerror(err));
return err;
}
err = snd_pcm_sw_params_set_stop_threshold(handle, swparams, -1 );
if (err < 0) {
printf("Unable to set start threshold mode for capture: %s\n", snd_strerror(err));
return err;
}
/* allow the transfer when at least period_size samples can be processed */
err = snd_pcm_sw_params_set_avail_min(handle, swparams, 2*period );
if (err < 0) {
printf("Unable to set avail min for capture: %s\n", snd_strerror(err));
return err;
}
/* align all transfers to 1 sample */
err = snd_pcm_sw_params_set_xfer_align(handle, swparams, 1);
if (err < 0) {
printf("Unable to set transfer align for capture: %s\n", snd_strerror(err));
return err;
}
/* write the parameters to the playback device */
err = snd_pcm_sw_params(handle, swparams);
if (err < 0) {
printf("Unable to set sw params for capture: %s\n", snd_strerror(err));
return err;
}
return 0;
}

// ok... i only need this function to communicate with the alsa bloat api...

static snd_pcm_t *open_audiofd( char *device_name, int capture, int rate, int channels, int period, int nperiods ) {
int err;
snd_pcm_t *handle;
snd_pcm_hw_params_t *hwparams;
snd_pcm_sw_params_t *swparams;

snd_pcm_hw_params_alloca(&hwparams);
snd_pcm_sw_params_alloca(&swparams);

if ((err = snd_pcm_open(&(handle), device_name, capture ? SND_PCM_STREAM_CAPTURE : SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK )) < 0) {
printf("Capture open error: %s\n", snd_strerror(err));
return NULL;
}

if ((err = set_hwparams(handle, hwparams,SND_PCM_ACCESS_RW_INTERLEAVED, rate, channels, period, nperiods )) < 0) {
printf("Setting of hwparams failed: %s\n", snd_strerror(err));
return NULL;
}
if ((err = set_swparams(handle, swparams, period)) < 0) {
printf("Setting of swparams failed: %s\n", snd_strerror(err));
return NULL;
}

snd_pcm_start( handle );
snd_pcm_wait( handle, 200 );

return handle;
}

double hann( double x )
{
return 0.5 * (1.0 - cos( 2*M_PI * x ) );
}

/**
* The process callback for this JACK application.
* It is called by JACK at the appropriate times.
*/
int process (jack_nframes_t nframes, void *arg) {

char *outbuf;
float *resampbuf;
int rlen;
int err;
snd_pcm_sframes_t delay = target_delay;
int put_back_samples=0;
int i;

snd_pcm_delay( alsa_handle, &delay );

//delay -= jack_frames_since_cycle_start( client );
// Do it the hard way.
// this is for compensating xruns etc...

if( delay > (target_delay+max_diff) ) {
char *tmp = alloca( (delay-target_delay) * formats[format].sample_size * num_channels );
snd_pcm_readi( alsa_handle, tmp, delay-target_delay );
output_new_delay = (int) delay;

delay = target_delay;

// Set the resample_rate... we need to adjust the offset integral, to do this.
// first look at the PI controller, this code is just a special case, which should never execute once
// everything is swung in.
offset_integral = - (resample_mean - static_resample_factor) * catch_factor * catch_factor2;
// Also clear the array. we are beginning a new control cycle.
for( i=0; i<smooth_size; i++ )
offset_array[i] = 0.0;
}
if( delay < (target_delay-max_diff) ) {
snd_pcm_rewind( alsa_handle, target_delay - delay );
output_new_delay = (int) delay;
delay = target_delay;

// Set the resample_rate... we need to adjust the offset integral, to do this.
offset_integral = - (resample_mean - static_resample_factor) * catch_factor * catch_factor2;
// Also clear the array. we are beginning a new control cycle.
for( i=0; i<smooth_size; i++ )
offset_array[i] = 0.0;
}
/* ok... now we should have target_delay +- max_diff on the alsa side.
*
* calculate the number of frames, we want to get.
*/

double offset = delay - target_delay;

// Save offset.
offset_array[(offset_differential_index++)% smooth_size ] = offset;

// Build the mean of the windowed offset array
// basically fir lowpassing.
double smooth_offset = 0.0;
for( i=0; i<smooth_size; i++ )
smooth_offset +=
offset_array[ (i + offset_differential_index-1) % smooth_size] * window_array[i];
smooth_offset /= (double) smooth_size;

// this is the integral of the smoothed_offset
offset_integral += smooth_offset;

// Clamp offset.
// the smooth offset still contains unwanted noise
// which would go straigth onto the resample coeff.
// it only used in the P component and the I component is used for the fine tuning anyways.
if( fabs( smooth_offset ) < pclamp )
smooth_offset = 0.0;

// ok. now this is the PI controller.
// u(t) = K * ( e(t) + 1/T \int e(t') dt' )
// K = 1/catch_factor and T = catch_factor2
double current_resample_factor = static_resample_factor - smooth_offset / (double) catch_factor - offset_integral / (double) catch_factor / (double)catch_factor2;

// now quantize this value around resample_mean, so that the noise which is in the integral component doesnt hurt.
current_resample_factor = floor( (current_resample_factor - resample_mean) * controlquant + 0.5 ) / controlquant + resample_mean;

// Output "instrumentatio" gonna change that to real instrumentation in a few.
output_resampling_factor = (float) current_resample_factor;
output_diff = (float) smooth_offset;
output_integral = (float) offset_integral;
output_offset = (float) offset;

// Clamp a bit.
if( current_resample_factor < 0.25 ) current_resample_factor = 0.25;
if( current_resample_factor > 4 ) current_resample_factor = 4;

// Now Calculate how many samples we need.
rlen = ceil( ((double)nframes) / current_resample_factor )+2;
assert( rlen > 2 );

// Calculate resample_mean so we can init ourselves to saner values.
resample_mean = 0.9999 * resample_mean + 0.0001 * current_resample_factor;
/*
* now this should do it...
*/

outbuf = alloca( rlen * formats[format].sample_size * num_channels );

resampbuf = alloca( rlen * sizeof( float ) );

// get the data...
again:
err = snd_pcm_readi(alsa_handle, outbuf, rlen);
if( err < 0 ) {
printf( "err = %d\n", err );
if (xrun_recovery(alsa_handle, err) < 0) {
//printf("Write error: %s\n", snd_strerror(err));
//exit(EXIT_FAILURE);
}
goto again;
}
if( err != rlen ) {
//printf( "read = %d\n", rlen );
}

/*
* render jack ports to the outbuf...
*/

int chn = 0;
JSList *node = capture_ports;
JSList *src_node = capture_srcs;
SRC_DATA src;

while ( node != NULL)
{
jack_port_t *port = (jack_port_t *) node->data;
float *buf = jack_port_get_buffer (port, nframes);

SRC_STATE *src_state = src_node->data;

formats[format].soundcard_to_jack( resampbuf, outbuf + format[formats].sample_size * chn, rlen, num_channels*format[formats].sample_size );

src.data_in = resampbuf;
src.input_frames = rlen;

src.data_out = buf;
src.output_frames = nframes;
src.end_of_input = 0;

src.src_ratio = current_resample_factor;

src_process( src_state, &src );

put_back_samples = rlen-src.input_frames_used;

src_node = jack_slist_next (src_node);
node = jack_slist_next (node);
chn++;
}

// Put back the samples libsamplerate did not consume.
//printf( "putback = %d\n", put_back_samples );
snd_pcm_rewind( alsa_handle, put_back_samples );

return 0;
}


/**
* Allocate the necessary jack ports...
*/

void alloc_ports( int n_capture, int n_playback ) {

int port_flags = JackPortIsOutput;
int chn;
jack_port_t *port;
char buf[32];

capture_ports = NULL;
for (chn = 0; chn < n_capture; chn++)
{
snprintf (buf, sizeof(buf) - 1, "capture_%u", chn+1);

port = jack_port_register (client, buf,
JACK_DEFAULT_AUDIO_TYPE,
port_flags, 0);

if (!port)
{
printf( "jacknet_client: cannot register port for %s", buf);
break;
}

capture_srcs = jack_slist_append( capture_srcs, src_new( 4-samplerate_quality, 1, NULL ) );
capture_ports = jack_slist_append (capture_ports, port);
}

port_flags = JackPortIsInput;

playback_ports = NULL;
for (chn = 0; chn < n_playback; chn++)
{
snprintf (buf, sizeof(buf) - 1, "playback_%u", chn+1);

port = jack_port_register (client, buf,
JACK_DEFAULT_AUDIO_TYPE,
port_flags, 0);

if (!port)
{
printf( "jacknet_client: cannot register port for %s", buf);
break;
}

playback_srcs = jack_slist_append( playback_srcs, src_new( 4-samplerate_quality, 1, NULL ) );
playback_ports = jack_slist_append (playback_ports, port);
}
}

/**
* This is the shutdown callback for this JACK application.
* It is called by JACK if the server ever shuts down or
* decides to disconnect the client.
*/

void jack_shutdown (void *arg) {

exit (1);
}

/**
* be user friendly.
* be user friendly.
* be user friendly.
*/

void printUsage() {
fprintf(stderr, "usage: alsa_out [options]\n"
"\n"
" -j <jack name> - client name\n"
" -d <alsa_device> \n"
" -c <channels> \n"
" -p <period_size> \n"
" -n <num_period> \n"
" -r <sample_rate> \n"
" -q <sample_rate quality [0..4]\n"
" -m <max_diff> \n"
" -t <target_delay> \n"
" -i turns on instrumentation\n"
" -v turns on printouts\n"
"\n");
}


/**
* the main function....
*/

void
sigterm_handler( int signal )
{
quit = 1;
}


int main (int argc, char *argv[]) {
char jack_name[30] = "alsa_in";
char alsa_device[30] = "hw:0";

extern char *optarg;
extern int optind, optopt;
int errflg=0;
int c;

while ((c = getopt(argc, argv, "ivj:r:c:p:n:d:q:m:t:f:F:C:Q:s:")) != -1) {
switch(c) {
case 'j':
strcpy(jack_name,optarg);
break;
case 'r':
sample_rate = atoi(optarg);
break;
case 'c':
num_channels = atoi(optarg);
break;
case 'p':
period_size = atoi(optarg);
break;
case 'n':
num_periods = atoi(optarg);
break;
case 'd':
strcpy(alsa_device,optarg);
break;
case 't':
target_delay = atoi(optarg);
break;
case 'q':
samplerate_quality = atoi(optarg);
break;
case 'm':
max_diff = atoi(optarg);
break;
case 'f':
catch_factor = atoi(optarg);
break;
case 'F':
catch_factor2 = atoi(optarg);
break;
case 'C':
pclamp = (double) atoi(optarg);
break;
case 'Q':
controlquant = (double) atoi(optarg);
break;
case 'v':
verbose = 1;
break;
case 'i':
instrument = 1;
break;
case 's':
smooth_size = atoi(optarg);
break;
case ':':
fprintf(stderr,
"Option -%c requires an operand\n", optopt);
errflg++;
break;
case '?':
fprintf(stderr,
"Unrecognized option: -%c\n", optopt);
errflg++;
}
}
if (errflg) {
printUsage();
exit(2);
}

if( (samplerate_quality < 0) || (samplerate_quality > 4) ) {
fprintf (stderr, "invalid samplerate quality\n");
return 1;
}
if ((client = jack_client_open (jack_name, 0, NULL)) == 0) {
fprintf (stderr, "jack server not running?\n");
return 1;
}

/* tell the JACK server to call `process()' whenever
there is work to be done.
*/

jack_set_process_callback (client, process, 0);

/* tell the JACK server to call `jack_shutdown()' if
it ever shuts down, either entirely, or if it
just decides to stop calling us.
*/

jack_on_shutdown (client, jack_shutdown, 0);


// get jack sample_rate
jack_sample_rate = jack_get_sample_rate( client );

if( !sample_rate )
sample_rate = jack_sample_rate;

// now open the alsa fd...
alsa_handle = open_audiofd( alsa_device, 1, sample_rate, num_channels, period_size, num_periods);
if( alsa_handle == 0 )
exit(20);

printf( "selected sample format: %s\n", formats[format].name );

static_resample_factor = (double) jack_sample_rate / (double) sample_rate;
resample_mean = static_resample_factor;

offset_array = malloc( sizeof(double) * smooth_size );
if( offset_array == NULL ) {
fprintf( stderr, "no memory for offset_array !!!\n" );
exit(20);
}
window_array = malloc( sizeof(double) * smooth_size );
if( window_array == NULL ) {
fprintf( stderr, "no memory for window_array !!!\n" );
exit(20);
}
int i;
for( i=0; i<smooth_size; i++ ) {
offset_array[i] = 0.0;
window_array[i] = hann( (double) i / ((double) smooth_size - 1.0) );
}

jack_buffer_size = jack_get_buffer_size( client );
// Setup target delay and max_diff for the normal user, who does not play with them...
if( !target_delay )
target_delay = (num_periods*period_size / 2) - jack_buffer_size/2;

if( !max_diff )
max_diff = target_delay;

if( max_diff > target_delay ) {
fprintf( stderr, "target_delay (%d) cant be smaller than max_diff(%d)\n", target_delay, max_diff );
exit(20);
}
if( (target_delay+max_diff) > (num_periods*period_size) ) {
fprintf( stderr, "target_delay+max_diff (%d) cant be bigger than buffersize(%d)\n", target_delay+max_diff, num_periods*period_size );
exit(20);
}
// alloc input ports, which are blasted out to alsa...
alloc_ports( num_channels, 0 );


/* tell the JACK server that we are ready to roll */

if (jack_activate (client)) {
fprintf (stderr, "cannot activate client");
return 1;
}

signal( SIGTERM, sigterm_handler );
signal( SIGINT, sigterm_handler );

if( verbose ) {
while(!quit) {
usleep(500000);
if( output_new_delay ) {
printf( "delay = %d\n", output_new_delay );
output_new_delay = 0;
}
printf( "res: %f, \tdiff = %f, \toffset = %f \n", output_resampling_factor, output_diff, output_offset );
}
} else if( instrument ) {
printf( "# n\tresamp\tdiff\toffseti\tintegral\n");
int n=0;
while(!quit) {
usleep(1000);
printf( "%d\t%f\t%f\t%f\t%f\n", n++, output_resampling_factor, output_diff, output_offset, output_integral );
}
} else {
while(!quit)
{
usleep(500000);
if( output_new_delay ) {
printf( "delay = %d\n", output_new_delay );
output_new_delay = 0;
}
}
}

jack_deactivate( client );
jack_client_close (client);
exit (0);
}


+ 755
- 0
example-clients/alsa_out.c View File

@@ -0,0 +1,755 @@
/** @file simple_client.c
*
* @brief This simple client demonstrates the basic features of JACK
* as they would be used by many applications.
*/

#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>

#include <alloca.h>
#include <math.h>

#include <jack/jack.h>
#include <jack/jslist.h>
#include <memops.h>

#include "alsa/asoundlib.h"

#include <samplerate.h>

// Here are the lists of the jack ports...

JSList *capture_ports = NULL;
JSList *capture_srcs = NULL;
JSList *playback_ports = NULL;
JSList *playback_srcs = NULL;
jack_client_t *client;

snd_pcm_t *alsa_handle;

int jack_sample_rate;
int jack_buffer_size;

double resample_mean = 1.0;
double static_resample_factor = 1.0;

double *offset_array;
double *window_array;
int offset_differential_index = 0;

double offset_integral = 0;
int quit = 0;

// ------------------------------------------------------ commandline parameters

int sample_rate = 0; /* stream rate */
int num_channels = 2; /* count of channels */
int period_size = 1024;
int num_periods = 2;

int target_delay = 0; /* the delay which the program should try to approach. */
int max_diff = 0; /* the diff value, when a hard readpointer skip should occur */
int catch_factor = 100000;
int catch_factor2 = 10000;
double pclamp = 15.0;
double controlquant = 10000.0;
int smooth_size = 256;
int good_window=0;
int verbose = 0;
int instrument = 0;
int samplerate_quality = 2;

// Debug stuff:

volatile float output_resampling_factor = 1.0;
volatile int output_new_delay = 0;
volatile float output_offset = 0.0;
volatile float output_integral = 0.0;
volatile float output_diff = 0.0;

snd_pcm_uframes_t real_buffer_size;
snd_pcm_uframes_t real_period_size;

// format selection, and corresponding functions from memops in a nice set of structs.

typedef struct alsa_format {
snd_pcm_format_t format_id;
size_t sample_size;
void (*jack_to_soundcard) (char *dst, jack_default_audio_sample_t *src, unsigned long nsamples, unsigned long dst_skip, dither_state_t *state);
void (*soundcard_to_jack) (jack_default_audio_sample_t *dst, char *src, unsigned long nsamples, unsigned long src_skip);
const char *name;
} alsa_format_t;

alsa_format_t formats[] = {
{ SND_PCM_FORMAT_FLOAT_LE, 4, sample_move_dS_floatLE, sample_move_floatLE_sSs, "float" },
{ SND_PCM_FORMAT_S32, 4, sample_move_d32u24_sS, sample_move_dS_s32u24, "32bit" },
{ SND_PCM_FORMAT_S24, 4, sample_move_d24_sS, sample_move_dS_s24, "24bit" },
{ SND_PCM_FORMAT_S16, 2, sample_move_d16_sS, sample_move_dS_s16, "16bit" }
};
#define NUMFORMATS (sizeof(formats)/sizeof(formats[0]))
int format=0;

// Alsa stuff... i dont want to touch this bullshit in the next years.... please...

static int xrun_recovery(snd_pcm_t *handle, int err) {
// printf( "xrun !!!.... %d\n", err );
if (err == -EPIPE) { /* under-run */
err = snd_pcm_prepare(handle);
if (err < 0)
printf("Can't recovery from underrun, prepare failed: %s\n", snd_strerror(err));
return 0;
} else if (err == -EAGAIN) {
while ((err = snd_pcm_resume(handle)) == -EAGAIN)
usleep(100); /* wait until the suspend flag is released */
if (err < 0) {
err = snd_pcm_prepare(handle);
if (err < 0)
printf("Can't recovery from suspend, prepare failed: %s\n", snd_strerror(err));
}
return 0;
}
return err;
}

static int set_hwformat( snd_pcm_t *handle, snd_pcm_hw_params_t *params )
{
int i;
int err;

for( i=0; i<NUMFORMATS; i++ ) {
/* set the sample format */
err = snd_pcm_hw_params_set_format(handle, params, formats[i].format_id);
if (err == 0) {
format = i;
return 0;
}
}

return err;
}

static int set_hwparams(snd_pcm_t *handle, snd_pcm_hw_params_t *params, snd_pcm_access_t access, int rate, int channels, int period, int nperiods ) {
int err, dir=0;
unsigned int buffer_time;
unsigned int period_time;
unsigned int rrate;
unsigned int rchannels;

/* choose all parameters */
err = snd_pcm_hw_params_any(handle, params);
if (err < 0) {
printf("Broken configuration for playback: no configurations available: %s\n", snd_strerror(err));
return err;
}
/* set the interleaved read/write format */
err = snd_pcm_hw_params_set_access(handle, params, access);
if (err < 0) {
printf("Access type not available for playback: %s\n", snd_strerror(err));
return err;
}

/* set the sample format */
err = set_hwformat(handle, params);
if (err < 0) {
printf("Sample format not available for playback: %s\n", snd_strerror(err));
return err;
}
/* set the count of channels */
rchannels = channels;
err = snd_pcm_hw_params_set_channels_near(handle, params, &rchannels);
if (err < 0) {
printf("Channels count (%i) not available for record: %s\n", channels, snd_strerror(err));
return err;
}
if (rchannels != channels) {
printf("WARNING: chennel count does not match (requested %d got %d)\n", channels, rchannels);
num_channels = rchannels;
}
/* set the stream rate */
rrate = rate;
err = snd_pcm_hw_params_set_rate_near(handle, params, &rrate, 0);
if (err < 0) {
printf("Rate %iHz not available for playback: %s\n", rate, snd_strerror(err));
return err;
}
if (rrate != rate) {
printf("Rate doesn't match (requested %iHz, get %iHz)\n", rate, rrate);
return -EINVAL;
}
/* set the buffer time */

buffer_time = 1000000*period*nperiods/rate;
err = snd_pcm_hw_params_set_buffer_time_near(handle, params, &buffer_time, &dir);
if (err < 0) {
printf("Unable to set buffer time %i for playback: %s\n", 1000000*period*nperiods/rate, snd_strerror(err));
return err;
}
err = snd_pcm_hw_params_get_buffer_size( params, &real_buffer_size );
if (err < 0) {
printf("Unable to get buffer size back: %s\n", snd_strerror(err));
return err;
}
if( real_buffer_size != nperiods * period ) {
printf( "WARNING: buffer size does not match: (requested %d, got %d)\n", nperiods * period, (int) real_buffer_size );
}
/* set the period time */
period_time = 1000000*period/rate;
err = snd_pcm_hw_params_set_period_time_near(handle, params, &period_time, &dir);
if (err < 0) {
printf("Unable to set period time %i for playback: %s\n", 1000000*period/rate, snd_strerror(err));
return err;
}
err = snd_pcm_hw_params_get_period_size(params, &real_period_size, NULL );
if (err < 0) {
printf("Unable to get period size back: %s\n", snd_strerror(err));
return err;
}
if( real_period_size != period ) {
printf( "WARNING: period size does not match: (requested %i, got %i)\n", period, (int)real_period_size );
}
/* write the parameters to device */
err = snd_pcm_hw_params(handle, params);
if (err < 0) {
printf("Unable to set hw params for playback: %s\n", snd_strerror(err));
return err;
}
return 0;
}

static int set_swparams(snd_pcm_t *handle, snd_pcm_sw_params_t *swparams, int period, int nperiods) {
int err;

/* get the current swparams */
err = snd_pcm_sw_params_current(handle, swparams);
if (err < 0) {
printf("Unable to determine current swparams for capture: %s\n", snd_strerror(err));
return err;
}
/* start the transfer when the buffer is full */
err = snd_pcm_sw_params_set_start_threshold(handle, swparams, period );
if (err < 0) {
printf("Unable to set start threshold mode for capture: %s\n", snd_strerror(err));
return err;
}
err = snd_pcm_sw_params_set_stop_threshold(handle, swparams, -1 );
if (err < 0) {
printf("Unable to set start threshold mode for capture: %s\n", snd_strerror(err));
return err;
}
/* allow the transfer when at least period_size samples can be processed */
err = snd_pcm_sw_params_set_avail_min(handle, swparams, 1 );
if (err < 0) {
printf("Unable to set avail min for capture: %s\n", snd_strerror(err));
return err;
}
/* align all transfers to 1 sample */
err = snd_pcm_sw_params_set_xfer_align(handle, swparams, 1);
if (err < 0) {
printf("Unable to set transfer align for capture: %s\n", snd_strerror(err));
return err;
}
/* write the parameters to the playback device */
err = snd_pcm_sw_params(handle, swparams);
if (err < 0) {
printf("Unable to set sw params for capture: %s\n", snd_strerror(err));
return err;
}
return 0;
}

// ok... i only need this function to communicate with the alsa bloat api...

static snd_pcm_t *open_audiofd( char *device_name, int capture, int rate, int channels, int period, int nperiods ) {
int err;
snd_pcm_t *handle;
snd_pcm_hw_params_t *hwparams;
snd_pcm_sw_params_t *swparams;

snd_pcm_hw_params_alloca(&hwparams);
snd_pcm_sw_params_alloca(&swparams);

if ((err = snd_pcm_open(&(handle), device_name, capture ? SND_PCM_STREAM_CAPTURE : SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK )) < 0) {
printf("Capture open error: %s\n", snd_strerror(err));
return NULL;
}

if ((err = set_hwparams(handle, hwparams,SND_PCM_ACCESS_RW_INTERLEAVED, rate, channels, period, nperiods )) < 0) {
printf("Setting of hwparams failed: %s\n", snd_strerror(err));
return NULL;
}
if ((err = set_swparams(handle, swparams, period, nperiods)) < 0) {
printf("Setting of swparams failed: %s\n", snd_strerror(err));
return NULL;
}

//snd_pcm_start( handle );
//snd_pcm_wait( handle, 200 );
int num_null_samples = nperiods * period * channels;
char *tmp = alloca( num_null_samples * formats[format].sample_size );
memset( tmp, 0, num_null_samples * formats[format].sample_size );
snd_pcm_writei( handle, tmp, num_null_samples );

return handle;
}

double hann( double x )
{
return 0.5 * (1.0 - cos( 2*M_PI * x ) );
}

/**
* The process callback for this JACK application.
* It is called by JACK at the appropriate times.
*/
int process (jack_nframes_t nframes, void *arg) {

char *outbuf;
float *resampbuf;
int rlen;
int err;
snd_pcm_sframes_t delay = target_delay;
int i;

snd_pcm_delay( alsa_handle, &delay );

delay -= jack_frames_since_cycle_start( client );
delay += jack_get_buffer_size( client ) / 2;
// Do it the hard way.
// this is for compensating xruns etc...

if( delay > (target_delay+max_diff) ) {
snd_pcm_rewind( alsa_handle, delay - target_delay );
output_new_delay = (int) delay;

delay = target_delay;

// Set the resample_rate... we need to adjust the offset integral, to do this.
// first look at the PI controller, this code is just a special case, which should never execute once
// everything is swung in.
offset_integral = - (resample_mean - static_resample_factor) * catch_factor * catch_factor2;
// Also clear the array. we are beginning a new control cycle.
for( i=0; i<smooth_size; i++ )
offset_array[i] = 0.0;
}
if( delay < (target_delay-max_diff) ) {
char *tmp = alloca( (target_delay-delay) * formats[format].sample_size * num_channels );
memset( tmp, 0, formats[format].sample_size * num_channels * (target_delay-delay) );
snd_pcm_writei( alsa_handle, tmp, target_delay-delay );

output_new_delay = (int) delay;

delay = target_delay;

// Set the resample_rate... we need to adjust the offset integral, to do this.
offset_integral = - (resample_mean - static_resample_factor) * catch_factor * catch_factor2;
// Also clear the array. we are beginning a new control cycle.
for( i=0; i<smooth_size; i++ )
offset_array[i] = 0.0;
}
/* ok... now we should have target_delay +- max_diff on the alsa side.
*
* calculate the number of frames, we want to get.
*/

double offset = delay - target_delay;

// Save offset.
offset_array[(offset_differential_index++)% smooth_size ] = offset;

// Build the mean of the windowed offset array
// basically fir lowpassing.
double smooth_offset = 0.0;
for( i=0; i<smooth_size; i++ )
smooth_offset +=
offset_array[ (i + offset_differential_index-1) % smooth_size] * window_array[i];
smooth_offset /= (double) smooth_size;

// this is the integral of the smoothed_offset
offset_integral += smooth_offset;

// Clamp offset.
// the smooth offset still contains unwanted noise
// which would go straigth onto the resample coeff.
// it only used in the P component and the I component is used for the fine tuning anyways.
if( fabs( smooth_offset ) < pclamp )
smooth_offset = 0.0;

// ok. now this is the PI controller.
// u(t) = K * ( e(t) + 1/T \int e(t') dt' )
// K = 1/catch_factor and T = catch_factor2
double current_resample_factor = static_resample_factor - smooth_offset / (double) catch_factor - offset_integral / (double) catch_factor / (double)catch_factor2;

// now quantize this value around resample_mean, so that the noise which is in the integral component doesnt hurt.
current_resample_factor = floor( (current_resample_factor - resample_mean) * controlquant + 0.5 ) / controlquant + resample_mean;

// Output "instrumentatio" gonna change that to real instrumentation in a few.
output_resampling_factor = (float) current_resample_factor;
output_diff = (float) smooth_offset;
output_integral = (float) offset_integral;
output_offset = (float) offset;

// Clamp a bit.
if( current_resample_factor < 0.25 ) current_resample_factor = 0.25;
if( current_resample_factor > 4 ) current_resample_factor = 4;

// Now Calculate how many samples we need.
rlen = ceil( ((double)nframes) * current_resample_factor )+2;
assert( rlen > 2 );

// Calculate resample_mean so we can init ourselves to saner values.
resample_mean = 0.9999 * resample_mean + 0.0001 * current_resample_factor;
/*
* now this should do it...
*/

outbuf = alloca( rlen * formats[format].sample_size * num_channels );

resampbuf = alloca( rlen * sizeof( float ) );
/*
* render jack ports to the outbuf...
*/

int chn = 0;
JSList *node = playback_ports;
JSList *src_node = playback_srcs;
SRC_DATA src;

while ( node != NULL)
{
jack_port_t *port = (jack_port_t *) node->data;
float *buf = jack_port_get_buffer (port, nframes);

SRC_STATE *src_state = src_node->data;

src.data_in = buf;
src.input_frames = nframes;

src.data_out = resampbuf;
src.output_frames = rlen;
src.end_of_input = 0;

src.src_ratio = current_resample_factor;

src_process( src_state, &src );

formats[format].jack_to_soundcard( outbuf + format[formats].sample_size * chn, resampbuf, src.output_frames_gen, num_channels*format[formats].sample_size, NULL);

src_node = jack_slist_next (src_node);
node = jack_slist_next (node);
chn++;
}

// now write the output...
again:
err = snd_pcm_writei(alsa_handle, outbuf, src.output_frames_gen);
//err = snd_pcm_writei(alsa_handle, outbuf, src.output_frames_gen);
if( err < 0 ) {
printf( "err = %d\n", err );
if (xrun_recovery(alsa_handle, err) < 0) {
printf("Write error: %s\n", snd_strerror(err));
exit(EXIT_FAILURE);
}
goto again;
}

return 0;
}


/**
* Allocate the necessary jack ports...
*/

void alloc_ports( int n_capture, int n_playback ) {

int port_flags = JackPortIsOutput | JackPortIsPhysical | JackPortIsTerminal;
int chn;
jack_port_t *port;
char buf[32];

capture_ports = NULL;
for (chn = 0; chn < n_capture; chn++)
{
snprintf (buf, sizeof(buf) - 1, "capture_%u", chn+1);

port = jack_port_register (client, buf,
JACK_DEFAULT_AUDIO_TYPE,
port_flags, 0);

if (!port)
{
printf( "jacknet_client: cannot register port for %s", buf);
break;
}

capture_srcs = jack_slist_append( capture_srcs, src_new( 4-samplerate_quality, 1, NULL ) );
capture_ports = jack_slist_append (capture_ports, port);
}

port_flags = JackPortIsInput;

playback_ports = NULL;
for (chn = 0; chn < n_playback; chn++)
{
snprintf (buf, sizeof(buf) - 1, "playback_%u", chn+1);

port = jack_port_register (client, buf,
JACK_DEFAULT_AUDIO_TYPE,
port_flags, 0);

if (!port)
{
printf( "jacknet_client: cannot register port for %s", buf);
break;
}

playback_srcs = jack_slist_append( playback_srcs, src_new( 4-samplerate_quality, 1, NULL ) );
playback_ports = jack_slist_append (playback_ports, port);
}
}

/**
* This is the shutdown callback for this JACK application.
* It is called by JACK if the server ever shuts down or
* decides to disconnect the client.
*/

void jack_shutdown (void *arg) {

exit (1);
}

/**
* be user friendly.
* be user friendly.
* be user friendly.
*/

void printUsage() {
fprintf(stderr, "usage: alsa_out [options]\n"
"\n"
" -j <jack name> - client name\n"
" -d <alsa_device> \n"
" -c <channels> \n"
" -p <period_size> \n"
" -n <num_period> \n"
" -r <sample_rate> \n"
" -q <sample_rate quality [0..4]\n"
" -m <max_diff> \n"
" -t <target_delay> \n"
" -i turns on instrumentation\n"
" -v turns on printouts\n"
"\n");
}


/**
* the main function....
*/

void
sigterm_handler( int signal )
{
quit = 1;
}


int main (int argc, char *argv[]) {
char jack_name[30] = "alsa_out";
char alsa_device[30] = "hw:0";

extern char *optarg;
extern int optind, optopt;
int errflg=0;
int c;

while ((c = getopt(argc, argv, "ivj:r:c:p:n:d:q:m:t:f:F:C:Q:s:")) != -1) {
switch(c) {
case 'j':
strcpy(jack_name,optarg);
break;
case 'r':
sample_rate = atoi(optarg);
break;
case 'c':
num_channels = atoi(optarg);
break;
case 'p':
period_size = atoi(optarg);
break;
case 'n':
num_periods = atoi(optarg);
break;
case 'd':
strcpy(alsa_device,optarg);
break;
case 't':
target_delay = atoi(optarg);
break;
case 'q':
samplerate_quality = atoi(optarg);
break;
case 'm':
max_diff = atoi(optarg);
break;
case 'f':
catch_factor = atoi(optarg);
break;
case 'F':
catch_factor2 = atoi(optarg);
break;
case 'C':
pclamp = (double) atoi(optarg);
break;
case 'Q':
controlquant = (double) atoi(optarg);
break;
case 'v':
verbose = 1;
break;
case 'i':
instrument = 1;
break;
case 's':
smooth_size = atoi(optarg);
break;
case ':':
fprintf(stderr,
"Option -%c requires an operand\n", optopt);
errflg++;
break;
case '?':
fprintf(stderr,
"Unrecognized option: -%c\n", optopt);
errflg++;
}
}
if (errflg) {
printUsage();
exit(2);
}

if( (samplerate_quality < 0) || (samplerate_quality > 4) ) {
fprintf (stderr, "invalid samplerate quality\n");
return 1;
}
if ((client = jack_client_open (jack_name, 0, NULL)) == 0) {
fprintf (stderr, "jack server not running?\n");
return 1;
}

/* tell the JACK server to call `process()' whenever
there is work to be done.
*/

jack_set_process_callback (client, process, 0);

/* tell the JACK server to call `jack_shutdown()' if
it ever shuts down, either entirely, or if it
just decides to stop calling us.
*/

jack_on_shutdown (client, jack_shutdown, 0);


// get jack sample_rate
jack_sample_rate = jack_get_sample_rate( client );

if( !sample_rate )
sample_rate = jack_sample_rate;

static_resample_factor = (double) sample_rate / (double) jack_sample_rate;
resample_mean = static_resample_factor;

offset_array = malloc( sizeof(double) * smooth_size );
if( offset_array == NULL ) {
fprintf( stderr, "no memory for offset_array !!!\n" );
exit(20);
}
window_array = malloc( sizeof(double) * smooth_size );
if( window_array == NULL ) {
fprintf( stderr, "no memory for window_array !!!\n" );
exit(20);
}
int i;
for( i=0; i<smooth_size; i++ ) {
offset_array[i] = 0.0;
window_array[i] = hann( (double) i / ((double) smooth_size - 1.0) );
}

jack_buffer_size = jack_get_buffer_size( client );
// Setup target delay and max_diff for the normal user, who does not play with them...
if( !target_delay )
target_delay = (num_periods*period_size / 2) - jack_buffer_size/2;

if( !max_diff )
max_diff = target_delay;

if( max_diff > target_delay ) {
fprintf( stderr, "target_delay (%d) cant be smaller than max_diff(%d)\n", target_delay, max_diff );
exit(20);
}
if( (target_delay+max_diff) > (num_periods*period_size) ) {
fprintf( stderr, "target_delay+max_diff (%d) cant be bigger than buffersize(%d)\n", target_delay+max_diff, num_periods*period_size );
exit(20);
}
// now open the alsa fd...
alsa_handle = open_audiofd( alsa_device, 0, sample_rate, num_channels, period_size, num_periods);
if( alsa_handle == 0 )
exit(20);

printf( "selected sample format: %s\n", formats[format].name );

// alloc input ports, which are blasted out to alsa...
alloc_ports( 0, num_channels );


/* tell the JACK server that we are ready to roll */

if (jack_activate (client)) {
fprintf (stderr, "cannot activate client");
return 1;
}

signal( SIGTERM, sigterm_handler );
signal( SIGINT, sigterm_handler );

if( verbose ) {
while(!quit) {
usleep(500000);
if( output_new_delay ) {
printf( "delay = %d\n", output_new_delay );
output_new_delay = 0;
}
printf( "res: %f, \tdiff = %f, \toffset = %f \n", output_resampling_factor, output_diff, output_offset );
}
} else if( instrument ) {
printf( "# n\tresamp\tdiff\toffseti\tintegral\n");
int n=0;
while(!quit) {
usleep(1000);
printf( "%d\t%f\t%f\t%f\t%f\n", n++, output_resampling_factor, output_diff, output_offset, output_integral );
}
} else {
while(!quit)
{
usleep(500000);
if( output_new_delay ) {
printf( "delay = %d\n", output_new_delay );
output_new_delay = 0;
}
}
}

jack_deactivate( client );
jack_client_close (client);
exit (0);
}


+ 758
- 0
example-clients/netsource.c View File

@@ -0,0 +1,758 @@
/*
NetJack Client

Copyright (C) 2008 Marc-Olivier Barre <marco@marcochapeau.org>
Copyright (C) 2008 Pieter Palmers <pieterpalmers@users.sourceforge.net>
Copyright (C) 2006 Torben Hohn <torbenh@gmx.de>

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program 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 General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/

/** @file netsource.c
*
* @brief This client connects a remote slave JACK to a local JACK server assumed to be the master
*/


#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>

#ifdef WIN32
#include <winsock2.h>
#include <malloc.h>
#else
#include <netinet/in.h>
#include <netdb.h>
#include <sys/socket.h>
#endif

/* These two required by FreeBSD. */
#include <sys/types.h>

#if HAVE_ALLOCA_H
#include <alloca.h>
#endif

#include <jack/jack.h>

//#include <net_driver.h>
#include <netjack_packet.h>
#if HAVE_SAMPLERATE
#include <samplerate.h>
#endif

#if HAVE_CELT
#include <celt/celt.h>
#endif

#include <math.h>

JSList *capture_ports = NULL;
JSList *capture_srcs = NULL;
int capture_channels = 0;
int capture_channels_audio = 2;
int capture_channels_midi = 1;
JSList *playback_ports = NULL;
JSList *playback_srcs = NULL;
int playback_channels = 0;
int playback_channels_audio = 2;
int playback_channels_midi = 1;
int dont_htonl_floats = 0;

int latency = 5;
jack_nframes_t factor = 1;
int bitdepth = 0;
int mtu = 1400;
int reply_port = 0;
int redundancy = 1;
jack_client_t *client;

int state_connected = 0;
int state_latency = 0;
int state_netxruns = 0;
int state_currentframe = 0;
int state_recv_packet_queue_time = 0;


int outsockfd;
int insockfd;
#ifdef WIN32
struct sockaddr_in destaddr;
struct sockaddr_in bindaddr;
#else
struct sockaddr destaddr;
struct sockaddr bindaddr;
#endif

int sync_state;
jack_transport_state_t last_transport_state;

int framecnt = 0;

int cont_miss = 0;

int freewheeling = 0;

/**
* This Function allocates all the I/O Ports which are added the lists.
*/
void
alloc_ports (int n_capture_audio, int n_playback_audio, int n_capture_midi, int n_playback_midi)
{

int port_flags = JackPortIsOutput;
int chn;
jack_port_t *port;
char buf[32];

capture_ports = NULL;
/* Allocate audio capture channels */
for (chn = 0; chn < n_capture_audio; chn++)
{
snprintf (buf, sizeof (buf) - 1, "capture_%u", chn + 1);
port = jack_port_register (client, buf, JACK_DEFAULT_AUDIO_TYPE, port_flags, 0);
if (!port)
{
printf( "jack_netsource: cannot register %s port\n", buf);
break;
}
if( bitdepth == 1000 ) {
#if HAVE_CELT
#if HAVE_CELT_API_0_7
CELTMode *celt_mode = celt_mode_create( jack_get_sample_rate( client ), jack_get_buffer_size(client), NULL );
capture_srcs = jack_slist_append(capture_srcs, celt_decoder_create( celt_mode, 1, NULL ) );
#else
CELTMode *celt_mode = celt_mode_create( jack_get_sample_rate( client ), 1, jack_get_buffer_size(client), NULL );
capture_srcs = jack_slist_append(capture_srcs, celt_decoder_create( celt_mode ) );
#endif
#endif
} else {
#if HAVE_SAMPLERATE
capture_srcs = jack_slist_append (capture_srcs, src_new (SRC_LINEAR, 1, NULL));
#endif
}
capture_ports = jack_slist_append (capture_ports, port);
}

/* Allocate midi capture channels */
for (chn = n_capture_audio; chn < n_capture_midi + n_capture_audio; chn++)
{
snprintf (buf, sizeof (buf) - 1, "capture_%u", chn + 1);
port = jack_port_register (client, buf, JACK_DEFAULT_MIDI_TYPE, port_flags, 0);
if (!port)
{
printf ("jack_netsource: cannot register %s port\n", buf);
break;
}
capture_ports = jack_slist_append(capture_ports, port);
}

/* Allocate audio playback channels */
port_flags = JackPortIsInput;
playback_ports = NULL;
for (chn = 0; chn < n_playback_audio; chn++)
{
snprintf (buf, sizeof (buf) - 1, "playback_%u", chn + 1);
port = jack_port_register (client, buf, JACK_DEFAULT_AUDIO_TYPE, port_flags, 0);
if (!port)
{
printf ("jack_netsource: cannot register %s port\n", buf);
break;
}
if( bitdepth == 1000 ) {
#if HAVE_CELT
#if HAVE_CELT_API_0_7
CELTMode *celt_mode = celt_mode_create( jack_get_sample_rate (client), jack_get_buffer_size(client), NULL );
playback_srcs = jack_slist_append(playback_srcs, celt_encoder_create( celt_mode, 1, NULL ) );
#else
CELTMode *celt_mode = celt_mode_create( jack_get_sample_rate (client), 1, jack_get_buffer_size(client), NULL );
playback_srcs = jack_slist_append(playback_srcs, celt_encoder_create( celt_mode ) );
#endif
#endif
} else {
#if HAVE_SAMPLERATE
playback_srcs = jack_slist_append (playback_srcs, src_new (SRC_LINEAR, 1, NULL));
#endif
}
playback_ports = jack_slist_append (playback_ports, port);
}

/* Allocate midi playback channels */
for (chn = n_playback_audio; chn < n_playback_midi + n_playback_audio; chn++)
{
snprintf (buf, sizeof (buf) - 1, "playback_%u", chn + 1);
port = jack_port_register (client, buf, JACK_DEFAULT_MIDI_TYPE, port_flags, 0);
if (!port)
{
printf ("jack_netsource: cannot register %s port\n", buf);
break;
}
playback_ports = jack_slist_append (playback_ports, port);
}
}

/**
* The Sync callback... sync state is set elsewhere...
* we will see if this is working correctly.
* i dont really believe in it yet.
*/
int
sync_cb (jack_transport_state_t state, jack_position_t *pos, void *arg)
{
static int latency_count = 0;
int retval = sync_state;

if (latency_count) {
latency_count--;
retval = 0;
}

else if (state == JackTransportStarting && last_transport_state != JackTransportStarting)
{
retval = 0;
latency_count = latency - 1;
}

last_transport_state = state;
return retval;
}

void
freewheel_cb (int starting, void *arg)
{
freewheeling = starting;
}

int deadline_goodness=0;
/**
* The process callback for this JACK application.
* It is called by JACK at the appropriate times.
*/
int
process (jack_nframes_t nframes, void *arg)
{
jack_nframes_t net_period;
int rx_bufsize, tx_bufsize;

jack_default_audio_sample_t *buf;
jack_port_t *port;
JSList *node;
int chn;
int size, i;
const char *porttype;
int input_fd;

jack_position_t local_trans_pos;

uint32_t *packet_buf, *packet_bufX;
uint32_t *rx_packet_ptr;
jack_time_t packet_recv_timestamp;

if( bitdepth == 1000 )
net_period = factor;
else
net_period = (float) nframes / (float) factor;

rx_bufsize = get_sample_size (bitdepth) * capture_channels * net_period + sizeof (jacknet_packet_header);
tx_bufsize = get_sample_size (bitdepth) * playback_channels * net_period + sizeof (jacknet_packet_header);


/* Allocate a buffer where both In and Out Buffer will fit */
packet_buf = alloca ((rx_bufsize > tx_bufsize) ? rx_bufsize : tx_bufsize);

jacknet_packet_header *pkthdr = (jacknet_packet_header *) packet_buf;

/*
* for latency==0 we need to send out the packet before we wait on the reply.
* but this introduces a cycle of latency, when netsource is connected to itself.
* so we send out before read only in zero latency mode.
*
*/

if( latency == 0 ) {
/* reset packet_bufX... */
packet_bufX = packet_buf + sizeof (jacknet_packet_header) / sizeof (jack_default_audio_sample_t);

/* ---------- Send ---------- */
render_jack_ports_to_payload (bitdepth, playback_ports, playback_srcs, nframes,
packet_bufX, net_period, dont_htonl_floats);

/* fill in packet hdr */
pkthdr->transport_state = jack_transport_query (client, &local_trans_pos);
pkthdr->transport_frame = local_trans_pos.frame;
pkthdr->framecnt = framecnt;
pkthdr->latency = latency;
pkthdr->reply_port = reply_port;
pkthdr->sample_rate = jack_get_sample_rate (client);
pkthdr->period_size = nframes;

/* playback for us is capture on the other side */
pkthdr->capture_channels_audio = playback_channels_audio;
pkthdr->playback_channels_audio = capture_channels_audio;
pkthdr->capture_channels_midi = playback_channels_midi;
pkthdr->playback_channels_midi = capture_channels_midi;
pkthdr->mtu = mtu;
if( freewheeling!= 0 )
pkthdr->sync_state = (jack_nframes_t)MASTER_FREEWHEELS;
else
pkthdr->sync_state = (jack_nframes_t)deadline_goodness;
//printf("goodness=%d\n", deadline_goodness );

packet_header_hton (pkthdr);
if (cont_miss < 3*latency+5) {
int r;
for( r=0; r<redundancy; r++ )
netjack_sendto (outsockfd, (char *) packet_buf, tx_bufsize, 0, &destaddr, sizeof (destaddr), mtu);
}
else if (cont_miss > 50+5*latency)
{
state_connected = 0;
packet_cache_reset_master_address( global_packcache );
//printf ("Frame %d \tRealy too many packets missed (%d). Let's reset the counter\n", framecnt, cont_miss);
cont_miss = 0;
}
}

/*
* ok... now the RECEIVE code.
*
*/

/* reset packet_bufX... */
packet_bufX = packet_buf + sizeof (jacknet_packet_header) / sizeof (jack_default_audio_sample_t);

if( reply_port )
input_fd = insockfd;
else
input_fd = outsockfd;

// for latency == 0 we can poll.
if( (latency == 0) || (freewheeling!=0) ) {
jack_time_t deadline = jack_get_time() + 1000000 * jack_get_buffer_size(client)/jack_get_sample_rate(client);
// Now loop until we get the right packet.
while(1) {
jack_nframes_t got_frame;
if ( ! netjack_poll_deadline( input_fd, deadline ) )
break;

packet_cache_drain_socket(global_packcache, input_fd);

if (packet_cache_get_next_available_framecnt( global_packcache, framecnt - latency, &got_frame ))
if( got_frame == (framecnt - latency) )
break;
}
} else {
// normally:
// only drain socket.
packet_cache_drain_socket(global_packcache, input_fd);
}

size = packet_cache_retreive_packet_pointer( global_packcache, framecnt - latency, (char**)&rx_packet_ptr, rx_bufsize, &packet_recv_timestamp );
/* First alternative : we received what we expected. Render the data
* to the JACK ports so it can be played. */
if (size == rx_bufsize)
{
packet_buf = rx_packet_ptr;
pkthdr = (jacknet_packet_header *) packet_buf;
packet_bufX = packet_buf + sizeof (jacknet_packet_header) / sizeof (jack_default_audio_sample_t);
// calculate how much time there would have been, if this packet was sent at the deadline.

int recv_time_offset = (int) (jack_get_time() - packet_recv_timestamp);
packet_header_ntoh (pkthdr);
deadline_goodness = recv_time_offset - (int)pkthdr->latency;
//printf( "deadline goodness = %d ---> off: %d\n", deadline_goodness, recv_time_offset );

if (cont_miss)
{
//printf("Frame %d \tRecovered from dropouts\n", framecnt);
cont_miss = 0;
}
render_payload_to_jack_ports (bitdepth, packet_bufX, net_period,
capture_ports, capture_srcs, nframes, dont_htonl_floats);

state_currentframe = framecnt;
state_recv_packet_queue_time = recv_time_offset;
state_connected = 1;
sync_state = pkthdr->sync_state;
packet_cache_release_packet( global_packcache, framecnt - latency );
}
/* Second alternative : we've received something that's not
* as big as expected or we missed a packet. We render silence
* to the ouput ports */
else
{
jack_nframes_t latency_estimate;
if( packet_cache_find_latency( global_packcache, framecnt, &latency_estimate ) )
//if( (state_latency == 0) || (latency_estimate < state_latency) )
state_latency = latency_estimate;

// Set the counters up.
state_currentframe = framecnt;
//state_latency = framecnt - pkthdr->framecnt;
state_netxruns += 1;

//printf ("Frame %d \tPacket missed or incomplete (expected: %d bytes, got: %d bytes)\n", framecnt, rx_bufsize, size);
//printf ("Frame %d \tPacket missed or incomplete\n", framecnt);
cont_miss += 1;
chn = 0;
node = capture_ports;
while (node != NULL)
{
port = (jack_port_t *) node->data;
buf = jack_port_get_buffer (port, nframes);
porttype = jack_port_type (port);
if (strncmp (porttype, JACK_DEFAULT_AUDIO_TYPE, jack_port_type_size ()) == 0)
for (i = 0; i < nframes; i++)
buf[i] = 0.0;
else if (strncmp (porttype, JACK_DEFAULT_MIDI_TYPE, jack_port_type_size ()) == 0)
jack_midi_clear_buffer (buf);
node = jack_slist_next (node);
chn++;
}
}
if( latency != 0 ) {
/* reset packet_bufX... */
packet_bufX = packet_buf + sizeof (jacknet_packet_header) / sizeof (jack_default_audio_sample_t);

/* ---------- Send ---------- */
render_jack_ports_to_payload (bitdepth, playback_ports, playback_srcs, nframes,
packet_bufX, net_period, dont_htonl_floats);

/* fill in packet hdr */
pkthdr->transport_state = jack_transport_query (client, &local_trans_pos);
pkthdr->transport_frame = local_trans_pos.frame;
pkthdr->framecnt = framecnt;
pkthdr->latency = latency;
pkthdr->reply_port = reply_port;
pkthdr->sample_rate = jack_get_sample_rate (client);
pkthdr->period_size = nframes;

/* playback for us is capture on the other side */
pkthdr->capture_channels_audio = playback_channels_audio;
pkthdr->playback_channels_audio = capture_channels_audio;
pkthdr->capture_channels_midi = playback_channels_midi;
pkthdr->playback_channels_midi = capture_channels_midi;
pkthdr->mtu = mtu;
if( freewheeling!= 0 )
pkthdr->sync_state = (jack_nframes_t)MASTER_FREEWHEELS;
else
pkthdr->sync_state = (jack_nframes_t)deadline_goodness;
//printf("goodness=%d\n", deadline_goodness );

packet_header_hton (pkthdr);
if (cont_miss < 3*latency+5) {
int r;
for( r=0; r<redundancy; r++ )
netjack_sendto (outsockfd, (char *) packet_buf, tx_bufsize, 0, &destaddr, sizeof (destaddr), mtu);
}
else if (cont_miss > 50+5*latency)
{
state_connected = 0;
packet_cache_reset_master_address( global_packcache );
//printf ("Frame %d \tRealy too many packets missed (%d). Let's reset the counter\n", framecnt, cont_miss);
cont_miss = 0;
}
}

framecnt++;
return 0;
}

/**
* This is the shutdown callback for this JACK application.
* It is called by JACK if the server ever shuts down or
* decides to disconnect the client.
*/

void
jack_shutdown (void *arg)
{
exit (1);
}

void
init_sockaddr_in (struct sockaddr_in *name , const char *hostname , uint16_t port)
{
printf( "still here... \n" );
fflush( stdout );

name->sin_family = AF_INET ;
name->sin_port = htons (port);
if (hostname)
{
struct hostent *hostinfo = gethostbyname (hostname);
if (hostinfo == NULL) {
fprintf (stderr, "init_sockaddr_in: unknown host: %s.\n", hostname);
fflush( stderr );
}
#ifdef WIN32
name->sin_addr.s_addr = inet_addr( hostname );
#else
name->sin_addr = *(struct in_addr *) hostinfo->h_addr ;
#endif
}
else
name->sin_addr.s_addr = htonl (INADDR_ANY) ;

}

void
printUsage ()
{
fprintf (stderr, "usage: jack_netsource -h <host peer> [options]\n"
"\n"
" -n <jack name> - Reports a different name to jack\n"
" -s <server name> - The name of the local jack server\n"
" -h <host_peer> - Host name of the slave JACK\n"
" -p <port> - UDP port used by the slave JACK\n"
" -P <num channels> - Number of audio playback channels\n"
" -C <num channels> - Number of audio capture channels\n"
" -o <num channels> - Number of midi playback channels\n"
" -i <num channels> - Number of midi capture channels\n"
" -l <latency> - Network latency in number of NetJack frames\n"
" -r <reply port> - Local UDP port to use\n"
" -f <downsample ratio> - Downsample data in the wire by this factor\n"
" -b <bitdepth> - Set transport to use 16bit or 8bit\n"
" -m <mtu> - Assume this mtu for the link\n"
" -c <bytes> - Use Celt and encode <bytes> per channel and packet.\n"
" -R <N> - Send out packets N times.\n"
"\n");
}

int
main (int argc, char *argv[])
{
/* Some startup related basics */
char *client_name, *server_name = NULL, *peer_ip;
int peer_port = 3000;
jack_options_t options = JackNullOption;
jack_status_t status;
#ifdef WIN32
WSADATA wsa;
int rc = WSAStartup(MAKEWORD(2,0),&wsa);
#endif
/* Torben's famous state variables, aka "the reporting API" ! */
/* heh ? these are only the copies of them ;) */
int statecopy_connected, statecopy_latency, statecopy_netxruns;
jack_nframes_t net_period;
/* Argument parsing stuff */
extern char *optarg;
extern int optind, optopt;
int errflg=0, c;

if (argc < 3)
{
printUsage ();
return 1;
}

client_name = (char *) malloc (sizeof (char) * 10);
peer_ip = (char *) malloc (sizeof (char) * 10);
sprintf(client_name, "netsource");
sprintf(peer_ip, "localhost");

while ((c = getopt (argc, argv, ":H:R:n:s:h:p:C:P:i:o:l:r:f:b:m:c:")) != -1)
{
switch (c)
{
case 'n':
free(client_name);
client_name = (char *) malloc (sizeof (char) * strlen (optarg)+1);
strcpy (client_name, optarg);
break;
case 's':
server_name = (char *) malloc (sizeof (char) * strlen (optarg)+1);
strcpy (server_name, optarg);
options |= JackServerName;
break;
case 'h':
free(peer_ip);
peer_ip = (char *) malloc (sizeof (char) * strlen (optarg)+1);
strcpy (peer_ip, optarg);
break;
case 'p':
peer_port = atoi (optarg);
break;
case 'P':
playback_channels_audio = atoi (optarg);
break;
case 'C':
capture_channels_audio = atoi (optarg);
break;
case 'o':
playback_channels_midi = atoi (optarg);
break;
case 'i':
capture_channels_midi = atoi (optarg);
break;
case 'l':
latency = atoi (optarg);
break;
case 'r':
reply_port = atoi (optarg);
break;
case 'f':
factor = atoi (optarg);
break;
case 'b':
bitdepth = atoi (optarg);
break;
case 'c':
#if HAVE_CELT
bitdepth = 1000;
factor = atoi (optarg);
#else
printf( "not built with celt supprt\n" );
exit(10);
#endif
break;
case 'm':
mtu = atoi (optarg);
break;
case 'R':
redundancy = atoi (optarg);
break;
case 'H':
dont_htonl_floats = atoi (optarg);
break;
case ':':
fprintf (stderr, "Option -%c requires an operand\n", optopt);
errflg++;
break;
case '?':
fprintf (stderr, "Unrecognized option: -%c\n", optopt);
errflg++;
}
}
if (errflg)
{
printUsage ();
exit (2);
}

capture_channels = capture_channels_audio + capture_channels_midi;
playback_channels = playback_channels_audio + playback_channels_midi;

outsockfd = socket (AF_INET, SOCK_DGRAM, 0);
insockfd = socket (AF_INET, SOCK_DGRAM, 0);

if( (outsockfd == -1) || (insockfd == -1) ) {
fprintf (stderr, "cant open sockets\n" );
return 1;
}

init_sockaddr_in ((struct sockaddr_in *) &destaddr, peer_ip, peer_port);
if(reply_port)
{
init_sockaddr_in ((struct sockaddr_in *) &bindaddr, NULL, reply_port);
if( bind (insockfd, &bindaddr, sizeof (bindaddr)) ) {
fprintf (stderr, "bind failure\n" );
}
}

/* try to become a client of the JACK server */
client = jack_client_open (client_name, options, &status, server_name);
if (client == NULL)
{
fprintf (stderr, "jack_client_open() failed, status = 0x%2.0x\n"
"Is the JACK server running ?\n", status);
return 1;
}

/* Set up jack callbacks */
jack_set_process_callback (client, process, 0);
jack_set_sync_callback (client, sync_cb, 0);
jack_set_freewheel_callback (client, freewheel_cb, 0);
jack_on_shutdown (client, jack_shutdown, 0);

alloc_ports (capture_channels_audio, playback_channels_audio, capture_channels_midi, playback_channels_midi);

if( bitdepth == 1000 )
net_period = factor;
else
net_period = ceilf((float) jack_get_buffer_size (client) / (float) factor);

int rx_bufsize = get_sample_size (bitdepth) * capture_channels * net_period + sizeof (jacknet_packet_header);
global_packcache = packet_cache_new (latency + 50, rx_bufsize, mtu);

/* tell the JACK server that we are ready to roll */
if (jack_activate (client))
{
fprintf (stderr, "Cannot activate client");
return 1;
}

/* Now sleep forever... and evaluate the state_ vars */

statecopy_connected = 2; // make it report unconnected on start.
statecopy_latency = state_latency;
statecopy_netxruns = state_netxruns;

while (1)
{
#ifdef WIN32
Sleep (1000);
#else
sleep(1);
#endif
if (statecopy_connected != state_connected)
{
statecopy_connected = state_connected;
if (statecopy_connected)
{
state_netxruns = 1; // We want to reset the netxrun count on each new connection
printf ("Connected :-)\n");
}
else
printf ("Not Connected\n");

fflush(stdout);
}

if (statecopy_connected)
{
if (statecopy_netxruns != state_netxruns) {
statecopy_netxruns = state_netxruns;
printf ("%s: at frame %06d -> total netxruns %d (%d%%) queue time= %d\n",
client_name,
state_currentframe,
statecopy_netxruns,
100*statecopy_netxruns/state_currentframe,
state_recv_packet_queue_time);

fflush(stdout);
}
}
else
{
if (statecopy_latency != state_latency)
{
statecopy_latency = state_latency;
if (statecopy_latency > 1)
printf ("current latency %d\n", statecopy_latency);
fflush(stdout);
}
}
}

/* Never reached. Well we will be a GtkApp someday... */
packet_cache_free (global_packcache);
jack_client_close (client);
exit (0);
}

+ 38
- 0
example-clients/wscript View File

@@ -30,9 +30,16 @@ example_libs = {
def configure(conf): def configure(conf):
e = conf.check_cc(header_name='sndfile.h', define_name="HAVE_SNDFILE") e = conf.check_cc(header_name='sndfile.h', define_name="HAVE_SNDFILE")
conf.check_cc(header_name='samplerate.h', define_name="HAVE_SAMPLERATE")
if conf.is_defined('HAVE_SAMPLERATE'):
conf.env['LIB_SAMPLERATE'] = ['samplerate']

if conf.is_defined('HAVE_SNDFILE'): if conf.is_defined('HAVE_SNDFILE'):
conf.env['LIB_SNDFILE'] = ['sndfile'] conf.env['LIB_SNDFILE'] = ['sndfile']


conf.check_cfg(package='celt', atleast_version='0.5.0', args='--cflags --libs')

e = conf.check_cc(header_name='ncurses.h', define_name="HAVE_NCURSES") e = conf.check_cc(header_name='ncurses.h', define_name="HAVE_NCURSES")


if conf.is_defined('HAVE_NCURSES'): if conf.is_defined('HAVE_NCURSES'):
@@ -105,6 +112,37 @@ def build(bld):
prog.uselib_local = 'clientlib' prog.uselib_local = 'clientlib'
prog.target = 'jack_rec' prog.target = 'jack_rec'


prog = bld.new_task_gen('cc', 'program')
prog.includes = os_incdir + ['../common/jack', '../common']
prog.source = ['netsource.c', '../common/netjack_packet.c']
prog.env.append_value("CCFLAGS", "-DNO_JACK_ERROR")
if bld.env['HAVE_CELT']:
if bld.env['HAVE_CELT_API_0_5']:
prog.defines = ['HAVE_CELT', 'HAVE_CELT_API_0_5']
elif bld.env['HAVE_CELT_API_0_7']:
prog.defines = ['HAVE_CELT', 'HAVE_CELT_API_0_7']
prog.uselib = 'CELT SAMPLERATE'

prog.uselib_local = 'clientlib'
prog.target = 'jack_netsource'

if bld.env['IS_LINUX']:
prog = bld.new_task_gen('cc', 'program')
prog.includes = os_incdir + ['../common/jack', '../common']
prog.source = ['alsa_in.c', '../common/memops.c']
prog.env.append_value("CCFLAGS", "-DNO_JACK_ERROR")
prog.uselib = 'ALSA SAMPLERATE'
prog.uselib_local = 'clientlib'
prog.target = 'alsa_in'

prog = bld.new_task_gen('cc', 'program')
prog.includes = os_incdir + ['../common/jack', '../common']
prog.source = ['alsa_out.c', '../common/memops.c']
prog.env.append_value("CCFLAGS", "-DNO_JACK_ERROR")
prog.uselib = 'ALSA SAMPLERATE'
prog.uselib_local = 'clientlib'
prog.target = 'alsa_out'

for example_lib, example_lib_source in example_libs.items(): for example_lib, example_lib_source in example_libs.items():
lib = bld.new_task_gen('cc', 'shlib') lib = bld.new_task_gen('cc', 'shlib')
lib.env['shlib_PATTERN'] = '%s.so' lib.env['shlib_PATTERN'] = '%s.so'


+ 11
- 1
linux/wscript View File

@@ -15,7 +15,12 @@ def create_jack_driver_obj(bld, target, sources, uselib = None):
driver = bld.new_task_gen('cxx', 'shlib') driver = bld.new_task_gen('cxx', 'shlib')
driver.features.append('cc') driver.features.append('cc')
driver.env['shlib_PATTERN'] = 'jack_%s.so' driver.env['shlib_PATTERN'] = 'jack_%s.so'
driver.defines = ['HAVE_CONFIG_H','SERVER_SIDE']
driver.defines = ['HAVE_CONFIG_H','SERVER_SIDE', 'HAVE_PPOLL']
if bld.env['HAVE_CELT']:
if bld.env['HAVE_CELT_API_0_5']:
driver.defines += ['HAVE_CELT', 'HAVE_CELT_API_0_5']
elif bld.env['HAVE_CELT_API_0_7']:
driver.defines += ['HAVE_CELT', 'HAVE_CELT_API_0_7']
driver.includes = ['.', '../linux', '../posix', '../common', '../common/jack', '../dbus'] driver.includes = ['.', '../linux', '../posix', '../common', '../common/jack', '../dbus']
driver.target = target driver.target = target
driver.source = sources driver.source = sources
@@ -64,3 +69,8 @@ def build(bld):
create_jack_driver_obj(bld, 'net', '../common/JackNetDriver.cpp') create_jack_driver_obj(bld, 'net', '../common/JackNetDriver.cpp')


create_jack_driver_obj(bld, 'loopback', '../common/JackLoopbackDriver.cpp') create_jack_driver_obj(bld, 'loopback', '../common/JackLoopbackDriver.cpp')

create_jack_driver_obj(bld, 'netone', [ '../common/JackNetOneDriver.cpp',
'../common/netjack.c',
'../common/netjack_packet.c' ], "SAMPLERATE CELT" )


+ 8
- 0
macosx/wscript View File

@@ -6,6 +6,11 @@ def create_jack_driver_obj(bld, target, sources, uselib = None):
driver.features.append('cc') driver.features.append('cc')
driver.env['shlib_PATTERN'] = 'jack_%s.so' driver.env['shlib_PATTERN'] = 'jack_%s.so'
driver.defines = ['HAVE_CONFIG_H','SERVER_SIDE'] driver.defines = ['HAVE_CONFIG_H','SERVER_SIDE']
if bld.env['HAVE_CELT']:
if bld.env['HAVE_CELT_API_0_5']:
driver.defines += ['HAVE_CELT', 'HAVE_CELT_API_0_5']
elif bld.env['HAVE_CELT_API_0_7']:
driver.defines += ['HAVE_CELT', 'HAVE_CELT_API_0_7']
driver.includes = ['.', '../macosx', '../posix', '../common', '../common/jack'] driver.includes = ['.', '../macosx', '../posix', '../common', '../common/jack']
driver.target = target driver.target = target
driver.source = sources driver.source = sources
@@ -73,3 +78,6 @@ def build(bld):


create_jack_driver_obj(bld, 'net', '../common/JackNetDriver.cpp') create_jack_driver_obj(bld, 'net', '../common/JackNetDriver.cpp')


create_jack_driver_obj(bld, 'netone', [ '../common/JackNetOneDriver.cpp',
'../common/netjack.c',
'../common/netjack_packet.c' ], "SAMPLERATE CELT" )

+ 9
- 0
wscript View File

@@ -224,6 +224,15 @@ def configure(conf):
else: else:
conf.env['LIBDIR'] = conf.env['PREFIX'] + '/lib32' conf.env['LIBDIR'] = conf.env['PREFIX'] + '/lib32'


if conf.check_cfg(package='celt', atleast_version='0.7.0', args='--cflags --libs'):
conf.env['HAVE_CELT'] = True
conf.env['HAVE_CELT_API_0_7'] = True
elif conf.check_cfg(package='celt', atleast_version='0.5.0', args='--cflags --libs', required=True):
conf.env['HAVE_CELT'] = True
conf.env['HAVE_CELT_API_0_5'] = True
else:
conf.env['HAVE_CELT'] = False

def build(bld): def build(bld):
print ("make[1]: Entering directory `" + os.getcwd() + "/" + blddir + "'" ) print ("make[1]: Entering directory `" + os.getcwd() + "/" + blddir + "'" )
if not os.access('svnversion.h', os.R_OK): if not os.access('svnversion.h', os.R_OK):


Loading…
Cancel
Save