Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

sink / source API #7622

Merged
merged 10 commits into from
Jun 19, 2023
1 change: 1 addition & 0 deletions src/audio/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ if((NOT CONFIG_LIBRARY) OR CONFIG_LIBRARY_STATIC)
component.c
buffer.c
marcinszkudlinski marked this conversation as resolved.
Show resolved Hide resolved
source_api_helper.c
sink_api_helper.c
channel_map.c
)
if(CONFIG_COMP_BLOB)
Expand Down
177 changes: 177 additions & 0 deletions src/audio/sink_api_helper.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
// SPDX-License-Identifier: BSD-3-Clause
//
// Copyright(c) 2023 Intel Corporation. All rights reserved.
//

#include <sof/audio/sink_api.h>
#include <sof/audio/sink_api_implementation.h>
#include <sof/audio/audio_stream.h>

void sink_init(struct sof_sink __sparse_cache *sink, const struct sink_ops *ops,
struct sof_audio_stream_params *audio_stream_params)
{
sink->ops = ops;
sink->audio_stream_params = audio_stream_params;
}

size_t sink_get_free_size(struct sof_sink __sparse_cache *sink)
{
return sink->ops->get_free_size(sink);
}

int sink_get_buffer(struct sof_sink __sparse_cache *sink, size_t req_size,
void **data_ptr, void **buffer_start, size_t *buffer_size)
{
int ret;

if (sink->requested_write_frag_size)
return -EBUSY;

ret = sink->ops->get_buffer(sink, req_size, data_ptr,
buffer_start, buffer_size);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also IIRC checkpatch would complain - align on opening parenthesis above. We have plenty of examples in the code :-)


if (!ret)
sink->requested_write_frag_size = req_size;
return ret;
}

int sink_commit_buffer(struct sof_sink __sparse_cache *sink, size_t commit_size)
{
int ret;

/* check if there was a buffer obtained for writing by sink_get_buffer */
if (!sink->requested_write_frag_size)
return -ENODATA;

/* limit size of data to be committed to previously obtained size */
if (commit_size > sink->requested_write_frag_size)
commit_size = sink->requested_write_frag_size;

ret = sink->ops->commit_buffer(sink, commit_size);

if (!ret)
sink->requested_write_frag_size = 0;

sink->num_of_bytes_processed += commit_size;
return ret;
}

size_t sink_get_num_of_processed_bytes(struct sof_sink __sparse_cache *sink)
{
return sink->num_of_bytes_processed;
}

void sink_reset_num_of_processed_bytes(struct sof_sink __sparse_cache *sink)
{
sink->num_of_bytes_processed = 0;
}

enum sof_ipc_frame sink_get_frm_fmt(struct sof_sink __sparse_cache *sink)
{
return sink->audio_stream_params->frame_fmt;
}

enum sof_ipc_frame sink_get_valid_fmt(struct sof_sink __sparse_cache *sink)
{
return sink->audio_stream_params->valid_sample_fmt;
}

uint32_t sink_get_rate(struct sof_sink __sparse_cache *sink)
{
return sink->audio_stream_params->rate;
}

uint32_t sink_get_channels(struct sof_sink __sparse_cache *sink)
{
return sink->audio_stream_params->channels;
}

uint32_t sink_get_buffer_fmt(struct sof_sink __sparse_cache *sink)
{
return sink->audio_stream_params->buffer_fmt;
}

bool sink_get_overrun(struct sof_sink __sparse_cache *sink)
{
return sink->audio_stream_params->overrun_permitted;
}

int sink_set_frm_fmt(struct sof_sink __sparse_cache *sink, enum sof_ipc_frame frame_fmt)
marcinszkudlinski marked this conversation as resolved.
Show resolved Hide resolved
{
sink->audio_stream_params->frame_fmt = frame_fmt;

/* notify the implementation */
if (sink->ops->on_audio_format_set)
return sink->ops->on_audio_format_set(sink);
return 0;
}

int sink_set_valid_fmt(struct sof_sink __sparse_cache *sink,
enum sof_ipc_frame valid_sample_fmt)
{
sink->audio_stream_params->valid_sample_fmt = valid_sample_fmt;
if (sink->ops->on_audio_format_set)
return sink->ops->on_audio_format_set(sink);
return 0;
}

