// static void help(void);
int init(int, char **);
// static void onmove_cb(void *, int);
-// static void deinit(void);
+void jack_deinit(void);
void jack_start(int, int);
int play(void *, int);
void jack_stop(void);
audio_output audio_jack = {.name = "jack",
.help = NULL,
.init = &init,
- .deinit = NULL,
+ .deinit = &jack_deinit,
.start = &jack_start,
.stop = &jack_stop,
.is_running = &jack_is_running,
.parameters = NULL,
.mute = NULL};
-typedef jack_default_audio_sample_t sample_t;
-
-const double PI = 3.14;
-
jack_port_t *left_port;
jack_port_t *right_port;
+
long offset = 0;
-int transport_aware = 0;
-jack_transport_state_t transport_state;
int client_is_open;
jack_client_t *client;
jack_nframes_t sample_rate;
-jack_latency_range_t latest_latency_range;
-int64_t time_of_latest_latency_range;
+jack_latency_range_t latest_left_latency_range,latest_right_latency_range;
+int64_t time_of_latest_transfer;
int play(void *buf, int samples) {
// debug(1,"jack_play of %d samples.",samples);
audio_eoq = audio_lmb + bytes_to_transfer - space_to_end_of_buffer;
pthread_mutex_unlock(&buffer_mutex);
}
-
- if ((audio_occupancy >= 11025 * 2 * 2)) {
- }
-
return 0;
}
void deinterleave_and_convert_stream(const char *interleaved_frames,
- const sample_t *jack_frame_buffer,
+ const jack_default_audio_sample_t *jack_frame_buffer,
jack_nframes_t number_of_frames, enum ift_type side) {
jack_nframes_t i;
short *ifp = (short *)interleaved_frames;
- sample_t *fp = (sample_t *)jack_frame_buffer;
+ jack_default_audio_sample_t *fp = (jack_default_audio_sample_t *)jack_frame_buffer;
if (side == IFT_frame_right_sample)
ifp++;
for (i = 0; i < number_of_frames; i++) {
short sample = *ifp;
- sample_t converted_value;
+ jack_default_audio_sample_t converted_value;
if (sample >= 0)
converted_value = (1.0 * sample) / SHRT_MAX;
else
int jack_stream_write_cb(jack_nframes_t nframes, __attribute__((unused)) void *arg) {
- sample_t *left_buffer = (sample_t *)jack_port_get_buffer(left_port, nframes);
- sample_t *right_buffer = (sample_t *)jack_port_get_buffer(right_port, nframes);
+ jack_default_audio_sample_t *left_buffer =
+ (jack_default_audio_sample_t *)jack_port_get_buffer(left_port, nframes);
+ jack_default_audio_sample_t *right_buffer =
+ (jack_default_audio_sample_t *)jack_port_get_buffer(right_port, nframes);
size_t frames_we_can_transfer = nframes;
// lock
}
// debug(1,"transferring %u frames",frames_we_can_transfer);
audio_occupancy -= frames_we_can_transfer;
+ jack_port_get_latency_range(left_port, JackPlaybackLatency, &latest_left_latency_range);
+ jack_port_get_latency_range(right_port, JackPlaybackLatency, &latest_right_latency_range);
+ time_of_latest_transfer = get_absolute_time_in_fp();
pthread_mutex_unlock(&buffer_mutex);
// unlock
-
- // now, if there are any more frames to put into the buffer, fill them with
+
+ // now, if there are any more frames to put into the buffer, fill them with
// silence
jack_nframes_t i;
left_buffer[i] = 0.0;
right_buffer[i] = 0.0;
}
-
- jack_port_get_latency_range(left_port, JackPlaybackLatency, &latest_latency_range);
- time_of_latest_latency_range = get_absolute_time_in_fp();
-
return 0;
}
-void default_jack_error_callback(const char *desc) {
- debug(2,"jackd error: \"%s\"",desc);
-}
+void default_jack_error_callback(const char *desc) { debug(2, "jackd error: \"%s\"", desc); }
-void default_jack_info_callback(const char *desc) {
- inform("jackd information: \"%s\"",desc);
-}
+void default_jack_info_callback(const char *desc) { inform("jackd information: \"%s\"", desc); }
int jack_is_running() {
- int reply = -1;
+ int reply = -1;
// if the client is open and initialised, see if the status is "rolling"
if (client_is_open) {
jack_position_t pos;
- jack_transport_state_t transport_state = jack_transport_query (client, &pos);
- if (transport_state == JackTransportRolling)
- reply = 0;
- else
- reply = -2;
+ jack_transport_state_t transport_state = jack_transport_query(client, &pos);
+ if (transport_state == JackTransportRolling) {
+ // check if either port has a zero latency -- if so, then it's disconnected. This might be too
+ // much
+ jack_latency_range_t left_latency_range, right_latency_range;
+ jack_port_get_latency_range(left_port, JackPlaybackLatency, &left_latency_range);
+ jack_port_get_latency_range(right_port, JackPlaybackLatency, &right_latency_range);
+
+ if ((left_latency_range.min == 0) && (left_latency_range.max == 0) &&
+ (right_latency_range.min == 0) && (right_latency_range.max == 0)) {
+ reply = -3; // not connected
+ } else {
+ reply = 0;
+ }
+ } else {
+ reply = -2; // not rolling
+ }
}
return reply;
}
+int jack_client_open_if_needed(void) {
+ if (client_is_open == 0) {
+ jack_status_t status;
+ client = jack_client_open(config.jack_client_name, JackNoStartServer, &status);
+ if (client) {
+ jack_set_process_callback(client, jack_stream_write_cb, 0);
+ left_port = jack_port_register(client, config.jack_left_channel_name, JACK_DEFAULT_AUDIO_TYPE,
+ JackPortIsOutput, 0);
+ right_port = jack_port_register(client, config.jack_right_channel_name,
+ JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0);
+ sample_rate = jack_get_sample_rate(client);
+ // debug(1, "jackaudio sample rate = %" PRId32 ".", sample_rate);
+ if (sample_rate == 44100) {
+ if (jack_activate(client)) {
+ debug(1, "jackaudio cannot activate client");
+ } else {
+ client_is_open = 1;
+ debug(2, "jackaudio client opened.");
+ }
+ } else {
+ inform(
+ "jackaudio is running at the wrong speed (%d) for Shairport Sync, which must be 44100",
+ sample_rate);
+ }
+ }
+ }
+ return client_is_open;
+}
+
+void jack_deinit() {
+ if (client_is_open) {
+ if (jack_deactivate(client))
+ debug(1, "Error deactivating jack client");
+ if (jack_client_close(client))
+ debug(1, "Error closing jack client");
+ }
+}
+
int init(__attribute__((unused)) int argc, __attribute__((unused)) char **argv) {
config.audio_backend_latency_offset = 0;
config.audio_backend_buffer_desired_length = 0.15;
// do the "general" audio options. Note, these options are in the "general" stanza!
parse_general_audio_options();
-
+
// other options would be picked up here...
- jack_set_error_function(default_jack_error_callback);
+ // now the specific options
+ if (config.cfg != NULL) {
+ const char *str;
+ /* Get the Client Name. */
+ if (config_lookup_string(config.cfg, "jack.client_name", &str)) {
+ config.jack_client_name = (char *)str;
+ }
+ /* Get the Left Channel Name. */
+ if (config_lookup_string(config.cfg, "jack.left_channel_name", &str)) {
+ config.jack_left_channel_name = (char *)str;
+ }
+ /* Get the Right Channel Name. */
+ if (config_lookup_string(config.cfg, "jack.right_channel_name", &str)) {
+ config.jack_right_channel_name = (char *)str;
+ }
+ }
+
+ if (config.jack_client_name == NULL)
+ config.jack_client_name = strdup("Shairport Sync");
+ if (config.jack_left_channel_name == NULL)
+ config.jack_left_channel_name = strdup("left");
+ if (config.jack_right_channel_name == NULL)
+ config.jack_right_channel_name = strdup("right");
+
+ jack_set_error_function(default_jack_error_callback);
jack_set_info_function(default_jack_info_callback);
- client_is_open = 0;
// allocate space for the audio buffer
audio_lmb = malloc(buffer_size);
audio_toq = audio_eoq = audio_lmb;
audio_umb = audio_lmb + buffer_size;
audio_occupancy = 0; // frames
+
+ client_is_open = 0;
+ jack_client_open_if_needed();
+
return 0;
}
-void jack_start(__attribute__((unused)) int i_sample_rate, __attribute__((unused)) int i_sample_format) {
- debug(1,"jack start");
+void jack_start(__attribute__((unused)) int i_sample_rate,
+ __attribute__((unused)) int i_sample_format) {
+ debug(1, "jack start");
// int reply = -1;
-
- // see if the client is running. If not, try to open and initialise it
- if (client_is_open == 0) {
- jack_status_t status;
- client = jack_client_open("Shairport Sync", JackNoStartServer, &status);
- if (client) {
- jack_set_process_callback(client, jack_stream_write_cb, 0);
- left_port = jack_port_register(client, "Left", JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0);
- right_port = jack_port_register(client, "Right", JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0);
- sample_rate = jack_get_sample_rate(client);
- debug(1, "jackaudio sample rate = %" PRId32 ".", sample_rate);
- if (jack_activate(client)) {
- debug(1, "jackaudio cannot activate client");
- } else {
- client_is_open = 1;
- debug(1, "jackaudio client opened.");
- }
- }
- }
-
- if (client_is_open == 0)
- debug(1,"cannot open a jack client for a play session");
+
+ // see if the client is running. If not, try to open and initialise it
+
+ if (jack_client_open_if_needed() == 0)
+ debug(1, "cannot open a jack client for a play session");
}
int jack_delay(long *the_delay) {
- int64_t time_now = get_absolute_time_in_fp();
- int64_t delta = time_now - time_of_latest_latency_range;
+ // without the mutex, we could get the time of what is the last transfer of data to a jack buffer,
+ // but then a transfer could occur and we would get the buffer occupancy after another transfer had occurred
+ // so we could "lose" a full transfer (e.g. 1024 frames @ 44,100 fps ~ 23.2 milliseconds)
+ pthread_mutex_lock(&buffer_mutex);
+ int64_t time_now = get_absolute_time_in_fp();
+ int64_t delta = time_now - time_of_latest_transfer; // this is the time back to the last time data was transferred into a jack buffer
+ size_t audio_occupancy_now = audio_occupancy; // this is the buffer occupancy before any subsequent transfer because transfer is blocked by the mutex
+ pthread_mutex_unlock(&buffer_mutex);
+
int64_t frames_processed_since_latest_latency_check = (delta * 44100) >> 32;
-
+
// debug(1,"delta: %" PRId64 " frames.",frames_processed_since_latest_latency_check);
-
- *the_delay = latest_latency_range.min + audio_occupancy - frames_processed_since_latest_latency_check;
+ jack_nframes_t base_latency = (latest_left_latency_range.min + latest_left_latency_range.max)/2;
+ if (base_latency == 0)
+ base_latency = (latest_right_latency_range.min + latest_right_latency_range.max)/2;
+ *the_delay =
+ base_latency + audio_occupancy_now - frames_processed_since_latest_latency_check;
// debug(1,"reporting a delay of %d frames",*the_delay);
return 0;
}
+
void jack_flush() {
- debug(1,"jack flush");
+ // debug(1,"jack flush");
// lock
pthread_mutex_lock(&buffer_mutex);
audio_toq = audio_eoq = audio_lmb;
// unlock
}
-void jack_stop(void) {
- debug(1,"jack stop");
-}
\ No newline at end of file
+void jack_stop(void) { debug(1, "jack stop"); }
\ No newline at end of file