diff --git a/Cargo.toml b/Cargo.toml index 04e838f..15f6665 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sphn" -version = "0.1.4" +version = "0.1.5" edition = "2021" license = "MIT/Apache-2.0" description = "pyo3 wrappers to read/write audio files" @@ -15,10 +15,10 @@ crate-type = ["cdylib"] [dependencies] anyhow = "1.0.79" byteorder = "1.5.0" -numpy = "0.21.0" +numpy = "0.23.0" ogg = "0.9.1" opus = "0.3.0" -pyo3 = "0.21.0" +pyo3 = "0.23.0" rayon = "1.8.1" rubato = "0.15.0" serde = { version = "1.0", features = ["derive"] } diff --git a/src/lib.rs b/src/lib.rs index 2e6aa01..f659ddb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,6 +3,7 @@ mod opus; mod wav; use pyo3::prelude::*; +use std::sync::Mutex; trait PyRes { #[allow(unused)] @@ -80,7 +81,12 @@ impl FileReader { fn decode(&mut self, start_sec: f64, duration_sec: f64, py: Python) -> PyResult { let (data, _unpadded_len) = self.inner.decode(start_sec, duration_sec, false).w_f(&self.path)?; - Ok(numpy::PyArray2::from_vec2_bound(py, &data)?.into_py(py)) + Ok(numpy::PyArray2::from_vec2(py, &data) + .unwrap() + .into_pyobject(py) + .unwrap() + .into_any() + .unbind()) } /// Decodes the audio data from `start_sec` to `start_sec + duration_sec` and return the PCM @@ -88,22 +94,22 @@ impl FileReader { /// is time. /// If the end of the file is reached, the array is padded with zeros so that its length is /// still matching `duration_sec`. - fn decode_with_padding( + fn decode_with_padding<'a>( &mut self, start_sec: f64, duration_sec: f64, - py: Python, - ) -> PyResult<(PyObject, usize)> { + py: Python<'a>, + ) -> PyResult<(Bound<'a, PyAny>, usize)> { let (data, unpadded_len) = self.inner.decode(start_sec, duration_sec, true).w_f(&self.path)?; - let data = numpy::PyArray2::from_vec2_bound(py, &data)?.into_py(py); + let data = numpy::PyArray2::from_vec2(py, &data)?.into_any(); Ok((data, unpadded_len)) } /// Decodes the audio data for the whole file and return it as a two dimensional numpy array. - fn decode_all(&mut self, py: Python) -> PyResult { + fn decode_all<'a>(&mut self, py: Python<'a>) -> PyResult> { let data = self.inner.decode_all().w_f(&self.path)?; - Ok(numpy::PyArray2::from_vec2_bound(py, &data)?.into_py(py)) + Ok(numpy::PyArray2::from_vec2(py, &data)?.into_any()) } } @@ -158,7 +164,7 @@ fn read( } }; let data = Python::with_gil(|py| { - Ok::<_, PyErr>(numpy::PyArray2::from_vec2_bound(py, &data)?.into_py(py)) + Ok::<_, PyErr>(numpy::PyArray2::from_vec2(py, &data)?.into_any().unbind()) }) .w_f(&filename)?; Ok((data, sample_rate)) @@ -285,7 +291,7 @@ fn resample( let pcm = to_cow(&pcm); let pcm = audio::resample(&pcm[..], src_sample_rate, dst_sample_rate).w()?; Python::with_gil(|py| { - Ok::<_, PyErr>(numpy::PyArray1::from_vec_bound(py, pcm).into_py(py)) + Ok::<_, PyErr>(numpy::PyArray1::from_vec(py, pcm).into_any().unbind()) }) } 2 => { @@ -298,7 +304,7 @@ fn resample( .collect::>>() .w()?; Python::with_gil(|py| { - Ok::<_, PyErr>(numpy::PyArray2::from_vec2_bound(py, &pcm)?.into_py(py)) + Ok::<_, PyErr>(numpy::PyArray2::from_vec2(py, &pcm)?.into_any().unbind()) }) } _ => py_bail!("expected one or two dimensions, got shape {:?}", pcm.shape()), @@ -315,7 +321,7 @@ fn read_opus(filename: std::path::PathBuf, py: Python) -> PyResult<(PyObject, u3 let file = std::fs::File::open(&filename)?; let file = std::io::BufReader::new(file); let (data, sample_rate) = opus::read_ogg(file).w_f(&filename)?; - let data = numpy::PyArray2::from_vec2_bound(py, &data)?.into_py(py); + let data = numpy::PyArray2::from_vec2(py, &data)?.into_any().unbind(); Ok((data, sample_rate)) } @@ -328,13 +334,13 @@ fn read_opus(filename: std::path::PathBuf, py: Python) -> PyResult<(PyObject, u3 fn read_opus_bytes(bytes: Vec, py: Python) -> PyResult<(PyObject, u32)> { let bytes = std::io::Cursor::new(bytes); let (data, sample_rate) = opus::read_ogg(bytes).w()?; - let data = numpy::PyArray2::from_vec2_bound(py, &data)?.into_py(py); + let data = numpy::PyArray2::from_vec2(py, &data)?.into_any().unbind(); Ok((data, sample_rate)) } #[pyclass] struct OpusStreamWriter { - inner: opus::StreamWriter, + inner: Mutex, sample_rate: u32, } @@ -343,7 +349,7 @@ impl OpusStreamWriter { #[new] fn new(sample_rate: u32) -> PyResult { let inner = opus::StreamWriter::new(sample_rate).w()?; - Ok(Self { inner, sample_rate }) + Ok(Self { inner: Mutex::new(inner), sample_rate }) } fn __str__(&self) -> String { @@ -355,22 +361,23 @@ impl OpusStreamWriter { fn append_pcm(&mut self, pcm: numpy::PyReadonlyArray1) -> PyResult<()> { let pcm = pcm.as_array(); let pcm = to_cow(&pcm); - self.inner.append_pcm(&pcm).w()?; + self.inner.lock().unwrap().append_pcm(&pcm).w()?; Ok(()) } /// Gets the pending opus bytes from the stream. An empty bytes object is returned if no data /// is currently available. fn read_bytes(&mut self) -> PyResult { - let bytes = self.inner.read_bytes().w()?; - let bytes = Python::with_gil(|py| pyo3::types::PyBytes::new_bound(py, &bytes).into_py(py)); + let bytes = self.inner.lock().unwrap().read_bytes().w()?; + let bytes = + Python::with_gil(|py| pyo3::types::PyBytes::new(py, &bytes).into_any().unbind()); Ok(bytes) } } #[pyclass] struct OpusStreamReader { - inner: opus::StreamReader, + inner: Mutex, sample_rate: u32, } @@ -379,7 +386,7 @@ impl OpusStreamReader { #[new] fn new(sample_rate: u32) -> PyResult { let inner = opus::StreamReader::new(sample_rate).w()?; - Ok(Self { inner, sample_rate }) + Ok(Self { inner: Mutex::new(inner), sample_rate }) } fn __str__(&self) -> String { @@ -388,18 +395,18 @@ impl OpusStreamReader { /// Writes some ogg/opus bytes to the current stream. fn append_bytes(&mut self, data: &[u8]) -> PyResult<()> { - self.inner.append(data.to_vec()).w() + self.inner.lock().unwrap().append(data.to_vec()).w() } // TODO(laurent): maybe we should also have a pyo3_async api here. /// Gets the pcm data decoded by the stream, this returns a 1d numpy array or None if the /// stream has been closed. The array is empty if no data is currently available. fn read_pcm(&mut self) -> PyResult { - let pcm_data = self.inner.read_pcm().w()?; + let pcm_data = self.inner.lock().unwrap().read_pcm().w()?; Python::with_gil(|py| match pcm_data { None => Ok(py.None()), Some(data) => { - let data = numpy::PyArray1::from_vec_bound(py, data.to_vec()).into_py(py); + let data = numpy::PyArray1::from_vec(py, data.to_vec()).into_any().unbind(); Ok(data) } }) @@ -408,7 +415,7 @@ impl OpusStreamReader { /// Closes the stream, this results in the worker thread exiting and the follow up /// calls to `read_pcm` will return None once all the pcm data has been returned. fn close(&mut self) { - self.inner.close() + self.inner.lock().unwrap().close() } }