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

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

Fix printf compile issues

  • Property mode set to 100644
File size: 13.1 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 <malloc.h>
38#include <macros.h>
39#include <errno.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 * List instance helper.
179 * @param l link
180 * @return pointer to a hound context structure, NULL on failure.
181 */
182static inline hound_ctx_stream_t *hound_ctx_stream_from_link(link_t *l)
183{
184 return l ? list_get_instance(l, hound_ctx_stream_t, link) : NULL;
185}
186
187/**
188 * New stream append helper.
189 * @param ctx hound context.
190 * @param stream A new stream.
191 */
192static inline void stream_append(hound_ctx_t *ctx, hound_ctx_stream_t *stream)
193{
194 assert(ctx);
195 assert(stream);
196 fibril_mutex_lock(&ctx->guard);
197 list_append(&stream->link, &ctx->streams);
198 if (list_count(&ctx->streams) == 1) {
199 if (ctx->source && list_count(&ctx->source->connections) == 0)
200 ctx->source->format = stream->format;
201 }
202 fibril_mutex_unlock(&ctx->guard);
203}
204
205/**
206 * Push new data to stream, do not block.
207 * @param stream The target stream.
208 * @param adata The new data.
209 * @return Error code.
210 */
211static int stream_push_data(hound_ctx_stream_t *stream, audio_data_t *adata)
212{
213 assert(stream);
214 assert(adata);
215
216 if (stream->allowed_size && adata->size > stream->allowed_size)
217 return EINVAL;
218
219 fibril_mutex_lock(&stream->guard);
220 if (stream->allowed_size &&
221 (audio_pipe_bytes(&stream->fifo) + adata->size
222 > stream->allowed_size)) {
223 fibril_mutex_unlock(&stream->guard);
224 return EOVERFLOW;
225
226 }
227
228 const int ret = audio_pipe_push(&stream->fifo, adata);
229 fibril_mutex_unlock(&stream->guard);
230 if (ret == EOK)
231 fibril_condvar_signal(&stream->change);
232 return ret;
233}
234
235/**
236 * Old stream remove helper.
237 * @param ctx hound context.
238 * @param stream An old stream.
239 */
240static inline void stream_remove(hound_ctx_t *ctx, hound_ctx_stream_t *stream)
241{
242 assert(ctx);
243 assert(stream);
244 fibril_mutex_lock(&ctx->guard);
245 list_remove(&stream->link);
246 fibril_mutex_unlock(&ctx->guard);
247}
248
249/**
250 * Create new stream.
251 * @param ctx Assocaited hound context.
252 * @param flags Stream modidfiers.
253 * @param format PCM data format.
254 * @param buffer_size Maximum allowed buffer size.
255 * @return Pointer to a new stream structure, NULL on failure.
256 */
257hound_ctx_stream_t *hound_ctx_create_stream(hound_ctx_t *ctx, int flags,
258 pcm_format_t format, size_t buffer_size)
259{
260 assert(ctx);
261 hound_ctx_stream_t *stream = malloc(sizeof(hound_ctx_stream_t));
262 if (stream) {
263 audio_pipe_init(&stream->fifo);
264 link_initialize(&stream->link);
265 fibril_mutex_initialize(&stream->guard);
266 fibril_condvar_initialize(&stream->change);
267 stream->ctx = ctx;
268 stream->flags = flags;
269 stream->format = format;
270 stream->allowed_size = buffer_size;
271 stream_append(ctx, stream);
272 log_verbose("CTX: %p added stream; flags:%#x ch: %u r:%u f:%s",
273 ctx, flags, format.channels, format.sampling_rate,
274 pcm_sample_format_str(format.sample_format));
275 }
276 return stream;
277}
278
279/**
280 * Destroy existing stream structure.
281 * @param stream The stream to destroy.
282 *
283 * The function will print warning if there are data in the buffer.
284 */
285void hound_ctx_destroy_stream(hound_ctx_stream_t *stream)
286{
287 if (stream) {
288 stream_remove(stream->ctx, stream);
289 if (audio_pipe_bytes(&stream->fifo))
290 log_warning("Destroying stream with non empty buffer");
291 log_verbose("CTX: %p remove stream (%zu/%zu); "
292 "flags:%#x ch: %u r:%u f:%s",
293 stream->ctx, audio_pipe_bytes(&stream->fifo),
294 stream->allowed_size, stream->flags,
295 stream->format.channels, stream->format.sampling_rate,
296 pcm_sample_format_str(stream->format.sample_format));
297 audio_pipe_fini(&stream->fifo);
298 free(stream);
299 }
300}
301
302/**
303 * Write new data to a stream.
304 * @param stream The destination stream.
305 * @param data audio data buffer.
306 * @param size size of the @p data buffer.
307 * @return Error code.
308 */
309int hound_ctx_stream_write(hound_ctx_stream_t *stream, const void *data,
310 size_t size)
311{
312 assert(stream);
313
314 if (stream->allowed_size && size > stream->allowed_size)
315 return EINVAL;
316
317 fibril_mutex_lock(&stream->guard);
318 while (stream->allowed_size &&
319 (audio_pipe_bytes(&stream->fifo) + size > stream->allowed_size)) {
320 fibril_condvar_wait(&stream->change, &stream->guard);
321
322 }
323
324 const int ret =
325 audio_pipe_push_data(&stream->fifo, data, size, stream->format);
326 fibril_mutex_unlock(&stream->guard);
327 if (ret == EOK)
328 fibril_condvar_signal(&stream->change);
329 return ret;
330}
331
332/**
333 * Read data from a buffer.
334 * @param stream The source buffer.
335 * @param data Destination data buffer.
336 * @param size Size of the @p data buffer.
337 * @return Error code.
338 */
339int hound_ctx_stream_read(hound_ctx_stream_t *stream, void *data, size_t size)
340{
341 assert(stream);
342
343 if (stream->allowed_size && size > stream->allowed_size)
344 return EINVAL;
345
346 fibril_mutex_lock(&stream->guard);
347 while (audio_pipe_bytes(&stream->fifo) < size) {
348 fibril_condvar_wait(&stream->change, &stream->guard);
349 }
350
351 pcm_format_silence(data, size, &stream->format);
352 const int ret =
353 audio_pipe_mix_data(&stream->fifo, data, size, &stream->format);
354 fibril_mutex_unlock(&stream->guard);
355 if (ret == EOK)
356 fibril_condvar_signal(&stream->change);
357 return ret;
358}
359
360/**
361 * Add (mix) stream data to the destination buffer.
362 * @param stream The source stream.
363 * @param data Destination audio buffer.
364 * @param size Size of the @p data buffer.
365 * @param format Destination data format.
366 * @return Size of the destination buffer touch with stream's data,
367 * error code on failure.
368 */
369ssize_t hound_ctx_stream_add_self(hound_ctx_stream_t *stream, void *data,
370 size_t size, const pcm_format_t *f)
371{
372 assert(stream);
373 fibril_mutex_lock(&stream->guard);
374 const int ret = audio_pipe_mix_data(&stream->fifo, data, size, f);
375 fibril_condvar_signal(&stream->change);
376 fibril_mutex_unlock(&stream->guard);
377 return ret;
378}
379
380/**
381 * Block until the stream's buffer is empty.
382 * @param stream Target stream.
383 */
384void hound_ctx_stream_drain(hound_ctx_stream_t *stream)
385{
386 assert(stream);
387 log_debug("Draining stream");
388 fibril_mutex_lock(&stream->guard);
389 while (audio_pipe_bytes(&stream->fifo))
390 fibril_condvar_wait(&stream->change, &stream->guard);
391 fibril_mutex_unlock(&stream->guard);
392}
393
394/**
395 * Update context data.
396 * @param source Source abstraction.
397 * @param size Required size in source's format.
398 * @return error code.
399 *
400 * Mixes data from all streams and pushes it to all connections.
401 */
402int update_data(audio_source_t *source, size_t size)
403{
404 assert(source);
405 assert(source->private_data);
406 hound_ctx_t *ctx = source->private_data;
407 void *buffer = malloc(size);
408 if (!buffer)
409 return ENOMEM;
410 audio_data_t *adata = audio_data_create(buffer, size, source->format);
411 if (!adata) {
412 free(buffer);
413 return ENOMEM;
414 }
415 log_verbose("CTX: %p: Mixing %u streams", ctx,
416 list_count(&ctx->streams));
417 pcm_format_silence(buffer, size, &source->format);
418 fibril_mutex_lock(&ctx->guard);
419 list_foreach(ctx->streams, it) {
420 hound_ctx_stream_t *stream = hound_ctx_stream_from_link(it);
421 ssize_t copied = hound_ctx_stream_add_self(
422 stream, buffer, size, &source->format);
423 if (copied != (ssize_t)size)
424 log_warning("Not enough data in stream buffer");
425 }
426 log_verbose("CTX: %p. Pushing audio to %u connections", ctx,
427 list_count(&source->connections));
428 list_foreach(source->connections, it) {
429 connection_t *conn = connection_from_source_list(it);
430 connection_push_data(conn, adata);
431 }
432 fibril_mutex_unlock(&ctx->guard);
433 return EOK;
434}
435
436int new_data(audio_sink_t *sink)
437{
438 assert(sink);
439 assert(sink->private_data);
440 hound_ctx_t *ctx = sink->private_data;
441
442 fibril_mutex_lock(&ctx->guard);
443
444 /* count available data */
445 size_t available_frames = -1; /* this is ugly.... */
446 list_foreach(sink->connections, it) {
447 connection_t *conn = connection_from_source_list(it);
448 available_frames = min(available_frames,
449 audio_pipe_frames(&conn->fifo));
450 }
451
452 const size_t bsize =
453 available_frames * pcm_format_frame_size(&sink->format);
454 void *buffer = malloc(bsize);
455 if (!buffer) {
456 fibril_mutex_unlock(&ctx->guard);
457 return ENOMEM;
458 }
459 audio_data_t *adata = audio_data_create(buffer, bsize, sink->format);
460 if (!adata) {
461 fibril_mutex_unlock(&ctx->guard);
462 free(buffer);
463 return ENOMEM;
464 }
465
466 /* mix data */
467 pcm_format_silence(buffer, bsize, &sink->format);
468 list_foreach(sink->connections, it) {
469 connection_t *conn = connection_from_source_list(it);
470 /* This should not trigger data update on the source */
471 const size_t copied = connection_add_source_data(
472 conn, buffer, bsize, sink->format);
473 if (copied != bsize)
474 log_error("Copied less than advertised data, "
475 "something is wrong");
476 }
477 /* push to all streams */
478 list_foreach(ctx->streams, it) {
479 hound_ctx_stream_t *stream = hound_ctx_stream_from_link(it);
480 const int ret = stream_push_data(stream, adata);
481 if (ret != EOK)
482 log_error("Failed to push data to stream: %s",
483 str_error(ret));
484 }
485 audio_data_unref(adata);
486 fibril_mutex_unlock(&ctx->guard);
487 return ENOTSUP;
488}
489
490/**
491 * @}
492 */
Note: See TracBrowser for help on using the repository browser.