1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428
|
#include <pthread.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <jack/jack.h>
#include <jack/thread.h>
#include <samplerate.h>
#include "c-common/failure.h"
#include "c-common/file.h"
#include "c-common/int.h"
#include "c-common/jack-client.h"
#include "c-common/jack-ringbuffer.h"
#include "c-common/jack-port.h"
#include "c-common/jack-transport.h"
#include "c-common/memory.h"
#include "c-common/observe-signal.h"
#include "c-common/print.h"
#include "c-common/sound-file.h"
struct player_opt
{
int buffer_frames;
int minimal_frames;
i64 seek_request;
bool transport_aware;
int unique_name;
double src_ratio;
int rb_request_frames;
int converter;
char client_name[64];
};
struct player
{
int buffer_bytes;
int buffer_samples;
float *d_buffer;
float *j_buffer;
float *k_buffer;
SNDFILE *sound_file;
int channels;
jack_port_t **output_port;
float **out;
jack_ringbuffer_t *rb;
pthread_t disk_thread;
int pipe[2];
jack_client_t *client;
SRC_STATE *src;
struct player_opt o;
};
/* Read the sound file from disk and write to the ring buffer until
the end of file, at which point return. */
void *disk_proc(void *PTR)
{
struct player *d = (struct player *)PTR;
while(!observe_end_of_process()) {
/* Handle seek request. */
if(d->o.seek_request >= 0) {
sf_count_t err = sf_seek(d->sound_file,
(sf_count_t)d->o.seek_request, SEEK_SET);
if(err == -1) {
eprintf("jack-play: seek request failed, %ld\n",
(long)d->o.seek_request);
}
d->o.seek_request = -1;
}
/* Wait for write space at the ring buffer. */
int nbytes = d->o.minimal_frames * sizeof(float) * d->channels;
nbytes = jack_ringbuffer_wait_for_write(d->rb, nbytes, d->pipe[0]);
/* Do not overflow the local buffer. */
if(nbytes > d->buffer_bytes) {
eprintf("jack-play: impossible condition, write space.\n");
nbytes = d->buffer_bytes;
}
/* Read sound file data, which *must* be frame aligned. */
int nframes =(nbytes / sizeof(float))/ d->channels;
int nsamples = nframes * d->channels;
sf_count_t err = xsf_read_float(d->sound_file,
d->d_buffer,
(sf_count_t)nsamples);
if(err == 0) {
if(d->o.transport_aware) {
memset(d->d_buffer, 0, nsamples * sizeof(float));
err = nsamples;
} else {
return NULL;
}
}
/* Write data to ring buffer. */
jack_ringbuffer_write(d->rb,
(char *)d->d_buffer,
(size_t)err * sizeof(float));
}
return NULL;
}
int sync_handler(jack_transport_state_t state,
jack_position_t *position,
void *PTR)
{
struct player *d = PTR;
d->o.seek_request = (i64)position->frame;
return 1;
}
void signal_set(float **s, int n, int c, float z)
{
int j;
for(j = 0; j < c; j++) {
int i;
for(i = 0; i < n; i++) {
s[j][i] = z;
}
}
}
/* Write data from the ring buffer to the JACK output ports. If the
disk thread is late, ie. the ring buffer is empty print a warning
and zero the output ports. */
int signal_proc(jack_nframes_t nframes, void *PTR)
{
struct player *d = (struct player *)PTR;
int nsamples = nframes * d->channels;
int nbytes = nsamples * sizeof(float);
/* Ensure the period size is workable. */
if(nbytes >= d->buffer_bytes) {
eprintf("jack-play: period size exceeds limit\n");
FAILURE;
return 1;
}
/* Get port data buffers. */
int i,j;
for(i = 0; i < d->channels; i++) {
d->out[i] = (float *)jack_port_get_buffer(d->output_port[i], nframes);
}
/* Write silence if the transport is stopped. If stopped the disk
thread will sleep and signals will be ignored, so check here
also. */
if(d->o.transport_aware && !jack_transport_is_rolling(d->client)) {
if(observe_end_of_process ()) {
FAILURE;
return 1;
} else {
signal_set(d->out, nframes, d->channels, 0.0);
return 0;
}
}
/* Get data from sample rate converter, this returns the number of
frames acquired. */
long err = src_callback_read (d->src,
d->o.src_ratio,
(long)nframes,
d->j_buffer);
if(err==0) {
eprintf("jack-play: sample rate converter failed: %s\n",
src_strerror(src_error(d->src)));
FAILURE;
}
/* Uninterleave available data to the output buffers. */
for(i = 0; i < err; i++) {
for(j = 0; j < d->channels; j++) {
d->out[j][i] = d->j_buffer[(i*d->channels)+j];
}
}
/* If any sample data is unavailable inform the user and zero the
output buffers. The print statement is not correct, a this
should set a flag and have another thread take appropriate
action. */
if(err < nframes) {
eprintf("jack-play: disk thread late (%ld < %d)\n", err, nframes);
for(i = err; i < nframes; i++) {
for(j = 0; j < d->channels; j++) {
d->out[j][i] = 0.0;
}
}
}
/* Indicate to the disk thread that the ring buffer has been read
from. This is done by writing a single byte to a communication
pipe. Once the disk thread gets so far ahead that the ring
buffer is full it reads this communication channel to wait for
space to become available. So long as PIPE_BUF is not a
pathologically small value this write operation is atomic and
will not block. The number of bytes that can accumulate in the
pipe is a factor of the relative sizes of the ring buffer and the
process callback, but should in no case be very large. */
char b = 1;
xwrite(d->pipe[1], &b, 1);
return 0;
}
void usage(void)
{
eprintf("Usage: jack-play [ options ] sound-file...\n");
eprintf(" -b N : Ring buffer size in frames (default=4096).\n");
eprintf(" -c N : ID of conversion algorithm (default=2, SRC_SINC_FASTEST).\n");
eprintf(" -i N : Initial disk seek in frames (default=0).\n");
eprintf(" -m N : Minimal disk read size in frames (default=32).\n");
eprintf(" -q N : Frames to request from ring buffer (default=64).\n");
eprintf(" -r N : Resampling ratio multiplier (default=1.0).\n");
eprintf(" -t : Jack transport awareness.\n");
FAILURE;
}
/* Get data from ring buffer. Return number of frames read. This
could check the read size first, but then would still need to check
the actual result size, and therefore have two error cases. Since
there is no alternative but to drop sample data in any case it does
not matter much. */
long read_input_from_rb(void *PTR, float **buf)
{
struct player *d = PTR;
int nsamples = d->channels * d->o.rb_request_frames;
int nbytes = (size_t)nsamples * sizeof(float);
int err = jack_ringbuffer_read(d->rb,
(char *)d->k_buffer,
nbytes);
err /= d->channels * sizeof(float);
*buf = d->k_buffer;
/* SRC locks up if we return zero here, return a silent frame */
if(err==0) {
eprintf("jack-play: ringbuffer empty... zeroing data\n");
memset(d->k_buffer, 0, (size_t)nsamples * sizeof(float));
err = d->o.rb_request_frames;
}
return (long)err;
}
int jackplay(const char *file_name,
struct player_opt o)
{
struct player d;
d.o = o;
observe_signals ();
/* Open sound file. */
SF_INFO sfinfo;
d.sound_file = xsf_open(file_name, SFM_READ, &sfinfo);
d.channels = sfinfo.channels;
/* Allocate channel based data. */
if(d.channels < 1) {
eprintf("jack-play: illegal number of channels in file: %d\n",
d.channels);
FAILURE;
}
d.out = xmalloc(d.channels * sizeof(float *));
d.output_port = xmalloc(d.channels * sizeof(jack_port_t *));
/* Allocate buffers. */
d.buffer_samples = d.o.buffer_frames * d.channels;
d.buffer_bytes = d.buffer_samples * sizeof(float);
d.d_buffer = xmalloc(d.buffer_bytes);
d.j_buffer = xmalloc(d.buffer_bytes);
d.k_buffer = xmalloc(d.buffer_bytes);
d.rb = jack_ringbuffer_create(d.buffer_bytes);
/* Setup sample rate conversion. */
int err;
d.src = src_callback_new (read_input_from_rb,
d.o.converter,
d.channels,
&err,
&d);
if(!d.src) {
eprintf("jack-play: sample rate conversion setup failed: %s\n",
src_strerror(err));
FAILURE;
}
/* Create communication pipe. */
xpipe(d.pipe);
/* Become a client of the JACK server. */
if(d.o.unique_name) {
d.client = jack_client_unique_store(d.o.client_name);
} else {
d.client = jack_client_open(d.o.client_name,JackNullOption,NULL);
}
if(!d.client) {
eprintf("jack-play: could not create jack client: %s", d.o.client_name);
FAILURE;
}
/* Start disk thread, the priority number is a random guess.... */
jack_client_create_thread (d.client,
&(d.disk_thread),
50,
true,
disk_proc,
&d);
/* Set error, process and shutdown handlers. */
jack_set_error_function(jack_client_minimal_error_handler);
jack_on_shutdown(d.client, jack_client_minimal_shutdown_handler, 0);
if(d.o.transport_aware) {
jack_set_sync_callback(d.client, sync_handler, &d);
}
jack_set_process_callback(d.client, signal_proc, &d);
/* Inform the user of sample-rate mismatch and set SRC ratio. */
double osr = (double) jack_get_sample_rate(d.client);
double isr = (double) sfinfo.samplerate;
if(osr != isr) {
d.o.src_ratio *= (osr / isr);
eprintf("jack-play: resampling, sample rate of file != server, %G != %G (%G)\n",
isr,
osr,
d.o.src_ratio);
}
/* Create output ports, connect if env variable set and activate
client. */
jack_port_make_standard(d.client, d.output_port, d.channels, true);
jack_client_activate(d.client);
char *dst_pattern = getenv("JACK_PLAY_CONNECT_TO");
if (dst_pattern) {
char src_pattern[128];
snprintf(src_pattern,128,"%s:out_%%d",d.o.client_name);
jack_port_connect_pattern(d.client,d.channels,0,src_pattern,dst_pattern);
}
/* Wait for disk thread to end, which it does when it reaches the
end of the file or is interrupted. */
pthread_join(d.disk_thread, NULL);
/* Close sound file, free ring buffer, close JACK connection, close
pipe, free data buffers, indicate success. */
jack_client_close(d.client);
sf_close(d.sound_file);
jack_ringbuffer_free(d.rb);
close(d.pipe[0]);
close(d.pipe[1]);
free(d.d_buffer);
free(d.j_buffer);
free(d.k_buffer);
free(d.out);
free(d.output_port);
src_delete(d.src);
return 0;
}
int main(int argc, char *argv[])
{
struct player_opt o;
int c;
o.buffer_frames = 4096;
o.minimal_frames = 32;
o.seek_request = -1;
o.transport_aware = false;
o.unique_name = true;
o.src_ratio = 1.0;
o.rb_request_frames = 64;
o.converter = SRC_SINC_FASTEST;
strncpy(o.client_name, "jack-play", 64);
while((c = getopt(argc, argv, "b:c:hi:m:n:q:r:tu")) != -1) {
switch(c) {
case 'b':
o.buffer_frames = (int)strtol(optarg, NULL, 0);
break;
case 'c':
o.converter = (int)strtol(optarg, NULL, 0);
break;
case 'h':
usage ();
break;
case 'i':
o.seek_request = (i64)strtol(optarg, NULL, 0);
break;
case 'm':
o.minimal_frames = (int)strtoll(optarg, NULL, 0);
break;
case 'n':
strncpy(o.client_name, optarg, 64);
eprintf("jack client name: %s\n", o.client_name);
break;
case 'q':
o.rb_request_frames = strtol(optarg, NULL, 0);
break;
case 'r':
o.src_ratio = strtod(optarg, NULL);
break;
case 't':
o.transport_aware = true;
break;
case 'u':
o.unique_name = false;
break;
default:
eprintf("jack-play: illegal option, %c\n", c);
usage ();
break;
}
}
if(optind > argc - 1) {
usage ();
}
int i;
for(i = optind; i < argc; i++) {
printf("jack-play: %s\n", argv[i]);
jackplay(argv[i], o);
}
return EXIT_SUCCESS;
}
|