Merge branch 'main' of https://github.com/Blitblank/metabalus
This commit is contained in:
@@ -77,7 +77,6 @@ qt_add_executable(metabolus
|
|||||||
src/synth/Voice.h
|
src/synth/Voice.h
|
||||||
src/synth/WavetableController.cpp
|
src/synth/WavetableController.cpp
|
||||||
src/synth/WavetableController.h
|
src/synth/WavetableController.h
|
||||||
resources/resources.qrc
|
|
||||||
src/ui/widgets/SmartSlider/SmartSlider.cpp
|
src/ui/widgets/SmartSlider/SmartSlider.cpp
|
||||||
src/ui/widgets/SmartSlider/SmartSlider.h
|
src/ui/widgets/SmartSlider/SmartSlider.h
|
||||||
src/ui/widgets/SmartSlider/SmartSlider.ui
|
src/ui/widgets/SmartSlider/SmartSlider.ui
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ This synthesizer isn't very good, but it's neat :3
|
|||||||
- [x] Create oscillator class where the actual tone generation occurs. Multiple
|
- [x] Create oscillator class where the actual tone generation occurs. Multiple
|
||||||
oscillators increase the sound complexity considerably
|
oscillators increase the sound complexity considerably
|
||||||
- [x] Create a UI scope to visualize the synthesized composite waveform
|
- [x] Create a UI scope to visualize the synthesized composite waveform
|
||||||
- [ ] Create wavetables for more complex tone generation. Needs to be selectable from ui
|
- [x] Create wavetables for more complex tone generation. Needs to be selectable from ui
|
||||||
- [ ] Wavetable file loading
|
- [ ] Wavetable file loading
|
||||||
- [x] Create digital filters, prob biquad. Controllable from ui obv (cutoff + resonance)
|
- [x] Create digital filters, prob biquad. Controllable from ui obv (cutoff + resonance)
|
||||||
- [x] Add polyphony somewhere. Probably involves a voice class. If processing power
|
- [x] Add polyphony somewhere. Probably involves a voice class. If processing power
|
||||||
@@ -30,9 +30,9 @@ This synthesizer isn't very good, but it's neat :3
|
|||||||
- [ ] Filter upgrades including some more complex solving techniques (State Variable Filter),
|
- [ ] Filter upgrades including some more complex solving techniques (State Variable Filter),
|
||||||
better key tracking, more natural envelope curves, filter drive, etc.
|
better key tracking, more natural envelope curves, filter drive, etc.
|
||||||
- [ ] Reverb (quite a few ways to do this, needs more research)
|
- [ ] Reverb (quite a few ways to do this, needs more research)
|
||||||
- [ ] Pitch tuning with the multiple oscillators (Octave > Semitone > Fine-tune)
|
- [x] Pitch tuning with the multiple oscillators (Octave > Semitone > Fine-tune)
|
||||||
- [ ] Frequency Modulation (huge)
|
- [ ] Frequency Modulation (huge)
|
||||||
- [ ] Profile saving and loading, also includes loading configurations like keymaps, audio
|
- [x] Profile saving and loading, also includes loading configurations like keymaps, audio
|
||||||
engine config, etc. from a yaml instead of being hardcoded
|
engine config, etc. from a yaml instead of being hardcoded
|
||||||
- [ ] Noise
|
- [ ] Noise
|
||||||
- [ ] LFO modulation
|
- [ ] LFO modulation
|
||||||
|
|||||||
338
config/keymap.yaml
Normal file
338
config/keymap.yaml
Normal file
@@ -0,0 +1,338 @@
|
|||||||
|
|
||||||
|
# keymap.yaml
|
||||||
|
# Configures a computer keyboard mapping of keys to midi notes for when you don't have a midi device available
|
||||||
|
# (there's probably tools available that are able to make your keyboard act as a midi device but this was easier)
|
||||||
|
|
||||||
|
keymap:
|
||||||
|
Key_Shift: B_2
|
||||||
|
Key_Z: C_3
|
||||||
|
Key_S: C#3
|
||||||
|
Key_X: D_3
|
||||||
|
Key_D: D#3
|
||||||
|
Key_C: E_3
|
||||||
|
Key_V: F_3
|
||||||
|
Key_G: F#3
|
||||||
|
Key_B: G_3
|
||||||
|
Key_H: G#3
|
||||||
|
Key_N: A_3
|
||||||
|
Key_J: A#3
|
||||||
|
Key_M: B_3
|
||||||
|
Key_Q: C_4
|
||||||
|
Key_2: C#4
|
||||||
|
Key_W: D_4
|
||||||
|
Key_3: D#4
|
||||||
|
Key_E: E_4
|
||||||
|
Key_R: F_4
|
||||||
|
Key_5: F#4
|
||||||
|
Key_T: G_4
|
||||||
|
Key_6: G#4
|
||||||
|
Key_Y: A_4
|
||||||
|
Key_7: A#4
|
||||||
|
Key_U: B_4
|
||||||
|
Key_I: C_5
|
||||||
|
Key_9: C#5
|
||||||
|
Key_O: D_5
|
||||||
|
|
||||||
|
# below are translations from strings to numbers that both Qt and the Oscillators can understand
|
||||||
|
# do not touch them unless you like the program not running
|
||||||
|
|
||||||
|
# note strings to midi ids, do not touch
|
||||||
|
# only has sharps for now, you can figure the rest out
|
||||||
|
notes:
|
||||||
|
A_0: 21
|
||||||
|
A#0: 22
|
||||||
|
B_0: 23
|
||||||
|
C_1: 24
|
||||||
|
C#1: 25
|
||||||
|
D_1: 26
|
||||||
|
D#1: 27
|
||||||
|
E_1: 28
|
||||||
|
F_1: 29
|
||||||
|
F#1: 30
|
||||||
|
G_1: 31
|
||||||
|
G#1: 32
|
||||||
|
A_1: 33
|
||||||
|
A#1: 34
|
||||||
|
B_1: 35
|
||||||
|
C_2: 36
|
||||||
|
C#2: 37
|
||||||
|
D_2: 38
|
||||||
|
D#2: 39
|
||||||
|
E_2: 40
|
||||||
|
F_2: 41
|
||||||
|
F#2: 42
|
||||||
|
G_2: 43
|
||||||
|
G#2: 44
|
||||||
|
A_2: 45
|
||||||
|
A#2: 46
|
||||||
|
B_2: 47
|
||||||
|
C_3: 48
|
||||||
|
C#3: 49
|
||||||
|
D_3: 50
|
||||||
|
D#3: 51
|
||||||
|
E_3: 52
|
||||||
|
F_3: 53
|
||||||
|
F#3: 54
|
||||||
|
G_3: 55
|
||||||
|
G#3: 56
|
||||||
|
A_3: 57
|
||||||
|
A#3: 58
|
||||||
|
B_3: 59
|
||||||
|
C_4: 60
|
||||||
|
C#4: 61
|
||||||
|
D_4: 62
|
||||||
|
D#4: 63
|
||||||
|
E_4: 64
|
||||||
|
F_4: 65
|
||||||
|
F#4: 66
|
||||||
|
G_4: 67
|
||||||
|
G#4: 68
|
||||||
|
A_4: 69
|
||||||
|
A#4: 70
|
||||||
|
B_4: 71
|
||||||
|
C_5: 72
|
||||||
|
C#5: 73
|
||||||
|
D_5: 74
|
||||||
|
D#5: 75
|
||||||
|
E_5: 76
|
||||||
|
F_5: 77
|
||||||
|
F#5: 78
|
||||||
|
G_5: 79
|
||||||
|
G#5: 80
|
||||||
|
A_5: 81
|
||||||
|
A#5: 82
|
||||||
|
B_5: 83
|
||||||
|
C_6: 84
|
||||||
|
C#6: 85
|
||||||
|
D_6: 86
|
||||||
|
D#6: 87
|
||||||
|
E_6: 88
|
||||||
|
F_6: 89
|
||||||
|
F#6: 90
|
||||||
|
G_6: 91
|
||||||
|
G#6: 92
|
||||||
|
A_6: 93
|
||||||
|
A#6: 94
|
||||||
|
B_6: 95
|
||||||
|
C_7: 96
|
||||||
|
C#7: 97
|
||||||
|
D_7: 98
|
||||||
|
D#7: 99
|
||||||
|
E_7: 100
|
||||||
|
F_7: 101
|
||||||
|
F#7: 102
|
||||||
|
G_7: 103
|
||||||
|
G#7: 104
|
||||||
|
A_7: 105
|
||||||
|
A#7: 106
|
||||||
|
B_7: 107
|
||||||
|
C_8: 108
|
||||||
|
|
||||||
|
# key strings to qt-key-ids, do not touch
|
||||||
|
keys:
|
||||||
|
Key_Space: 0x20
|
||||||
|
Key_Any: 0x20
|
||||||
|
Key_Exclam: 0x21
|
||||||
|
Key_QuoteDbl: 0x22
|
||||||
|
Key_NumberSign: 0x23
|
||||||
|
Key_Dollar: 0x24
|
||||||
|
Key_Percent: 0x25
|
||||||
|
Key_Ampersand: 0x26
|
||||||
|
Key_Apostrophe: 0x27
|
||||||
|
Key_ParenLeft: 0x28
|
||||||
|
Key_ParenRight: 0x29
|
||||||
|
Key_Asterisk: 0x2a
|
||||||
|
Key_Plus: 0x2b
|
||||||
|
Key_Comma: 0x2c
|
||||||
|
Key_Minus: 0x2d
|
||||||
|
Key_Period: 0x2e
|
||||||
|
Key_Slash: 0x2f
|
||||||
|
Key_0: 0x30
|
||||||
|
Key_1: 0x31
|
||||||
|
Key_2: 0x32
|
||||||
|
Key_3: 0x33
|
||||||
|
Key_4: 0x34
|
||||||
|
Key_5: 0x35
|
||||||
|
Key_6: 0x36
|
||||||
|
Key_7: 0x37
|
||||||
|
Key_8: 0x38
|
||||||
|
Key_9: 0x39
|
||||||
|
Key_Colon: 0x3a
|
||||||
|
Key_Semicolon: 0x3b
|
||||||
|
Key_Less: 0x3c
|
||||||
|
Key_Equal: 0x3d
|
||||||
|
Key_Greater: 0x3e
|
||||||
|
Key_Question: 0x3f
|
||||||
|
Key_At: 0x40
|
||||||
|
Key_A: 0x41
|
||||||
|
Key_B: 0x42
|
||||||
|
Key_C: 0x43
|
||||||
|
Key_D: 0x44
|
||||||
|
Key_E: 0x45
|
||||||
|
Key_F: 0x46
|
||||||
|
Key_G: 0x47
|
||||||
|
Key_H: 0x48
|
||||||
|
Key_I: 0x49
|
||||||
|
Key_J: 0x4a
|
||||||
|
Key_K: 0x4b
|
||||||
|
Key_L: 0x4c
|
||||||
|
Key_M: 0x4d
|
||||||
|
Key_N: 0x4e
|
||||||
|
Key_O: 0x4f
|
||||||
|
Key_P: 0x50
|
||||||
|
Key_Q: 0x51
|
||||||
|
Key_R: 0x52
|
||||||
|
Key_S: 0x53
|
||||||
|
Key_T: 0x54
|
||||||
|
Key_U: 0x55
|
||||||
|
Key_V: 0x56
|
||||||
|
Key_W: 0x57
|
||||||
|
Key_X: 0x58
|
||||||
|
Key_Y: 0x59
|
||||||
|
Key_Z: 0x5a
|
||||||
|
Key_BracketLeft: 0x5b
|
||||||
|
Key_Backslash: 0x5c
|
||||||
|
Key_BracketRight: 0x5d
|
||||||
|
Key_AsciiCircum: 0x5e
|
||||||
|
Key_Underscore: 0x5f
|
||||||
|
Key_QuoteLeft: 0x60
|
||||||
|
Key_BraceLeft: 0x7b
|
||||||
|
Key_Bar: 0x7c
|
||||||
|
Key_BraceRight: 0x7d
|
||||||
|
Key_AsciiTilde: 0x7e
|
||||||
|
Key_nobreakspace: 0x0a0
|
||||||
|
Key_exclamdown: 0x0a1
|
||||||
|
Key_cent: 0x0a2
|
||||||
|
Key_sterling: 0x0a3
|
||||||
|
Key_currency: 0x0a4
|
||||||
|
Key_yen: 0x0a5
|
||||||
|
Key_brokenbar: 0x0a6
|
||||||
|
Key_section: 0x0a7
|
||||||
|
Key_diaeresis: 0x0a8
|
||||||
|
Key_copyright: 0x0a9
|
||||||
|
Key_ordfeminine: 0x0aa
|
||||||
|
Key_guillemotleft: 0x0ab
|
||||||
|
Key_notsign: 0x0ac
|
||||||
|
Key_hyphen: 0x0ad
|
||||||
|
Key_registered: 0x0ae
|
||||||
|
Key_macron: 0x0af
|
||||||
|
Key_degree: 0x0b0
|
||||||
|
Key_plusminus: 0x0b1
|
||||||
|
Key_twosuperior: 0x0b2
|
||||||
|
Key_threesuperior: 0x0b3
|
||||||
|
Key_acute: 0x0b4
|
||||||
|
Key_micro: 0x0b5
|
||||||
|
Key_paragraph: 0x0b6
|
||||||
|
Key_periodcentered: 0x0b7
|
||||||
|
Key_cedilla: 0x0b8
|
||||||
|
Key_onesuperior: 0x0b9
|
||||||
|
Key_masculine: 0x0ba
|
||||||
|
Key_guillemotright: 0x0bb
|
||||||
|
Key_onequarter: 0x0bc
|
||||||
|
Key_onehalf: 0x0bd
|
||||||
|
Key_threequarters: 0x0be
|
||||||
|
Key_questiondown: 0x0bf
|
||||||
|
Key_Agrave: 0x0c0
|
||||||
|
Key_Aacute: 0x0c1
|
||||||
|
Key_Acircumflex: 0x0c2
|
||||||
|
Key_Atilde: 0x0c3
|
||||||
|
Key_Adiaeresis: 0x0c4
|
||||||
|
Key_Aring: 0x0c5
|
||||||
|
Key_AE: 0x0c6
|
||||||
|
Key_Ccedilla: 0x0c7
|
||||||
|
Key_Egrave: 0x0c8
|
||||||
|
Key_Eacute: 0x0c9
|
||||||
|
Key_Ecircumflex: 0x0ca
|
||||||
|
Key_Ediaeresis: 0x0cb
|
||||||
|
Key_Igrave: 0x0cc
|
||||||
|
Key_Iacute: 0x0cd
|
||||||
|
Key_Icircumflex: 0x0ce
|
||||||
|
Key_Idiaeresis: 0x0cf
|
||||||
|
Key_ETH: 0x0d0
|
||||||
|
Key_Ntilde: 0x0d1
|
||||||
|
Key_Ograve: 0x0d2
|
||||||
|
Key_Oacute: 0x0d3
|
||||||
|
Key_Ocircumflex: 0x0d4
|
||||||
|
Key_Otilde: 0x0d5
|
||||||
|
Key_Odiaeresis: 0x0d6
|
||||||
|
Key_multiply: 0x0d7
|
||||||
|
Key_Ooblique: 0x0d8
|
||||||
|
Key_Ugrave: 0x0d9
|
||||||
|
Key_Uacute: 0x0da
|
||||||
|
Key_Ucircumflex: 0x0db
|
||||||
|
Key_Udiaeresis: 0x0dc
|
||||||
|
Key_Yacute: 0x0dd
|
||||||
|
Key_THORN: 0x0de
|
||||||
|
Key_ssharp: 0x0df
|
||||||
|
Key_division: 0x0f7
|
||||||
|
Key_ydiaeresis: 0x0ff
|
||||||
|
Key_Escape: 0x01000000
|
||||||
|
Key_Tab: 0x01000001
|
||||||
|
Key_Backtab: 0x01000002
|
||||||
|
Key_Backspace: 0x01000003
|
||||||
|
Key_Return: 0x01000004
|
||||||
|
Key_Enter: 0x01000005
|
||||||
|
Key_Insert: 0x01000006
|
||||||
|
Key_Delete: 0x01000007
|
||||||
|
Key_Pause: 0x01000008
|
||||||
|
Key_Print: 0x01000009
|
||||||
|
Key_SysReq: 0x0100000a
|
||||||
|
Key_Clear: 0x0100000b
|
||||||
|
Key_Home: 0x01000010
|
||||||
|
Key_End: 0x01000011
|
||||||
|
Key_Left: 0x01000012
|
||||||
|
Key_Up: 0x01000013
|
||||||
|
Key_Right: 0x01000014
|
||||||
|
Key_Down: 0x01000015
|
||||||
|
Key_PageUp: 0x01000016
|
||||||
|
Key_PageDown: 0x01000017
|
||||||
|
Key_Shift: 0x01000020
|
||||||
|
Key_Control: 0x01000021
|
||||||
|
Key_Meta: 0x01000022
|
||||||
|
Key_Alt: 0x01000023
|
||||||
|
Key_CapsLock: 0x01000024
|
||||||
|
Key_NumLock: 0x01000025
|
||||||
|
Key_ScrollLock: 0x01000026
|
||||||
|
Key_F1: 0x01000030
|
||||||
|
Key_F2: 0x01000031
|
||||||
|
Key_F3: 0x01000032
|
||||||
|
Key_F4: 0x01000033
|
||||||
|
Key_F5: 0x01000034
|
||||||
|
Key_F6: 0x01000035
|
||||||
|
Key_F7: 0x01000036
|
||||||
|
Key_F8: 0x01000037
|
||||||
|
Key_F9: 0x01000038
|
||||||
|
Key_F10: 0x01000039
|
||||||
|
Key_F11: 0x0100003a
|
||||||
|
Key_F12: 0x0100003b
|
||||||
|
Key_F13: 0x0100003c
|
||||||
|
Key_F14: 0x0100003d
|
||||||
|
Key_F15: 0x0100003e
|
||||||
|
Key_F16: 0x0100003f
|
||||||
|
Key_F17: 0x01000040
|
||||||
|
Key_F18: 0x01000041
|
||||||
|
Key_F19: 0x01000042
|
||||||
|
Key_F20: 0x01000043
|
||||||
|
Key_F21: 0x01000044
|
||||||
|
Key_F22: 0x01000045
|
||||||
|
Key_F23: 0x01000046
|
||||||
|
Key_F24: 0x01000047
|
||||||
|
Key_F25: 0x01000048
|
||||||
|
Key_F26: 0x01000049
|
||||||
|
Key_F27: 0x0100004a
|
||||||
|
Key_F28: 0x0100004b
|
||||||
|
Key_F29: 0x0100004c
|
||||||
|
Key_F30: 0x0100004d
|
||||||
|
Key_F31: 0x0100004e
|
||||||
|
Key_F32: 0x0100004f
|
||||||
|
Key_F33: 0x01000050
|
||||||
|
Key_F34: 0x01000051
|
||||||
|
Key_F35: 0x01000052
|
||||||
|
Key_Super_L: 0x01000053
|
||||||
|
Key_Super_R: 0x01000054
|
||||||
|
Key_Menu: 0x01000055
|
||||||
|
Key_Hyper_L: 0x01000056
|
||||||
|
Key_Hyper_R: 0x01000057
|
||||||
|
Key_Help: 0x01000058
|
||||||
|
Key_Direction_L: 0x01000059
|
||||||
|
Key_Direction_R: 0x01000060
|
||||||
@@ -4,29 +4,38 @@
|
|||||||
|
|
||||||
# sequences in the form [x, x, x] denote [setValue, sliderMinimum, sliderMaximum]
|
# sequences in the form [x, x, x] denote [setValue, sliderMinimum, sliderMaximum]
|
||||||
|
|
||||||
version: 0x0002
|
version: 0x0003
|
||||||
|
|
||||||
# deprecated, useless
|
# deprecated, useless
|
||||||
Osc1Freq: [100, 20, 600]
|
Osc1Freq: [100, 20, 600]
|
||||||
|
|
||||||
# wavetable selections
|
# wavetable selections
|
||||||
OscWaveSelector1: 2
|
OscWaveSelector1: 2
|
||||||
OscWaveSelector2: 1
|
OscWaveSelector2: 3
|
||||||
|
|
||||||
# Oscillator frequency parameters
|
# Frequency parameters
|
||||||
|
MasterOctaveOffset: [0, -5, 5]
|
||||||
|
MasterSemitoneOffset: [0, -12, 12]
|
||||||
|
MasterPitchOffset: [0, -100, 100]
|
||||||
Osc1OctaveOffset: [0, -5, 5]
|
Osc1OctaveOffset: [0, -5, 5]
|
||||||
Osc1SemitoneOffset: [0, -12, 12]
|
Osc1SemitoneOffset: [0, -12, 12]
|
||||||
Osc1PitchOffset: [0, -100, 100]
|
Osc1PitchOffset: [1.34, -100, 100]
|
||||||
Osc2OctaveOffset: [1, -5, 5]
|
Osc2OctaveOffset: [1, -5, 5]
|
||||||
Osc2SemitoneOffset: [0, -12, 12]
|
Osc2SemitoneOffset: [0, -12, 12]
|
||||||
Osc2PitchOffset: [0, -100, 100]
|
Osc2PitchOffset: [12.86, -100, 100]
|
||||||
Osc3OctaveOffset: [1, -5, 5]
|
Osc3OctaveOffset: [1, -5, 5]
|
||||||
Osc3SemitoneOffset: [7, -12, 12]
|
Osc3SemitoneOffset: [7, -12, 12]
|
||||||
Osc3PitchOffset: [1.96, -100, 100]
|
Osc3PitchOffset: [-8.79, -100, 100]
|
||||||
|
|
||||||
|
# gonna have something like this:
|
||||||
|
#MasterPitchOffset:
|
||||||
|
# - [0, -5, 5] # Octave
|
||||||
|
# - [0, -12, -12] # Semitone
|
||||||
|
# - [0, -100, 100] # Pitch
|
||||||
|
|
||||||
# Envelope generator parameters
|
# Envelope generator parameters
|
||||||
Osc1Volume:
|
Osc1Volume:
|
||||||
- [1, 0, 2] # Depth
|
- [1, 0, 10] # Depth
|
||||||
- [0.05, 0, 2] # Attack
|
- [0.05, 0, 2] # Attack
|
||||||
- [0.2, 0, 2] # Decay
|
- [0.2, 0, 2] # Decay
|
||||||
- [0.7, 0, 1] # Sustain
|
- [0.7, 0, 1] # Sustain
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ int ConfigInterface::getValue(ConfigFile file, std::string key, int defaultVal)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ugly but if it works it works
|
// ugly but if it works it works
|
||||||
void ConfigInterface::loadProfile(std::string filename) {
|
YAML::Node ConfigInterface::loadProfile(std::string filename) {
|
||||||
|
|
||||||
// load file
|
// load file
|
||||||
std::string filepath = "config/profiles/" + filename + ".yaml";
|
std::string filepath = "config/profiles/" + filename + ".yaml";
|
||||||
@@ -57,16 +57,16 @@ void ConfigInterface::loadProfile(std::string filename) {
|
|||||||
config = YAML::LoadFile(filepath);
|
config = YAML::LoadFile(filepath);
|
||||||
} catch(const std::exception& e) {
|
} catch(const std::exception& e) {
|
||||||
std::cerr << e.what() << std::endl;
|
std::cerr << e.what() << std::endl;
|
||||||
return;
|
return config;
|
||||||
}
|
}
|
||||||
|
|
||||||
// check version
|
// check version
|
||||||
int version = config["version"].as<int>(); // yaml-cpp parses unquoted hex as integers
|
int version = config["version"].as<int>(); // yaml-cpp parses unquoted hex as integers
|
||||||
if(version < CONFIG_VERSION) {
|
if(version < CONFIG_VERSION) {
|
||||||
std::cout << "Parameter profile version " << version << "is outdated below the compatible version " << CONFIG_VERSION << std::endl;
|
std::cout << "Parameter profile version " << version << "is outdated below the compatible version " << CONFIG_VERSION << std::endl;
|
||||||
return;
|
return config;
|
||||||
} else {
|
} else {
|
||||||
std::cout << version << std::endl;
|
std::cout << "Parameter profile version " << version << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
// extract values from the config file
|
// extract values from the config file
|
||||||
@@ -74,6 +74,27 @@ void ConfigInterface::loadProfile(std::string filename) {
|
|||||||
std::array<ParamDefault, 5> fCutoffProfile = loadEnvProfile(&config, "FilterCutoff");
|
std::array<ParamDefault, 5> fCutoffProfile = loadEnvProfile(&config, "FilterCutoff");
|
||||||
std::array<ParamDefault, 5> fResonanceProfile = loadEnvProfile(&config, "FilterResonance");
|
std::array<ParamDefault, 5> fResonanceProfile = loadEnvProfile(&config, "FilterResonance");
|
||||||
|
|
||||||
|
std::array<ParamDefault, 3> masterPitchOffsets = {{
|
||||||
|
{ config["MasterOctaveOffset"][0].as<float>(), config["MasterOctaveOffset"][1].as<float>(), config["MasterOctaveOffset"][2].as<float>() },
|
||||||
|
{ config["MasterSemitoneOffset"][0].as<float>(), config["MasterSemitoneOffset"][1].as<float>(), config["MasterSemitoneOffset"][2].as<float>() },
|
||||||
|
{ config["MasterPitchOffset"][0].as<float>(), config["MasterPitchOffset"][1].as<float>(), config["MasterPitchOffset"][2].as<float>() },
|
||||||
|
}};
|
||||||
|
std::array<ParamDefault, 3> osc1PitchOffsets = {{
|
||||||
|
{ config["Osc1OctaveOffset"][0].as<float>(), config["Osc1OctaveOffset"][1].as<float>(), config["Osc1OctaveOffset"][2].as<float>() },
|
||||||
|
{ config["Osc1SemitoneOffset"][0].as<float>(), config["Osc1SemitoneOffset"][1].as<float>(), config["Osc1SemitoneOffset"][2].as<float>() },
|
||||||
|
{ config["Osc1PitchOffset"][0].as<float>(), config["Osc1PitchOffset"][1].as<float>(), config["Osc1PitchOffset"][2].as<float>() },
|
||||||
|
}};
|
||||||
|
std::array<ParamDefault, 3> osc2PitchOffsets = {{
|
||||||
|
{ config["Osc2OctaveOffset"][0].as<float>(), config["Osc2OctaveOffset"][1].as<float>(), config["Osc2OctaveOffset"][2].as<float>() },
|
||||||
|
{ config["Osc2SemitoneOffset"][0].as<float>(), config["Osc2SemitoneOffset"][1].as<float>(), config["Osc2SemitoneOffset"][2].as<float>() },
|
||||||
|
{ config["Osc2PitchOffset"][0].as<float>(), config["Osc2PitchOffset"][1].as<float>(), config["Osc2PitchOffset"][2].as<float>() },
|
||||||
|
}};
|
||||||
|
std::array<ParamDefault, 3> osc3PitchOffsets = {{
|
||||||
|
{ config["Osc3OctaveOffset"][0].as<float>(), config["Osc3OctaveOffset"][1].as<float>(), config["Osc3OctaveOffset"][2].as<float>() },
|
||||||
|
{ config["Osc3SemitoneOffset"][0].as<float>(), config["Osc3SemitoneOffset"][1].as<float>(), config["Osc3SemitoneOffset"][2].as<float>() },
|
||||||
|
{ config["Osc3PitchOffset"][0].as<float>(), config["Osc3PitchOffset"][1].as<float>(), config["Osc3PitchOffset"][2].as<float>() },
|
||||||
|
}};
|
||||||
|
|
||||||
// TODO: remove this once all the parameters are set properly
|
// TODO: remove this once all the parameters are set properly
|
||||||
params_->resetToDefaults();
|
params_->resetToDefaults();
|
||||||
|
|
||||||
@@ -81,13 +102,26 @@ void ConfigInterface::loadProfile(std::string filename) {
|
|||||||
params_->set(EnvelopeId::Osc1Volume, osc1VolumeProfile[0].def, osc1VolumeProfile[1].def, osc1VolumeProfile[2].def, osc1VolumeProfile[3].def, osc1VolumeProfile[4].def);
|
params_->set(EnvelopeId::Osc1Volume, osc1VolumeProfile[0].def, osc1VolumeProfile[1].def, osc1VolumeProfile[2].def, osc1VolumeProfile[3].def, osc1VolumeProfile[4].def);
|
||||||
params_->set(EnvelopeId::FilterCutoff, fCutoffProfile[0].def, fCutoffProfile[1].def, fCutoffProfile[2].def, fCutoffProfile[3].def, fCutoffProfile[4].def);
|
params_->set(EnvelopeId::FilterCutoff, fCutoffProfile[0].def, fCutoffProfile[1].def, fCutoffProfile[2].def, fCutoffProfile[3].def, fCutoffProfile[4].def);
|
||||||
params_->set(EnvelopeId::FilterResonance, fResonanceProfile[0].def, fResonanceProfile[1].def, fResonanceProfile[2].def, fResonanceProfile[3].def, fResonanceProfile[4].def);
|
params_->set(EnvelopeId::FilterResonance, fResonanceProfile[0].def, fResonanceProfile[1].def, fResonanceProfile[2].def, fResonanceProfile[3].def, fResonanceProfile[4].def);
|
||||||
// TODO: why do I bother passing in 5 values independently when I can just do an array ?
|
// TODO: why do I bother passing in 5 values independently when I can just do an array like in loadEnvProfile ?
|
||||||
// VVV look down there its so easy
|
params_->set(ParamId::MasterOctaveOffset, masterPitchOffsets[0].def);
|
||||||
|
params_->set(ParamId::MasterSemitoneOffset, masterPitchOffsets[1].def);
|
||||||
|
params_->set(ParamId::MasterPitchOffset, masterPitchOffsets[2].def);
|
||||||
|
params_->set(ParamId::Osc1OctaveOffset, osc1PitchOffsets[0].def);
|
||||||
|
params_->set(ParamId::Osc1SemitoneOffset, osc1PitchOffsets[1].def);
|
||||||
|
params_->set(ParamId::Osc1PitchOffset, osc1PitchOffsets[2].def);
|
||||||
|
params_->set(ParamId::Osc2OctaveOffset, osc2PitchOffsets[0].def);
|
||||||
|
params_->set(ParamId::Osc2SemitoneOffset, osc2PitchOffsets[1].def);
|
||||||
|
params_->set(ParamId::Osc2PitchOffset, osc2PitchOffsets[2].def);
|
||||||
|
params_->set(ParamId::Osc3OctaveOffset, osc3PitchOffsets[0].def);
|
||||||
|
params_->set(ParamId::Osc3SemitoneOffset, osc3PitchOffsets[1].def);
|
||||||
|
params_->set(ParamId::Osc3PitchOffset, osc3PitchOffsets[2].def);
|
||||||
|
|
||||||
// TODO:
|
// TODO:
|
||||||
// load wavetable settings
|
// load wavetable settings
|
||||||
// load oscillator pitch settings
|
// load oscillator pitch settings
|
||||||
|
|
||||||
|
return config;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::array<ParamDefault, 5> ConfigInterface::loadEnvProfile(YAML::Node* node, std::string profile) {
|
std::array<ParamDefault, 5> ConfigInterface::loadEnvProfile(YAML::Node* node, std::string profile) {
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ public:
|
|||||||
|
|
||||||
int getValue(ConfigFile file, std::string key, int defaultVal);
|
int getValue(ConfigFile file, std::string key, int defaultVal);
|
||||||
|
|
||||||
void loadProfile(std::string filename);
|
YAML::Node loadProfile(std::string filename);
|
||||||
std::array<ParamDefault, 5> loadEnvProfile(YAML::Node* node, std::string profile);
|
std::array<ParamDefault, 5> loadEnvProfile(YAML::Node* node, std::string profile);
|
||||||
std::array<ParamDefault, 5> loadEnvProfile(std::string filename, std::string profile);
|
std::array<ParamDefault, 5> loadEnvProfile(std::string filename, std::string profile);
|
||||||
|
|
||||||
|
|||||||
@@ -2,38 +2,40 @@
|
|||||||
#include "KeyboardController.h"
|
#include "KeyboardController.h"
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <yaml-cpp/yaml.h>
|
||||||
|
#include <filesystem>
|
||||||
|
|
||||||
KeyboardController::KeyboardController(NoteQueue& queue) : queue_(queue) {
|
KeyboardController::KeyboardController(NoteQueue& queue, ConfigInterface* config) : queue_(queue), config_(config) {
|
||||||
|
|
||||||
|
// load keymap from config file
|
||||||
|
std::string filepath = "config/keymap.yaml";
|
||||||
|
filepath = std::filesystem::absolute(filepath).string();
|
||||||
|
YAML::Node file;
|
||||||
|
try {
|
||||||
|
file = YAML::LoadFile(filepath);
|
||||||
|
} catch(const std::exception& e) {
|
||||||
|
std::cerr << e.what() << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
YAML::Node keymapNode = file["keymap"]; // node for string to string mappings
|
||||||
|
YAML::Node notesNode = file["notes"]; // string to midi int mappings
|
||||||
|
YAML::Node keysNode = file["keys"]; // string to qt key id mappings
|
||||||
|
|
||||||
|
// for each element in the keymap
|
||||||
|
for (const auto& entry : keymapNode) {
|
||||||
|
|
||||||
|
std::string keyString = entry.first.as<std::string>();
|
||||||
|
std::string noteString = entry.second.as<std::string>();
|
||||||
|
|
||||||
|
// match the strings to ints
|
||||||
|
uint8_t noteValue = notesNode[noteString].as<uint8_t>();
|
||||||
|
uint32_t keyValue = keysNode[keyString].as<uint32_t>();
|
||||||
|
|
||||||
|
// insert into map
|
||||||
|
keymap_.emplace(keyValue, noteValue);
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: also configurable via a yml
|
|
||||||
keymap_ = {
|
|
||||||
{ Qt::Key_Shift, 47 }, // B 2
|
|
||||||
{ Qt::Key_Z, 48 }, // C 3
|
|
||||||
{ Qt::Key_S, 49 }, // C#
|
|
||||||
{ Qt::Key_X, 50 }, // D
|
|
||||||
{ Qt::Key_D, 51 }, // D#
|
|
||||||
{ Qt::Key_C, 52 }, // E
|
|
||||||
{ Qt::Key_V, 53 }, // F
|
|
||||||
{ Qt::Key_G, 54 }, // F#
|
|
||||||
{ Qt::Key_B, 55 }, // G
|
|
||||||
{ Qt::Key_H, 56 }, // G#
|
|
||||||
{ Qt::Key_N, 57 }, // A
|
|
||||||
{ Qt::Key_J, 58 }, // A#
|
|
||||||
{ Qt::Key_M, 59 }, // B 3
|
|
||||||
{ Qt::Key_Q, 60 }, // C 4
|
|
||||||
{ Qt::Key_2, 61 }, // C#
|
|
||||||
{ Qt::Key_W, 62 }, // D
|
|
||||||
{ Qt::Key_3, 63 }, // D#
|
|
||||||
{ Qt::Key_E, 64 }, // E
|
|
||||||
{ Qt::Key_R, 65 }, // F
|
|
||||||
{ Qt::Key_5, 66 }, // F#
|
|
||||||
{ Qt::Key_T, 67 }, // G
|
|
||||||
{ Qt::Key_6, 68 }, // G#
|
|
||||||
{ Qt::Key_Y, 69 }, // A
|
|
||||||
{ Qt::Key_7, 70 }, // A#
|
|
||||||
{ Qt::Key_U, 71 }, // B 4
|
|
||||||
{ Qt::Key_I, 72 }, // C 5
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void KeyboardController::handleKeyPress(QKeyEvent* e) {
|
void KeyboardController::handleKeyPress(QKeyEvent* e) {
|
||||||
|
|||||||
@@ -5,12 +5,13 @@
|
|||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
|
||||||
#include "NoteQueue.h"
|
#include "NoteQueue.h"
|
||||||
|
#include "ConfigInterface.h"
|
||||||
|
|
||||||
// The keyboardcontroller handles user inputs from a keyboard and maps them to note events
|
// The keyboardcontroller handles user inputs from a keyboard and maps them to note events
|
||||||
class KeyboardController {
|
class KeyboardController {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit KeyboardController(NoteQueue& queue);
|
explicit KeyboardController(NoteQueue& queue, ConfigInterface* config);
|
||||||
~KeyboardController() = default;
|
~KeyboardController() = default;
|
||||||
|
|
||||||
void handleKeyPress(QKeyEvent* e);
|
void handleKeyPress(QKeyEvent* e);
|
||||||
@@ -19,6 +20,7 @@ public:
|
|||||||
private:
|
private:
|
||||||
|
|
||||||
NoteQueue& queue_;
|
NoteQueue& queue_;
|
||||||
|
ConfigInterface* config_;
|
||||||
|
|
||||||
// keymap is key -> midi note id
|
// keymap is key -> midi note id
|
||||||
std::unordered_map<int32_t, uint8_t> keymap_;
|
std::unordered_map<int32_t, uint8_t> keymap_;
|
||||||
|
|||||||
@@ -9,6 +9,9 @@ enum class ParamId : uint16_t {
|
|||||||
Osc1Frequency,
|
Osc1Frequency,
|
||||||
Osc1WaveSelector1,
|
Osc1WaveSelector1,
|
||||||
Osc1WaveSelector2,
|
Osc1WaveSelector2,
|
||||||
|
MasterOctaveOffset,
|
||||||
|
MasterSemitoneOffset,
|
||||||
|
MasterPitchOffset,
|
||||||
Osc1OctaveOffset,
|
Osc1OctaveOffset,
|
||||||
Osc1SemitoneOffset,
|
Osc1SemitoneOffset,
|
||||||
Osc1PitchOffset,
|
Osc1PitchOffset,
|
||||||
@@ -69,12 +72,13 @@ struct ParamDefault {
|
|||||||
float max;
|
float max;
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO: make these configurable via yml file too
|
|
||||||
// later TODO: and then when I have full on profile saving there will be a default profile to load from
|
|
||||||
constexpr std::array<ParamDefault, static_cast<size_t>(ParamId::Count)> PARAM_DEFS {{
|
constexpr std::array<ParamDefault, static_cast<size_t>(ParamId::Count)> PARAM_DEFS {{
|
||||||
{ 100.0f, 20.0f, 600.0f}, // Osc1Freq
|
{ 100.0f, 20.0f, 600.0f}, // Osc1Freq
|
||||||
{ 2.0f, 0.0f, 0.0f}, // OscWaveSelector1
|
{ 2.0f, 0.0f, 0.0f}, // OscWaveSelector1
|
||||||
{ 1.0f, 0.0f, 0.0f}, // OscWaveSelector2
|
{ 1.0f, 0.0f, 0.0f}, // OscWaveSelector2
|
||||||
|
{ 0.0f, -5.0f, 5.0f}, // MasterOctaveOffset
|
||||||
|
{ 0.0f, -12.0f, 12.0f}, // MasterSemitoneOffset
|
||||||
|
{ 0.0f, -100.0f, 100.0f}, // MasterPitchOffset
|
||||||
{ 0.0f, -5.0f, 5.0f}, // Osc1OctaveOffset
|
{ 0.0f, -5.0f, 5.0f}, // Osc1OctaveOffset
|
||||||
{ 0.0f, -12.0f, 12.0f}, // Osc1SemitoneOffset
|
{ 0.0f, -12.0f, 12.0f}, // Osc1SemitoneOffset
|
||||||
{ 0.0f, -100.0f, 100.0f}, // Osc1PitchOffset
|
{ 0.0f, -100.0f, 100.0f}, // Osc1PitchOffset
|
||||||
|
|||||||
@@ -35,9 +35,17 @@ bool AudioEngine::start() {
|
|||||||
RtAudio::StreamOptions options;
|
RtAudio::StreamOptions options;
|
||||||
options.flags = RTAUDIO_MINIMIZE_LATENCY;
|
options.flags = RTAUDIO_MINIMIZE_LATENCY;
|
||||||
|
|
||||||
// TODO: error check this please
|
RtAudioErrorType status = audio_.openStream(¶ms, nullptr, RTAUDIO_FLOAT32, sampleRate_, &bufferFrames_, &AudioEngine::audioCallback, this, &options);
|
||||||
audio_.openStream(¶ms, nullptr, RTAUDIO_FLOAT32, sampleRate_, &bufferFrames_, &AudioEngine::audioCallback, this, &options);
|
if(status != RTAUDIO_NO_ERROR) {
|
||||||
audio_.startStream();
|
std::cout << "Error opening RtAudio stream" << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
status = audio_.startStream();
|
||||||
|
if(status != RTAUDIO_NO_ERROR) {
|
||||||
|
std::cout << "Error starting RtAudio stream" << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// sanity check
|
// sanity check
|
||||||
std::cout << "sample rate: " << sampleRate_ << " buffer frames: " << bufferFrames_ << std::endl;
|
std::cout << "sample rate: " << sampleRate_ << " buffer frames: " << bufferFrames_ << std::endl;
|
||||||
|
|||||||
@@ -49,7 +49,8 @@ private:
|
|||||||
ScopeBuffer scope_ { 1024 }; // stores audio samples for visualization
|
ScopeBuffer scope_ { 1024 }; // stores audio samples for visualization
|
||||||
|
|
||||||
RtAudio audio_{AUDIO_API}; // audio device
|
RtAudio audio_{AUDIO_API}; // audio device
|
||||||
// TODO: id like a yml config file or something for these
|
|
||||||
|
// Configurable in the audio.yaml file, assigned defaults if the file is not found
|
||||||
uint32_t sampleRate_ = 44100;
|
uint32_t sampleRate_ = 44100;
|
||||||
uint32_t bufferFrames_ = 512; // time per buffer = BF/SR (256/44100 = 5.8ms)
|
uint32_t bufferFrames_ = 512; // time per buffer = BF/SR (256/44100 = 5.8ms)
|
||||||
uint32_t channels_ = 2; // stereo
|
uint32_t channels_ = 2; // stereo
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
#include "Voice.h"
|
#include "Voice.h"
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <random>
|
||||||
|
|
||||||
Voice::Voice(SmoothedParam* params, WavetableController* wavetable) : params_(params), wavetable_(wavetable) {
|
Voice::Voice(SmoothedParam* params, WavetableController* wavetable) : params_(params), wavetable_(wavetable) {
|
||||||
|
|
||||||
@@ -89,16 +90,22 @@ float Voice::process(float* params, bool& scopeTrigger) {
|
|||||||
|
|
||||||
// calculate the note/pitch of the oscillators
|
// calculate the note/pitch of the oscillators
|
||||||
bool temp = false;
|
bool temp = false;
|
||||||
uint8_t osc1NoteOffset = static_cast<uint8_t>((SYNTH_NOTES_PER_OCTAVE+1) * getParam(ParamId::Osc1OctaveOffset) + getParam(ParamId::Osc1SemitoneOffset));
|
uint8_t osc1NoteOffset = static_cast<uint8_t>(std::round((SYNTH_NOTES_PER_OCTAVE) * (getParam(ParamId::Osc1OctaveOffset) + getParam(ParamId::MasterOctaveOffset)) + getParam(ParamId::Osc1SemitoneOffset) + getParam(ParamId::MasterSemitoneOffset)));
|
||||||
uint8_t osc2NoteOffset = static_cast<uint8_t>((SYNTH_NOTES_PER_OCTAVE+1) * getParam(ParamId::Osc2OctaveOffset) + getParam(ParamId::Osc2SemitoneOffset));
|
uint8_t osc2NoteOffset = static_cast<uint8_t>(std::round((SYNTH_NOTES_PER_OCTAVE) * (getParam(ParamId::Osc2OctaveOffset) + getParam(ParamId::MasterOctaveOffset)) + getParam(ParamId::Osc2SemitoneOffset) + getParam(ParamId::MasterSemitoneOffset)));
|
||||||
uint8_t osc3NoteOffset = static_cast<uint8_t>((SYNTH_NOTES_PER_OCTAVE+1) * getParam(ParamId::Osc3OctaveOffset) + getParam(ParamId::Osc3SemitoneOffset));
|
uint8_t osc3NoteOffset = static_cast<uint8_t>(std::round((SYNTH_NOTES_PER_OCTAVE) * (getParam(ParamId::Osc3OctaveOffset) + getParam(ParamId::MasterOctaveOffset)) + getParam(ParamId::Osc3SemitoneOffset) + getParam(ParamId::MasterSemitoneOffset)));
|
||||||
// sample oscillators
|
// sample oscillators
|
||||||
float osc1 = oscillators_[0].process(osc1NoteOffset + note_, getParam(ParamId::Osc1PitchOffset)/100.0f, scopeTrigger);
|
float osc1 = oscillators_[0].process(osc1NoteOffset + note_, (getParam(ParamId::Osc1PitchOffset) + getParam(ParamId::MasterPitchOffset))/100.0f, scopeTrigger);
|
||||||
float osc2 = oscillators_[1].process(osc2NoteOffset + note_, getParam(ParamId::Osc2PitchOffset)/100.0f, temp);
|
float osc2 = oscillators_[1].process(osc2NoteOffset + note_, (getParam(ParamId::Osc2PitchOffset) + getParam(ParamId::MasterPitchOffset))/100.0f, temp);
|
||||||
float osc3 = oscillators_[2].process(osc3NoteOffset + note_, getParam(ParamId::Osc3PitchOffset)/100.0f, temp);
|
float osc3 = oscillators_[2].process(osc3NoteOffset + note_, (getParam(ParamId::Osc3PitchOffset) + getParam(ParamId::MasterPitchOffset))/100.0f, temp);
|
||||||
|
|
||||||
|
// TODO: implement controls for noise
|
||||||
|
//float scale = static_cast<float>(rand()) / static_cast<float>(RAND_MAX); // Range [0.0, 1.0]
|
||||||
|
//float noise = -1.0f + 2.0f * scale;
|
||||||
|
// these values didn't sound good so I commented them out before I get controls for them
|
||||||
|
|
||||||
// mix oscillators
|
// mix oscillators
|
||||||
float sampleOut = (osc1 + osc2*0.25f + osc3*0.125f) * gain;
|
// TODO: implement gain controls for the other oscillators
|
||||||
|
float sampleOut = (osc1 + osc2*0.4f + osc3*0.15f) * gain; // pre-filtered noise
|
||||||
|
|
||||||
// filter sample
|
// filter sample
|
||||||
float baseFreq = oscillators_[0].frequency();
|
float baseFreq = oscillators_[0].frequency();
|
||||||
@@ -109,5 +116,7 @@ float Voice::process(float* params, bool& scopeTrigger) {
|
|||||||
float filteredSample = filter1_.biquadProcess(sampleOut);
|
float filteredSample = filter1_.biquadProcess(sampleOut);
|
||||||
// sampleOut = filter2_.biquadProcess(sampleOut); // TODO: for some reason second filter is unstable only on windows 🤷
|
// sampleOut = filter2_.biquadProcess(sampleOut); // TODO: for some reason second filter is unstable only on windows 🤷
|
||||||
|
|
||||||
|
//filteredSample += noise*0.125f; // post-filtered noise
|
||||||
|
|
||||||
return filteredSample;
|
return filteredSample;
|
||||||
}
|
}
|
||||||
@@ -12,6 +12,7 @@
|
|||||||
|
|
||||||
// TODO: make configurable
|
// TODO: make configurable
|
||||||
#define SYNTH_OSCILLATOR_COUNT 3
|
#define SYNTH_OSCILLATOR_COUNT 3
|
||||||
|
// if there's different oscillator amounts then we need to be able to dynamically create the ui for each of them
|
||||||
|
|
||||||
struct SmoothedParam {
|
struct SmoothedParam {
|
||||||
float current = 0.0f;
|
float current = 0.0f;
|
||||||
@@ -63,7 +64,6 @@ private:
|
|||||||
// filters
|
// filters
|
||||||
Filter filter1_;
|
Filter filter1_;
|
||||||
Filter filter2_;
|
Filter filter2_;
|
||||||
// TODO: I think the filter's state being uninitialized is what's causing popping when a voice starts for the first time
|
|
||||||
|
|
||||||
// paramstore pointer
|
// paramstore pointer
|
||||||
SmoothedParam* params_;
|
SmoothedParam* params_;
|
||||||
|
|||||||
@@ -17,6 +17,11 @@ void WavetableController::init() {
|
|||||||
|
|
||||||
wavetables_.resize(4); // resize for however many files we find
|
wavetables_.resize(4); // resize for however many files we find
|
||||||
|
|
||||||
|
// don't really know how the files are gonna work
|
||||||
|
// but I'd like two files- a yaml that contains metadata like name, length, range, datatype, etc.
|
||||||
|
// and the main data be just a big array of that data type in a binary file
|
||||||
|
// although having it all in a single bin makes the most sense with the metadata being in the header
|
||||||
|
|
||||||
float phase = 0.0f;
|
float phase = 0.0f;
|
||||||
float phaseInc = 2.0f * M_PI / static_cast<float>(SYNTH_WAVETABLE_SIZE);
|
float phaseInc = 2.0f * M_PI / static_cast<float>(SYNTH_WAVETABLE_SIZE);
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ MainWindow::MainWindow(QWidget *parent) :
|
|||||||
ui_(new Ui::MainWindow),
|
ui_(new Ui::MainWindow),
|
||||||
config_(ConfigInterface(¶ms_)),
|
config_(ConfigInterface(¶ms_)),
|
||||||
audio_(new AudioEngine(&config_, ¶ms_)),
|
audio_(new AudioEngine(&config_, ¶ms_)),
|
||||||
keyboard_(audio_->noteQueue()),
|
keyboard_(audio_->noteQueue(), &config_),
|
||||||
midi_(audio_->noteQueue()) {
|
midi_(audio_->noteQueue()) {
|
||||||
|
|
||||||
// initialize ui
|
// initialize ui
|
||||||
@@ -49,6 +49,67 @@ MainWindow::MainWindow(QWidget *parent) :
|
|||||||
audio_->parameters()->set(ParamId::Osc1WaveSelector2, index);
|
audio_->parameters()->set(ParamId::Osc1WaveSelector2, index);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// rogue sliders, TODO: clean these up in a package
|
||||||
|
connect(ui_->sliderMasterOctave, &SmartSlider::valueChanged,
|
||||||
|
this, [this](float value) {
|
||||||
|
audio_->parameters()->set(ParamId::MasterOctaveOffset, value);
|
||||||
|
ui_->sliderMasterOctave->setResolution();
|
||||||
|
});
|
||||||
|
connect(ui_->sliderMasterSemitone, &SmartSlider::valueChanged,
|
||||||
|
this, [this](float value) {
|
||||||
|
audio_->parameters()->set(ParamId::MasterSemitoneOffset, value);
|
||||||
|
ui_->sliderMasterSemitone->setResolution();
|
||||||
|
});
|
||||||
|
connect(ui_->sliderMasterPitch, &SmartSlider::valueChanged,
|
||||||
|
this, [this](float value) {
|
||||||
|
audio_->parameters()->set(ParamId::MasterPitchOffset, value);
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(ui_->sliderOsc1Octave, &SmartSlider::valueChanged,
|
||||||
|
this, [this](float value) {
|
||||||
|
audio_->parameters()->set(ParamId::Osc1OctaveOffset, value);
|
||||||
|
ui_->sliderOsc1Octave->setResolution();
|
||||||
|
});
|
||||||
|
connect(ui_->sliderOsc1Semitone, &SmartSlider::valueChanged,
|
||||||
|
this, [this](float value) {
|
||||||
|
audio_->parameters()->set(ParamId::Osc1SemitoneOffset, value);
|
||||||
|
ui_->sliderOsc1Semitone->setResolution();
|
||||||
|
});
|
||||||
|
connect(ui_->sliderOsc1Pitch, &SmartSlider::valueChanged,
|
||||||
|
this, [this](float value) {
|
||||||
|
audio_->parameters()->set(ParamId::Osc1PitchOffset, value);
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(ui_->sliderOsc2Octave, &SmartSlider::valueChanged,
|
||||||
|
this, [this](float value) {
|
||||||
|
audio_->parameters()->set(ParamId::Osc2OctaveOffset, value);
|
||||||
|
ui_->sliderOsc2Octave->setResolution();
|
||||||
|
});
|
||||||
|
connect(ui_->sliderOsc2Semitone, &SmartSlider::valueChanged,
|
||||||
|
this, [this](float value) {
|
||||||
|
audio_->parameters()->set(ParamId::Osc2SemitoneOffset, value);
|
||||||
|
ui_->sliderOsc2Semitone->setResolution();
|
||||||
|
});
|
||||||
|
connect(ui_->sliderOsc2Pitch, &SmartSlider::valueChanged,
|
||||||
|
this, [this](float value) {
|
||||||
|
audio_->parameters()->set(ParamId::Osc2PitchOffset, value);
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(ui_->sliderOsc3Octave, &SmartSlider::valueChanged,
|
||||||
|
this, [this](float value) {
|
||||||
|
audio_->parameters()->set(ParamId::Osc3OctaveOffset, value);
|
||||||
|
ui_->sliderOsc3Octave->setResolution();
|
||||||
|
});
|
||||||
|
connect(ui_->sliderOsc3Semitone, &SmartSlider::valueChanged,
|
||||||
|
this, [this](float value) {
|
||||||
|
audio_->parameters()->set(ParamId::Osc3SemitoneOffset, value);
|
||||||
|
ui_->sliderOsc3Semitone->setResolution();
|
||||||
|
});
|
||||||
|
connect(ui_->sliderOsc3Pitch, &SmartSlider::valueChanged,
|
||||||
|
this, [this](float value) {
|
||||||
|
audio_->parameters()->set(ParamId::Osc3PitchOffset, value);
|
||||||
|
});
|
||||||
|
|
||||||
// synth business
|
// synth business
|
||||||
audio_->start();
|
audio_->start();
|
||||||
|
|
||||||
@@ -75,14 +136,60 @@ void MainWindow::onResetClicked() {
|
|||||||
|
|
||||||
// initialize to defaults
|
// initialize to defaults
|
||||||
|
|
||||||
config_.loadProfile("default");
|
YAML::Node configRoot = config_.loadProfile("default");
|
||||||
|
|
||||||
// update ui from the paramstore
|
// update ui from the paramstore
|
||||||
ui_->envelopeOsc1Volume->init(EnvelopeId::Osc1Volume, config_.loadEnvProfile("default", "Osc1Volume"));
|
ui_->envelopeOsc1Volume->init(EnvelopeId::Osc1Volume, config_.loadEnvProfile("default", "Osc1Volume"));
|
||||||
ui_->envelopeFilterCutoff->init(EnvelopeId::FilterCutoff, config_.loadEnvProfile("default", "FilterCutoff"));
|
ui_->envelopeFilterCutoff->init(EnvelopeId::FilterCutoff, config_.loadEnvProfile("default", "FilterCutoff"));
|
||||||
ui_->envelopeFilterResonance->init(EnvelopeId::FilterResonance, config_.loadEnvProfile("default", "FilterResonance"));
|
ui_->envelopeFilterResonance->init(EnvelopeId::FilterResonance, config_.loadEnvProfile("default", "FilterResonance"));
|
||||||
|
|
||||||
ui_->comboOsc1WaveSelector1->setCurrentIndex(static_cast<int>(PARAM_DEFS[static_cast<size_t>(ParamId::Osc1WaveSelector1)].def));
|
// TODO: clean these up, maybe put them in a package like the envelope generators (it'll help encapsulate the int-snapping business)
|
||||||
ui_->comboOsc1WaveSelector2->setCurrentIndex(static_cast<int>(PARAM_DEFS[static_cast<size_t>(ParamId::Osc1WaveSelector2)].def));
|
// what I might do is make a variable-length slider-package object
|
||||||
|
ui_->sliderMasterOctave->setResolution(configRoot["MasterOctaveOffset"][2].as<int>() - configRoot["MasterOctaveOffset"][1].as<int>());
|
||||||
|
ui_->sliderMasterOctave->setRange(configRoot["MasterOctaveOffset"][1].as<int>(), configRoot["MasterOctaveOffset"][2].as<int>());
|
||||||
|
ui_->sliderMasterOctave->setValue(configRoot["MasterOctaveOffset"][0].as<int>());
|
||||||
|
|
||||||
|
ui_->sliderMasterSemitone->setResolution(configRoot["MasterSemitoneOffset"][2].as<int>() - configRoot["MasterSemitoneOffset"][1].as<int>());
|
||||||
|
ui_->sliderMasterSemitone->setRange(configRoot["MasterSemitoneOffset"][1].as<int>(), configRoot["MasterSemitoneOffset"][2].as<int>());
|
||||||
|
ui_->sliderMasterSemitone->setValue(configRoot["MasterSemitoneOffset"][0].as<int>());
|
||||||
|
|
||||||
|
ui_->sliderMasterPitch->setRange(configRoot["MasterPitchOffset"][1].as<float>(), configRoot["MasterPitchOffset"][2].as<float>());
|
||||||
|
ui_->sliderMasterPitch->setValue(configRoot["MasterPitchOffset"][0].as<float>());
|
||||||
|
|
||||||
|
ui_->sliderOsc1Octave->setResolution(configRoot["Osc1OctaveOffset"][2].as<int>() - configRoot["Osc1OctaveOffset"][1].as<int>());
|
||||||
|
ui_->sliderOsc1Octave->setRange(configRoot["Osc1OctaveOffset"][1].as<int>(), configRoot["Osc1OctaveOffset"][2].as<int>());
|
||||||
|
ui_->sliderOsc1Octave->setValue(configRoot["Osc1OctaveOffset"][0].as<int>());
|
||||||
|
|
||||||
|
ui_->sliderOsc1Semitone->setResolution(configRoot["Osc1SemitoneOffset"][2].as<int>() - configRoot["Osc1SemitoneOffset"][1].as<int>());
|
||||||
|
ui_->sliderOsc1Semitone->setRange(configRoot["Osc1SemitoneOffset"][1].as<int>(), configRoot["Osc1SemitoneOffset"][2].as<int>());
|
||||||
|
ui_->sliderOsc1Semitone->setValue(configRoot["Osc1SemitoneOffset"][0].as<int>());
|
||||||
|
|
||||||
|
ui_->sliderOsc1Pitch->setRange(configRoot["Osc1PitchOffset"][1].as<float>(), configRoot["Osc1PitchOffset"][2].as<float>());
|
||||||
|
ui_->sliderOsc1Pitch->setValue(configRoot["Osc1PitchOffset"][0].as<float>());
|
||||||
|
|
||||||
|
ui_->sliderOsc2Octave->setResolution(configRoot["Osc2OctaveOffset"][2].as<int>() - configRoot["Osc2OctaveOffset"][1].as<int>());
|
||||||
|
ui_->sliderOsc2Octave->setRange(configRoot["Osc2OctaveOffset"][1].as<int>(), configRoot["Osc2OctaveOffset"][2].as<int>());
|
||||||
|
ui_->sliderOsc2Octave->setValue(configRoot["Osc2OctaveOffset"][0].as<int>());
|
||||||
|
|
||||||
|
ui_->sliderOsc2Semitone->setResolution(configRoot["Osc2SemitoneOffset"][2].as<int>() - configRoot["Osc2SemitoneOffset"][1].as<int>());
|
||||||
|
ui_->sliderOsc2Semitone->setRange(configRoot["Osc2SemitoneOffset"][1].as<int>(), configRoot["Osc2SemitoneOffset"][2].as<int>());
|
||||||
|
ui_->sliderOsc2Semitone->setValue(configRoot["Osc2SemitoneOffset"][0].as<int>());
|
||||||
|
|
||||||
|
ui_->sliderOsc2Pitch->setRange(configRoot["Osc2PitchOffset"][1].as<float>(), configRoot["Osc2PitchOffset"][2].as<float>());
|
||||||
|
ui_->sliderOsc2Pitch->setValue(configRoot["Osc2PitchOffset"][0].as<float>());
|
||||||
|
|
||||||
|
ui_->sliderOsc3Octave->setResolution(configRoot["Osc3OctaveOffset"][2].as<int>() - configRoot["Osc3OctaveOffset"][1].as<int>());
|
||||||
|
ui_->sliderOsc3Octave->setRange(configRoot["Osc3OctaveOffset"][1].as<int>(), configRoot["Osc3OctaveOffset"][2].as<int>());
|
||||||
|
ui_->sliderOsc3Octave->setValue(configRoot["Osc3OctaveOffset"][0].as<int>());
|
||||||
|
|
||||||
|
ui_->sliderOsc3Semitone->setResolution(configRoot["Osc3SemitoneOffset"][2].as<int>() - configRoot["Osc3SemitoneOffset"][1].as<int>());
|
||||||
|
ui_->sliderOsc3Semitone->setRange(configRoot["Osc3SemitoneOffset"][1].as<int>(), configRoot["Osc3SemitoneOffset"][2].as<int>());
|
||||||
|
ui_->sliderOsc3Semitone->setValue(configRoot["Osc3SemitoneOffset"][0].as<int>());
|
||||||
|
|
||||||
|
ui_->sliderOsc3Pitch->setRange(configRoot["Osc3PitchOffset"][1].as<float>(), configRoot["Osc3PitchOffset"][2].as<float>());
|
||||||
|
ui_->sliderOsc3Pitch->setValue(configRoot["Osc3PitchOffset"][0].as<float>());
|
||||||
|
|
||||||
|
ui_->comboOsc1WaveSelector1->setCurrentIndex(configRoot["OscWaveSelector1"].as<int>());
|
||||||
|
ui_->comboOsc1WaveSelector2->setCurrentIndex(configRoot["OscWaveSelector2"].as<int>());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,8 +6,8 @@
|
|||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>1102</width>
|
<width>1100</width>
|
||||||
<height>564</height>
|
<height>900</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="windowTitle">
|
<property name="windowTitle">
|
||||||
@@ -261,6 +261,602 @@
|
|||||||
<set>Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter</set>
|
<set>Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter</set>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
|
<widget class="Line" name="line_3">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>340</x>
|
||||||
|
<y>880</y>
|
||||||
|
<width>211</width>
|
||||||
|
<height>20</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Orientation::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="SmartSlider" name="sliderOsc1Semitone" native="true">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>410</x>
|
||||||
|
<y>610</y>
|
||||||
|
<width>65</width>
|
||||||
|
<height>280</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="autoFillBackground">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="QLabel" name="label_8">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>490</x>
|
||||||
|
<y>580</y>
|
||||||
|
<width>41</width>
|
||||||
|
<height>31</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<pointsize>12</pointsize>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Pitch</string>
|
||||||
|
</property>
|
||||||
|
<property name="alignment">
|
||||||
|
<set>Qt::AlignmentFlag::AlignCenter</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="SmartSlider" name="sliderOsc1Octave" native="true">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>340</x>
|
||||||
|
<y>610</y>
|
||||||
|
<width>65</width>
|
||||||
|
<height>280</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="autoFillBackground">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="QLabel" name="label_9">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>410</x>
|
||||||
|
<y>580</y>
|
||||||
|
<width>71</width>
|
||||||
|
<height>31</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<pointsize>12</pointsize>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Semitone</string>
|
||||||
|
</property>
|
||||||
|
<property name="alignment">
|
||||||
|
<set>Qt::AlignmentFlag::AlignCenter</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="Line" name="line_4">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>340</x>
|
||||||
|
<y>600</y>
|
||||||
|
<width>211</width>
|
||||||
|
<height>20</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Orientation::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="SmartSlider" name="sliderOsc1Pitch" native="true">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>480</x>
|
||||||
|
<y>610</y>
|
||||||
|
<width>65</width>
|
||||||
|
<height>280</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="autoFillBackground">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="QLabel" name="label_6">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>340</x>
|
||||||
|
<y>580</y>
|
||||||
|
<width>71</width>
|
||||||
|
<height>31</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<pointsize>12</pointsize>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Octave</string>
|
||||||
|
</property>
|
||||||
|
<property name="alignment">
|
||||||
|
<set>Qt::AlignmentFlag::AlignCenter</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="QLabel" name="label_10">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>630</x>
|
||||||
|
<y>580</y>
|
||||||
|
<width>71</width>
|
||||||
|
<height>31</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<pointsize>12</pointsize>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Semitone</string>
|
||||||
|
</property>
|
||||||
|
<property name="alignment">
|
||||||
|
<set>Qt::AlignmentFlag::AlignCenter</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="Line" name="line_5">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>560</x>
|
||||||
|
<y>600</y>
|
||||||
|
<width>211</width>
|
||||||
|
<height>20</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Orientation::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="QLabel" name="label_7">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>560</x>
|
||||||
|
<y>580</y>
|
||||||
|
<width>71</width>
|
||||||
|
<height>31</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<pointsize>12</pointsize>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Octave</string>
|
||||||
|
</property>
|
||||||
|
<property name="alignment">
|
||||||
|
<set>Qt::AlignmentFlag::AlignCenter</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="Line" name="line_6">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>560</x>
|
||||||
|
<y>880</y>
|
||||||
|
<width>211</width>
|
||||||
|
<height>20</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Orientation::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="SmartSlider" name="sliderOsc2Semitone" native="true">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>630</x>
|
||||||
|
<y>610</y>
|
||||||
|
<width>65</width>
|
||||||
|
<height>280</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="autoFillBackground">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="QLabel" name="label_11">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>710</x>
|
||||||
|
<y>580</y>
|
||||||
|
<width>41</width>
|
||||||
|
<height>31</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<pointsize>12</pointsize>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Pitch</string>
|
||||||
|
</property>
|
||||||
|
<property name="alignment">
|
||||||
|
<set>Qt::AlignmentFlag::AlignCenter</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="SmartSlider" name="sliderOsc2Octave" native="true">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>560</x>
|
||||||
|
<y>610</y>
|
||||||
|
<width>65</width>
|
||||||
|
<height>280</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="autoFillBackground">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="SmartSlider" name="sliderOsc2Pitch" native="true">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>700</x>
|
||||||
|
<y>610</y>
|
||||||
|
<width>65</width>
|
||||||
|
<height>280</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="autoFillBackground">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="SmartSlider" name="sliderOsc3Semitone" native="true">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>850</x>
|
||||||
|
<y>610</y>
|
||||||
|
<width>65</width>
|
||||||
|
<height>280</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="autoFillBackground">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="SmartSlider" name="sliderOsc3Pitch" native="true">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>920</x>
|
||||||
|
<y>610</y>
|
||||||
|
<width>65</width>
|
||||||
|
<height>280</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="autoFillBackground">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="QLabel" name="label_12">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>780</x>
|
||||||
|
<y>580</y>
|
||||||
|
<width>71</width>
|
||||||
|
<height>31</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<pointsize>12</pointsize>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Octave</string>
|
||||||
|
</property>
|
||||||
|
<property name="alignment">
|
||||||
|
<set>Qt::AlignmentFlag::AlignCenter</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="SmartSlider" name="sliderOsc3Octave" native="true">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>780</x>
|
||||||
|
<y>610</y>
|
||||||
|
<width>65</width>
|
||||||
|
<height>280</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="autoFillBackground">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="QLabel" name="label_13">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>930</x>
|
||||||
|
<y>580</y>
|
||||||
|
<width>41</width>
|
||||||
|
<height>31</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<pointsize>12</pointsize>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Pitch</string>
|
||||||
|
</property>
|
||||||
|
<property name="alignment">
|
||||||
|
<set>Qt::AlignmentFlag::AlignCenter</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="Line" name="line_7">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>780</x>
|
||||||
|
<y>600</y>
|
||||||
|
<width>211</width>
|
||||||
|
<height>20</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Orientation::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="Line" name="line_8">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>780</x>
|
||||||
|
<y>880</y>
|
||||||
|
<width>211</width>
|
||||||
|
<height>20</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Orientation::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="QLabel" name="label_14">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>850</x>
|
||||||
|
<y>580</y>
|
||||||
|
<width>71</width>
|
||||||
|
<height>31</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<pointsize>12</pointsize>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Semitone</string>
|
||||||
|
</property>
|
||||||
|
<property name="alignment">
|
||||||
|
<set>Qt::AlignmentFlag::AlignCenter</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="QLabel" name="label_15">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>390</x>
|
||||||
|
<y>567</y>
|
||||||
|
<width>111</width>
|
||||||
|
<height>20</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<pointsize>12</pointsize>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Oscillator 1</string>
|
||||||
|
</property>
|
||||||
|
<property name="alignment">
|
||||||
|
<set>Qt::AlignmentFlag::AlignCenter</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="QLabel" name="label_16">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>611</x>
|
||||||
|
<y>567</y>
|
||||||
|
<width>111</width>
|
||||||
|
<height>20</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<pointsize>12</pointsize>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Oscillator 2</string>
|
||||||
|
</property>
|
||||||
|
<property name="alignment">
|
||||||
|
<set>Qt::AlignmentFlag::AlignCenter</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="QLabel" name="label_17">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>830</x>
|
||||||
|
<y>569</y>
|
||||||
|
<width>111</width>
|
||||||
|
<height>20</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<pointsize>12</pointsize>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Oscillator 3</string>
|
||||||
|
</property>
|
||||||
|
<property name="alignment">
|
||||||
|
<set>Qt::AlignmentFlag::AlignCenter</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="Line" name="line_9">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>120</x>
|
||||||
|
<y>880</y>
|
||||||
|
<width>211</width>
|
||||||
|
<height>20</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Orientation::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="QLabel" name="label_18">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>170</x>
|
||||||
|
<y>567</y>
|
||||||
|
<width>111</width>
|
||||||
|
<height>20</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<pointsize>12</pointsize>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Master</string>
|
||||||
|
</property>
|
||||||
|
<property name="alignment">
|
||||||
|
<set>Qt::AlignmentFlag::AlignCenter</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="QLabel" name="label_19">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>270</x>
|
||||||
|
<y>580</y>
|
||||||
|
<width>41</width>
|
||||||
|
<height>31</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<pointsize>12</pointsize>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Pitch</string>
|
||||||
|
</property>
|
||||||
|
<property name="alignment">
|
||||||
|
<set>Qt::AlignmentFlag::AlignCenter</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="SmartSlider" name="sliderMasterOctave" native="true">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>120</x>
|
||||||
|
<y>610</y>
|
||||||
|
<width>65</width>
|
||||||
|
<height>280</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="autoFillBackground">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="SmartSlider" name="sliderMasterPitch" native="true">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>260</x>
|
||||||
|
<y>610</y>
|
||||||
|
<width>65</width>
|
||||||
|
<height>280</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="autoFillBackground">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="Line" name="line_10">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>120</x>
|
||||||
|
<y>600</y>
|
||||||
|
<width>211</width>
|
||||||
|
<height>20</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Orientation::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="QLabel" name="label_20">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>190</x>
|
||||||
|
<y>580</y>
|
||||||
|
<width>71</width>
|
||||||
|
<height>31</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<pointsize>12</pointsize>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Semitone</string>
|
||||||
|
</property>
|
||||||
|
<property name="alignment">
|
||||||
|
<set>Qt::AlignmentFlag::AlignCenter</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="SmartSlider" name="sliderMasterSemitone" native="true">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>190</x>
|
||||||
|
<y>610</y>
|
||||||
|
<width>65</width>
|
||||||
|
<height>280</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="autoFillBackground">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="QLabel" name="label_21">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>120</x>
|
||||||
|
<y>580</y>
|
||||||
|
<width>71</width>
|
||||||
|
<height>31</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<pointsize>12</pointsize>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Octave</string>
|
||||||
|
</property>
|
||||||
|
<property name="alignment">
|
||||||
|
<set>Qt::AlignmentFlag::AlignCenter</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
</widget>
|
</widget>
|
||||||
</widget>
|
</widget>
|
||||||
<customwidgets>
|
<customwidgets>
|
||||||
@@ -276,6 +872,12 @@
|
|||||||
<header>Scope/Scope.h</header>
|
<header>Scope/Scope.h</header>
|
||||||
<container>1</container>
|
<container>1</container>
|
||||||
</customwidget>
|
</customwidget>
|
||||||
|
<customwidget>
|
||||||
|
<class>SmartSlider</class>
|
||||||
|
<extends>QWidget</extends>
|
||||||
|
<header>SmartSlider/SmartSlider.h</header>
|
||||||
|
<container>1</container>
|
||||||
|
</customwidget>
|
||||||
</customwidgets>
|
</customwidgets>
|
||||||
<resources/>
|
<resources/>
|
||||||
<connections/>
|
<connections/>
|
||||||
|
|||||||
@@ -75,24 +75,6 @@ void EnvelopeGenerator::emitEnvelope() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void EnvelopeGenerator::init(EnvelopeId id) {
|
|
||||||
|
|
||||||
EnvelopeParam params = ENV_PARAMS[static_cast<size_t>(id)];
|
|
||||||
|
|
||||||
ui_->sliderDepth->setRange(PARAM_DEFS[static_cast<size_t>(params.depth)].min, PARAM_DEFS[static_cast<size_t>(params.depth)].max);
|
|
||||||
ui_->sliderAttack->setRange(PARAM_DEFS[static_cast<size_t>(params.a)].min, PARAM_DEFS[static_cast<size_t>(params.a)].max);
|
|
||||||
ui_->sliderDecay->setRange(PARAM_DEFS[static_cast<size_t>(params.d)].min, PARAM_DEFS[static_cast<size_t>(params.d)].max);
|
|
||||||
ui_->sliderSustain->setRange(PARAM_DEFS[static_cast<size_t>(params.s)].min, PARAM_DEFS[static_cast<size_t>(params.s)].max);
|
|
||||||
ui_->sliderRelease->setRange(PARAM_DEFS[static_cast<size_t>(params.r)].min, PARAM_DEFS[static_cast<size_t>(params.r)].max);
|
|
||||||
|
|
||||||
setDepth(PARAM_DEFS[static_cast<size_t>(params.depth)].def);
|
|
||||||
setAttack(PARAM_DEFS[static_cast<size_t>(params.a)].def);
|
|
||||||
setDecay(PARAM_DEFS[static_cast<size_t>(params.d)].def);
|
|
||||||
setSustain(PARAM_DEFS[static_cast<size_t>(params.s)].def);
|
|
||||||
setRelease(PARAM_DEFS[static_cast<size_t>(params.r)].def);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void EnvelopeGenerator::init(EnvelopeId id, std::array<ParamDefault, 5> profile) {
|
void EnvelopeGenerator::init(EnvelopeId id, std::array<ParamDefault, 5> profile) {
|
||||||
|
|
||||||
EnvelopeParam params = ENV_PARAMS[static_cast<size_t>(id)];
|
EnvelopeParam params = ENV_PARAMS[static_cast<size_t>(id)];
|
||||||
|
|||||||
@@ -16,8 +16,7 @@ public:
|
|||||||
explicit EnvelopeGenerator(QWidget* parent = nullptr);
|
explicit EnvelopeGenerator(QWidget* parent = nullptr);
|
||||||
~EnvelopeGenerator();
|
~EnvelopeGenerator();
|
||||||
|
|
||||||
// connects signals, sets parameters to the defaults defined in paramStore
|
// connects signals, sets parameters to a provided profile
|
||||||
void init(EnvelopeId id);
|
|
||||||
void init(EnvelopeId id, std::array<ParamDefault, 5> profile);
|
void init(EnvelopeId id, std::array<ParamDefault, 5> profile);
|
||||||
|
|
||||||
// setters
|
// setters
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ void SmartSlider::setRange(float min, float max) {
|
|||||||
|
|
||||||
ui_->slider->setValue(sliderValue);
|
ui_->slider->setValue(sliderValue);
|
||||||
ui_->spinValue->setValue(value);
|
ui_->spinValue->setValue(value);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// sets value of the slider and the spinBox, called by other classes
|
// sets value of the slider and the spinBox, called by other classes
|
||||||
@@ -57,6 +58,10 @@ void SmartSlider::setValue(float value) {
|
|||||||
emit valueChanged(value);
|
emit valueChanged(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SmartSlider::setResolution(int resolution) {
|
||||||
|
sliderResolution_ = resolution;
|
||||||
|
}
|
||||||
|
|
||||||
float SmartSlider::value() {
|
float SmartSlider::value() {
|
||||||
return ui_->spinValue->value();
|
return ui_->spinValue->value();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ QT_END_NAMESPACE
|
|||||||
|
|
||||||
// SmartSlider is the widget including a slider, min/max settings, and a value setting parameter
|
// SmartSlider is the widget including a slider, min/max settings, and a value setting parameter
|
||||||
class SmartSlider : public QWidget {
|
class SmartSlider : public QWidget {
|
||||||
|
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@@ -18,6 +19,8 @@ public:
|
|||||||
// setters
|
// setters
|
||||||
void setRange(float min, float max);
|
void setRange(float min, float max);
|
||||||
void setValue(float value);
|
void setValue(float value);
|
||||||
|
void setResolution(int resolution);
|
||||||
|
void setResolution() { setResolution(static_cast<int>(max_ - min_)); }
|
||||||
|
|
||||||
// getters
|
// getters
|
||||||
float value();
|
float value();
|
||||||
|
|||||||
@@ -19,6 +19,9 @@
|
|||||||
<property name="windowTitle">
|
<property name="windowTitle">
|
||||||
<string>Form</string>
|
<string>Form</string>
|
||||||
</property>
|
</property>
|
||||||
|
<property name="isInt" stdset="0">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
<widget class="QSlider" name="slider">
|
<widget class="QSlider" name="slider">
|
||||||
<property name="geometry">
|
<property name="geometry">
|
||||||
<rect>
|
<rect>
|
||||||
@@ -62,6 +65,12 @@
|
|||||||
<property name="buttonSymbols">
|
<property name="buttonSymbols">
|
||||||
<enum>QAbstractSpinBox::ButtonSymbols::NoButtons</enum>
|
<enum>QAbstractSpinBox::ButtonSymbols::NoButtons</enum>
|
||||||
</property>
|
</property>
|
||||||
|
<property name="minimum">
|
||||||
|
<double>-100000.000000000000000</double>
|
||||||
|
</property>
|
||||||
|
<property name="maximum">
|
||||||
|
<double>100000.000000000000000</double>
|
||||||
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
<widget class="QDoubleSpinBox" name="spinMax">
|
<widget class="QDoubleSpinBox" name="spinMax">
|
||||||
<property name="geometry">
|
<property name="geometry">
|
||||||
@@ -84,8 +93,11 @@
|
|||||||
<property name="buttonSymbols">
|
<property name="buttonSymbols">
|
||||||
<enum>QAbstractSpinBox::ButtonSymbols::NoButtons</enum>
|
<enum>QAbstractSpinBox::ButtonSymbols::NoButtons</enum>
|
||||||
</property>
|
</property>
|
||||||
|
<property name="minimum">
|
||||||
|
<double>-100000.000000000000000</double>
|
||||||
|
</property>
|
||||||
<property name="maximum">
|
<property name="maximum">
|
||||||
<double>40000.000000000000000</double>
|
<double>100000.000000000000000</double>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
<widget class="QDoubleSpinBox" name="spinValue">
|
<widget class="QDoubleSpinBox" name="spinValue">
|
||||||
@@ -117,8 +129,11 @@
|
|||||||
<property name="buttonSymbols">
|
<property name="buttonSymbols">
|
||||||
<enum>QAbstractSpinBox::ButtonSymbols::NoButtons</enum>
|
<enum>QAbstractSpinBox::ButtonSymbols::NoButtons</enum>
|
||||||
</property>
|
</property>
|
||||||
|
<property name="minimum">
|
||||||
|
<double>-100000.000000000000000</double>
|
||||||
|
</property>
|
||||||
<property name="maximum">
|
<property name="maximum">
|
||||||
<double>80000.000000000000000</double>
|
<double>100000.000000000000000</double>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
<widget class="Line" name="line">
|
<widget class="Line" name="line">
|
||||||
|
|||||||
Reference in New Issue
Block a user