CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
orangepi-xunlong

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place. Commercial Alternative to JupyterHub.

GitHub Repository: orangepi-xunlong/orangepi-build
Path: blob/next/external/cache/sources/tinyalsa_hub/tinyplay_hub.c
Views: 3959
1
/* tinyplay.c
2
**
3
** Copyright 2011, The Android Open Source Project
4
**
5
** Redistribution and use in source and binary forms, with or without
6
** modification, are permitted provided that the following conditions are met:
7
** * Redistributions of source code must retain the above copyright
8
** notice, this list of conditions and the following disclaimer.
9
** * Redistributions in binary form must reproduce the above copyright
10
** notice, this list of conditions and the following disclaimer in the
11
** documentation and/or other materials provided with the distribution.
12
** * Neither the name of The Android Open Source Project nor the names of
13
** its contributors may be used to endorse or promote products derived
14
** from this software without specific prior written permission.
15
**
16
** THIS SOFTWARE IS PROVIDED BY The Android Open Source Project ``AS IS'' AND
17
** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18
** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19
** ARE DISCLAIMED. IN NO EVENT SHALL The Android Open Source Project BE LIABLE
20
** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21
** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22
** SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
23
** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24
** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25
** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
26
** DAMAGE.
27
*/
28
29
#include <tinyalsa/asoundlib.h>
30
#include <stdio.h>
31
#include <stdlib.h>
32
#include <stdint.h>
33
#include <string.h>
34
#include <signal.h>
35
#include <sys/time.h>
36
37
#define ID_RIFF 0x46464952
38
#define ID_WAVE 0x45564157
39
#define ID_FMT 0x20746d66
40
#define ID_DATA 0x61746164
41
42
struct riff_wave_header {
43
uint32_t riff_id;
44
uint32_t riff_sz;
45
uint32_t wave_id;
46
};
47
48
struct chunk_header {
49
uint32_t id;
50
uint32_t sz;
51
};
52
53
struct chunk_fmt {
54
uint16_t audio_format;
55
uint16_t num_channels;
56
uint32_t sample_rate;
57
uint32_t byte_rate;
58
uint16_t block_align;
59
uint16_t bits_per_sample;
60
};
61
62
static int close = 0;
63
static unsigned int loop_num = 1;
64
static unsigned int loop_minutes = 0;
65
66
void play_sample(FILE *file, unsigned int card, unsigned int device, unsigned int channels,
67
unsigned int rate, unsigned int bits, unsigned int period_size,
68
unsigned int period_count);
69
70
void stream_close(int sig)
71
{
72
/* allow the stream to be closed gracefully */
73
signal(sig, SIG_IGN);
74
close = 1;
75
}
76
77
int main(int argc, char **argv)
78
{
79
FILE *file;
80
struct riff_wave_header riff_wave_header;
81
struct chunk_header chunk_header;
82
struct chunk_fmt chunk_fmt;
83
unsigned int device = 0;
84
unsigned int card = 0;
85
unsigned int period_size = 1024;
86
unsigned int period_count = 4;
87
char *filename;
88
int more_chunks = 1;
89
90
if (argc < 2) {
91
fprintf(stderr, "Usage: %s file.wav [-D card] [-d device] "
92
"[-m loop_minutes] [-i loop_num] "
93
"[-p period_size] [-n period_count] \n", argv[0]);
94
return 1;
95
}
96
97
filename = argv[1];
98
file = fopen(filename, "rb");
99
if (!file) {
100
fprintf(stderr, "Unable to open file '%s'\n", filename);
101
return 1;
102
}
103
104
fread(&riff_wave_header, sizeof(riff_wave_header), 1, file);
105
if ((riff_wave_header.riff_id != ID_RIFF) ||
106
(riff_wave_header.wave_id != ID_WAVE)) {
107
fprintf(stderr, "Error: '%s' is not a riff/wave file\n", filename);
108
fclose(file);
109
return 1;
110
}
111
112
do {
113
fread(&chunk_header, sizeof(chunk_header), 1, file);
114
115
switch (chunk_header.id) {
116
case ID_FMT:
117
fread(&chunk_fmt, sizeof(chunk_fmt), 1, file);
118
/* If the format header is larger, skip the rest */
119
if (chunk_header.sz > sizeof(chunk_fmt))
120
fseek(file, chunk_header.sz - sizeof(chunk_fmt), SEEK_CUR);
121
break;
122
case ID_DATA:
123
/* Stop looking for chunks */
124
more_chunks = 0;
125
break;
126
default:
127
/* Unknown chunk, skip bytes */
128
fseek(file, chunk_header.sz, SEEK_CUR);
129
}
130
} while (more_chunks);
131
132
/* parse command line arguments */
133
argv += 2;
134
while (*argv) {
135
if (strcmp(*argv, "-d") == 0) {
136
argv++;
137
if (*argv)
138
device = atoi(*argv);
139
}
140
if (strcmp(*argv, "-p") == 0) {
141
argv++;
142
if (*argv)
143
period_size = atoi(*argv);
144
}
145
if (strcmp(*argv, "-n") == 0) {
146
argv++;
147
if (*argv)
148
period_count = atoi(*argv);
149
}
150
if (strcmp(*argv, "-D") == 0) {
151
argv++;
152
if (*argv)
153
card = atoi(*argv);
154
}
155
if (strcmp(*argv, "-m") == 0) {
156
argv++;
157
if (*argv)
158
loop_minutes = atoi(*argv);
159
}
160
if (strcmp(*argv, "-i") == 0) {
161
argv++;
162
if (*argv)
163
loop_num = atoi(*argv);
164
}
165
if (*argv)
166
argv++;
167
}
168
169
play_sample(file, card, device, chunk_fmt.num_channels, chunk_fmt.sample_rate,
170
chunk_fmt.bits_per_sample, period_size, period_count);
171
172
fclose(file);
173
174
return 0;
175
}
176
177
int check_param(struct pcm_params *params, unsigned int param, unsigned int value,
178
char *param_name, char *param_unit)
179
{
180
unsigned int min;
181
unsigned int max;
182
int is_within_bounds = 1;
183
184
min = pcm_params_get_min(params, param);
185
if (value < min) {
186
fprintf(stderr, "%s is %u%s, device only supports >= %u%s\n", param_name, value,
187
param_unit, min, param_unit);
188
is_within_bounds = 0;
189
}
190
191
max = pcm_params_get_max(params, param);
192
if (value > max) {
193
fprintf(stderr, "%s is %u%s, device only supports <= %u%s\n", param_name, value,
194
param_unit, max, param_unit);
195
is_within_bounds = 0;
196
}
197
198
return is_within_bounds;
199
}
200
201
int sample_is_playable(unsigned int card, unsigned int device, unsigned int channels,
202
unsigned int rate, unsigned int bits, unsigned int period_size,
203
unsigned int period_count)
204
{
205
struct pcm_params *params;
206
int can_play;
207
208
params = pcm_params_get(card, device, PCM_OUT);
209
if (params == NULL) {
210
fprintf(stderr, "Unable to open PCM device %u.\n", device);
211
return 0;
212
}
213
214
can_play = check_param(params, PCM_PARAM_RATE, rate, "Sample rate", "Hz");
215
can_play &= check_param(params, PCM_PARAM_CHANNELS, channels, "Sample", " channels");
216
can_play &= check_param(params, PCM_PARAM_SAMPLE_BITS, bits, "Bitrate", " bits");
217
can_play &= check_param(params, PCM_PARAM_PERIOD_SIZE, period_size, "Period size", "Hz");
218
can_play &= check_param(params, PCM_PARAM_PERIODS, period_count, "Period count", "Hz");
219
220
pcm_params_free(params);
221
222
return can_play;
223
}
224
225
void play_sample(FILE *file, unsigned int card, unsigned int device, unsigned int channels,
226
unsigned int rate, unsigned int bits, unsigned int period_size,
227
unsigned int period_count)
228
{
229
struct pcm_config config;
230
struct pcm *pcm;
231
struct pcm *pcm1;
232
char *buffer;
233
int size;
234
int num_read;
235
struct timeval tv;
236
struct timezone tz;
237
long sec_start = 0;
238
long sec_end = 0;
239
int loop_count = 0;
240
241
config.channels = channels;
242
config.rate = rate;
243
config.period_size = period_size;
244
config.period_count = period_count;
245
if (bits == 32)
246
config.format = PCM_FORMAT_S32_LE;
247
else if (bits == 16)
248
config.format = PCM_FORMAT_S16_LE;
249
else if (bits == 24)
250
config.format = PCM_FORMAT_S24_LE;
251
config.start_threshold = 0;
252
config.stop_threshold = 0;
253
config.silence_threshold = 0;
254
255
if (!sample_is_playable(card, device, channels, rate, bits, period_size, period_count)) {
256
return;
257
}
258
259
pcm = pcm_open(card, device, PCM_OUT, &config);
260
if (!pcm || !pcm_is_ready(pcm)) {
261
fprintf(stderr, "Unable to open PCM device %u (%s)\n",
262
device, pcm_get_error(pcm));
263
return;
264
}
265
266
if (device == 0)
267
pcm1 = pcm_open(1, 0, PCM_OUT, &config);
268
else if (device == 1)
269
pcm1 = pcm_open(2, 0, PCM_OUT, &config);
270
else if (device == 2)
271
pcm1 = pcm_open(4, 0, PCM_OUT, &config);
272
else
273
printf("error: device is error");
274
if (!pcm1 || !pcm_is_ready(pcm1)) {
275
fprintf(stderr, "Unable to open PCM device %u (%s)\n",
276
device, pcm_get_error(pcm1));
277
return;
278
}
279
280
pcm_prepare(pcm);
281
pcm_prepare(pcm1);
282
283
size = pcm_frames_to_bytes(pcm, pcm_get_buffer_size(pcm));
284
buffer = malloc(size);
285
if (!buffer) {
286
fprintf(stderr, "Unable to allocate %d bytes\n", size);
287
free(buffer);
288
pcm_close(pcm);
289
pcm_close(pcm1);
290
return;
291
}
292
293
printf("Playing sample: %u ch, %u hz, %u bit\n", channels, rate, bits);
294
295
/* catch ctrl-c to shutdown cleanly */
296
signal(SIGINT, stream_close);
297
298
gettimeofday(&tv, &tz);
299
sec_start = tv.tv_sec;
300
301
printf("start --> loop_minutes = %d; loop_num = %d\n",
302
loop_minutes, loop_num);
303
304
do {
305
num_read = fread(buffer, 1, size, file);
306
if (num_read > 0) {
307
if (pcm_write(pcm, buffer, num_read)) {
308
fprintf(stderr, "Error playing sample\n");
309
break;
310
}
311
if (feof(file)) {
312
printf("--> loop_count = %d\n", ++loop_count);
313
fseek(file, 0L, SEEK_SET);
314
}
315
if (loop_minutes == 0) {
316
if (loop_count >= loop_num)
317
break;
318
} else {
319
gettimeofday(&tv, &tz);
320
sec_end = tv.tv_sec;
321
if ((sec_end - sec_start) > (loop_minutes * 60)) {
322
printf("End loop_minutes: %ld\n", (sec_end - sec_start)/60);
323
break;
324
}
325
}
326
}
327
} while (!close && num_read > 0);
328
329
free(buffer);
330
pcm_close(pcm);
331
pcm_close(pcm1);
332
}
333
334
335