Skip to content
Open
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
4 changes: 4 additions & 0 deletions examples/app_1mic_override_record/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
*.svg
*.png
*.bin
*.wav
26 changes: 26 additions & 0 deletions examples/app_1mic_override_record/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
cmake_minimum_required(VERSION 3.21)
include($ENV{XMOS_CMAKE_PATH}/xcommon.cmake)
project(app_mic_array)

set(XMOS_SANDBOX_DIR ${CMAKE_CURRENT_LIST_DIR}/../../..)

# targets (IF NOT DEFINED OR NOT SUPPORTED)
if(NOT DEFINED APP_HW_TARGET OR NOT APP_HW_TARGET STREQUAL "XK-EVK-XU416")
message(FATAL_ERROR "This application is only supported for XK-EVK-XU416. Please re-run cmake with -DAPP_HW_TARGET=XK-EVK-XU416.")
endif()

set(APP_DEPENDENT_MODULES "lib_mic_array" "lib_sw_pll")

set(APP_COMPILER_FLAGS
-Os
-g
-report
-Wall
-fxscope
# Mic array config
-DMIC_ARRAY_CONFIG_SAMPLES_PER_FRAME=1
-DMIC_ARRAY_CONFIG_MIC_COUNT=2
-DMIC_ARRAY_CONFIG_USE_PDM_ISR=0
)

XMOS_REGISTER_APP()
40 changes: 40 additions & 0 deletions examples/app_1mic_override_record/convert.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# Copyright 2026 XMOS LIMITED.
# This Software is subject to the terms of the XMOS Public Licence: Version 1.

import argparse
import numpy as np
import soundfile as sf


def convert_to_wav(
input_file, output_file, num_channels=1, sample_rate=16000, bits_per_sample=32
):
with open(input_file, "rb") as inp_f:
data = np.frombuffer(inp_f.read(), dtype=np.int32)

if num_channels > 1:
# Interleaved layout: [ch0_s0, ch1_s0, ch0_s1, ch1_s1, ...]
# Reshape to [n_samples, n_channels] as expected by soundfile
data = data.reshape(-1, num_channels)

sf.write(output_file, data, sample_rate, subtype='PCM_32')
print(f"Converted {input_file} to {output_file} with {num_channels} channel(s), "
f"{sample_rate} Hz sample rate, and {bits_per_sample} bits per sample.")


if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Convert raw PCM binary to WAV.")
parser.add_argument("--input", default="mic_array_output.bin", help="Input binary file")
parser.add_argument("--output", default="output.wav", help="Output WAV file")
parser.add_argument("--channels", default=2, type=int, help="Number of channels (default: 1)")
parser.add_argument("--rate", default=16000, type=int, help="Sample rate in Hz (default: 16000)")
parser.add_argument("--bits", default=32, type=int, help="Bits per sample (default: 32)")
args = parser.parse_args()

convert_to_wav(
input_file=args.input,
output_file=args.output,
num_channels=args.channels,
sample_rate=args.rate,
bits_per_sample=args.bits,
)
37 changes: 37 additions & 0 deletions examples/app_1mic_override_record/run.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
#!/usr/bin/env python3
import subprocess
import numpy as np
from scipy.signal import chirp
import matplotlib.pyplot as plt

import soundfile as sf
import sounddevice as sd

import threading

def play_chirp():
print("Playing chirp...")
sample_rate = 44100
t = np.linspace(0, 10, int(sample_rate * 10))
chirp_signal = chirp(t, f0=10, f1=7000, t1=10, method='linear')
chirp_signal *= 0.1
sd.play(chirp_signal, sample_rate)
sd.wait()

threading.Thread(target=play_chirp, daemon=True).start()
subprocess.run(['xrun', '--xscope', 'bin/app_mic_array.xe'])
subprocess.run(['python', 'convert.py'])

# Plot both channels
audio, sr = sf.read('output.wav')
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(10, 6))
ax1.specgram(audio[:, 0], Fs=sr, cmap='viridis')
ax1.set_ylabel('Frequency (Hz)')
ax1.set_title('Channel 1')
ax2.specgram(audio[:, 1], Fs=sr, cmap='viridis')
ax2.set_ylabel('Frequency (Hz)')
ax2.set_xlabel('Time (s)')
ax2.set_title('Channel 2')
plt.tight_layout()
plt.savefig('analysis.svg')
plt.show()
149 changes: 149 additions & 0 deletions examples/app_1mic_override_record/src/app.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
// Copyright 2026 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>

