HelenOS sources

root/uspace/srv/audio/hound/audio_data.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. audio_data_create
  2. audio_data_addref
  3. audio_data_unref
  4. audio_data_link_list_instance
  5. audio_data_link_create
  6. audio_data_link_destroy
  7. audio_data_link_start
  8. audio_data_link_remain_size
  9. audio_data_link_available_frames
  10. audio_pipe_init
  11. audio_pipe_fini
  12. audio_pipe_push
  13. audio_pipe_pop
  14. audio_pipe_mix_data

/*
 * Copyright (c) 2013 Jan Vesely
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * - Redistributions of source code must retain the above copyright
 *   notice, this list of conditions and the following disclaimer.
 * - Redistributions in binary form must reproduce the above copyright
 *   notice, this list of conditions and the following disclaimer in the
 *   documentation and/or other materials provided with the distribution.
 * - The name of the author may not be used to endorse or promote products
 *   derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/** @addtogroup audio
 * @brief HelenOS sound server
 * @{
 */
/** @file
 */

#include <macros.h>
#include <stdlib.h>

#include "audio_data.h"
#include "log.h"

/**
 * Create reference counted buffer out of ordinary data buffer.
 * @param data audio buffer. The memory passed will be freed eventually.
 * @param size Size of the @p data buffer.
 * @param format audio data format.
 * @return pointer to valid audio data structure, NULL on failure.
 */
audio_data_t *audio_data_create(void *data, size_t size,
    pcm_format_t format)
{
        audio_data_t *adata = malloc(sizeof(audio_data_t) + size);
        if (adata) {
                unsigned overflow = size % pcm_format_frame_size(&format);
                if (overflow)
                        log_warning("Data not a multiple of frame size, "
                            "clipping.");
                adata->data = data;
                adata->size = size - overflow;
                adata->format = format;
                refcount_init(&adata->refcount);
        }
        return adata;
}

/**
 * Get a new reference to the audio data buffer.
 * @param adata The audio data buffer.
 */
void audio_data_addref(audio_data_t *adata)
{
        assert(adata);
        refcount_up(&adata->refcount);
}

/**
 * Release a reference to the audio data buffer.
 * @param adata The audio data buffer.
 */
void audio_data_unref(audio_data_t *adata)
{
        assert(adata);
        if (refcount_down(&adata->refcount)) {
                free(adata->data);
                free(adata);
        }
}

/* Data link helpers */

/** Audio data buffer list helper structure. */
typedef struct {
        link_t link;
        audio_data_t *adata;
        size_t position;
} audio_data_link_t;

/** List instance helper function.
 * @param l link
 * @return valid pointer to data link structure, NULL on failure.
 */
static inline audio_data_link_t *audio_data_link_list_instance(link_t *l)
{
        return l ? list_get_instance(l, audio_data_link_t, link) : NULL;
}

/**
 * Create a new audio data link.
 * @param adata Audio data to store.
 * @return Valid pointer to a new audio data link structure, NULL on failure.
 */
static audio_data_link_t *audio_data_link_create(audio_data_t *adata)
{
        assert(adata);
        audio_data_link_t *link = malloc(sizeof(audio_data_link_t));
        if (link) {
                audio_data_addref(adata);
                link->adata = adata;
                link->position = 0;
        }
        return link;
}

/**
 * Destroy audio data link.
 * @param link The link to destroy.
 *
 * Releases data reference.
 */
static void audio_data_link_destroy(audio_data_link_t *link)
{
        assert(link);
        assert(!link_in_use(&link->link));
        audio_data_unref(link->adata);
        free(link);
}

/**
 * Data link buffer start helper function.
 * @param alink audio data link
 * @return pointer to the beginning of data buffer.
 */
static inline const void *audio_data_link_start(audio_data_link_t *alink)
{
        assert(alink);
        assert(alink->adata);
        return alink->adata->data + alink->position;
}

/**
 * Data link remaining size getter.
 * @param alink audio data link
 * @return Remaining size of valid data in the buffer.
 */
static inline size_t audio_data_link_remain_size(audio_data_link_t *alink)
{
        assert(alink);
        assert(alink->adata);
        assert(alink->position <= alink->adata->size);
        return alink->adata->size - alink->position;
}

/**
 * Data link remaining frames getter.
 * @param alink audio data link
 * @return Number of remaining frames in the buffer.
 */
static inline size_t audio_data_link_available_frames(audio_data_link_t *alink)
{
        assert(alink);
        assert(alink->adata);
        return pcm_format_size_to_frames(audio_data_link_remain_size(alink),
            &alink->adata->format);
}

