You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

386 lines
10KB

  1. // #define USE_CURL defined
  2. #include "Bidoo.hpp"
  3. #include "dsp/digital.hpp"
  4. #include "dsp/samplerate.hpp"
  5. #include "BidooComponents.hpp"
  6. #include "dsp/ringbuffer.hpp"
  7. #include "dsp/frame.hpp"
  8. #include <algorithm>
  9. #include <cctype>
  10. #include <atomic>
  11. #include <sstream>
  12. #include <thread>
  13. #ifdef USE_CURL
  14. #include "curl/curl.h"
  15. #endif // USE_CURL
  16. #define MINIMP3_IMPLEMENTATION
  17. #include "dep/minimp3/minimp3.h"
  18. using namespace std;
  19. namespace rack_plugin_Bidoo {
  20. struct threadReadData {
  21. DoubleRingBuffer<char,262144> *dataToDecodeRingBuffer;
  22. string url;
  23. string secUrl;
  24. std::atomic<bool> *dl;
  25. std::atomic<bool> *free;
  26. };
  27. struct threadDecodeData {
  28. DoubleRingBuffer<char,262144> *dataToDecodeRingBuffer;
  29. DoubleRingBuffer<Frame<2>,262144> *dataAudioRingBuffer;
  30. mp3dec_t mp3d;
  31. std::atomic<bool> *dc;
  32. std::atomic<bool> *free;
  33. };
  34. size_t WriteMemoryCallback(void *contents, size_t size, size_t nmemb, void *userp)
  35. {
  36. struct threadReadData *pData = (struct threadReadData *) userp;
  37. size_t realsize = size * nmemb;
  38. if ((pData->dl->load()) && (realsize < pData->dataToDecodeRingBuffer->capacity())) //
  39. {
  40. memcpy(pData->dataToDecodeRingBuffer->endData(), contents, realsize);
  41. pData->dataToDecodeRingBuffer->endIncr(realsize);
  42. return realsize;
  43. }
  44. return 0;
  45. }
  46. size_t WriteUrlCallback(void *contents, size_t size, size_t nmemb, void *userp)
  47. {
  48. struct threadReadData *pData = (struct threadReadData *) userp;
  49. size_t realsize = size * nmemb;
  50. pData->secUrl += (const char*) contents;
  51. return realsize;
  52. }
  53. void * threadDecodeTask(threadDecodeData data)
  54. {
  55. data.free->store(false);
  56. mp3dec_frame_info_t info;
  57. DoubleRingBuffer<Frame<2>,4096> *tmpBuffer = new DoubleRingBuffer<Frame<2>,4096>();
  58. SampleRateConverter<2> conv;
  59. int inSize;
  60. int outSize;
  61. while (data.dc->load()) {
  62. short pcm[MINIMP3_MAX_SAMPLES_PER_FRAME];
  63. if (data.dataToDecodeRingBuffer->size() > 64000) {
  64. int samples = mp3dec_decode_frame(&data.mp3d, (const uint8_t*)data.dataToDecodeRingBuffer->startData(), data.dataToDecodeRingBuffer->size(), pcm, &info);
  65. if (info.frame_bytes > 0) {
  66. if (samples > 0) {
  67. if (info.channels == 1) {
  68. for(int i = 0; i < samples; i++) {
  69. if (!data.dc->load()) break;
  70. Frame<2> newFrame;
  71. newFrame.samples[0]=(float)pcm[i]/32768;
  72. newFrame.samples[1]=(float)pcm[i]/32768;
  73. tmpBuffer->push(newFrame);
  74. }
  75. }
  76. else {
  77. for(int i = 0; i < 2 * samples; i=i+2) {
  78. if (!data.dc->load()) break;
  79. Frame<2> newFrame;
  80. newFrame.samples[0]=(float)pcm[i]/32768;
  81. newFrame.samples[1]=(float)pcm[i+1]/32768;
  82. tmpBuffer->push(newFrame);
  83. }
  84. }
  85. }
  86. if (samples == 0) {
  87. //printf("invalid data \n");
  88. }
  89. data.dataToDecodeRingBuffer->startIncr(info.frame_bytes);
  90. conv.setRates(info.hz, engineGetSampleRate());
  91. conv.setQuality(10);
  92. inSize = tmpBuffer->size();
  93. outSize = data.dataAudioRingBuffer->capacity();
  94. conv.process(tmpBuffer->startData(), &inSize,data.dataAudioRingBuffer->endData(), &outSize);
  95. tmpBuffer->startIncr(inSize);
  96. data.dataAudioRingBuffer->endIncr((size_t)outSize);
  97. }
  98. }
  99. }
  100. data.free->store(true);
  101. return 0;
  102. }
  103. #ifdef USE_CURL
  104. void * threadReadTask(threadReadData data)
  105. {
  106. data.free->store(false);
  107. CURL *curl;
  108. curl = curl_easy_init();
  109. curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
  110. curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
  111. curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
  112. string zeUrl;
  113. data.secUrl == "" ? zeUrl = data.url : zeUrl = data.secUrl;
  114. zeUrl.erase(std::remove_if(zeUrl.begin(), zeUrl.end(), [](unsigned char x){return std::isspace(x);}), zeUrl.end());
  115. if (stringExtension(data.url) == "pls") {
  116. istringstream iss(zeUrl);
  117. for (std::string line; std::getline(iss, line); )
  118. {
  119. std::size_t found=line.find("http");
  120. if (found!=std::string::npos) {
  121. zeUrl = line.substr(found);
  122. break;
  123. }
  124. }
  125. }
  126. curl_easy_setopt(curl, CURLOPT_URL, zeUrl.c_str());
  127. curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
  128. curl_easy_setopt(curl, CURLOPT_WRITEDATA, &data);
  129. curl_easy_perform(curl);
  130. curl_easy_cleanup(curl);
  131. data.free->store(true);
  132. return 0;
  133. }
  134. #endif // USE_CURL
  135. #ifdef USE_CURL
  136. void * urlTask(threadReadData data)
  137. {
  138. data.free->store(false);
  139. CURL *curl;
  140. curl = curl_easy_init();
  141. curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
  142. curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
  143. curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
  144. curl_easy_setopt(curl, CURLOPT_URL, data.url.c_str());
  145. curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteUrlCallback);
  146. curl_easy_setopt(curl, CURLOPT_WRITEDATA, &data);
  147. data.secUrl = "";
  148. curl_easy_perform(curl);
  149. curl_easy_cleanup(curl);
  150. data.free->store(true);
  151. thread iThread = thread(threadReadTask, data);
  152. iThread.detach();
  153. return 0;
  154. }
  155. #endif // USE_CURL
  156. struct ANTN : Module {
  157. enum ParamIds {
  158. URL_PARAM,
  159. TRIG_PARAM,
  160. GAIN_PARAM,
  161. NUM_PARAMS
  162. };
  163. enum InputIds {
  164. NUM_INPUTS
  165. };
  166. enum OutputIds {
  167. OUTL_OUTPUT,
  168. OUTR_OUTPUT,
  169. NUM_OUTPUTS
  170. };
  171. enum LightIds {
  172. NUM_LIGHTS
  173. };
  174. string url;
  175. SchmittTrigger trigTrigger;
  176. bool read = false;
  177. DoubleRingBuffer<Frame<2>,262144> dataAudioRingBuffer;
  178. DoubleRingBuffer<char,262144> dataToDecodeRingBuffer;
  179. thread rThread, dThread;
  180. threadReadData rData;
  181. threadDecodeData dData;
  182. std::atomic<bool> tDl;
  183. std::atomic<bool> tDc;
  184. std::atomic<bool> trFree;
  185. std::atomic<bool> tdFree;
  186. ANTN() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) {
  187. tDl.store(true);
  188. tDc.store(true);
  189. trFree.store(true);
  190. tdFree.store(true);
  191. rData.dataToDecodeRingBuffer = &dataToDecodeRingBuffer;
  192. rData.dl = &tDl;
  193. rData.free = &trFree;
  194. dData.dataToDecodeRingBuffer = &dataToDecodeRingBuffer;
  195. dData.dataAudioRingBuffer = &dataAudioRingBuffer;
  196. dData.dc = &tDc;
  197. dData.free = &tdFree;
  198. mp3dec_init(&dData.mp3d);
  199. }
  200. ~ANTN() {
  201. tDc.store(false);
  202. while(!tdFree) {
  203. }
  204. tDl.store(false);
  205. while(!trFree) {
  206. }
  207. }
  208. json_t *toJson() override {
  209. json_t *rootJ = json_object();
  210. json_object_set_new(rootJ, "url", json_string(url.c_str()));
  211. return rootJ;
  212. }
  213. void fromJson(json_t *rootJ) override {
  214. json_t *urlJ = json_object_get(rootJ, "url");
  215. if (urlJ)
  216. url = json_string_value(urlJ);
  217. }
  218. void step() override;
  219. void onSampleRateChange() override;
  220. };
  221. void ANTN::onSampleRateChange() {
  222. read = false;
  223. dataAudioRingBuffer.clear();
  224. }
  225. void ANTN::step() {
  226. if (trigTrigger.process(params[TRIG_PARAM].value)) {
  227. tDc.store(false);
  228. while(!tdFree) {
  229. }
  230. tDl.store(false);
  231. while(!trFree) {
  232. }
  233. read = false;
  234. dataToDecodeRingBuffer.clear();
  235. dataAudioRingBuffer.clear();
  236. tDl.store(true);
  237. #ifdef USE_CURL
  238. rData.url = url;
  239. if ((stringExtension(rData.url) == "m3u") || (stringExtension(rData.url) == "pls")) {
  240. rThread = thread(urlTask, std::ref(rData));
  241. }
  242. else {
  243. rThread = thread(threadReadTask, std::ref(rData));
  244. }
  245. rThread.detach();
  246. #endif // USE_CURL
  247. tDc.store(true);
  248. mp3dec_init(&dData.mp3d);
  249. dThread = thread(threadDecodeTask, std::ref(dData));
  250. dThread.detach();
  251. }
  252. if ((dataAudioRingBuffer.size()>64000) && (engineGetSampleRate()<96000)) {
  253. read = true;
  254. }
  255. if ((dataAudioRingBuffer.size()>128000) && (engineGetSampleRate()>=96000)) {
  256. read = true;
  257. }
  258. if (read) {
  259. Frame<2> currentFrame = *dataAudioRingBuffer.startData();
  260. outputs[OUTL_OUTPUT].value = 10*currentFrame.samples[0]*params[GAIN_PARAM].value;
  261. outputs[OUTR_OUTPUT].value = 10*currentFrame.samples[1]*params[GAIN_PARAM].value;
  262. dataAudioRingBuffer.startIncr(1);
  263. }
  264. }
  265. struct ANTNTextField : LedDisplayTextField {
  266. ANTNTextField(ANTN *mod) {
  267. module = mod;
  268. font = Font::load(assetPlugin(plugin, "res/DejaVuSansMono.ttf"));
  269. color = YELLOW_BIDOO;
  270. textOffset = Vec(3, 3);
  271. }
  272. void onTextChange() override;
  273. ANTN *module;
  274. };
  275. void ANTNTextField::onTextChange() {
  276. if (text.size() > 0) {
  277. string tText = text;
  278. tText.erase(std::remove_if(tText.begin(), tText.end(), [](unsigned char x){return std::isspace(x);}), tText.end());
  279. module->url = tText;
  280. }
  281. }
  282. struct ANTNWidget : ModuleWidget {
  283. TextField *textField;
  284. json_t *toJson() override;
  285. void fromJson(json_t *rootJ) override;
  286. ANTNWidget(ANTN *module) : ModuleWidget(module) {
  287. setPanel(SVG::load(assetPlugin(plugin, "res/ANTN.svg")));
  288. addChild(Widget::create<ScrewSilver>(Vec(RACK_GRID_WIDTH, 0)));
  289. addChild(Widget::create<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0)));
  290. addChild(Widget::create<ScrewSilver>(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  291. addChild(Widget::create<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  292. textField = new ANTNTextField(module);
  293. textField->box.pos = Vec(5, 25);
  294. textField->box.size = Vec(125, 100);
  295. textField->multiline = true;
  296. addChild(textField);
  297. addParam(ParamWidget::create<BidooBlueKnob>(Vec(54, 183), module, ANTN::GAIN_PARAM, 0.5f, 3.0f, 1.0f));
  298. addParam(ParamWidget::create<BlueCKD6>(Vec(54, 245), module, ANTN::TRIG_PARAM, 0.0f, 1.0f, 0.0f));
  299. static const float portX0[4] = {34, 67, 101};
  300. addOutput(Port::create<TinyPJ301MPort>(Vec(portX0[1]-17, 334),Port::OUTPUT, module, ANTN::OUTL_OUTPUT));
  301. addOutput(Port::create<TinyPJ301MPort>(Vec(portX0[1]+4, 334),Port::OUTPUT, module, ANTN::OUTR_OUTPUT));
  302. }
  303. };
  304. json_t *ANTNWidget::toJson() {
  305. json_t *rootJ = ModuleWidget::toJson();
  306. // text
  307. json_object_set_new(rootJ, "text", json_string(textField->text.c_str()));
  308. return rootJ;
  309. }
  310. void ANTNWidget::fromJson(json_t *rootJ) {
  311. ModuleWidget::fromJson(rootJ);
  312. // text
  313. json_t *textJ = json_object_get(rootJ, "text");
  314. if (textJ)
  315. textField->text = json_string_value(textJ);
  316. }
  317. } // namespace rack_plugin_Bidoo
  318. using namespace rack_plugin_Bidoo;
  319. RACK_PLUGIN_MODEL_INIT(Bidoo, ANTN) {
  320. Model *modelANTN = Model::create<ANTN, ANTNWidget>("Bidoo", "antN", "antN oscillator", OSCILLATOR_TAG);
  321. return modelANTN;
  322. }