#include <xscope.h>
#include <xcore/chanend.h>
#include <xcore/channel.h>
#include <xcore/parallel.h>
#include <xcore/hwtimer.h>

#include "app_config.h"
#include "mic_array.h"
#include "sw_pll.h"

#define APP_FILENAME ("mic_array_output.bin")

DECLARE_JOB(user_mic, (chanend_t));
DECLARE_JOB(user_audio, (chanend_t));

pdm_rx_resources_t pdm_res_ddr = PDM_RX_RESOURCES_DDR(
PORT_MCLK_IN,
PORT_PDM_CLK,
PORT_PDM_DATA,
MIC_ARRAY_CONFIG_MCLK_FREQ,
MIC_ARRAY_CONFIG_PDM_FREQ,
MIC_CLKBLK_A,
MIC_CLKBLK_B);

pdm_rx_resources_t pdm_res_sdr = PDM_RX_RESOURCES_SDR(
PORT_MCLK_IN,
PORT_PDM_CLK,
PORT_PDM_DATA,
MIC_ARRAY_CONFIG_MCLK_FREQ,
MIC_ARRAY_CONFIG_PDM_FREQ,
MIC_CLKBLK_A);

void user_mic(chanend_t c_mic_audio)
{
int ret = sw_pll_fixed_clock(MIC_ARRAY_CONFIG_MCLK_FREQ, SW_PLL_TILE_1);
xassert(ret == 0 && "Error: Failed to configure PLL for MCLK generation");
while (1)
{
unsigned override = chan_in_word(c_mic_audio);
if (override == 2) {return;}
if (override) {
mic_array_enable_1mic_override();
mic_array_init(&pdm_res_sdr, NULL, APP_OUT_FREQ_HZ);
}
else {
mic_array_init(&pdm_res_ddr, NULL, APP_OUT_FREQ_HZ);
}
mic_array_start(c_mic_audio);
}
}

void user_audio(chanend_t c_mic_audio)
{
// init mic array for 1 mic
chan_out_word(c_mic_audio, 1);
uint32_t chans_read = 1;

static int32_t WORD_ALIGNED tmp_buff[APP_BUFF_SIZE] = {0};
int32_t *buff_ptr = &tmp_buff[0];
unsigned frame_counter = APP_N_FRAMES;
int32_t samps[APP_MIC_COUNT * APP_N_SAMPLES] = {0};

hwtimer_t tmr = hwtimer_alloc();
unsigned t0 = 0, t1 = 0;
unsigned t2 = 0, t3 = 0;
uint64_t num = 0;
uint64_t den = 0;

printf("record start\n");
t2 = hwtimer_get_time(tmr);
while (frame_counter--)
{
// at APP_FRAME_TRANS reinitialise mic array with 2 mics
if (frame_counter == APP_FRAME_TRANS) {
ma_shutdown(c_mic_audio);
chan_out_word(c_mic_audio, 0);
chans_read = APP_MIC_COUNT;
}

t0 = hwtimer_get_time(tmr);
ma_frame_rx(samps, (chanend_t)c_mic_audio, chans_read, APP_N_SAMPLES);

for (unsigned i = 0; i < APP_N_SAMPLES * APP_MIC_COUNT; i ++) {
buff_ptr[i] = samps[i];
}
buff_ptr += APP_N_SAMPLES * APP_MIC_COUNT;

t1 = hwtimer_get_time(tmr);
num += (t1 - t0);
den += 1;
}
t3 = hwtimer_get_time(tmr);
printf("record end\n");

// Profile the average time taken per frame
const float ma_expected = (float)(APP_N_SAMPLES) / (float)(APP_OUT_FREQ_HZ);
const float tilef = 600.0;
const float ref = tilef / (5.0 + 1.0);

float avg = (float)num / (float)den;
float total = (float)(t3 - t2);
float avg_us = avg / ref;
float total_us = total / ref;
float ma_exp_us = ma_expected * 1e6;
float perc_err = ((avg_us - ma_exp_us) / ma_exp_us) * 100.0;

printf("Tile freq: %.2f MHz\n", tilef);
printf("Reference freq: %.2f MHz\n", ref);
printf("ma_frame_rx avg: %.2f ticks\n", avg);
printf("ma_frame_rx avg: %.2f us\n", avg_us);
printf("ma_frame_rx expected: %.2f us\n", ma_exp_us);
printf("ma_frame_rx error: %.2f %%\n", perc_err);
printf("total ticks: %.2f\n", total);
printf("total us: %.2f us\n", total_us);

// write samples to a binary file
printf("Writing output to %s\n", APP_FILENAME);
FILE *f = fopen(APP_FILENAME, "wb");
assert(f != NULL);
fwrite(tmp_buff, sizeof(int32_t), APP_BUFF_SIZE, f);
fclose(f);
ma_shutdown(c_mic_audio);
chan_out_word(c_mic_audio, 2);
printf("Done\n");
}

