Skip to content

Commit 17bc853

Browse files
committed
Audio: Cadence: Add a PCM (WAV) decoder
This patch adds a PCM decoder that is compatible with Cadence codec API. It allows to test tinycompress cplay playback with wav format files. Signed-off-by: Seppo Ingalsuo <seppo.ingalsuo@linux.intel.com>
1 parent 96ab79a commit 17bc853

File tree

9 files changed

+375
-0
lines changed

9 files changed

+375
-0
lines changed

src/audio/base_fw.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,10 @@ static void get_codec_info(struct sof_tlv **tuple)
9999
codec_info.items[codec_info.count++] =
100100
SET_CODEC_INFO_ITEM(SND_AUDIOCODEC_VORBIS, SOF_IPC_STREAM_PLAYBACK);
101101
#endif
102+
#ifdef CONFIG_SOF_COMPRESS_CODEC_PCM_DEC
103+
codec_info.items[codec_info.count++] =
104+
SET_CODEC_INFO_ITEM(SND_AUDIOCODEC_PCM, SOF_IPC_STREAM_PLAYBACK);
105+
#endif
102106

103107
if (!codec_info.count)
104108
return;

src/audio/module_adapter/CMakeLists.txt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ endif()
3333
zephyr_library_import(xa_mp3_enc ${CONFIG_CADENCE_CODEC_MP3_ENC_LIB})
3434
endif()
3535

36+
zephyr_library_sources_ifdef(CONFIG_SOF_COMPRESS_CODEC_PCM_DEC
37+
module/cadence_other/xa_pcm_dec.c)
38+
3639
if (CONFIG_COMP_DOLBY_DAX_AUDIO_PROCESSING)
3740
if(CONFIG_COMP_DOLBY_DAX_AUDIO_PROCESSING STREQUAL "m" AND DEFINED CONFIG_LLEXT)
3841
add_subdirectory(module/dolby/llext
@@ -147,6 +150,10 @@ if(NOT CONFIG_COMP_MODULE_SHARED_LIBRARY_BUILD)
147150

148151
endif()
149152

153+
if(CONFIG_SOF_COMPRESS_CODEC_PCM_DEC)
154+
add_subdirectory(module/cadence_other)
155+
endif()
156+
150157
if(CONFIG_COMP_DOLBY_DAX_AUDIO_PROCESSING)
151158
target_include_directories(sof PRIVATE ${PROJECT_SOURCE_DIR}/third_party/include)
152159
add_local_sources(sof module/dolby/dax.c)

src/audio/module_adapter/Kconfig

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,16 @@ if CADENCE_CODEC
173173
This option is a string and takes the full name of the SRC library binary.
174174
endif
175175

176+
config SOF_COMPRESS_CODEC_PCM_DEC
177+
bool "SOF PCM (WAV) decoder"
178+
help
179+
Select to enable PCM (WAV) decoder.
180+
This provides PCM/WAV file decoding support through a simple
181+
implementation that handles the PCM data. Note that this is not
182+
a Cadence codec module but an open-source addition to support the
183+
PCM data format. In addition need to build the cplay from
184+
tinycompress with option --enable-pcm to use this.
185+
176186
endif # Cadence
177187

178188
config COMP_DOLBY_DAX_AUDIO_PROCESSING

src/audio/module_adapter/module/cadence.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,12 @@ struct cadence_api cadence_api_table[] = {
7474
.api = xa_src_pp,
7575
},
7676
#endif
77+
#ifdef CONFIG_SOF_COMPRESS_CODEC_PCM_DEC
78+
{
79+
.id = SOF_COMPRESS_CODEC_PCM_DEC_ID,
80+
.api = xa_pcm_dec,
81+
},
82+
#endif
7783
};
7884

7985
static int cadence_codec_get_api_id(uint32_t compress_id, uint32_t direction)
@@ -89,6 +95,8 @@ static int cadence_codec_get_api_id(uint32_t compress_id, uint32_t direction)
8995
return CADENCE_CODEC_AAC_DEC_ID;
9096
case SND_AUDIOCODEC_VORBIS:
9197
return CADENCE_CODEC_VORBIS_DEC_ID;
98+
case SND_AUDIOCODEC_PCM:
99+
return SOF_COMPRESS_CODEC_PCM_DEC_ID;
92100
default:
93101
return -EINVAL;
94102
}
@@ -236,6 +244,8 @@ int cadence_codec_get_samples(struct processing_module *mod)
236244
return 1152;
237245
case CADENCE_CODEC_AAC_DEC_ID:
238246
return 1024;
247+
case SOF_COMPRESS_CODEC_PCM_DEC_ID:
248+
return 1024;
239249
default:
240250
break;
241251
}

