source: mainline/uspace/srv/audio/hound/hound_ctx.c@ d4a829e

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

Hound error handling fixes.

  • Property mode set to 100644
File size: 12.7 KB
Line 
1/*
2 * Copyright (c) 2013 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/**
30 * @addtogroup audio
31 * @brief HelenOS sound server.
32 * @{
33 */
34/** @file
35 */
36
37#include <macros.h>
38#include <errno.h>
39#include <stdlib.h>
40#include <str_error.h>
41
42#include "hound_ctx.h"
43#include "audio_data.h"
44#include "connection.h"
45#include "log.h"
46
47static int update_data(audio_source_t *source, size_t size);
48static int new_data(audio_sink_t *sink);
49
50/**
51 * Allocate and initialize hound context structure.
52 * @param name String identifier.
53 * @return Pointer to a new context structure, NULL on failure
54 *
55 * Creates record context structure.
56 */
57hound_ctx_t *hound_record_ctx_get(const char *name)
58{
59 hound_ctx_t *ctx = malloc(sizeof(hound_ctx_t));
60 if (ctx) {
61 link_initialize(&ctx->link);
62 list_initialize(&ctx->streams);
63 fibril_mutex_initialize(&ctx->guard);
64 ctx->source = NULL;
65 ctx->sink = malloc(sizeof(audio_sink_t));
66 if (!ctx->sink) {
67 free(ctx);
68 return NULL;
69 }
70 const int ret = audio_sink_init(ctx->sink, name, ctx, NULL,
71 NULL, new_data, &AUDIO_FORMAT_DEFAULT);
72 if (ret != EOK) {
73 free(ctx->sink);
74 free(ctx);
75 return NULL;
76 }
77 }
78 return ctx;
79}
80
81/**
82 * Allocate and initialize hound context structure.
83 * @param name String identifier.
84 * @return Pointer to a new context structure, NULL on failure
85 *
86 * Creates record context structure.
87 */
88hound_ctx_t *hound_playback_ctx_get(const char *name)
89{
90 hound_ctx_t *ctx = malloc(sizeof(hound_ctx_t));
91 if (ctx) {
92 link_initialize(&ctx->link);
93 list_initialize(&ctx->streams);
94 fibril_mutex_initialize(&ctx->guard);
95 ctx->sink = NULL;
96 ctx->source = malloc(sizeof(audio_source_t));
97 if (!ctx->source) {
98 free(ctx);
99 return NULL;
100 }
101 const int ret = audio_source_init(ctx->source, name, ctx, NULL,
102 update_data, &AUDIO_FORMAT_DEFAULT);
103 if (ret != EOK) {
104 free(ctx->source);
105 free(ctx);
106 return NULL;
107 }
108 }
109 return ctx;
110}
111
112/**
113 * Destroy existing context structure.
114 * @param ctx hound cotnext to destroy.
115 */
116void hound_ctx_destroy(hound_ctx_t *ctx)
117{
118 assert(ctx);
119 assert(!link_in_use(&ctx->link));
120 assert(list_empty(&ctx->streams));
121 if (ctx->source)
122 audio_source_fini(ctx->source);
123 if (ctx->sink)
124 audio_sink_fini(ctx->sink);
125 free(ctx->source);
126 free(ctx->sink);
127 free(ctx);
128}
129
130/**
131 * Retrieve associated context id.
132 * @param ctx hound context.
133 * @return context id of the context.
134 */
135hound_context_id_t hound_ctx_get_id(hound_ctx_t *ctx)
136{
137 assert(ctx);
138 return (hound_context_id_t)ctx;
139}
140
141/**
142 * Query playback/record status of a hound context.
143 * @param ctx Hound context.
144 * @return True if ctx is a recording context.
145 */
146bool hound_ctx_is_record(hound_ctx_t *ctx)
147{
148 assert(ctx);
149 return ctx->source == NULL;
150}
151
152
153/*
154 * STREAMS
155 */
156
157/** Hound stream structure. */
158typedef struct hound_ctx_stream {
159 /** Hound context streams link */
160 link_t link;
161 /** Audio data pipe */
162 audio_pipe_t fifo;
163 /** Parent context */
164 hound_ctx_t *ctx;
165 /** Stream data format */
166 pcm_format_t format;
167 /** Stream modifiers */
168 int flags;
169 /** Maximum allowed buffer size */
170 size_t allowed_size;
171 /** Fifo access synchronization */
172 fibril_mutex_t guard;
173 /** buffer status change condition */
174 fibril_condvar_t change;
175} hound_ctx_stream_t;
176
177/**
178 * New stream append helper.
179 * @param ctx hound context.
180 * @param stream A new stream.
181 */
182static inline void stream_append(hound_ctx_t *ctx, hound_ctx_stream_t *stream)
183{
184 assert(ctx);
185 assert(stream);
186 fibril_mutex_lock(&ctx->guard);
187 list_append(&stream->link, &ctx->streams);
188 if (list_count(&ctx->streams) == 1) {
189 if (ctx->source && list_count(&ctx->source->connections) == 0)
190 ctx->source->format = stream->format;
191 }
192 fibril_mutex_unlock(&ctx->guard);
193}
194
195/**
196 * Push new data to stream, do not block.
197 * @param stream The target stream.
198 * @param adata The new data.
199 * @return Error code.
200 */
201static int stream_push_data(hound_ctx_stream_t *stream, audio_data_t *adata)
202{
203 assert(stream);
204 assert(adata);
205
206 if (stream->allowed_size && adata->size > stream->allowed_size)
207 return EINVAL;
208
209 fibril_mutex_lock(&stream->guard);
210 if (stream->allowed_size &&
211 (audio_pipe_bytes(&stream->fifo) + adata->size
212 > stream->allowed_size)) {
213 fibril_mutex_unlock(&stream->guard);
214 return EOVERFLOW;
215
216 }
217
218 const int ret = audio_pipe_push(&stream->fifo, adata);
219 fibril_mutex_unlock(&stream->guard);
220 if (ret == EOK)
221 fibril_condvar_signal(&stream->change);
222 return ret;
223}
224
225/**
226 * Old stream remove helper.
227 * @param ctx hound context.
228 * @param stream An old stream.
229 */
230static inline void stream_remove(hound_ctx_t *ctx, hound_ctx_stream_t *stream)
231{
232 assert(ctx);
233 assert(stream);
234 fibril_mutex_lock(&ctx->guard);
235 list_remove(&stream->link);
236 fibril_mutex_unlock(&ctx->guard);
237}
238
239/**
240 * Create new stream.
241 * @param ctx Assocaited hound context.
242 * @param flags Stream modidfiers.
243 * @param format PCM data format.
244 * @param buffer_size Maximum allowed buffer size.
245 * @return Pointer to a new stream structure, NULL on failure.
246 */
247hound_ctx_stream_t *hound_ctx_create_stream(hound_ctx_t *ctx, int flags,
248 pcm_format_t format, size_t buffer_size)
249{
250 assert(ctx);
251 hound_ctx_stream_t *stream = malloc(sizeof(hound_ctx_stream_t));
252 if (stream) {
253 audio_pipe_init(&stream->fifo);
254 link_initialize(&stream->link);
255 fibril_mutex_initialize(&stream->guard);
256 fibril_condvar_initialize(&stream->change);
257 stream->ctx = ctx;
258 stream->flags = flags;
259 stream->format = format;
260 stream->allowed_size = buffer_size;
261 stream_append(ctx, stream);
262 log_verbose("CTX: %p added stream; flags:%#x ch: %u r:%u f:%s",
263 ctx, flags, format.channels, format.sampling_rate,
264 pcm_sample_format_str(format.sample_format));
265 }
266 return stream;
267}
268
269/**
270 * Destroy existing stream structure.
271 * @param stream The stream to destroy.
272 *
273 * The function will print warning if there are data in the buffer.
274 */
275void hound_ctx_destroy_stream(hound_ctx_stream_t *stream)
276{
277 if (stream) {
278 stream_remove(stream->ctx, stream);
279 if (audio_pipe_bytes(&stream->fifo))
280 log_warning("Destroying stream with non empty buffer");
281 log_verbose("CTX: %p remove stream (%zu/%zu); "
282 "flags:%#x ch: %u r:%u f:%s",
283 stream->ctx, audio_pipe_bytes(&stream->fifo),
284 stream->allowed_size, stream->flags,
285 stream->format.channels, stream->format.sampling_rate,
286 pcm_sample_format_str(stream->format.sample_format));
287 audio_pipe_fini(&stream->fifo);
288 free(stream);
289 }
290}
291
292/**
293 * Write new data to a stream.
294 * @param stream The destination stream.
295 * @param data audio data buffer.
296 * @param size size of the @p data buffer.
297 * @return Error code.
298 */
299int hound_ctx_stream_write(hound_ctx_stream_t *stream, const void *data,
300 size_t size)
301{
302 assert(stream);
303
304 if (stream->allowed_size && size > stream->allowed_size)
305 return EINVAL;
306
307 fibril_mutex_lock(&stream->guard);
308 while (stream->allowed_size &&
309 (audio_pipe_bytes(&stream->fifo) + size > stream->allowed_size)) {
310 fibril_condvar_wait(&stream->change, &stream->guard);
311
312 }
313
314 const int ret =
315 audio_pipe_push_data(&stream->fifo, data, size, stream->format);
316 fibril_mutex_unlock(&stream->guard);
317 if (ret == EOK)
318 fibril_condvar_signal(&stream->change);
319 return ret;
320}
321
322/**
323 * Read data from a buffer.
324 * @param stream The source buffer.
325 * @param data Destination data buffer.
326 * @param size Size of the @p data buffer.
327 * @return Error code.
328 */
329int hound_ctx_stream_read(hound_ctx_stream_t *stream, void *data, size_t size)
330{
331 assert(stream);
332
333 if (stream->allowed_size && size > stream->allowed_size)
334 return EINVAL;
335
336 fibril_mutex_lock(&stream->guard);
337 while (audio_pipe_bytes(&stream->fifo) < size) {
338 fibril_condvar_wait(&stream->change, &stream->guard);
339 }
340
341 pcm_format_silence(data, size, &stream->format);
342 const size_t ret =
343 audio_pipe_mix_data(&stream->fifo, data, size, &stream->format);
344 fibril_mutex_unlock(&stream->guard);
345 if (ret > 0) {
346 fibril_condvar_signal(&stream->change);
347 return EOK;
348 }
349 return EEMPTY;
350}
351
352/**
353 * Add (mix) stream data to the destination buffer.
354 * @param stream The source stream.
355 * @param data Destination audio buffer.
356 * @param size Size of the @p data buffer.
357 * @param format Destination data format.
358 * @return Size of the destination buffer touch with stream's data.
359 */
360size_t hound_ctx_stream_add_self(hound_ctx_stream_t *stream, void *data,
361 size_t size, const pcm_format_t *f)
362{
363 assert(stream);
364 fibril_mutex_lock(&stream->guard);
365 const size_t ret = audio_pipe_mix_data(&stream->fifo, data, size, f);
366 fibril_condvar_signal(&stream->change);
367 fibril_mutex_unlock(&stream->guard);
368 return ret;
369}
370
371/**
372 * Block until the stream's buffer is empty.
373 * @param stream Target stream.
374 */
375void hound_ctx_stream_drain(hound_ctx_stream_t *stream)
376{
377 assert(stream);
378 log_debug("Draining stream");
379 fibril_mutex_lock(&stream->guard);
380 while (audio_pipe_bytes(&stream->fifo))
381 fibril_condvar_wait(&stream->change, &stream->guard);
382 fibril_mutex_unlock(&stream->guard);
383}
384
385/**
386 * Update context data.
387 * @param source Source abstraction.
388 * @param size Required size in source's format.
389 * @return error code.
390 *
391 * Mixes data from all streams and pushes it to all connections.
392 */
393int update_data(audio_source_t *source, size_t size)
394{
395 assert(source);
396 assert(source->private_data);
397 hound_ctx_t *ctx = source->private_data;
398 void *buffer = malloc(size);
399 if (!buffer)
400 return ENOMEM;
401 audio_data_t *adata = audio_data_create(buffer, size, source->format);
402 if (!adata) {
403 free(buffer);
404 return ENOMEM;
405 }
406 log_verbose("CTX: %p: Mixing %lu streams", ctx,
407 list_count(&ctx->streams));
408 pcm_format_silence(buffer, size, &source->format);
409 fibril_mutex_lock(&ctx->guard);
410 list_foreach(ctx->streams, link, hound_ctx_stream_t, stream) {
411 ssize_t copied = hound_ctx_stream_add_self(
412 stream, buffer, size, &source->format);
413 if (copied != (ssize_t)size)
414 log_warning("Not enough data in stream buffer");
415 }
416 log_verbose("CTX: %p. Pushing audio to %lu connections", ctx,
417 list_count(&source->connections));
418 list_foreach(source->connections, source_link, connection_t, conn) {
419 connection_push_data(conn, adata);
420 }
421 /* all connections should now have their refs */
422 audio_data_unref(adata);
423 fibril_mutex_unlock(&ctx->guard);
424 return EOK;
425}
426
427int new_data(audio_sink_t *sink)
428{
429 assert(sink);
430 assert(sink->private_data);
431 hound_ctx_t *ctx = sink->private_data;
432
433 fibril_mutex_lock(&ctx->guard);
434
435 /* count available data */
436 size_t available_frames = -1; /* this is ugly.... */
437 list_foreach(sink->connections, source_link, connection_t, conn) {
438 available_frames = min(available_frames,
439 audio_pipe_frames(&conn->fifo));
440 }
441
442 const size_t bsize =
443 available_frames * pcm_format_frame_size(&sink->format);
444 void *buffer = malloc(bsize);
445 if (!buffer) {
446 fibril_mutex_unlock(&ctx->guard);
447 return ENOMEM;
448 }
449 audio_data_t *adata = audio_data_create(buffer, bsize, sink->format);
450 if (!adata) {
451 fibril_mutex_unlock(&ctx->guard);
452 free(buffer);
453 return ENOMEM;
454 }
455
456 /* mix data */
457 pcm_format_silence(buffer, bsize, &sink->format);
458 list_foreach(sink->connections, source_link, connection_t, conn) {
459 /* This should not trigger data update on the source */
460 connection_add_source_data(
461 conn, buffer, bsize, sink->format);
462 }
463 /* push to all streams */
464 list_foreach(ctx->streams, link, hound_ctx_stream_t, stream) {
465 const int ret = stream_push_data(stream, adata);
466 if (ret != EOK)
467 log_error("Failed to push data to stream: %s",
468 str_error(ret));
469 }
470 audio_data_unref(adata);
471 fibril_mutex_unlock(&ctx->guard);
472 return ENOTSUP;
473}
474
475/**
476 * @}
477 */
Note: See TracBrowser for help on using the repository browser.