void main_tile_1(){
channel_t c_mic_audio = chan_alloc();
xscope_mode_lossless();

// Parallel Jobs
PAR_JOBS(
PJOB(user_mic, (c_mic_audio.end_a)),
PJOB(user_audio, (c_mic_audio.end_b))
);
chan_free(c_mic_audio);
}

void main_tile_0(){
// intentionally left empty
return;
}
27 changes: 27 additions & 0 deletions examples/app_1mic_override_record/src/app_config.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// Copyright 2026 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.

#pragma once

#include <vx_ports.h>
#define PORT_MCLK_IN VX_PORT_1D
#define PORT_PDM_CLK VX_PORT_1G
#define PORT_PDM_DATA VX_PORT_1F
#define MIC_CLKBLK_A VX_CLKBLK_1
#define MIC_CLKBLK_B VX_CLKBLK_2

// -------------------- Frecuency and Port definitions --------------------
#define MIC_ARRAY_CONFIG_MCLK_FREQ (24576000) /* 24 MHz */
#define MIC_ARRAY_CONFIG_PDM_FREQ (3072000) /* 768 KHz */
#define MIC_ARRAY_CONFIG_PORT_MCLK PORT_MCLK_IN /* X0D11, J14 - Pin 15, '11' */
#define MIC_ARRAY_CONFIG_PORT_PDM_CLK PORT_PDM_CLK /* X0D00, J14 - Pin 2, '00' */
#define MIC_ARRAY_CONFIG_PORT_PDM_DATA PORT_PDM_DATA /* X0D14..X0D21 | J14 - Pin 3,5,12,14 and Pin 6,7,10,11 */

// ------------------------- App Definitions -----------------------------------
#define APP_N_SAMPLES (MIC_ARRAY_CONFIG_SAMPLES_PER_FRAME)
#define APP_MIC_COUNT (MIC_ARRAY_CONFIG_MIC_COUNT)
#define APP_OUT_FREQ_HZ (16000)
#define APP_SAMPLE_SECONDS (2)
#define APP_N_FRAMES (APP_OUT_FREQ_HZ * APP_SAMPLE_SECONDS / APP_N_SAMPLES)
#define APP_FRAME_TRANS (APP_N_FRAMES / 2)
#define APP_BUFF_SIZE (APP_N_FRAMES * APP_N_SAMPLES * APP_MIC_COUNT)
18 changes: 18 additions & 0 deletions examples/app_1mic_override_record/src/config.xscope
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>

<!-- ======================================================= -->
<!-- The 'ioMode' attribute on the xSCOPEconfig -->
<!-- element can take the following values: -->
<!-- "none", "basic", "timed" -->
<!-- -->
<!-- The 'type' attribute on Probe -->
<!-- elements can take the following values: -->
<!-- "STARTSTOP", "CONTINUOUS", "DISCRETE", "STATEMACHINE" -->
<!-- -->
<!-- The 'datatype' attribute on Probe -->
<!-- elements can take the following values: -->
<!-- "NONE", "UINT", "INT", "FLOAT" -->
<!-- ======================================================= -->

<xSCOPEconfig ioMode="basic" enabled="true">
</xSCOPEconfig>
11 changes: 11 additions & 0 deletions examples/app_1mic_override_record/src/mapfile.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// Copyright 2026 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
#include <netmain.h>

extern void main_tile_0();
extern void main_tile_1();

NETWORK_MAIN(
TILE_MAIN(main_tile_1, 1, ()),
TILE_MAIN(main_tile_0, 0, ())
)