@@ -60,9 +60,10 @@ static soxr_datatype_t to_soxr_split_dtype(const type_info& ntype) {
6060
6161class CSoxr {
6262 soxr_t _soxr = nullptr ;
63- const double _oi_rate ;
63+ double _oi_ratio ;
6464 std::unique_ptr<uint8_t []> _y_buf;
65- size_t _y_buf_size = 0 ;
65+ size_t _y_buf_bytes = 0 ;
66+ size_t _olen = 0 ;
6667
6768public:
6869 const double _in_rate;
@@ -73,16 +74,16 @@ class CSoxr {
7374 bool _ended = false ;
7475
7576 CSoxr (double in_rate, double out_rate, unsigned num_channels,
76- soxr_datatype_t ntype, unsigned long quality) :
77+ soxr_datatype_t ntype, unsigned long quality, bool vr ) :
7778 _in_rate (in_rate),
7879 _out_rate (out_rate),
79- _oi_rate (out_rate / in_rate),
80+ _oi_ratio (out_rate / in_rate),
8081 _ntype (ntype),
8182 _channels (num_channels),
8283 _div_len (std::max(1000 ., 48000 * _in_rate / _out_rate)) {
8384 soxr_error_t err = NULL ;
8485 soxr_io_spec_t io_spec = soxr_io_spec (ntype, ntype);
85- soxr_quality_spec_t quality_spec = soxr_quality_spec (quality, 0 );
86+ soxr_quality_spec_t quality_spec = soxr_quality_spec (quality, vr ? SOXR_VR : 0 );
8687
8788 _soxr = soxr_create (
8889 in_rate, out_rate, num_channels,
@@ -97,6 +98,51 @@ class CSoxr {
9798 soxr_delete (_soxr);
9899 }
99100
101+ template <typename T>
102+ T* _resize_ybuf (size_t req_size, bool copy) {
103+ if (_y_buf && req_size < _y_buf_bytes)
104+ return reinterpret_cast <T*>(_y_buf.get ());
105+
106+ // Grow to next power of 2
107+ size_t new_size = 1024 ;
108+ while (new_size < req_size) new_size <<= 1 ;
109+ // size_t new_size = req_size;
110+
111+ auto new_buf = std::make_unique<uint8_t []>(new_size);
112+ if (copy && _y_buf) {
113+ std::copy_n (_y_buf.get (), _y_buf_bytes, new_buf.get ());
114+ }
115+ _y_buf = std::move (new_buf);
116+ _y_buf_bytes = new_size;
117+ _olen = _y_buf_bytes / (sizeof (T) * _channels);
118+
119+ // printf("Realloc output buffer: %zu bytes\n", new_size);
120+ // fflush(stdout);
121+ return reinterpret_cast <T*>(_y_buf.get ());
122+ }
123+
124+ template <typename T>
125+ T* _flush (soxr_in_t input, size_t & out_pos) {
126+ // flush until no more output
127+ T* y = reinterpret_cast <T*>(_y_buf.get ());
128+ size_t odone = 0 ;
129+ // int cnt = 0;
130+ do {
131+ if (_olen <= out_pos) {
132+ // printf("_flush Realloc cnt = %d\n", ++cnt);
133+ y = _resize_ybuf<T>(_y_buf_bytes * 2 , true );
134+ }
135+ soxr_error_t err = soxr_process (
136+ _soxr,
137+ input, 0 , NULL ,
138+ &y[out_pos*_channels], _olen-out_pos, &odone);
139+ out_pos += odone;
140+
141+ if (err != NULL ) throw std::runtime_error (err);
142+ } while (0 < odone);
143+ return y;
144+ }
145+
100146 template <typename T>
101147 auto process (
102148 ndarray<const T, nb::ndim<2 >, nb::c_contig, nb::device::cpu> x,
@@ -123,40 +169,35 @@ class CSoxr {
123169
124170 const size_t ilen = x.shape (0 );
125171
126- // This is slower than returning fixed `ilen * _oi_rate ` buffers w/o copying.
172+ // This is slower than returning fixed `ilen * _oi_ratio ` buffers w/o copying.
127173 // But it ensures the lowest output delay provided by libsoxr.
128- const size_t olen = soxr_delay (_soxr) + ilen * _oi_rate + 1 ;
129-
130- // Reuse output buffer if possible, else reallocate
131- size_t req_size = sizeof (T) * olen * channels;
132- if (!_y_buf || _y_buf_size < req_size) {
133- // Grow to next power of 2
134- size_t new_size = 1 ;
135- while (new_size < req_size) new_size <<= 1 ;
136- _y_buf = std::make_unique<uint8_t []>(new_size);
137- _y_buf_size = new_size;
138- }
139- y = reinterpret_cast <T*>(_y_buf.get ());
174+ const size_t req_len = soxr_delay (_soxr) + ilen * _oi_ratio + 1 ;
175+ y = _resize_ybuf<T>(sizeof (T) * req_len * channels, false );
140176
141177 // divide long input and process
142178 size_t odone = 0 ;
143179 for (size_t idx = 0 ; idx < ilen; idx += _div_len) {
144180 err = soxr_process (
145181 _soxr,
146182 &x.data ()[idx*channels], std::min (_div_len, ilen-idx), NULL ,
147- &y[out_pos*channels], olen -out_pos, &odone);
183+ &y[out_pos*channels], _olen -out_pos, &odone);
148184 out_pos += odone;
185+
186+ if (_olen <= out_pos) {
187+ // for VR mode, output buffer may be full
188+ y = _flush<T>(&x.data ()[idx*channels], out_pos);
189+ }
149190 }
150191
151192 // flush if last input
152193 if (last) {
153194 _ended = true ;
154- err = soxr_process (
155- _soxr,
156- NULL , 0 , NULL ,
157- &y[out_pos*channels], olen-out_pos, &odone);
158- out_pos += odone;
195+ y = _flush<T>(NULL , out_pos);
159196 }
197+
198+ // if (_olen <= out_pos) {
199+ // printf("Warning: output buffer overflow %zu <= %zu\n", _olen, out_pos);
200+ // }
160201 }
161202
162203 if (err) {
@@ -176,6 +217,12 @@ class CSoxr {
176217 if (err != NULL ) throw std::runtime_error (err);
177218 _ended = false ;
178219 }
220+
221+ void set_io_ratio (double io_ratio, size_t slew_len=0 ) {
222+ soxr_error_t err = soxr_set_io_ratio (_soxr, io_ratio, slew_len);
223+ if (err != NULL ) throw std::runtime_error (err);
224+ _oi_ratio = std::max (_oi_ratio, 1 / io_ratio);
225+ }
179226};
180227
181228
@@ -385,15 +432,16 @@ NB_MODULE(soxr_ext, m) {
385432 .def_ro (" ntype" , &CSoxr::_ntype)
386433 .def_ro (" channels" , &CSoxr::_channels)
387434 .def_ro (" ended" , &CSoxr::_ended)
388- .def (nb::init<double , double , unsigned , soxr_datatype_t , unsigned long >())
435+ .def (nb::init<double , double , unsigned , soxr_datatype_t , unsigned long , bool >())
389436 .def (" process_float32" , &CSoxr::process<float >)
390437 .def (" process_float64" , &CSoxr::process<double >)
391438 .def (" process_int32" , &CSoxr::process<int32_t >)
392439 .def (" process_int16" , &CSoxr::process<int16_t >)
393440 .def (" num_clips" , &CSoxr::num_clips)
394441 .def (" delay" , &CSoxr::delay)
395442 .def (" engine" , &CSoxr::engine)
396- .def (" clear" , &CSoxr::clear);
443+ .def (" clear" , &CSoxr::clear)
444+ .def (" set_io_ratio" , &CSoxr::set_io_ratio);
397445
398446 m.def (" csoxr_divide_proc_float32" , csoxr_divide_proc<float >);
399447 m.def (" csoxr_divide_proc_float64" , csoxr_divide_proc<double >);
0 commit comments