#include "Reftone.hpp" void Reftone::step() { // C4 -- the pitch.hpp reference frequency -- in knob values: const int referencePitch = 0; const int referenceOctave = 4; if (!( _pitch == params[PITCH_PARAM].value && _octave == params[OCTAVE_PARAM].value && _fine == params[FINE_PARAM].value )) { _pitch = params[PITCH_PARAM].value; _octave = params[OCTAVE_PARAM].value; _fine = params[FINE_PARAM].value; _frequency = semitoneToFrequency(referenceSemitone + 12*(_octave - referenceOctave) + (_pitch - referencePitch) + _fine); _cv = frequencyToCV(_frequency); } if (outputs[CV_OUTPUT].active) { outputs[CV_OUTPUT].value = _cv; } else { outputs[CV_OUTPUT].value = 0.0; } if (outputs[OUT_OUTPUT].active) { _sine.setFrequency(_frequency); outputs[OUT_OUTPUT].value = _sine.next() * 5.0f; } else { outputs[OUT_OUTPUT].value = 0.0; } } struct ReftoneDisplay : TransparentWidget { const NVGcolor _textColor = nvgRGBA(0x00, 0xff, 0x00, 0xee); Reftone* _module; const Vec _size; std::shared_ptr _font; ReftoneDisplay( Reftone* module, Vec size ) : _module(module) , _size(size) , _font(Font::load(assetPlugin(plugin, "res/fonts/inconsolata-bold.ttf"))) { } void draw(NVGcontext* vg) override; void drawBackground(NVGcontext* vg); void drawText(NVGcontext* vg, const char* s, float x, float y, int size); void drawCenteredText(NVGcontext* vg, const char* s, float y, int size); float textRenderWidth(NVGcontext* vg, const char* s, int size); }; void ReftoneDisplay::draw(NVGcontext* vg) { const int n = 20; char octave[n]; snprintf(octave, n, "%d", _module->_octave); char fine[n]; fine[0] = _module->_fine < 0.0 ? '-' : '+'; snprintf(fine + 1, n - 1, "%02d", abs((int)(_module->_fine * 100))); char frequency[20]; snprintf(frequency, n, _module->_frequency >= 1000.0 ? "%0.0f" : "%0.1f", _module->_frequency); const char* pitch = NULL; const char* sharpFlat = NULL; switch (_module->_pitch) { case 0: { pitch = "C"; break; } case 1: { pitch = "C"; sharpFlat = "#"; break; } case 2: { pitch = "D"; break; } case 3: { pitch = "E"; sharpFlat = "b"; break; } case 4: { pitch = "E"; break; } case 5: { pitch = "F"; break; } case 6: { pitch = "F"; sharpFlat = "#"; break; } case 7: { pitch = "G"; break; } case 8: { pitch = "G"; sharpFlat = "#"; break; } case 9: { pitch = "A"; break; } case 10: { pitch = "B"; sharpFlat = "b"; break; } case 11: { pitch = "B"; break; } } drawBackground(vg); if (sharpFlat) { drawText(vg, pitch, 3, 20, 28); drawText(vg, sharpFlat, 16, 12, 16); drawText(vg, octave, 22, 20, 28); } else { char s[n]; snprintf(s, n, "%s%s", pitch, octave); drawCenteredText(vg, s, 20, 28); } drawCenteredText(vg, fine, 32.5, 14); drawCenteredText(vg, frequency, 45, 14); } void ReftoneDisplay::drawBackground(NVGcontext* vg) { nvgSave(vg); nvgBeginPath(vg); nvgRect(vg, 0, 0, _size.x, _size.y); nvgFillColor(vg, nvgRGBA(0x00, 0x00, 0x00, 0xff)); nvgFill(vg); nvgRestore(vg); } void ReftoneDisplay::drawText(NVGcontext* vg, const char* s, float x, float y, int size) { nvgSave(vg); nvgTranslate(vg, x, y); nvgFontSize(vg, size); nvgFontFaceId(vg, _font->handle); nvgFillColor(vg, _textColor); nvgText(vg, 0, 0, s, NULL); nvgRestore(vg); } void ReftoneDisplay::drawCenteredText(NVGcontext* vg, const char* s, float y, int size) { float x = textRenderWidth(vg, s, size); x = std::max(0.0f, _size.x - x); x /= 2.0; drawText(vg, s, x, y, size); } float ReftoneDisplay::textRenderWidth(NVGcontext* vg, const char* s, int size) { // nvgSave(vg); // nvgFontSize(vg, size); // float w = nvgTextBounds(vg, 0, 0, s, NULL, NULL); // nvgRestore(vg); // return w - size/4.0; return strlen(s) * (size / 2.1); } struct ReftoneWidget : ModuleWidget { static constexpr int hp = 3; ReftoneWidget(Reftone* module) : ModuleWidget(module) { box.size = Vec(RACK_GRID_WIDTH * hp, RACK_GRID_HEIGHT); { SVGPanel *panel = new SVGPanel(); panel->box.size = box.size; panel->setBackground(SVG::load(assetPlugin(plugin, "res/Reftone.svg"))); addChild(panel); } { auto inset = Vec(3.5, 18); auto size = Vec(38, 48); auto display = new ReftoneDisplay(module, size); display->box.pos = inset; display->box.size = size; addChild(display); } addChild(Widget::create(Vec(0, 0))); addChild(Widget::create(Vec(box.size.x - 15, 365))); // generated by svg_widgets.rb auto pitchParamPosition = Vec(9.5, 89.5); auto octaveParamPosition = Vec(9.5, 143.5); auto fineParamPosition = Vec(9.5, 197.5); auto cvOutputPosition = Vec(10.5, 239.0); auto outOutputPosition = Vec(10.5, 274.0); // end generated by svg_widgets.rb { auto w = ParamWidget::create(pitchParamPosition, module, Reftone::PITCH_PARAM, 0.0, 11.0, 9.0); dynamic_cast(w)->snap = true; addParam(w); } { auto w = ParamWidget::create(octaveParamPosition, module, Reftone::OCTAVE_PARAM, 1.0, 8.0, 4.0); dynamic_cast(w)->snap = true; addParam(w); } addParam(ParamWidget::create(fineParamPosition, module, Reftone::FINE_PARAM, -0.99, 0.99, 0.0)); addOutput(Port::create(cvOutputPosition, Port::OUTPUT, module, Reftone::CV_OUTPUT)); addOutput(Port::create(outOutputPosition, Port::OUTPUT, module, Reftone::OUT_OUTPUT)); } }; RACK_PLUGIN_MODEL_INIT(Bogaudio, Reftone) { Model *modelReftone = createModel("Bogaudio-Reftone", "Reftone", "precision pitch CV generator", TUNER_TAG); return modelReftone; }