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