"launcher/vscode:/vscode.git/clone" did not exist on "08b3eac2ce54e25bec12088fd7e69ee3c07adaf5"
effects_chain.cpp 8.45 KB
Newer Older
1
2
#include <libtorchaudio/sox/effects_chain.h>
#include <libtorchaudio/sox/utils.h>
3
#include "c10/util/Exception.h"
moto's avatar
moto committed
4
5
6

using namespace torch::indexing;

Moto Hira's avatar
Moto Hira committed
7
namespace torchaudio::sox {
moto's avatar
moto committed
8
9
10
11
12
13

namespace {

/// helper classes for passing the location of input tensor and output buffer
///
/// drain/flow callback functions require plaing C style function signature and
14
15
/// the way to pass extra data is to attach data to sox_effect_t::priv pointer.
/// The following structs will be assigned to sox_effect_t::priv pointer which
moto's avatar
moto committed
16
17
18
/// gives sox_effect_t an access to input Tensor and output buffer object.
struct TensorInputPriv {
  size_t index;
19
20
21
  torch::Tensor* waveform;
  int64_t sample_rate;
  bool channels_first;
moto's avatar
moto committed
22
23
24
25
};
struct TensorOutputPriv {
  std::vector<sox_sample_t>* buffer;
};
26
27
28
struct FileOutputPriv {
  sox_format_t* sf;
};
moto's avatar
moto committed
29
30
31
32
33
34

/// Callback function to feed Tensor data to SoxEffectChain.
int tensor_input_drain(sox_effect_t* effp, sox_sample_t* obuf, size_t* osamp) {
  // Retrieve the input Tensor and current index
  auto priv = static_cast<TensorInputPriv*>(effp->priv);
  auto index = priv->index;
35
  auto tensor = *(priv->waveform);
moto's avatar
moto committed
36
37
38
39
40
41
42
43
44
45
  auto num_channels = effp->out_signal.channels;

  // Adjust the number of samples to read
  const size_t num_samples = tensor.numel();
  if (index + *osamp > num_samples) {
    *osamp = num_samples - index;
  }
  // Ensure that it's a multiple of the number of channels
  *osamp -= *osamp % num_channels;

46
  // Slice the input Tensor
47
  auto chunk = [&]() {
moto's avatar
moto committed
48
49
    auto i_frame = index / num_channels;
    auto num_frames = *osamp / num_channels;
50
    auto t = (priv->channels_first)
moto's avatar
moto committed
51
52
        ? tensor.index({Slice(), Slice(i_frame, i_frame + num_frames)}).t()
        : tensor.index({Slice(i_frame, i_frame + num_frames), Slice()});
53
    return t.reshape({-1});
moto's avatar
moto committed
54
55
  }();

56
57
  // Convert to sox_sample_t (int32_t)
  switch (chunk.dtype().toScalarType()) {
58
    case c10::ScalarType::Float: {
59
60
61
62
63
64
      // Need to convert to 64-bit precision so that
      // values around INT32_MIN/MAX are handled correctly.
      chunk = chunk.to(c10::ScalarType::Double);
      chunk *= 2147483648.;
      chunk.clamp_(INT32_MIN, INT32_MAX);
      chunk = chunk.to(c10::ScalarType::Int);
65
      break;
66
    }
67
68
    case c10::ScalarType::Int: {
      break;
69
    }
70
    case c10::ScalarType::Short: {
71
72
      chunk = chunk.to(c10::ScalarType::Int);
      chunk *= 65536;
73
      break;
74
    }
75
    case c10::ScalarType::Byte: {
76
77
78
      chunk = chunk.to(c10::ScalarType::Int);
      chunk -= 128;
      chunk *= 16777216;
79
      break;
80
    }
81
    default:
82
      TORCH_CHECK(false, "Unexpected dtype: ", chunk.dtype());
83
  }
84
85
86
  // Write to buffer
  chunk = chunk.contiguous();
  memcpy(obuf, chunk.data_ptr<int32_t>(), *osamp * 4);
87
  priv->index += *osamp;
moto's avatar
moto committed
88
89
90
91
92
  return (priv->index == num_samples) ? SOX_EOF : SOX_SUCCESS;
}

/// Callback function to fetch data from SoxEffectChain.
int tensor_output_flow(
93
    sox_effect_t* effp,
moto's avatar
moto committed
94
95
96
97
98
99
100
101
102
103
104
105
    sox_sample_t const* ibuf,
    sox_sample_t* obuf LSX_UNUSED,
    size_t* isamp,
    size_t* osamp) {
  *osamp = 0;
  // Get output buffer
  auto out_buffer = static_cast<TensorOutputPriv*>(effp->priv)->buffer;
  // Append at the end
  out_buffer->insert(out_buffer->end(), ibuf, ibuf + *isamp);
  return SOX_SUCCESS;
}

106
107
108
109
110
111
112
113
114
115
int file_output_flow(
    sox_effect_t* effp,
    sox_sample_t const* ibuf,
    sox_sample_t* obuf LSX_UNUSED,
    size_t* isamp,
    size_t* osamp) {
  *osamp = 0;
  if (*isamp) {
    auto sf = static_cast<FileOutputPriv*>(effp->priv)->sf;
    if (sox_write(sf, ibuf, *isamp) != *isamp) {
116
117
118
119
120
121
122
      TORCH_CHECK(
          !sf->sox_errno,
          sf->sox_errstr,
          " ",
          sox_strerror(sf->sox_errno),
          " ",
          sf->filename);
123
124
125
126
127
128
      return SOX_EOF;
    }
  }
  return SOX_SUCCESS;
}

moto's avatar
moto committed
129
sox_effect_handler_t* get_tensor_input_handler() {
moto's avatar
moto committed
130
131
132
133
134
135
136
137
138
139
140
  static sox_effect_handler_t handler{
      /*name=*/"input_tensor",
      /*usage=*/NULL,
      /*flags=*/SOX_EFF_MCHAN,
      /*getopts=*/NULL,
      /*start=*/NULL,
      /*flow=*/NULL,
      /*drain=*/tensor_input_drain,
      /*stop=*/NULL,
      /*kill=*/NULL,
      /*priv_size=*/sizeof(TensorInputPriv)};
moto's avatar
moto committed
141
142
143
144
  return &handler;
}

sox_effect_handler_t* get_tensor_output_handler() {
moto's avatar
moto committed
145
146
147
148
149
150
151
152
153
154
155
  static sox_effect_handler_t handler{
      /*name=*/"output_tensor",
      /*usage=*/NULL,
      /*flags=*/SOX_EFF_MCHAN,
      /*getopts=*/NULL,
      /*start=*/NULL,
      /*flow=*/tensor_output_flow,
      /*drain=*/NULL,
      /*stop=*/NULL,
      /*kill=*/NULL,
      /*priv_size=*/sizeof(TensorOutputPriv)};
moto's avatar
moto committed
156
157
158
  return &handler;
}

159
sox_effect_handler_t* get_file_output_handler() {
moto's avatar
moto committed
160
161
162
163
164
165
166
167
168
169
170
  static sox_effect_handler_t handler{
      /*name=*/"output_file",
      /*usage=*/NULL,
      /*flags=*/SOX_EFF_MCHAN,
      /*getopts=*/NULL,
      /*start=*/NULL,
      /*flow=*/file_output_flow,
      /*drain=*/NULL,
      /*stop=*/NULL,
      /*kill=*/NULL,
      /*priv_size=*/sizeof(FileOutputPriv)};
171
172
173
  return &handler;
}

moto's avatar
moto committed
174
175
} // namespace

176
177
178
179
180
181
182
183
184
185
186
187
SoxEffect::SoxEffect(sox_effect_t* se) noexcept : se_(se) {}

SoxEffect::~SoxEffect() {
  if (se_ != nullptr) {
    free(se_);
  }
}

SoxEffect::operator sox_effect_t*() const {
  return se_;
}

hwangjeff's avatar
hwangjeff committed
188
auto SoxEffect::operator->() noexcept -> sox_effect_t* {
189
190
191
  return se_;
}

moto's avatar
moto committed
192
193
194
195
196
197
198
SoxEffectsChain::SoxEffectsChain(
    sox_encodinginfo_t input_encoding,
    sox_encodinginfo_t output_encoding)
    : in_enc_(input_encoding),
      out_enc_(output_encoding),
      in_sig_(),
      interm_sig_(),
199
      out_sig_(),
moto's avatar
moto committed
200
      sec_(sox_create_effects_chain(&in_enc_, &out_enc_)) {
201
  TORCH_CHECK(sec_, "Failed to create effect chain.");
moto's avatar
moto committed
202
203
204
205
206
207
208
209
210
211
212
213
}

SoxEffectsChain::~SoxEffectsChain() {
  if (sec_ != nullptr) {
    sox_delete_effects_chain(sec_);
  }
}

void SoxEffectsChain::run() {
  sox_flow_effects(sec_, NULL, NULL);
}

214
215
216
217
218
void SoxEffectsChain::addInputTensor(
    torch::Tensor* waveform,
    int64_t sample_rate,
    bool channels_first) {
  in_sig_ = get_signalinfo(waveform, sample_rate, "wav", channels_first);
moto's avatar
moto committed
219
220
221
222
  interm_sig_ = in_sig_;
  SoxEffect e(sox_create_effect(get_tensor_input_handler()));
  auto priv = static_cast<TensorInputPriv*>(e->priv);
  priv->index = 0;
223
224
225
  priv->waveform = waveform;
  priv->sample_rate = sample_rate;
  priv->channels_first = channels_first;
226
227
228
  TORCH_CHECK(
      sox_add_effect(sec_, e, &interm_sig_, &in_sig_) == SOX_SUCCESS,
      "Internal Error: Failed to add effect: input_tensor");
moto's avatar
moto committed
229
230
231
232
233
234
}

void SoxEffectsChain::addOutputBuffer(
    std::vector<sox_sample_t>* output_buffer) {
  SoxEffect e(sox_create_effect(get_tensor_output_handler()));
  static_cast<TensorOutputPriv*>(e->priv)->buffer = output_buffer;
235
236
237
  TORCH_CHECK(
      sox_add_effect(sec_, e, &interm_sig_, &in_sig_) == SOX_SUCCESS,
      "Internal Error: Failed to add effect: output_tensor");
moto's avatar
moto committed
238
239
240
241
242
243
244
245
}

void SoxEffectsChain::addInputFile(sox_format_t* sf) {
  in_sig_ = sf->signal;
  interm_sig_ = in_sig_;
  SoxEffect e(sox_create_effect(sox_find_effect("input")));
  char* opts[] = {(char*)sf};
  sox_effect_options(e, 1, opts);
246
247
248
249
  TORCH_CHECK(
      sox_add_effect(sec_, e, &interm_sig_, &in_sig_) == SOX_SUCCESS,
      "Internal Error: Failed to add effect: input ",
      sf->filename);
moto's avatar
moto committed
250
251
}

252
253
254
255
void SoxEffectsChain::addOutputFile(sox_format_t* sf) {
  out_sig_ = sf->signal;
  SoxEffect e(sox_create_effect(get_file_output_handler()));
  static_cast<FileOutputPriv*>(e->priv)->sf = sf;
256
257
258
259
  TORCH_CHECK(
      sox_add_effect(sec_, e, &interm_sig_, &out_sig_) == SOX_SUCCESS,
      "Internal Error: Failed to add effect: output ",
      sf->filename);
260
261
}

moto's avatar
moto committed
262
263
void SoxEffectsChain::addEffect(const std::vector<std::string> effect) {
  const auto num_args = effect.size();
264
  TORCH_CHECK(num_args != 0, "Invalid argument: empty effect.");
moto's avatar
moto committed
265
  const auto name = effect[0];
266
267
268
269
  TORCH_CHECK(
      UNSUPPORTED_EFFECTS.find(name) == UNSUPPORTED_EFFECTS.end(),
      "Unsupported effect: ",
      name)
moto's avatar
moto committed
270

271
  auto returned_effect = sox_find_effect(name.c_str());
272
273
  TORCH_CHECK(returned_effect, "Unsupported effect: ", name)

274
  SoxEffect e(sox_create_effect(returned_effect));
moto's avatar
moto committed
275
276
277
278
279
280
  const auto num_options = num_args - 1;

  std::vector<char*> opts;
  for (size_t i = 1; i < num_args; ++i) {
    opts.push_back((char*)effect[i].c_str());
  }
281
282
283
284
285
286
287
288
289
290
  TORCH_CHECK(
      sox_effect_options(e, num_options, num_options ? opts.data() : nullptr) ==
          SOX_SUCCESS,
      "Invalid effect option: ",
      c10::Join(" ", effect))
  TORCH_CHECK(
      sox_add_effect(sec_, e, &interm_sig_, &in_sig_) == SOX_SUCCESS,
      "Internal Error: Failed to add effect: \"",
      c10::Join(" ", effect),
      "\"");
moto's avatar
moto committed
291
292
293
294
295
296
297
298
299
300
}

int64_t SoxEffectsChain::getOutputNumChannels() {
  return interm_sig_.channels;
}

int64_t SoxEffectsChain::getOutputSampleRate() {
  return interm_sig_.rate;
}

Moto Hira's avatar
Moto Hira committed
301
} // namespace torchaudio::sox