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

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

wavplay: Improve error and info messages.

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