summaryrefslogtreecommitdiff
path: root/pysoundtouch/src/pybpmdetect.cpp
blob: 03a673f820d8a426ec945d0ff5d020b45c2d599d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
/*
 * 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 "pybpmdetect.h"

#if PY_VERSION_HEX < 0x01060000
#define PyObject_DEL(op) PyMem_DEL((op))
#endif

PyTypeObject py_bpmdetect_t = {
    PyObject_HEAD_INIT(&PyType_Type)
    0,
    "BPMDetect",
    sizeof(py_bpmdetect),
    0,
    /* standard methods */
    (destructor) py_bpmdetect_dealloc,
    (printfunc) 0,
    (getattrfunc) py_bpmdetect_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_bpmdetect_new(PyObject* self, PyObject* args) {
  py_bpmdetect* ps = NULL;
  uint sampleRate, channels;
  
  // Needs to be constructed with 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_bpmdetect, &py_bpmdetect_t);
  ps->bpmdetect = new soundtouch::BPMDetect((int) channels, (int) sampleRate);
  ps->channels = (int) channels;

  return (PyObject*) ps;
}

// Deallocate the BPMDetect object
static void py_bpmdetect_dealloc(PyObject* self, PyObject* args) {
  py_bpmdetect* ps = PY_BPMDETECT(self);

  if (ps->bpmdetect) {
    delete ps->bpmdetect;
    ps->bpmdetect = NULL;
  }

  PyObject_DEL(self);
}

// Read in a number of samples for beat detection
static PyObject* py_bpmdetect_put_samples(PyObject* self, PyObject* args) {
  py_bpmdetect* ps = PY_BPMDETECT(self);
  int buflen;
  char* transfer;

  // Needs to be called with a string of samples
  if (!PyArg_ParseTuple(args, "s#", &transfer, &buflen)) {
    PyErr_SetString(PyExc_TypeError, "invalid argument");
	return NULL;
  }

  // Move all into our char-short union
  for (int ii = 0; ii < buflen; ii++)
    ps->buffer.chars[ii] = transfer[ii];

  // Input them into the BMP detector
  ps->bpmdetect->inputSamples(ps->buffer.shorts, (uint) buflen / (2 * ps->channels));

  Py_INCREF(Py_None);
  return Py_None;
}

// Perform the beat detection algorithm
// return the beats per minute
static PyObject* py_bpmdetect_get_bpm(PyObject* self, PyObject* args) {
  py_bpmdetect* ps = PY_BPMDETECT(self);

  // Return the BPM of the input samples
  float bpm = ps->bpmdetect->getBpm();
  
  return PyFloat_FromDouble(bpm);
}

/* housekeeping */

static PyMethodDef bpmdetect_methods[] = {
    { "put_samples", py_bpmdetect_put_samples, METH_VARARGS, "" },
    { "get_bpm", py_bpmdetect_get_bpm, METH_VARARGS, "" },
    { NULL, 0, 0, NULL }
};

// Extract information from the bpmdetect object
static PyObject* py_bpmdetect_getattr(PyObject* self, char* name) {
  return Py_FindMethod(bpmdetect_methods, self, name);
}