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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since cb500a2 was a8e87da, checked in by Jan Vesely <jano.vesely@…>, 12 years ago

wavplay: comments

  • Property mode set to 100644
File size: 8.5 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 <errno.h>
38#include <fibril_synch.h>
39#include <malloc.h>
40#include <str_error.h>
41#include <stdio.h>
42#include <hound/client.h>
43#include <pcm/sample_format.h>
44#include <getopt.h>
45
46#include "dplay.h"
47#include "drec.h"
48#include "wave.h"
49
50#define READ_SIZE (32 * 1024)
51#define STREAM_BUFFER_SIZE (64 * 1024)
52
53/**
54 * Play audio file using a new stream on provided context.
55 * @param ctx Provided context.
56 * @param filename File to play.
57 * @return Error code.
58 */
59static int hplay_ctx(hound_context_t *ctx, const char *filename)
60{
61 printf("Hound context playback: %s\n", filename);
62 FILE *source = fopen(filename, "rb");
63 if (!source) {
64 printf("Failed to open file %s\n", filename);
65 return EINVAL;
66 }
67
68 /* Read and parse WAV header */
69 wave_header_t header;
70 size_t read = fread(&header, sizeof(header), 1, source);
71 if (read != 1) {
72 printf("Failed to read WAV header: %zu\n", read);
73 fclose(source);
74 return EIO;
75 }
76 pcm_format_t format;
77 const char *error;
78 int ret = wav_parse_header(&header, NULL, NULL, &format.channels,
79 &format.sampling_rate, &format.sample_format, &error);
80 if (ret != EOK) {
81 printf("Error parsing wav header: %s.\n", error);
82 fclose(source);
83 return EINVAL;
84 }
85
86 /* Allocate buffer and create new context */
87 char * buffer = malloc(READ_SIZE);
88 if (!buffer) {
89 fclose(source);
90 return ENOMEM;
91 }
92 hound_stream_t *stream = hound_stream_create(ctx,
93 HOUND_STREAM_DRAIN_ON_EXIT, format, STREAM_BUFFER_SIZE);
94
95 /* Read and play */
96 while ((read = fread(buffer, sizeof(char), READ_SIZE, source)) > 0) {
97 ret = hound_stream_write(stream, buffer, read);
98 if (ret != EOK) {
99 printf("Failed to write to hound stream: %s\n",
100 str_error(ret));
101 break;
102 }
103 }
104
105 /* Cleanup */
106 free(buffer);
107 fclose(source);
108 return ret;
109}
110
111/**
112 * Play audio file via hound server.
113 * @param filename File to play.
114 * @return Error code
115 */
116static int hplay(const char *filename)
117{
118 printf("Hound playback: %s\n", filename);
119 FILE *source = fopen(filename, "rb");
120 if (!source) {
121 printf("Failed to open file %s\n", filename);
122 return EINVAL;
123 }
124
125 /* Read and parse WAV header */
126 wave_header_t header;
127 size_t read = fread(&header, sizeof(header), 1, source);
128 if (read != 1) {
129 printf("Failed to read WAV header: %zu\n", read);
130 fclose(source);
131 return EIO;
132 }
133 pcm_format_t format;
134 const char *error;
135 int ret = wav_parse_header(&header, NULL, NULL, &format.channels,
136 &format.sampling_rate, &format.sample_format, &error);
137 if (ret != EOK) {
138 printf("Error parsing wav header: %s.\n", error);
139 fclose(source);
140 return EINVAL;
141 }
142
143 /* Connect new playback context */
144 hound_context_t *hound = hound_context_create_playback(filename,
145 format, STREAM_BUFFER_SIZE);
146 if (!hound) {
147 printf("Failed to create HOUND context\n");
148 fclose(source);
149 return ENOMEM;
150 }
151
152 ret = hound_context_connect_target(hound, HOUND_DEFAULT_TARGET);
153 if (ret != EOK) {
154 printf("Failed to connect to default target: %s\n",
155 str_error(ret));
156 hound_context_destroy(hound);
157 fclose(source);
158 return ret;
159 }
160
161 /* Read and play */
162 static char buffer[READ_SIZE];
163 while ((read = fread(buffer, sizeof(char), READ_SIZE, source)) > 0) {
164 ret = hound_write_main_stream(hound, buffer, read);
165 if (ret != EOK) {
166 printf("Failed to write to main context stream: %s\n",
167 str_error(ret));
168 break;
169 }
170 }
171
172 /* Cleanup */
173 hound_context_destroy(hound);
174 fclose(source);
175 return ret;
176}
177
178/**
179 * Helper structure for playback in separate fibrils
180 */
181typedef struct {
182 hound_context_t *ctx;
183 atomic_t *count;
184 const char *file;
185} fib_play_t;
186
187/**
188 * Fibril playback wrapper.
189 * @param arg Argument, pointer to playback helper structure.
190 * @return Error code.
191 */
192static int play_wrapper(void *arg)
193{
194 assert(arg);
195 fib_play_t *p = arg;
196 const int ret = hplay_ctx(p->ctx, p->file);
197 atomic_dec(p->count);
198 free(arg);
199 return ret;
200}
201
202/**
203 * Array of supported commandline options
204 */
205static const struct option opts[] = {
206 {"device", required_argument, 0, 'd'},
207 {"parallel", no_argument, 0, 'p'},
208 {"record", no_argument, 0, 'r'},
209 {"help", no_argument, 0, 'h'},
210 {0, 0, 0, 0}
211};
212
213/**
214 * Print usage help.
215 * @param name Name of the program.
216 */
217static void print_help(const char* name)
218{
219 printf("Usage: %s [options] file [files...]\n", name);
220 printf("supported options:\n");
221 printf("\t -h, --help\t Print this help.\n");
222 printf("\t -r, --record\t Start recording instead of playback. "
223 "(Not implemented)\n");
224 printf("\t -d, --device\t Use specified device instead of the sound "
225 "service. Use location path or a special device `default'\n");
226 printf("\t -p, --parallel\t Play given files in parallel instead of "
227 "sequentially (does not work with -d).\n");
228}
229
230int main(int argc, char *argv[])
231{
232 const char *device = "default";
233 int idx = 0;
234 bool direct = false, record = false, parallel = false;
235 optind = 0;
236 int ret = 0;
237
238 /* Parse command line options */
239 while (ret != -1) {
240 ret = getopt_long(argc, argv, "d:prh", opts, &idx);
241 switch (ret) {
242 case 'd':
243 direct = true;
244 device = optarg;
245 break;
246 case 'r':
247 record = true;
248 break;
249 case 'p':
250 parallel = true;
251 break;
252 case 'h':
253 print_help(*argv);
254 return 0;
255 };
256 }
257
258 if (parallel && direct) {
259 printf("Parallel playback is available only if using sound "
260 "server (no -d)\n");
261 print_help(*argv);
262 return 1;
263 }
264
265 if (optind == argc) {
266 printf("Not enough arguments.\n");
267 print_help(*argv);
268 return 1;
269 }
270
271 /* Init parallel playback variables */
272 hound_context_t *hound_ctx = NULL;
273 atomic_t playcount;
274 atomic_set(&playcount, 0);
275
276 /* Init parallel playback context if necessary */
277 if (parallel) {
278 hound_ctx = hound_context_create_playback("wavplay",
279 AUDIO_FORMAT_DEFAULT, STREAM_BUFFER_SIZE);
280 if (!hound_ctx) {
281 printf("Failed to create global hound context\n");
282 return 1;
283 }
284 const int ret = hound_context_connect_target(hound_ctx,
285 HOUND_DEFAULT_TARGET);
286 if (ret != EOK) {
287 printf("Failed to connect hound context to default "
288 "target.\n");
289 hound_context_destroy(hound_ctx);
290 return 1;
291 }
292 }
293
294 /* play or record all files */
295 for (int i = optind; i < argc; ++i) {
296 const char *file = argv[i];
297
298 printf("%s (%d/%d) %s\n", record ? "Recording" : "Playing",
299 i - optind + 1, argc - optind, file);
300 if (record) {
301 if (direct) {
302 drecord(device, file);
303 } else {
304 printf("Indirect recording is not supported "
305 "yet.\n");
306 break;
307 }
308 }
309
310 if (direct) {
311 dplay(device, file);
312 } else {
313 if (parallel) {
314 /* Start new fibril for parallel playback */
315 fib_play_t *data = malloc(sizeof(fib_play_t));
316 if (!data) {
317 printf("Playback of %s failed.\n",
318 file);
319 continue;
320 }
321 data->file = file;
322 data->count = &playcount;
323 data->ctx = hound_ctx;
324 fid_t fid = fibril_create(play_wrapper, data);
325 atomic_inc(&playcount);
326 fibril_add_ready(fid);
327 } else {
328 hplay(file);
329 }
330 }
331 }
332
333 /* Wait for all fibrils to finish */
334 while (atomic_get(&playcount) > 0)
335 async_usleep(1000000);
336
337 /* Destroy parallel playback context, if initialized */
338 if (hound_ctx)
339 hound_context_destroy(hound_ctx);
340 return 0;
341}
342/**
343 * @}
344 */
Note: See TracBrowser for help on using the repository browser.