Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion app/compr/README.txt
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ cadence.conf - Base Cadence codec module only (no individual codecs)
mp3.conf - MP3 decoder and encoder
aac.conf - AAC decoder
vorbis.conf - Vorbis decoder
all_codecs.conf - All supported codecs (MP3, AAC, Vorbis)
pcm.conf - PCM (wav) decoder
all_codecs.conf - All supported codecs (MP3, AAC, Vorbis, pcm)

Usage Examples
--------------
Expand Down
2 changes: 1 addition & 1 deletion app/compr/all_codecs.conf
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,4 @@ CONFIG_CADENCE_CODEC_AAC_DEC=y
CONFIG_CADENCE_CODEC_AAC_DEC_LIB="../cadence_libs/xa_aac_dec.a"
CONFIG_CADENCE_CODEC_VORBIS_DEC=y
CONFIG_CADENCE_CODEC_VORBIS_DEC_LIB="../cadence_libs/xa_vorbis_dec.a"

CONFIG_SOF_COMPRESS_CODEC_PCM_DEC=y
3 changes: 3 additions & 0 deletions app/compr/pcm.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# PCM (wav) decoder
CONFIG_CADENCE_CODEC=y
CONFIG_SOF_COMPRESS_CODEC_PCM_DEC=y
4 changes: 4 additions & 0 deletions src/audio/base_fw.c
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,10 @@ static void get_codec_info(struct sof_tlv **tuple)
codec_info.items[codec_info.count++] =
SET_CODEC_INFO_ITEM(SND_AUDIOCODEC_VORBIS, SOF_IPC_STREAM_PLAYBACK);
#endif
#ifdef CONFIG_SOF_COMPRESS_CODEC_PCM_DEC
codec_info.items[codec_info.count++] =
SET_CODEC_INFO_ITEM(SND_AUDIOCODEC_PCM, SOF_IPC_STREAM_PLAYBACK);
#endif

if (!codec_info.count)
return;
Expand Down
7 changes: 7 additions & 0 deletions src/audio/module_adapter/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ endif()
zephyr_library_import(xa_mp3_enc ${CONFIG_CADENCE_CODEC_MP3_ENC_LIB})
endif()

zephyr_library_sources_ifdef(CONFIG_SOF_COMPRESS_CODEC_PCM_DEC
module/cadence_other/xa_pcm_dec.c)

if (CONFIG_COMP_DOLBY_DAX_AUDIO_PROCESSING)
if(CONFIG_COMP_DOLBY_DAX_AUDIO_PROCESSING STREQUAL "m" AND DEFINED CONFIG_LLEXT)
add_subdirectory(module/dolby/llext
Expand Down Expand Up @@ -147,6 +150,10 @@ if(NOT CONFIG_COMP_MODULE_SHARED_LIBRARY_BUILD)

endif()

if(CONFIG_SOF_COMPRESS_CODEC_PCM_DEC)
add_subdirectory(module/cadence_other)
endif()

if(CONFIG_COMP_DOLBY_DAX_AUDIO_PROCESSING)
target_include_directories(sof PRIVATE ${PROJECT_SOURCE_DIR}/third_party/include)
add_local_sources(sof module/dolby/dax.c)
Expand Down
10 changes: 10 additions & 0 deletions src/audio/module_adapter/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,16 @@ if CADENCE_CODEC
This option is a string and takes the full name of the SRC library binary.
endif

config SOF_COMPRESS_CODEC_PCM_DEC
bool "SOF PCM (WAV) decoder"
help
Select to enable PCM (WAV) decoder.
This provides PCM/WAV file decoding support through a simple
implementation that handles the PCM data. Note that this is not
a Cadence codec module but an open-source addition to support the
PCM data format. In addition need to build the cplay from
tinycompress with option --enable-pcm to use this.

endif # Cadence

config COMP_DOLBY_DAX_AUDIO_PROCESSING
Expand Down
10 changes: 10 additions & 0 deletions src/audio/module_adapter/module/cadence.c
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,12 @@ struct cadence_api cadence_api_table[] = {
.api = xa_src_pp,
},
#endif
#ifdef CONFIG_SOF_COMPRESS_CODEC_PCM_DEC
{
.id = SOF_COMPRESS_CODEC_PCM_DEC_ID,
.api = xa_pcm_dec,
},
#endif
};

