| @@ -0,0 +1,419 @@ |
| | 1 | // |
| | 2 | // A Scream receiver JACK client |
| | 3 | // |
| | 4 | // (c) 2020 Oleksandr Dunayevskyy <oleksandr.dunayevskyy@gmail.com> |
| | 5 | // |
| | 6 | |
| | 7 | #include <jack/jack.h> |
| | 8 | #include <soxr.h> |
| | 9 | #include <stdio.h> |
| | 10 | #include <stdlib.h> |
| | 11 | #include <string.h> |
| | 12 | |
| | 13 | #include "jack.h" |
| | 14 | |
| | 15 | |
| | 16 | |
| | 17 | typedef jack_default_audio_sample_t ringbuffer_element_t; |
| | 18 | #define RINGBUFFER_SIZE 8192 |
| | 19 | struct ringbuffer_t |
| | 20 | { |
| | 21 | ringbuffer_element_t elements[RINGBUFFER_SIZE]; |
| | 22 | uint32_t read_pos; |
| | 23 | uint32_t write_pos; |
| | 24 | }; |
| | 25 | |
| | 26 | |
| | 27 | uint32_t ringbuffer_mask(uint32_t val) |
| | 28 | { |
| | 29 | return val & (RINGBUFFER_SIZE - 1u); |
| | 30 | } |
| | 31 | |
| | 32 | void ringbuffer_init(struct ringbuffer_t *rb) |
| | 33 | { |
| | 34 | rb->read_pos = 0u; |
| | 35 | rb->write_pos = 0u; |
| | 36 | } |
| | 37 | |
| | 38 | uint32_t ringbuffer_size(struct ringbuffer_t *rb) |
| | 39 | { |
| | 40 | return rb->write_pos - rb->read_pos; |
| | 41 | } |
| | 42 | |
| | 43 | int ringbuffer_empty(struct ringbuffer_t *rb) |
| | 44 | { |
| | 45 | return ringbuffer_size(rb) == 0u; |
| | 46 | } |
| | 47 | |
| | 48 | int ringbuffer_full(struct ringbuffer_t *rb) |
| | 49 | { |
| | 50 | return ringbuffer_size(rb) == RINGBUFFER_SIZE; |
| | 51 | } |
| | 52 | |
| | 53 | void ringbuffer_push(struct ringbuffer_t *rb, ringbuffer_element_t val) |
| | 54 | { |
| | 55 | if (!ringbuffer_full(rb)) |
| | 56 | { |
| | 57 | rb->elements[ringbuffer_mask(rb->write_pos++)] = val; |
| | 58 | } |
| | 59 | else |
| | 60 | { |
| | 61 | fprintf(stderr, "ringbuffer overflow\n"); |
| | 62 | } |
| | 63 | |
| | 64 | } |
| | 65 | |
| | 66 | ringbuffer_element_t ringbuffer_pop(struct ringbuffer_t *rb) |
| | 67 | { |
| | 68 | if (!ringbuffer_empty(rb)) |
| | 69 | { |
| | 70 | return rb->elements[ringbuffer_mask(rb->read_pos++)]; |
| | 71 | } |
| | 72 | else |
| | 73 | { |
| | 74 | fprintf(stderr, "ringbuffer underflow\n"); |
| | 75 | return (ringbuffer_element_t)0; |
| | 76 | } |
| | 77 | |
| | 78 | } |
| | 79 | |
| | 80 | |
| | 81 | static struct jack_output_data |
| | 82 | { |
| | 83 | jack_client_t *client; |
| | 84 | jack_port_t **output_ports; ///< number of ports actually used == rf->channels |
| | 85 | jack_default_audio_sample_t **buffers; |
| | 86 | receiver_format_t receiver_format; |
| | 87 | uint32_t sample_rate; ///< source sample rate |
| | 88 | soxr_t soxr; |
| | 89 | jack_default_audio_sample_t *resample_buffer; |
| | 90 | size_t resample_buffer_size; |
| | 91 | struct ringbuffer_t rb; |
| | 92 | } |
| | 93 | jo_data; |
| | 94 | |
| | 95 | |
| | 96 | |
| | 97 | static int init_resampler(); |
| | 98 | static int init_channels(); |
| | 99 | static int connect_ports(); |
| | 100 | static int process_source_data(); |
| | 101 | |
| | 102 | // JACK realtime process callback |
| | 103 | int jack_process(jack_nframes_t nframes, void *arg); |
| | 104 | |
| | 105 | |
| | 106 | |
| | 107 | int jack_output_init(char *stream_name) |
| | 108 | { |
| | 109 | jack_status_t status; |
| | 110 | |
| | 111 | // init receiver format to track changes |
| | 112 | jo_data.receiver_format.sample_rate = 0; |
| | 113 | jo_data.receiver_format.sample_size = 0; |
| | 114 | jo_data.receiver_format.channels = 2; |
| | 115 | jo_data.receiver_format.channel_map = 0x0003; |
| | 116 | |
| | 117 | jo_data.soxr = NULL; |
| | 118 | jo_data.resample_buffer = NULL; |
| | 119 | jo_data.resample_buffer_size = 0; |
| | 120 | |
| | 121 | ringbuffer_init(&jo_data.rb); |
| | 122 | |
| | 123 | |
| | 124 | jo_data.client = jack_client_open(stream_name, JackNullOption, &status, NULL); |
| | 125 | if (jo_data.client == NULL) |
| | 126 | { |
| | 127 | fprintf(stderr, "jack_client_open() failed, status = 0x%2.0x\n", status); |
| | 128 | if (status & JackServerFailed) |
| | 129 | { |
| | 130 | fprintf(stderr, "Unable to connect to JACK server\n"); |
| | 131 | } |
| | 132 | return 1; |
| | 133 | } |
| | 134 | |
| | 135 | if (status & JackServerStarted) |
| | 136 | { |
| | 137 | fprintf(stderr, "JACK server started\n"); |
| | 138 | } |
| | 139 | |
| | 140 | if (status & JackNameNotUnique) |
| | 141 | { |
| | 142 | const char *client_name = jack_get_client_name(jo_data.client); |
| | 143 | fprintf(stderr, "unique name `%s' assigned\n", client_name); |
| | 144 | } |
| | 145 | |
| | 146 | jack_set_process_callback(jo_data.client, jack_process, 0); |
| | 147 | |
| | 148 | return 0; |
| | 149 | } |
| | 150 | |
| | 151 | |
| | 152 | |
| | 153 | |
| | 154 | int jack_output_send(receiver_data_t *data) |
| | 155 | { |
| | 156 | receiver_format_t *rf = &data->format; |
| | 157 | |
| | 158 | if (memcmp(&jo_data.receiver_format, rf, sizeof(receiver_format_t))) |
| | 159 | { |
| | 160 | // audio format changed, reconfigure |
| | 161 | memcpy(&jo_data.receiver_format, rf, sizeof(receiver_format_t)); |
| | 162 | |
| | 163 | jo_data.sample_rate = ((rf->sample_rate >= 128) ? 44100 : 48000) * (rf->sample_rate % 128); |
| | 164 | |
| | 165 | printf( |
| | 166 | "Switched sample rate %"PRIu32", sample size %u and %u channels\n", |
| | 167 | jo_data.sample_rate, |
| | 168 | jo_data.receiver_format.sample_size, |
| | 169 | rf->channels); |
| | 170 | |
| | 171 | printf("JACK sample rate %" PRIu32 "\n", jack_get_sample_rate(jo_data.client)); |
| | 172 | |
| | 173 | |
| | 174 | if (init_resampler()) |
| | 175 | { |
| | 176 | return 1; |
| | 177 | } |
| | 178 | |
| | 179 | |
| | 180 | |
| | 181 | if (jack_deactivate(jo_data.client)) |
| | 182 | { |
| | 183 | fprintf(stderr, "cannot deactivate client"); |
| | 184 | return 1; |
| | 185 | } |
| | 186 | |
| | 187 | if (init_channels()) |
| | 188 | { |
| | 189 | return 1; |
| | 190 | } |
| | 191 | |
| | 192 | // activating JJACK client - jack_process() callback will start running now |
| | 193 | if (jack_activate(jo_data.client)) |
| | 194 | { |
| | 195 | fprintf(stderr, "cannot activate client"); |
| | 196 | return 1; |
| | 197 | } |
| | 198 | |
| | 199 | if (connect_ports()) |
| | 200 | { |
| | 201 | return 1; |
| | 202 | } |
| | 203 | } |
| | 204 | |
| | 205 | |
| | 206 | if (process_source_data(data)) |
| | 207 | { |
| | 208 | return 1; |
| | 209 | } |
| | 210 | |
| | 211 | return 0; |
| | 212 | } |
| | 213 | |
| | 214 | |
| | 215 | |
| | 216 | |
| | 217 | static int init_resampler() |
| | 218 | { |
| | 219 | soxr_io_spec_t io_spec; |
| | 220 | soxr_datatype_t in_datatype; |
| | 221 | switch(jo_data.receiver_format.sample_size) |
| | 222 | { |
| | 223 | case 16: in_datatype = SOXR_INT16_I; break; |
| | 224 | case 32: in_datatype = SOXR_INT32_I; break; |
| | 225 | default: |
| | 226 | return 1; |
| | 227 | } |
| | 228 | |
| | 229 | io_spec = soxr_io_spec(in_datatype, SOXR_FLOAT32_I); |
| | 230 | if (jo_data.soxr) |
| | 231 | { |
| | 232 | soxr_delete(jo_data.soxr); |
| | 233 | } |
| | 234 | jo_data.soxr = soxr_create( |
| | 235 | jo_data.sample_rate, |
| | 236 | jack_get_sample_rate(jo_data.client), |
| | 237 | jo_data.receiver_format.channels, |
| | 238 | NULL, &io_spec, NULL, NULL ); |
| | 239 | if (!jo_data.soxr ) |
| | 240 | { |
| | 241 | fprintf(stderr, "failed to initialize resampler"); |
| | 242 | return 1; |
| | 243 | } |
| | 244 | return 0; |
| | 245 | } |
| | 246 | |
| | 247 | |
| | 248 | static const char *channel_index_to_name(uint8_t channel) |
| | 249 | { |
| | 250 | const char *channel_name; |
| | 251 | switch (channel) { |
| | 252 | case 0: channel_name = "Front Left"; break; |
| | 253 | case 1: channel_name = "Front Right"; break; |
| | 254 | case 2: channel_name = "Front Center"; break; |
| | 255 | case 3: channel_name = "LFE / Subwoofer"; break; |
| | 256 | case 4: channel_name = "Rear Left"; break; |
| | 257 | case 5: channel_name = "Rear Right"; break; |
| | 258 | case 6: channel_name = "Front-Left Center"; break; |
| | 259 | case 7: channel_name = "Front-Right Center"; break; |
| | 260 | case 8: channel_name = "Rear Center"; break; |
| | 261 | case 9: channel_name = "Side Left"; break; |
| | 262 | case 10: channel_name = "Side Right"; break; |
| | 263 | default: channel_name = "unknown_channel"; |
| | 264 | } |
| | 265 | |
| | 266 | return channel_name; |
| | 267 | } |
| | 268 | |
| | 269 | |
| | 270 | static int init_channels() |
| | 271 | { |
| | 272 | free(jo_data.output_ports); |
| | 273 | jo_data.output_ports = malloc(sizeof(jack_port_t*) * jo_data.receiver_format.channels); |
| | 274 | free(jo_data.buffers); |
| | 275 | jo_data.buffers = malloc(sizeof(jack_default_audio_sample_t*) * jo_data.receiver_format.channels); |
| | 276 | for (int i = 0 ; i < jo_data.receiver_format.channels; ++i) |
| | 277 | { |
| | 278 | if ( (jo_data.receiver_format.channel_map >> i) & 1 ) |
| | 279 | { |
| | 280 | const char *port_name = channel_index_to_name(i); |
| | 281 | jo_data.output_ports[i] = jack_port_register(jo_data.client, |
| | 282 | port_name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0); |
| | 283 | printf("registered jack port '%s' for channel %u\n", port_name, i); |
| | 284 | |
| | 285 | if (jo_data.output_ports[i] == NULL) |
| | 286 | { |
| | 287 | fprintf(stderr, "no more JACK ports available\n"); |
| | 288 | return 1; |
| | 289 | } |
| | 290 | } |
| | 291 | } |
| | 292 | return 0; |
| | 293 | } |
| | 294 | |
| | 295 | |
| | 296 | static int connect_ports() |
| | 297 | { |
| | 298 | int num_output_ports = 0; |
| | 299 | |
| | 300 | const char **ports = jack_get_ports(jo_data.client, NULL, NULL, JackPortIsPhysical|JackPortIsInput); |
| | 301 | if (ports == NULL) |
| | 302 | { |
| | 303 | fprintf(stderr, "no physical playback ports\n"); |
| | 304 | return 1; |
| | 305 | } |
| | 306 | |
| | 307 | // connect the receiver if there is enough output ports |
| | 308 | for ( ; ports[num_output_ports] && (!strstr(ports[num_output_ports],"midi")); ++num_output_ports) |
| | 309 | { |
| | 310 | ; |
| | 311 | } |
| | 312 | |
| | 313 | |
| | 314 | if (num_output_ports >= jo_data.receiver_format.channels) |
| | 315 | { |
| | 316 | for (int port = 0; port < jo_data.receiver_format.channels && ports[port] ; ++port) |
| | 317 | { |
| | 318 | if (jack_connect(jo_data.client, jack_port_name(jo_data.output_ports[port]), ports[port])) |
| | 319 | { |
| | 320 | fprintf(stderr, "cannot connect to output port %s\n", ports[port]); |
| | 321 | } |
| | 322 | else |
| | 323 | { |
| | 324 | printf("channel %u connected to output port %s\n", port, ports[port]); |
| | 325 | } |
| | 326 | |
| | 327 | } |
| | 328 | } |
| | 329 | |
| | 330 | jack_free(ports); |
| | 331 | } |
| | 332 | |
| | 333 | |
| | 334 | static int process_source_data(receiver_data_t *data) |
| | 335 | { |
| | 336 | // per channel! |
| | 337 | size_t src_nsamples; |
| | 338 | size_t dst_nsamples; |
| | 339 | size_t idone; |
| | 340 | size_t odone; |
| | 341 | |
| | 342 | size_t resample_ideal_buf_size; |
| | 343 | soxr_error_t soxr_error; |
| | 344 | |
| | 345 | // (re)allocate a resampling buffer if needed |
| | 346 | src_nsamples = data->audio_size / (jo_data.receiver_format.sample_size >> 3) / jo_data.receiver_format.channels; |
| | 347 | dst_nsamples = src_nsamples * jack_get_sample_rate(jo_data.client) / jo_data.sample_rate + 1; |
| | 348 | resample_ideal_buf_size = dst_nsamples*jo_data.receiver_format.channels; |
| | 349 | if (jo_data.resample_buffer_size < resample_ideal_buf_size) |
| | 350 | { |
| | 351 | free(jo_data.resample_buffer); |
| | 352 | jo_data.resample_buffer = malloc(resample_ideal_buf_size* sizeof(jack_default_audio_sample_t)); |
| | 353 | jo_data.resample_buffer_size = resample_ideal_buf_size; |
| | 354 | } |
| | 355 | |
| | 356 | soxr_error = soxr_process(jo_data.soxr, data->audio, src_nsamples, &idone, jo_data.resample_buffer, dst_nsamples, &odone); |
| | 357 | if (soxr_error != NULL) |
| | 358 | { |
| | 359 | fprintf(stderr,"soxr error : %s\n", soxr_strerror(soxr_error)); |
| | 360 | return 1; |
| | 361 | } |
| | 362 | |
| | 363 | for (int i = 0; i < odone*jo_data.receiver_format.channels; ++i) |
| | 364 | { |
| | 365 | ringbuffer_push(&jo_data.rb, jo_data.resample_buffer[i]); |
| | 366 | } |
| | 367 | |
| | 368 | return 0; |
| | 369 | } |
| | 370 | |
| | 371 | |
| | 372 | |
| | 373 | |
| | 374 | |
| | 375 | int jack_process(jack_nframes_t nframes, void *arg) |
| | 376 | { |
| | 377 | uint8_t channels = jo_data.receiver_format.channels; |
| | 378 | uint32_t total_nframes = nframes * channels; |
| | 379 | uint32_t read_frames = ringbuffer_size(&jo_data.rb); |
| | 380 | |
| | 381 | // wait until rb fills up a bit to prevent early underruns |
| | 382 | if (read_frames < (total_nframes + 200)) |
| | 383 | { |
| | 384 | return 0; |
| | 385 | } |
| | 386 | |
| | 387 | if (total_nframes <= read_frames) |
| | 388 | { |
| | 389 | read_frames = total_nframes; |
| | 390 | } |
| | 391 | else |
| | 392 | { |
| | 393 | // JACK wants more than we can fullfill |
| | 394 | fprintf(stderr, "jack_process: underflow!! read_frames=%u nframes=%u\n", read_frames, total_nframes); |
| | 395 | } |
| | 396 | |
| | 397 | |
| | 398 | if (read_frames > 0) |
| | 399 | { |
| | 400 | for (int port = 0; port < channels; ++port) |
| | 401 | { |
| | 402 | jo_data.buffers[port] = jack_port_get_buffer(jo_data.output_ports[port], nframes); |
| | 403 | } |
| | 404 | |
| | 405 | // transfer samples from ringbuffer to JACK port buffers |
| | 406 | for (uint32_t sample = 0; sample < read_frames /channels; ++sample) |
| | 407 | { |
| | 408 | for (int port = 0; port < channels; ++port) |
| | 409 | { |
| | 410 | *jo_data.buffers[port] = ringbuffer_pop(&jo_data.rb); |
| | 411 | jo_data.buffers[port]++; |
| | 412 | |
| | 413 | } |
| | 414 | } |
| | 415 | } |
| | 416 | |
| | 417 | return 0; |
| | 418 | } |
| | 419 | |