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);
}
|