static int cadence_codec_get_api_id(uint32_t compress_id, uint32_t direction)
Expand All @@ -89,6 +95,8 @@ static int cadence_codec_get_api_id(uint32_t compress_id, uint32_t direction)
return CADENCE_CODEC_AAC_DEC_ID;
case SND_AUDIOCODEC_VORBIS:
return CADENCE_CODEC_VORBIS_DEC_ID;
case SND_AUDIOCODEC_PCM:
return SOF_COMPRESS_CODEC_PCM_DEC_ID;
default:
return -EINVAL;
}
Expand Down Expand Up @@ -236,6 +244,8 @@ int cadence_codec_get_samples(struct processing_module *mod)
return 1152;
case CADENCE_CODEC_AAC_DEC_ID:
return 1024;
case SOF_COMPRESS_CODEC_PCM_DEC_ID:
return 1024;
default:
break;
}
Expand Down
3 changes: 3 additions & 0 deletions src/audio/module_adapter/module/cadence_ipc4.c
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,9 @@ static int cadence_configure_codec_params(struct processing_module *mod)
case CADENCE_CODEC_VORBIS_DEC_ID:
/* No configuration needed for Vorbis */
return 0;
case SOF_COMPRESS_CODEC_PCM_DEC_ID:
/* No configuration needed for PCM decoder */
return 0;
default:
break;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# SPDX-License-Identifier: BSD-3-Clause

if(CONFIG_SOF_COMPRESS_CODEC_PCM_DEC)
add_local_sources(sof xa_pcm_dec.c)
endif()
280 changes: 280 additions & 0 deletions src/audio/module_adapter/module/cadence_other/xa_pcm_dec.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,280 @@
// SPDX-License-Identifier: BSD-3-Clause
//
// Copyright(c) 2026 Intel Corporation.
//

#include <sof/audio/cadence/xa_type_def.h>
#include <sof/audio/cadence/xa_error_standards.h>
#include <sof/audio/cadence/xa_apicmd_standards.h>
#include <sof/audio/cadence/xa_memory_standards.h>
#include <sof/audio/cadence_other/pcm_dec/xa_pcm_dec_api.h>
#include <rtos/string.h>
#include <stdint.h>

/* Note: This is a workaround with empirically found count to stop producing
* output after pipeline eos indication. The input buffer size isn't becoming
* smaller and zero when stream ends. Without this the last buf size amount
* of data keeps looping forever.
*/
#define PCM_DEC_COUNT_SINCE_EOS_TO_DONE 12

/* For XA_API_CMD_GET_MEM_INFO_SIZE */
#define PCM_DEC_IN_BUF_SIZE 16384
#define PCM_DEC_OUT_BUF_SIZE 16384

/* PCM decoder state structure */
struct xa_pcm_dec_state {
/* Configuration parameters */
uint32_t sample_rate;
uint32_t num_channels;
uint32_t pcm_width;

/* State variables */
uint32_t bytes_consumed;
uint32_t bytes_produced;
uint32_t init_done;
uint32_t exec_done;
uint32_t input_over;
uint32_t eos_set_count;

/* Memory pointers */
void *input_buf;
void *output_buf;
uint32_t output_buf_size;
uint32_t input_bytes;
};

static const char lib_name[] = "PCM Decoder";

