IFT_frame_right_sample,
} ift_type;
+// Two-channel, 16bit audio:
+static const int bytes_per_frame = 4;
// Four seconds buffer -- should be plenty
-#define buffer_size 44100 * 4 * 2 * 2
+#define buffer_size 44100 * 4 * bytes_per_frame
static pthread_mutex_t buffer_mutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_mutex_t client_mutex = PTHREAD_MUTEX_INITIALIZER;
+/*
char *audio_lmb, *audio_umb, *audio_toq, *audio_eoq;
+
size_t audio_occupancy; // this is in frames, not bytes. A frame is a left and
// right sample, each 16 bits, hence 4 bytes
+*/
pthread_t *open_client_if_necessary_thread = NULL;
int jack_init(int, char **);
jack_nframes_t jack_latency;
static jack_ringbuffer_t *jackbuf;
+static int flush_please = 0;
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);
// copy the samples into the queue
- size_t bytes_to_transfer = samples * 2 * 2;
+ size_t bytes_to_transfer, bytes_transferred;
+ bytes_to_transfer = samples * bytes_per_frame;
+ pthread_mutex_lock(&buffer_mutex); // it's ok to lock here since we're not in the realtime callback
+ bytes_transferred = jack_ringbuffer_write(jackbuf, buf, bytes_to_transfer);
+ pthread_mutex_unlock(&buffer_mutex);
+ if (bytes_transferred < bytes_to_transfer) {
+ debug(1, "JACK ringbuffer overrun. Only wrote %d of %d bytes.", bytes_transferred, bytes_to_transfer);
+ }
+/*
size_t space_to_end_of_buffer = audio_umb - audio_eoq;
if (space_to_end_of_buffer >= bytes_to_transfer) {
memcpy(audio_eoq, buf, bytes_to_transfer);
audio_eoq = audio_lmb + bytes_to_transfer - space_to_end_of_buffer;
pthread_mutex_unlock(&buffer_mutex);
}
+*/
return 0;
}
}
}
+static inline jack_default_audio_sample_t sample_conv(short sample) {
+ return ((sample < 0) ? (-1.0 * sample / SHRT_MIN) : (1.0 * sample / SHRT_MAX));
+}
+
+void deinterleave_and_convert(const char *interleaved_frames,
+ jack_default_audio_sample_t * const jack_buffer_left,
+ jack_default_audio_sample_t * const jack_buffer_right,
+ jack_nframes_t nframes) {
+ jack_nframes_t i;
+ short *ifp = (short *)interleaved_frames; // we're dealing with 16bit audio here
+ jack_default_audio_sample_t *fpl = jack_buffer_left;
+ jack_default_audio_sample_t *fpr = jack_buffer_right;
+ for (i=0; i<nframes; i++) {
+ fpl[i] = sample_conv(*ifp++);
+ fpr[i] = sample_conv(*ifp++);
+ }
+}
+
int jack_stream_write_cb(jack_nframes_t nframes, __attribute__((unused)) void *arg) {
jack_default_audio_sample_t *left_buffer =
jack_default_audio_sample_t *right_buffer =
(jack_default_audio_sample_t *)jack_port_get_buffer(right_port, nframes);
+ jack_ringbuffer_data_t v[2] = { 0 };
+ jack_nframes_t i, thisbuf;
+ int frames_written = 0;
+ int frames_required = 0;
+
+ jack_ringbuffer_get_read_vector(jackbuf, v); // an array of two elements because of possible ringbuffer wrap-around
+ for (i=0; i<2; i++) {
+ thisbuf = v[i].len / bytes_per_frame;
+ if (thisbuf > nframes) {
+ frames_required = nframes;
+ } else {
+ frames_required = thisbuf;
+ }
+ deinterleave_and_convert(v[i].buf, &left_buffer[frames_written], &right_buffer[frames_written], frames_required);
+ frames_written = frames_required;
+ nframes -= frames_written;
+ }
+ jack_ringbuffer_read_advance(jackbuf, frames_written * bytes_per_frame);
+
+ // debug(1,"transferring %u frames",written);
+ // Why are we doing this? The port latency should never change unless the JACK graph is reordered. Should happen on a graph reorder callback only.
+ // jack_port_get_latency_range(left_port, JackPlaybackLatency, &latest_left_latency_range);
+ // jack_port_get_latency_range(right_port, JackPlaybackLatency, &latest_right_latency_range);
+
+ // now, if there are any more frames to put into the buffer, fill them with
+ // silence
+ while (nframes > 0) {
+ left_buffer[frames_written] = 0.0;
+ right_buffer[frames_written] = 0.0;
+ frames_written++;
+ nframes--;
+ }
+ time_of_latest_transfer = get_absolute_time_in_fp(); // this is the only writer, no locking necessary.
+
+/*
size_t frames_we_can_transfer = nframes;
// lock
pthread_mutex_lock(&buffer_mutex);
IFT_frame_right_sample);
audio_toq = audio_lmb + (frames_we_can_transfer - first_portion_to_write) * 2 * 2;
}
- // 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);
pthread_mutex_unlock(&buffer_mutex);
// unlock
- // now, if there are any more frames to put into the buffer, fill them with
- // silence
- jack_nframes_t i;
- for (i = frames_we_can_transfer; i < nframes; i++) {
- left_buffer[i] = 0.0;
- right_buffer[i] = 0.0;
- }
+*/
return 0;
}
+// FIXME: set_graph_order_callback(), recompute latencies here!
+
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); }
b_latency = (right_latency_range.min + right_latency_range.max) / 2;
// jack_latency = b_latency; // actually, we are not interested in the latency of the jack
// devices connected...
+ // FIXME: yes, we are.
jack_latency = 0;
debug(1, "playback latency callback: %" PRIu32 ".", jack_latency);
}
jack_set_error_function(default_jack_error_callback);
jack_set_info_function(default_jack_info_callback);
+/*
// allocate space for the audio buffer
audio_lmb = malloc(buffer_size);
if (audio_lmb == NULL)
audio_toq = audio_eoq = audio_lmb;
audio_umb = audio_lmb + buffer_size;
audio_occupancy = 0; // frames
+*/
client_is_open = 0;
// 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
+ int64_t time_now = get_absolute_time_in_fp();
+ // this is the time back to the last time data
+ // was transferred into a jack buffer
+ int64_t delta = time_now - time_of_latest_transfer;
+ // this is the buffer occupancy before any
+ // subsequent transfer because transfer is blocked
+ // by the mutex
+ size_t audio_occupancy_now = jack_ringbuffer_read_space(jackbuf);
pthread_mutex_unlock(&buffer_mutex);
int64_t frames_processed_since_latest_latency_check = (delta * 44100) >> 32;
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, "It is not possible to safely flush a lock-free ringbuffer. Asking the process callback to do it...");
+ flush_please = 1;
+/*
// debug(1,"jack flush");
- pthread_mutex_lock(&buffer_mutex);
audio_toq = audio_eoq = audio_lmb;
audio_umb = audio_lmb + buffer_size;
audio_occupancy = 0; // frames
- pthread_mutex_unlock(&buffer_mutex);
+*/
}
void jack_stop(void) {