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

Last change on this file was 36795edf, checked in by Martin Decky <martin@…>, 4 years ago

Improve lists and other data structures

Provide more standard-compliant member_to_inst implementation that uses
offsetof. Avoid potential undefined behavior in list_foreach and
list_foreach_rev by avoiding assinging an unaligned pointer value. Use
size_t instead of unsigned long for list length.

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