Skip to content

Commit eb07acf

Browse files
authored
Add C API for source separation (#3404)
1 parent 1ffe82b commit eb07acf

9 files changed

Lines changed: 755 additions & 3 deletions

File tree

.github/workflows/c-api.yaml

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ on:
44
push:
55
branches:
66
- master
7+
- c-api
78
paths:
89
- '.github/workflows/c-api.yaml'
910
- 'cmake/**'
@@ -75,6 +76,79 @@ jobs:
7576
otool -L ./install/lib/libsherpa-onnx-c-api.dylib
7677
fi
7778
79+
- name: Test source separation (Spleeter)
80+
shell: bash
81+
run: |
82+
name=source-separation-spleeter-c-api
83+
gcc -o $name ./c-api-examples/$name.c \
84+
-I ./build/install/include \
85+
-L ./build/install/lib/ \
86+
-l sherpa-onnx-c-api \
87+
-l onnxruntime
88+
89+
ls -lh $name
90+
91+
if [[ ${{ matrix.os }} == ubuntu-latest || ${{ matrix.os }} == ubuntu-22.04-arm ]]; then
92+
ldd ./$name
93+
echo "----"
94+
readelf -d ./$name
95+
fi
96+
97+
curl -SL -O https://github.com/k2-fsa/sherpa-onnx/releases/download/source-separation-models/sherpa-onnx-spleeter-2stems-fp16.tar.bz2
98+
tar xjf sherpa-onnx-spleeter-2stems-fp16.tar.bz2
99+
rm sherpa-onnx-spleeter-2stems-fp16.tar.bz2
100+
101+
curl -SL -O https://github.com/k2-fsa/sherpa-onnx/releases/download/source-separation-models/qi-feng-le-zh.wav
102+
103+
export LD_LIBRARY_PATH=$PWD/build/install/lib:$LD_LIBRARY_PATH
104+
export DYLD_LIBRARY_PATH=$PWD/build/install/lib:$DYLD_LIBRARY_PATH
105+
106+
./$name
107+
ls -lh *.wav
108+
109+
mkdir -p source-separation-wavs
110+
mv -v vocals.wav accompaniment.wav source-separation-wavs/
111+
112+
rm -rf sherpa-onnx-spleeter-2stems-fp16 qi-feng-le-zh.wav
113+
rm $name
114+
115+
- name: Test source separation (UVR)
116+
shell: bash
117+
run: |
118+
name=source-separation-uvr-c-api
119+
gcc -o $name ./c-api-examples/$name.c \
120+
-I ./build/install/include \
121+
-L ./build/install/lib/ \
122+
-l sherpa-onnx-c-api \
123+
-l onnxruntime
124+
125+
ls -lh $name
126+
127+
if [[ ${{ matrix.os }} == ubuntu-latest || ${{ matrix.os }} == ubuntu-22.04-arm ]]; then
128+
ldd ./$name
129+
echo "----"
130+
readelf -d ./$name
131+
fi
132+
133+
curl -SL -O https://github.com/k2-fsa/sherpa-onnx/releases/download/source-separation-models/UVR-MDX-NET-Voc_FT.onnx
134+
curl -SL -O https://github.com/k2-fsa/sherpa-onnx/releases/download/source-separation-models/qi-feng-le-zh.wav
135+
136+
export LD_LIBRARY_PATH=$PWD/build/install/lib:$LD_LIBRARY_PATH
137+
export DYLD_LIBRARY_PATH=$PWD/build/install/lib:$DYLD_LIBRARY_PATH
138+
139+
./$name
140+
ls -lh *.wav
141+
142+
mv -v uvr-vocals.wav uvr-non-vocals.wav source-separation-wavs/
143+
144+
rm UVR-MDX-NET-Voc_FT.onnx qi-feng-le-zh.wav
145+
rm $name
146+
147+
- uses: actions/upload-artifact@v4
148+
with:
149+
name: source-separation-wavs-${{ matrix.os }}
150+
path: ./source-separation-wavs/*.wav
151+
78152
- name: Test Moonshine v2
79153
shell: bash
80154
run: |

c-api-examples/CMakeLists.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,12 @@ target_link_libraries(kws-c-api sherpa-onnx-c-api)
1010
add_executable(speech-enhancement-gtcrn-c-api speech-enhancement-gtcrn-c-api.c)
1111
target_link_libraries(speech-enhancement-gtcrn-c-api sherpa-onnx-c-api)
1212

13+
add_executable(source-separation-spleeter-c-api source-separation-spleeter-c-api.c)
14+
target_link_libraries(source-separation-spleeter-c-api sherpa-onnx-c-api)
15+
16+
add_executable(source-separation-uvr-c-api source-separation-uvr-c-api.c)
17+
target_link_libraries(source-separation-uvr-c-api sherpa-onnx-c-api)
18+
1319
add_executable(speech-enhancement-dpdfnet-c-api speech-enhancement-dpdfnet-c-api.c)
1420
target_link_libraries(speech-enhancement-dpdfnet-c-api sherpa-onnx-c-api)
1521

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
// c-api-examples/source-separation-spleeter-c-api.c
2+
//
3+
// Copyright (c) 2026 Xiaomi Corporation
4+
5+
// This file demonstrates how to use the source-separation C API
6+
// with the Spleeter 2-stems model.
7+
//
8+
// Usage:
9+
//
10+
// 1. Download the test model and audio
11+
//
12+
// wget https://github.com/k2-fsa/sherpa-onnx/releases/download/source-separation-models/sherpa-onnx-spleeter-2stems-fp16.tar.bz2
13+
// tar xjf sherpa-onnx-spleeter-2stems-fp16.tar.bz2
14+
//
15+
// wget https://github.com/k2-fsa/sherpa-onnx/releases/download/source-separation-models/qi-feng-le-zh.wav
16+
//
17+
// 2. Build
18+
//
19+
// cmake -DSHERPA_ONNX_ENABLE_C_API=ON ..
20+
// make source-separation-spleeter-c-api
21+
//
22+
// 3. Run
23+
//
24+
// ./bin/source-separation-spleeter-c-api
25+
26+
#include <stdio.h>
27+
#include <stdlib.h>
28+
#include <string.h>
29+
30+
#include "sherpa-onnx/c-api/c-api.h"
31+
32+
int32_t main() {
33+
SherpaOnnxOfflineSourceSeparationConfig config;
34+
memset(&config, 0, sizeof(config));
35+
config.model.spleeter.vocals =
36+
"./sherpa-onnx-spleeter-2stems-fp16/vocals.fp16.onnx";
37+
config.model.spleeter.accompaniment =
38+
"./sherpa-onnx-spleeter-2stems-fp16/accompaniment.fp16.onnx";
39+
config.model.num_threads = 1;
40+
41+
const SherpaOnnxOfflineSourceSeparation *ss =
42+
SherpaOnnxCreateOfflineSourceSeparation(&config);
43+
if (!ss) {
44+
fprintf(stderr, "Failed to create source separation engine\n");
45+
return -1;
46+
}
47+
48+
const SherpaOnnxMultiChannelWave *wave =
49+
SherpaOnnxReadWaveMultiChannel("./qi-feng-le-zh.wav");
50+
if (!wave) {
51+
fprintf(stderr, "Failed to read ./qi-feng-le-zh.wav\n");
52+
SherpaOnnxDestroyOfflineSourceSeparation(ss);
53+
return -1;
54+
}
55+
56+
fprintf(stdout, "Input wave: channels=%d, samples_per_channel=%d, sample_rate=%d\n",
57+
wave->num_channels, wave->num_samples, wave->sample_rate);
58+
59+
int32_t num_channels = wave->num_channels;
60+
61+
const SherpaOnnxSourceSeparationOutput *output =
62+
SherpaOnnxOfflineSourceSeparationProcess(
63+
ss, wave->samples, num_channels, wave->num_samples,
64+
wave->sample_rate);
65+
66+
if (!output) {
67+
fprintf(stderr, "Source separation failed\n");
68+
SherpaOnnxFreeMultiChannelWave(wave);
69+
SherpaOnnxDestroyOfflineSourceSeparation(ss);
70+
return -1;
71+
}
72+
73+
fprintf(stdout, "Output: %d stems, sample_rate=%d\n", output->num_stems,
74+
output->sample_rate);
75+
76+
// Write each stem to a separate multi-channel wave file.
77+
const char *stem_names[] = {"vocals", "accompaniment"};
78+
for (int32_t s = 0; s < output->num_stems && s < 2; ++s) {
79+
int32_t nc = output->stems[s].num_channels;
80+
int32_t ns = output->stems[s].n;
81+
char filename[256];
82+
snprintf(filename, sizeof(filename), "%s.wav", stem_names[s]);
83+
SherpaOnnxWriteWaveMultiChannel((const float *const *)output->stems[s].samples, ns,
84+
output->sample_rate, nc, filename);
85+
fprintf(stdout, "Saved %s (%d channels, %d samples, %d Hz)\n", filename,
86+
nc, ns, output->sample_rate);
87+
}
88+
89+
// Cleanup
90+
SherpaOnnxDestroySourceSeparationOutput(output);
91+
SherpaOnnxFreeMultiChannelWave(wave);
92+
SherpaOnnxDestroyOfflineSourceSeparation(ss);
93+
94+
return 0;
95+
}
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
// c-api-examples/source-separation-uvr-c-api.c
2+
//
3+
// Copyright (c) 2026 Xiaomi Corporation
4+
5+
// This file demonstrates how to use the source-separation C API
6+
// with the UVR (MDX-Net) model.
7+
//
8+
// Usage:
9+
//
10+
// 1. Download the test model and audio
11+
//
12+
// wget https://github.com/k2-fsa/sherpa-onnx/releases/download/source-separation-models/UVR-MDX-NET-Voc_FT.onnx
13+
//
14+
// wget https://github.com/k2-fsa/sherpa-onnx/releases/download/source-separation-models/qi-feng-le-zh.wav
15+
//
16+
// 2. Build
17+
//
18+
// cmake -DSHERPA_ONNX_ENABLE_C_API=ON ..
19+
// make source-separation-uvr-c-api
20+
//
21+
// 3. Run
22+
//
23+
// ./bin/source-separation-uvr-c-api
24+
25+
#include <stdio.h>
26+
#include <stdlib.h>
27+
#include <string.h>
28+
29+
#include "sherpa-onnx/c-api/c-api.h"
30+
31+
int32_t main() {
32+
SherpaOnnxOfflineSourceSeparationConfig config;
33+
memset(&config, 0, sizeof(config));
34+
config.model.uvr.model = "./UVR-MDX-NET-Voc_FT.onnx";
35+
config.model.num_threads = 1;
36+
37+
const SherpaOnnxOfflineSourceSeparation *ss =
38+
SherpaOnnxCreateOfflineSourceSeparation(&config);
39+
if (!ss) {
40+
fprintf(stderr, "Failed to create source separation engine\n");
41+
return -1;
42+
}
43+
44+
const SherpaOnnxMultiChannelWave *wave =
45+
SherpaOnnxReadWaveMultiChannel("./qi-feng-le-zh.wav");
46+
if (!wave) {
47+
fprintf(stderr, "Failed to read ./qi-feng-le-zh.wav\n");
48+
SherpaOnnxDestroyOfflineSourceSeparation(ss);
49+
return -1;
50+
}
51+
52+
fprintf(stdout,
53+
"Input wave: channels=%d, samples_per_channel=%d, sample_rate=%d\n",
54+
wave->num_channels, wave->num_samples, wave->sample_rate);
55+
56+
int32_t num_channels = wave->num_channels;
57+
58+
const SherpaOnnxSourceSeparationOutput *output =
59+
SherpaOnnxOfflineSourceSeparationProcess(
60+
ss, wave->samples, num_channels, wave->num_samples,
61+
wave->sample_rate);
62+
63+
if (!output) {
64+
fprintf(stderr, "Source separation failed\n");
65+
SherpaOnnxFreeMultiChannelWave(wave);
66+
SherpaOnnxDestroyOfflineSourceSeparation(ss);
67+
return -1;
68+
}
69+
70+
fprintf(stdout, "Output: %d stems, sample_rate=%d\n", output->num_stems,
71+
output->sample_rate);
72+
73+
// Write each stem to a separate multi-channel wave file.
74+
const char *stem_names[] = {"uvr-vocals", "uvr-non-vocals"};
75+
for (int32_t s = 0; s < output->num_stems && s < 2; ++s) {
76+
int32_t nc = output->stems[s].num_channels;
77+
int32_t ns = output->stems[s].n;
78+
char filename[256];
79+
snprintf(filename, sizeof(filename), "%s.wav", stem_names[s]);
80+
SherpaOnnxWriteWaveMultiChannel((const float *const *)output->stems[s].samples, ns,
81+
output->sample_rate, nc, filename);
82+
fprintf(stdout, "Saved %s (%d channels, %d samples, %d Hz)\n", filename,
83+
nc, ns, output->sample_rate);
84+
}
85+
86+
// Cleanup
87+
SherpaOnnxDestroySourceSeparationOutput(output);
88+
SherpaOnnxFreeMultiChannelWave(wave);
89+
SherpaOnnxDestroyOfflineSourceSeparation(ss);
90+
91+
return 0;
92+
}

0 commit comments

Comments
 (0)