1
1
/*
2
2
* BlueALSA - alsa-pcm.c
3
- * Copyright (c) 2016-2023 Arkadiusz Bokowy
3
+ * Copyright (c) 2016-2025 Arkadiusz Bokowy
4
4
*
5
5
* This file is a part of bluez-alsa.
6
6
*
14
14
#include <stdlib.h>
15
15
#include <string.h>
16
16
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 ) {
19
27
20
28
const snd_pcm_access_t access = SND_PCM_ACCESS_RW_INTERLEAVED ;
29
+ snd_pcm_t * snd_pcm = pcm -> pcm ;
21
30
snd_pcm_hw_params_t * params ;
22
31
char buf [256 ];
23
32
int dir ;
24
33
int err ;
25
34
26
35
snd_pcm_hw_params_alloca (& params );
27
36
28
- if ((err = snd_pcm_hw_params_any (pcm , params )) < 0 ) {
37
+ if ((err = snd_pcm_hw_params_any (snd_pcm , params )) < 0 ) {
29
38
snprintf (buf , sizeof (buf ), "Set all possible ranges: %s" , snd_strerror (err ));
30
39
goto fail ;
31
40
}
32
41
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 ) {
34
43
snprintf (buf , sizeof (buf ), "Set assess type: %s: %s" , snd_strerror (err ), snd_pcm_access_name (access ));
35
44
goto fail ;
36
45
}
37
46
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 ) {
39
48
snprintf (buf , sizeof (buf ), "Set format: %s: %s" , snd_strerror (err ), snd_pcm_format_name (format ));
40
49
goto fail ;
41
50
}
42
51
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 ) {
44
53
snprintf (buf , sizeof (buf ), "Set channels: %s: %d" , snd_strerror (err ), channels );
45
54
goto fail ;
46
55
}
47
56
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 ) {
49
58
snprintf (buf , sizeof (buf ), "Set sample rate: %s: %d" , snd_strerror (err ), rate );
50
59
goto fail ;
51
60
}
52
61
53
62
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 ) {
55
64
snprintf (buf , sizeof (buf ), "Set period time: %s: %u" , snd_strerror (err ), * period_time );
56
65
goto fail ;
57
66
}
58
67
59
68
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 ) {
61
70
snprintf (buf , sizeof (buf ), "Set buffer time: %s: %u" , snd_strerror (err ), * buffer_time );
62
71
goto fail ;
63
72
}
64
73
65
- if ((err = snd_pcm_hw_params (pcm , params )) != 0 ) {
74
+ if ((err = snd_pcm_hw_params (snd_pcm , params )) != 0 ) {
66
75
snprintf (buf , sizeof (buf ), "%s" , snd_strerror (err ));
67
76
goto fail ;
68
77
}
@@ -75,36 +84,40 @@ static int alsa_pcm_set_hw_params(snd_pcm_t *pcm, snd_pcm_format_t format, int c
75
84
return err ;
76
85
}
77
86
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 ) {
80
92
93
+ snd_pcm_t * snd_pcm = pcm -> pcm ;
81
94
snd_pcm_sw_params_t * params ;
82
95
char buf [256 ];
83
96
int err ;
84
97
85
98
snd_pcm_sw_params_alloca (& params );
86
99
87
- if ((err = snd_pcm_sw_params_current (pcm , params )) != 0 ) {
100
+ if ((err = snd_pcm_sw_params_current (snd_pcm , params )) != 0 ) {
88
101
snprintf (buf , sizeof (buf ), "Get current params: %s" , snd_strerror (err ));
89
102
goto fail ;
90
103
}
91
104
92
105
/* Start the transfer when the buffer is half full - this allows
93
106
* spare capacity to accommodate bursts and short breaks in the
94
107
* 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 ) {
97
110
snprintf (buf , sizeof (buf ), "Set start threshold: %s: %lu" , snd_strerror (err ), threshold );
98
111
goto fail ;
99
112
}
100
113
101
114
/* 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 ) {
103
116
snprintf (buf , sizeof (buf ), "Set avail min: %s: %lu" , snd_strerror (err ), period_size );
104
117
goto fail ;
105
118
}
106
119
107
- if ((err = snd_pcm_sw_params (pcm , params )) != 0 ) {
120
+ if ((err = snd_pcm_sw_params (snd_pcm , params )) != 0 ) {
108
121
snprintf (buf , sizeof (buf ), "%s" , snd_strerror (err ));
109
122
goto fail ;
110
123
}
@@ -117,58 +130,112 @@ static int alsa_pcm_set_sw_params(snd_pcm_t *pcm, snd_pcm_uframes_t buffer_size,
117
130
return err ;
118
131
}
119
132
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 ,
123
146
char * * msg ) {
124
147
125
- snd_pcm_t * _pcm = NULL ;
126
148
char * tmp = NULL ;
127
149
char buf [256 ];
128
150
int err ;
129
151
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 ) {
131
153
snprintf (buf , sizeof (buf ), "Open PCM: %s" , snd_strerror (err ));
132
154
goto fail ;
133
155
}
134
156
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 ) {
136
161
snprintf (buf , sizeof (buf ), "Set HW params: %s" , tmp );
137
162
goto fail ;
138
163
}
139
164
140
165
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 ) {
142
167
snprintf (buf , sizeof (buf ), "Get params: %s" , snd_strerror (err ));
143
168
goto fail ;
144
169
}
145
170
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 ) {
147
172
snprintf (buf , sizeof (buf ), "Set SW params: %s" , tmp );
148
173
goto fail ;
149
174
}
150
175
151
- if ((err = snd_pcm_prepare (_pcm )) != 0 ) {
176
+ if ((err = snd_pcm_prepare (pcm -> pcm )) != 0 ) {
152
177
snprintf (buf , sizeof (buf ), "Prepare: %s" , snd_strerror (err ));
153
178
goto fail ;
154
179
}
155
180
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
+
157
191
return 0 ;
158
192
159
193
fail :
160
- if (_pcm != NULL )
161
- snd_pcm_close (_pcm );
194
+ alsa_pcm_close (pcm );
162
195
if (msg != NULL )
163
196
* msg = strdup (buf );
164
197
if (tmp != NULL )
165
198
free (tmp );
166
199
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 ;
167
234
}
168
235
169
- void alsa_pcm_dump (snd_pcm_t * pcm , FILE * fp ) {
236
+ void alsa_pcm_dump (const struct alsa_pcm * pcm , FILE * fp ) {
170
237
snd_output_t * out ;
171
238
snd_output_stdio_attach (& out , fp , 0 );
172
- snd_pcm_dump (pcm , out );
239
+ snd_pcm_dump (pcm -> pcm , out );
173
240
snd_output_close (out );
174
241
}
0 commit comments