diff --git a/doc/bluealsa-api.txt b/doc/bluealsa-api.txt index 9cdd63e12..66f6fb2e5 100644 --- a/doc/bluealsa-api.txt +++ b/doc/bluealsa-api.txt @@ -83,11 +83,12 @@ Properties object Device [readonly] Stream format identifier. The highest two bits of the 16-bit identifier determine the signedness and the - endianness, where the remaining bits are used to store - the bit-width. + endianness. Next 6 bits determine the physical width + of a sample in bytes. The lowest 8 bits are used to + store the actual sample bit-width. - Examples: 0x4010 - unsigned 16-bit big-endian - 0x8018 - signed 24-bit little-endian + Examples: 0x4210 - unsigned 16-bit 2 bytes big-endian + 0x8418 - signed 24-bit 4 bytes little-endian byte Channels [readonly] diff --git a/src/a2dp-audio.c b/src/a2dp-audio.c index b1c650396..3aaee8510 100644 --- a/src/a2dp-audio.c +++ b/src/a2dp-audio.c @@ -77,7 +77,7 @@ struct io_thread_data { * Scale PCM signal according to the volume configuration. */ static void ba_transport_pcm_scale( const struct ba_transport_pcm *pcm, - int16_t *buffer, + void *buffer, size_t samples) { unsigned int vmax = pcm->max_volume; @@ -108,10 +108,10 @@ static void ba_transport_pcm_scale( * Flush read buffer of the transport PCM FIFO. */ ssize_t ba_transport_pcm_flush(struct ba_transport_pcm *pcm) { ssize_t rv = splice(pcm->fd, NULL, config.null_fd, NULL, 1024 * 32, SPLICE_F_NONBLOCK); - if (rv == -1 && errno == EAGAIN) - rv = 0; if (rv > 0) - rv /= sizeof(int16_t); + rv /= BA_TRANSPORT_PCM_FORMAT_BYTES(pcm->format); + else if (rv == -1 && errno == EAGAIN) + rv = 0; return rv; } @@ -119,9 +119,10 @@ ssize_t ba_transport_pcm_flush(struct ba_transport_pcm *pcm) { * Read PCM signal from the transport PCM FIFO. */ ssize_t ba_transport_pcm_read( struct ba_transport_pcm *pcm, - int16_t *buffer, + void *buffer, size_t samples) { + const size_t sample_size = BA_TRANSPORT_PCM_FORMAT_BYTES(pcm->format); ssize_t ret; /* If the passed file descriptor is invalid (e.g. -1) is means, that other @@ -129,12 +130,12 @@ ssize_t ba_transport_pcm_read( * closed during this call, we will still read correct data, because Linux * kernel does not decrement file descriptor reference counter until the * read returns. */ - while ((ret = read(pcm->fd, buffer, samples * sizeof(int16_t))) == -1 && + while ((ret = read(pcm->fd, buffer, samples * sample_size)) == -1 && errno == EINTR) continue; if (ret > 0) { - samples = ret / sizeof(int16_t); + samples = ret / sample_size; ba_transport_pcm_scale(pcm, buffer, samples); return samples; } @@ -156,12 +157,12 @@ ssize_t ba_transport_pcm_read( * This function temporally re-enables thread cancellation! */ ssize_t ba_transport_pcm_write( struct ba_transport_pcm *pcm, - int16_t *buffer, + void *buffer, size_t samples) { + const uint8_t *head = buffer; + size_t len = samples * BA_TRANSPORT_PCM_FORMAT_BYTES(pcm->format); struct pollfd pfd = { pcm->fd, POLLOUT, 0 }; - const uint8_t *head = (uint8_t *)buffer; - size_t len = samples * sizeof(int16_t); int oldstate; ssize_t ret; diff --git a/src/a2dp-audio.h b/src/a2dp-audio.h index 89ce903ba..f594036da 100644 --- a/src/a2dp-audio.h +++ b/src/a2dp-audio.h @@ -16,7 +16,6 @@ #endif #include -#include #include "ba-transport.h" @@ -25,12 +24,12 @@ ssize_t ba_transport_pcm_flush( ssize_t ba_transport_pcm_read( struct ba_transport_pcm *pcm, - int16_t *buffer, + void *buffer, size_t samples); ssize_t ba_transport_pcm_write( struct ba_transport_pcm *pcm, - int16_t *buffer, + void *buffer, size_t samples); int a2dp_audio_thread_create(struct ba_transport *t); diff --git a/src/asound/bluealsa-pcm.c b/src/asound/bluealsa-pcm.c index f675c863d..540a7a268 100644 --- a/src/asound/bluealsa-pcm.c +++ b/src/asound/bluealsa-pcm.c @@ -358,7 +358,7 @@ static int bluealsa_stop(snd_pcm_ioplug_t *io) { /* Bug in ioplug - if pcm->io_hw_ptr == -1 then it reports state * SND_PCM_STATE_XRUN instead of SND_PCM_STATE_SETUP after pcm is stopped */ - pcm->io_hw_ptr = 0; + pcm->io_hw_ptr = 0; if (!bluealsa_dbus_pcm_ctrl_send_drop(pcm->ba_pcm_ctrl_fd, NULL)) return -errno; @@ -736,14 +736,18 @@ static int str2profile(const char *str) { static snd_pcm_format_t get_snd_pcm_format(uint16_t format) { switch (format) { - case 0x0008: + case 0x0108: return SND_PCM_FORMAT_U8; - case 0x8010: + case 0x8210: return SND_PCM_FORMAT_S16_LE; - case 0x8018: + case 0x8318: return SND_PCM_FORMAT_S24_3LE; + case 0x8418: + return SND_PCM_FORMAT_S24_LE; + case 0x8420: + return SND_PCM_FORMAT_S32_LE; default: - SNDERR("Unsupported PCM format: %#x", format); + SNDERR("Unknown PCM format: %#x", format); return SND_PCM_FORMAT_UNKNOWN; } } diff --git a/src/ba-transport.c b/src/ba-transport.c index 0578e8cdd..8a213ee1d 100644 --- a/src/ba-transport.c +++ b/src/ba-transport.c @@ -494,12 +494,12 @@ int ba_transport_select_codec_sco( static void transport_update_format(struct ba_transport *t) { if (t->type.profile & BA_TRANSPORT_PROFILE_MASK_A2DP) { - t->a2dp.pcm.format = BA_TRANSPORT_PCM_FORMAT_S16LE; + t->a2dp.pcm.format = BA_TRANSPORT_PCM_FORMAT_S16_2LE; } if (t->type.profile & BA_TRANSPORT_PROFILE_MASK_SCO) { - t->sco.spk_pcm.format = BA_TRANSPORT_PCM_FORMAT_S16LE; - t->sco.mic_pcm.format = BA_TRANSPORT_PCM_FORMAT_S16LE; + t->sco.spk_pcm.format = BA_TRANSPORT_PCM_FORMAT_S16_2LE; + t->sco.mic_pcm.format = BA_TRANSPORT_PCM_FORMAT_S16_2LE; } } diff --git a/src/ba-transport.h b/src/ba-transport.h index 4ee0f3846..0628cfefb 100644 --- a/src/ba-transport.h +++ b/src/ba-transport.h @@ -75,12 +75,19 @@ enum ba_transport_pcm_mode { /** * Builder for 16-bit PCM stream format identifier. */ -#define BA_TRANSPORT_PCM_FORMAT(sign, width, byteorder) \ - (((sign & 1) << 15) | ((byteorder & 1) << 14) | ((width) & 0x3F)) - -#define BA_TRANSPORT_PCM_FORMAT_U8 BA_TRANSPORT_PCM_FORMAT(0, 8, 0) -#define BA_TRANSPORT_PCM_FORMAT_S16LE BA_TRANSPORT_PCM_FORMAT(1, 16, 0) -#define BA_TRANSPORT_PCM_FORMAT_S24LE BA_TRANSPORT_PCM_FORMAT(1, 24, 0) +#define BA_TRANSPORT_PCM_FORMAT(sign, width, bytes, endian) \ + (((sign & 1) << 15) | ((endian & 1) << 14) | ((bytes & 0x3F) << 8) | (width & 0xFF)) + +#define BA_TRANSPORT_PCM_FORMAT_SIGN(format) (((format) >> 15) & 0x1) +#define BA_TRANSPORT_PCM_FORMAT_WIDTH(format) ((format) & 0xFF) +#define BA_TRANSPORT_PCM_FORMAT_BYTES(format) (((format) >> 8) & 0x3F) +#define BA_TRANSPORT_PCM_FORMAT_ENDIAN(format) (((format) >> 14) & 0x1) + +#define BA_TRANSPORT_PCM_FORMAT_U8 BA_TRANSPORT_PCM_FORMAT(0, 8, 1, 0) +#define BA_TRANSPORT_PCM_FORMAT_S16_2LE BA_TRANSPORT_PCM_FORMAT(1, 16, 2, 0) +#define BA_TRANSPORT_PCM_FORMAT_S24_3LE BA_TRANSPORT_PCM_FORMAT(1, 24, 3, 0) +#define BA_TRANSPORT_PCM_FORMAT_S24_4LE BA_TRANSPORT_PCM_FORMAT(1, 24, 4, 0) +#define BA_TRANSPORT_PCM_FORMAT_S32_4LE BA_TRANSPORT_PCM_FORMAT(1, 32, 4, 0) struct ba_transport_pcm { diff --git a/test/server-mock.c b/test/server-mock.c index a0ceba215..95661918d 100644 --- a/test/server-mock.c +++ b/test/server-mock.c @@ -126,7 +126,7 @@ static void *test_a2dp_sink_sbc(struct ba_transport *t) { if (asrs.frames == 0) asrsync_init(&asrs, t->a2dp.pcm.sampling); - int samples = sizeof(buffer) / sizeof(int16_t); + const size_t samples = ARRAYSIZE(buffer); x = snd_pcm_sine_s16le(buffer, samples, 2, x, 1.0 / 128); if (ba_transport_pcm_write(&t->a2dp.pcm, buffer, samples) == -1) diff --git a/test/test-ba.c b/test/test-ba.c index 624cb6f85..4a95766e9 100644 --- a/test/test-ba.c +++ b/test/test-ba.c @@ -111,6 +111,25 @@ START_TEST(test_ba_transport) { } END_TEST +START_TEST(test_ba_transport_pcm_format) { + + uint16_t format_u8 = BA_TRANSPORT_PCM_FORMAT_U8; + uint16_t format_s32_4le = BA_TRANSPORT_PCM_FORMAT_S32_4LE; + + ck_assert_int_eq(format_u8, 0x0108); + ck_assert_int_eq(BA_TRANSPORT_PCM_FORMAT_SIGN(format_u8), 0); + ck_assert_int_eq(BA_TRANSPORT_PCM_FORMAT_WIDTH(format_u8), 8); + ck_assert_int_eq(BA_TRANSPORT_PCM_FORMAT_BYTES(format_u8), 1); + ck_assert_int_eq(BA_TRANSPORT_PCM_FORMAT_ENDIAN(format_u8), 0); + + ck_assert_int_eq(format_s32_4le, 0x8420); + ck_assert_int_eq(BA_TRANSPORT_PCM_FORMAT_SIGN(format_s32_4le), 1); + ck_assert_int_eq(BA_TRANSPORT_PCM_FORMAT_WIDTH(format_s32_4le), 32); + ck_assert_int_eq(BA_TRANSPORT_PCM_FORMAT_BYTES(format_s32_4le), 4); + ck_assert_int_eq(BA_TRANSPORT_PCM_FORMAT_ENDIAN(format_s32_4le), 0); + +} END_TEST + START_TEST(test_ba_transport_volume_packed) { struct ba_adapter *a; @@ -196,6 +215,7 @@ int main(void) { tcase_add_test(tc, test_ba_adapter); tcase_add_test(tc, test_ba_device); tcase_add_test(tc, test_ba_transport); + tcase_add_test(tc, test_ba_transport_pcm_format); tcase_add_test(tc, test_ba_transport_volume_packed); tcase_add_test(tc, test_cascade_free); diff --git a/test/test-io.c b/test/test-io.c index 6f032f758..faf24bd5e 100644 --- a/test/test-io.c +++ b/test/test-io.c @@ -256,8 +256,9 @@ static void *test_io_thread_a2dp_dump_pcm(struct ba_transport *t) { continue; } - debug("Decoded samples: %zd", len / sizeof(int16_t)); - decoded_samples_total += len / sizeof(int16_t); + size_t sample_size = BA_TRANSPORT_PCM_FORMAT_BYTES(t->a2dp.pcm.format); + debug("Decoded samples: %zd", len / sample_size); + decoded_samples_total += len / sample_size; if (f != NULL) fwrite(buffer, 1, len, f); @@ -366,8 +367,9 @@ static void test_sco(struct ba_transport *t, void *(*cb)(struct ba_transport *)) if (pfds[1].revents & POLLIN) { ck_assert_int_gt(len = read(pcm_mic_fds[0], buffer, sizeof(buffer)), 0); - debug("Decoded samples: %zd", len / sizeof(int16_t)); - decoded_samples_total += len / sizeof(int16_t); + size_t sample_size = BA_TRANSPORT_PCM_FORMAT_BYTES(t->sco.mic_pcm.format); + debug("Decoded samples: %zd", len / sample_size); + decoded_samples_total += len / sample_size; } } diff --git a/utils/aplay/aplay.c b/utils/aplay/aplay.c index 7392e0c06..bbd2773da 100644 --- a/utils/aplay/aplay.c +++ b/utils/aplay/aplay.c @@ -104,12 +104,16 @@ static int parse_bt_addresses(char *argv[], size_t count) { static snd_pcm_format_t bluealsa_get_snd_pcm_format(const struct ba_pcm *pcm) { switch (pcm->format) { - case 0x0008: + case 0x0108: return SND_PCM_FORMAT_U8; - case 0x8010: + case 0x8210: return SND_PCM_FORMAT_S16_LE; - case 0x8018: + case 0x8318: return SND_PCM_FORMAT_S24_3LE; + case 0x8418: + return SND_PCM_FORMAT_S24_LE; + case 0x8420: + return SND_PCM_FORMAT_S32_LE; default: error("Unknown PCM format: %#x", pcm->format); return SND_PCM_FORMAT_UNKNOWN;