/* Main codec API function */
XA_ERRORCODE xa_pcm_dec(xa_codec_handle_t handle, WORD32 cmd, WORD32 idx, pVOID value)
{
struct xa_pcm_dec_state *state = (struct xa_pcm_dec_state *)handle;

/* Commands that don't need initialized state */
switch (cmd) {
case XA_API_CMD_GET_API_SIZE:
*(WORD32 *)value = sizeof(struct xa_pcm_dec_state);
return XA_NO_ERROR;

case XA_API_CMD_GET_LIB_ID_STRINGS:
if (idx == XA_CMD_TYPE_LIB_NAME) {
strcpy((char *)value, lib_name);
return XA_NO_ERROR;
}
return XA_API_FATAL_INVALID_CMD_TYPE;

case XA_API_CMD_GET_MEMTABS_SIZE:
/* PCM decoder needs minimal memtabs structure */
*(WORD32 *)value = 4;
return XA_NO_ERROR;

case XA_API_CMD_SET_MEMTABS_PTR:
/* PCM decoder doesn't use memtabs, just return success */
return XA_NO_ERROR;

default:
break;
}

/* All other commands need initialized state */
if (!handle)
return XA_PCMDEC_EXECUTE_FATAL_UNINITIALIZED;

switch (cmd) {
case XA_API_CMD_INIT:
switch (idx) {
case XA_CMD_TYPE_INIT_API_PRE_CONFIG_PARAMS:
/* Initialize with default values */
bzero(state, sizeof(*state));
state->sample_rate = 48000;
state->num_channels = 2;
state->pcm_width = 16;
return XA_NO_ERROR;

case XA_CMD_TYPE_INIT_API_POST_CONFIG_PARAMS:
/* Nothing to do here for simple PCM decoder */
return XA_NO_ERROR;

case XA_CMD_TYPE_INIT_PROCESS:
state->init_done = 1;
return XA_NO_ERROR;

case XA_CMD_TYPE_INIT_DONE_QUERY:
*(WORD32 *)value = state->init_done;
return XA_NO_ERROR;

default:
return XA_API_FATAL_INVALID_CMD_TYPE;
}

case XA_API_CMD_SET_CONFIG_PARAM:
switch (idx) {
case XA_PCM_DEC_CONFIG_PARAM_SAMPLE_RATE:
state->sample_rate = *(WORD32 *)value;
return XA_NO_ERROR;

case XA_PCM_DEC_CONFIG_PARAM_CHANNELS:
state->num_channels = *(WORD32 *)value;
return XA_NO_ERROR;

case XA_PCM_DEC_CONFIG_PARAM_PCM_WIDTH:
state->pcm_width = *(WORD32 *)value;
return XA_NO_ERROR;

case XA_PCM_DEC_CONFIG_PARAM_INTERLEAVE:
return XA_NO_ERROR;

default:
return XA_PCMDEC_CONFIG_NONFATAL_INVALID_PCM_WIDTH;
}

case XA_API_CMD_GET_CONFIG_PARAM:
switch (idx) {
case XA_PCM_DEC_CONFIG_PARAM_SAMPLE_RATE:
*(WORD32 *)value = state->sample_rate;
return XA_NO_ERROR;

case XA_PCM_DEC_CONFIG_PARAM_CHANNELS:
*(WORD32 *)value = state->num_channels;
return XA_NO_ERROR;

case XA_PCM_DEC_CONFIG_PARAM_PCM_WIDTH:
*(WORD32 *)value = state->pcm_width;
return XA_NO_ERROR;

case XA_PCM_DEC_CONFIG_PARAM_PRODUCED:
*(WORD32 *)value = state->bytes_produced;
return XA_NO_ERROR;

default:
return XA_API_FATAL_INVALID_CMD_TYPE;
}

case XA_API_CMD_GET_N_MEMTABS:
/* We need 2 memory tables: input and output buffers */
*(WORD32 *)value = 2;
return XA_NO_ERROR;

case XA_API_CMD_GET_MEM_INFO_TYPE:
if (idx == 0)
*(WORD32 *)value = XA_MEMTYPE_INPUT;
else if (idx == 1)
*(WORD32 *)value = XA_MEMTYPE_OUTPUT;
else
return XA_API_FATAL_INVALID_CMD_TYPE;
return XA_NO_ERROR;

case XA_API_CMD_GET_MEM_INFO_SIZE:
if (idx == 0)
*(WORD32 *)value = PCM_DEC_IN_BUF_SIZE;
else if (idx == 1)
*(WORD32 *)value = PCM_DEC_OUT_BUF_SIZE;
else
return XA_API_FATAL_INVALID_CMD_TYPE;
return XA_NO_ERROR;

case XA_API_CMD_GET_MEM_INFO_ALIGNMENT:
*(WORD32 *)value = 4; /* 4-byte alignment */
return XA_NO_ERROR;

case XA_API_CMD_SET_MEM_PTR:
if (idx == 0) {
state->input_buf = value;
} else if (idx == 1) {
state->output_buf = value;
state->output_buf_size = PCM_DEC_OUT_BUF_SIZE;
} else {
return XA_API_FATAL_INVALID_CMD_TYPE;
}
return XA_NO_ERROR;

case XA_API_CMD_SET_INPUT_BYTES:
state->input_bytes = *(WORD32 *)value;
state->bytes_consumed = 0;
if (state->input_bytes > 0)
state->exec_done = 0;
return XA_NO_ERROR;

case XA_API_CMD_GET_OUTPUT_BYTES:
*(WORD32 *)value = state->bytes_produced;
return XA_NO_ERROR;

case XA_API_CMD_GET_CURIDX_INPUT_BUF:
*(WORD32 *)value = state->bytes_consumed;
return XA_NO_ERROR;

case XA_API_CMD_INPUT_OVER:
/* Indicate no more input buffers will be provided */
state->input_over = 1;
return XA_NO_ERROR;

case XA_API_CMD_GET_N_TABLES:
/* PCM decoder doesn't use tables */
*(WORD32 *)value = 0;
return XA_NO_ERROR;

case XA_API_CMD_GET_TABLE_PTR:
case XA_API_CMD_SET_TABLE_PTR:
case XA_API_CMD_GET_TABLE_INFO_SIZE:
case XA_API_CMD_GET_TABLE_INFO_ALIGNMENT:
case XA_API_CMD_GET_TABLE_INFO_PRIORITY:
/* PCM decoder doesn't use tables, return success */
return XA_NO_ERROR;

case XA_API_CMD_GET_MEM_INFO_PLACEMENT:
case XA_API_CMD_GET_MEM_INFO_PRIORITY:
case XA_API_CMD_SET_MEM_INFO_SIZE:
case XA_API_CMD_SET_MEM_PLACEMENT:
/* Return success for optional memory info commands */
return XA_NO_ERROR;

case XA_API_CMD_EXECUTE:
if (idx == XA_CMD_TYPE_DO_EXECUTE) {
uint32_t to_copy;

state->bytes_produced = 0;
state->bytes_consumed = 0;

if (state->input_over) {
state->eos_set_count++;
if (state->eos_set_count > PCM_DEC_COUNT_SINCE_EOS_TO_DONE) {
state->exec_done = 1;
return XA_PCMDEC_EXECUTE_NONFATAL_INSUFFICIENT_DATA;
}
}

/* Safety check for buffers - should not happen */
if (!state->input_buf || !state->output_buf) {
/* Consume input even if buffers invalid to avoid hang */
state->bytes_consumed = state->input_bytes;
state->bytes_produced = 0;
return XA_NO_ERROR;
}

/* Copy PCM data from input to output */
to_copy = state->input_bytes;
if (to_copy > state->output_buf_size)
to_copy = state->output_buf_size;

if (to_copy > 0) {
memcpy_s(state->output_buf, state->output_buf_size,
state->input_buf, to_copy);
state->bytes_produced = to_copy;
state->bytes_consumed = to_copy;
} else {
state->bytes_consumed = 0;
}

return XA_NO_ERROR;
} else if (idx == XA_CMD_TYPE_DONE_QUERY) {
/* Query if execution is done */
*(WORD32 *)value = state->exec_done;
return XA_NO_ERROR;
}
return XA_API_FATAL_INVALID_CMD_TYPE;

default:
return XA_API_FATAL_INVALID_CMD;
}
}
Loading
Loading