}
*/
- debug(1,"alsa: precision_delay_available starting check...");
+// this will return true if the DAC can return precision delay information and false if not
+// if it is not yet known, it will test the output device to find out
+
++// note -- once it has done the test, it decides -- even if the delay comes back with
++// "don't know", it will take that as a "No" and remember it.
++// If you want it to check again, set precision_delay_available_status to YNDK_DONT_KNOW
++// first.
++
++
+int precision_delay_available() {
+ if (precision_delay_available_status == YNDK_DONT_KNOW) {
- debug(1,"alsa: precision_delay_available asking for delay and status with a return status of %d, a delay of %ld and a uses_update_timestamps of %d.", ret, delay, uses_update_timestamps);
+ // At present, the only criterion as to whether precision delay is available
+ // is whether the device driver returns non-zero update timestamps
+ // If it does, it is considered precision delay is available
+ // Otherwise, it's considered to be unavailable
+
+ // To test, we play a silence buffer (fairly large to avoid underflow)
+ // and then we check the delay return. It will tell us if it
+ // was able to use the (non-zero) update timestamps
+
+ int frames_of_silence = 4410;
+ size_t size_of_silence_buffer = frames_of_silence * frame_size;
+ void *silence = malloc(size_of_silence_buffer);
+ if (silence == NULL) {
+ debug(1, "alsa: precision_delay_available -- failed to "
+ "allocate memory for a "
+ "silent frame buffer.");
+ } else {
+ pthread_cleanup_push(malloc_cleanup, silence);
+ int use_dither = 0;
+ if ((hardware_mixer == 0) && (config.ignore_volume_control == 0) &&
+ (config.airplay_volume != 0.0))
+ use_dither = 1;
+ dither_random_number_store =
+ generate_zero_frames(silence, frames_of_silence, config.output_format,
+ use_dither, // i.e. with dither
+ dither_random_number_store);
+ // debug(1,"Play %d frames of silence with most_recent_write_time of
+ // %" PRIx64 ".",
+ // frames_of_silence,most_recent_write_time);
+ do_play(silence, frames_of_silence);
+ pthread_cleanup_pop(1);
+ // now we can get the delay, and we'll note if it uses update timestamps
+ enum yndk_type uses_update_timestamps;
+ snd_pcm_state_t state;
+ snd_pcm_sframes_t delay;
+ int ret = delay_and_status(&state, &delay, &uses_update_timestamps);
- precision_delay_available_status = uses_update_timestamps;
++ // debug(3,"alsa: precision_delay_available asking for delay and status with a return status of %d, a delay of %ld and a uses_update_timestamps of %d.", ret, delay, uses_update_timestamps);
+ if (ret == 0) {
++ if (uses_update_timestamps == YNDK_YES) {
++ precision_delay_available_status = YNDK_YES;
++ debug(2,"alsa: precision delay timing available.");
++ } else {
++ precision_delay_available_status = YNDK_NO;
++ inform("Note: the alsa output device \"%s\" is not capable of precision delay timing. Could it be a virtual device?", snd_pcm_name(alsa_handle));
++ if (config.disable_standby_mode != disable_standby_off)
++ inform("Note: disable_standby_mode has been turned off because the output device is not capable of precision delay timing.");
++ }
+ }
+ }
+ }
+ return (precision_delay_available_status == YNDK_YES);
+}
+
void *alsa_buffer_monitor_thread_code(__attribute__((unused)) void *arg) {
int okb = -1;
while (1) {
if (okb != config.keep_dac_busy) {
-- debug(1,"keep_dac_busy is now \"%s\"",config.keep_dac_busy == 0 ? "no" : "yes");
++ debug(2,"keep_dac_busy is now \"%s\"",config.keep_dac_busy == 0 ? "no" : "yes");
okb = config.keep_dac_busy;
}
if ((config.keep_dac_busy != 0) && (alsa_device_initialised == 0)) {
cp = next;
}
- debug(1, "pUncompressedCDAudio detected.");
+ if (pUncompressedCDAudio) {
++ debug(1, "An uncompressed PCM stream has been detected.");
+ conn->stream.type = ast_uncompressed;
+ conn->max_frames_per_packet = 352; // number of audio frames per packet.
+ conn->input_rate = 44100;
+ conn->input_num_channels = 2;
+ conn->input_bit_depth = 16;
+ conn->input_bytes_per_frame = conn->input_num_channels * ((conn->input_bit_depth + 7) / 8);
+
+ /*
+ int y = strlen(pAudioMediaInfo);
+ if (y > 0) {
+ char obf[4096];
+ if (y > 4096)
+ y = 4096;
+ char *p = pAudioMediaInfo;
+ char *obfp = obf;
+ int obfc;
+ for (obfc = 0; obfc < y; obfc++) {
+ snprintf(obfp, 3, "%02X", (unsigned int)*p);
+ p++;
+ obfp += 2;
+ };
+ *obfp = 0;
+ debug(1, "AudioMediaInfo: \"%s\".", obf);
+ }
+ */
+ }
+
if (pssid) {
uint32_t ssid = uatoi(pssid);
debug(3, "Synchronisation Source Identifier: %08X,%u", ssid, ssid);
memcpy(conn->stream.aeskey, aeskey, 16);
free(aeskey);
}
- unsigned int i;
- for (i = 0; i < sizeof(conn->stream.fmtp) / sizeof(conn->stream.fmtp[0]); i++)
- conn->stream.fmtp[i] = atoi(strsep(&pfmtp, " \t"));
- // here we should check the sanity ot the fmtp values
- // for (i = 0; i < sizeof(conn->stream.fmtp) / sizeof(conn->stream.fmtp[0]); i++)
- // debug(1," fmtp[%2d] is: %10d",i,conn->stream.fmtp[i]);
+
+ if (pfmtp) {
+ conn->stream.type = ast_apple_lossless;
- debug(1, "pAppleLossless detected.");
++ debug(1, "An ALAC stream has been detected.");
+ unsigned int i;
+ for (i = 0; i < sizeof(conn->stream.fmtp) / sizeof(conn->stream.fmtp[0]); i++)
+ conn->stream.fmtp[i] = atoi(strsep(&pfmtp, " \t"));
+ // here we should check the sanity ot the fmtp values
+ // for (i = 0; i < sizeof(conn->stream.fmtp) / sizeof(conn->stream.fmtp[0]); i++)
+ // debug(1," fmtp[%2d] is: %10d",i,conn->stream.fmtp[i]);
+
+ // set the parameters of the player (as distinct from the parameters of the decoder -- that's
+ // done later).
+ conn->max_frames_per_packet = conn->stream.fmtp[1]; // number of audio frames per packet.
+ conn->input_rate = conn->stream.fmtp[11];
+ conn->input_num_channels = conn->stream.fmtp[7];
+ conn->input_bit_depth = conn->stream.fmtp[3];
+ conn->input_bytes_per_frame = conn->input_num_channels * ((conn->input_bit_depth + 7) / 8);
+ }
+
+ if (conn->stream.type == ast_unknown) {
+ warn("Can not process the following ANNOUNCE message:");
+ // print each line of the request content
+ // the problem is that nextline has replace all returns, newlines, etc. by
+ // NULLs
+ char *cp = req->content;
+ int cp_left = req->contentlength;
+ while (cp_left > 1) {
+ if (strlen(cp) != 0)
+ warn(" %s", cp);
+ cp += strlen(cp) + 1;
+ cp_left -= strlen(cp) + 1;
+ }
+ goto out;
+ }
char *hdr = msg_get_header(req, "X-Apple-Client-Name");
if (hdr) {