summaryrefslogtreecommitdiff
path: root/pysoundtouch/src/pysoundtouch.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'pysoundtouch/src/pysoundtouch.cpp')
-rw-r--r--pysoundtouch/src/pysoundtouch.cpp243
1 files changed, 243 insertions, 0 deletions
diff --git a/pysoundtouch/src/pysoundtouch.cpp b/pysoundtouch/src/pysoundtouch.cpp
new file mode 100644
index 0000000..ddc832b
--- /dev/null
+++ b/pysoundtouch/src/pysoundtouch.cpp
@@ -0,0 +1,243 @@
+/*
+ * python interface to soundtouch (the open-source audio processing library)
+ *
+ * The structure of this code was based on pymad-0.5.4
+ * This is a C++ file.
+ */
+
+#include <Python.h>
+
+extern "C" {
+ #include "soundtouchmodule.h"
+}
+#include "pysoundtouch.h"
+
+#if PY_VERSION_HEX < 0x01060000
+#define PyObject_DEL(op) PyMem_DEL((op))
+#endif
+
+/* For debugging:
+#include "WavFile.h"
+WavOutFile* inputs = new WavOutFile("/Users/jrising/inputs.wav", 44100, 16, 1);
+WavOutFile* outputs = new WavOutFile("/Users/jrising/outputs.wav", 44100, 16, 1);*/
+
+PyTypeObject py_soundtouch_t = {
+ PyObject_HEAD_INIT(&PyType_Type)
+ 0,
+ "Soundtouch",
+ sizeof(py_soundtouch),
+ 0,
+ /* standard methods */
+ (destructor) py_soundtouch_dealloc,
+ (printfunc) 0,
+ (getattrfunc) py_soundtouch_getattr,
+ (setattrfunc) 0,
+ (cmpfunc) 0,
+ (reprfunc) 0,
+ /* type categories */
+ 0, /* as number */
+ 0, /* as sequence */
+ 0, /* as mapping */
+ 0, /* hash */
+ 0, /* binary */
+ 0, /* repr */
+ 0, /* getattro */
+ 0, /* setattro */
+ 0, /* as buffer */
+ 0, /* tp_flags */
+ NULL
+};
+
+// Constructor
+PyObject* py_soundtouch_new(PyObject* self, PyObject* args) {
+ py_soundtouch* ps = NULL;
+ uint sampleRate, channels;
+
+ // Needs to be given the sampling rate and number of channels
+ if (!PyArg_ParseTuple(args, "II:Soundtouch", &sampleRate, &channels)) {
+ PyErr_SetString(PyExc_RuntimeError, "Requires sampling rate and number of channels (sample size must be 2)");
+ return NULL;
+ }
+
+ // Create the object
+ ps = PyObject_NEW(py_soundtouch, &py_soundtouch_t);
+ ps->soundtouch = new soundtouch::SoundTouch();
+ ps->channels = (int) channels;
+ ps->soundtouch->setSampleRate(sampleRate);
+ ps->soundtouch->setChannels(channels);
+
+ return (PyObject*) ps;
+}
+
+// Deallocate the SoundTouch object
+static void py_soundtouch_dealloc(PyObject* self, PyObject* args) {
+ py_soundtouch* ps = PY_SOUNDTOUCH(self);
+
+ if (ps->soundtouch) {
+ delete ps->soundtouch;
+ ps->soundtouch = NULL;
+ }
+
+ PyObject_DEL(self);
+}
+
+// Set the rate of playback
+static PyObject* py_soundtouch_set_rate(PyObject* self, PyObject* args) {
+ float rate;
+
+ // Given the rate as a fraction
+ if (!PyArg_ParseTuple(args, "f", &rate) || rate < 0) {
+ PyErr_SetString(PyExc_TypeError, "invalid argument");
+ return NULL;
+ }
+
+ PY_SOUNDTOUCH(self)->soundtouch->setRate(rate);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+// Set the tempo of playback
+static PyObject* py_soundtouch_set_tempo(PyObject* self, PyObject* args) {
+ float tempo;
+
+ // Given the tempo as a ratio
+ if (!PyArg_ParseTuple(args, "f", &tempo) || tempo < 0) {
+ PyErr_SetString(PyExc_TypeError, "invalid argument");
+ return NULL;
+ }
+
+ PY_SOUNDTOUCH(self)->soundtouch->setTempo(tempo);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+// Shift all pitches
+static PyObject* py_soundtouch_set_pitch(PyObject* self, PyObject* args) {
+ float pitch;
+
+ // Given the pitch adjustment as a fraction
+ if (!PyArg_ParseTuple(args, "f", &pitch) || pitch < 0) {
+ PyErr_SetString(PyExc_TypeError, "invalid argument");
+ return NULL;
+ }
+
+ PY_SOUNDTOUCH(self)->soundtouch->setPitch(pitch);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject* py_soundtouch_set_pitch_shift(PyObject* self, PyObject* args) {
+ float pitch;
+
+ // Given the pitch adjustment in half-steps
+ if (!PyArg_ParseTuple(args, "f", &pitch) || pitch < -12 || pitch > 12) {
+ PyErr_SetString(PyExc_TypeError, "invalid argument: pitch must be between -12 and 12");
+ return NULL;
+ }
+
+ PY_SOUNDTOUCH(self)->soundtouch->setPitchSemiTones(pitch);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+// Flush all samples to the output
+static PyObject* py_soundtouch_flush(PyObject* self, PyObject* args) {
+ PY_SOUNDTOUCH(self)->soundtouch->flush();
+
+ // For debugging:
+ //delete inputs;
+ //delete outputs;
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+// Clear all buffers
+static PyObject* py_soundtouch_clear(PyObject* self, PyObject* args) {
+ PY_SOUNDTOUCH(self)->soundtouch->clear();
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+// Add new samples
+static PyObject* py_soundtouch_put_samples(PyObject* self, PyObject* args) {
+ py_soundtouch* ps = PY_SOUNDTOUCH(self);
+ int buflen;
+ char* transfer;
+
+ // Given a string of samples to add
+ if (!PyArg_ParseTuple(args, "s#", &transfer, &buflen)) {
+ PyErr_SetString(PyExc_TypeError, "invalid argument");
+ return NULL;
+ }
+
+ // Move them into our char-short union
+ for (int ii = 0; ii < buflen; ii++)
+ ps->buffer.chars[ii] = transfer[ii];
+
+ // Add them
+ ps->soundtouch->putSamples(ps->buffer.shorts, (uint) buflen / (2 * ps->channels));
+
+ // For debugging:
+ //inputs->write(ps->buffer.shorts, buflen / 2);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+// Return the shifted samples
+static PyObject* py_soundtouch_get_samples(PyObject* self, PyObject* args) {
+ py_soundtouch* ps = PY_SOUNDTOUCH(self);
+ uint maxSamples;
+
+ // Given the number of samples to request
+ if (!PyArg_ParseTuple(args, "|I", &maxSamples)) {
+ PyErr_SetString(PyExc_TypeError, "invalid argument");
+ return NULL;
+ }
+
+ // Move them into our char-short union
+ uint received = ps->soundtouch->receiveSamples(ps->buffer.shorts, maxSamples);
+
+ // For debugging:
+ //outputs->write(ps->buffer.shorts, received * ps->channels);
+
+ return PyString_FromStringAndSize(ps->buffer.chars, received * 2 * ps->channels);
+}
+
+// Return how many samples are available for output
+static PyObject* py_soundtouch_ready_count(PyObject* self, PyObject* args) {
+ return PyInt_FromLong(PY_SOUNDTOUCH(self)->soundtouch->numSamples());
+}
+
+// Return how many samples will be available given the current data
+static PyObject* py_soundtouch_waiting_count(PyObject* self, PyObject* args) {
+ return PyInt_FromLong(PY_SOUNDTOUCH(self)->soundtouch->numUnprocessedSamples());
+}
+
+/* housekeeping */
+
+static PyMethodDef soundtouch_methods[] = {
+ { "set_rate", py_soundtouch_set_rate, METH_VARARGS, "" },
+ { "set_tempo", py_soundtouch_set_tempo, METH_VARARGS, "" },
+ { "set_pitch", py_soundtouch_set_pitch, METH_VARARGS, "" },
+ { "set_pitch_shift", py_soundtouch_set_pitch_shift, METH_VARARGS, "" },
+ { "flush", py_soundtouch_flush, METH_VARARGS, "" },
+ { "clear", py_soundtouch_clear, METH_VARARGS, "" },
+ { "put_samples", py_soundtouch_put_samples, METH_VARARGS, "" },
+ { "get_samples", py_soundtouch_get_samples, METH_VARARGS, "" },
+ { "ready_count", py_soundtouch_ready_count, METH_VARARGS, "" },
+ { "waiting_count", py_soundtouch_waiting_count, METH_VARARGS, "" },
+ { NULL, 0, 0, NULL }
+};
+
+// Get additional attributes from the SoundTouch object
+static PyObject* py_soundtouch_getattr(PyObject* self, char* name) {
+ // TODO: add soundtouch.getSetting here? Add a setattr with soundtouch.setSetting?
+ return Py_FindMethod(soundtouch_methods, self, name);
+}