Skip to content

Commit

Permalink
Store PCM sample physical width in format property
Browse files Browse the repository at this point in the history
  • Loading branch information
arkq committed Sep 7, 2020
1 parent defe35c commit 34a0b0b
Show file tree
Hide file tree
Showing 10 changed files with 77 additions and 39 deletions.
9 changes: 5 additions & 4 deletions doc/bluealsa-api.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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]

Expand Down
21 changes: 11 additions & 10 deletions src/a2dp-audio.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -108,33 +108,34 @@ 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;
}

/**
* 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
* thread (the controller) has closed the connection. If the connection was
* 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;
}
Expand All @@ -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;

Expand Down
5 changes: 2 additions & 3 deletions src/a2dp-audio.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
#endif

#include <stddef.h>
#include <stdint.h>

#include "ba-transport.h"

Expand All @@ -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);
Expand Down
14 changes: 9 additions & 5 deletions src/asound/bluealsa-pcm.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}
}
Expand Down
6 changes: 3 additions & 3 deletions src/ba-transport.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

}
Expand Down
19 changes: 13 additions & 6 deletions src/ba-transport.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 {

Expand Down
2 changes: 1 addition & 1 deletion test/server-mock.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
20 changes: 20 additions & 0 deletions test/test-ba.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);

Expand Down
10 changes: 6 additions & 4 deletions test/test-io.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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;
}

}
Expand Down
10 changes: 7 additions & 3 deletions utils/aplay/aplay.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down

0 comments on commit 34a0b0b

Please sign in to comment.