ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
c6_cam_audio.c
Go to the documentation of this file.
1
19
20#include <stdint.h>
21#include <string.h>
22
23#include "c6_camera_server.h"
24#include "ra8_attributes.h"
25#include "ra8_audio.h"
27#include "ra8_board_ek_ra8d2.h"
28#include "ra8_err.h"
29#include "ra8_register_guard.h"
30
57
59typedef struct {
60 uint32_t write_index;
61 uint32_t sample_count;
64
69
71[[gnu::section(".sdram_data"),
72 gnu::aligned(
74[[gnu::section(".sdram_data"), gnu::aligned(8)]] static uint8_t s_audio_wav[k_c6_cam_wav_capacity];
75
77static volatile uint8_t s_audio_active_bank;
79
89static const uint8_t s_wav_tag_riff[] = {'R', 'I', 'F', 'F'};
90static const uint8_t s_wav_tag_wavefmt[] = {'W', 'A', 'V', 'E', 'f', 'm', 't', ' '};
91static const uint8_t s_wav_tag_data[] = {'d', 'a', 't', 'a'};
92
105RA8_INTERNAL static void internal_c6_cam_audio_put_u16(uint8_t* out, uint16_t value)
106{
107 out[0] = (uint8_t)(value & (uint16_t)k_c6_cam_byte_mask);
108 out[1] = (uint8_t)(value >> 8U);
109}
110
123RA8_INTERNAL static void internal_c6_cam_audio_put_u32(uint8_t* out, uint32_t value)
124{
125 out[0] = (uint8_t)(value & (uint32_t)k_c6_cam_byte_mask);
126 out[1] = (uint8_t)((value >> 8U) & (uint32_t)k_c6_cam_byte_mask);
127 out[2] = (uint8_t)((value >> 16U) & (uint32_t)k_c6_cam_byte_mask);
128 out[3] = (uint8_t)(value >> (uint32_t)k_c6_cam_shift_24);
129}
130
146RA8_INTERNAL static int16_t internal_c6_cam_audio_s16(int32_t sample)
147{
148 const int64_t amplified = (int64_t)sample * (int64_t)k_c6_cam_audio_gain;
149 int32_t scaled = (int32_t)(amplified / 16);
150 if (scaled > INT16_MAX) {
151 scaled = INT16_MAX;
152 } else if (scaled < INT16_MIN) {
153 scaled = INT16_MIN;
154 }
155 return (int16_t)scaled;
156}
157
171{
172 (void)ctx;
173 if ((frame == nullptr) || (frame->data == nullptr) ||
175 return;
176 }
177 const uint8_t bank = s_audio_active_bank;
178 c6_cam_audio_bank_t* meta = &s_audio_banks[bank];
179 const int32_t* samples = (const int32_t*)frame->data;
180 for (uint32_t i = 0U; i < frame->sample_count; ++i) {
181 s_audio_ring[bank][meta->write_index] = samples[i];
182 meta->write_index += 1U;
183 if (meta->write_index == (uint32_t)k_c6_cam_audio_ring_samples) {
184 meta->write_index = 0U;
185 }
186 }
187 if (meta->sample_count < (uint32_t)k_c6_cam_audio_ring_samples) {
188 const uint32_t room = (uint32_t)k_c6_cam_audio_ring_samples - meta->sample_count;
189 meta->sample_count += (frame->sample_count < room) ? frame->sample_count : room;
190 }
191 const uint32_t retained_ms =
192 (meta->sample_count * (uint32_t)k_c6_cam_ms_per_second) / (uint32_t)k_c6_cam_audio_rate_hz;
193 const uint32_t frame_end_ms =
194 frame->timestamp_ms +
195 ((frame->sample_count * (uint32_t)k_c6_cam_ms_per_second) / (uint32_t)k_c6_cam_audio_rate_hz);
196 meta->oldest_timestamp_ms = frame_end_ms - retained_ms;
197}
198
228
230{
232 if (err != k_ra8_ok) {
233 return err;
234 }
237 if (err != k_ra8_ok) {
238 return err;
239 }
240 if (board.sample_rate_hz != (uint32_t)k_c6_cam_audio_rate_hz) {
242 }
243 const ra8_audio_source_pdm_cfg_t cfg = {
244 .pdm = board.pdm,
245 .sample_rate_hz = board.sample_rate_hz,
246 .samples_per_frame = (uint32_t)k_c6_cam_audio_frame_samples,
247 .settle_ms = (uint32_t)k_c6_cam_audio_settle_ms,
248 .discard_samples = (uint32_t)k_c6_cam_audio_discard,
249 .poll_attempts = (uint32_t)k_c6_cam_audio_poll_attempts,
250 .channel = board.channel,
251 .valid_bits = board.valid_bits,
252 .irq_priority = (uint8_t)k_c6_cam_audio_irq_priority,
253 };
255 if (err != k_ra8_ok) {
256 return err;
257 }
258 const ra8_audio_buffer_t buffer = {
259 .data = s_audio_frame,
260 .capacity = (uint32_t)sizeof(s_audio_frame),
261 };
263 &buffer,
265 nullptr);
266 if (err != k_ra8_ok) {
268 }
269 return err;
270}
271
273c6_cam_audio_snapshot_wav(const uint8_t** out_wav, uint32_t* out_bytes, uint32_t* out_timestamp_ms)
274{
275 if ((out_wav == nullptr) || (out_bytes == nullptr) || (out_timestamp_ms == nullptr)) {
276 return k_ra8_err_null_ptr;
277 }
278 *out_wav = nullptr;
279 *out_bytes = 0U;
280 *out_timestamp_ms = 0U;
283 const uint8_t snapshot_bank = s_audio_active_bank;
284 const uint32_t samples = s_audio_banks[snapshot_bank].sample_count;
285 if (samples == 0U) {
287 return k_ra8_err_no_data;
288 }
289 const uint32_t write = s_audio_banks[snapshot_bank].write_index;
290 const uint32_t timestamp_ms = s_audio_banks[snapshot_bank].oldest_timestamp_ms;
291 const uint8_t capture_bank = snapshot_bank ^ 1U;
292 s_audio_banks[capture_bank] = (c6_cam_audio_bank_t){};
293 s_audio_active_bank = capture_bank;
295
296 const uint32_t oldest = (samples == (uint32_t)k_c6_cam_audio_ring_samples) ? write : 0U;
298 for (uint32_t i = 0U; i < samples; ++i) {
299 uint32_t ring_index = oldest + i;
300 if (ring_index >= (uint32_t)k_c6_cam_audio_ring_samples) {
301 ring_index -= (uint32_t)k_c6_cam_audio_ring_samples;
302 }
303 const uint16_t pcm =
304 (uint16_t)internal_c6_cam_audio_s16(s_audio_ring[snapshot_bank][ring_index]);
307 pcm);
308 }
309 *out_wav = s_audio_wav;
310 *out_bytes =
311 (uint32_t)k_c6_cam_wav_header_bytes + (samples * (uint32_t)k_c6_cam_wav_sample_bytes);
312 *out_timestamp_ms = timestamp_ms;
313 return k_ra8_ok;
314}
static const uint8_t s_wav_tag_riff[]
The three fixed-width chunk tags of a canonical PCM WAV header.
static void internal_c6_cam_audio_put_u32(uint8_t *out, uint32_t value)
Write one little-endian 32-bit scalar.
static void internal_c6_cam_audio_write_header(uint32_t samples)
Serialize the canonical mono PCM-S16LE WAV header.
static c6_cam_audio_bank_t s_audio_banks[k_c6_cam_audio_bank_count]
ra8_err_t c6_cam_audio_snapshot_wav(const uint8_t **out_wav, uint32_t *out_bytes, uint32_t *out_timestamp_ms)
Snapshot the rolling microphone ring as mono PCM-S16LE WAV.
static ra8_audio_source_t s_audio_source
Fixed audio source objects and interrupt assembly scratch.
static volatile uint8_t s_audio_active_bank
Active ISR bank and per-bank metadata guarded during handoff.
static ra8_audio_source_pdm_state_t s_audio_state
ra8_err_t c6_cam_audio_start(void)
Start continuous MIC1 capture into the SDRAM audio ring.
static int32_t s_audio_ring[k_c6_cam_audio_bank_count][k_c6_cam_audio_ring_samples]
Ping-pong PCM rings and generated WAV live in external SDRAM.
static uint8_t s_audio_wav[k_c6_cam_wav_capacity]
static const uint8_t s_wav_tag_data[]
static const uint8_t s_wav_tag_wavefmt[]
static void internal_c6_cam_audio_put_u16(uint8_t *out, uint16_t value)
Write one little-endian 16-bit scalar.
static void internal_c6_cam_audio_on_frame(void *ctx, const ra8_audio_frame_t *frame)
Append one complete audio frame to the active SDRAM ring.
c6_cam_audio_cfg_t
Audio capture, storage, and WAV constants.
@ k_c6_cam_shift_24
Most-significant byte shift.
@ k_c6_cam_wav_bits_off
WAV bits-per-sample field offset.
@ k_c6_cam_audio_rate_hz
Mono PCM sample rate.
@ k_c6_cam_audio_frame_samples
Samples assembled per IRQ frame.
@ k_c6_cam_ms_per_second
Milliseconds per second.
@ k_c6_cam_audio_bank_count
Ping-pong SDRAM capture banks.
@ k_c6_cam_wav_riff_overhead
RIFF size excluding 8-byte lead.
@ k_c6_cam_wav_sample_bytes
PCM-S16LE bytes per mono sample.
@ k_c6_cam_audio_discard
Startup transient samples.
@ k_c6_cam_audio_gain
WAV digital gain before scaling.
@ k_c6_cam_audio_ring_samples
One-second rolling sample window.
@ k_c6_cam_audio_irq_priority
PDM FIFO NVIC priority.
@ k_c6_cam_wav_audio_format_off
WAV audio-format field offset.
@ k_c6_cam_wav_capacity
Complete WAV capacity.
@ k_c6_cam_wav_data_size_off
WAV data-size field offset.
@ k_c6_cam_wav_channels_off
WAV channel-count field offset.
@ k_c6_cam_byte_mask
One serialized byte mask.
@ k_c6_cam_audio_poll_attempts
Initialization polling bound.
@ k_c6_cam_audio_settle_ms
Microphone/filter settling delay.
@ k_c6_cam_wav_header_bytes
Canonical PCM WAV header size.
@ k_c6_cam_wav_byte_rate_off
WAV byte-rate field offset.
static int16_t internal_c6_cam_audio_s16(int32_t sample)
Convert signed 20-bit PDM PCM to saturated signed 16-bit PCM.
static int32_t s_audio_frame[k_c6_cam_audio_frame_samples]
Shared contracts for ESP32-C6 browser camera servers.
-proof
Annotation-attribute framework macros for ra8-firmware.
#define RA8_INTERNAL
Marker that a function is intended to be static (file-local).
Transport-neutral, caller-owned audio capture facade.
ra8_err_t ra8_audio_source_stop(ra8_audio_source_t *source)
Stop and release a bound source backend.
Definition ra8_audio.c:265
@ k_ra8_audio_format_pcm_s32le
Signed 32-bit little-endian PCM.
Definition ra8_audio.h:40
ra8_err_t ra8_audio_source_stream_start(ra8_audio_source_t *source, const ra8_audio_buffer_t *buffer, ra8_audio_frame_callback_t callback, void *ctx)
Start asynchronous fixed-frame delivery into caller-owned scratch.
Definition ra8_audio.c:228
RA8 PDM-IF backend for the transport-neutral audio facade.
ra8_err_t ra8_audio_source_pdm_init(ra8_audio_source_t *source, ra8_audio_source_pdm_state_t *state, const ra8_audio_source_pdm_cfg_t *cfg)
Configure, start, settle, and bind one PDM audio source.
Board-support layer for the Renesas EK-RA8D2 v1 evaluation kit.
ra8_err_t ra8_board_pdm_mic_get_config(ra8_board_pdm_mic_t microphone, ra8_board_pdm_mic_config_t *out_config)
Return the validated SPH0690 filter policy for one board microphone.
@ k_ra8_board_pdm_mic1
Rising-edge MIC1 (SELECT tied low).
ra8_err_t ra8_board_pdm_mic_route(void)
Route P812/P502 to PDMCLK2/PDMDAT2.
Error Code Definitions for ra8-firmware.
@ k_ra8_err_no_data
No application data available (e.g.
Definition ra8_err.h:202
@ k_ra8_err_invalid_state
Module in wrong state for requested operation.
Definition ra8_err.h:161
@ k_ra8_ok
Success – operation completed with all postconditions satisfied.
Definition ra8_err.h:119
@ k_ra8_err_null_ptr
Pointer was NULL where a valid pointer was required.
Definition ra8_err.h:478
ra8_err_codes_t ra8_err_t
Canonical error-return type used by every ra8-firmware API.
Definition ra8_err.h:546
void * memcpy(void *dst, const void *src, size_t n)
Copy memory area between non-overlapping regions.
IRQ-masked read-modify-write helper for shared registers.
static void ra8_register_guard_exit(const ra8_register_guard_t *guard)
Exit a critical section: restore PRIMASK.
static void ra8_register_guard_enter(ra8_register_guard_t *guard)
Enter a critical section: save PRIMASK, mask interrupts.
Writer position and timing metadata for one SDRAM audio bank.
uint32_t write_index
Next sample index written by the ISR.
uint32_t oldest_timestamp_ms
Monotonic timestamp of the oldest sample.
uint32_t sample_count
Valid samples currently retained.
Caller-owned writable byte buffer.
Definition ra8_audio.h:44
Immutable view of one interleaved PCM frame.
Definition ra8_audio.h:56
ra8_audio_format_t format
PCM container layout.
Definition ra8_audio.h:64
uint32_t sample_count
Sample frames per channel.
Definition ra8_audio.h:59
const void * data
Borrowed interleaved PCM bytes.
Definition ra8_audio.h:57
uint32_t timestamp_ms
Capture-start monotonic milliseconds.
Definition ra8_audio.h:61
Complete PDM source initialization and capture policy.
Caller-owned mutable state for one PDM source backend.
Caller-owned handle binding an audio source backend and its state.
Definition ra8_audio.h:78
Board-owned PDM mechanism configuration for one microphone.
uint8_t channel
PDM-IF channel selector.
uint32_t sample_rate_hz
Decimated PCM sample rate.
ra8_pdm_channel_cfg_t pdm
SPH0690 filter coefficients.
uint8_t valid_bits
Significant PCM bits.
Opaque save-restore handle for IRQ masking.