soundtouch-jni.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419
  1. #include <jni.h>
  2. #include <android/log.h>
  3. #include <queue>
  4. #include <string>
  5. #include <stdlib.h>
  6. #include <stdio.h>
  7. #include <vector>
  8. //#include <stdio.h>
  9. //#include <dlfcn.h>
  10. #include "soundtouch/include/SoundTouch.h"
  11. //#include "TimeShiftEffect.h"
  12. #define LOGV(...) __android_log_print((int)ANDROID_LOG_INFO, "SOUNDTOUCH", __VA_ARGS__)
  13. //#define //LOGV(...)
  14. #define DLL_PUBLIC __attribute__ ((visibility ("default")))
  15. using namespace soundtouch;
  16. using namespace std;
  17. class SoundTouchStream: public SoundTouch {
  18. private:
  19. queue<jbyte>* byteBufferOut;
  20. int sampleRate;
  21. int bytesPerSample;
  22. public:
  23. queue<jbyte>* getStream() {
  24. return byteBufferOut;
  25. }
  26. int getSampleRate() {
  27. return sampleRate;
  28. }
  29. int getBytesPerSample() {
  30. return bytesPerSample;
  31. }
  32. void setSampleRate(int sampleRate) {
  33. SoundTouch::setSampleRate(sampleRate);
  34. this->sampleRate = sampleRate;
  35. }
  36. void setBytesPerSample(int bytesPerSample) {
  37. this->bytesPerSample = bytesPerSample;
  38. }
  39. uint getChannels() {
  40. return channels;
  41. }
  42. SoundTouchStream() {
  43. byteBufferOut = new queue<jbyte>();
  44. sampleRate = bytesPerSample = 0;
  45. }
  46. SoundTouchStream(const SoundTouchStream& other) {
  47. byteBufferOut = new queue<jbyte>();
  48. sampleRate = bytesPerSample = 0;
  49. }
  50. };
  51. const int MAX_TRACKS = 16;
  52. vector<SoundTouchStream> stStreams(MAX_TRACKS);
  53. static void* getConvBuffer(int);
  54. static int write(const float*, queue<jbyte>*, int, int);
  55. static void setup(SoundTouchStream&, int, int, int, float, float);
  56. static void convertInput(jbyte*, float*, int, int);
  57. static inline int saturate(float, float, float);
  58. static void* getConvBuffer(int);
  59. static int process(SoundTouchStream&, SAMPLETYPE*, queue<jbyte>*, int, bool);
  60. static void setPitchSemi(SoundTouchStream&, float);
  61. static void setTempo(SoundTouchStream&, float);
  62. static void setRate(SoundTouchStream&, float);
  63. static void setTempoChange(SoundTouchStream&, float);
  64. static void setRateChange(SoundTouchStream&, float);
  65. static int copyBytes(jbyte*, queue<jbyte>*, int);
  66. #ifdef __cplusplus
  67. extern "C" DLL_PUBLIC void Java_com_smp_soundtouchandroid_SoundTouch_clearBytes(
  68. JNIEnv *env, jobject thiz, jint track) {
  69. SoundTouchStream& soundTouch = stStreams.at(track);
  70. const int BUFF_SIZE = 8192;
  71. queue<jbyte>* byteBufferOut = soundTouch.getStream();
  72. SAMPLETYPE* fBufferIn = new SAMPLETYPE[BUFF_SIZE];
  73. soundTouch.clear();
  74. delete[] fBufferIn;
  75. fBufferIn = NULL;
  76. while (!byteBufferOut->empty()) {
  77. byteBufferOut->pop();
  78. }
  79. }
  80. extern "C" DLL_PUBLIC void Java_com_smp_soundtouchandroid_SoundTouch_setup(
  81. JNIEnv *env, jobject thiz, jint track, jint channels, jint samplingRate,
  82. jint bytesPerSample, jfloat tempo, jfloat pitchSemi) {
  83. SoundTouchStream& soundTouch = stStreams.at(track);
  84. setup(soundTouch, channels, samplingRate, bytesPerSample, tempo, pitchSemi);
  85. }
  86. extern "C" DLL_PUBLIC void Java_com_smp_soundtouchandroid_SoundTouch_finish(
  87. JNIEnv *env, jobject thiz, jint track, int length) {
  88. SoundTouchStream& soundTouch = stStreams.at(track);
  89. const int bytesPerSample = soundTouch.getBytesPerSample();
  90. const int BUFF_SIZE = length / bytesPerSample;
  91. queue<jbyte>* byteBufferOut = soundTouch.getStream();
  92. SAMPLETYPE* fBufferIn = new SAMPLETYPE[BUFF_SIZE];
  93. process(soundTouch, fBufferIn, byteBufferOut, BUFF_SIZE, true); //audio is finishing
  94. delete[] fBufferIn;
  95. fBufferIn = NULL;
  96. }
  97. extern "C" DLL_PUBLIC void Java_com_smp_soundtouchandroid_SoundTouch_putBytes(
  98. JNIEnv *env, jobject thiz, jint track, jbyteArray input, jint length) {
  99. SoundTouchStream& soundTouch = stStreams.at(track);
  100. const int bytesPerSample = soundTouch.getBytesPerSample();
  101. const int BUFF_SIZE = length / bytesPerSample;
  102. queue<jbyte>* byteBufferOut = soundTouch.getStream();
  103. jboolean isCopy;
  104. jbyte* ar = env->GetByteArrayElements(input, &isCopy);
  105. SAMPLETYPE* fBufferIn = new SAMPLETYPE[BUFF_SIZE];
  106. convertInput(ar, fBufferIn, BUFF_SIZE, bytesPerSample);
  107. process(soundTouch, fBufferIn, byteBufferOut, BUFF_SIZE, false); //audio is ongoing.
  108. env->ReleaseByteArrayElements(input, ar, JNI_ABORT);
  109. delete[] fBufferIn;
  110. fBufferIn = NULL;
  111. }
  112. extern "C" DLL_PUBLIC jint Java_com_smp_soundtouchandroid_SoundTouch_getBytes(
  113. JNIEnv *env, jobject thiz, jint track, jbyteArray get, jint toGet) {
  114. queue<jbyte>* byteBufferOut = stStreams.at(track).getStream();
  115. jbyte* res = new jbyte[toGet];
  116. jint bytesWritten;
  117. jboolean isCopy;
  118. jbyte* ar = (jbyte*) env->GetPrimitiveArrayCritical(get, &isCopy);
  119. bytesWritten = copyBytes(ar, byteBufferOut, toGet);
  120. env->ReleasePrimitiveArrayCritical(get, ar, JNI_ABORT);
  121. delete[] res;
  122. res = NULL;
  123. return bytesWritten;
  124. }
  125. static int copyBytes(jbyte* arrayOut, queue<jbyte>* byteBufferOut, int toGet) {
  126. int bytesWritten = 0;
  127. for (int i = 0; i < toGet; i++) {
  128. if (byteBufferOut->size() > 0) {
  129. arrayOut[i] = byteBufferOut->front();
  130. byteBufferOut->pop();
  131. ++bytesWritten;
  132. } else {
  133. break;
  134. }
  135. }
  136. return bytesWritten;
  137. }
  138. extern "C" DLL_PUBLIC void Java_com_smp_soundtouchandroid_SoundTouch_setPitchSemi(
  139. JNIEnv *env, jobject thiz, jint track, jfloat pitchSemi) {
  140. SoundTouchStream& soundTouch = stStreams.at(track);
  141. setPitchSemi(soundTouch, pitchSemi);
  142. }
  143. extern "C" DLL_PUBLIC void Java_com_smp_soundtouchandroid_SoundTouch_setTempo(
  144. JNIEnv *env, jobject thiz, jint track, jfloat tempo) {
  145. SoundTouchStream& soundTouch = stStreams.at(track);
  146. setTempo(soundTouch, tempo);
  147. }
  148. extern "C" DLL_PUBLIC void Java_com_smp_soundtouchandroid_SoundTouch_setRate(
  149. JNIEnv *env, jobject thiz, jint track, jfloat rate) {
  150. SoundTouchStream& soundTouch = stStreams.at(track);
  151. setRate(soundTouch, rate);
  152. }
  153. extern "C" DLL_PUBLIC void Java_com_smp_soundtouchandroid_SoundTouch_setRateChange(
  154. JNIEnv *env, jobject thiz, jint track, jfloat rateChange) {
  155. SoundTouchStream& soundTouch = stStreams.at(track);
  156. setRateChange(soundTouch, rateChange);
  157. }
  158. extern "C" DLL_PUBLIC jlong Java_com_smp_soundtouchandroid_SoundTouch_getOutputBufferSize(
  159. JNIEnv *env, jobject thiz, jint track) {
  160. SoundTouchStream& soundTouch = stStreams.at(track);
  161. queue<jbyte>* byteBufferOut = soundTouch.getStream();
  162. return byteBufferOut->size();
  163. }
  164. extern "C" DLL_PUBLIC void Java_com_smp_soundtouchandroid_SoundTouch_setTempoChange(
  165. JNIEnv *env, jobject thiz, jint track, jfloat tempoChange) {
  166. SoundTouchStream& soundTouch = stStreams.at(track);
  167. setTempoChange(soundTouch, tempoChange);
  168. }
  169. extern "C" DLL_PUBLIC void Java_com_smp_soundtouchandroid_SoundTouch_setSpeech(
  170. JNIEnv *env, jobject thiz, jint track, jboolean speech) {
  171. SoundTouchStream& soundTouch = stStreams.at(track);
  172. if (speech) {
  173. // use settings for speech processing
  174. soundTouch.setSetting(SETTING_SEQUENCE_MS, 40);
  175. soundTouch.setSetting(SETTING_SEEKWINDOW_MS, 15);
  176. soundTouch.setSetting(SETTING_OVERLAP_MS, 8);
  177. //fprintf(stderr, "Tune processing parameters for speech processing.\n");
  178. }
  179. else
  180. {
  181. soundTouch.setSetting(SETTING_SEQUENCE_MS, 0);
  182. soundTouch.setSetting(SETTING_SEEKWINDOW_MS, 0);
  183. soundTouch.setSetting(SETTING_OVERLAP_MS, 8);
  184. }
  185. }
  186. static int process(SoundTouchStream& soundTouch, SAMPLETYPE* fBufferIn,
  187. queue<jbyte>* byteBufferOut, const int BUFF_SIZE, bool finishing) {
  188. const uint channels = soundTouch.getChannels();
  189. const int buffSizeSamples = BUFF_SIZE / channels;
  190. const int bytesPerSample = soundTouch.getBytesPerSample();
  191. int nSamples = BUFF_SIZE / channels;
  192. int processed = 0;
  193. if (finishing) {
  194. soundTouch.flush();
  195. } else {
  196. soundTouch.putSamples(fBufferIn, nSamples);
  197. }
  198. do {
  199. nSamples = soundTouch.receiveSamples(fBufferIn, buffSizeSamples);
  200. processed += write(fBufferIn, byteBufferOut, nSamples * channels,
  201. bytesPerSample);
  202. } while (nSamples != 0);
  203. return processed;
  204. }
  205. static void* getConvBuffer(int sizeBytes) {
  206. int convBuffSize = (sizeBytes + 15) & -8;
  207. // round up to following 8-byte bounday
  208. char *convBuff = new char[convBuffSize];
  209. return convBuff;
  210. }
  211. static int write(const float *bufferIn, queue<jbyte>* bufferOut, int numElems,
  212. int bytesPerSample) {
  213. int numBytes;
  214. int oldSize = bufferOut->size();
  215. if (numElems == 0)
  216. return 0;
  217. numBytes = numElems * bytesPerSample;
  218. short *temp = (short*) getConvBuffer(numBytes);
  219. switch (bytesPerSample) {
  220. case 1: {
  221. unsigned char *temp2 = (unsigned char *) temp;
  222. for (int i = 0; i < numElems; i++) {
  223. temp2[i] = (unsigned char) saturate(bufferIn[i] * 128.0f + 128.0f,
  224. 0.0f, 255.0f);
  225. }
  226. break;
  227. }
  228. case 2: {
  229. short *temp2 = (short *) temp;
  230. for (int i = 0; i < numElems; i++) {
  231. short value = (short) saturate(bufferIn[i] * 32768.0f, -32768.0f,
  232. 32767.0f);
  233. temp2[i] = value;
  234. }
  235. break;
  236. }
  237. case 3: {
  238. char *temp2 = (char *) temp;
  239. for (int i = 0; i < numElems; i++) {
  240. int value = saturate(bufferIn[i] * 8388608.0f, -8388608.0f,
  241. 8388607.0f);
  242. *((int*) temp2) = value;
  243. temp2 += 3;
  244. }
  245. break;
  246. }
  247. case 4: {
  248. int *temp2 = (int *) temp;
  249. for (int i = 0; i < numElems; i++) {
  250. int value = saturate(bufferIn[i] * 2147483648.0f, -2147483648.0f,
  251. 2147483647.0f);
  252. temp2[i] = value;
  253. }
  254. break;
  255. }
  256. default:
  257. //should throw
  258. break;
  259. }
  260. for (int i = 0; i < numBytes / 2; ++i) {
  261. bufferOut->push(temp[i] & 0xff);
  262. bufferOut->push((temp[i] >> 8) & 0xff);
  263. }
  264. delete[] temp;
  265. temp = NULL;
  266. return bufferOut->size() - oldSize;
  267. }
  268. static void setPitchSemi(SoundTouchStream& soundTouch, float pitchSemi) {
  269. soundTouch.setPitchSemiTones(pitchSemi);
  270. }
  271. static void setTempo(SoundTouchStream& soundTouch, float tempo) {
  272. soundTouch.setTempo(tempo);
  273. }
  274. static void setRate(SoundTouchStream& soundTouch, float rate) {
  275. soundTouch.setRate(rate);
  276. }
  277. static void setTempoChange(SoundTouchStream& soundTouch, float tempoChange) {
  278. soundTouch.setTempoChange(tempoChange);
  279. }
  280. static void setRateChange(SoundTouchStream& soundTouch, float rateChange) {
  281. soundTouch.setRateChange(rateChange);
  282. }
  283. static void setup(SoundTouchStream& soundTouch, int channels, int sampleRate,
  284. int bytesPerSample, float tempoChange, float pitchSemi) {
  285. soundTouch.setBytesPerSample(bytesPerSample);
  286. soundTouch.setSampleRate(sampleRate);
  287. soundTouch.setChannels(channels);
  288. soundTouch.setTempo(tempoChange);
  289. soundTouch.setPitchSemiTones(pitchSemi);
  290. soundTouch.setRateChange(0);
  291. soundTouch.setSetting(SETTING_USE_QUICKSEEK, false);
  292. soundTouch.setSetting(SETTING_USE_AA_FILTER, true);
  293. }
  294. static void convertInput(jbyte* input, float* output, const int BUFF_SIZE,
  295. int bytesPerSample) {
  296. switch (bytesPerSample) {
  297. case 1: {
  298. unsigned char *temp2 = (unsigned char*) input;
  299. double conv = 1.0 / 128.0;
  300. for (int i = 0; i < BUFF_SIZE; i++) {
  301. output[i] = (float) (temp2[i] * conv - 1.0);
  302. }
  303. break;
  304. }
  305. case 2: {
  306. short *temp2 = (short*) input;
  307. double conv = 1.0 / 32768.0;
  308. for (int i = 0; i < BUFF_SIZE; i++) {
  309. short value = temp2[i];
  310. output[i] = (float) (value * conv);
  311. }
  312. break;
  313. }
  314. case 3: {
  315. char *temp2 = (char *) input;
  316. double conv = 1.0 / 8388608.0;
  317. for (int i = 0; i < BUFF_SIZE; i++) {
  318. int value = *((int*) temp2);
  319. value = value & 0x00ffffff; // take 24 bits
  320. value |= (value & 0x00800000) ? 0xff000000 : 0; // extend minus sign bits
  321. output[i] = (float) (value * conv);
  322. temp2 += 3;
  323. }
  324. break;
  325. }
  326. case 4: {
  327. int *temp2 = (int *) input;
  328. double conv = 1.0 / 2147483648.0;
  329. assert(sizeof(int) == 4);
  330. for (int i = 0; i < BUFF_SIZE; i++) {
  331. int value = temp2[i];
  332. output[i] = (float) (value * conv);
  333. }
  334. break;
  335. }
  336. }
  337. }
  338. static inline int saturate(float fvalue, float minval, float maxval) {
  339. if (fvalue > maxval) {
  340. fvalue = maxval;
  341. } else if (fvalue < minval) {
  342. fvalue = minval;
  343. }
  344. return (int) fvalue;
  345. }
  346. #endif