diff --git a/src/a2dp-aac.c b/src/a2dp-aac.c index 2aed30cca..78e707a80 100644 --- a/src/a2dp-aac.c +++ b/src/a2dp-aac.c @@ -179,7 +179,7 @@ void *a2dp_aac_enc_thread(struct ba_transport_pcm *t_pcm) { struct io_poll io = { .timeout = -1 }; HANDLE_AACENCODER handle; - AACENC_InfoStruct aacinf; + AACENC_InfoStruct info; AACENC_ERROR err; const a2dp_aac_t *configuration = &t->a2dp.configuration.aac; @@ -293,7 +293,7 @@ void *a2dp_aac_enc_thread(struct ba_transport_pcm *t_pcm) { error("Couldn't initialize AAC encoder: %s", aacenc_strerror(err)); goto fail_init; } - if ((err = aacEncInfo(handle, &aacinf)) != AACENC_OK) { + if ((err = aacEncInfo(handle, &info)) != AACENC_OK) { error("Couldn't get encoder info: %s", aacenc_strerror(err)); goto fail_init; } @@ -303,14 +303,17 @@ void *a2dp_aac_enc_thread(struct ba_transport_pcm *t_pcm) { pthread_cleanup_push(PTHREAD_CLEANUP(ffb_free), &bt); pthread_cleanup_push(PTHREAD_CLEANUP(ffb_free), &pcm); - const unsigned int aac_frame_size = aacinf.inputChannels * aacinf.frameLength; + const unsigned int aac_frame_size = info.inputChannels * info.frameLength; const size_t sample_size = BA_TRANSPORT_PCM_FORMAT_BYTES(t_pcm->format); if (ffb_init(&pcm, aac_frame_size, sample_size) == -1 || - ffb_init_uint8_t(&bt, RTP_HEADER_LEN + aacinf.maxOutBufBytes) == -1) { + ffb_init_uint8_t(&bt, RTP_HEADER_LEN + info.maxOutBufBytes) == -1) { error("Couldn't create data buffers: %s", strerror(errno)); goto fail_ffb; } + /* Get the delay introduced by the encoder. */ + t_pcm->codec_delay_dms = info.nDelay * 10000 / rate; + rtp_header_t *rtp_header; /* initialize RTP header and get anchor for payload */ uint8_t *rtp_payload = rtp_a2dp_init(bt.data, &rtp_header, NULL, 0); @@ -322,7 +325,7 @@ void *a2dp_aac_enc_thread(struct ba_transport_pcm *t_pcm) { int in_bufferIdentifiers[] = { IN_AUDIO_DATA }; int out_bufferIdentifiers[] = { OUT_BITSTREAM_DATA }; int in_bufSizes[] = { pcm.nmemb * pcm.size }; - int out_bufSizes[] = { aacinf.maxOutBufBytes }; + int out_bufSizes[] = { info.maxOutBufBytes }; int in_bufElSizes[] = { pcm.size }; int out_bufElSizes[] = { bt.size }; @@ -363,7 +366,7 @@ void *a2dp_aac_enc_thread(struct ba_transport_pcm *t_pcm) { continue; } - while ((in_args.numInSamples = ffb_len_out(&pcm)) > (int)aacinf.inputChannels) { + while ((in_args.numInSamples = ffb_len_out(&pcm)) > (int)info.inputChannels) { if ((err = aacEncEncode(handle, &in_buf, &out_buf, &in_args, &out_args)) != AACENC_OK) error("AAC encoding error: %s", aacenc_strerror(err)); @@ -409,14 +412,14 @@ void *a2dp_aac_enc_thread(struct ba_transport_pcm *t_pcm) { } - unsigned int pcm_frames = out_args.numInSamples / aacinf.inputChannels; + unsigned int pcm_frames = out_args.numInSamples / info.inputChannels; /* keep data transfer at a constant bit rate */ asrsync_sync(&io.asrs, pcm_frames); /* move forward RTP timestamp clock */ rtp_state_update(&rtp, pcm_frames); /* update busy delay (encoding overhead) */ - t_pcm->delay = asrsync_get_busy_usec(&io.asrs) / 100; + t_pcm->processing_delay_dms = asrsync_get_busy_usec(&io.asrs) / 100; /* If the input buffer was not consumed, we have to append new data to * the existing one. Since we do not use ring buffer, we will simply @@ -553,26 +556,29 @@ void *a2dp_aac_dec_thread(struct ba_transport_pcm *t_pcm) { unsigned int data_len = ffb_len_out(&latm); unsigned int valid = ffb_len_out(&latm); - CStreamInfo *aacinf; + CStreamInfo *info; if ((err = aacDecoder_Fill(handle, (uint8_t **)&latm.data, &data_len, &valid)) != AAC_DEC_OK) error("AAC buffer fill error: %s", aacdec_strerror(err)); else if ((err = aacDecoder_DecodeFrame(handle, pcm.tail, ffb_blen_in(&pcm), 0)) != AAC_DEC_OK) error("AAC decode frame error: %s", aacdec_strerror(err)); - else if ((aacinf = aacDecoder_GetStreamInfo(handle)) == NULL) + else if ((info = aacDecoder_GetStreamInfo(handle)) == NULL) error("Couldn't get AAC stream info"); else { - if ((unsigned int)aacinf->numChannels != channels) - warn("AAC channels mismatch: %u != %u", aacinf->numChannels, channels); + if ((unsigned int)info->numChannels != channels) + warn("AAC channels mismatch: %u != %u", info->numChannels, channels); - const size_t samples = (size_t)aacinf->frameSize * channels; + const size_t samples = (size_t)info->frameSize * channels; io_pcm_scale(t_pcm, pcm.data, samples); if (io_pcm_write(t_pcm, pcm.data, samples) == -1) error("PCM write error: %s", strerror(errno)); + /* Update the delay introduced by the decoder. */ + t_pcm->codec_delay_dms = info->outputDelay * 10000 / rate; + /* update local state with decoded PCM frames */ - rtp_state_update(&rtp, aacinf->frameSize); + rtp_state_update(&rtp, info->frameSize); } diff --git a/src/a2dp-aptx-hd.c b/src/a2dp-aptx-hd.c index 56c43c8cd..99115963d 100644 --- a/src/a2dp-aptx-hd.c +++ b/src/a2dp-aptx-hd.c @@ -220,7 +220,7 @@ void *a2dp_aptx_hd_enc_thread(struct ba_transport_pcm *t_pcm) { rtp.ts_pcm_frames += pcm_frames; /* update busy delay (encoding overhead) */ - t_pcm->delay = asrsync_get_busy_usec(&io.asrs) / 100; + t_pcm->processing_delay_dms = asrsync_get_busy_usec(&io.asrs) / 100; /* reinitialize output buffer */ ffb_rewind(&bt); diff --git a/src/a2dp-aptx.c b/src/a2dp-aptx.c index f02572908..9a100d213 100644 --- a/src/a2dp-aptx.c +++ b/src/a2dp-aptx.c @@ -201,7 +201,7 @@ void *a2dp_aptx_enc_thread(struct ba_transport_pcm *t_pcm) { asrsync_sync(&io.asrs, pcm_samples / channels); /* update busy delay (encoding overhead) */ - t_pcm->delay = asrsync_get_busy_usec(&io.asrs) / 100; + t_pcm->processing_delay_dms = asrsync_get_busy_usec(&io.asrs) / 100; /* reinitialize output buffer */ ffb_rewind(&bt); diff --git a/src/a2dp-faststream.c b/src/a2dp-faststream.c index 1234ec67e..5d089907c 100644 --- a/src/a2dp-faststream.c +++ b/src/a2dp-faststream.c @@ -150,9 +150,10 @@ void *a2dp_fs_enc_thread(struct ba_transport_pcm *t_pcm) { pthread_cleanup_push(PTHREAD_CLEANUP(ffb_free), &pcm); pthread_cleanup_push(PTHREAD_CLEANUP(sbc_finish), &sbc); - const unsigned int channels = t_pcm->channels; const size_t sbc_frame_len = sbc_get_frame_length(&sbc); const size_t sbc_frame_samples = sbc_get_codesize(&sbc) / sizeof(int16_t); + const unsigned int channels = t_pcm->channels; + const unsigned int rate = t_pcm->rate; if (ffb_init_int16_t(&pcm, sbc_frame_samples * 3) == -1 || ffb_init_uint8_t(&bt, t->mtu_write) == -1) { @@ -160,6 +161,10 @@ void *a2dp_fs_enc_thread(struct ba_transport_pcm *t_pcm) { goto fail_ffb; } + const unsigned int sbc_delay_frames = 73; + /* Get the total delay introduced by the codec. */ + t_pcm->codec_delay_dms = sbc_delay_frames * 10000 / rate; + debug_transport_pcm_thread_loop(t_pcm, "START"); for (ba_transport_pcm_state_set_running(t_pcm);;) { @@ -223,7 +228,7 @@ void *a2dp_fs_enc_thread(struct ba_transport_pcm *t_pcm) { asrsync_sync(&io.asrs, pcm_frames); /* update busy delay (encoding overhead) */ - t_pcm->delay = asrsync_get_busy_usec(&io.asrs) / 100; + t_pcm->processing_delay_dms = asrsync_get_busy_usec(&io.asrs) / 100; /* If the input buffer was not consumed (due to codesize limit), we * have to append new data to the existing one. Since we do not use diff --git a/src/a2dp-lc3plus.c b/src/a2dp-lc3plus.c index b402c2edd..58fa251fe 100644 --- a/src/a2dp-lc3plus.c +++ b/src/a2dp-lc3plus.c @@ -255,6 +255,13 @@ void *a2dp_lc3plus_enc_thread(struct ba_transport_pcm *t_pcm) { goto fail_ffb; } + /* Get the total delay introduced by the codec. The LC3plus library + * reports total codec delay in case of both encoder and decoder API. + * In order not to overestimate the delay, we are not going to report + * delay in the decoder thread. */ + const int lc3plus_delay_frames = lc3plus_enc_get_delay(handle); + t_pcm->codec_delay_dms = lc3plus_delay_frames * 10000 / rate; + rtp_header_t *rtp_header; rtp_media_header_t *rtp_media_header; /* initialize RTP headers and get anchor for payload */ @@ -377,7 +384,7 @@ void *a2dp_lc3plus_enc_thread(struct ba_transport_pcm *t_pcm) { rtp_state_update(&rtp, pcm_frames); /* update busy delay (encoding overhead) */ - t_pcm->delay = asrsync_get_busy_usec(&io.asrs) / 100; + t_pcm->processing_delay_dms = asrsync_get_busy_usec(&io.asrs) / 100; /* If the input buffer was not consumed (due to codesize limit), we * have to append new data to the existing one. Since we do not use diff --git a/src/a2dp-ldac.c b/src/a2dp-ldac.c index 0c450fefa..9a4446b5e 100644 --- a/src/a2dp-ldac.c +++ b/src/a2dp-ldac.c @@ -174,6 +174,10 @@ void *a2dp_ldac_enc_thread(struct ba_transport_pcm *t_pcm) { goto fail_ffb; } + const unsigned int ldac_delay_frames = 128; + /* Get the total delay introduced by the codec. */ + t_pcm->codec_delay_dms = ldac_delay_frames * 10000 / rate; + rtp_header_t *rtp_header; rtp_media_header_t *rtp_media_header; /* initialize RTP headers and get anchor for payload */ @@ -266,7 +270,7 @@ void *a2dp_ldac_enc_thread(struct ba_transport_pcm *t_pcm) { rtp_state_update(&rtp, pcm_frames); /* update busy delay (encoding overhead) */ - t_pcm->delay = asrsync_get_busy_usec(&io.asrs) / 100; + t_pcm->processing_delay_dms = asrsync_get_busy_usec(&io.asrs) / 100; } diff --git a/src/a2dp-mpeg.c b/src/a2dp-mpeg.c index 7a9bfa957..fd0266b38 100644 --- a/src/a2dp-mpeg.c +++ b/src/a2dp-mpeg.c @@ -234,6 +234,10 @@ void *a2dp_mp3_enc_thread(struct ba_transport_pcm *t_pcm) { goto fail_ffb; } + /* Get the total delay introduced by the codec. */ + const int mpeg_delay_frames = lame_get_encoder_delay(handle); + t_pcm->codec_delay_dms = mpeg_delay_frames * 10000 / rate; + rtp_header_t *rtp_header; rtp_mpeg_audio_header_t *rtp_mpeg_audio_header; /* initialize RTP headers and get anchor for payload */ @@ -319,7 +323,7 @@ void *a2dp_mp3_enc_thread(struct ba_transport_pcm *t_pcm) { rtp_state_update(&rtp, pcm_frames); /* update busy delay (encoding overhead) */ - t_pcm->delay = asrsync_get_busy_usec(&io.asrs) / 100; + t_pcm->processing_delay_dms = asrsync_get_busy_usec(&io.asrs) / 100; /* If the input buffer was not consumed (due to frame alignment), we * have to append new data to the existing one. Since we do not use diff --git a/src/a2dp-opus.c b/src/a2dp-opus.c index 32c2933ec..4631bcb8b 100644 --- a/src/a2dp-opus.c +++ b/src/a2dp-opus.c @@ -174,6 +174,11 @@ void *a2dp_opus_enc_thread(struct ba_transport_pcm *t_pcm) { goto fail_ffb; } + int32_t opus_delay_frames = 0; + /* Get the delay introduced by the encoder. */ + opus_encoder_ctl(opus, OPUS_GET_LOOKAHEAD(&opus_delay_frames)); + t_pcm->codec_delay_dms = opus_delay_frames * 10000 / rate; + rtp_header_t *rtp_header; rtp_media_header_t *rtp_media_header; /* initialize RTP headers and get anchor for payload */ @@ -237,7 +242,7 @@ void *a2dp_opus_enc_thread(struct ba_transport_pcm *t_pcm) { rtp_state_update(&rtp, opus_frame_pcm_frames); /* update busy delay (encoding overhead) */ - t_pcm->delay = asrsync_get_busy_usec(&io.asrs) / 100; + t_pcm->processing_delay_dms = asrsync_get_busy_usec(&io.asrs) / 100; /* If the input buffer was not consumed (due to encoder frame * constraint), we have to append new data to the existing one. @@ -300,6 +305,11 @@ void *a2dp_opus_dec_thread(struct ba_transport_pcm *t_pcm) { goto fail_ffb; } + int32_t opus_delay_frames = 0; + /* Get the delay introduced by the decoder. */ + opus_decoder_ctl(opus, OPUS_GET_LOOKAHEAD(&opus_delay_frames)); + t_pcm->codec_delay_dms = opus_delay_frames * 10000 / rate; + struct rtp_state rtp = { .synced = false }; /* RTP clock frequency equal to PCM sample rate */ rtp_state_init(&rtp, rate, rate); diff --git a/src/a2dp-sbc.c b/src/a2dp-sbc.c index 1ffd27585..696ac1378 100644 --- a/src/a2dp-sbc.c +++ b/src/a2dp-sbc.c @@ -185,6 +185,10 @@ void *a2dp_sbc_enc_thread(struct ba_transport_pcm *t_pcm) { goto fail_ffb; } + const unsigned int sbc_delay_frames = 73; + /* Get the total delay introduced by the codec. */ + t_pcm->codec_delay_dms = sbc_delay_frames * 10000 / rate; + rtp_header_t *rtp_header; rtp_media_header_t *rtp_media_header; @@ -269,7 +273,7 @@ void *a2dp_sbc_enc_thread(struct ba_transport_pcm *t_pcm) { rtp_state_update(&rtp, pcm_frames); /* update busy delay (encoding overhead) */ - t_pcm->delay = asrsync_get_busy_usec(&io.asrs) / 100; + t_pcm->processing_delay_dms = asrsync_get_busy_usec(&io.asrs) / 100; /* If the input buffer was not consumed (due to codesize limit), we * have to append new data to the existing one. Since we do not use diff --git a/src/ba-transport-pcm.c b/src/ba-transport-pcm.c index adbb944d2..af9be01c7 100644 --- a/src/ba-transport-pcm.c +++ b/src/ba-transport-pcm.c @@ -724,7 +724,9 @@ int ba_transport_pcm_get_hardware_volume( int ba_transport_pcm_get_delay(const struct ba_transport_pcm *pcm) { const struct ba_transport *t = pcm->t; - int delay = pcm->delay + ba_transport_pcm_delay_adjustment_get(pcm); + + int delay = pcm->codec_delay_dms + pcm->processing_delay_dms; + delay += ba_transport_pcm_delay_adjustment_get(pcm); if (t->profile & BA_TRANSPORT_PROFILE_MASK_A2DP) delay += t->a2dp.delay; diff --git a/src/ba-transport-pcm.h b/src/ba-transport-pcm.h index 40d7a0c2d..adc4836d6 100644 --- a/src/ba-transport-pcm.h +++ b/src/ba-transport-pcm.h @@ -122,9 +122,13 @@ struct ba_transport_pcm { /* PCM sample rate */ unsigned int rate; - /* Overall PCM delay in 1/10 of millisecond, caused by - * audio encoding or decoding and data transfer. */ - unsigned int delay; + /* Delay caused by the codec due to internal buffering. The delay is + * expressed in 1/10 of millisecond. */ + unsigned int codec_delay_dms; + /* Delay caused by data processing. This delay component depends on + * the host computational power. It is used to compensate for the time + * required to encode or decode audio. */ + unsigned int processing_delay_dms; /* guard delay adjustments access */ pthread_mutex_t delay_adjustments_mtx; diff --git a/src/codec-lc3-swb.c b/src/codec-lc3-swb.c index ba59d843b..01eba66d4 100644 --- a/src/codec-lc3-swb.c +++ b/src/codec-lc3-swb.c @@ -43,6 +43,13 @@ void lc3_swb_init(struct esco_lc3_swb *lc3_swb) { } +/** + * Get LC3-SWB delay in number of samples. */ +ssize_t lc3_swb_get_delay(struct esco_lc3_swb *lc3_swb) { + (void)lc3_swb; + return lc3_delay_samples(7500, 32000); +} + /** * Encode single eSCO LC3-SWB frame. */ ssize_t lc3_swb_encode(struct esco_lc3_swb *lc3_swb) { diff --git a/src/codec-lc3-swb.h b/src/codec-lc3-swb.h index 30da8732e..1658fdf53 100644 --- a/src/codec-lc3-swb.h +++ b/src/codec-lc3-swb.h @@ -74,6 +74,7 @@ struct esco_lc3_swb { void lc3_swb_init(struct esco_lc3_swb *lc3_swb); +ssize_t lc3_swb_get_delay(struct esco_lc3_swb *lc3_swb); ssize_t lc3_swb_encode(struct esco_lc3_swb *lc3_swb); ssize_t lc3_swb_decode(struct esco_lc3_swb *lc3_swb); diff --git a/src/sco-cvsd.c b/src/sco-cvsd.c index a61d24b1f..dea164c16 100644 --- a/src/sco-cvsd.c +++ b/src/sco-cvsd.c @@ -79,7 +79,7 @@ void *sco_cvsd_enc_thread(struct ba_transport_pcm *t_pcm) { /* keep data transfer at a constant bit rate */ asrsync_sync(&io.asrs, mtu_samples); /* update busy delay (encoding overhead) */ - t_pcm->delay = asrsync_get_busy_usec(&io.asrs) / 100; + t_pcm->processing_delay_dms = asrsync_get_busy_usec(&io.asrs) / 100; } diff --git a/src/sco-lc3-swb.c b/src/sco-lc3-swb.c index 507f6ef29..b9efb590f 100644 --- a/src/sco-lc3-swb.c +++ b/src/sco-lc3-swb.c @@ -41,6 +41,10 @@ void *sco_lc3_swb_enc_thread(struct ba_transport_pcm *t_pcm) { struct esco_lc3_swb codec; lc3_swb_init(&codec); + /* Get the total delay introduced by the codec. */ + const ssize_t lc3_swb_delay_frames = lc3_swb_get_delay(&codec); + t_pcm->codec_delay_dms = lc3_swb_delay_frames * 10000 / t_pcm->rate; + debug_transport_pcm_thread_loop(t_pcm, "START"); for (ba_transport_pcm_state_set_running(t_pcm);;) { @@ -81,7 +85,7 @@ void *sco_lc3_swb_enc_thread(struct ba_transport_pcm *t_pcm) { /* keep data transfer at a constant bit rate */ asrsync_sync(&io.asrs, codec.frames * LC3_SWB_CODESAMPLES); /* update busy delay (encoding overhead) */ - t_pcm->delay = asrsync_get_busy_usec(&io.asrs) / 100; + t_pcm->processing_delay_dms = asrsync_get_busy_usec(&io.asrs) / 100; /* Move unprocessed data to the front of our linear * buffer and clear the LC3-SWB frame counter. */ diff --git a/src/sco-msbc.c b/src/sco-msbc.c index c83eb8045..3c9ceaae7 100644 --- a/src/sco-msbc.c +++ b/src/sco-msbc.c @@ -43,6 +43,10 @@ void *sco_msbc_enc_thread(struct ba_transport_pcm *t_pcm) { goto fail_msbc; } + const unsigned int sbc_delay_frames = 73; + /* Get the total delay introduced by the codec. */ + t_pcm->codec_delay_dms = sbc_delay_frames * 10000 / t_pcm->rate; + debug_transport_pcm_thread_loop(t_pcm, "START"); for (ba_transport_pcm_state_set_running(t_pcm);;) { @@ -88,7 +92,7 @@ void *sco_msbc_enc_thread(struct ba_transport_pcm *t_pcm) { /* keep data transfer at a constant bit rate */ asrsync_sync(&io.asrs, msbc.frames * MSBC_CODESAMPLES); /* update busy delay (encoding overhead) */ - t_pcm->delay = asrsync_get_busy_usec(&io.asrs) / 100; + t_pcm->processing_delay_dms = asrsync_get_busy_usec(&io.asrs) / 100; /* Move unprocessed data to the front of our linear * buffer and clear the mSBC frame counter. */ diff --git a/test/sndalign.c b/test/sndalign.c index 2910cbf27..692d0de15 100644 --- a/test/sndalign.c +++ b/test/sndalign.c @@ -9,23 +9,31 @@ */ #include +#include #include #include #include #include +static bool is_silence(const short *src, unsigned int channels, size_t frames) { + for (size_t i = 0; i < channels * frames; i++) + if (src[i] != 0) + return false; + return true; +} + int main(int argc, char *argv[]) { if (argc != 3) { - fprintf(stderr, "Usage: %s \n", argv[0]); - return 1; + printf("Usage: %s \n", argv[0]); + return EXIT_FAILURE; } SNDFILE *sf1; SF_INFO sf1_info = { 0 }; if ((sf1 = sf_open(argv[1], SFM_READ, &sf1_info)) == NULL) { - fprintf(stderr, "Couldn't open audio file: %s: %s\n", argv[1], sf_strerror(NULL)); + fprintf(stderr, "ERR: Couldn't open audio file: %s: %s\n", argv[1], sf_strerror(NULL)); return EXIT_FAILURE; } @@ -37,7 +45,7 @@ int main(int argc, char *argv[]) { SNDFILE *sf2; SF_INFO sf2_info = { 0 }; if ((sf2 = sf_open(argv[2], SFM_READ, &sf2_info)) == NULL) { - fprintf(stderr, "Couldn't open audio file: %s: %s\n", argv[2], sf_strerror(NULL)); + fprintf(stderr, "ERR: Couldn't open audio file: %s: %s\n", argv[2], sf_strerror(NULL)); return EXIT_FAILURE; } @@ -47,30 +55,41 @@ int main(int argc, char *argv[]) { printf(" Channels: %d\n", sf2_info.channels); if (sf1_info.channels != sf2_info.channels) { - fprintf(stderr, "Channels mismatch: %d != %d\n", sf1_info.channels, sf2_info.channels); + fprintf(stderr, "ERR: Channels mismatch: %d != %d\n", sf1_info.channels, sf2_info.channels); return EXIT_FAILURE; } if (sf1_info.samplerate != sf2_info.samplerate) { - fprintf(stderr, "Sample rate mismatch: %d != %d\n", sf1_info.samplerate, sf2_info.samplerate); + fprintf(stderr, "ERR: Sample rate mismatch: %d != %d\n", sf1_info.samplerate, sf2_info.samplerate); return EXIT_FAILURE; } short *sf1_data = malloc(sf1_info.frames * sf1_info.channels * sizeof(short)); if (sf_readf_short(sf1, sf1_data, sf1_info.frames) != sf1_info.frames) { - fprintf(stderr, "Couldn't read audio data: %s\n", sf_strerror(sf1)); + fprintf(stderr, "ERR: Couldn't read audio data: %s\n", sf_strerror(sf1)); + return EXIT_FAILURE; + } + + if (is_silence(sf1_data, sf1_info.channels, sf1_info.frames)) { + fprintf(stderr, "ERR: Source 1 is all silence\n"); return EXIT_FAILURE; } short *sf2_data = malloc(sf2_info.frames * sf2_info.channels * sizeof(short)); if (sf_readf_short(sf2, sf2_data, sf2_info.frames) != sf2_info.frames) { - fprintf(stderr, "Couldn't read audio data: %s\n", sf_strerror(sf2)); + fprintf(stderr, "ERR: Couldn't read audio data: %s\n", sf_strerror(sf2)); + return EXIT_FAILURE; + } + + if (is_silence(sf2_data, sf2_info.channels, sf2_info.frames)) { + fprintf(stderr, "ERR: Source 2 is all silence\n"); return EXIT_FAILURE; } /* Calculate cross-correlation between two audio streams by applying * different offsets while keeping the defined minimal overlap. */ + const size_t channels = sf1_info.channels; const size_t sf1_frames = sf1_info.frames; const size_t sf2_frames = sf2_info.frames; const size_t cross_correlation_frames = sf1_frames + sf2_frames; @@ -88,14 +107,17 @@ int main(int argc, char *argv[]) { break; for (size_t j = 0; j < overlap; j++) - cross_correlation[i] += sf1_data[sf1_begin + j] * sf2_data[sf2_begin + j]; + for (size_t k = 0; k < channels; k++) + cross_correlation[i] += + sf1_data[(sf1_begin + j) * channels + k] * + sf2_data[(sf2_begin + j) * channels + k]; } ssize_t max_i = 0; long long max_v = cross_correlation[0]; /* Find the maximum value in the cross-correlation array. */ - for (size_t i = 1; i < cross_correlation_frames; i++) + for (size_t i = min_overlap; i < cross_correlation_frames; i++) if (cross_correlation[i] > max_v) max_v = cross_correlation[max_i = i]; @@ -107,5 +129,5 @@ int main(int argc, char *argv[]) { free(sf1_data); free(sf2_data); - return 0; + return EXIT_SUCCESS; } diff --git a/test/test-io.c b/test/test-io.c index 8c131ace5..158b6738b 100644 --- a/test/test-io.c +++ b/test/test-io.c @@ -394,7 +394,7 @@ static void pcm_write_frames(struct ba_transport_pcm *pcm, size_t frames) { if (dump_data) { #if HAVE_SNDFILE char fname[128]; - sprintf(fname, "sample-sine-%s.wav", transport_pcm_to_fname(pcm)); + sprintf(fname, "sine-%s.wav", transport_pcm_to_fname(pcm)); ck_assert_ptr_nonnull(sf = sf_open_format(fname, pcm->rate, pcm->channels, pcm->format)); #else error("Dumping audio files requires sndfile library!");