aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Li <dvdli@google.com>2021-03-18 04:36:12 +0000
committerAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>2021-03-18 04:36:12 +0000
commit3e21898afa6b498ebdde6f0a621e02247886ded0 (patch)
treed696685895605c6752e4363dd00c21b205d22194
parent882dbb1a2cf5e1cbccad294a5624444bdcf8ce4c (diff)
parent61254c737f5ac80bb1a1a66c6c1420e08bc16021 (diff)
downloadtinyalsa_new-3e21898afa6b498ebdde6f0a621e02247886ded0.tar.gz
Merge remote-tracking branch 'remotes/aosp/upstream-master' into am: 62e402fb24 am: 6f0089016a am: 61254c737f
Original change: https://android-review.googlesource.com/c/platform/external/tinyalsa_new/+/1639959 Change-Id: If409a3721cc0d66e066e319e658f84b1713ef1fe
-rw-r--r--.travis.yml2
-rw-r--r--Android.bp8
-rw-r--r--include/tinyalsa/pcm.h8
-rw-r--r--src/pcm.c88
-rw-r--r--tests/src/mixer_test.cc2
-rw-r--r--tests/src/pcm_in_test.cc1
-rw-r--r--tests/src/pcm_loopback_test.cc118
-rw-r--r--tests/src/pcm_out_test.cc6
-rw-r--r--tests/src/pcm_test.cc2
-rw-r--r--utils/tinymix.c2
-rw-r--r--utils/tinyplay.c113
11 files changed, 235 insertions, 115 deletions
diff --git a/.travis.yml b/.travis.yml
index 28667cb..4186666 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,5 +1,5 @@
os: linux
-dist: xenial
+dist: bionic
sudo: false
language: c
compiler:
diff --git a/Android.bp b/Android.bp
index 330393c..d6fec57 100644
--- a/Android.bp
+++ b/Android.bp
@@ -67,7 +67,7 @@ cc_binary {
name: "tinyplay2",
host_supported: true,
srcs: ["utils/tinyplay.c"],
- shared_libs: ["libtinyalsav2"],
+ static_libs: ["libtinyalsav2"],
cflags: ["-Werror"],
target: {
darwin: {
@@ -79,20 +79,20 @@ cc_binary {
cc_binary {
name: "tinycap2",
srcs: ["utils/tinycap.c"],
- shared_libs: ["libtinyalsav2"],
+ static_libs: ["libtinyalsav2"],
cflags: ["-Werror"],
}
cc_binary {
name: "tinymix2",
srcs: ["utils/tinymix.c"],
- shared_libs: ["libtinyalsav2"],
+ static_libs: ["libtinyalsav2"],
cflags: ["-Werror", "-Wall"],
}
cc_binary {
name: "tinypcminfo2",
srcs: ["utils/tinypcminfo.c"],
- shared_libs: ["libtinyalsav2"],
+ static_libs: ["libtinyalsav2"],
cflags: ["-Werror"],
}
diff --git a/include/tinyalsa/pcm.h b/include/tinyalsa/pcm.h
index 6569146..5c11e2a 100644
--- a/include/tinyalsa/pcm.h
+++ b/include/tinyalsa/pcm.h
@@ -178,6 +178,10 @@ enum pcm_format {
PCM_FORMAT_S24_3BE,
/** Signed, 32-bit, big endian */
PCM_FORMAT_S32_BE,
+ /** 32-bit float, little endian */
+ PCM_FORMAT_FLOAT_LE,
+ /** 32-bit float, big endian */
+ PCM_FORMAT_FLOAT_BE,
/** Max of the enumeration list, not an actual format. */
PCM_FORMAT_MAX
};
@@ -335,9 +339,9 @@ int pcm_write(struct pcm *pcm, const void *data, unsigned int count) TINYALSA_DE
int pcm_read(struct pcm *pcm, void *data, unsigned int count) TINYALSA_DEPRECATED;
-int pcm_mmap_write(struct pcm *pcm, const void *data, unsigned int count);
+int pcm_mmap_write(struct pcm *pcm, const void *data, unsigned int count) TINYALSA_DEPRECATED;
-int pcm_mmap_read(struct pcm *pcm, void *data, unsigned int count);
+int pcm_mmap_read(struct pcm *pcm, void *data, unsigned int count) TINYALSA_DEPRECATED;
int pcm_mmap_begin(struct pcm *pcm, void **areas, unsigned int *offset, unsigned int *frames);
diff --git a/src/pcm.c b/src/pcm.c
index 98ca9eb..63ca65f 100644
--- a/src/pcm.c
+++ b/src/pcm.c
@@ -282,6 +282,11 @@ static unsigned int pcm_format_to_alsa(enum pcm_format format)
return SNDRV_PCM_FORMAT_S32_LE;
case PCM_FORMAT_S32_BE:
return SNDRV_PCM_FORMAT_S32_BE;
+
+ case PCM_FORMAT_FLOAT_LE:
+ return SNDRV_PCM_FORMAT_FLOAT_LE;
+ case PCM_FORMAT_FLOAT_BE:
+ return SNDRV_PCM_FORMAT_FLOAT_BE;
};
}
@@ -556,6 +561,8 @@ unsigned int pcm_format_to_bits(enum pcm_format format)
case PCM_FORMAT_S32_BE:
case PCM_FORMAT_S24_LE:
case PCM_FORMAT_S24_BE:
+ case PCM_FORMAT_FLOAT_LE:
+ case PCM_FORMAT_FLOAT_BE:
return 32;
case PCM_FORMAT_S24_3LE:
case PCM_FORMAT_S24_3BE:
@@ -995,24 +1002,25 @@ int pcm_close(struct pcm *pcm)
* - @ref PCM_MONOTONIC
* @param config The hardware and software parameters to open the PCM with.
* @returns A PCM structure.
- * If an error occurs allocating memory for the PCM, NULL is returned.
- * Otherwise, client code should check that the PCM opened properly by calling @ref pcm_is_ready.
- * If @ref pcm_is_ready, check @ref pcm_get_error for more information.
+ * If an error occurs, the pointer of bad_pcm is returned.
+ * Otherwise, it returns the pointer of PCM object.
+ * Client code should check that the PCM opened properly by calling @ref pcm_is_ready.
+ * If @ref pcm_is_ready returns false, check @ref pcm_get_error for more information.
* @ingroup libtinyalsa-pcm
*/
struct pcm *pcm_open_by_name(const char *name,
unsigned int flags,
const struct pcm_config *config)
{
- unsigned int card, device;
- if ((name[0] != 'h')
- || (name[1] != 'w')
- || (name[2] != ':')) {
- return NULL;
- } else if (sscanf(&name[3], "%u,%u", &card, &device) != 2) {
- return NULL;
- }
- return pcm_open(card, device, flags, config);
+ unsigned int card, device;
+ if (name[0] != 'h' || name[1] != 'w' || name[2] != ':') {
+ oops(&bad_pcm, 0, "name format is not matched");
+ return &bad_pcm;
+ } else if (sscanf(&name[3], "%u,%u", &card, &device) != 2) {
+ oops(&bad_pcm, 0, "name format is not matched");
+ return &bad_pcm;
+ }
+ return pcm_open(card, device, flags, config);
}
/** Opens a PCM.
@@ -1029,9 +1037,10 @@ struct pcm *pcm_open_by_name(const char *name,
* - @ref PCM_MONOTONIC
* @param config The hardware and software parameters to open the PCM with.
* @returns A PCM structure.
- * If an error occurs allocating memory for the PCM, NULL is returned.
- * Otherwise, client code should check that the PCM opened properly by calling @ref pcm_is_ready.
- * If @ref pcm_is_ready, check @ref pcm_get_error for more information.
+ * If an error occurs, the pointer of bad_pcm is returned.
+ * Otherwise, it returns the pointer of PCM object.
+ * Client code should check that the PCM opened properly by calling @ref pcm_is_ready.
+ * If @ref pcm_is_ready returns false, check @ref pcm_get_error for more information.
* @ingroup libtinyalsa-pcm
*/
struct pcm *pcm_open(unsigned int card, unsigned int device,
@@ -1042,8 +1051,10 @@ struct pcm *pcm_open(unsigned int card, unsigned int device,
int rc;
pcm = calloc(1, sizeof(struct pcm));
- if (!pcm)
+ if (!pcm) {
+ oops(&bad_pcm, ENOMEM, "can't allocate PCM object");
return &bad_pcm;
+ }
/* Default to hw_ops, attemp plugin open only if hw (/dev/snd/pcm*) open fails */
pcm->ops = &hw_ops;
@@ -1055,7 +1066,7 @@ struct pcm *pcm_open(unsigned int card, unsigned int device,
pcm->snd_node = snd_utils_open_pcm(card, device);
pcm_type = snd_utils_get_node_type(pcm->snd_node);
if (!pcm->snd_node || pcm_type != SND_NODE_TYPE_PLUGIN) {
- oops(pcm, -ENODEV, "no device (hw/plugin) for card(%u), device(%u)",
+ oops(&bad_pcm, ENODEV, "no device (hw/plugin) for card(%u), device(%u)",
card, device);
goto fail_close_dev_node;
}
@@ -1064,7 +1075,7 @@ struct pcm *pcm_open(unsigned int card, unsigned int device,
}
#endif
if (pcm->fd < 0) {
- oops(pcm, errno, "cannot open device (%u) for card (%u)",
+ oops(&bad_pcm, errno, "cannot open device (%u) for card (%u)",
device, card);
goto fail_close_dev_node;
}
@@ -1072,7 +1083,7 @@ struct pcm *pcm_open(unsigned int card, unsigned int device,
pcm->flags = flags;
if (pcm->ops->ioctl(pcm->data, SNDRV_PCM_IOCTL_INFO, &info)) {
- oops(pcm, errno, "cannot get info");
+ oops(&bad_pcm, errno, "cannot get info");
goto fail_close;
}
pcm->subdevice = info.subdevice;
@@ -1082,7 +1093,7 @@ struct pcm *pcm_open(unsigned int card, unsigned int device,
rc = pcm_hw_mmap_status(pcm);
if (rc < 0) {
- oops(pcm, errno, "mmap status failed");
+ oops(&bad_pcm, errno, "mmap status failed");
goto fail;
}
@@ -1091,16 +1102,12 @@ struct pcm *pcm_open(unsigned int card, unsigned int device,
int arg = SNDRV_PCM_TSTAMP_TYPE_MONOTONIC;
rc = pcm->ops->ioctl(pcm->data, SNDRV_PCM_IOCTL_TTSTAMP, &arg);
if (rc < 0) {
- oops(pcm, errno, "cannot set timestamp type");
+ oops(&bad_pcm, errno, "cannot set timestamp type");
goto fail;
}
}
#endif
- /* prepare here so the user does not need to do this later */
- if (pcm_prepare(pcm))
- goto fail;
-
pcm->xruns = 0;
return pcm;
@@ -1238,6 +1245,7 @@ static inline int pcm_mmap_capture_avail(struct pcm *pcm)
int pcm_mmap_avail(struct pcm *pcm)
{
+ pcm_sync_ptr(pcm, SNDRV_PCM_SYNC_PTR_HWSYNC);
if (pcm->flags & PCM_IN)
return pcm_mmap_capture_avail(pcm);
else
@@ -1403,7 +1411,8 @@ again:
int pcm_state(struct pcm *pcm)
{
- int err = pcm_sync_ptr(pcm, 0);
+ // Update the state only. Do not sync HW sync.
+ int err = pcm_sync_ptr(pcm, SNDRV_PCM_SYNC_PTR_APPL | SNDRV_PCM_SYNC_PTR_AVAIL_MIN);
if (err < 0)
return err;
@@ -1546,18 +1555,31 @@ int pcm_mmap_transfer(struct pcm *pcm, void *buffer, unsigned int frames)
int pcm_mmap_write(struct pcm *pcm, const void *data, unsigned int count)
{
if ((~pcm->flags) & (PCM_OUT | PCM_MMAP))
- return -ENOSYS;
+ return -EINVAL;
+
+ unsigned int frames = pcm_bytes_to_frames(pcm, count);
+ int res = pcm_writei(pcm, (void *) data, frames);
- return pcm_mmap_transfer(pcm, (void *)data,
- pcm_bytes_to_frames(pcm, count));
+ if (res < 0) {
+ return res;
+ }
+
+ return (unsigned int) res == frames ? 0 : -EIO;
}
int pcm_mmap_read(struct pcm *pcm, void *data, unsigned int count)
{
if ((~pcm->flags) & (PCM_IN | PCM_MMAP))
- return -ENOSYS;
+ return -EINVAL;
+
+ unsigned int frames = pcm_bytes_to_frames(pcm, count);
+ int res = pcm_readi(pcm, data, frames);
- return pcm_mmap_transfer(pcm, data, pcm_bytes_to_frames(pcm, count));
+ if (res < 0) {
+ return res;
+ }
+
+ return (unsigned int) res == frames ? 0 : -EIO;
}
/* Returns current read/write position in the mmap buffer with associated time stamp. */
@@ -1623,6 +1645,10 @@ static int pcm_generic_transfer(struct pcm *pcm, void *data,
if (frames > INT_MAX)
return -EINVAL;
+ if (pcm_state(pcm) == PCM_STATE_SETUP && pcm_prepare(pcm) != 0) {
+ return -1;
+ }
+
again:
if (pcm->flags & PCM_MMAP)
diff --git a/tests/src/mixer_test.cc b/tests/src/mixer_test.cc
index 717269c..903fb8f 100644
--- a/tests/src/mixer_test.cc
+++ b/tests/src/mixer_test.cc
@@ -288,7 +288,7 @@ TEST_P(MixerControlsTest, Event) {
EXPECT_EQ(mixer_wait_event(mixer_object, 1000), 1);
- EXPECT_EQ(mixer_consume_event(mixer_object), 0);
+ EXPECT_EQ(mixer_consume_event(mixer_object), 1);
thread.join();
ASSERT_EQ(mixer_subscribe_events(mixer_object, 0), 0);
diff --git a/tests/src/pcm_in_test.cc b/tests/src/pcm_in_test.cc
index e912abb..4d8a697 100644
--- a/tests/src/pcm_in_test.cc
+++ b/tests/src/pcm_in_test.cc
@@ -73,6 +73,7 @@ class PcmInTest : public ::testing::Test {
};
TEST_F(PcmInTest, GetDelay) {
+ pcm_prepare(pcm_object);
long delay = pcm_get_delay(pcm_object);
std::cout << delay << std::endl;
ASSERT_GE(delay, 0);
diff --git a/tests/src/pcm_loopback_test.cc b/tests/src/pcm_loopback_test.cc
index 6a3ffb8..5c6ff4d 100644
--- a/tests/src/pcm_loopback_test.cc
+++ b/tests/src/pcm_loopback_test.cc
@@ -64,6 +64,7 @@ public:
template<pcm_format F>
struct PcmFormat {
using Type = void;
+ static constexpr pcm_format kFormat = F;
static constexpr int32_t kMax = 0;
static constexpr int32_t kMin = 0;
};
@@ -71,10 +72,19 @@ struct PcmFormat {
template<>
struct PcmFormat<PCM_FORMAT_S16_LE> {
using Type = int16_t;
+ static constexpr pcm_format kFormat = PCM_FORMAT_S16_LE;
static constexpr Type kMax = std::numeric_limits<Type>::max();
static constexpr Type kMin = std::numeric_limits<Type>::min();
};
+template<>
+struct PcmFormat<PCM_FORMAT_FLOAT_LE> {
+ using Type = float;
+ static constexpr pcm_format kFormat = PCM_FORMAT_FLOAT_LE;
+ static constexpr Type kMax = 1.0;
+ static constexpr Type kMin = -1.0;
+};
+
// CH: channels
// SR: sampling rate
// FQ: sine wave frequency
@@ -139,43 +149,77 @@ static double Energy(T *buffer, size_t samples) {
return sum;
}
-TEST(PcmLoopbackTest, LoopbackS16le) {
+template<typename F>
+class PcmLoopbackTest : public ::testing::Test {
+ protected:
+ PcmLoopbackTest() = default;
+ virtual ~PcmLoopbackTest() = default;
+
+ void SetUp() override {
+ static constexpr pcm_config kInConfig = {
+ .channels = kDefaultChannels,
+ .rate = kDefaultSamplingRate,
+ .period_size = kDefaultPeriodSize,
+ .period_count = kDefaultPeriodCount,
+ .format = kPcmForamt,
+ .start_threshold = 0,
+ .stop_threshold = 0,
+ .silence_threshold = 0,
+ .silence_size = 0,
+ };
+ pcm_in = pcm_open(kLoopbackCard, kLoopbackCaptureDevice, PCM_IN, &kInConfig);
+ ASSERT_TRUE(pcm_is_ready(pcm_in));
+
+ static constexpr pcm_config kOutConfig = {
+ .channels = kDefaultChannels,
+ .rate = kDefaultSamplingRate,
+ .period_size = kDefaultPeriodSize,
+ .period_count = kDefaultPeriodCount,
+ .format = kPcmForamt,
+ .start_threshold = kDefaultPeriodSize,
+ .stop_threshold = kDefaultPeriodSize * kDefaultPeriodCount,
+ .silence_threshold = 0,
+ .silence_size = 0,
+ };
+ pcm_out = pcm_open(kLoopbackCard, kLoopbackPlaybackDevice, PCM_OUT, &kOutConfig);
+ ASSERT_TRUE(pcm_is_ready(pcm_out));
+ ASSERT_EQ(pcm_link(pcm_in, pcm_out), 0);
+ }
+
+ void TearDown() override {
+ ASSERT_EQ(pcm_unlink(pcm_in), 0);
+ pcm_close(pcm_in);
+ pcm_close(pcm_out);
+ std::this_thread::sleep_for(std::chrono::milliseconds(100));
+ }
+
static constexpr unsigned int kDefaultChannels = 2;
static constexpr unsigned int kDefaultSamplingRate = 48000;
static constexpr unsigned int kDefaultPeriodSize = 1024;
static constexpr unsigned int kDefaultPeriodCount = 3;
static constexpr unsigned int kDefaultPeriodTimeInMs =
kDefaultPeriodSize * 1000 / kDefaultSamplingRate;
+ static constexpr pcm_format kPcmForamt = F::Format;
+ pcm *pcm_in;
+ pcm *pcm_out;
+};
- static constexpr pcm_config kInConfig = {
- .channels = kDefaultChannels,
- .rate = kDefaultSamplingRate,
- .period_size = kDefaultPeriodSize,
- .period_count = kDefaultPeriodCount,
- .format = PCM_FORMAT_S16_LE,
- .start_threshold = 0,
- .stop_threshold = 0,
- .silence_threshold = 0,
- .silence_size = 0,
- };
- pcm *pcm_in = pcm_open(kLoopbackCard, kLoopbackCaptureDevice, PCM_IN, &kInConfig);
- ASSERT_TRUE(pcm_is_ready(pcm_in));
-
- static constexpr pcm_config kOutConfig = {
- .channels = kDefaultChannels,
- .rate = kDefaultSamplingRate,
- .period_size = kDefaultPeriodSize,
- .period_count = kDefaultPeriodCount,
- .format = PCM_FORMAT_S16_LE,
- .start_threshold = kDefaultPeriodSize,
- .stop_threshold = kDefaultPeriodSize * kDefaultPeriodCount,
- .silence_threshold = 0,
- .silence_size = 0,
- };
- pcm *pcm_out = pcm_open(kLoopbackCard, kLoopbackPlaybackDevice, PCM_OUT, &kOutConfig);
- ASSERT_TRUE(pcm_is_ready(pcm_out));
+using S16bitlePcmFormat = PcmFormat<PCM_FORMAT_S16_LE>;
+using FloatPcmFormat = PcmFormat<PCM_FORMAT_FLOAT_LE>;
- ASSERT_EQ(pcm_link(pcm_in, pcm_out), 0);
+using Formats = ::testing::Types<S16bitlePcmFormat, FloatPcmFormat>;
+
+TYPED_TEST_SUITE(PcmLoopbackTest, Formats);
+
+TYPED_TEST(PcmLoopbackTest, Loopback) {
+ static constexpr unsigned int kDefaultChannels = this->kDefaultChannels;
+ static constexpr unsigned int kDefaultSamplingRate = this->kDefaultSamplingRate;
+ static constexpr unsigned int kDefaultPeriodSize = this->kDefaultPeriodSize;
+ // static constexpr unsigned int kDefaultPeriodCount = this->kDefaultPeriodCount;
+ static constexpr unsigned int kDefaultPeriodTimeInMs = this->kDefaultPeriodTimeInMs;
+ static constexpr pcm_format kPcmForamt = this->kPcmForamt;
+ pcm *pcm_in = this->pcm_in;
+ pcm *pcm_out = this->pcm_out;
bool stopping = false;
ASSERT_EQ(pcm_get_subdevice(pcm_in), pcm_get_subdevice(pcm_out));
@@ -190,23 +234,26 @@ TEST(PcmLoopbackTest, LoopbackS16le) {
if (res == -1) {
std::cout << pcm_get_error(pcm_in) << std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds(kDefaultPeriodTimeInMs));
+ counter++;
continue;
}
- EXPECT_EQ(pcm_readi(pcm_in, buffer.get(), frames), frames) << counter;
+
// Test the energy of the buffer after the sine tone samples fill in the buffer.
// Therefore, check the buffer 5 times later.
if (counter >= 5) {
- double e = Energy(buffer.get(), frames * kInConfig.channels);
+ double e = Energy(buffer.get(), frames * kDefaultChannels);
EXPECT_GT(e, 0.0) << counter;
}
counter++;
}
+ std::cout << "read count = " << counter << std::endl;
});
std::thread playback([pcm_out, &stopping] {
- SineToneGenerator<2, 48000, 1000, 0, PCM_FORMAT_S16_LE> generator;
+ SineToneGenerator<kDefaultChannels, kDefaultSamplingRate, 1000, 0, kPcmForamt> generator;
size_t buffer_size = pcm_frames_to_bytes(pcm_out, kDefaultPeriodSize);
unsigned int frames = pcm_bytes_to_frames(pcm_out, buffer_size);
+ std::cout << buffer_size << std::endl;
auto buffer = std::make_unique<unsigned char[]>(buffer_size);
int32_t counter = 0;
while (!stopping) {
@@ -214,16 +261,13 @@ TEST(PcmLoopbackTest, LoopbackS16le) {
EXPECT_EQ(pcm_writei(pcm_out, buffer.get(), frames), frames) << counter;
counter++;
}
+ std::cout << "write count = " << counter << std::endl;
});
- std::this_thread::sleep_for(std::chrono::seconds(1));
+ std::this_thread::sleep_for(std::chrono::milliseconds(500));
stopping = true;
capture.join();
playback.join();
-
- ASSERT_EQ(pcm_unlink(pcm_in), 0);
- pcm_close(pcm_in);
- pcm_close(pcm_out);
}
} // namespace testing
diff --git a/tests/src/pcm_out_test.cc b/tests/src/pcm_out_test.cc
index cbc6983..f8d4345 100644
--- a/tests/src/pcm_out_test.cc
+++ b/tests/src/pcm_out_test.cc
@@ -192,13 +192,13 @@ TEST_F(PcmOutMmapTest, Write) {
buffer[i] = static_cast<char>(i);
}
- int written_frames = 0;
+ int res = 0;
unsigned int frames = pcm_bytes_to_frames(pcm_object, buffer_size);
pcm_start(pcm_object);
auto start = std::chrono::steady_clock::now();
for (uint32_t i = 0; i < write_count; ++i) {
- written_frames = pcm_mmap_write(pcm_object, buffer.get(), buffer_size);
- ASSERT_EQ(written_frames, frames);
+ res = pcm_mmap_write(pcm_object, buffer.get(), buffer_size);
+ ASSERT_EQ(res, 0);
}
pcm_stop(pcm_object);
diff --git a/tests/src/pcm_test.cc b/tests/src/pcm_test.cc
index 2668350..da114de 100644
--- a/tests/src/pcm_test.cc
+++ b/tests/src/pcm_test.cc
@@ -49,6 +49,8 @@ TEST(PcmTest, FormatToBits) {
ASSERT_EQ(pcm_format_to_bits(PCM_FORMAT_S24_BE), 32);
ASSERT_EQ(pcm_format_to_bits(PCM_FORMAT_S24_3BE), 24);
ASSERT_EQ(pcm_format_to_bits(PCM_FORMAT_S32_BE), 32);
+ ASSERT_EQ(pcm_format_to_bits(PCM_FORMAT_FLOAT_LE), 32);
+ ASSERT_EQ(pcm_format_to_bits(PCM_FORMAT_FLOAT_BE), 32);
}
TEST(PcmTest, OpenAndCloseOutPcm) {
diff --git a/utils/tinymix.c b/utils/tinymix.c
index 5c0378c..e272ade 100644
--- a/utils/tinymix.c
+++ b/utils/tinymix.c
@@ -370,7 +370,7 @@ fail:
static int is_int(const char *value)
{
- return value[0] >= '0' || value[0] <= '9';
+ return value[0] >= '0' && value[0] <= '9';
}
struct parsed_int
diff --git a/utils/tinyplay.c b/utils/tinyplay.c
index 2689158..96d0f60 100644
--- a/utils/tinyplay.c
+++ b/utils/tinyplay.c
@@ -27,11 +27,12 @@
*/
#include <tinyalsa/asoundlib.h>
+#include <signal.h>
+#include <stdbool.h>
+#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
-#include <stdint.h>
#include <string.h>
-#include <signal.h>
#define OPTPARSE_IMPLEMENTATION
#include "optparse.h"
@@ -44,6 +45,7 @@ struct cmd {
int flags;
struct pcm_config config;
unsigned int bits;
+ bool is_float;
};
void cmd_init(struct cmd *cmd)
@@ -63,6 +65,7 @@ void cmd_init(struct cmd *cmd)
cmd->config.stop_threshold = cmd->config.period_size * cmd->config.period_count;
cmd->config.start_threshold = cmd->config.period_size;
cmd->bits = 16;
+ cmd->is_float = false;
}
#define ID_RIFF 0x46464952
@@ -70,6 +73,9 @@ void cmd_init(struct cmd *cmd)
#define ID_FMT 0x20746d66
#define ID_DATA 0x61746164
+#define WAVE_FORMAT_PCM 0x0001
+#define WAVE_FORMAT_IEEE_FLOAT 0x0003
+
struct riff_wave_header {
uint32_t riff_id;
uint32_t riff_sz;
@@ -100,10 +106,32 @@ struct ctx {
FILE *file;
};
-int ctx_init(struct ctx* ctx, const struct cmd *cmd)
+static bool is_wave_file(const char *filetype)
+{
+ return filetype != NULL && strcmp(filetype, "wav") == 0;
+}
+
+static bool signed_pcm_bits_to_format(int bits)
+{
+ switch (bits) {
+ case 8:
+ return PCM_FORMAT_S8;
+ case 16:
+ return PCM_FORMAT_S16_LE;
+ case 24:
+ return PCM_FORMAT_S24_3LE;
+ case 32:
+ return PCM_FORMAT_S32_LE;
+ default:
+ return -1;
+ }
+}
+
+static int ctx_init(struct ctx* ctx, struct cmd *cmd)
{
unsigned int bits = cmd->bits;
- struct pcm_config config = cmd->config;
+ struct pcm_config *config = &cmd->config;
+ bool is_float = cmd->is_float;
if (cmd->filename == NULL) {
fprintf(stderr, "filename not specified\n");
@@ -120,7 +148,7 @@ int ctx_init(struct ctx* ctx, const struct cmd *cmd)
return -1;
}
- if ((cmd->filetype != NULL) && (strcmp(cmd->filetype, "wav") == 0)) {
+ if (is_wave_file(cmd->filetype)) {
if (fread(&ctx->wave_header, sizeof(ctx->wave_header), 1, ctx->file) != 1){
fprintf(stderr, "error: '%s' does not contain a riff/wave header\n", cmd->filename);
fclose(ctx->file);
@@ -159,35 +187,31 @@ int ctx_init(struct ctx* ctx, const struct cmd *cmd)
fseek(ctx->file, ctx->chunk_header.sz, SEEK_CUR);
}
} while (more_chunks);
- config.channels = ctx->chunk_fmt.num_channels;
- config.rate = ctx->chunk_fmt.sample_rate;
+ config->channels = ctx->chunk_fmt.num_channels;
+ config->rate = ctx->chunk_fmt.sample_rate;
bits = ctx->chunk_fmt.bits_per_sample;
+ is_float = ctx->chunk_fmt.audio_format == WAVE_FORMAT_IEEE_FLOAT;
}
- if (bits == 8) {
- config.format = PCM_FORMAT_S8;
- } else if (bits == 16) {
- config.format = PCM_FORMAT_S16_LE;
- } else if (bits == 24) {
- config.format = PCM_FORMAT_S24_3LE;
- } else if (bits == 32) {
- config.format = PCM_FORMAT_S32_LE;
+ if (is_float) {
+ config->format = PCM_FORMAT_FLOAT_LE;
} else {
- fprintf(stderr, "bit count '%u' not supported\n", bits);
- fclose(ctx->file);
- return -1;
+ config->format = signed_pcm_bits_to_format(bits);
+ if (config->format == -1) {
+ fprintf(stderr, "bit count '%u' not supported\n", bits);
+ fclose(ctx->file);
+ return -1;
+ }
}
ctx->pcm = pcm_open(cmd->card,
cmd->device,
cmd->flags,
- &config);
- if (ctx->pcm == NULL) {
- fprintf(stderr, "failed to allocate memory for pcm\n");
- fclose(ctx->file);
- return -1;
- } else if (!pcm_is_ready(ctx->pcm)) {
- fprintf(stderr, "failed to open for pcm %u,%u\n", cmd->card, cmd->device);
+ config);
+ if (!pcm_is_ready(ctx->pcm)) {
+ fprintf(stderr, "failed to open for pcm %u,%u. %s\n",
+ cmd->card, cmd->device,
+ pcm_get_error(ctx->pcm));
fclose(ctx->file);
pcm_close(ctx->pcm);
return -1;
@@ -228,10 +252,11 @@ void print_usage(const char *argv0)
fprintf(stderr, "-d | --device <device number> The device to receive the audio\n");
fprintf(stderr, "-p | --period-size <size> The size of the PCM's period\n");
fprintf(stderr, "-n | --period-count <count> The number of PCM periods\n");
- fprintf(stderr, "-i | --file-type <file-type > The type of file to read (raw or wav)\n");
+ fprintf(stderr, "-i | --file-type <file-type> The type of file to read (raw or wav)\n");
fprintf(stderr, "-c | --channels <count> The amount of channels per frame\n");
fprintf(stderr, "-r | --rate <rate> The amount of frames per second\n");
fprintf(stderr, "-b | --bits <bit-count> The number of bits in one sample\n");
+ fprintf(stderr, "-f | --float The frames are in floating-point PCM\n");
fprintf(stderr, "-M | --mmap Use memory mapped IO to play audio\n");
}
@@ -250,6 +275,7 @@ int main(int argc, char **argv)
{ "channels", 'c', OPTPARSE_REQUIRED },
{ "rate", 'r', OPTPARSE_REQUIRED },
{ "bits", 'b', OPTPARSE_REQUIRED },
+ { "float", 'f', OPTPARSE_NONE },
{ "mmap", 'M', OPTPARSE_NONE },
{ "help", 'h', OPTPARSE_NONE },
{ 0, 0, 0 }
@@ -303,6 +329,18 @@ int main(int argc, char **argv)
case 'i':
cmd.filetype = opts.optarg;
break;
+ case 'b':
+ if (sscanf(opts.optarg, "%u", &cmd.bits) != 1) {
+ fprintf(stderr, "failed parsing bits per one sample '%s'\n", argv[1]);
+ return EXIT_FAILURE;
+ }
+ break;
+ case 'f':
+ cmd.is_float = true;
+ break;
+ case 'M':
+ cmd.flags |= PCM_MMAP;
+ break;
case 'h':
print_usage(argv[0]);
return EXIT_SUCCESS;
@@ -326,12 +364,13 @@ int main(int argc, char **argv)
return EXIT_FAILURE;
}
- /* TODO get parameters from context */
- printf("playing '%s': %u ch, %u hz, %u bit\n",
- cmd.filename,
- cmd.config.channels,
- cmd.config.rate,
- cmd.bits);
+ printf("playing '%s': %u ch, %u hz, %u-bit ", cmd.filename, cmd.config.channels,
+ cmd.config.rate, pcm_format_to_bits(cmd.config.format));
+ if (cmd.config.format == PCM_FORMAT_FLOAT_LE) {
+ printf("floating-point PCM\n");
+ } else {
+ printf("signed PCM\n");
+ }
if (play_sample(&ctx) < 0) {
ctx_free(&ctx);
@@ -396,6 +435,7 @@ int play_sample(struct ctx *ctx)
size_t buffer_size = 0;
size_t num_read = 0;
size_t remaining_data_size = ctx->chunk_header.sz;
+ size_t played_data_size = 0;
size_t read_size = 0;
const struct pcm_config *config = pcm_get_config(ctx->pcm);
@@ -418,15 +458,18 @@ int play_sample(struct ctx *ctx)
read_size = remaining_data_size > buffer_size ? buffer_size : remaining_data_size;
num_read = fread(buffer, 1, read_size, ctx->file);
if (num_read > 0) {
- if (pcm_writei(ctx->pcm, buffer,
- pcm_bytes_to_frames(ctx->pcm, num_read)) < 0) {
- fprintf(stderr, "error playing sample\n");
+ int written_frames = pcm_writei(ctx->pcm, buffer,
+ pcm_bytes_to_frames(ctx->pcm, num_read));
+ if (written_frames < 0) {
+ fprintf(stderr, "error playing sample. %s\n", pcm_get_error(ctx->pcm));
break;
}
remaining_data_size -= num_read;
+ played_data_size += pcm_frames_to_bytes(ctx->pcm, written_frames);
}
} while (!close && num_read > 0 && remaining_data_size > 0);
+ printf("Played %zu bytes. Remains %zu bytes.\n", played_data_size, remaining_data_size);
pcm_wait(ctx->pcm, -1);
free(buffer);