This commit is contained in:
Steven Dan
2025-12-11 09:43:42 +08:00
commit d8b2974133
1822 changed files with 280037 additions and 0 deletions

View File

@@ -0,0 +1,2 @@
# Copyright 2024-2025 XMOS LIMITED.
# This Software is subject to the terms of the XMOS Public Licence: Version 1.

View File

@@ -0,0 +1,125 @@
# Copyright 2024-2025 XMOS LIMITED.
# This Software is subject to the terms of the XMOS Public Licence: Version 1.
"""
Helper functions for creating and validating audio files
"""
import numpy as np
import scipy
import matplotlib.pyplot as plt
from pathlib import Path
from audio_dsp.dsp import generic
def read_wav(path):
rate, data = scipy.io.wavfile.read(path)
if data.ndim == 1:
data = np.expand_dims(data, axis=1)
return rate, data
def write_wav(path, fs, data):
return scipy.io.wavfile.write(path, fs, data)
def read_and_truncate(path, f_bits=generic.Q_SIG):
"""Read wav and truncate the least significant fractional bits"""
rate, data = read_wav(path)
if data.dtype != np.int32:
raise TypeError(f"wav data type {data.dtype} not supported")
mask = ~int(2**(31 - f_bits) - 1)
print("mask ", mask)
return rate, data & mask
def correlate_and_diff(output_file, input_file, out_ch_start_end, in_ch_start_end, skip_seconds_start, skip_seconds_end, tol, corr_plot_file=None, verbose=False):
rate_out, data_out = scipy.io.wavfile.read(output_file)
rate_in, data_in = scipy.io.wavfile.read(input_file)
if data_out.ndim == 1:
data_out = data_out.reshape(len(data_out), 1)
if data_in.ndim == 1:
data_in = data_in.reshape(len(data_in), 1)
if rate_out != rate_in:
assert False, f"input and output file rates are not equal. input rate {rate_in}, output rate {rate_out}"
if data_in.dtype != np.int32:
if data_in.dtype == np.int16:
data_in = np.array(data_in, dtype=np.int32)
data_in = data_in * (2**16)
else:
assert False, "Unsupported data_in.dtype {data_in.dtype}"
if data_out.dtype != np.int32:
if data_out.dtype == np.int16:
data_out = np.array(data_out, dtype=np.int32)
else:
assert False, "Unsupported data_out.dtype {data_out.dtype}"
assert out_ch_start_end[1]-out_ch_start_end[0] == in_ch_start_end[1]-in_ch_start_end[0], "input and output files have different channel nos."
skip_samples_start = int(rate_out * skip_seconds_start)
skip_samples_end = int(rate_out * skip_seconds_end)
data_in = data_in[:,in_ch_start_end[0]:in_ch_start_end[1]+1]
data_out = data_out[:,out_ch_start_end[0]:out_ch_start_end[1]+1]
small_len = min(len(data_in), len(data_out), 64000)
data_in_small = data_in[skip_samples_start : small_len+skip_samples_start, :].astype(np.float64)
data_out_small = data_out[skip_samples_start : small_len+skip_samples_start, :].astype(np.float64)
corr = scipy.signal.correlate(data_in_small[:, 0], data_out_small[:, 0], "full")
delay = (corr.shape[0] // 2) - np.argmax(corr)
print(f"delay = {delay}")
if corr_plot_file != None:
plt.plot(corr)
plt.savefig(corr_plot_file)
plt.clf()
delay_orig = delay
data_size = min(data_in.shape[0], data_out.shape[0])
data_size -= skip_samples_end
print(f"compare {data_size - skip_samples_start} samples")
print(data_in.shape)
print(data_out.shape)
print(delay)
num_channels = out_ch_start_end[1]-out_ch_start_end[0]+1
all_close = True
max_diff = []
for ch in range(num_channels):
print(f"comparing ch {ch}")
close = np.isclose(
data_in[skip_samples_start : data_size - delay, ch],
data_out[skip_samples_start + delay : data_size, ch],
atol=tol,
)
print(f"ch {ch}, close = {np.all(close)}")
if verbose:
int_max_idxs = np.argwhere(close[:] == False)
print("shape = ", int_max_idxs.shape)
print(int_max_idxs)
if np.all(close) == False:
if int_max_idxs[0] != 0:
count = 0
for i in int_max_idxs:
if count < 100: # Print first 100 values that were not close
print(i, data_in[skip_samples_start+i, ch], data_out[skip_samples_start + delay + i, ch])
count += 1
diff = np.abs((data_in[skip_samples_start : data_size - delay, ch]) - (data_out[skip_samples_start + delay : data_size, ch]))
max_diff.append(np.amax(diff))
print(f"max diff value is {max_diff[-1]}")
all_close = all_close & np.all(close)
print(f"all_close: {np.all(all_close)}")
return all_close, max(max_diff), delay_orig

View File

@@ -0,0 +1,41 @@
# Copyright 2024-2025 XMOS LIMITED.
# This Software is subject to the terms of the XMOS Public Licence: Version 1.
"""
Utility functions for building and running the application within
the jupyter notebook
"""
import subprocess
import ipywidgets as widgets
from IPython import display
from pathlib import Path
import shutil
import os
BUILD_LOCK = "test_pipeline_build.lock"
def build(source_dir, build_dir, target):
"""
Attempt to build and xrun the application
"""
print("Build and run - output will be in the terminal if it is not displayed below\r")
cache = build_dir / "CMakeCache.txt"
print("Configuring...\r")
if cache.exists():
# Generator is already known by cmake
ret = subprocess.run([*(f"cmake -S {source_dir} -B {build_dir}".split())])
else:
# need to configure, default to Ninja because its better
generator = "Ninja" if shutil.which("ninja") else "Unix Makefiles"
ret = subprocess.run([*(f"cmake -S {source_dir} -B {build_dir} -G".split()), generator])
if ret.returncode:
print("Configuring failed, check log for details\r")
assert(0)
print("Compiling...\r")
if os.name == "nt":
ret = subprocess.run(f"cmake --build {build_dir} --target {target}".split())
else:
ret = subprocess.run(f"cmake --build {build_dir} --target {target} -j".split())
if ret.returncode:
print("ERROR: Building failed, check log for details\r")
assert(0)

View File

@@ -0,0 +1,130 @@
# Copyright 2022-2025 XMOS LIMITED.
# This Software is subject to the terms of the XMOS Public Licence: Version 1.
import xscope_fileio
import argparse
import shutil
import subprocess
import scipy.io.wavfile
import pathlib
from filelock import FileLock
import time
import subprocess
FORCE_ADAPTER_ID = None
def get_adapter_id():
# check the --adapter-id option
if FORCE_ADAPTER_ID is not None:
return FORCE_ADAPTER_ID
try:
xrun_out = subprocess.check_output(['xrun', '-l'], text=True, stderr=subprocess.STDOUT)
except subprocess.CalledProcessError as e:
print('Error: %s' % e.output)
assert False
xrun_out = xrun_out.split('\n')
# Check that the first 4 lines of xrun_out match the expected lines
expected_header = ["", "Available XMOS Devices", "----------------------", ""]
if len(xrun_out) < len(expected_header):
raise RuntimeError(
f"Error: xrun output:\n{xrun_out}\n"
f"does not contain expected header:\n{expected_header}"
)
header_match = True
for i, expected_line in enumerate(expected_header):
if xrun_out[i] != expected_line:
header_match = False
if not header_match:
raise RuntimeError(
f"Error: xrun output header:\n{xrun_out[:4]}\n"
f"does not match expected header:\n{expected_header}"
)
try:
if "No Available Devices Found" in xrun_out[4]:
raise RuntimeError(f"Error: No available devices found\n")
return
except IndexError:
raise RuntimeError(f"Error: xrun output is too short:\n{xrun_out}\n")
for line in xrun_out[6:]:
if line.strip():
adapterID = line[26:34].strip()
status = line[34:].strip()
else:
continue
print("adapter_id = ",adapterID)
return adapterID
def parse_arguments():
parser = argparse.ArgumentParser()
parser.add_argument("xe", nargs='?',
help=".xe file to run")
parser.add_argument('-i', '--input', type=pathlib.Path, required=True, help="input wav file")
parser.add_argument('-o', '--output', type=pathlib.Path, required=True, help="output wav file")
parser.add_argument('-n', '--num-out-channels', type=str, help="Number of channels in the output file. If unspecified, set to be same as the input number of channels",
default=None)
args = parser.parse_args()
return args
def run(xe, input_file, output_file, num_out_channels, pipeline_stages=1, return_stdout=False):
"""
Run an xe on an xcore with fileio enabled.
Parameters
----------
xe : str
file to run
input_file : str
wav file to play
output_file : str
output wav file
num_out_channels : int
number of channels the pipeline will output.
pipeline_stages : int
Number of stages in the pipeline, the frame delay from the input to the output. This
is used to synchronise inputs and outputs. Set to 1 to get unmodified output from the
pipeline.
return_stdout : bool
if true the process output will be returned
"""
# Create the cmd line string
args = f"-i {input_file} -o {output_file} -n {num_out_channels} -t {pipeline_stages}"
with FileLock("run_pipeline.lock"):
with open("args.txt", "w") as fp:
fp.write(args)
adapter_id = get_adapter_id()
print("Running on adapter_id ",adapter_id)
if return_stdout == False:
xscope_fileio.run_on_target(adapter_id, xe)
time.sleep(0.1)
else:
with open("stdout.txt", "w+") as ff:
xscope_fileio.run_on_target(adapter_id, xe, stdout=ff)
ff.seek(0)
stdout = ff.readlines()
return stdout
if __name__ == "__main__":
args = parse_arguments()
assert args.xe is not None, "Specify vaild .xe file"
print(f"args.input = {args.input}")
if(args.num_out_channels == None):
rate, data = scipy.io.wavfile.read(args.input)
if data.ndim == 1:
args.num_out_channels = 1
else:
args.num_out_channels = data.shape[1]
print(f"num_out_channels = {args.num_out_channels}")
run(args.xe, args.input, args.output, args.num_out_channels)