Skip to content

Commit 9a55882

Browse files
borinearkq
authored andcommitted
aplay: Refactor code related to ALSA PCM
1 parent 4a661e8 commit 9a55882

File tree

4 files changed

+199
-92
lines changed

4 files changed

+199
-92
lines changed

test/test-utils-aplay.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -291,14 +291,14 @@ CK_START_TEST(test_play_dbus_signals) {
291291
"Used configuration for 12:34:56:78:9A:BC"), NULL);
292292
/* check proper sample rate for CVSD codec */
293293
ck_assert_ptr_ne(strstr(output,
294-
"Sample rate: 8000 Hz"), NULL);
294+
"ALSA PCM sample rate: 8000 Hz"), NULL);
295295

296296
#if ENABLE_MSBC
297297
ck_assert_ptr_ne(strstr(output,
298298
"Used configuration for 12:34:56:78:9A:BC"), NULL);
299299
/* check proper sample rate for mSBC codec */
300300
ck_assert_ptr_ne(strstr(output,
301-
"Sample rate: 16000 Hz"), NULL);
301+
"ALSA PCM sample rate: 16000 Hz"), NULL);
302302
#endif
303303

304304
spawn_close(&sp_ba_aplay, NULL);

utils/aplay/alsa-pcm.c

Lines changed: 99 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/*
22
* BlueALSA - alsa-pcm.c
3-
* Copyright (c) 2016-2023 Arkadiusz Bokowy
3+
* Copyright (c) 2016-2025 Arkadiusz Bokowy
44
*
55
* This file is a part of bluez-alsa.
66
*
@@ -14,55 +14,64 @@
1414
#include <stdlib.h>
1515
#include <string.h>
1616

17-
static int alsa_pcm_set_hw_params(snd_pcm_t *pcm, snd_pcm_format_t format, int channels,
18-
int rate, unsigned int *buffer_time, unsigned int *period_time, char **msg) {
17+
#include "shared/log.h"
18+
19+
static int alsa_pcm_set_hw_params(
20+
struct alsa_pcm *pcm,
21+
snd_pcm_format_t format,
22+
unsigned int channels,
23+
unsigned int rate,
24+
unsigned int *buffer_time,
25+
unsigned int *period_time,
26+
char **msg) {
1927

2028
const snd_pcm_access_t access = SND_PCM_ACCESS_RW_INTERLEAVED;
29+
snd_pcm_t *snd_pcm = pcm->pcm;
2130
snd_pcm_hw_params_t *params;
2231
char buf[256];
2332
int dir;
2433
int err;
2534

2635
snd_pcm_hw_params_alloca(&params);
2736

28-
if ((err = snd_pcm_hw_params_any(pcm, params)) < 0) {
37+
if ((err = snd_pcm_hw_params_any(snd_pcm, params)) < 0) {
2938
snprintf(buf, sizeof(buf), "Set all possible ranges: %s", snd_strerror(err));
3039
goto fail;
3140
}
3241

33-
if ((err = snd_pcm_hw_params_set_access(pcm, params, access)) != 0) {
42+
if ((err = snd_pcm_hw_params_set_access(snd_pcm, params, access)) != 0) {
3443
snprintf(buf, sizeof(buf), "Set assess type: %s: %s", snd_strerror(err), snd_pcm_access_name(access));
3544
goto fail;
3645
}
3746

38-
if ((err = snd_pcm_hw_params_set_format(pcm, params, format)) != 0) {
47+
if ((err = snd_pcm_hw_params_set_format(snd_pcm, params, format)) != 0) {
3948
snprintf(buf, sizeof(buf), "Set format: %s: %s", snd_strerror(err), snd_pcm_format_name(format));
4049
goto fail;
4150
}
4251

43-
if ((err = snd_pcm_hw_params_set_channels(pcm, params, channels)) != 0) {
52+
if ((err = snd_pcm_hw_params_set_channels(snd_pcm, params, channels)) != 0) {
4453
snprintf(buf, sizeof(buf), "Set channels: %s: %d", snd_strerror(err), channels);
4554
goto fail;
4655
}
4756

48-
if ((err = snd_pcm_hw_params_set_rate(pcm, params, rate, 0)) != 0) {
57+
if ((err = snd_pcm_hw_params_set_rate(snd_pcm, params, rate, 0)) != 0) {
4958
snprintf(buf, sizeof(buf), "Set sample rate: %s: %d", snd_strerror(err), rate);
5059
goto fail;
5160
}
5261

5362
dir = 0;
54-
if ((err = snd_pcm_hw_params_set_period_time_near(pcm, params, period_time, &dir)) != 0) {
63+
if ((err = snd_pcm_hw_params_set_period_time_near(snd_pcm, params, period_time, &dir)) != 0) {
5564
snprintf(buf, sizeof(buf), "Set period time: %s: %u", snd_strerror(err), *period_time);
5665
goto fail;
5766
}
5867

5968
dir = 0;
60-
if ((err = snd_pcm_hw_params_set_buffer_time_near(pcm, params, buffer_time, &dir)) != 0) {
69+
if ((err = snd_pcm_hw_params_set_buffer_time_near(snd_pcm, params, buffer_time, &dir)) != 0) {
6170
snprintf(buf, sizeof(buf), "Set buffer time: %s: %u", snd_strerror(err), *buffer_time);
6271
goto fail;
6372
}
6473

65-
if ((err = snd_pcm_hw_params(pcm, params)) != 0) {
74+
if ((err = snd_pcm_hw_params(snd_pcm, params)) != 0) {
6675
snprintf(buf, sizeof(buf), "%s", snd_strerror(err));
6776
goto fail;
6877
}
@@ -75,36 +84,40 @@ static int alsa_pcm_set_hw_params(snd_pcm_t *pcm, snd_pcm_format_t format, int c
7584
return err;
7685
}
7786

78-
static int alsa_pcm_set_sw_params(snd_pcm_t *pcm, snd_pcm_uframes_t buffer_size,
79-
snd_pcm_uframes_t period_size, char **msg) {
87+
static int alsa_pcm_set_sw_params(
88+
struct alsa_pcm *pcm,
89+
snd_pcm_uframes_t buffer_size,
90+
snd_pcm_uframes_t period_size,
91+
char **msg) {
8092

93+
snd_pcm_t *snd_pcm = pcm->pcm;
8194
snd_pcm_sw_params_t *params;
8295
char buf[256];
8396
int err;
8497

8598
snd_pcm_sw_params_alloca(&params);
8699

87-
if ((err = snd_pcm_sw_params_current(pcm, params)) != 0) {
100+
if ((err = snd_pcm_sw_params_current(snd_pcm, params)) != 0) {
88101
snprintf(buf, sizeof(buf), "Get current params: %s", snd_strerror(err));
89102
goto fail;
90103
}
91104

92105
/* Start the transfer when the buffer is half full - this allows
93106
* spare capacity to accommodate bursts and short breaks in the
94107
* Bluetooth stream. */
95-
snd_pcm_uframes_t threshold = buffer_size / 2;
96-
if ((err = snd_pcm_sw_params_set_start_threshold(pcm, params, threshold)) != 0) {
108+
snd_pcm_uframes_t threshold = pcm->start_threshold = buffer_size / 2;
109+
if ((err = snd_pcm_sw_params_set_start_threshold(snd_pcm, params, threshold)) != 0) {
97110
snprintf(buf, sizeof(buf), "Set start threshold: %s: %lu", snd_strerror(err), threshold);
98111
goto fail;
99112
}
100113

101114
/* Allow the transfer when at least period_size samples can be processed. */
102-
if ((err = snd_pcm_sw_params_set_avail_min(pcm, params, period_size)) != 0) {
115+
if ((err = snd_pcm_sw_params_set_avail_min(snd_pcm, params, period_size)) != 0) {
103116
snprintf(buf, sizeof(buf), "Set avail min: %s: %lu", snd_strerror(err), period_size);
104117
goto fail;
105118
}
106119

107-
if ((err = snd_pcm_sw_params(pcm, params)) != 0) {
120+
if ((err = snd_pcm_sw_params(snd_pcm, params)) != 0) {
108121
snprintf(buf, sizeof(buf), "%s", snd_strerror(err));
109122
goto fail;
110123
}
@@ -117,58 +130,112 @@ static int alsa_pcm_set_sw_params(snd_pcm_t *pcm, snd_pcm_uframes_t buffer_size,
117130
return err;
118131
}
119132

120-
int alsa_pcm_open(snd_pcm_t **pcm, const char *name,
121-
snd_pcm_format_t format, int channels, int rate,
122-
unsigned int *buffer_time, unsigned int *period_time,
133+
void alsa_pcm_init(struct alsa_pcm *pcm) {
134+
pcm->pcm = NULL;
135+
}
136+
137+
int alsa_pcm_open(
138+
struct alsa_pcm *pcm,
139+
const char *name,
140+
snd_pcm_format_t format,
141+
unsigned int channels,
142+
unsigned int rate,
143+
unsigned int buffer_time,
144+
unsigned int period_time,
145+
int flags,
123146
char **msg) {
124147

125-
snd_pcm_t *_pcm = NULL;
126148
char *tmp = NULL;
127149
char buf[256];
128150
int err;
129151

130-
if ((err = snd_pcm_open(&_pcm, name, SND_PCM_STREAM_PLAYBACK, 0)) != 0) {
152+
if ((err = snd_pcm_open(&pcm->pcm, name, SND_PCM_STREAM_PLAYBACK, flags)) != 0) {
131153
snprintf(buf, sizeof(buf), "Open PCM: %s", snd_strerror(err));
132154
goto fail;
133155
}
134156

135-
if ((err = alsa_pcm_set_hw_params(_pcm, format, channels, rate, buffer_time, period_time, &tmp)) != 0) {
157+
unsigned int actual_buffer_time = buffer_time;
158+
unsigned int actual_period_time = period_time;
159+
if ((err = alsa_pcm_set_hw_params(pcm, format, channels, rate,
160+
&actual_buffer_time, &actual_period_time, &tmp)) != 0) {
136161
snprintf(buf, sizeof(buf), "Set HW params: %s", tmp);
137162
goto fail;
138163
}
139164

140165
snd_pcm_uframes_t buffer_size, period_size;
141-
if ((err = snd_pcm_get_params(_pcm, &buffer_size, &period_size)) != 0) {
166+
if ((err = snd_pcm_get_params(pcm->pcm, &buffer_size, &period_size)) != 0) {
142167
snprintf(buf, sizeof(buf), "Get params: %s", snd_strerror(err));
143168
goto fail;
144169
}
145170

146-
if ((err = alsa_pcm_set_sw_params(_pcm, buffer_size, period_size, &tmp)) != 0) {
171+
if ((err = alsa_pcm_set_sw_params(pcm, buffer_size, period_size, &tmp)) != 0) {
147172
snprintf(buf, sizeof(buf), "Set SW params: %s", tmp);
148173
goto fail;
149174
}
150175

151-
if ((err = snd_pcm_prepare(_pcm)) != 0) {
176+
if ((err = snd_pcm_prepare(pcm->pcm)) != 0) {
152177
snprintf(buf, sizeof(buf), "Prepare: %s", snd_strerror(err));
153178
goto fail;
154179
}
155180

156-
*pcm = _pcm;
181+
pcm->format = format;
182+
pcm->channels = channels;
183+
pcm->sample_size = snd_pcm_format_size(format, 1);
184+
pcm->frame_size = snd_pcm_format_size(format, channels);
185+
pcm->rate = rate;
186+
pcm->buffer_time = actual_buffer_time;
187+
pcm->period_time = actual_period_time;
188+
pcm->buffer_frames = buffer_size;
189+
pcm->period_frames = period_size;
190+
157191
return 0;
158192

159193
fail:
160-
if (_pcm != NULL)
161-
snd_pcm_close(_pcm);
194+
alsa_pcm_close(pcm);
162195
if (msg != NULL)
163196
*msg = strdup(buf);
164197
if (tmp != NULL)
165198
free(tmp);
166199
return err;
200+
201+
}
202+
203+
void alsa_pcm_close(struct alsa_pcm *pcm) {
204+
if (pcm->pcm != NULL)
205+
snd_pcm_close(pcm->pcm);
206+
pcm->pcm = NULL;
207+
}
208+
209+
int alsa_pcm_write(struct alsa_pcm *pcm, ffb_t *buffer) {
210+
211+
size_t samples = ffb_len_out(buffer);
212+
snd_pcm_sframes_t frames;
213+
214+
for (;;) {
215+
frames = samples / pcm->channels;
216+
if ((frames = snd_pcm_writei(pcm->pcm, buffer->data, frames)) > 0)
217+
break;
218+
switch (-frames) {
219+
case EINTR:
220+
continue;
221+
case EPIPE:
222+
debug("ALSA playback PCM underrun");
223+
snd_pcm_prepare(pcm->pcm);
224+
continue;
225+
default:
226+
error("ALSA playback PCM write error: %s", snd_strerror(frames));
227+
return -1;
228+
}
229+
}
230+
231+
/* Move leftovers to the beginning of buffer and reposition tail. */
232+
ffb_shift(buffer, frames * pcm->channels);
233+
return 0;
167234
}
168235

169-
void alsa_pcm_dump(snd_pcm_t *pcm, FILE *fp) {
236+
void alsa_pcm_dump(const struct alsa_pcm *pcm, FILE *fp) {
170237
snd_output_t *out;
171238
snd_output_stdio_attach(&out, fp, 0);
172-
snd_pcm_dump(pcm, out);
239+
snd_pcm_dump(pcm->pcm, out);
173240
snd_output_close(out);
174241
}

utils/aplay/alsa-pcm.h

Lines changed: 73 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/*
22
* BlueALSA - alsa-pcm.h
3-
* Copyright (c) 2016-2023 Arkadiusz Bokowy
3+
* Copyright (c) 2016-2025 Arkadiusz Bokowy
44
*
55
* This file is a part of bluez-alsa.
66
*
@@ -12,15 +12,83 @@
1212
#ifndef BLUEALSA_APLAY_ALSAPCM_H_
1313
#define BLUEALSA_APLAY_ALSAPCM_H_
1414

15+
#include <stdbool.h>
1516
#include <stdio.h>
1617

1718
#include <alsa/asoundlib.h>
1819

19-
int alsa_pcm_open(snd_pcm_t **pcm, const char *name,
20-
snd_pcm_format_t format, int channels, int rate,
21-
unsigned int *buffer_time, unsigned int *period_time,
20+
#include "shared/ffb.h"
21+
22+
struct alsa_pcm {
23+
24+
/* The ALSA device handle. */
25+
snd_pcm_t *pcm;
26+
27+
snd_pcm_format_t format;
28+
/* The hardware parameters actually set within the ALSA device. Note that
29+
* these may differ from the values requested by the application. */
30+
unsigned int channels;
31+
unsigned int rate;
32+
unsigned int buffer_time;
33+
unsigned int period_time;
34+
snd_pcm_uframes_t buffer_frames;
35+
snd_pcm_uframes_t period_frames;
36+
37+
/* The number of frames that must be written to trigger
38+
* automatic start of the ALSA device. */
39+
snd_pcm_uframes_t start_threshold;
40+
41+
/* The number of bytes in 1 sample. */
42+
size_t sample_size;
43+
/* The number of bytes in 1 frame. */
44+
size_t frame_size;
45+
46+
/* The internal delay of the ALSA device immediately
47+
* after the last write. */
48+
size_t delay;
49+
50+
};
51+
52+
void alsa_pcm_init(
53+
struct alsa_pcm *pcm);
54+
55+
int alsa_pcm_open(
56+
struct alsa_pcm *pcm,
57+
const char *name,
58+
snd_pcm_format_t format,
59+
unsigned int channels,
60+
unsigned int rate,
61+
unsigned int buffer_time,
62+
unsigned int period_time,
63+
int flags,
2264
char **msg);
2365

24-
void alsa_pcm_dump(snd_pcm_t *pcm, FILE *fp);
66+
void alsa_pcm_close(
67+
struct alsa_pcm *pcm);
68+
69+
inline static bool alsa_pcm_is_open(
70+
const struct alsa_pcm *pcm) {
71+
return pcm->pcm != NULL;
72+
}
73+
74+
inline static int alsa_pcm_delay(
75+
const struct alsa_pcm *pcm,
76+
snd_pcm_sframes_t *delay) {
77+
return snd_pcm_delay(pcm->pcm, delay);
78+
}
79+
80+
inline static ssize_t alsa_pcm_frames_to_bytes(
81+
const struct alsa_pcm *pcm,
82+
snd_pcm_sframes_t frames) {
83+
return snd_pcm_frames_to_bytes(pcm->pcm, frames);
84+
}
85+
86+
int alsa_pcm_write(
87+
struct alsa_pcm *pcm,
88+
ffb_t *buffer);
89+
90+
void alsa_pcm_dump(
91+
const struct alsa_pcm *pcm,
92+
FILE *fp);
2593

2694
#endif

0 commit comments

Comments
 (0)