int sink_set_rate(struct sof_sink __sparse_cache *sink, unsigned int rate)
{
sink->audio_stream_params->rate = rate;
if (sink->ops->on_audio_format_set)
return sink->ops->on_audio_format_set(sink);
return 0;
}

int sink_set_channels(struct sof_sink __sparse_cache *sink, unsigned int channels)
{
sink->audio_stream_params->channels = channels;
if (sink->ops->on_audio_format_set)
return sink->ops->on_audio_format_set(sink);
return 0;
}

int sink_set_buffer_fmt(struct sof_sink __sparse_cache *sink, uint32_t buffer_fmt)
{
sink->audio_stream_params->buffer_fmt = buffer_fmt;
if (sink->ops->on_audio_format_set)
return sink->ops->on_audio_format_set(sink);
return 0;
}

int sink_set_overrun(struct sof_sink __sparse_cache *sink, bool overrun_permitted)
{
sink->audio_stream_params->overrun_permitted = overrun_permitted;
if (sink->ops->on_audio_format_set)
return sink->ops->on_audio_format_set(sink);
return 0;
}

size_t sink_get_frame_bytes(struct sof_sink __sparse_cache *sink)
{
return get_frame_bytes(sink_get_frm_fmt(sink),
sink_get_channels(sink));
}

size_t sink_get_free_frames(struct sof_sink __sparse_cache *sink)
{
return sink_get_free_size(sink) /
sink_get_frame_bytes(sink);
}

int sink_set_params(struct sof_sink __sparse_cache *sink,
struct sof_ipc_stream_params *params, bool force_update)
{
if (sink->ops->audio_set_ipc_params)
return sink->ops->audio_set_ipc_params(sink, params, force_update);
return 0;
}

int sink_set_alignment_constants(struct sof_sink __sparse_cache *sink,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

prefix functions with sof_ ?

const uint32_t byte_align,
const uint32_t frame_align_req)
{
if (sink->ops->set_alignment_constants)
return sink->ops->set_alignment_constants(sink, byte_align, frame_align_req);
return 0;
}
163 changes: 163 additions & 0 deletions src/include/sof/audio/sink_api.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
/* SPDX-License-Identifier: BSD-3-Clause
*
* Copyright(c) 2023 Intel Corporation. All rights reserved.
*
*/

#ifndef __SOF_SINK_API_H__
#define __SOF_SINK_API_H__

#include <sof/common.h>
#include <errno.h>
#include <stdint.h>
#include <stdbool.h>

/**
* this is a definition of API to sink of audio data
*
* THE SINK is any component that can store data somehow and provide a buffer to be filled
* with data at request. The sink API does not define how the data will be processed/used
*
* The user - a module - sees this API as a destination it must send data to
* The IMPLEMENTATION - audio_stream, DP Queue - sees this as a producer that
* PROVIDES data for processing
*
* Examples of components that should expose SINK api
* - /dev/null
* all the data stored in sink buffer are just simply discarded
* - I2S sender
* Data stored in sink buffer will be sent to the external world
* - a memory ring buffer
* data stored in the buffer will be sent to another module (usually using source API, but it
* does not matter in fact).
*
* The main advantage of using sink API instead of just taking pointers to the buffers is that
* the buffer may be prepared at the moment the data producer is requesting it. i.e.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

'may be prepared' is ambiguous, it could mean

a) may be in prepared state ('prepared' is an adjective in your sentence)
b) may be transitioned to prepared state ('prepared is a verb in your sentence)

Which is it?

* - cache may be written back/invalidated if necessary
* - data may be moved to make linear space
* - part of the buffer may be locked to prevent reading
* etc.etc. it depends on implementation of the data sink
*
* NOTE: the module should get a complete portion of space it needs for processing, fill it
* than release. The reason is - the depending on the implementation, the calls may be
* expensive - may involve some data moving in memory, cache writebacks, etc.
*
*/

/** definition of obfsfucated handler of sink API */
struct sof_sink;

/* forward def */
struct sof_ipc_stream_params;

/**
* Retrieves size of free space available in sink (in bytes)
* return number of free bytes in buffer available to immediate filling
*/
size_t sink_get_free_size(struct sof_sink __sparse_cache *sink);

/**
* Retrieves size of free space available in sink (in frames)
* return number of free bytes in buffer available to immediate filling
*/
size_t sink_get_free_frames(struct sof_sink __sparse_cache *sink);

