Skip to content

Commit 716ba83

Browse files
authored
Add C++ runtime for spleeter about source separation (k2-fsa#2242)
1 parent 55a4479 commit 716ba83

28 files changed

Lines changed: 1267 additions & 72 deletions

.github/workflows/export-spleeter-to-onnx.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ name: export-spleeter-to-onnx
33
on:
44
push:
55
branches:
6-
- spleeter-2
6+
- spleeter-cpp-2
77
workflow_dispatch:
88

99
concurrency:

cmake/cmake_extension.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ def get_binaries():
5656
"sherpa-onnx-offline-denoiser",
5757
"sherpa-onnx-offline-language-identification",
5858
"sherpa-onnx-offline-punctuation",
59+
"sherpa-onnx-offline-source-separation",
5960
"sherpa-onnx-offline-speaker-diarization",
6061
"sherpa-onnx-offline-tts",
6162
"sherpa-onnx-offline-tts-play",

scripts/spleeter/convert_to_torch.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -217,8 +217,8 @@ def main(name):
217217
# for the batchnormalization in torch,
218218
# default input shape is NCHW
219219

220-
# NHWC to NCHW
221-
torch_y1_out = unet(torch.from_numpy(y0_out).permute(0, 3, 1, 2))
220+
torch_y1_out = unet(torch.from_numpy(y0_out).permute(3, 0, 1, 2))
221+
torch_y1_out = torch_y1_out.permute(1, 0, 2, 3)
222222

223223
# print(torch_y1_out.shape, torch.from_numpy(y1_out).permute(0, 3, 1, 2).shape)
224224
assert torch.allclose(

scripts/spleeter/export_onnx.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ def add_meta_data(filename, prefix):
4646

4747
def export(model, prefix):
4848
num_splits = 1
49-
x = torch.rand(num_splits, 2, 512, 1024, dtype=torch.float32)
49+
x = torch.rand(2, num_splits, 512, 1024, dtype=torch.float32)
5050

5151
filename = f"./2stems/{prefix}.onnx"
5252
torch.onnx.export(
@@ -56,7 +56,7 @@ def export(model, prefix):
5656
input_names=["x"],
5757
output_names=["y"],
5858
dynamic_axes={
59-
"x": {0: "num_splits"},
59+
"x": {1: "num_splits"},
6060
},
6161
opset_version=13,
6262
)

scripts/spleeter/separate.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,13 +101,17 @@ def main():
101101
print("y2", y.shape, y.dtype)
102102

103103
y = y.abs()
104-
y = y.permute(0, 3, 1, 2)
105-
# (1, 2, 512, 1024)
104+
105+
y = y.permute(3, 0, 1, 2)
106+
# (2, 1, 512, 1024)
106107
print("y3", y.shape, y.dtype)
107108

108109
vocals_spec = vocals(y)
109110
accompaniment_spec = accompaniment(y)
110111

112+
vocals_spec = vocals_spec.permute(1, 0, 2, 3)
113+
accompaniment_spec = accompaniment_spec.permute(1, 0, 2, 3)
114+
111115
sum_spec = (vocals_spec**2 + accompaniment_spec**2) + 1e-10
112116
print(
113117
"vocals_spec",

scripts/spleeter/separate_onnx.py

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,14 @@
1212

1313
"""
1414
----------inputs for ./2stems/vocals.onnx----------
15-
NodeArg(name='x', type='tensor(float)', shape=['num_splits', 2, 512, 1024])
15+
NodeArg(name='x', type='tensor(float)', shape=[2, 'num_splits', 512, 1024])
1616
----------outputs for ./2stems/vocals.onnx----------
17-
NodeArg(name='y', type='tensor(float)', shape=['Muly_dim_0', 2, 512, 1024])
17+
NodeArg(name='y', type='tensor(float)', shape=[2, 'Transposey_dim_1', 512, 1024])
1818
1919
----------inputs for ./2stems/accompaniment.onnx----------
20-
NodeArg(name='x', type='tensor(float)', shape=['num_splits', 2, 512, 1024])
20+
NodeArg(name='x', type='tensor(float)', shape=[2, 'num_splits', 512, 1024])
2121
----------outputs for ./2stems/accompaniment.onnx----------
22-
NodeArg(name='y', type='tensor(float)', shape=['Muly_dim_0', 2, 512, 1024])
23-
22+
NodeArg(name='y', type='tensor(float)', shape=[2, 'Transposey_dim_1', 512, 1024])
2423
"""
2524

2625

@@ -123,16 +122,16 @@ def main():
123122
if padding > 0:
124123
stft0 = torch.nn.functional.pad(stft0, (0, 0, 0, padding))
125124
stft1 = torch.nn.functional.pad(stft1, (0, 0, 0, padding))
126-
stft0 = stft0.reshape(-1, 1, 512, 1024)
127-
stft1 = stft1.reshape(-1, 1, 512, 1024)
125+
stft0 = stft0.reshape(1, -1, 512, 1024)
126+
stft1 = stft1.reshape(1, -1, 512, 1024)
128127

129-
stft_01 = torch.cat([stft0, stft1], axis=1)
128+
stft_01 = torch.cat([stft0, stft1], axis=0)
130129

131130
print("stft_01", stft_01.shape, stft_01.dtype)
132131

133132
vocals_spec = vocals(stft_01)
134133
accompaniment_spec = accompaniment(stft_01)
135-
# (num_splits, num_channels, 512, 1024)
134+
# (num_channels, num_splits, 512, 1024)
136135

137136
sum_spec = (vocals_spec.square() + accompaniment_spec.square()) + 1e-10
138137

@@ -142,8 +141,8 @@ def main():
142141
for name, spec in zip(
143142
["vocals", "accompaniment"], [vocals_spec, accompaniment_spec]
144143
):
145-
spec_c0 = spec[:, 0, :, :]
146-
spec_c1 = spec[:, 1, :, :]
144+
spec_c0 = spec[0]
145+
spec_c1 = spec[1]
147146

148147
spec_c0 = spec_c0.reshape(-1, 1024)
149148
spec_c1 = spec_c1.reshape(-1, 1024)

scripts/spleeter/unet.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,14 @@ def __init__(self):
6767
self.up7 = torch.nn.Conv2d(1, 2, kernel_size=4, dilation=2, padding=3)
6868

6969
def forward(self, x):
70+
"""
71+
Args:
72+
x: (num_audio_channels, num_splits, 512, 1024)
73+
Returns:
74+
y: (num_audio_channels, num_splits, 512, 1024)
75+
"""
76+
x = x.permute(1, 0, 2, 3)
77+
7078
in_x = x
7179
# in_x is (3, 2, 512, 1024) = (T, 2, 512, 1024)
7280
x = torch.nn.functional.pad(x, (1, 2, 1, 2), "constant", 0)
@@ -147,4 +155,5 @@ def forward(self, x):
147155
up7 = self.up7(batch12)
148156
up7 = torch.sigmoid(up7) # (3, 2, 512, 1024)
149157

150-
return up7 * in_x
158+
ans = up7 * in_x
159+
return ans.permute(1, 0, 2, 3)

sherpa-onnx/csrc/CMakeLists.txt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,13 @@ set(sources
5050
offline-rnn-lm.cc
5151
offline-sense-voice-model-config.cc
5252
offline-sense-voice-model.cc
53+
54+
offline-source-separation-impl.cc
55+
offline-source-separation-model-config.cc
56+
offline-source-separation-spleeter-model-config.cc
57+
offline-source-separation-spleeter-model.cc
58+
offline-source-separation.cc
59+
5360
offline-stream.cc
5461
offline-tdnn-ctc-model.cc
5562
offline-tdnn-model-config.cc
@@ -326,6 +333,7 @@ if(SHERPA_ONNX_ENABLE_BINARY)
326333
add_executable(sherpa-onnx-offline-language-identification sherpa-onnx-offline-language-identification.cc)
327334
add_executable(sherpa-onnx-offline-parallel sherpa-onnx-offline-parallel.cc)
328335
add_executable(sherpa-onnx-offline-punctuation sherpa-onnx-offline-punctuation.cc)
336+
add_executable(sherpa-onnx-offline-source-separation sherpa-onnx-offline-source-separation.cc)
329337
add_executable(sherpa-onnx-online-punctuation sherpa-onnx-online-punctuation.cc)
330338
add_executable(sherpa-onnx-vad sherpa-onnx-vad.cc)
331339

@@ -346,6 +354,7 @@ if(SHERPA_ONNX_ENABLE_BINARY)
346354
sherpa-onnx-offline-language-identification
347355
sherpa-onnx-offline-parallel
348356
sherpa-onnx-offline-punctuation
357+
sherpa-onnx-offline-source-separation
349358
sherpa-onnx-online-punctuation
350359
sherpa-onnx-vad
351360
)
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// sherpa-onnx/csrc/offline-source-separation-impl.cc
2+
//
3+
// Copyright (c) 2025 Xiaomi Corporation
4+
5+
#include "sherpa-onnx/csrc/offline-source-separation-impl.h"
6+
7+
#include <memory>
8+
9+
#include "sherpa-onnx/csrc/offline-source-separation-spleeter-impl.h"
10+
11+
namespace sherpa_onnx {
12+
13+
std::unique_ptr<OfflineSourceSeparationImpl>
14+
OfflineSourceSeparationImpl::Create(
15+
const OfflineSourceSeparationConfig &config) {
16+
// TODO(fangjun): Support other models
17+
return std::make_unique<OfflineSourceSeparationSpleeterImpl>(config);
18+
}
19+
20+
template <typename Manager>
21+
std::unique_ptr<OfflineSourceSeparationImpl>
22+
OfflineSourceSeparationImpl::Create(
23+
Manager *mgr, const OfflineSourceSeparationConfig &config) {
24+
// TODO(fangjun): Support other models
25+
return std::make_unique<OfflineSourceSeparationSpleeterImpl>(mgr, config);
26+
}
27+
28+
#if __ANDROID_API__ >= 9
29+
template std::unique_ptr<OfflineSourceSeparationImpl>
30+
OfflineSourceSeparationImpl::Create(
31+
AAssetManager *mgr, const OfflineSourceSeparationConfig &config);
32+
#endif
33+
34+
#if __OHOS__
35+
template std::unique_ptr<OfflineSourceSeparationImpl>
36+
OfflineSourceSeparationImpl::Create(
37+
NativeResourceManager *mgr, const OfflineSourceSeparationConfig &config);
38+
#endif
39+
40+
} // namespace sherpa_onnx
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// sherpa-onnx/csrc/offline-source-separation-impl.h
2+
//
3+
// Copyright (c) 2025 Xiaomi Corporation
4+
5+
#ifndef SHERPA_ONNX_CSRC_OFFLINE_SOURCE_SEPARATION_IMPL_H_
6+
#define SHERPA_ONNX_CSRC_OFFLINE_SOURCE_SEPARATION_IMPL_H_
7+
8+
#include <vector>
9+
10+
#include "sherpa-onnx/csrc/offline-source-separation.h"
11+
12+
namespace sherpa_onnx {
13+
14+
class OfflineSourceSeparationImpl {
15+
public:
16+
static std::unique_ptr<OfflineSourceSeparationImpl> Create(
17+
const OfflineSourceSeparationConfig &config);
18+
19+
template <typename Manager>
20+
static std::unique_ptr<OfflineSourceSeparationImpl> Create(
21+
Manager *mgr, const OfflineSourceSeparationConfig &config);
22+
23+
virtual ~OfflineSourceSeparationImpl() = default;
24+
25+
virtual OfflineSourceSeparationOutput Process(
26+
const OfflineSourceSeparationInput &input) const = 0;
27+
28+
virtual int32_t GetOutputSampleRate() const = 0;
29+
30+
virtual int32_t GetNumberOfStems() const = 0;
31+
};
32+
33+
} // namespace sherpa_onnx
34+
35+
#endif // SHERPA_ONNX_CSRC_OFFLINE_SOURCE_SEPARATION_IMPL_H_

0 commit comments

Comments
 (0)