source: mainline/uspace/app/wavplay/main.c@ c65590a

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since c65590a was 508b0df1, checked in by Jiří Zárevúcky <jiri.zarevucky@…>, 7 years ago

Remove uspace <atomic.h>, use <stdatomic.h> instead

  • Property mode set to 100644
File size: 9.0 KB
Line 
1/*
2 * Copyright (c) 2012 Jan Vesely
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup wavplay
30 * @{
31 */
32/**
33 * @file PCM playback audio devices
34 */
35
36#include <assert.h>
37#include <stdatomic.h>
38#include <errno.h>
39#include <fibril_synch.h>
40#include <stdlib.h>
41#include <str_error.h>
42#include <stdio.h>
43#include <hound/client.h>
44#include <pcm/sample_format.h>
45#include <getopt.h>
46
47#include "dplay.h"
48#include "drec.h"
49#include "wave.h"
50
51#define READ_SIZE (32 * 1024)
52#define STREAM_BUFFER_SIZE (64 * 1024)
53
54/**
55 * Play audio file using a new stream on provided context.
56 * @param ctx Provided context.
57 * @param filename File to play.
58 * @return Error code.
59 */
60static errno_t hplay_ctx(hound_context_t *ctx, const char *filename)
61{
62 printf("Hound context playback: %s\n", filename);
63 FILE *source = fopen(filename, "rb");
64 if (!source) {
65 printf("Failed to open file %s\n", filename);
66 return EINVAL;
67 }
68
69 /* Read and parse WAV header */
70 wave_header_t header;
71 size_t read = fread(&header, sizeof(header), 1, source);
72 if (read != 1) {
73 printf("Failed to read WAV header: %zu\n", read);
74 fclose(source);
75 return EIO;
76 }
77 pcm_format_t format;
78 const char *error;
79 errno_t ret = wav_parse_header(&header, NULL, NULL, &format.channels,
80 &format.sampling_rate, &format.sample_format, &error);
81 if (ret != EOK) {
82 printf("Error parsing `%s' wav header: %s.\n", filename, error);
83 fclose(source);
84 return EINVAL;
85 }
86
87 printf("File `%s' format: %u channel(s), %uHz, %s.\n", filename,
88 format.channels, format.sampling_rate,
89 pcm_sample_format_str(format.sample_format));
90
91 /* Allocate buffer and create new context */
92 char *buffer = malloc(READ_SIZE);
93 if (!buffer) {
94 fclose(source);
95 return ENOMEM;
96 }
97 hound_stream_t *stream = hound_stream_create(ctx,
98 HOUND_STREAM_DRAIN_ON_EXIT, format, STREAM_BUFFER_SIZE);
99
100 /* Read and play */
101 while ((read = fread(buffer, sizeof(char), READ_SIZE, source)) > 0) {
102 ret = hound_stream_write(stream, buffer, read);
103 if (ret != EOK) {
104 printf("Failed to write to hound stream: %s\n",
105 str_error(ret));
106 break;
107 }
108 }
109
110 /* Cleanup */
111 free(buffer);
112 fclose(source);
113 return ret;
114}
115
116/**
117 * Play audio file via hound server.
118 * @param filename File to play.
119 * @return Error code
120 */
121static errno_t hplay(const char *filename)
122{
123 printf("Hound playback: %s\n", filename);
124 FILE *source = fopen(filename, "rb");
125 if (!source) {
126 printf("Failed to open file %s\n", filename);
127 return EINVAL;
128 }
129
130 /* Read and parse WAV header */
131 wave_header_t header;
132 size_t read = fread(&header, sizeof(header), 1, source);
133 if (read != 1) {
134 printf("Failed to read WAV header: %zu\n", read);
135 fclose(source);
136 return EIO;
137 }
138 pcm_format_t format;
139 const char *error;
140 errno_t ret = wav_parse_header(&header, NULL, NULL, &format.channels,
141 &format.sampling_rate, &format.sample_format, &error);
142 if (ret != EOK) {
143 printf("Error parsing `%s' wav header: %s.\n", filename, error);
144 fclose(source);
145 return EINVAL;
146 }
147 printf("File `%s' format: %u channel(s), %uHz, %s.\n", filename,
148 format.channels, format.sampling_rate,
149 pcm_sample_format_str(format.sample_format));
150
151 /* Connect new playback context */
152 hound_context_t *hound = hound_context_create_playback(filename,
153 format, STREAM_BUFFER_SIZE);
154 if (!hound) {
155 printf("Failed to create HOUND context\n");
156 fclose(source);
157 return ENOMEM;
158 }
159
160 ret = hound_context_connect_target(hound, HOUND_DEFAULT_TARGET);
161 if (ret != EOK) {
162 printf("Failed to connect to default target: %s\n",
163 str_error(ret));
164 hound_context_destroy(hound);
165 fclose(source);
166 return ret;
167 }
168
169 /* Read and play */
170 static char buffer[READ_SIZE];
171 while ((read = fread(buffer, sizeof(char), READ_SIZE, source)) > 0) {
172 ret = hound_write_main_stream(hound, buffer, read);
173 if (ret != EOK) {
174 printf("Failed to write to main context stream: %s\n",
175 str_error(ret));
176 break;
177 }
178 }
179
180 /* Cleanup */
181 hound_context_destroy(hound);
182 fclose(source);
183 return ret;
184}
185
186/**
187 * Helper structure for playback in separate fibrils
188 */
189typedef struct {
190 hound_context_t *ctx;
191 atomic_int *count;
192 const char *file;
193} fib_play_t;
194
195/**
196 * Fibril playback wrapper.
197 * @param arg Argument, pointer to playback helper structure.
198 * @return Error code.
199 */
200static errno_t play_wrapper(void *arg)
201{
202 assert(arg);
203 fib_play_t *p = arg;
204 const errno_t ret = hplay_ctx(p->ctx, p->file);
205 atomic_fetch_sub(p->count, 1);
206 free(arg);
207 return ret;
208}
209
210/**
211 * Array of supported commandline options
212 */
213static const struct option opts[] = {
214 { "device", required_argument, 0, 'd' },
215 { "parallel", no_argument, 0, 'p' },
216 { "record", no_argument, 0, 'r' },
217 { "help", no_argument, 0, 'h' },
218 { 0, 0, 0, 0 }
219};
220
221/**
222 * Print usage help.
223 * @param name Name of the program.
224 */
225static void print_help(const char *name)
226{
227 printf("Usage: %s [options] file [files...]\n", name);
228 printf("supported options:\n");
229 printf("\t -h, --help\t Print this help.\n");
230 printf("\t -r, --record\t Start recording instead of playback. "
231 "(Not implemented)\n");
232 printf("\t -d, --device\t Use specified device instead of the sound "
233 "service. Use location path or a special device `default'\n");
234 printf("\t -p, --parallel\t Play given files in parallel instead of "
235 "sequentially (does not work with -d).\n");
236}
237
238int main(int argc, char *argv[])
239{
240 const char *device = "default";
241 int idx = 0;
242 bool direct = false, record = false, parallel = false;
243 optind = 0;
244 int ret = 0;
245
246 /* Parse command line options */
247 while (ret != -1) {
248 ret = getopt_long(argc, argv, "d:prh", opts, &idx);
249 switch (ret) {
250 case 'd':
251 direct = true;
252 device = optarg;
253 break;
254 case 'r':
255 record = true;
256 break;
257 case 'p':
258 parallel = true;
259 break;
260 case 'h':
261 print_help(*argv);
262 return 0;
263 }
264 }
265
266 if (parallel && direct) {
267 printf("Parallel playback is available only if using sound "
268 "server (no -d)\n");
269 print_help(*argv);
270 return 1;
271 }
272
273 if (optind == argc) {
274 printf("Not enough arguments.\n");
275 print_help(*argv);
276 return 1;
277 }
278
279 /* Init parallel playback variables */
280 hound_context_t *hound_ctx = NULL;
281 atomic_int playcount = 0;
282
283 /* Init parallel playback context if necessary */
284 if (parallel) {
285 hound_ctx = hound_context_create_playback("wavplay",
286 AUDIO_FORMAT_DEFAULT, STREAM_BUFFER_SIZE);
287 if (!hound_ctx) {
288 printf("Failed to create global hound context\n");
289 return 1;
290 }
291 const errno_t ret = hound_context_connect_target(hound_ctx,
292 HOUND_DEFAULT_TARGET);
293 if (ret != EOK) {
294 printf("Failed to connect hound context to default "
295 "target.\n");
296 hound_context_destroy(hound_ctx);
297 return 1;
298 }
299 }
300
301 /* play or record all files */
302 for (int i = optind; i < argc; ++i) {
303 const char *file = argv[i];
304
305 printf("%s (%d/%d) %s\n", record ? "Recording" : "Playing",
306 i - optind + 1, argc - optind, file);
307 if (record) {
308 if (direct) {
309 drecord(device, file);
310 continue;
311 } else {
312 printf("Indirect recording is not supported "
313 "yet.\n");
314 break;
315 }
316 }
317
318 if (direct) {
319 dplay(device, file);
320 } else {
321 if (parallel) {
322 /* Start new fibril for parallel playback */
323 fib_play_t *data = malloc(sizeof(fib_play_t));
324 if (!data) {
325 printf("Playback of %s failed.\n",
326 file);
327 continue;
328 }
329 data->file = file;
330 data->count = &playcount;
331 data->ctx = hound_ctx;
332 fid_t fid = fibril_create(play_wrapper, data);
333 atomic_fetch_add(&playcount, 1);
334 fibril_add_ready(fid);
335 } else {
336 hplay(file);
337 }
338 }
339 }
340
341 /* Wait for all fibrils to finish */
342 while (atomic_load(&playcount) > 0)
343 fibril_usleep(1000000);
344
345 /* Destroy parallel playback context, if initialized */
346 if (hound_ctx)
347 hound_context_destroy(hound_ctx);
348 return 0;
349}
350/**
351 * @}
352 */
Note: See TracBrowser for help on using the repository browser.