From f6b4b7eddae15e6be75b6ad5e9fbdddd5b78e5d4 Mon Sep 17 00:00:00 2001 From: Nils Clark-Bernhard Date: Fri, 17 Jun 2022 21:39:23 +0200 Subject: [PATCH] Misc pull requests from Chysn and Logarhythm1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adding BigScope App – Changes from pull request https://github.com/Chysn/O_C-HemisphereSuite/pull/76 Changes from pull request https://github.com/Chysn/O_C-HemisphereSuite/pull/51 Changes from pull request https://github.com/Chysn/O_C-HemisphereSuite/pull/105 Changes from pull request https://github.com/Logarhythm1/O_C-HemisphereSuite/pull/2 --- software/o_c_REV/APP_BIGSCOPE.ino | 225 ++++++++++++++++++++++++++++ software/o_c_REV/APP_HEMISPHERE.ino | 2 + software/o_c_REV/HEM_ADSREG.ino | 8 + software/o_c_REV/HEM_Carpeggio.ino | 41 ++++- software/o_c_REV/HemisphereApplet.h | 9 ++ software/o_c_REV/OC_apps.ino | 3 + software/o_c_REV/OC_options.h | 3 + 7 files changed, 288 insertions(+), 3 deletions(-) create mode 100644 software/o_c_REV/APP_BIGSCOPE.ino diff --git a/software/o_c_REV/APP_BIGSCOPE.ino b/software/o_c_REV/APP_BIGSCOPE.ino new file mode 100644 index 000000000..08d77659e --- /dev/null +++ b/software/o_c_REV/APP_BIGSCOPE.ino @@ -0,0 +1,225 @@ +#include "HSApplication.h" +#include "HSMIDI.h" + +#define HEMISPHERE_MAX_CV 7680 +#define HEMISPHERE_CENTER_CV 0 +#define SCOPE_WIDTH 128 + +class BigScope : public HSApplication, public SystemExclusiveHandler { +public: + void Start() { + last_bpm_tick = OC::CORE::ticks; + bpm = 0; + sample_ticks = 320; + freeze = 0; + last_scope_tick = 0; + } + + void Resume() { + } + + void Controller() { + if (Clock(0)) { + int this_tick = OC::CORE::ticks; + int time = this_tick - last_bpm_tick; + last_bpm_tick = this_tick; + bpm = 1000000 / time; + if (bpm > 9999) bpm = 9999; + + if (last_scope_tick) { + int cycle_ticks = OC::CORE::ticks - last_scope_tick; + sample_ticks = cycle_ticks / 64; + sample_ticks = constrain(sample_ticks, 2, 64000); + } + last_scope_tick = OC::CORE::ticks; + } + + if (!freeze) { + last_cv = In(0); + + if (--sample_countdown < 1) { + sample_countdown = sample_ticks; + if (++sample_num > 127) sample_num = 0; + int sample = Proportion(In(0), HEMISPHERE_MAX_CV, 128); + sample = constrain(sample, -128, 127) + 127; + snapshot[sample_num] = (uint8_t) sample; + } + + Out(0, In(0)); + } + } + + void View() { + gfxHeader("Scope"); + //gfxPrint(1, 2, "Scope"); + //DrawTicks(); + DrawVoltage(); + DrawBPM(); + DrawInput1(); + if (freeze) { + gfxInvert(0, 24, 128, 40); + } + } + + void OnSendSysEx() { + } + + void OnReceiveSysEx() { + } + + ///////////////////////////////////////////////////////////////// + // Control handlers + ///////////////////////////////////////////////////////////////// + void OnLeftButtonPress() { + freeze = 1 - freeze; + } + + void OnLeftButtonLongPress() { + + } + + void OnRightButtonPress() { + } + + void OnUpButtonPress() { + } + + void OnDownButtonPress() { + } + + void OnDownButtonLongPress() { + } + + void OnLeftEncoderMove(int direction) { + if (sample_ticks < 32) sample_ticks += direction; + else sample_ticks += direction * 10; + sample_ticks = constrain(sample_ticks, 2, 64000); + last_encoder_move = OC::CORE::ticks; + } + + void OnRightEncoderMove(int direction) { + } + +private: + // BPM Calcultion + int last_bpm_tick; + int bpm; + + // CV monitor + int last_cv; + bool freeze; + + // Scope + uint8_t snapshot[128]; + int sample_ticks; // Ticks between samples + int sample_countdown; // Last time a sample was taken + int sample_num; // Current sample number at the start + int last_encoder_move; // The last the the sample_ticks value was changed + int last_scope_tick; // Used to auto-calculate sample countdown + + void DrawBPM() { + gfxPrint(110, 1, bpm / 4); + gfxBitmap(102, 1, 8, CLOCK_ICON); + } + +// void DrawTicks() { +// gfxPrint(40, 1, sample_ticks); +// } + + + void gfxPrintVoltage(int cv) { + int v = (cv * 100) / (12 << 7); + bool neg = v < 0 ? 1 : 0; + if (v < 0) v = -v; + int wv = v / 100; // whole volts + int dv = v - (wv * 100); // decimal + gfxPrint(neg ? "-" : "+"); + gfxPrint(wv); + gfxPrint("."); + if (dv < 10) gfxPrint("0"); + gfxPrint(dv); + gfxPrint("V"); + } + + void DrawVoltage() { + gfxBitmap(45, 3, 8, CV_ICON); + gfxPos(55, 1); + gfxPrintVoltage(last_cv); + } + + void DrawInput1() { + for (int s = 0; s < 128; s++) + { + int x = s + sample_num; + if (x > 127) x -= 128; + int l = Proportion(snapshot[x], 255, 48); + gfxPixel(x, (48 - l) + 14); + } + + } + +}; + +BigScope BigScope_instance; + +// App stubs +void BigScope_init() { + BigScope_instance.BaseStart(); +} + +// Not using O_C Storage +size_t BigScope_storageSize() { return 0; } + +size_t BigScope_save(void *storage) { return 0; } + +size_t BigScope_restore(const void *storage) { return 0; } + +void BigScope_isr() { + return BigScope_instance.BaseController(); +} + +void BigScope_handleAppEvent(OC::AppEvent event) { + if (event == OC::APP_EVENT_RESUME) { + BigScope_instance.Resume(); + } + if (event == OC::APP_EVENT_SUSPEND) { + BigScope_instance.OnSendSysEx(); + } +} + +void BigScope_loop() {} // Deprecated + +void BigScope_menu() { + BigScope_instance.BaseView(); +} + +void BigScope_screensaver() {} // Deprecated + +void BigScope_handleButtonEvent(const UI::Event &event) { + // For left encoder, handle press and long press + if (event.control == OC::CONTROL_BUTTON_L) { + if (event.type == UI::EVENT_BUTTON_LONG_PRESS) BigScope_instance.OnLeftButtonLongPress(); + else BigScope_instance.OnLeftButtonPress(); + } + + // For right encoder, only handle press (long press is reserved) + if (event.control == OC::CONTROL_BUTTON_R && event.type == UI::EVENT_BUTTON_PRESS) + BigScope_instance.OnRightButtonPress(); + + // For up button, handle only press (long press is reserved) + if (event.control == OC::CONTROL_BUTTON_UP) BigScope_instance.OnUpButtonPress(); + + // For down button, handle press and long press + if (event.control == OC::CONTROL_BUTTON_DOWN) { + if (event.type == UI::EVENT_BUTTON_PRESS) BigScope_instance.OnDownButtonPress(); + if (event.type == UI::EVENT_BUTTON_LONG_PRESS) BigScope_instance.OnDownButtonLongPress(); + } +} + +void BigScope_handleEncoderEvent(const UI::Event &event) { + // Left encoder turned + if (event.control == OC::CONTROL_ENCODER_L) BigScope_instance.OnLeftEncoderMove(event.value); + + // Right encoder turned + if (event.control == OC::CONTROL_ENCODER_R) BigScope_instance.OnRightEncoderMove(event.value); +} diff --git a/software/o_c_REV/APP_HEMISPHERE.ino b/software/o_c_REV/APP_HEMISPHERE.ino index 068a2b18c..721311faf 100644 --- a/software/o_c_REV/APP_HEMISPHERE.ino +++ b/software/o_c_REV/APP_HEMISPHERE.ino @@ -80,6 +80,8 @@ public: help_hemisphere = -1; clock_setup = 0; + OC::DAC::set_all_octave(0); + SetApplet(0, get_applet_index_by_id(8)); // ADSR SetApplet(1, get_applet_index_by_id(26)); // Scale Duet } diff --git a/software/o_c_REV/HEM_ADSREG.ino b/software/o_c_REV/HEM_ADSREG.ino index 5922ed9b4..4a9df3922 100644 --- a/software/o_c_REV/HEM_ADSREG.ino +++ b/software/o_c_REV/HEM_ADSREG.ino @@ -93,7 +93,15 @@ public: } void View() { + int adsr[4] = {attack, decay, sustain, release}; + const char *labels[] = {"Attack","Decay","Sustain","Release"}; + gfxHeader(applet_name()); + gfxPos(1, 15); + gfxPrint(labels[edit_stage]); + gfxPrint(45 + 18 - digitCount(adsr[edit_stage]) * 6, 15, adsr[edit_stage]); + gfxCursor(46, 23, 17); + DrawIndicator(); DrawADSR(); } diff --git a/software/o_c_REV/HEM_Carpeggio.ino b/software/o_c_REV/HEM_Carpeggio.ino index 9e037ac30..63b3b341e 100644 --- a/software/o_c_REV/HEM_Carpeggio.ino +++ b/software/o_c_REV/HEM_Carpeggio.ino @@ -36,6 +36,7 @@ public: replay = 0; transpose = 0; ImprintChord(2); + pitch_out_for_step(); } void Controller() { @@ -53,8 +54,8 @@ public: step = (y * 4) + x; pitch_out_for_step(); } else { - pitch_out_for_step(); if (++step > 15) step = 0; + pitch_out_for_step(); } replay = 0; } else if (replay) { @@ -85,7 +86,7 @@ public: cursor = 0; // Don't advance cursor when chord is changed ImprintChord(chord); } - if (++cursor > 2) cursor = 0; + if (++cursor > 3) cursor = 0; ResetCursor(); } @@ -93,6 +94,17 @@ public: if (cursor == 0) sequence[step] = constrain(sequence[step] += direction, -24, 60); if (cursor == 1) chord = constrain(chord += direction, 0, Nr_of_arp_chords - 1); if (cursor == 2) transpose = constrain(transpose += direction, -24, 24); + if (cursor == 3) { + // only imprint cord when turning left from shuffle + if (shuffle && direction < 0) { + ImprintChord(sel_chord); + } + // shuffle chord every time we turn right + if (direction > 0) { + ShuffleChord(); + } + + } if (cursor != 1) replay = 1; } @@ -130,6 +142,7 @@ private: int chord; // Selected chord int sel_chord; // Most recently-imprinted chord int transpose; // Transposition setting (-24 ~ +24) + bool shuffle = false; // Variables to handle imprint confirmation animation int confirm_animation_countdown; @@ -149,6 +162,12 @@ private: gfxPrint(transpose); if (cursor == 2) gfxCursor(32, 33, 30); + // Shuffle selector + gfxBitmap(37, 36, 8, PLAY_ICON); + gfxBitmap(49, 36, 8, LOOP_ICON); + gfxInvert(36 + (shuffle ? 12 : 0), 35, 10, 10); + if (cursor == 3) gfxCursor(37, 46, 20); + // Note name editor uint8_t midi_note = constrain(sequence[step] + 36 + transpose, 0, 127); gfxPrint(38, 50, midi_note_numbers[midi_note]); @@ -188,6 +207,22 @@ private: chord = new_chord; confirm_animation_position = 16; confirm_animation_countdown = HEM_CARPEGGIO_ANIMATION_SPEED; + shuffle = false; + } + + void ShuffleChord() { + int16_t old; // temp var for note being swapped + int16_t rnd; // temp var for index of note swapping in + for (int i = 0; i < 16; i++) { + // set old to current step value + old = sequence[i]; + rnd = random(0, 16); + sequence[i] = sequence[rnd]; + sequence[rnd] = old; + } + confirm_animation_position = 16; + confirm_animation_countdown = HEM_CARPEGGIO_ANIMATION_SPEED; + shuffle = true; } void pitch_out_for_step() { @@ -237,4 +272,4 @@ uint32_t Carpeggio_OnDataRequest(bool hemisphere) { void Carpeggio_OnDataReceive(bool hemisphere, uint32_t data) { Carpeggio_instance[hemisphere].OnDataReceive(data); -} +} \ No newline at end of file diff --git a/software/o_c_REV/HemisphereApplet.h b/software/o_c_REV/HemisphereApplet.h index 0a64d681d..a47a425de 100644 --- a/software/o_c_REV/HemisphereApplet.h +++ b/software/o_c_REV/HemisphereApplet.h @@ -279,6 +279,15 @@ class HemisphereApplet { return padding; } + int digitCount(int n) { + int count = 0; + while (n != 0) { + n /= 10; // n = n/10 + ++count; + } + return count; + } + //////////////// Hemisphere-specific graphics methods //////////////////////////////////////////////////////////////////////////////// diff --git a/software/o_c_REV/OC_apps.ino b/software/o_c_REV/OC_apps.ino index d1432ddd8..f25d0e0e9 100644 --- a/software/o_c_REV/OC_apps.ino +++ b/software/o_c_REV/OC_apps.ino @@ -42,6 +42,9 @@ OC::App available_apps[] = { #ifdef ENABLE_APP_DARKEST_TIMELINE DECLARE_APP('D','2', "Darkest Timeline", TheDarkestTimeline), #endif + #ifdef ENABLE_APP_BIG_SCOPE + DECLARE_APP('B','S', "Scope", BigScope), + #endif DECLARE_APP('E','N', "Enigma", EnigmaTMWS), DECLARE_APP('N','N', "Neural Net", NeuralNetwork), DECLARE_APP('S','C', "Scale Editor", SCALEEDITOR), diff --git a/software/o_c_REV/OC_options.h b/software/o_c_REV/OC_options.h index 208646c83..12d059ca1 100644 --- a/software/o_c_REV/OC_options.h +++ b/software/o_c_REV/OC_options.h @@ -37,5 +37,8 @@ // This uses approximately 640 additional bytes of program storage space #define HEM_LOGARHYTHM_MOD_SCALES +// Define this to enable the BigScope App +#define ENABLE_APP_BIG_SCOPE + #endif