src/audio/module_adapter/module/cadence_ipc4.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,9 @@ static int cadence_configure_codec_params(struct processing_module *mod)
209209
case CADENCE_CODEC_VORBIS_DEC_ID:
210210
/* No configuration needed for Vorbis */
211211
return 0;
212+
case SOF_COMPRESS_CODEC_PCM_DEC_ID:
213+
/* No configuration needed for PCM decoder */
214+
return 0;
212215
default:
213216
break;
214217
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# SPDX-License-Identifier: BSD-3-Clause
2+
3+
if(CONFIG_SOF_COMPRESS_CODEC_PCM_DEC)
4+
add_local_sources(sof xa_pcm_dec.c)
5+
endif()
Lines changed: 280 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,280 @@
1+
// SPDX-License-Identifier: BSD-3-Clause
2+
//
3+
// Copyright(c) 2026 Intel Corporation.
4+
//
5+
6+
#include <sof/audio/cadence/xa_type_def.h>
7+
#include <sof/audio/cadence/xa_error_standards.h>
8+
#include <sof/audio/cadence/xa_apicmd_standards.h>
9+
#include <sof/audio/cadence/xa_memory_standards.h>
10+
#include <sof/audio/cadence_other/pcm_dec/xa_pcm_dec_api.h>
11+
#include <rtos/string.h>
12+
#include <stdint.h>
13+
14+
/* Note: This is a workaround with empirically found count to stop producing
15+
* output after pipeline eos indication. The input buffer size isn't becoming
16+
* smaller and zero when stream ends. Without this the last buf size amount
17+
* of data keeps looping forever.
18+
*/
19+
#define PCM_DEC_COUNT_SINCE_EOS_TO_DONE 12
20+
21+
/* For XA_API_CMD_GET_MEM_INFO_SIZE */
22+
#define PCM_DEC_IN_BUF_SIZE 16384
23+
#define PCM_DEC_OUT_BUF_SIZE 16384
24+
25+
/* PCM decoder state structure */
26+
struct xa_pcm_dec_state {
27+
/* Configuration parameters */
28+
uint32_t sample_rate;
29+
uint32_t num_channels;
30+
uint32_t pcm_width;
31+
32+
/* State variables */
33+
uint32_t bytes_consumed;
34+
uint32_t bytes_produced;
35+
uint32_t init_done;
36+
uint32_t exec_done;
37+
uint32_t input_over;
38+
uint32_t eos_set_count;
39+
40+
/* Memory pointers */
41+
void *input_buf;
42+
void *output_buf;
43+
uint32_t output_buf_size;
44+
uint32_t input_bytes;
45+
};
46+
47+
static const char lib_name[] = "PCM Decoder";
48+
49+
/* Main codec API function */
50+
XA_ERRORCODE xa_pcm_dec(xa_codec_handle_t handle, WORD32 cmd, WORD32 idx, pVOID value)
51+
{
52+
struct xa_pcm_dec_state *state = (struct xa_pcm_dec_state *)handle;
53+
54+
/* Commands that don't need initialized state */
55+
switch (cmd) {
56+
case XA_API_CMD_GET_API_SIZE:
57+
*(WORD32 *)value = sizeof(struct xa_pcm_dec_state);
58+
return XA_NO_ERROR;
59+
60+
case XA_API_CMD_GET_LIB_ID_STRINGS:
61+
if (idx == XA_CMD_TYPE_LIB_NAME) {
62+
strcpy((char *)value, lib_name);
63+
return XA_NO_ERROR;
64+
}
65+
return XA_API_FATAL_INVALID_CMD_TYPE;
66+
67+
case XA_API_CMD_GET_MEMTABS_SIZE:
68+
/* PCM decoder needs minimal memtabs structure */
69+
*(WORD32 *)value = 4;
70+
return XA_NO_ERROR;
71+
72+
case XA_API_CMD_SET_MEMTABS_PTR:
73+
/* PCM decoder doesn't use memtabs, just return success */
74+
return XA_NO_ERROR;
75+
76+
default:
77+
break;
78+
}
79+
80+
/* All other commands need initialized state */
81+
if (!handle)
82+
return XA_PCMDEC_EXECUTE_FATAL_UNINITIALIZED;
83+
84+
switch (cmd) {
85+
case XA_API_CMD_INIT:
86+
switch (idx) {
87+
case XA_CMD_TYPE_INIT_API_PRE_CONFIG_PARAMS:
88+
/* Initialize with default values */
89+
bzero(state, sizeof(*state));
90+
state->sample_rate = 48000;
91+
state->num_channels = 2;
92+
state->pcm_width = 16;
93+
return XA_NO_ERROR;
94+
95+
case XA_CMD_TYPE_INIT_API_POST_CONFIG_PARAMS:
96+
/* Nothing to do here for simple PCM decoder */
97+
return XA_NO_ERROR;
98+
99+
case XA_CMD_TYPE_INIT_PROCESS:
100+
state->init_done = 1;
101+
return XA_NO_ERROR;
102+
103+
case XA_CMD_TYPE_INIT_DONE_QUERY:
104+
*(WORD32 *)value = state->init_done;
105+
return XA_NO_ERROR;
106+
107+
default:
108+
return XA_API_FATAL_INVALID_CMD_TYPE;
109+
}
110+
111+
case XA_API_CMD_SET_CONFIG_PARAM:
112+
switch (idx) {
113+
case XA_PCM_DEC_CONFIG_PARAM_SAMPLE_RATE:
114+
state->sample_rate = *(WORD32 *)value;
115+
return XA_NO_ERROR;
116+
117+
case XA_PCM_DEC_CONFIG_PARAM_CHANNELS:
118+
state->num_channels = *(WORD32 *)value;
119+
return XA_NO_ERROR;
120+
121+
case XA_PCM_DEC_CONFIG_PARAM_PCM_WIDTH:
122+
state->pcm_width = *(WORD32 *)value;
123+
return XA_NO_ERROR;
124+
125+
case XA_PCM_DEC_CONFIG_PARAM_INTERLEAVE:
126+
return XA_NO_ERROR;
127+
128+
default:
129+
return XA_PCMDEC_CONFIG_NONFATAL_INVALID_PCM_WIDTH;
130+
}
131+
132+
case XA_API_CMD_GET_CONFIG_PARAM:
133+
switch (idx) {
134+
case XA_PCM_DEC_CONFIG_PARAM_SAMPLE_RATE:
135+
*(WORD32 *)value = state->sample_rate;
136+
return XA_NO_ERROR;
137+
138+
case XA_PCM_DEC_CONFIG_PARAM_CHANNELS:
139+
*(WORD32 *)value = state->num_channels;
140+
return XA_NO_ERROR;
141+
142+
case XA_PCM_DEC_CONFIG_PARAM_PCM_WIDTH:
143+
*(WORD32 *)value = state->pcm_width;
144+
return XA_NO_ERROR;
145+
146+
case XA_PCM_DEC_CONFIG_PARAM_PRODUCED:
147+
*(WORD32 *)value = state->bytes_produced;
148+
return XA_NO_ERROR;
149+
150+
default:
151+
return XA_API_FATAL_INVALID_CMD_TYPE;
152+
}
153+
154+
case XA_API_CMD_GET_N_MEMTABS:
155+
/* We need 2 memory tables: input and output buffers */
156+
*(WORD32 *)value = 2;
157+
return XA_NO_ERROR;
158+
159+
case XA_API_CMD_GET_MEM_INFO_TYPE:
160+
if (idx == 0)
161+
*(WORD32 *)value = XA_MEMTYPE_INPUT;
162+
else if (idx == 1)
163+
*(WORD32 *)value = XA_MEMTYPE_OUTPUT;
164+
else
165+
return XA_API_FATAL_INVALID_CMD_TYPE;
166+
return XA_NO_ERROR;
167+
168+
case XA_API_CMD_GET_MEM_INFO_SIZE:
169+
if (idx == 0)
170+
*(WORD32 *)value = PCM_DEC_IN_BUF_SIZE;
171+
else if (idx == 1)
172+
*(WORD32 *)value = PCM_DEC_OUT_BUF_SIZE;
173+
else
174+
return XA_API_FATAL_INVALID_CMD_TYPE;
175+
return XA_NO_ERROR;
176+
177+
case XA_API_CMD_GET_MEM_INFO_ALIGNMENT:
178+
*(WORD32 *)value = 4; /* 4-byte alignment */
179+
return XA_NO_ERROR;
180+
181+
case XA_API_CMD_SET_MEM_PTR:
182+
if (idx == 0) {
183+
state->input_buf = value;
184+
} else if (idx == 1) {
185+
state->output_buf = value;
186+
state->output_buf_size = PCM_DEC_OUT_BUF_SIZE;
187+
} else {
188+
return XA_API_FATAL_INVALID_CMD_TYPE;
189+
}
190+
return XA_NO_ERROR;
191+
192+
case XA_API_CMD_SET_INPUT_BYTES:
193+
state->input_bytes = *(WORD32 *)value;
194+
state->bytes_consumed = 0;
195+
if (state->input_bytes > 0)
196+
state->exec_done = 0;
197+
return XA_NO_ERROR;
198+
199+
case XA_API_CMD_GET_OUTPUT_BYTES:
200+
*(WORD32 *)value = state->bytes_produced;
201+
return XA_NO_ERROR;
202+
203+
case XA_API_CMD_GET_CURIDX_INPUT_BUF:
204+
*(WORD32 *)value = state->bytes_consumed;
205+
return XA_NO_ERROR;
206+
207+
case XA_API_CMD_INPUT_OVER:
208+
/* Indicate no more input buffers will be provided */
209+
state->input_over = 1;
210+
return XA_NO_ERROR;
211+
212+
case XA_API_CMD_GET_N_TABLES:
213+
/* PCM decoder doesn't use tables */
214+
*(WORD32 *)value = 0;
215+
return XA_NO_ERROR;
216+
217+
case XA_API_CMD_GET_TABLE_PTR:
218+
case XA_API_CMD_SET_TABLE_PTR:
219+
case XA_API_CMD_GET_TABLE_INFO_SIZE:
220+
case XA_API_CMD_GET_TABLE_INFO_ALIGNMENT:
221+
case XA_API_CMD_GET_TABLE_INFO_PRIORITY:
222+
/* PCM decoder doesn't use tables, return success */
223+
return XA_NO_ERROR;
224+
225+
case XA_API_CMD_GET_MEM_INFO_PLACEMENT:
226+
case XA_API_CMD_GET_MEM_INFO_PRIORITY:
227+
case XA_API_CMD_SET_MEM_INFO_SIZE:
228+
case XA_API_CMD_SET_MEM_PLACEMENT:
229+
/* Return success for optional memory info commands */
230+
return XA_NO_ERROR;
231+
232+
case XA_API_CMD_EXECUTE:
233+
if (idx == XA_CMD_TYPE_DO_EXECUTE) {
234+
uint32_t to_copy;
235+
236+
state->bytes_produced = 0;
237+
state->bytes_consumed = 0;
238+
239+
if (state->input_over) {
240+
state->eos_set_count++;
241+
if (state->eos_set_count > PCM_DEC_COUNT_SINCE_EOS_TO_DONE) {
242+
state->exec_done = 1;
243+
return XA_PCMDEC_EXECUTE_NONFATAL_INSUFFICIENT_DATA;
244+
}
245+
}
246+
247+
/* Safety check for buffers - should not happen */
248+
if (!state->input_buf || !state->output_buf) {
249+
/* Consume input even if buffers invalid to avoid hang */
250+
state->bytes_consumed = state->input_bytes;
251+
state->bytes_produced = 0;
252+
return XA_NO_ERROR;
253+
}
254+
255+
/* Copy PCM data from input to output */
256+
to_copy = state->input_bytes;
257+
if (to_copy > state->output_buf_size)
258+
to_copy = state->output_buf_size;
259+
260+
if (to_copy > 0) {
261+
memcpy_s(state->output_buf, state->output_buf_size,
262+
state->input_buf, to_copy);
263+
state->bytes_produced = to_copy;
264+
state->bytes_consumed = to_copy;
265+
} else {
266+
state->bytes_consumed = 0;
267+
}
268+
269+
return XA_NO_ERROR;
270+
} else if (idx == XA_CMD_TYPE_DONE_QUERY) {
271+
/* Query if execution is done */
272+
*(WORD32 *)value = state->exec_done;
273+
return XA_NO_ERROR;
274+
}
275+
return XA_API_FATAL_INVALID_CMD_TYPE;
276+
277+
default:
278+
return XA_API_FATAL_INVALID_CMD;
279+
}
280+
}

0 commit comments

Comments
 (0)