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.

430 lines
11KB

  1. #pragma once
  2. #include <rack.hpp>
  3. #include <osdialog.h>
  4. #include "dr_wav.h"
  5. #include <thread>
  6. static const char WAVETABLE_LOAD_FILTERS[] = "WAV:wav,WAV,aif,AIF,aiff,AIFF,f32,s8,i8,s16,i16,s24,i24,s32,i32";
  7. static const char WAVETABLE_SAVE_FILTERS[] = "WAV:wav,WAV";
  8. static std::string wavetableDir;
  9. /** Loads and stores wavetable samples and metadata */
  10. struct Wavetable {
  11. /** All waves concatenated
  12. (waveCount, waveLen)
  13. */
  14. std::vector<float> samples;
  15. /** Number of points in each wave */
  16. size_t waveLen = 0;
  17. /** Name of loaded wavetable. */
  18. std::string filename;
  19. // Interpolated wavetables
  20. /** Upsampling factor. No upsampling if 0. */
  21. size_t quality = 0;
  22. /** Number of filtered wavetables. Automatically computed from waveLen. */
  23. size_t octaves = 0;
  24. /** Waves bandlimited at each octave
  25. (octave, waveCount, waveLen * quality)
  26. */
  27. std::vector<float> interpolatedSamples;
  28. bool loading = false;
  29. Wavetable() {}
  30. float &at(size_t waveIndex, size_t sampleIndex) {
  31. return samples[waveLen * waveIndex + sampleIndex];
  32. }
  33. float at(size_t waveIndex, size_t sampleIndex) const {
  34. return samples[waveLen * waveIndex + sampleIndex];
  35. }
  36. float interpolatedAt(size_t octave, size_t waveIndex, size_t sampleIndex) const {
  37. return interpolatedSamples[samples.size() * quality * octave + waveLen * quality * waveIndex + sampleIndex];
  38. }
  39. void reset() {
  40. filename = "Basic.wav";
  41. waveLen = 1024;
  42. loading = true;
  43. DEFER({loading = false;});
  44. // HACK Sleep 100us so DSP thread is likely to finish processing before we resize the vector
  45. std::this_thread::sleep_for(std::chrono::duration<double>(100e-6));
  46. samples.resize(waveLen * 4);
  47. // Sine
  48. for (size_t i = 0; i < waveLen; i++) {
  49. float p = float(i) / waveLen;
  50. at(0, i) = std::sin(2 * float(M_PI) * p);
  51. }
  52. // Triangle
  53. for (size_t i = 0; i < waveLen; i++) {
  54. float p = float(i) / waveLen;
  55. at(1, i) = (p < 0.25f) ? 4*p : (p < 0.75f) ? 2 - 4*p : 4*p - 4;
  56. }
  57. // Sawtooth
  58. for (size_t i = 0; i < waveLen; i++) {
  59. float p = float(i) / waveLen;
  60. at(2, i) = (p < 0.5f) ? 2*p : 2*p - 2;
  61. }
  62. // Square
  63. for (size_t i = 0; i < waveLen; i++) {
  64. float p = float(i) / waveLen;
  65. at(3, i) = (p < 0.5f) ? 1 : -1;
  66. }
  67. interpolate();
  68. }
  69. void setQuality(size_t quality) {
  70. if (quality == this->quality)
  71. return;
  72. this->quality = quality;
  73. interpolate();
  74. }
  75. void setWaveLen(size_t waveLen) {
  76. if (waveLen == this->waveLen)
  77. return;
  78. this->waveLen = waveLen;
  79. interpolate();
  80. }
  81. /** Returns the number of waves in the wavetable. */
  82. size_t getWaveCount() const {
  83. if (waveLen == 0)
  84. return 0;
  85. return samples.size() / waveLen;
  86. }
  87. void interpolate() {
  88. if (quality == 0)
  89. return;
  90. // pffft only supports >=32 points
  91. if (waveLen < 32)
  92. return;
  93. // pffft only supports multiples of 32 points
  94. if ((waveLen % 32) != 0)
  95. return;
  96. size_t waveCount = getWaveCount();
  97. if (waveCount == 0)
  98. return;
  99. octaves = math::log2(waveLen) - 1;
  100. interpolatedSamples.clear();
  101. interpolatedSamples.resize(octaves * samples.size() * quality);
  102. float* in = new float[waveLen];
  103. float* inF = new float[2 * waveLen];
  104. dsp::RealFFT inFFT(waveLen);
  105. float* outF = new float[2 * waveLen * quality]();
  106. dsp::RealFFT outFFT(waveLen * quality);
  107. for (size_t i = 0; i < waveCount; i++) {
  108. // Compute FFT of wave
  109. for (size_t j = 0; j < waveLen; j++) {
  110. in[j] = samples[waveLen * i + j] / waveLen;
  111. }
  112. inFFT.rfft(in, inF);
  113. // Compute FFT-filtered versions of each wave
  114. for (size_t octave = 0; octave < octaves; octave++) {
  115. size_t bins = 1 << octave;
  116. // Only overwrite the first waveLen bins
  117. for (size_t j = 0; j < waveLen; j++) {
  118. outF[2 * j + 0] = (j <= bins) ? inF[2 * j + 0] : 0.f;
  119. outF[2 * j + 1] = (j <= bins) ? inF[2 * j + 1] : 0.f;
  120. }
  121. outFFT.irfft(outF, &interpolatedSamples[samples.size() * quality * octave + waveLen * quality * i]);
  122. }
  123. }
  124. delete[] in;
  125. delete[] inF;
  126. delete[] outF;
  127. }
  128. json_t* toJson() const {
  129. json_t* rootJ = json_object();
  130. // waveLen
  131. json_object_set_new(rootJ, "waveLen", json_integer(waveLen));
  132. // filename
  133. json_object_set_new(rootJ, "filename", json_string(filename.c_str()));
  134. return rootJ;
  135. }
  136. void fromJson(json_t* rootJ) {
  137. // waveLen
  138. json_t* waveLenJ = json_object_get(rootJ, "waveLen");
  139. if (waveLenJ)
  140. setWaveLen(json_integer_value(waveLenJ));
  141. // filename
  142. json_t* filenameJ = json_object_get(rootJ, "filename");
  143. if (filenameJ)
  144. filename = json_string_value(filenameJ);
  145. }
  146. void load(std::string path) {
  147. loading = true;
  148. DEFER({loading = false;});
  149. // HACK Sleep 100us so DSP thread is likely to finish processing before we resize the vector
  150. std::this_thread::sleep_for(std::chrono::duration<double>(100e-6));
  151. std::string ext = string::lowercase(system::getExtension(path));
  152. if (ext == ".wav") {
  153. // Load WAV
  154. drwav wav;
  155. #if defined ARCH_WIN
  156. if (!drwav_init_file_w(&wav, string::UTF8toUTF16(path).c_str(), NULL))
  157. #else
  158. if (!drwav_init_file(&wav, path.c_str(), NULL))
  159. #endif
  160. return;
  161. size_t len = wav.totalPCMFrameCount * wav.channels;
  162. if (len == 0 || len >= (1 << 20))
  163. return;
  164. samples.clear();
  165. samples.resize(len);
  166. // If sample rate is a power of 2, set waveLen to it.
  167. if ((wav.sampleRate & (wav.sampleRate - 1)) == 0)
  168. waveLen = wav.sampleRate;
  169. drwav_read_pcm_frames_f32(&wav, wav.totalPCMFrameCount, samples.data());
  170. drwav_uninit(&wav);
  171. }
  172. else {
  173. // Load bytes from file
  174. std::vector<uint8_t> data = system::readFile(path);
  175. samples.clear();
  176. if (ext == ".f32") {
  177. size_t len = data.size() / sizeof(float);
  178. samples.resize(len);
  179. // This is the same as memcpy but consistent with the other conversions.
  180. dsp::convert((const float*) data.data(), samples.data(), len);
  181. }
  182. else if (ext == ".s8" || ext == ".i8") {
  183. size_t len = data.size() / sizeof(int8_t);
  184. samples.resize(len);
  185. dsp::convert((const int8_t*) data.data(), samples.data(), len);
  186. }
  187. else if (ext == ".s16" || ext == ".i16") {
  188. size_t len = data.size() / sizeof(int16_t);
  189. samples.resize(len);
  190. dsp::convert((const int16_t*) data.data(), samples.data(), len);
  191. }
  192. else if (ext == ".s24" || ext == ".i24") {
  193. size_t len = data.size() / sizeof(dsp::Int24);
  194. samples.resize(len);
  195. dsp::convert((const dsp::Int24*) data.data(), samples.data(), len);
  196. }
  197. else if (ext == ".s32" || ext == ".i32") {
  198. size_t len = data.size() / sizeof(int32_t);
  199. samples.resize(len);
  200. dsp::convert((const int32_t*) data.data(), samples.data(), len);
  201. }
  202. else {
  203. return;
  204. }
  205. }
  206. interpolate();
  207. }
  208. void loadDialog() {
  209. osdialog_filters* filters = osdialog_filters_parse(WAVETABLE_LOAD_FILTERS);
  210. DEFER({osdialog_filters_free(filters);});
  211. char* pathC = osdialog_file(OSDIALOG_OPEN, wavetableDir.empty() ? NULL : wavetableDir.c_str(), NULL, filters);
  212. if (!pathC) {
  213. // Fail silently
  214. return;
  215. }
  216. std::string path = pathC;
  217. std::free(pathC);
  218. wavetableDir = system::getDirectory(path);
  219. load(path);
  220. filename = system::getFilename(path);
  221. }
  222. void save(std::string path) const {
  223. if (samples.size() == 0)
  224. return;
  225. drwav_data_format format;
  226. format.container = drwav_container_riff;
  227. format.format = DR_WAVE_FORMAT_PCM;
  228. format.channels = 1;
  229. format.sampleRate = waveLen;
  230. format.bitsPerSample = 16;
  231. drwav wav;
  232. if (!drwav_init_file_write(&wav, path.c_str(), &format, NULL))
  233. return;
  234. int16_t* buf = new int16_t[samples.size()];
  235. drwav_f32_to_s16(buf, samples.data(), samples.size());
  236. drwav_write_pcm_frames(&wav, samples.size(), buf);
  237. delete[] buf;
  238. drwav_uninit(&wav);
  239. }
  240. void saveDialog() const {
  241. osdialog_filters* filters = osdialog_filters_parse(WAVETABLE_SAVE_FILTERS);
  242. DEFER({osdialog_filters_free(filters);});
  243. char* pathC = osdialog_file(OSDIALOG_SAVE, wavetableDir.empty() ? NULL : wavetableDir.c_str(), filename.c_str(), filters);
  244. if (!pathC) {
  245. // Cancel silently
  246. return;
  247. }
  248. DEFER({std::free(pathC);});
  249. // Automatically append .wav extension
  250. std::string path = pathC;
  251. if (system::getExtension(path) != ".wav") {
  252. path += ".wav";
  253. }
  254. wavetableDir = system::getDirectory(path);
  255. save(path);
  256. }
  257. void appendContextMenu(Menu* menu) {
  258. menu->addChild(createMenuItem("Initialize wavetable", "",
  259. [=]() {reset();}
  260. ));
  261. menu->addChild(createMenuItem("Load wavetable", "",
  262. [=]() {loadDialog();}
  263. ));
  264. menu->addChild(createMenuItem("Save wavetable", "",
  265. [=]() {saveDialog();}
  266. ));
  267. int sizeOffset = 5;
  268. std::vector<std::string> sizeLabels;
  269. for (int i = sizeOffset; i <= 14; i++) {
  270. sizeLabels.push_back(string::f("%d", 1 << i));
  271. }
  272. menu->addChild(createIndexSubmenuItem("Wave points", sizeLabels,
  273. [=]() {return math::log2(waveLen) - sizeOffset;},
  274. [=](int i) {waveLen = 1 << (i + sizeOffset);}
  275. ));
  276. }
  277. };
  278. static Wavetable defaultWavetable;
  279. template <class TModule>
  280. struct WTDisplay : LedDisplay {
  281. TModule* module;
  282. void drawLayer(const DrawArgs& args, int layer) override {
  283. nvgScissor(args.vg, RECT_ARGS(args.clipBox));
  284. if (layer == 1) {
  285. if (defaultWavetable.samples.empty())
  286. defaultWavetable.reset();
  287. // Get module data or defaults
  288. const Wavetable& wavetable = module ? module->wavetable : defaultWavetable;
  289. float lastPos = module ? module->lastPos : 0.f;
  290. // Draw filename text
  291. std::string fontPath = asset::system("res/fonts/ShareTechMono-Regular.ttf");
  292. std::shared_ptr<Font> font = APP->window->loadFont(fontPath);
  293. if (!font)
  294. return;
  295. nvgFontSize(args.vg, 13);
  296. nvgFontFaceId(args.vg, font->handle);
  297. nvgFillColor(args.vg, SCHEME_YELLOW);
  298. nvgText(args.vg, 4.0, 13.0, wavetable.filename.c_str(), NULL);
  299. // Get wavetable metadata
  300. if (wavetable.waveLen < 2)
  301. return;
  302. size_t waveCount = wavetable.getWaveCount();
  303. if (waveCount < 1)
  304. return;
  305. if (lastPos > waveCount - 1)
  306. return;
  307. float posF = lastPos - std::trunc(lastPos);
  308. size_t pos0 = std::trunc(lastPos);
  309. // Draw scope
  310. nvgScissor(args.vg, RECT_ARGS(args.clipBox));
  311. nvgBeginPath(args.vg);
  312. Vec scopePos = Vec(0.0, 13.0);
  313. Rect scopeRect = Rect(scopePos, box.size - scopePos);
  314. scopeRect = scopeRect.shrink(Vec(4, 5));
  315. size_t iSkip = wavetable.waveLen / 128 + 1;
  316. for (size_t i = 0; i <= wavetable.waveLen; i += iSkip) {
  317. // Get wave value
  318. float wave;
  319. float wave0 = wavetable.at(pos0, i % wavetable.waveLen);
  320. if (posF > 0.f) {
  321. float wave1 = wavetable.at(pos0 + 1, i % wavetable.waveLen);
  322. wave = crossfade(wave0, wave1, posF);
  323. }
  324. else {
  325. wave = wave0;
  326. }
  327. // Add point to line
  328. Vec p;
  329. p.x = float(i) / wavetable.waveLen;
  330. p.y = 0.5f - 0.5f * wave;
  331. p = scopeRect.pos + scopeRect.size * p;
  332. if (i == 0)
  333. nvgMoveTo(args.vg, VEC_ARGS(p));
  334. else
  335. nvgLineTo(args.vg, VEC_ARGS(p));
  336. }
  337. nvgLineCap(args.vg, NVG_ROUND);
  338. nvgMiterLimit(args.vg, 2.f);
  339. nvgStrokeWidth(args.vg, 1.5f);
  340. nvgStrokeColor(args.vg, SCHEME_YELLOW);
  341. nvgStroke(args.vg);
  342. }
  343. nvgResetScissor(args.vg);
  344. LedDisplay::drawLayer(args, layer);
  345. }
  346. // void onButton(const ButtonEvent& e) override {
  347. // if (e.action == GLFW_PRESS && e.button == GLFW_MOUSE_BUTTON_LEFT) {
  348. // if (module)
  349. // module->loadWavetableDialog();
  350. // e.consume(this);
  351. // }
  352. // LedDisplay::onButton(e);
  353. // }
  354. void onPathDrop(const PathDropEvent& e) override {
  355. if (!module)
  356. return;
  357. if (e.paths.empty())
  358. return;
  359. std::string path = e.paths[0];
  360. if (system::getExtension(path) != ".wav")
  361. return;
  362. module->wavetable.load(path);
  363. module->wavetable.filename = system::getFilename(path);
  364. e.consume(this);
  365. }
  366. };