PageRenderTime 475ms CodeModel.GetById 121ms app.highlight 174ms RepoModel.GetById 117ms app.codeStats 1ms

/external/pydirac225/diracmodule.cpp

http://echo-nest-remix.googlecode.com/
C++ | 207 lines | 157 code | 34 blank | 16 comment | 26 complexity | 17968bdef185e94ae73d06d126a8fb62 MD5 | raw file
  1#include <Python.h>
  2#include <numpy/arrayobject.h>
  3#include <numpy/libnumarray.h>
  4#include "source/Dirac.h"
  5#include "source/Dirac_LE.h"
  6#include <stdexcept>
  7
  8#define DIMENSIONS 2
  9
 10#ifndef Py_RETURN_NONE
 11#define Py_RETURN_NONE Py_INCREF(Py_None); return Py_None;
 12#endif
 13
 14static PyObject *DiracError;
 15
 16static PyObject *Dirac_timeScale(PyObject *self, PyObject *args) 
 17{
 18    // Only for lists:
 19    double *listInDurations = NULL;
 20    double *listOutDurations = NULL; 
 21    uint numChunks = 0; 
 22    
 23    float rate = 1.0f; // Only for single rates
 24    uint sampleRate = 44100;// default
 25    uint quality = 0;       // default
 26    
 27    // Parse input sound object, a numpy array.
 28    PyObject *objInSound, *objRate;
 29    if (!PyArg_ParseTuple(args, "OO|ii", &objInSound, &objRate, &sampleRate, &quality))
 30        return NULL;
 31    
 32    // Convert to actual PyArray with proper type
 33    PyArrayObject *inSound = (PyArrayObject *) NA_InputArray(objInSound, tFloat32, NUM_C_ARRAY);
 34    
 35    // Check that everything looks good
 36    if (!inSound) 
 37    {
 38        Py_XDECREF(inSound);
 39        PyErr_Format(DiracError, "couldn't convert array to PyArrayObject.");
 40        return NULL; 
 41    } 
 42    if (inSound->nd != 1 && inSound->nd != 2) 
 43    {
 44        Py_XDECREF(inSound);
 45        PyErr_Format(DiracError, "sound arrays must have 1 (mono) or 2 (stereo) dimensions.");
 46        return NULL;
 47    }
 48    
 49    if (!PyList_Check(objRate) && !PyFloat_Check(objRate)) 
 50    {
 51        PyErr_Format(DiracError, "expecting a float or list of tuples as second argument.");
 52        return NULL;
 53    }
 54    
 55    // Are we dealing with a list or a float?
 56    bool isList = PyList_Check(objRate);
 57    
 58    uint numOutSamples = 0;
 59    uint numInSamples = inSound->dimensions[0];
 60    uint numInChannels = inSound->dimensions[1];
 61    uint numOutChannels = numInChannels;
 62    
 63    // Main switch
 64    if (isList) 
 65    {
 66        numChunks = (uint) PyList_Size(objRate);
 67        // Let's add one more space in the case there's no 0 index
 68        long *listIndexes = (long *)malloc(numChunks * sizeof(long));
 69        double *listRates = (double *)malloc(numChunks * sizeof(double));
 70        
 71        for (uint i = 0; i < numChunks; i++)
 72        {
 73            PyObject *item = PyList_GetItem(objRate, i);
 74            if (!PyTuple_Check(item)) 
 75            {
 76                PyErr_Format(DiracError, "expecting a list of tuples for second argument.");
 77                return NULL;
 78            }
 79            
 80            long index = (long) PyLong_AsLong(PyTuple_GetItem(item, 0));
 81            double rate = (double) PyFloat_AsDouble(PyTuple_GetItem(item, 1));
 82            
 83            if (i == 0 && index != 0) 
 84            {
 85                PyErr_Format(DiracError, "first index must be 0.");
 86                return NULL;
 87            }
 88            if ((long)numInSamples < index)
 89            {
 90                PyErr_Format(DiracError, "at least one index goes beyond the limits of the array.");
 91                return NULL;
 92            }
 93            
 94            listIndexes[i] = index;
 95            listRates[i] = rate;
 96        }
 97        
 98        listInDurations = (double *)malloc(numChunks * sizeof(double));
 99        listOutDurations = (double *)malloc(numChunks * sizeof(double));
100        
101        for (uint i = 0; i < numChunks; i++)
102        {   
103            long thisNumSamples = 0;
104            if (i == numChunks-1) 
105                thisNumSamples = numInSamples - listIndexes[numChunks-1];
106            else
107                thisNumSamples = listIndexes[i+1] - listIndexes[i];
108            numOutSamples += int(thisNumSamples * listRates[i]);
109            listInDurations[i] = (double)thisNumSamples / sampleRate;
110            listOutDurations[i] = listInDurations[i] * listRates[i];
111        }
112        
113        free(listIndexes);
114        free(listRates);
115    }
116    else 
117    {
118        rate = (float) PyFloat_AsDouble(objRate);
119        numOutSamples = (uint)(numInSamples * rate);
120    }
121    
122    // Create buffers we'll use for processing
123    float **inSamples = allocateAudioBuffer(numInChannels, numInSamples);
124    float **outSamples = allocateAudioBuffer(numOutChannels, numOutSamples);
125    
126    // For now, Dirac uses non-interlaced buffers. Make a copy.
127    float *interlacedInSamples = (Float32 *) NA_OFFSETDATA(inSound);
128    deinterlace(inSamples, interlacedInSamples, numInSamples, numInChannels);
129    
130    // Convert to real floats between -1 and 1 before processing...
131    for (uint i = 0; i < numInSamples; i++)
132    {
133        inSamples[0][i] /= 32768.0f;
134        inSamples[1][i] /= 32768.0f;
135    }
136    
137    try 
138    {
139        // Actual time stretching
140        if (isList) 
141        {
142            if (time_scale_list(outSamples, listOutDurations, inSamples, listInDurations, numChunks, numInChannels, (float)sampleRate, quality) < 0)
143            {
144                PyErr_Format(DiracError, "problem with time_scale_list.");
145                return NULL;
146            }
147            free(listInDurations);
148            free(listOutDurations);
149        }
150        else 
151        {
152            if (time_scale(outSamples, (double)numOutSamples/sampleRate, inSamples, (double)numInSamples/sampleRate, numInChannels, (float)sampleRate, quality) < 0) 
153            {
154                PyErr_Format(DiracError, "problem with time_scale.");
155                return NULL;
156            }
157        }
158    }
159    catch (std::runtime_error &error) 
160    {
161        PyErr_Format(PyExc_RuntimeError, error.what());
162        return NULL;
163    }
164    
165    for (uint i = 0; i < numOutSamples; i++) 
166    {
167        outSamples[0][i] = limiter(outSamples[0][i]) * 32768.0f; // Not sure why limiting is necessary!
168        outSamples[1][i] = limiter(outSamples[1][i]) * 32768.0f;
169    }
170    
171    // Set dimensions for output object
172    npy_intp dims[DIMENSIONS];
173    dims[0] = numOutSamples;
174    dims[1] = numOutChannels;
175    
176    // Allocate interlaced memory for output sound object
177    PyArrayObject* outSound = (PyArrayObject *)PyArray_SimpleNew(DIMENSIONS, dims, NPY_FLOAT);
178    
179    // Get the actual array
180    float* interlacedOutSamples = (float *)outSound->data;
181    
182    // Copy processed data into the interlaced buffer of floats
183    interlace(interlacedOutSamples, outSamples, numOutSamples, numOutChannels);
184    
185    // Deallocate memory we don't need anymore
186    deallocateAudioBuffer(inSamples, numInChannels);
187    deallocateAudioBuffer(outSamples, numOutChannels);
188    
189    return PyArray_Return(outSound);
190}
191
192static PyMethodDef Dirac_methods[] = 
193{
194    {"timeScale", (PyCFunction) Dirac_timeScale, METH_VARARGS, "Time scale an audio buffer given a single rate, or a list of indexes and rates."},
195    {NULL}
196};
197
198PyMODINIT_FUNC initdirac(void) 
199{
200    Py_InitModule3("dirac", Dirac_methods, "Dirac LE audio time-stretching library");
201    
202    DiracError = PyErr_NewException("dirac.error", NULL, NULL);
203    Py_INCREF(DiracError);
204    
205    import_array();
206    import_libnumarray();
207}