ComputerscareFolyPace.cpp (13363B)
1 #include <string.h> 2 #include "Computerscare.hpp" 3 #include "dtpulse.hpp" 4 5 6 static const int BUFFER_SIZE = 512; 7 8 9 struct FolyPace : Module { 10 enum ParamIds { 11 TIME_PARAM, 12 TRIM, 13 OFFSET, 14 SCRAMBLE, 15 NUM_PARAMS 16 }; 17 enum InputIds { 18 X_INPUT, 19 NUM_INPUTS 20 }; 21 enum OutputIds { 22 NUM_OUTPUTS 23 }; 24 enum LightIds { 25 NUM_LIGHTS 26 }; 27 28 float bufferX[16][BUFFER_SIZE] = {}; 29 int channelsX = 0; 30 int bufferIndex = 0; 31 int frameIndex = 0; 32 float lastScramble = 0; 33 int cnt = 0; 34 int cmap[16]; 35 36 int A = 31; 37 int B = 32; 38 int C = 29; 39 int D = 2; 40 41 bool faceEmitsLight = true; 42 43 FolyPace() { 44 config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS); 45 const float timeBase = (float) BUFFER_SIZE / 6; 46 for (int i = 0; i < 16; i++) { 47 cmap[i] = i; 48 } 49 configParam(TIME_PARAM, 6.f, 16.f, 14.f, "Time", " ms/div", 1 / 2.f, 1000 * timeBase); 50 51 configParam(TRIM, -2.f, 2.f, 0.2f, "Input Trim"); 52 configParam(OFFSET, -5.f, 5.f, 0.f, "Input Offset", " Volts"); 53 configParam(SCRAMBLE, -10.f, 10.f, 0.f, "Scrambling"); 54 55 configInput(X_INPUT, "Main"); 56 57 58 } 59 60 void onReset() override { 61 //std::memset(bufferX, 0, sizeof(bufferX)); 62 } 63 void updateScramble(float v) { 64 for (int i = 0; i < 16; i++) { 65 cmap[i] = (i * A + B + (int)std::floor(v * 1010.1)) % 16; 66 } 67 } 68 void checkScramble() { 69 float xx = params[SCRAMBLE].getValue(); 70 if (lastScramble != xx) { 71 lastScramble = xx; 72 updateScramble(xx); 73 } 74 } 75 76 void process(const ProcessArgs &args) override { 77 // Modes 78 // Compute time 79 float deltaTime = std::pow(2.f, -params[TIME_PARAM].getValue()); 80 81 int frameCount = (int) std::ceil(deltaTime * args.sampleRate); 82 83 // Set channels 84 int channelsX = inputs[X_INPUT].getChannels(); 85 if (channelsX != this->channelsX) { 86 std::memset(bufferX, 0, sizeof(bufferX)); 87 this->channelsX = channelsX; 88 } 89 if (cnt > 4101) { 90 91 checkScramble(); 92 cnt = 0; 93 } 94 cnt++; 95 // Add frame to buffer 96 if (bufferIndex < BUFFER_SIZE) { 97 if (++frameIndex > frameCount) { 98 frameIndex = 0; 99 float trimVal = params[TRIM].getValue(); 100 float offsetVal = params[OFFSET].getValue(); 101 /* 102 103 if (inputs[X_INPUT].isConnected()) { 104 for (int c = 0; c < 16; c++) { 105 bufferX[c][bufferIndex] = inputs[X_INPUT].getVoltage(std::min(cmap[c], this->channelsX)) * trimVal + offsetVal + 99 + (1071 * cmap[c]) % 19; 106 //bufferX[c][bufferIndex]=inputs[X_INPUT].getVoltage(cmap[c]) 107 } 108 } 109 else { 110 for (int c = 0; c < 16; c++) { 111 bufferX[c][bufferIndex] = offsetVal + 99 + (1071 * cmap[c]) % 19; 112 } 113 } 114 */ 115 if (inputs[X_INPUT].isConnected()) { 116 for (int c = 0; c < 16; c++) { 117 bufferX[c][bufferIndex] = inputs[X_INPUT].getVoltage(std::min(cmap[c], this->channelsX)) * trimVal + offsetVal + 99 + (1071 * cmap[c]) % 19; 118 } 119 } 120 else { 121 for (int c = 0; c < 16; c++) { 122 bufferX[c][bufferIndex] = offsetVal + 99 + (1071 * cmap[c]) % 19; 123 } 124 } 125 126 bufferIndex++; 127 } 128 } 129 130 // Don't wait for trigger if still filling buffer 131 if (bufferIndex < BUFFER_SIZE) { 132 return; 133 } 134 135 // Trigger immediately if external but nothing plugged in, or in Lissajous mode 136 if (true) { 137 trigger(); 138 return; 139 } 140 141 frameIndex++; 142 143 144 } 145 146 void trigger() { 147 bufferIndex = 0; 148 frameIndex = 0; 149 } 150 json_t* dataToJson() override { 151 json_t* rootJ = json_object(); 152 153 json_object_set_new(rootJ, "faceEmitsLight", json_boolean(faceEmitsLight)); 154 155 return rootJ; 156 } 157 158 void dataFromJson(json_t* rootJ) override { 159 json_t* faceEmitsLightJ = json_object_get(rootJ, "faceEmitsLight"); 160 if (faceEmitsLightJ) 161 faceEmitsLight = json_boolean_value(faceEmitsLightJ); 162 } 163 }; 164 165 166 struct FolyPaceDisplay : TransparentWidget { 167 FolyPace *module; 168 169 FolyPaceDisplay() { 170 } 171 172 void drawFace(const DrawArgs &args, float A, float B, float C, float D, float E, float F, float G, float H, float I, float J, float K, float L, float M, float N, float O, float P) { 173 174 175 176 float sf = 1 + 0.2 * sin(B - C); //scaleFactor 177 float ox = 67.5 + sf * 20.33 * sin(D - C / 2); 178 float oy = 180 + sf * 30.33 * sin(G - F); 179 180 float h = 0.4 + 0.3 * sin(A / 2) + 0.3 * sin(K / 3); //face hue 181 float s = 0.5 + 0.32 * sin(B / 3 - 33.21 - D / 2); //face saturation 182 float l = 0.5 + 0.35 * sin(C / 2); //face lightness 183 float fx = ox ; //face y 184 float fy = oy; //face x 185 186 187 float frx = sf * (70 + 40 * sin(A - B / 2)); // face x radius 188 float fry = sf * (150 + 80 * sin(F / 2.2)); //face y radius 189 float fr = 0.04 * sin(H - M) + 0.02 * sin(H / 3 + 2.2) + 0.02 * sin(L + P + 8.222); //face rotation 190 NVGcolor faceColor = nvgHSLA(h, s, l, 0xff); 191 192 float epx = ox; 193 float epy = oy - 10 * (2 + sf + sin(I - J / 2)); 194 195 float eyeSpacing = frx / 2 * (1.8 + 0.5 * sin(200 - J)); 196 float erlx = frx / 4 * (1 + 0.4 * sin(G)); 197 float erly = frx / 4 * (1 + 0.4 * sin(H - N + 100)); 198 199 float irisRad = erly * 0.4 * (1.3 + 0.4 * sin(K - D + 1)); 200 float pupilRad = irisRad * 0.4 * (1 + 0.6 * sin(E)); 201 202 float gazeDir = 3.14159 * (1 + sin(B - K)); 203 float gazeStrength = 4 * (1.3 + 0.5 * sin(D - 1) + 0.6 * sin(1 - L / 2)); 204 205 NVGcolor irisColor = nvgHSLA(l, s, h, 0xff); 206 NVGcolor pupilColor = nvgHSLA(0.1, 0.1, 0.1, 0xff); 207 208 //nvgSave(args.vg); 209 210 Rect b = Rect(Vec(0, 0), box.size); 211 212 nvgFillColor(args.vg, faceColor); 213 nvgScissor(args.vg, b.pos.x, b.pos.y, b.size.x, b.size.y); 214 215 nvgRotate(args.vg, fr); 216 217 drawHead(args, fx, fy, frx, fry, faceColor); 218 219 220 float leftEyebrowHeight = erly * (1.9 + 0.6 * sin(G) + 0.3 * sin(K - B / 2)); 221 float rightEyebrowHeight = erly * (1.9 + 0.6 * sin(G - 2.2 + N) + 0.2 * sin(L + 33)); 222 float leftEyebrowAngle = 0.5 * sin(C) + 0.2 * sin(H / 2 - 2); 223 float rightEyebrowAngle = 0.7 * sin(F) + 0.3 * sin(2 - I); 224 NVGcolor eyebrowColor = nvgHSLA(0.1, 0.2, 0.2, 0xff); 225 float eyebrowThickness = 5.f * (1.3 + sin(M - 2)); 226 float eyebrowLength = frx * 0.3 * (2.2 + sin(G) + 0.4 * sin(B - 2)); 227 228 drawEyes(args, epx, epy, eyeSpacing, erlx, erly, 1, irisRad, pupilRad, gazeDir, gazeStrength, irisColor, pupilColor); 229 drawEyebrows(args, epx, epy, eyeSpacing, leftEyebrowHeight, rightEyebrowHeight, leftEyebrowAngle, rightEyebrowAngle, eyebrowColor, eyebrowThickness, eyebrowLength); 230 231 232 float mouthX = ox; 233 float mouthY = oy + 0.4 * fry * (1 + 0.4 * sin(C / 2)); 234 float mouthWidth = frx * 0.6 * (1.2 + 0.6 * sin(C)); 235 float mouthOpen = fry * 0.06 * (1 + sin(O) - sin(A * 2 + 44)); 236 float mouthSmile = sin(D) * 2.0; 237 float mouthSkew = sin(L) - sin(H); 238 float mouthThickness = 5.4 * (sin(H) - sin(M / 2)); 239 NVGcolor mouthLipColor = nvgHSLA(0.1 * sin(N) - 0.1, 0.6 + 0.3 * sin(M), 0.5 + .4 * sin(I), 0xff); 240 241 nvgGlobalCompositeOperation(args.vg, NVG_ATOP); 242 243 244 drawMouth(args, mouthX, mouthY, mouthWidth, mouthOpen, mouthSmile, mouthSkew, mouthThickness, mouthLipColor); 245 246 247 248 nvgResetScissor(args.vg); 249 //nvgRestore(args.vg); 250 } 251 252 void drawEyebrows(const DrawArgs &args, float x, float y, float eyeSpacing, float leftEyebrowHeight, float rightEyebrowHeight, float leftEyebrowAngle, float rightEyebrowAngle, NVGcolor eyebrowColor, float eyebrowThickness, float eyebrowLength) { 253 nvgBeginPath(args.vg); 254 nvgStrokeColor(args.vg, eyebrowColor); 255 nvgStrokeWidth(args.vg, eyebrowThickness); 256 float cosLeft = cos(leftEyebrowAngle); 257 float sinLeft = sin(leftEyebrowAngle); 258 float cosRight = cos(rightEyebrowAngle); 259 float sinRight = sin(rightEyebrowAngle); 260 261 float r = eyebrowLength / 2; 262 263 nvgMoveTo(args.vg, x - eyeSpacing / 2 - r * cosLeft, y - leftEyebrowHeight - r * sinLeft); 264 nvgLineTo(args.vg, x - eyeSpacing / 2 + r * cosLeft, y - leftEyebrowHeight + r * sinLeft); 265 //nvgStroke(args.vg); 266 267 nvgMoveTo(args.vg, x + eyeSpacing / 2 - r * cosRight, y - rightEyebrowHeight - r * sinRight); 268 nvgLineTo(args.vg, x + eyeSpacing / 2 + r * cosRight, y - rightEyebrowHeight + r * sinRight); 269 nvgStroke(args.vg); 270 271 nvgClosePath(args.vg); 272 } 273 void drawHead(const DrawArgs &args, float x, float y, float width, float height, NVGcolor color) { 274 nvgBeginPath(args.vg); 275 276 277 nvgLineCap(args.vg, NVG_ROUND); 278 nvgMiterLimit(args.vg, 2.f); 279 nvgStrokeWidth(args.vg, 4.5f); 280 281 nvgEllipse(args.vg, x, y, width, height); 282 nvgClosePath(args.vg); 283 nvgGlobalCompositeOperation(args.vg, NVG_SOURCE_OVER); 284 nvgFill(args.vg); 285 } 286 void drawMouth(const DrawArgs &args, float x, float y, float width, float open, float smile, float skew, float thickness, NVGcolor lipColor) { 287 nvgBeginPath(args.vg); 288 nvgStrokeWidth(args.vg, thickness); 289 nvgStrokeColor(args.vg, lipColor); 290 //nvgStrokeWidth(args.vg, 4.5f); 291 292 nvgMoveTo(args.vg, x - width / 2, y - 20.f * smile); 293 294 //top 295 nvgBezierTo(args.vg, x - width / 4, y - open * smile, x + width / 4, y - open * smile, x + width / 2, y - 10.f * smile); 296 297 //bottom 298 nvgBezierTo(args.vg, x + width / 4, y + smile * open, x - width / 4, y + smile * open, x - width / 2, y - 20.f * smile); 299 nvgClosePath(args.vg); 300 nvgGlobalCompositeOperation(args.vg, NVG_ATOP); 301 nvgFillColor(args.vg, nvgRGBA(0, 0, 0, 0xff)); 302 nvgStroke(args.vg); 303 nvgFill(args.vg); 304 305 } 306 void drawEyes(const DrawArgs &args, float x, float y, float spacing, float rx, float ry, float open, float irisRad, float pupilRad, float gazeDir, float gazeStrength, NVGcolor irisColor, NVGcolor pupilColor) { 307 float leftX = x - spacing / 2; 308 float rightX = x + spacing / 2; 309 float pupilOffsetX = gazeStrength * cos(gazeDir); 310 float pupilOffsetY = gazeStrength * sin(gazeDir); 311 float leftPupilX = leftX + pupilOffsetX; 312 float leftPupilY = y + pupilOffsetY; 313 float rightPupilX = rightX + pupilOffsetX; 314 float rightPupilY = y + pupilOffsetY; 315 316 nvgBeginPath(args.vg); 317 nvgStrokeWidth(args.vg, 1.f); 318 //nvgStrokeWidth(args.vg, 4.5f); 319 320 //whites of eyes 321 nvgFillColor(args.vg, nvgRGB(250, 250, 250)); 322 323 324 nvgEllipse(args.vg, leftX, y, rx, ry); 325 nvgEllipse(args.vg, rightX, y, rx, ry); 326 327 nvgGlobalCompositeOperation(args.vg, NVG_SOURCE_OVER); 328 nvgFill(args.vg); 329 nvgClosePath(args.vg); 330 331 //iris 332 333 nvgBeginPath(args.vg); 334 nvgFillColor(args.vg, irisColor); 335 336 337 nvgCircle(args.vg, leftPupilX, leftPupilY, irisRad); 338 nvgCircle(args.vg, rightPupilX, rightPupilY, irisRad); 339 nvgGlobalCompositeOperation(args.vg, NVG_ATOP); 340 nvgFill(args.vg); 341 nvgClosePath(args.vg); 342 343 //pupil 344 nvgBeginPath(args.vg); 345 nvgFillColor(args.vg, pupilColor); 346 347 348 nvgCircle(args.vg, leftPupilX, leftPupilY, pupilRad); 349 nvgCircle(args.vg, rightPupilX, rightPupilY, pupilRad); 350 nvgGlobalCompositeOperation(args.vg, NVG_ATOP); 351 nvgFill(args.vg); 352 nvgClosePath(args.vg); 353 354 nvgGlobalCompositeOperation(args.vg, NVG_SOURCE_OVER); 355 //eyebrows 356 357 } 358 void drawNose(const DrawArgs &args, float x, float y, float height, float width, float thickness, NVGcolor color) { 359 360 } 361 362 363 void draw(const DrawArgs &args) override { 364 if (!module) { 365 drawFace(args, random::uniform() * 10, random::uniform() * 10, random::uniform() * 10, random::uniform() * 10, random::uniform() * 10, random::uniform() * 10, random::uniform() * 10, random::uniform() * 10, random::uniform() * 10, random::uniform() * 10, random::uniform() * 10, random::uniform() * 10, random::uniform() * 10, random::uniform() * 10, random::uniform() * 10, random::uniform() * 10); 366 } 367 else { 368 if (!module->faceEmitsLight) { 369 drawFace(args, module->bufferX[0][0], module->bufferX[1][0], module->bufferX[2][0], module->bufferX[3][0], module->bufferX[4][0], module->bufferX[5][0], module->bufferX[6][0], module->bufferX[7][0], module->bufferX[8][0], module->bufferX[9][0], module->bufferX[10][0], module->bufferX[11][0], module->bufferX[12][0], module->bufferX[13][0], module->bufferX[14][0], module->bufferX[15][0]); 370 } 371 } 372 } 373 void drawLayer(const BGPanel::DrawArgs& args, int layer) override { 374 if (layer == 1 && module) { 375 if (module->faceEmitsLight) { 376 drawFace(args, module->bufferX[0][0], module->bufferX[1][0], module->bufferX[2][0], module->bufferX[3][0], module->bufferX[4][0], module->bufferX[5][0], module->bufferX[6][0], module->bufferX[7][0], module->bufferX[8][0], module->bufferX[9][0], module->bufferX[10][0], module->bufferX[11][0], module->bufferX[12][0], module->bufferX[13][0], module->bufferX[14][0], module->bufferX[15][0]); 377 } 378 } 379 Widget::drawLayer(args, layer); 380 } 381 }; 382 383 384 struct FolyPaceWidget : ModuleWidget { 385 FolyPaceWidget(FolyPace *module) { 386 setModule(module); 387 388 box.size = Vec(9 * 15, 380); 389 { 390 ComputerscareSVGPanel *panel = new ComputerscareSVGPanel(); 391 panel->box.size = box.size; 392 panel->setBackground(APP->window->loadSvg(asset::plugin(pluginInstance, "res/ComputerscareFolyPacePanel.svg"))); 393 addChild(panel); 394 395 } 396 397 { 398 FolyPaceDisplay *display = new FolyPaceDisplay(); 399 display->module = module; 400 display->box.pos = Vec(0, 0); 401 display->box.size = Vec(box.size.x, box.size.y); 402 addChild(display); 403 } 404 405 addInput(createInput<PointingUpPentagonPort>(Vec(1, 353), module, FolyPace::X_INPUT)); 406 addParam(createParam<SmallKnob>(Vec(31, 357), module, FolyPace::TRIM)); 407 addParam(createParam<SmoothKnob>(Vec(51, 353), module, FolyPace::OFFSET)); 408 addParam(createParam<ScrambleKnob>(Vec(81, 357), module, FolyPace::SCRAMBLE)); 409 } 410 411 void appendContextMenu(Menu* menu) override { 412 FolyPace* module = dynamic_cast<FolyPace*>(this->module); 413 414 menu->addChild(new MenuSeparator); 415 416 menu->addChild(createBoolPtrMenuItem("Face Emits Light", "", &module->faceEmitsLight)); 417 } 418 }; 419 420 421 Model *modelComputerscareFolyPace = createModel<FolyPace, FolyPaceWidget>("computerscare-foly-pace");