/* Audio Pipe */

/**
 * Initialize audio pipe structure.
 * @param pipe The pipe structure to initialize.
 */
void audio_pipe_init(audio_pipe_t *pipe)
{
        assert(pipe);
        list_initialize(&pipe->list);
        fibril_mutex_initialize(&pipe->guard);
        pipe->frames = 0;
        pipe->bytes = 0;
}

/**
 * Destroy all data in a pipe.
 * @param pipe The audio pipe to clean.
 */
void audio_pipe_fini(audio_pipe_t *pipe)
{
        assert(pipe);
        while (!list_empty(&pipe->list)) {
                audio_data_t *adata = audio_pipe_pop(pipe);
                audio_data_unref(adata);
        }
}

/**
 * Add new audio data to a pipe.
 * @param pipe The target pipe.
 * @param data The data.
 * @return Error code.
 */
errno_t audio_pipe_push(audio_pipe_t *pipe, audio_data_t *data)
{
        assert(pipe);
        assert(data);
        audio_data_link_t *alink = audio_data_link_create(data);
        if (!alink)
                return ENOMEM;

        fibril_mutex_lock(&pipe->guard);
        list_append(&alink->link, &pipe->list);
        pipe->bytes += audio_data_link_remain_size(alink);
        pipe->frames += audio_data_link_available_frames(alink);
        fibril_mutex_unlock(&pipe->guard);
        return EOK;
}

/**
 * Retrieve data form a audio pipe.
 * @param pipe THe target pipe.
 * @return Valid pointer to audio data, NULL if the pipe was empty.
 */
audio_data_t *audio_pipe_pop(audio_pipe_t *pipe)
{
        assert(pipe);
        fibril_mutex_lock(&pipe->guard);
        audio_data_t *adata = NULL;
        link_t *l = list_first(&pipe->list);
        if (l) {
                audio_data_link_t *alink = audio_data_link_list_instance(l);
                list_remove(&alink->link);
                pipe->bytes -= audio_data_link_remain_size(alink);
                pipe->frames -= audio_data_link_available_frames(alink);
                adata = alink->adata;
                audio_data_addref(adata);
                audio_data_link_destroy(alink);
        }
        fibril_mutex_unlock(&pipe->guard);
        return adata;
}

/**
 * Use data store in a pipe and mix it into the provided buffer.
 * @param pipe The piep that should provide data.
 * @param data Target buffer.
 * @param size Target buffer size.
 * @param format Target data format.
 * @return Size of the target buffer used.
 */
size_t audio_pipe_mix_data(audio_pipe_t *pipe, void *data,
    size_t size, const pcm_format_t *f)
{
        assert(pipe);

        const size_t dst_frame_size = pcm_format_frame_size(f);
        size_t needed_frames = pcm_format_size_to_frames(size, f);
        size_t copied_size = 0;

        fibril_mutex_lock(&pipe->guard);
        while (needed_frames > 0 && !list_empty(&pipe->list)) {
                /* Get first audio chunk */
                link_t *l = list_first(&pipe->list);
                audio_data_link_t *alink = audio_data_link_list_instance(l);

                /* Get audio chunk metadata */
                const size_t src_frame_size =
                    pcm_format_frame_size(&alink->adata->format);
                const size_t available_frames =
                    audio_data_link_available_frames(alink);
                const size_t copy_frames = min(available_frames, needed_frames);
                const size_t dst_copy_size = copy_frames * dst_frame_size;
                const size_t src_copy_size = copy_frames * src_frame_size;

                assert(src_copy_size <= audio_data_link_remain_size(alink));

                /* Copy audio data */
                pcm_format_convert_and_mix(data, dst_copy_size,
                    audio_data_link_start(alink), src_copy_size,
                    &alink->adata->format, f);

                /* Update values */
                needed_frames -= copy_frames;
                copied_size += dst_copy_size;
                data += dst_copy_size;
                alink->position += src_copy_size;
                pipe->bytes -= src_copy_size;
                pipe->frames -= copy_frames;
                if (audio_data_link_remain_size(alink) == 0) {
                        list_remove(&alink->link);
                        audio_data_link_destroy(alink);
                } else {
                        assert(needed_frames == 0);
                }
        }
        fibril_mutex_unlock(&pipe->guard);
        return copied_size;
}

/**
 * @}
 */

/* [<][>][^][v][top][bottom][index][help] */
HelenOS homepage, sources at GitHub