Skip to content

Commit 02b32f5

Browse files
committed
Audio: Phase Vocoder: Tune: Add setup script
This patch adds Octave script setup_phase_vocoder.m that exports the configuration blobs for module initialization for desired STFT processing parameters: Window type, FFT length and hop. Also two scripts are provided to run the module in sof-testbench4 with s32 and s16 sample formats. The script applies all playback speeds in range 0.5 to 2.0 with step of 0.1. Signed-off-by: Seppo Ingalsuo <seppo.ingalsuo@linux.intel.com>
1 parent fc997c4 commit 02b32f5

3 files changed

Lines changed: 276 additions & 0 deletions

File tree

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
#!/bin/bash
2+
set -euo pipefail
3+
4+
if [ $# -lt 2 ]; then
5+
echo "Usage: $0 <input_file.wav/flac/etc.> <output_file.wav>" >&2
6+
exit 1
7+
fi
8+
9+
: "${SOF_WORKSPACE:?SOF_WORKSPACE must be set}"
10+
11+
CLIP_IN=$1
12+
WAV_OUT=$2
13+
TESTBENCH=$SOF_WORKSPACE/sof/tools/testbench/build_testbench/install/bin/sof-testbench4
14+
TPLG_DIR=$SOF_WORKSPACE/sof/tools/build_tools/topology/topology2/development
15+
TPLG=sof-hda-benchmark-phase_vocoder16.tplg
16+
17+
RAW_IN=$(mktemp --suffix=_in.raw)
18+
RAW_OUT=$(mktemp --suffix=_out.raw)
19+
CONTROL_SCRIPT_LONG=$(mktemp --suffix=_controls.sh)
20+
trap 'rm -f "$RAW_IN" "$RAW_OUT" "$CONTROL_SCRIPT_LONG"' EXIT
21+
22+
# Uncomment to run under valgrind:
23+
# VALGRIND=(valgrind --leak-check=full --track-origins=yes)
24+
VALGRIND=()
25+
CONTROL_SCRIPT=(-s "$CONTROL_SCRIPT_LONG")
26+
27+
# Generated script is consumed by the testbench's -s option, not executed
28+
# directly by a shell.
29+
{
30+
echo "#!/bin/sh"
31+
echo "amixer -c0 cset name='Analog Playback Phase Vocoder speed' 0.5"
32+
echo "amixer -c0 cset name='Analog Playback Phase Vocoder enable' on"
33+
for s in 0.5 0.6 0.7 0.8 0.9 1.0 1.1 1.2 1.3 1.4 1.5 1.6 1.7 1.8 1.9 \
34+
2.0 1.9 1.8 1.7 1.6 1.5 1.4 1.3 1.2 1.1 1.0; do
35+
echo "sleep 1"
36+
echo "amixer -c0 cset name='Analog Playback Phase Vocoder speed' $s"
37+
done
38+
echo "amixer -c0 cset name='Analog Playback Phase Vocoder enable' off"
39+
} > "$CONTROL_SCRIPT_LONG"
40+
41+
sox "$CLIP_IN" --encoding signed-integer -L -r 48000 -c 2 -b 16 "$RAW_IN"
42+
"${VALGRIND[@]}" "$TESTBENCH" "${CONTROL_SCRIPT[@]}" \
43+
-r 48000 -c 2 -b S16_LE -p 1,2 \
44+
-t "$TPLG_DIR/$TPLG" -i "$RAW_IN" -o "$RAW_OUT"
45+
sox --encoding signed-integer -L -r 48000 -c 2 -b 16 "$RAW_OUT" "$WAV_OUT"
46+
47+
printf '\n'
48+
file "$WAV_OUT"
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
#!/bin/bash
2+
set -euo pipefail
3+
4+
if [ $# -lt 2 ]; then
5+
echo "Usage: $0 <input_file.wav/flac/etc.> <output_file.wav>" >&2
6+
exit 1
7+
fi
8+
9+
: "${SOF_WORKSPACE:?SOF_WORKSPACE must be set}"
10+
11+
CLIP_IN=$1
12+
WAV_OUT=$2
13+
TESTBENCH=$SOF_WORKSPACE/sof/tools/testbench/build_testbench/install/bin/sof-testbench4
14+
TPLG_DIR=$SOF_WORKSPACE/sof/tools/build_tools/topology/topology2/development
15+
TPLG=sof-hda-benchmark-phase_vocoder32.tplg
16+
17+
RAW_IN=$(mktemp --suffix=_in.raw)
18+
RAW_OUT=$(mktemp --suffix=_out.raw)
19+
CONTROL_SCRIPT_LONG=$(mktemp --suffix=_controls.sh)
20+
trap 'rm -f "$RAW_IN" "$RAW_OUT" "$CONTROL_SCRIPT_LONG"' EXIT
21+
22+
# Uncomment to run under valgrind:
23+
# VALGRIND=(valgrind --leak-check=full --track-origins=yes)
24+
VALGRIND=()
25+
CONTROL_SCRIPT=(-s "$CONTROL_SCRIPT_LONG")
26+
27+
# Generated script is consumed by the testbench's -s option, not executed
28+
# directly by a shell.
29+
{
30+
echo "#!/bin/sh"
31+
echo "amixer -c0 cset name='Analog Playback Phase Vocoder speed' 0.5"
32+
echo "amixer -c0 cset name='Analog Playback Phase Vocoder enable' on"
33+
for s in 0.5 0.6 0.7 0.8 0.9 1.0 1.1 1.2 1.3 1.4 1.5 1.6 1.7 1.8 1.9 \
34+
2.0 1.9 1.8 1.7 1.6 1.5 1.4 1.3 1.2 1.1 1.0; do
35+
echo "sleep 1"
36+
echo "amixer -c0 cset name='Analog Playback Phase Vocoder speed' $s"
37+
done
38+
echo "amixer -c0 cset name='Analog Playback Phase Vocoder enable' off"
39+
} > "$CONTROL_SCRIPT_LONG"
40+
41+
sox "$CLIP_IN" --encoding signed-integer -L -r 48000 -c 2 -b 32 "$RAW_IN"
42+
"${VALGRIND[@]}" "$TESTBENCH" "${CONTROL_SCRIPT[@]}" \
43+
-r 48000 -c 2 -b S32_LE -p 1,2 \
44+
-t "$TPLG_DIR/$TPLG" -i "$RAW_IN" -o "$RAW_OUT"
45+
sox --encoding signed-integer -L -r 48000 -c 2 -b 32 "$RAW_OUT" "$WAV_OUT"
46+
47+
printf '\n'
48+
file "$WAV_OUT"
Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
% setup_phase_vocoder()
2+
%
3+
% Create binary configuration blob for Phase Vocoder component.
4+
5+
% SPDX-License-Identifier: BSD-3-Clause
6+
%
7+
% Copyright (c) 2026, Intel Corporation.
8+
9+
function setup_phase_vocoder(cfg)
10+
11+
cfg.tools_path = '../../../../tools/';
12+
cfg.tplg_path = [cfg.tools_path 'topology/topology2/include/components/phase_vocoder/'];
13+
cfg.common_path = [cfg.tools_path 'tune/common'];
14+
cfg.tplg_ver = 2;
15+
cfg.ipc_ver = 4;
16+
cfg.mono = 0;
17+
cfg.sample_frequency = 48000;
18+
19+
cfg.frame_length = 256; % 5.3 ms
20+
cfg.frame_shift = 128; % 2.7 ms
21+
cfg.window_type = 'hann';
22+
cfg.tplg_fn = 'hann_256_128.conf';
23+
export_phase_vocoder_setup(cfg);
24+
25+
cfg.frame_length = 512; % 10.7 ms
26+
cfg.frame_shift = 128; % 2.7 ms
27+
cfg.window_type = 'hann';
28+
cfg.tplg_fn = 'hann_512_128.conf';
29+
export_phase_vocoder_setup(cfg);
30+
31+
cfg.frame_length = 512; % 10.7 ms
32+
cfg.frame_shift = 256; % 5.3 ms
33+
cfg.window_type = 'hann';
34+
cfg.tplg_fn = 'hann_512_256.conf';
35+
export_phase_vocoder_setup(cfg);
36+
37+
% Default configuration, stereo
38+
cfg.frame_length = 1024; % 21.3 ms
39+
cfg.frame_shift = 256; % 5.3 ms
40+
cfg.window_type = 'hann';
41+
cfg.tplg_fn = 'hann_1024_256.conf';
42+
export_phase_vocoder_setup(cfg);
43+
44+
% Default configuration, force mono processing
45+
cfg.mono = 1;
46+
cfg.tplg_fn = 'hann_1024_256_mono.conf';
47+
export_phase_vocoder_setup(cfg);
48+
49+
end
50+
51+
function export_phase_vocoder_setup(cfg)
52+
53+
% Use blob tool from EQ
54+
addpath(cfg.common_path);
55+
56+
% Blob size, size plus reserved(8) + current parameters
57+
nbytes_data = 64;
58+
59+
% Get ABI information
60+
[abi_bytes, nbytes_abi] = sof_get_abi(nbytes_data, cfg.ipc_ver);
61+
62+
% Initialize correct size uint8 array
63+
nbytes = nbytes_abi + nbytes_data;
64+
b8 = uint8(zeros(1,nbytes));
65+
66+
% Insert ABI header
67+
fprintf(1, 'PHASE_VOCODER blob size is %d, ABI header is %d, data is %d\n',nbytes, nbytes_abi, nbytes_data);
68+
b8(1:nbytes_abi) = abi_bytes;
69+
j = nbytes_abi + 1;
70+
71+
% Apply default PHASE_VOCODER configuration, first struct header and reserved, then data
72+
[b8, j] = add_w32b(nbytes_data, b8, j);
73+
for i = 1:8
74+
[b8, j] = add_w32b(0, b8, j);
75+
end
76+
77+
fft_length = cfg.frame_length;
78+
fft_hop = cfg.frame_shift;
79+
[window_idx, window_gain_comp] = get_window(cfg, fft_length, fft_hop);
80+
81+
v = q_convert(cfg.sample_frequency, 0); [b8, j] = add_w32b(v, b8, j);
82+
v = q_convert(window_gain_comp, 31); [b8, j] = add_w32b(v, b8, j);
83+
v = 0; [b8, j] = add_w32b(v, b8, j); % reserved
84+
v = cfg.mono; [b8, j] = add_w16b(v, b8, j);
85+
v = fft_length; [b8, j] = add_w16b(v, b8, j);
86+
v = fft_hop; [b8, j] = add_w16b(v, b8, j);
87+
v = 0; [b8, j] = add_w16b(v, b8, j); % reserved
88+
v = 0; [b8, j] = add_w32b(v, b8, j); % reserved_pad
89+
v = window_idx; [b8, j] = add_w32b(v, b8, j); % enum window
90+
91+
% Export
92+
switch cfg.tplg_ver
93+
case 2
94+
sof_tplg2_write([cfg.tplg_path cfg.tplg_fn], b8, "phase_vocoder_config", ...
95+
"Exported PHASE_VOCODER configuration", ...
96+
"cd src/audio/phase_vocoder/tune; octave setup_phase_vocoder.m");
97+
otherwise
98+
error("Illegal cfg.tplg_ver, use 2 topology v2.");
99+
end
100+
101+
rmpath(cfg.common_path);
102+
103+
end
104+
105+
%% Helper functions
106+
107+
function [idx, iwg] = get_window(cfg, len, hop)
108+
switch lower(cfg.window_type)
109+
case 'rectangular'
110+
win = boxcar(len);
111+
idx = 0;
112+
case 'blackman'
113+
win = blackman(len);
114+
idx = 1;
115+
case 'hamming'
116+
win = hamming(len);
117+
idx = 2;
118+
case 'hann'
119+
win = hann(len);
120+
idx = 3;
121+
otherwise
122+
error('Unknown window type');
123+
end
124+
iwg = hop / sum(win.^2);
125+
end
126+
127+
function bytes = w8b(word)
128+
bytes = uint8(zeros(1,1));
129+
bytes(1) = bitand(word, 255);
130+
end
131+
132+
function bytes = w16b(word)
133+
sh = [0 -8];
134+
bytes = uint8(zeros(1,2));
135+
bytes(1) = bitand(bitshift(word, sh(1)), 255);
136+
bytes(2) = bitand(bitshift(word, sh(2)), 255);
137+
end
138+
139+
function bytes = w32b(word)
140+
sh = [0 -8 -16 -24];
141+
bytes = uint8(zeros(1,4));
142+
bytes(1) = bitand(bitshift(word, sh(1)), 255);
143+
bytes(2) = bitand(bitshift(word, sh(2)), 255);
144+
bytes(3) = bitand(bitshift(word, sh(3)), 255);
145+
bytes(4) = bitand(bitshift(word, sh(4)), 255);
146+
end
147+
148+
function n = q_convert(val, q)
149+
n = round(val * 2^q);
150+
end
151+
152+
function [blob8, j] = add_w8b(v, blob8, j)
153+
if j > length(blob8)
154+
error('Blob size is not sufficient');
155+
end
156+
blob8(j) = w8b(v);
157+
j = j + 1;
158+
end
159+
160+
function [blob8, j] = add_w16b(v, blob8, j)
161+
if j + 1 > length(blob8)
162+
error('Blob size is not sufficient');
163+
end
164+
if v < 0
165+
v = 2^16 + v;
166+
end
167+
blob8(j : j + 1) = w16b(v);
168+
j = j + 2;
169+
end
170+
171+
function [blob8, j] = add_w32b(v, blob8, j)
172+
if j + 3 > length(blob8)
173+
error('Blob size is not sufficient');
174+
end
175+
if v < 0
176+
v = 2^32 + v;
177+
end
178+
blob8(j : j + 3) = w32b(v);
179+
j = j + 4;
180+
end

0 commit comments

Comments
 (0)