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

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

wavplay: implement parallel playback

  • Property mode set to 100644
File size: 7.4 KB
RevLine 
[e677b08]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>
[d26233c]39#include <malloc.h>
[5cd5079]40#include <str_error.h>
[e677b08]41#include <stdio.h>
[5cd5079]42#include <hound/client.h>
[ea6c838]43#include <pcm/sample_format.h>
[aef1799]44#include <getopt.h>
[e677b08]45
[aef1799]46#include "dplay.h"
[e677b08]47#include "wave.h"
48
[d26233c]49#define READ_SIZE (32 * 1024)
50#define STREAM_BUFFER_SIZE (64 * 1024)
[abaef81]51
[d26233c]52static int hplay_ctx(hound_context_t *ctx, const char *filename)
53{
54 printf("Hound context playback: %s\n", filename);
55 FILE *source = fopen(filename, "rb");
56 if (!source) {
57 printf("Failed to open file %s\n", filename);
58 return EINVAL;
59 }
60 wave_header_t header;
61 size_t read = fread(&header, sizeof(header), 1, source);
62 if (read != 1) {
63 printf("Failed to read WAV header: %zu\n", read);
64 fclose(source);
65 return EIO;
66 }
67 pcm_format_t format;
68 const char *error;
69 int ret = wav_parse_header(&header, NULL, NULL, &format.channels,
70 &format.sampling_rate, &format.sample_format, &error);
71 if (ret != EOK) {
72 printf("Error parsing wav header: %s.\n", error);
73 fclose(source);
74 return EINVAL;
75 }
76
77 hound_stream_t *stream = hound_stream_create(ctx,
78 HOUND_STREAM_DRAIN_ON_EXIT, format, STREAM_BUFFER_SIZE);
79
80 char * buffer = malloc(READ_SIZE);
81 if (!buffer) {
82 fclose(source);
83 return ENOMEM;
84 }
85 while ((read = fread(buffer, sizeof(char), READ_SIZE, source)) > 0) {
86 ret = hound_stream_write(stream, buffer, read);
87 if (ret != EOK) {
88 printf("Failed to write to hound stream: %s\n",
89 str_error(ret));
90 break;
91 }
92 }
93 free(buffer);
94 fclose(source);
95 return ret;
96}
[abaef81]97
98static int hplay(const char *filename)
99{
[60e5696d]100 printf("Hound playback: %s\n", filename);
[abaef81]101 FILE *source = fopen(filename, "rb");
[bd5860f]102 if (!source) {
103 printf("Failed to open file %s\n", filename);
[abaef81]104 return EINVAL;
[bd5860f]105 }
[abaef81]106 wave_header_t header;
107 size_t read = fread(&header, sizeof(header), 1, source);
[bd5860f]108 if (read != 1) {
109 printf("Failed to read WAV header: %zu\n", read);
[abaef81]110 fclose(source);
111 return EIO;
112 }
[9e1800c]113 pcm_format_t format;
[abaef81]114 const char *error;
[9e1800c]115 int ret = wav_parse_header(&header, NULL, NULL, &format.channels,
116 &format.sampling_rate, &format.sample_format, &error);
[abaef81]117 if (ret != EOK) {
118 printf("Error parsing wav header: %s.\n", error);
119 fclose(source);
120 return EINVAL;
121 }
122 hound_context_t *hound = hound_context_create_playback(filename,
[d26233c]123 format, STREAM_BUFFER_SIZE);
[abaef81]124 if (!hound) {
125 printf("Failed to create HOUND context\n");
126 fclose(source);
127 return ENOMEM;
128 }
129
[ec81221]130 ret = hound_context_connect_target(hound, HOUND_DEFAULT_TARGET);
[abaef81]131 if (ret != EOK) {
132 printf("Failed to connect to default target: %s\n",
133 str_error(ret));
[62beb4b]134 hound_context_destroy(hound);
[abaef81]135 fclose(source);
[62beb4b]136 return ret;
[abaef81]137 }
[d26233c]138 static char buffer[READ_SIZE];
139 while ((read = fread(buffer, sizeof(char), READ_SIZE, source)) > 0) {
[bd5860f]140 ret = hound_write_main_stream(hound, buffer, read);
[abaef81]141 if (ret != EOK) {
[bd5860f]142 printf("Failed to write to main context stream: %s\n",
[abaef81]143 str_error(ret));
144 break;
145 }
146 }
[03c2d5f]147 hound_context_destroy(hound);
[abaef81]148 fclose(source);
149 return ret;
150}
151
[d26233c]152typedef struct {
153 hound_context_t *ctx;
154 atomic_t *count;
155 const char *file;
156} fib_play_t;
157
158static int play_wrapper(void *arg)
159{
160 assert(arg);
161 fib_play_t *p = arg;
162 const int ret = hplay_ctx(p->ctx, p->file);
163 atomic_dec(p->count);
164 free(arg);
165 return ret;
166}
167
[aef1799]168static const struct option opts[] = {
169 {"device", required_argument, 0, 'd'},
[d26233c]170 {"parallel", no_argument, 0, 'p'},
[aef1799]171 {"record", no_argument, 0, 'r'},
[86fe9d1]172 {"help", no_argument, 0, 'h'},
[aef1799]173 {0, 0, 0, 0}
174};
175
[86fe9d1]176static void print_help(const char* name)
177{
[19f6ea5b]178 printf("Usage: %s [options] file [files...]\n", name);
[86fe9d1]179 printf("supported options:\n");
180 printf("\t -h, --help\t Print this help.\n");
[60e5696d]181 printf("\t -r, --record\t Start recording instead of playback. "
182 "(Not implemented)\n");
183 printf("\t -d, --device\t Use specified device instead of the sound "
184 "service. Use location path or a special device `default'\n");
[d26233c]185 printf("\t -p, --parallel\t Play given files in parallel instead of "
186 "sequentially (does not work with -d).\n");
[86fe9d1]187}
[aef1799]188
[e677b08]189int main(int argc, char *argv[])
190{
[aef1799]191 const char *device = "default";
192 int idx = 0;
[d26233c]193 bool direct = false, record = false, parallel = false;
[aef1799]194 optind = 0;
195 int ret = 0;
196 while (ret != -1) {
[d26233c]197 ret = getopt_long(argc, argv, "d:prh", opts, &idx);
[aef1799]198 switch (ret) {
199 case 'd':
200 direct = true;
201 device = optarg;
202 break;
203 case 'r':
204 record = true;
205 break;
[d26233c]206 case 'p':
207 parallel = true;
208 break;
[86fe9d1]209 case 'h':
210 print_help(*argv);
211 return 0;
[aef1799]212 };
213 }
214
[d26233c]215 if (parallel && direct) {
216 printf("Parallel playback is available only if using sound "
217 "server (no -d)\n");
218 print_help(*argv);
219 return 1;
220 }
221
[aef1799]222 if (optind == argc) {
223 printf("Not enough arguments.\n");
[86fe9d1]224 print_help(*argv);
[e677b08]225 return 1;
[aef1799]226 }
227
[d26233c]228 hound_context_t *hound_ctx = NULL;
229 atomic_t playcount;
230 atomic_set(&playcount, 0);
231 if (parallel) {
232 hound_ctx = hound_context_create_playback("wavplay",
233 AUDIO_FORMAT_DEFAULT, STREAM_BUFFER_SIZE);
234 if (!hound_ctx) {
235 printf("Failed to create global hound context\n");
236 return 1;
237 }
238 const int ret = hound_context_connect_target(hound_ctx,
239 HOUND_DEFAULT_TARGET);
240 if (ret != EOK) {
241 printf("Failed to connect hound context to default "
242 "target.\n");
243 hound_context_destroy(hound_ctx);
244 return 1;
245 }
246 }
247
[19f6ea5b]248 for (int i = optind; i < argc; ++i) {
249 const char *file = argv[i];
250
251 printf("%s (%d/%d) %s\n", record ? "Recording" : "Playing",
252 i - optind + 1, argc - optind, file);
253 if (record) {
254 printf("Recording is not supported yet.\n");
255 return 1;
256 }
257 if (direct) {
258 dplay(device, file);
259 } else {
[d26233c]260 if (parallel) {
261 fib_play_t *data = malloc(sizeof(fib_play_t));
262 if (!data) {
263 printf("Playback of %s failed.\n",
264 file);
265 continue;
266 }
267 data->file = file;
268 data->count = &playcount;
269 data->ctx = hound_ctx;
270 fid_t fid = fibril_create(play_wrapper, data);
271 atomic_inc(&playcount);
272 fibril_add_ready(fid);
273 } else {
274 hplay(file);
275 }
[19f6ea5b]276 }
[4389076]277 }
[d26233c]278
279 while (atomic_get(&playcount) > 0)
280 async_usleep(1000000);
281
282 if (hound_ctx)
283 hound_context_destroy(hound_ctx);
[19f6ea5b]284 return 0;
[e677b08]285}
286/**
287 * @}
288 */
Note: See TracBrowser for help on using the repository browser.