/**
* Get a circular buffer to operate on (to write).
*
* Retrieves a fragment of circular data to be used by the caller
* After calling get_buffer, the space for data is guaranteed to be available
* for exclusive use on the caller core through provided pointer
* if the provided pointers are cached, it is guaranteed that the caller may safely use it without
* any additional cache operations
* The caller MUST take care of data circularity based on provided pointers
*
* @param sink a handler to sink
* @param [in] req_size requested size of space
* @param [out] data_ptr a pointer to the space will be provided there
* @param [out] buffer_start pointer to circular buffer start
* @param [out] buffer_size size of circular buffer
*
* @retval -ENODATA if req_size is bigger than free space
*
*/
int sink_get_buffer(struct sof_sink __sparse_cache *sink, size_t req_size,
void **data_ptr, void **buffer_start, size_t *buffer_size);

/**
* Commits that the buffer previously obtained by get_buffer is filled with data
* and ready to be used
*
* @param sink a handler to sink
* @param commit_size amount of data that the caller declares as valid
* if commit_size is bigger than the amount of data obtained before by get_buffer(), only
* the amount obtained before will be committed. That means - if somebody obtained a buffer,
* filled it with data and wants to commit it in whole, it may simple call
* commit_buffer with commit_size==MAXINT
* @return proper error code (0 on success)
*/
int sink_commit_buffer(struct sof_sink __sparse_cache *sink, size_t commit_size);

/**
* Get total number of bytes processed by the sink (meaning - committed by sink_commit_buffer())
*
* @param sink a handler to sink
* @return total number of processed data
*/
size_t sink_get_num_of_processed_bytes(struct sof_sink __sparse_cache *sink);

/**
* sets counter of total number of bytes processed to zero
*/
void sink_reset_num_of_processed_bytes(struct sof_sink __sparse_cache *sink);

/** get size of a single audio frame (in bytes) */
size_t sink_get_frame_bytes(struct sof_sink __sparse_cache *sink);

/** set of functions for retrieve audio parameters */
enum sof_ipc_frame sink_get_frm_fmt(struct sof_sink __sparse_cache *sink);
enum sof_ipc_frame sink_get_valid_fmt(struct sof_sink __sparse_cache *sink);
uint32_t sink_get_rate(struct sof_sink __sparse_cache *sink);
uint32_t sink_get_channels(struct sof_sink __sparse_cache *sink);
uint32_t sink_get_buffer_fmt(struct sof_sink __sparse_cache *sink);
bool sink_get_overrun(struct sof_sink __sparse_cache *sink);

/** set of functions for setting audio parameters */
int sink_set_frm_fmt(struct sof_sink __sparse_cache *sink, enum sof_ipc_frame frame_fmt);
int sink_set_valid_fmt(struct sof_sink __sparse_cache *sink, enum sof_ipc_frame valid_sample_fmt);
int sink_set_rate(struct sof_sink __sparse_cache *sink, unsigned int rate);
int sink_set_channels(struct sof_sink __sparse_cache *sink, unsigned int channels);
int sink_set_overrun(struct sof_sink __sparse_cache *sink, bool overrun_permitted);
int sink_set_buffer_fmt(struct sof_sink __sparse_cache *sink, uint32_t buffer_fmt);

/**
* initial set of audio parameters, provided in sof_ipc_stream_params
*
* @param sink a handler to sink
* @param params the set of parameters
* @param force_update tells the implementation that the params should override actual settings
* @return 0 if success
*/
int sink_set_params(struct sof_sink __sparse_cache *sink,
struct sof_ipc_stream_params *params, bool force_update);

/**
* Set frame_align_shift and frame_align of stream according to byte_align and
* frame_align_req alignment requirement. Once the channel number,frame size
* are determined, the frame_align and frame_align_shift are determined too.
* these two feature will be used in audio_stream_get_avail_frames_aligned
* to calculate the available frames. it should be called in component prepare
* or param functions only once before stream copy. if someone forgets to call
* this first, there would be unexampled error such as nothing is copied at all.
*
* @param sink a handler to sink
* @param byte_align Processing byte alignment requirement.
* @param frame_align_req Processing frames alignment requirement.
*
* @return 0 if success
*/
int sink_set_alignment_constants(struct sof_sink __sparse_cache *sink,
const uint32_t byte_align,
const uint32_t frame_align_req);

#endif /* __SOF_SINK_API_H__ */
Loading