naming of Darkice’s Jack port
sept 3rd, 2007 by Prune
Darkice is a really good free product when talking about live encoding and streaming. It is the best friend of Icecast, which is the deflector module (ie : the link between live stream and customers).
I used to use Darkice directly from ALSA, the Linux sound layer, and encode my live audio from the soundcard to MP3. But for my new radio project I had to use Jack. I already talked about Jack earlier. It is so damn easy you won’t believe it :
Start jack (or have Jack start automaticaly when you start something using jack - see /etc/jackd.conf)
Start your favorite app, let’s say Darkice
Connect your Darkice plug to the soundcard
and you’re done !
In the special case of Darkice, you can’t specify the name of the Jack plug you will get. This is hardcoded to “darkice-$PID” where $PID is the Darkice process number once started.
This means your Darkice plug name will change at every restart… not really conveniant.
Reading the Darkice Track, I found an opened bug related to this. The writer gives a solution to change that changing name to a static name, which is fine if you don’t have to run multiple Darkice on the same host. I do !
In fact, having a static name is also easyer when you are on a server (not a desktop) and you want to have Darkice connected to another Jack plug automaticaly. Else, you’ll have to find the PID of Darkice, then connect it. Ugly !
So I made another (dirty) patch to achieve that. As Darkice is C++ and is programmed in a way I even can’t read it I wasn’t able to add a new config parameter. Digging in the code showed me that only the first 4 letters of the
So you have plenty of room to add anything you like after that !
In fact it seems the code already use this feature to detect if Darkice have to auto connect to the first 2 Jack inputs -> don’t name your device as “jack_auto” so
My change keeps the same code. I just add something so the “name” defined in the “device” config parameter is accessible by the “open” function of the Jack layer of Darkice.
diff -ru darkice-0.18.1/src/JackDspSource.cpp darkice-0.18.1-new-rbsize/src/JackDspSource.cpp --- darkice-0.18.1/src/JackDspSource.cpp 2007-02-19 08:56:49.000000000 +0100 +++ darkice-0.18.1-new-rbsize/src/JackDspSource.cpp 2008-03-07 10:21:40.000000000 +0100 @@ -99,6 +99,7 @@ client = NULL; auto_connect = false; // Default is to not auto connect the JACK ports tmp_buffer = NULL; // Buffer big enough for one 'read' of audio + snprintf(jack_name, 255, "%s", name); // Auto connect the ports ? if ( Util::strEq( name, "jack_auto", 9) ) { @@ -188,7 +189,7 @@ } // Register client with Jack - snprintf(client_name, 255, "darkice-%d", getpid()); + snprintf(client_name, 255, "darkice-%s", jack_name); if ((client = jack_client_new(client_name)) == NULL) { throw Exception( __FILE__, __LINE__, "JACK server not running?"); } @@ -236,7 +237,7 @@ // Create a ring buffer for each channel - rb_size = 2 + rb_size = 32 * jack_get_sample_rate(client) * sizeof (jack_default_audio_sample_t); for (c=0; c<getchannel();> @@ -277,26 +278,42 @@ JackDspSource :: canRead ( unsigned int sec, unsigned int usec ) throw ( Exception ) { - size_t available=0; - + const unsigned int max_wait_time = sec * 1000000; + const unsigned int wait_increment = 10000; + if ( !isOpen() ) { return false; } - // How many bytes available in ring buffer ? - available = jack_ringbuffer_read_space( rb[0] ); - if (available) return true; - - // Sleep and check again - // FIXME: should we really sleep the full duration ? - usleep( (sec*1000000) + usec ); - - available = jack_ringbuffer_read_space( rb[0] ); - if (available) { - return true; - } else { - return false; + unsigned int cur_wait = 0; + + // wait for the buffer to be filled + while ( max_wait_time > cur_wait ){ + bool canRead = true; + for (unsigned int c = 0 ; c < getChannel() ; c++) + if ( jack_ringbuffer_read_space( rb[c] ) <= 2048 ) + canRead = false; + + if (canRead) + return true; + + cur_wait += wait_increment; + usleep ( wait_increment ); + } + + // impossible to get the buffer after max_wait_time + Reporter::reportEvent( 1, "Jack ringbuffer still not ready, waiting ", usec); + usleep( usec ); + + // check one last time + bool canRead = true; + for (unsigned int c = 0 ; c < getChannel() ; c++) { + if ( jack_ringbuffer_read_space( rb[c] ) <= 0 ) { + canRead = false; + Reporter::reportEvent( 1, "Jack ringbuffer still not ready on channel", c); + } } + return canRead; } @@ -324,6 +341,14 @@ throw Exception( __FILE__, __LINE__, "realloc on tmp_buffer failed"); } + // We must be sure to fetch as many data on both channels + // get minBytesAvailable samples or a smaller size available + int minBytesAvailable = samples * sizeof( jack_default_audio_sample_t ); + for (c=0; c<getchannel();> + int readable = jack_ringbuffer_read_space( rb[c] ); + if (readable < minBytesAvailable) + minBytesAvailable = readable; + } for (c=0; c<getchannel();> { @@ -331,7 +356,7 @@ // and then convert samples to output buffer int bytes_read = jack_ringbuffer_read(rb[c], (char*)tmp_buffer, - samples * sizeof( jack_default_audio_sample_t )); + minBytesAvailable); samples_read[c] = bytes_read / sizeof( jack_default_audio_sample_t ); @@ -418,7 +443,8 @@ char *buf = (char*)jack_port_get_buffer(self->ports[c], nframes); size_t len = jack_ringbuffer_write(self->rb[c], buf, to_write); if (len < to_write) { - Reporter::reportEvent( 1, "failed to write to ring ruffer"); + Reporter::reportEvent( 1, "failed to write to ring ruffer. wrote on channel ", len, to_write, c); + usleep( 1000000 ); return 1; } } </getchannel();></getchannel();></getchannel();>
root@dorado:/opt/src# diff -u darkice-0.18.1/src/JackDspSource.h darkice-0.18.1-new/src/JackDspSource.h --- darkice-0.18.1/src/JackDspSource.h 2007-02-19 08:56:49.000000000 +0100 +++ darkice-0.18.1-new/src/JackDspSource.h 2007-09-03 19:21:39.000000000 +0200 @@ -93,6 +93,9 @@ */ bool auto_connect; + + char jack_name[255]; + protected: /**
Compile Darkice and install it.
Change your config file so the “device” name start by “jack” and add anything you want the name to be.
For exemple, if the input section is :
[input] device = jack_in_1 sampleRate = 48000 bitsPerSample = 16 # bits per sample. try 16 channel = 2 # channels. 1 = mono, 2 = stereo
The resulting Jack plugs will be :
darkice-jack_in_1:left darkice-jack_in_1:right
j’aime beaucoup ton sens critiqu