--------------------------------------------------------------------------------
+0.15.2 ---
+- New sample-accurate Action Editor
+- New MIDI Velocity Editor widget
+- Ability to move MIDI events vertically in piano roll (i.e. change note)
+- Remove mute action recording
+- Better handling of MIDI devices that send NOTEON + velocity 0 as NOTEOFF
+- Avoid calls to deprecated JUCE plug-ins methods
+- Removed useless pthreadGC2.dll from Windows package
+- Can't kill MIDI channels (fix #197)
+- Can't record MIDI actions (fix #202)
+- Fix missing first beat on metronome rendering
+- Fix crash on opening plug-in window on macOS
+
+
0.15.1 --- 2018 . 07 . 03
- Deep code refactoring, featuring Channels processors
- Many new unit tests added
sourcesCore = \
src/core/const.h \
src/core/types.h \
+ src/core/range.h \
src/core/channel.h \
src/core/channel.cpp \
src/core/sampleChannel.h \
src/gui/dialogs/sampleEditor.cpp \
src/gui/dialogs/pluginWindowGUI.h \
src/gui/dialogs/pluginWindowGUI.cpp \
- src/gui/dialogs/gd_actionEditor.h \
- src/gui/dialogs/gd_actionEditor.cpp \
src/gui/dialogs/pluginChooser.h \
src/gui/dialogs/pluginChooser.cpp \
- src/gui/dialogs/browser/browserBase.h \
- src/gui/dialogs/browser/browserBase.cpp \
- src/gui/dialogs/browser/browserDir.h \
- src/gui/dialogs/browser/browserDir.cpp \
- src/gui/dialogs/browser/browserLoad.h \
- src/gui/dialogs/browser/browserLoad.cpp \
- src/gui/dialogs/browser/browserSave.h \
- src/gui/dialogs/browser/browserSave.cpp \
+ src/gui/dialogs/actionEditor/baseActionEditor.h \
+ src/gui/dialogs/actionEditor/baseActionEditor.cpp \
+ src/gui/dialogs/actionEditor/sampleActionEditor.h \
+ src/gui/dialogs/actionEditor/sampleActionEditor.cpp \
+ src/gui/dialogs/actionEditor/midiActionEditor.h \
+ src/gui/dialogs/actionEditor/midiActionEditor.cpp \
+ src/gui/dialogs/browser/browserBase.h \
+ src/gui/dialogs/browser/browserBase.cpp \
+ src/gui/dialogs/browser/browserDir.h \
+ src/gui/dialogs/browser/browserDir.cpp \
+ src/gui/dialogs/browser/browserLoad.h \
+ src/gui/dialogs/browser/browserLoad.cpp \
+ src/gui/dialogs/browser/browserSave.h \
+ src/gui/dialogs/browser/browserSave.cpp \
src/gui/dialogs/midiIO/midiOutputBase.h \
src/gui/dialogs/midiIO/midiOutputBase.cpp \
src/gui/dialogs/midiIO/midiOutputSampleCh.h \
src/gui/elems/sampleEditor/shiftTool.cpp \
src/gui/elems/actionEditor/baseActionEditor.h \
src/gui/elems/actionEditor/baseActionEditor.cpp \
+ src/gui/elems/actionEditor/baseAction.h \
+ src/gui/elems/actionEditor/baseAction.cpp \
src/gui/elems/actionEditor/envelopeEditor.h \
src/gui/elems/actionEditor/envelopeEditor.cpp \
+ src/gui/elems/actionEditor/velocityEditor.h \
+ src/gui/elems/actionEditor/velocityEditor.cpp \
+ src/gui/elems/actionEditor/envelopePoint.h \
+ src/gui/elems/actionEditor/envelopePoint.cpp \
src/gui/elems/actionEditor/pianoRoll.h \
src/gui/elems/actionEditor/pianoRoll.cpp \
src/gui/elems/actionEditor/noteEditor.h \
src/gui/elems/actionEditor/noteEditor.cpp \
- src/gui/elems/actionEditor/basePianoItem.h \
- src/gui/elems/actionEditor/basePianoItem.cpp \
src/gui/elems/actionEditor/pianoItem.h \
src/gui/elems/actionEditor/pianoItem.cpp \
- src/gui/elems/actionEditor/pianoItemOrphaned.h \
- src/gui/elems/actionEditor/pianoItemOrphaned.cpp \
- src/gui/elems/actionEditor/muteEditor.h \
- src/gui/elems/actionEditor/muteEditor.cpp \
- src/gui/elems/actionEditor/actionEditor.h \
- src/gui/elems/actionEditor/actionEditor.cpp \
- src/gui/elems/actionEditor/action.h \
- src/gui/elems/actionEditor/action.cpp \
+ src/gui/elems/actionEditor/sampleActionEditor.h \
+ src/gui/elems/actionEditor/sampleActionEditor.cpp \
+ src/gui/elems/actionEditor/sampleAction.h \
+ src/gui/elems/actionEditor/sampleAction.cpp \
src/gui/elems/actionEditor/gridTool.h \
src/gui/elems/actionEditor/gridTool.cpp \
src/gui/elems/mainWindow/mainIO.h \
## Documentation
-Docs are available online on the official website:
-
-http://www.giadamusic.com/documentation
+Docs are available online on [the official website](https://www.giadamusic.com/documentation-index).
Found a typo or a terrible mistake? Feel free to clone the [website repository](https://github.com/monocasual/giada-www) and send us your pull requests.
## Build Giada from source
-We do our best to make the compilation process as simple as possible. You can find all the information in the [official docs page](http://giadamusic.com/documentation/show/compiling-from-source).
+We do our best to make the compilation process as simple as possible. You can find all the information in the [official docs page](https://www.giadamusic.com/documentation-compiling-from-source).
Something went wrong? Try our new [Docker image](https://github.com/monocasual/giada-docker) for building and running Giada without hurdles.
## Bugs, requests and questions for non-developers
-Feel free to ask anything on our end-user forum:
-
-http://www.giadamusic.com/forum
+Feel free to ask anything on [our end-user forum](https://www.giadamusic.com/forum).
## Copyright
solo (false),
volume_i (1.0f),
volume_d (0.0f),
- mute_i (false),
hasActions (false),
readActions (false),
midiIn (true),
volume_i = src->volume_i;
volume_d = src->volume_d;
pan = src->pan;
- mute_i = src->mute_i;
mute = src->mute;
solo = src->solo;
hasActions = src->hasActions;
#endif
-#undef Status
-
-
class Plugin;
class MidiMapConf;
class geChannel;
/* set
What to do when channel is un/muted. */
- virtual void setMute(bool value, giada::EventType eventType) = 0;
+ virtual void setMute(bool value) = 0;
/* empty
Frees any associated resources (e.g. waveform for SAMPLE). */
virtual bool hasData() const { return false; };
virtual bool recordStart(bool canQuantize) { return true; };
- virtual bool recordKill() { return false; };
+ virtual bool recordKill() { return true; };
virtual void recordStop() {};
- virtual void recordMute() {};
/* prepareBuffer
Fill audio buffer with audio data from the internal source. This is actually
float volume_i;
float volume_d;
-
- bool mute_i; // internal mute
bool hasActions; // has something recorded
bool readActions; // read what's recorded
if (actionEditorGridVal < 0 || actionEditorGridVal > G_MAX_GRID_VAL) actionEditorGridVal = 0;
if (actionEditorGridOn < 0) actionEditorGridOn = 0;
if (pianoRollH <= 0) pianoRollH = 422;
+ if (sampleActionEditorH <= 0) sampleActionEditorH = 40;
+ if (velocityEditorH <= 0) velocityEditorH = 40;
+ if (envelopeEditorH <= 0) envelopeEditorH = 40;
if (sampleEditorX < 0) sampleEditorX = 0;
if (sampleEditorY < 0) sampleEditorY = 0;
if (sampleEditorW < 500) sampleEditorW = 500;
int midiSystem = 0;
int midiPortOut = G_DEFAULT_MIDI_PORT_OUT;
int midiPortIn = G_DEFAULT_MIDI_PORT_IN;
-bool noNoteOff = false;
string midiMapPath = "";
string lastFileMap = "";
int midiSync = MIDI_SYNC_NONE;
int pianoRollY = -1;
int pianoRollH = 422;
+int sampleActionEditorH = 40;
+int velocityEditorH = 40;
+int envelopeEditorH = 40;
+
int pluginListX = 0;
int pluginListY = 0;
if (!storager::setInt(jRoot, CONF_KEY_MIDI_SYSTEM, midiSystem)) return 0;
if (!storager::setInt(jRoot, CONF_KEY_MIDI_PORT_OUT, midiPortOut)) return 0;
if (!storager::setInt(jRoot, CONF_KEY_MIDI_PORT_IN, midiPortIn)) return 0;
- if (!storager::setBool(jRoot, CONF_KEY_NO_NOTE_OFF, noNoteOff)) return 0;
if (!storager::setString(jRoot, CONF_KEY_MIDIMAP_PATH, midiMapPath)) return 0;
if (!storager::setString(jRoot, CONF_KEY_LAST_MIDIMAP, lastFileMap)) return 0;
if (!storager::setInt(jRoot, CONF_KEY_MIDI_SYNC, midiSync)) return 0;
if (!storager::setInt(jRoot, CONF_KEY_SAMPLE_EDITOR_GRID_ON, sampleEditorGridOn)) return 0;
if (!storager::setInt(jRoot, CONF_KEY_PIANO_ROLL_Y, pianoRollY)) return 0;
if (!storager::setInt(jRoot, CONF_KEY_PIANO_ROLL_H, pianoRollH)) return 0;
+ if (!storager::setInt(jRoot, CONF_KEY_SAMPLE_ACTION_EDITOR_H, sampleActionEditorH)) return 0;
+ if (!storager::setInt(jRoot, CONF_KEY_VELOCITY_EDITOR_H, velocityEditorH)) return 0;
+ if (!storager::setInt(jRoot, CONF_KEY_ENVELOPE_EDITOR_H, envelopeEditorH)) return 0;
if (!storager::setInt(jRoot, CONF_KEY_PLUGIN_LIST_X, pluginListX)) return 0;
if (!storager::setInt(jRoot, CONF_KEY_PLUGIN_LIST_Y, pluginListY)) return 0;
if (!storager::setInt(jRoot, CONF_KEY_CONFIG_X, configX)) return 0;
json_object_set_new(jRoot, CONF_KEY_MIDI_SYSTEM, json_integer(midiSystem));
json_object_set_new(jRoot, CONF_KEY_MIDI_PORT_OUT, json_integer(midiPortOut));
json_object_set_new(jRoot, CONF_KEY_MIDI_PORT_IN, json_integer(midiPortIn));
- json_object_set_new(jRoot, CONF_KEY_NO_NOTE_OFF, json_boolean(noNoteOff));
json_object_set_new(jRoot, CONF_KEY_MIDIMAP_PATH, json_string(midiMapPath.c_str()));
json_object_set_new(jRoot, CONF_KEY_LAST_MIDIMAP, json_string(lastFileMap.c_str()));
json_object_set_new(jRoot, CONF_KEY_MIDI_SYNC, json_integer(midiSync));
json_object_set_new(jRoot, CONF_KEY_SAMPLE_EDITOR_GRID_ON, json_integer(sampleEditorGridOn));
json_object_set_new(jRoot, CONF_KEY_PIANO_ROLL_Y, json_integer(pianoRollY));
json_object_set_new(jRoot, CONF_KEY_PIANO_ROLL_H, json_integer(pianoRollH));
+ json_object_set_new(jRoot, CONF_KEY_SAMPLE_ACTION_EDITOR_H, json_integer(sampleActionEditorH));
+ json_object_set_new(jRoot, CONF_KEY_VELOCITY_EDITOR_H, json_integer(velocityEditorH));
+ json_object_set_new(jRoot, CONF_KEY_ENVELOPE_EDITOR_H, json_integer(envelopeEditorH));
json_object_set_new(jRoot, CONF_KEY_PLUGIN_LIST_X, json_integer(pluginListX));
json_object_set_new(jRoot, CONF_KEY_PLUGIN_LIST_Y, json_integer(pluginListY));
json_object_set_new(jRoot, CONF_KEY_CONFIG_X, json_integer(configX));
extern int midiSystem;
extern int midiPortOut;
extern int midiPortIn;
-extern bool noNoteOff;
extern std::string midiMapPath;
extern std::string lastFileMap;
extern int midiSync; // see const.h
extern int midiInputX, midiInputY, midiInputW, midiInputH;
extern int pianoRollY, pianoRollH;
+extern int sampleActionEditorH;
+extern int velocityEditorH;
+extern int envelopeEditorH;
extern int pluginListX, pluginListY;
extern int configX, configY;
extern int bpmX, bpmY;
/* -- version --------------------------------------------------------------- */
#define G_APP_NAME "Giada"
-#define G_VERSION_STR "0.15.1"
+#define G_VERSION_STR "0.15.2"
#define G_VERSION_MAJOR 0
#define G_VERSION_MINOR 15
-#define G_VERSION_PATCH 1
+#define G_VERSION_PATCH 2
#define CONF_FILENAME "giada.conf"
#define G_MIN_GUI_WIDTH 816
#define G_MIN_GUI_HEIGHT 510
#define G_MAX_IO_CHANS 2
+#define G_MAX_VELOCITY 0x7F
+#define G_MAX_MIDI_CHANS 16
#define G_DEFAULT_PATCH_NAME "(default patch)"
#define G_DEFAULT_MIDI_INPUT_UI_W 300
#define G_DEFAULT_MIDI_INPUT_UI_H 350
-#define G_DEFAULT_MIDI_ACTION_SIZE 8192 // frames
+#define G_DEFAULT_ACTION_SIZE 8192 // frames
+#define G_DEFAULT_ZOOM_RATIO 128
#define G_ACTION_KEYPRESS 0x01 // 0000 0001
#define G_ACTION_KEYREL 0x02 // 0000 0010
#define G_ACTION_KILL 0x04 // 0000 0100
-#define G_ACTION_MUTEON 0x08 // 0000 1000
-#define G_ACTION_MUTEOFF 0x10 // 0001 0000
#define G_ACTION_VOLUME 0x20 // 0010 0000
#define G_ACTION_MIDI 0x40 // 0100 0000
#define G_ACTION_KEYS 0x03 // 0000 0011 any key
-#define G_ACTION_MUTES 0x24 // 0001 1000 any mute
#define G_RANGE_CHAR 0x01 // range for MIDI (0-127)
#define G_RANGE_FLOAT 0x02 // range for volumes and VST params (0.0-1.0)
#define MIDI_CHAN_14 0x0E << 24
#define MIDI_CHAN_15 0x0F << 24
-const int MIDI_CHANS[16] = {
+const int MIDI_CHANS[G_MAX_MIDI_CHANS] = {
MIDI_CHAN_0, MIDI_CHAN_1, MIDI_CHAN_2, MIDI_CHAN_3,
MIDI_CHAN_4, MIDI_CHAN_5, MIDI_CHAN_6, MIDI_CHAN_7,
MIDI_CHAN_8, MIDI_CHAN_9, MIDI_CHAN_10, MIDI_CHAN_11,
#define CONF_KEY_MIDI_SYSTEM "midi_system"
#define CONF_KEY_MIDI_PORT_OUT "midi_port_out"
#define CONF_KEY_MIDI_PORT_IN "midi_port_in"
-#define CONF_KEY_NO_NOTE_OFF "no_note_off"
#define CONF_KEY_MIDIMAP_PATH "midimap_path"
#define CONF_KEY_LAST_MIDIMAP "last_midimap"
#define CONF_KEY_MIDI_SYNC "midi_sync"
#define CONF_KEY_SAMPLE_EDITOR_GRID_ON "sample_editor_grid_on"
#define CONF_KEY_PIANO_ROLL_Y "piano_roll_y"
#define CONF_KEY_PIANO_ROLL_H "piano_roll_h"
+#define CONF_KEY_SAMPLE_ACTION_EDITOR_H "sample_action_editor_h"
+#define CONF_KEY_VELOCITY_EDITOR_H "velocity_editor_h"
+#define CONF_KEY_ENVELOPE_EDITOR_H "envelope_editor_h"
#define CONF_KEY_PLUGIN_LIST_X "plugin_list_x"
#define CONF_KEY_PLUGIN_LIST_Y "plugin_list_y"
#define CONF_KEY_CONFIG_X "config_x"
#include "graphics.h"
-const char *giada_logo_xpm[] = {
-"300 82 8 1",
-" c #181917",
-". c #333432",
-"+ c #484A47",
-"@ c #5F615E",
-"# c #767875",
-"$ c #8D8F8C",
-"% c #A5A7A4",
-"& c #C5C7C4",
-" .#%%&$ ",
-" ..#%&&&&&# ",
-" +@#$%&&&&&&&&&@ ",
-" .. +&&&&&&&&&&&&&&. ",
-" +$%%#+ +%&&&&&&&&&&&&&. ",
-" #&&&&&%+ .+@@$&&&&&&&&&%. ",
-" #&&&&&&&$ +&&&&&&&&# ",
-" .&&&&&&&&%. #&&&&&&&@ ",
-" @&&&&&&&&$ +&&&&&&&+ ",
-" $&&&&&&&&# .&&&&&&&. ",
-" #&&&&&&&&. +&&&&&&%. ",
-" .&&&&&&&@ @&&&&&&$. ",
-" @&&&&&@ $&&&&&&# ",
-" .##@. %&&&&&&@ ",
-" &&&&&&&+ ",
-" .&&&&&&%. ",
-" @&&&&&&$. ",
-" .+@@###@+. ...... ... ++@@###@@. ....... .+@@###@@+. #&&&&&&# .+@@###@@+ ....... ",
-" .@$%%&&&&&&&%$+ +%%%%%%. .+@#$%%$. .@$%&&&&&&&&%$@. $%%%%%%+ .@#%%&&&&&&&&%$@ %&&&&&&@ .@$%%&&&&&&&%$#. @%%%%%%@ ",
-" #%&&&&&&&&&&&&&&$. #&&&&&& +@#$$%%%&&&&&%. .$%&&&&&&&&&&&&&&%@ .&&&&&&&+ #%&&&&&&&&&&&&&&&$+ &&&&&&&@ #%&&&&&&&&&&&&&&%#. %&&&&&&@ ",
-" +%&&&&&&&&&&&&&&&&&%@ .&&&&&&% .%&&&&&&&&&&&&$ #%&&&&&&&&&&&&&&&&&&$ +&&&&&&%. @%&&&&&&&&&&&&&&&&&&&# .&&&&&&%+ @%&&&&&&&&&&&&&&&&&&%. &&&&&&&+ ",
-" #&&&&&&&&&#+....@$&&&&@@&&&&&&$ .&&&&&&&&&&&&&# +%&&&&&&&&%#+....+#%&&&$#&&&&&&$. .$&&&&&&&&&$+.....@%&&&&$@&&&&&&%. .$&&&&&&&&&#@.....#%&&&%+&&&&&&%. ",
-" $&&&&&&&&@ .%&&&&&&&&&&@ .@$&&&&&&&&&&@ +%&&&&&&&%@ .#&&&&&&&&&&$ .%&&&&&&&&@ .$&&&&&&&&&&$ .%&&&&&&&&#. @&&&&&&&&&&%. ",
-" $&&&&&&&%. @&&&&&&&&&+ .%&&&&&&&%+ @&&&&&&&&# +%&&&&&&&&# +%&&&&&&&$. @&&&&&&&&&# .%&&&&&&&$. .%&&&&&&&&$ ",
-" %&&&&&&&# @&&&&&&&&. +%&&&&&&%. @&&&&&&&&# .%&&&&&&&@ +%&&&&&&&# @&&&&&&&&@ +%&&&&&&&# %&&&&&&&@ ",
-" $&&&&&&&$ #&&&&&&%. %&&&&&&% +&&&&&&&&@ +&&&&&&%+ .%&&&&&&&# @&&&&&&&+ .%&&&&&&&# &&&&&&&+ ",
-" @&&&&&&&$. #&&&&&&$ %&&&&&&$ .%&&&&&&&@ @&&&&&&%. .$&&&&&&&$ +&&&&&&%+ $&&&&&&&$ .&&&&&&&+ ",
-" +&&&&&&&%+ %&&&&&&# %&&&&&&# $&&&&&&&$ $&&&&&&% @&&&&&&&% #&&&&&&%. #&&&&&&&%. @&&&&&&%. ",
-" %&&&&&&&# &&&&&&&+ .%&&&&&&@ @&&&&&&&$ %&&&&&&$ +&&&&&&&&+ $&&&&&&$ +&&&&&&&%. $&&&&&&$. ",
-" @&&&&&&&%. .&&&&&&&. +%&&&&&%. .&&&&&&&&. .%&&&&&&# $&&&&&&&# %&&&&&&# .$&&&&&&&@ %&&&&&&# ",
-" .%&&&&&&&@ @&&&&&&&. @&&&&&&% @&&&&&&&# @&&&&&&&+ +&&&&&&&&. +&&&&&&&@ +&&&&&&&% .&&&&&&&@ ",
-" @&&&&&&&% $&&&&&&%. #&&&&&&% &&&&&&&&. #&&&&&&%. %&&&&&&&# @&&&&&&%+ .$&&&&&&&@ +&&&&&&&+ ",
-" .$&&&&&&&# %&&&&&&# $&&&&&&# #&&&&&&&# $&&&&&&% +&&&&&&&&. $&&&&&&%. +&&&&&&&% #&&&&&&%+ ",
-" +%&&&&&&&+ .&&&&&&&@ .%&&&&&&@ %&&&&&&&+ .%&&&&&&$ $&&&&&&&$ %&&&&&&% #&&&&&&&# %&&&&&&%. ",
-" @&&&&&&&% +&&&&&&&+ +%&&&&&&+ .&&&&&&&%. +&&&&&&&# &&&&&&&&+ +%&&&&&&$ .%&&&&&&&. &&&&&&&$ ",
-" $&&&&&&&@ #&&&&&&&. @&&&&&&&. #&&&&&&&# #&&&&&&&@ +&&&&&&&%. @&&&&&&&# .&&&&&&&% +&&&&&&&# ",
-" .%&&&&&&&. %&&&&&&%. #&&&&&&% %&&&&&&&+ $&&&&&&&+ #&&&&&&&$. $&&&&&&&+ @&&&&&&&@ #&&&&&&&@ ",
-" +&&&&&&&& &&&&&&&$. #&&&&&&$ &&&&&&&%. .%&&&&&&& %&&&&&&&# .%&&&&&&%. $&&&&&&&+ $&&&&&&%+ ",
-" +&&&&&&&$ +&&&&&&&# .$&&&&&&# .&&&&&&&$. +&&&&&&&% %&&&&&&&+ +%&&&&&&% %&&&&&&&. .%&&&&&&%. ",
-" @&&&&&&&@ $&&&&&&&@ .%&&&&&&+ +&&&&&&&# #&&&&&&&$ &&&&&&&&+ #&&&&&&&% &&&&&&&% @&&&&&&&$ ",
-" @&&&&&&&+ &&&&&&&&+ +%&&&&&&. @&&&&&&&@ .%&&&&&&&@ .&&&&&&&%. .%&&&&&&&# &&&&&&&# $&&&&&&&$ ",
-" #&&&&&&&. @&&&&&&&%. @&&&&&&& @&&&&&&&@ @&&&&&&&&+ .&&&&&&&%. @&&&&&&&&@ .&&&&&&&# +%&&&&&&&# ",
-" #&&&&&&&. %&&&&&&&$. #&&&&&&% #&&&&&&&+ .$&&&&&&&&. .&&&&&&&$. .$&&&&&&&&. .&&&&&&&@ $&&&&&&&&@ ",
-" #&&&&&&& #&&&&&&&&$ $&&&&&&# @&&&&&&&+ @&&&&&&&&& .&&&&&&&$. @&&&&&&&&& .&&&&&&&+ +%&&&&&&&%+ ",
-" @&&&&&&& .%&&&&&&&&# .%&&&&&&@ @&&&&&&%+ .%&&&&&&&&% &&&&&&&$. .%&&&&&&&&% &&&&&&&+ $&&&&&&&&%. ",
-" @&&&&&&&. $&&&&&&&&&@ +%&&&&&&. +&&&&&&&@ @&&&&&&&&&$ &&&&&&&%. #&&&&&&&&&$ &&&&&&&@ +&&&&&&&&&% ",
-" +&&&&&&&+ @&&&&&&&&&%+ +&&&&&&& &&&&&&&@ +&&&&&&&&&&# $&&&&&&%+ @&&&&&&&&&&# $&&&&&&# .%&&&&&&&&&% ",
-" .%&&&&&&@ +%&&$&&&&&&%. +&&&&&&& %&&&&&&# .&&&&&&&&&&&@ @&&&&&&&+ +&&&$%&&&&&&@ @&&&&&&$ .$&&&&&&&&&&$ ",
-" #&&&&&&$ .$&&##&&&&&&$ @&&&&&&& @&&&&&&%. .%&&%@%&&&&&&@ .&&&&&&&# .&&&$+%&&&&&&. .&&&&&&&. .#&&%@%&&&&&&$ ",
-" +&&&&&&&. .%&&% $&&&&&&# +&&&&&&&. +.&&&&&&&@ .%&&%+.%&&&&&&$ @+$&&&&&&&+ +&&&% +&&&&&&&+ .+ $&&&&&&$ .$&&&@ %&&&&&&% .# ",
-" $&&&&&&$ +%&&&+ %&&&&&&@ +&&&&&&&@ #&$#&&&&&&%+ @&&&&@ .$&&&&&&&+ #&&@&&&&&&&$. .#&&&%. +%&&&&&&# .$&$ +&&&&&&&+ +%&&&# $&&&&&&&# +&&$ ",
-" +%&&&&&&#. +#&&&%@ .%&&&&&&+ .%&&&&&&&+ .$&&%.%&&&&&&%+ +#&&&&@ #&&&&&&&%@..+@$&&&+@&&&&&&&%+ .@%&&&%+ .%&&&&&&&+ +%&&$. #&&&&&&%@ +#&&&&# @&&&&&&&&@...+#&&&# ",
-" @&&&&&&&$@+..++#%&&&%+ +&&&&&&%. $&&&&&&&%#@@#%&&%. .%&&&&&&%@+...+@$&&&&%+ +%&&&&&&&&%$%&&&&+ $&&&&&&&&$#@+@@#%&&&&$. #&&&&&&&&#@+@$&&&$. .$&&&&&&&#+...++#%&&&&@ .%&&&&&&&&%$%&&&&# ",
-" @&&&&&&&&%%%%&&&&&$. @&&&&&&% +%&&&&&&&&&&&&&$. +%&&&&&&&&%$%%&&&&&$. #&&&&&&&&&&&&&%+ .$&&&&&&&&&&&&&&&&&&# .%&&&&&&&&&&&&&&# .$&&&&&&&&%$%%&&&&&%+ @&&&&&&&&&&&&&%@ ",
-" @%&&&&&&&&&&&&&%@. #&&&&&&$ #&&&&&&&&&&&%@. .$&&&&&&&&&&&&&&%@. .%&&&&&&&&&&&#. @&&&&&&&&&&&&&&&$+ +&&&&&&&&&&&&%+ .#&&&&&&&&&&&&&&&#. $&&&&&&&&&&&$+ ",
-" .#%&&&&&&&&&%+. %&&&&&&# +%&&&&&&&%#. +$&&&&&&&&&&%@. .$&&&&&&&&#+ .#&&&&&&&&&&%#. .$&&&&&&&&%@. +$&&&&&&&&&&%@. .@&&&&&&&&$+. ",
-" .+#$%%$#+.. .%&&&&&&+ .@#$%$#+. .@#$%%$#@+ +@$%$#+. .+#$%%$#@+. +@$%$$@. .+#$%%$#@+. .@$%$#@. ",
-" +&&&&&&%. ",
-" #&&&&&&% ",
-" .%&&&&&&@ ",
-" @&&&&&&%. ",
-" $&&&&&&$ ",
-" @&&&&&&&+ ",
-" @#$#+ .$&&&&&&$ ",
-" $&&&&&# #&&&&&&% ",
-"#&&&&&&&@ @&&&&&&&+ ",
-"%&&&&&&&%. @&&&&&&%+ ",
-"%&&&&&&&&# #&&&&&&%. ",
-"@&&&&&&&&&@ .$&&&&&&#. ",
-" $&&&&&&&&&$+ +$&&&&&&%+ ",
-" +&&&&&&&&&&%#@@#$%&&&&&&&#. +. .+ +@. ++ .+ +++++ +. .+ ++ +++++. .++++. +@. .@+ .++++. .+++++++ +. +@. .+@. .++++. ++. ++. .+. .+@. .+ +. .+ .+. .+ .+++++++ ",
-" .$&&&&&&&&&&&&&&&&&&&%@ $%. %@ +&&%&%. .$$ +& .&&&&&&# .%@ +&. %&+ $&&&&&%. @&&&&&%. +&&%&%. .%&%&&+ #&&&&&&@ +&&&&&&%. &# +&&%&%. @&&%&%. +&&&&&&@ $&% +%&+ .$&@ @&&%&$. $% .%$ $% +&%. +%. +&&&&&&$. ",
-" +$%%&&&&&&&&&&%$@. +&# #% +&# %% $$ +& .&+ @&@ .%@ +&. +$&$ $$ .%% @% .%&. .&$ .%$. %%. #&+ #& .$&+ +&. &# +&# $%. @&# .%%. +&. $&+ $%&+ #%&+ .&%$ @&# +%$ $% .%# $% +&%$ +%. +&. ",
-" .++@###@@+. #&. .&+ $%. .&@ $$ +& .&+ %$ .%@ +&. ##$% $$ @&. @% %$ $%. @&. @&+ %$. #& .%@ +&. &# %% .&# %% .&@ +&. &# $#%$ $#&+ @%@%. %% @%+ $% .%# $% +&+%+ +%. +&. ",
-" .%$ $$ .&# %% $$ +& .&+ %$ .%@ +&. .%@+&+ $$ @&. @% #& &# +. %% #&. #& .%@ +&. &# .&@ $$ +&+ .$$ +&. %# $#@& +$@&+ $#.%@ +&@ .+. $% .%# $% +& $$ +%. +&. ",
-" @&#&. .&@ $& $$ +& .&#+++#&+ .%%####$&. +%. %$ $%.++@&$ @% +&..&@ &$ @&+ #&++++$%. +&$###@ &# +&+ #%.@&. #% +&+ ..#&+ $#.&@ ##+&+ .&..$$ @&. $&$###$&# $% +& +%@ +%. +&####+ ",
-" %&@ +&+ #& $$ +& .&%$$%%@ .%%####$&. #$ #& $&$$%&#. @% +&++&@ &# +&+ #&$$%&%+ +&$$$$# &# @&. #%.@&. #%.+%%%%%%@ $# $% .$+.&+ @% @%. @&. $&$###$&# $% +& #%.+%. +&$$$$@ ",
-" #&. .&@ $% $$ +& .&#+.$%. .%@ +&. .$%##$&+ $%.+@&@ @% +&..&@ &$ @&. #&+++$$ +&+ &# +&. #% @&. $$ +&#@@++ $# +&.+$.+&+ $%@#$&+ @&. $% .%# $% +& .%@+%. +&. ",
-" @& .%# &$ $$ +% .&+ .%@ .%@ +&. +%$###&$ $$ $% @% $& &$ .$+ $% #%. #& +&+ +&. &# .&@ .%$ +&@ .%# +&. $# .%### +&+ .&####&# .&@ .$+ $% .%# $% +& @%@%. +&. ",
-" @& #%. @&. #%. $% .&+ $% .%@ +&. @$. #& $$ +&+ @% +&@ #&. #&. +&+ .%@ #& .%# +&. &# $% +&+ %% @&+ +&. $# #&%+ +&+ @% #%. %% #%. $% .%# $% +& .$%%. +&. ",
-" @& .%%+.@&# .%%++#&@ .&+ +&+ .%@ +&. $# .&+ $$ %$ @&+++#&%. $%+.#&# #&@.+%%. #& @%. +&@+++++. &$++++. .%%+.@%# .%%..@&# +&. $# +&$ +&+ %# +&+ .%%+.#&# $% .%# $% +& +&&. +&@++++. %&",
-" @& .$&&%@ +%&&&@ .&+ %$ .%@ +&..%+ %$ $$ @&. @&&&&%@ .$%&&# @%&&$. #& .%@ +&&&&&&%+ &&&&&&$. .$&&%# .$&&%@ +&. $# .%# +&+.&. .%# .$&&&@ $% .%# $% +& $&. +&&&&&&%. &&"};
-
-
-const char *loopRepeat_xpm[] = {
+const char* giada_logo_xpm[] = {
+"245 86 12 1",
+" c #191919",
+". c #303030",
+"+ c #404040",
+"@ c #454545",
+"# c #5A5A5A",
+"$ c #686868",
+"% c #818181",
+"& c #9C9C9C",
+"* c #B8B8B8",
+"= c #CDCDCD",
+"- c #DDDDDD",
+"; c #FEFEFE",
+" ...+++@@@@++... ",
+" .+@@@@@@@@@@@@@@@@@@@@. ",
+" .+@@@@@@@@@@@@@@@@@@@@@@@@@@+. ",
+" .+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@. ",
+" .+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@. ",
+" @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@. ",
+" +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@. ",
+" .@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@+ ",
+" .@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@. ",
+" @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@. ",
+" @@@@@@@@@@@@@@@@@@++$*--;;;;;;;;-=&@+@@@@@@@@@@@@@@@@@@+ ",
+" .@@@@@@@@@@@@@@@@+#&=;;;;;;;;;;;;;;;;;-*%++@@@@@@@@@@@@@@@+ ",
+" .@@@@@@@@@@@@@@@@$=;;;;;;;;;;;;;;;;;;;;;;;;&#+@@@@@@@@@@@@@@@ ",
+" .@@@@@@@@@@@@@@@$;;;;;;;;;;;;;;;;;;;;;;;;;;;;;*@@@@@@@@@@@@@@@@ ",
+" @@@@@@@@@@@@@@#-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;*+@@@@@@@@@@@@@+ ",
+" @@@@@@@@@@@@@+*;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;$+@@@@@@@@@@@@+ ",
+" @@@@@@@@@@@@@#-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;&@@@@@@@@@@@@@. ",
+" .@@@@@@@@@@@@$;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-+@@@@@@@@@@@@ ",
+" .@@@@@@@@@@@+%;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-@@@@@@@@@@@@@ ",
+" @@@@@@@@@@@+&;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;#@@@@@@@@@@@. ",
+" +@@@@@@@@@@@%;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;@@@@@@@@@@@@ ",
+" @@@@@@@@@@@$;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-+@@@@@@@@@@+ ",
+" @@@@@@@@@@@#;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;=@@@@@@@@@@@. ",
+" .@@@@@@@@@@+-;;;;;;;;;;;;;;;;;;;;;=*&%%%%&*-;;;;;;;;;;;;;;;;;;;;;%+@@@@@@@@@@ ",
+" +@@@@@@@@@@*;;;;;;;;;;;;;;;;;;;-%++@@@@@@@++@*-;;;;;;;;;;;;;;;;;;;#@@@@@@@@@@. ",
+" .@@@@@@@@@@#;;;;;;;;;;;;;;;;;;=@@@@@@@@@@@@@@@@@$;;;;;;;;;;;;;;;;;;=+@@@@@@@@@@ ",
+" +@@@@@@@@@@-;;;;;;;;;;;;;;;;-$+@@@@@@@@@@@@@@@@@@@&;;;;;;;;;;;;;;;;;%@@@@@@@@@@. %*------=*%. $%%%%%%% $%%%%%%%%. #%%%%%%%%%%%%%%$@ %%%%%%%%% ",
+" @@@@@@@@@@%;;;;;;;;;;;;;;;;*@@@@@@@@@@@@@@@@@@@@@@+$;;;;;;;;;;;;;;;;-+@@@@@@@@@+ #=;;;;;;;;;;;;;=$ .;;;;;;;;& &;;;;;;;;;; ;;;;;;;;;;;;;;;;;;-&# -;;;;;;;;;= ",
+" .@@@@@@@@@@;;;;;;;;;;;;;;;;*+@@@@@@@@@@@@@@@@@@@@@@@@@;;;;;;;;;;;;;;;;%@@@@@@@@@@ .=;;;;;;;;;;;;;;;;;;# +;;;;;;;;* ;;;;;;;;;;;& .;;;;;;;;;;;;;;;;;;;;;;@ .;;;;;;;;;;;. ",
+" +@@@@@@@@@%;;;;;;;;;;;;;;;*@@@@@@@@@@@@@@@@@@@@@@@@@@@@;;;;;;;;;;;;;;;;@@@@@@@@@@. @;;;;;;;;;;;;;;;;;;;;;* +;;;;;;;;* +;;;;;;;;;;;; .;;;;;;;;;;;;;;;;;;;;;;;* &;;;;;;;;;;;* ",
+" @@@@@@@@@+-;;;;;;;;;;;;;;*+@@@@@@@@@@@@@@@@@@@@@@@@@@@@#;;;;;;;;;;;;;;;$@@@@@@@@@+ @;;;;;;;;;;;;;;;;;;;;;;;= +;;;;;;;;* *;;;;;;;;;;;;. .;;;;;;;;;;;;;;;;;;;;;;;;= ;;;;;;;;;;;;; ",
+" .@@@@@@@@@#;;;;;;;;;;;;;;-@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@%;;;;;;;;;;;;;;=+@@@@@@@@@ .;;;;;;;;;;;;;;;;;;;;;;;;;& +;;;;;;;;* .;;;;;;;;;;;;;& .;;;;;;;;;;;;;;;;;;;;;;;;;* #;;;;;;;;;;;;;+ ",
+" +@@@@@@@@@&;;;;;;;;;;;;;;#@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@+*;;;;;;;;;;;;;;#@@@@@@@@@ *;;;;;;;;;;;;;;;;;;;;;;;;;;# +;;;;;;;;* $;;;;;;;;;;;;;; .;;;;;;;;;;;;;;;;;;;;;;;;;;# *;;;;;;;;;;;;;= ",
+" @@@@@@@@@+-;;;;;;;;;;;;;*@@@@@@@@@@@@+#*;;;-&@@@@@@@@@@@@@@;;;;;;;;;;;;;;%@@@@@@@@@. +;;;;;;;;;;-# #-;;;;;;;;;;= +;;;;;;;;* =;;;;;;;;;;;;;;# .;;;;;;;;-&&&&*-;;;;;;;;;;;; ;;;;;;;;;;;;;;;. ",
+" @@@@@@@@@+;;;;;;;;;;;;;;@@@@@@@@@@@+%-;;;;;;;;*+@@@@@@@@@@+&;;;;;;;;;;;;;*+@@@@@@@@+ &;;;;;;;;;% %;;;;;;;;;; +;;;;;;;;* .;;;;;;;;;;;;;;;* .;;;;;;;;= #;;;;;;;;;;# %;;;;;;;;;;;;;;;$ ",
+".@@@@@@@@@%;;;;;;;;;;;;;=@@@@@@@@@@+&;;;;;;;;;;;;#@@@@@@@@@@#;;;;;;;;;;;;;;+@@@@@@@@@ =;;;;;;;;= =;;;;;;;;;@ +;;;;;;;;* &;;;;;;;%;;;;;;;; .;;;;;;;;= ;;;;;;;;;& -;;;;;;;%;;;;;;;= ",
+".@@@@@@@@@=;;;;;;;;;;;;;$@@@@@@@@@+*;;;;;;;;;;;;;;#@@@@@@@@@@-;;;;;;;;;;;;;#@@@@@@@@@ ;;;;;;;;;@ -;;;;;;;;. +;;;;;;;;* -;;;;;;;+*;;;;;;;% .;;;;;;;;= %;;;;;;;;- ;;;;;;;; ;;;;;;;;. ",
+".@@@@@@@@@-;;;;;;;;;;;;;+@@@@@@@@@%;;;;;;;;;;;;;;;;+@@@@@@@@@%;;;;;;;;;;;;;%@@@@@@@@@ ;;;;;;;;; +;;;;;;;;* .;;;;;;;; #;;;;;;;= .;;;;;;;;= ;;;;;;;;; &;;;;;;;& &;;;;;;;& ",
+"+@@@@@@@@@;;;;;;;;;;;;;*+@@@@@@@@@;;;;;;;;;;;;;;;;;&@@@@@@@@@@;;;;;;;;;;;;;&@@@@@@@@@ .;;;;;;;;- +;;;;;;;;* *;;;;;;;% ;;;;;;;; .;;;;;;;;= -;;;;;;;; ;;;;;;;;. @;;;;;;;- ",
+"+@@@@@@@@@;;;;;;;;;;;;;&@@@@@@@@@%;;;;;;;;;;;;;;;;;-+@@@@@@@@+-;;;;;;;;;;;;*@@@@@@@@@. .;;;;;;;;- +;;;;;;;;* ;;;;;;;;. *;;;;;;;& .;;;;;;;;= =;;;;;;;;. .;;;;;;;; ;;;;;;;;. ",
+"+@@@@@@@@@;;;;;;;;;;;;;%@@@@@@@@+-;;;;;;;;;;;;;;;;;;$@@@@@@@@@-;;;;;;;;;;;;=@@@@@@@@@. .;;;;;;;;- .%%%%%%%%%%%%$ +;;;;;;;;* +;;;;;;;- .;;;;;;;; .;;;;;;;;= =;;;;;;;;. &;;;;;;;* %;;;;;;;* ",
+"@@@@@@@@@@;;;;;;;;;;;;;$@@@@@@@@@;;;;;;;;;;;;;;;;;;;&@@@@@@@@@=;;;;;;;;;;;;=+@@@@@@@@. +;;;;;;;;- -;;;;;;;;;;;;;% +;;;;;;;;* *;;;;;;;& ;;;;;;;;. .;;;;;;;;= *;;;;;;;;. ;;;;;;;;. .;;;;;;;; ",
+"@@@@@@@@@@;;;;;;;;;;;;;$@@@@@@@@#;;;;;;;;;;;;;;;;;;;*@@@@@@@@@=;;;;;;;;;;;;=+@@@@@@@@. +;;;;;;;;- -;;;;;;;;;;;;;& +;;;;;;;;* .;;;;;;;; *;;;;;;;& .;;;;;;;;= *;;;;;;;;+ #;;;;;;;- ;;;;;;;;+ ",
+"@@@@@@@@@@;;;;;;;;;;;;;$@@@@@@@@#;;;;;;;;;;;;;;;;;;;*@@@@@@@@@#$$&*-;;;;;;;=+@@@@@@@@. +;;;;;;;;- -;;;;;;;;;;;;;& +;;;;;;;;* $;;;;;;;= .;;;;;;;; .;;;;;;;;= *;;;;;;;;+ *;;;;;;;& %;;;;;;;* ",
+"@@@@@@@@@@;;;;;;;;;;;;;$@@@@@@@@@;;;;;;;;;;;;;;;;;;;&@@@@@@@@@@@@@@+@=;;;;;=@@@@@@@@@. .;;;;;;;;- -;;;;;;;;;;;;;& +;;;;;;;;* =;;;;;;;% -;;;;;;;# .;;;;;;;;= *;;;;;;;;. ;;;;;;;;+ ;;;;;;;;. ",
+"+@@@@@@@@@;;;;;;;;;;;;;%@@@@@@@@+-;;;;;;;;;;;;;;;;;;$@@@@@@@@@@@@@@@@@#-;;;=@@@@@@@@@. .;;;;;;;;- -;;;;;;;;;;;;;& +;;;;;;;;* .;;;;;;;;. &;;;;;;;* .;;;;;;;;= =;;;;;;;;. %;;;;;;;- =;;;;;;;# ",
+"+@@@@@@@@@;;;;;;;;;;;;;&@@@@@@@@@%;;;;;;;;;;;;;;;;;;+@@@@@+++++++@@@@@@@&;;*@@@@@@@@@. .;;;;;;;;- @%%%%*;;;;;;;;& +;;;;;;;;* &;;;;;;;=.......#;;;;;;;; .;;;;;;;;= =;;;;;;;;. -;;;;;;;%.......&;;;;;;;= ",
+"+@@@@@@@@@;;;;;;;;;;;;;*+@@@@@@@@@;;;;;;;;;;;;;;;;;&@@@+#%*-;;-=&$@@@@@@@%;&@@@@@@@@@ ;;;;;;;;; %;;;;;;;;% +;;;;;;;;* -;;;;;;;;;;;;;;;;;;;;;;;;% .;;;;;;;;= -;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;. ",
+".@@@@@@@@@-;;;;;;;;;;;;;+@@@@@@@@@%;;;;;;;;;;;;;;;;+@@@*;;;;;;;;;;;%@@@@@@&%@@@@@@@@@ ;;;;;;;;; &;;;;;;;;% +;;;;;;;;* ;;;;;;;;;;;;;;;;;;;;;;;;;= .;;;;;;;;= ;;;;;;;;; %;;;;;;;;;;;;;;;;;;;;;;;;;& ",
+".@@@@@@@@@=;;;;;;;;;;;;;$@@@@@@@@@+*;;;;;;;;;;;;;;#@+%;;;;;;;;;;;;;;-@@@@@@@@@@@@@@@@ -;;;;;;;;# =;;;;;;;;@ +;;;;;;;;* *;;;;;;;;;;;;;;;;;;;;;;;;;; .;;;;;;;;= $;;;;;;;;- ;;;;;;;;;;;;;;;;;;;;;;;;;;- ",
+".@@@@@@@@@%;;;;;;;;;;;;;=@@@@@@@@@@+*;;;;;;;;;;;;#@+*;;;;;;;;;;;;;;;;;$+@@@@@@@@@@@@@ =;;;;;;;;= @;;;;;;;;; +;;;;;;;;* ;;;;;;;;;;;;;;;;;;;;;;;;;;;& .;;;;;;;;= -;;;;;;;;& .;;;;;;;;;;;;;;;;;;;;;;;;;;;. ",
+" @@@@@@@@@+;;;;;;;;;;;;;;@@@@@@@@@@@+%-;;;;;;;;*@@+*;;;;;;;;;;;;;;;;;;;$@@@@@@@@@@@@+ &;;;;;;;;;% +;;;;;;;;;= +;;;;;;;;* +;;;;;;;;;;;;;;;;;;;;;;;;;;;; .;;;;;;;;= +-;;;;;;;;;$ &;;;;;;;;;;;;;;;;;;;;;;;;;;;* ",
+" @@@@@@@@@+-;;;;;;;;;;;;;*@@@@@@@@@@@@+$=;;;-&@@@@&;;;;;;;;;;;;;;;;;;;;;@@@@@@@@@@@@. .;;;;;;;;;;-@ .*;;;;;;;;;;% +;;;;;;;;* *;;;;;;;;;;;;;;;;;;;;;;;;;;;;. .;;;;;;;;-&&&&&*;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ",
+" +@@@@@@@@@*;;;;;;;;;;;;;;#@@@@@@@@@@@@@@@@@+@@@@@;;;;;;;;;;;;;;;;;;;;;;-@@@@@@@@@@@ &;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;;;;;;* .;;;;;;;;;;;;;;;;;;;;;;;;;;;;;& .;;;;;;;;;;;;;;;;;;;;;;;;;;$ #;;;;;;;;;;;;;;;;;;;;;;;;;;;;;. ",
+" .@@@@@@@@@#;;;;;;;;;;;;;;-@@@@@@@@@@@@@@@@@@@@@@-;;;;;;;;;;;;;;;;;;;;;;;$@@@@@@@@@@ .;;;;;;;;;;;;;;;;;;;;;;;;;@ +;;;;;;;;* $;;;;;;;;% ;;;;;;;;; .;;;;;;;;;;;;;;;;;;;;;;;;;- *;;;;;;;; .;;;;;;;;* ",
+" @@@@@@@@@+-;;;;;;;;;;;;;;*+@@@@@@@@@@@@@@@@@@@#;;;;;;;;;;;;;;;;;;;;;;;;-+@@@@@@@@+ @;;;;;;;;;;;;;;;;;;;;;;;& +;;;;;;;;* *;;;;;;;; &;;;;;;;;# .;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;= -;;;;;;;;.",
+" +@@@@@@@@@%;;;;;;;;;;;;;;;&@@@@@@@@@@@@@@@@@@+%;;;;;;;;;;;;;;;;;;;;;;;;;#@@@@@@@@. #;;;;;;;;;;;;;;;;;;;;;% +;;;;;;;;* ;;;;;;;;* ;;;;;;;;* .;;;;;;;;;;;;;;;;;;;;;;;- %;;;;;;;;% %;;;;;;;;#",
+" .@@@@@@@@@@;;;;;;;;;;;;;;;;&+@@@@@@@@@@@@@@@@+-;;;;;;;;;;;;;;;;;;;;;;;;;%@@@@@@@@ .-;;;;;;;;;;;;;;;;;;+ +;;;;;;;;* &;;;;;;;;$ =;;;;;;;; .;;;;;;;;;;;;;;;;;;;;;;% -;;;;;;;; ;;;;;;;;*",
+" @@@@@@@@@@%;;;;;;;;;;;;;;;;*@@@@@@@@@@@@@@@@+;;;;;;;;;;;;;;;;;;;;;;;;;;&+@@@@@@+ $=;;;;;;;;;;;;;=$ .;;;;;;;;* =;;;;;;;; $;;;;;;;;# .;;;;;;;;;;;;;;;;;;;=%. ;;;;;;;;& *;;;;;;;;",
+" +@@@@@@@@@@-;;;;;;;;;;;;;;;;-#+@@@@@@@@@@@@@#;;;;;;;;;;;;;;;;;;;;;;;;;;*+@@@@@@. .%=---;---=%. %&&&&&&&. +&&&&&&&. $&&&&&&% #&&&&&&&&&&&&&&%$+ $&&&&&&% %&&&&&&#",
+" .@@@@@@@@@@#;;;;;;;;;;;;;;;;;;*@@@@@@@@@@@@@#;;;;;;;;;;;;;;;;;;;;;;;;;;=+@@@@@@ ",
+" +@@@@@@@@@@*;;;;;;;;;;;;;;;;;;;=%++@@@@@@@+@;;;;;;;;;;;;;;;;;;;;;;;;;;*+@@@@@. ",
+" .@@@@@@@@@@@-;;;;;;;;;;;;;;;;;;;;;=&%%$%%&*-;;;;;;;;;;;;;;;;;;;;;;;;;;&+@@@@@ ",
+" +@@@@@@@@@@#;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;%@@@@@. ",
+" @@@@@@@@@@@$;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;#@@@@+ ",
+" +@@@@@@@@@@@%;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-+@@@@ ",
+" @@@@@@@@@@@+&;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;$@@@@. ",
+" .@@@@@@@@@@@+%;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-@@@@@ ",
+" .@@@@@@@@@@@@$;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;@@@@@ ",
+" +@@@@@@@@@@@@#-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;$@@@@. ",
+" @@@@@@@@@@@@@@*;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;%+@@@+ ",
+" @@@@@@@@@@@@@+#-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-#@@@@+ ",
+" .@@@@@@@@@@@@@@@%;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;%@@@@@@ ",
+" .@@@@@@@@@@@@@@@@$-;;;;;;;;;;;;;;;;;;;;;;;;*$%*-;;;-*$@@@@@@@ ",
+" .@@@@@@@@@@@@@@@@+#&-;;;;;;;;;;;;;;;;;;=%@+@@+++.+++@@@@@@+ ",
+" @@@@@@@@@@@@@@@@@@++%*--;;;;;;;;--&#++@@@@@@@@@@@@@@@@@+ ",
+" @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@. ",
+" .@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@. ",
+" .@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@+ ",
+" +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@. ",
+" @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@. ",
+" .+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@. ",
+" .+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@. ",
+" .+@@@@@@@@@@@@@@@@@@@@@@@@@@+. ",
+" .+@@@@@@@@@@@@@@@@@@@@. ",
+" ...+++@@@@++... "};
+
+
+const char* loopRepeat_xpm[] = {
"18 18 8 1",
" c #181917",
". c #242523",
".................."};
-const char *loopBasic_xpm[] = {
+const char* loopBasic_xpm[] = {
"18 18 8 1",
" c #181917",
". c #242523",
".................."};
-const char *loopOnce_xpm[] = {
+const char* loopOnce_xpm[] = {
"18 18 8 1",
" c #181917",
". c #242523",
".................."};
-const char *loopOnceBar_xpm[] = {
+const char* loopOnceBar_xpm[] = {
"18 18 8 1",
" c #242523",
". c #393A38",
" "};
-const char *oneshotBasic_xpm[] = {
+const char* oneshotBasic_xpm[] = {
"18 18 8 1",
" c #181917",
". c #242523",
".................."};
-const char *oneshotRetrig_xpm[] = {
+const char* oneshotRetrig_xpm[] = {
"18 18 8 1",
" c #181917",
". c #242523",
".................."};
-const char *oneshotPress_xpm[] = {
+const char* oneshotPress_xpm[] = {
"18 18 8 1",
" c #181917",
". c #242523",
".................."};
-const char *oneshotEndless_xpm[] = {
+const char* oneshotEndless_xpm[] = {
"18 18 6 1",
" c #242523",
". c #464745",
" "};
-const char *updirOff_xpm[] = {
+const char* updirOff_xpm[] = {
"18 18 8 1",
" c #242523",
". c #332F2E",
" "};
-const char *updirOn_xpm[] = {
+const char* updirOn_xpm[] = {
"18 18 8 1",
" c #4D4F4C",
". c #555150",
" "};
-const char *pause_xpm[] = {
+const char* pause_xpm[] = {
"23 23 8 1",
" c #4D4F4C",
". c #514E53",
" "};
-const char *play_xpm[] = {
+const char* play_xpm[] = {
"23 23 8 1",
" c #242523",
". c #393534",
" "};
-const char *rewindOff_xpm[] = {
+const char* rewindOff_xpm[] = {
"23 23 8 1",
" c #242523",
". c #393534",
" "};
-const char *rewindOn_xpm[] = {
+const char* rewindOn_xpm[] = {
"23 23 8 1",
" c #4D4F4C",
". c #514E53",
" "};
-// 18x18
-/*
-const unsigned char giada_icon[] = {
- 0x00, 0x00, 0x00, 0xfc, 0xff, 0x00, 0xfe, 0xff, 0x01, 0xfe, 0xff, 0x01,
- 0x3e, 0xf0, 0x01, 0x1e, 0xe0, 0x01, 0x0e, 0xc3, 0x01, 0x8e, 0xff, 0x01,
- 0x8e, 0xc1, 0x01, 0x8e, 0xc1, 0x01, 0x8e, 0xc7, 0x01, 0x0e, 0xc7, 0x01,
- 0x1e, 0xc0, 0x01, 0x3e, 0xf0, 0x01, 0xfe, 0xff, 0x01, 0xfe, 0xff, 0x01,
- 0xfc, 0xff, 0x00, 0x00, 0x00, 0x00 };
-*/
-
-const char *giada_icon[] = {
-"65 65 11 1",
-" c None",
-". c #000000",
-"+ c #000100",
-"@ c #FFFFFF",
-"# c #FDFFFC",
-"$ c #CBCDC9",
-"% c #292B28",
-"& c #626461",
-"* c #484A47",
-"= c #888A87",
-"- c #A7A9A6",
-"....+++++++++++++++++++++++++++++++++++++++++++++++++++++++++....",
-".@@@#####################$%+++++++++++++%&&*%+++++++&##%+++++....",
-".@@#####################&++++++++++++=$#######-*+++++&$+++++++...",
-".@@####################&++++++++++%-############$*+++++++++++++..",
-"+#####################*++++++++++&################-++++++++++++%+",
-"+####################*++++++++++=##################$+++++++++++*+",
-"+###################*++++++++++$####################$++++++++++=+",
-"+##################=++++++++++-######################-+++++++++-+",
-"+#################$++++++++++=#######################=+++++++++$+",
-"+#################%+++++++++*########################*+++++++++#+",
-"+################*++++++++++#########################%++++++++*#+",
-"+###############-++++++++++=########################$+++++++++&#+",
-"+###############%+++++++++%#########################-+++++++++-#+",
-"+##############-++++++++++-#########################&+++++++++$#+",
-"+##############%+++++++++%##########################*+++++++++##+",
-"+#############-++++++++++-##########################+++++++++%##+",
-"+#############%++++++++++##########################$+++++++++*##+",
-"+############$++++++++++&##########################=+++++++++=##+",
-"+############=++++++++++$##########################&+++++++++-##+",
-"+############*+++++++++%###########################%+++++++++$##+",
-"+############++++++++++=###########################++++++++++###+",
-"+###########$++++++++++$##########################-+++++++++*###+",
-"+###########=++++++++++###########################=+++++++++&###+",
-"+###########*+++++++++%###########################*+++++++++-###+",
-"+###########%+++++++++&###########################++++++++++$###+",
-"+###########%+++++++++-##########################=++++++++++####+",
-"+###########++++++++++$##########################%+++++++++%####+",
-"+###########++++++++++##########################$++++++++++&####+",
-"+##########$++++++++++##########################&++++++++++=####+",
-"+##########$+++++++++%#########################$+++++++++++-####+",
-"+##########$+++++++++%#########################&+++++++++++$####+",
-"+###########+++++++++*########################$++++++++++++#####+",
-"+###########+++++++++*########################*+++++++++++*#####+",
-"+###########%++++++++%#######################=++++++++++++&#####+",
-"+###########&+++++++++######################$+++++++++++++-#####+",
-"+###########=+++++++++$#####################%+++%+++++++++$#####+",
-"+###########$+++++++++$####################&+++%=+++++++++######+",
-"+############%++++++++&###################=++++$&++++++++%######+",
-"+############-+++++++++$#################&++++=#*++++++++&######+",
-"+#############+++++++++&################*++++*##+++++++++=######+",
-"+#############=+++++++++-#############$%++++%###+++++++++-######+",
-"+##############*+++++++++=##########$*+++++%###$+++++++++#######+",
-"+###############++++++++++&$######$&++++++&####=+++++++++#######+",
-"+###############$++++++++++++%&*%++++++++=#####&++++++++*#######+",
-"+################$%+++++++++++++++++++++-######%++++++++=#######+",
-"+##################&++++++++++++++++++=########+++++++++-#######+",
-"+###################$%+++++++++++++%=#########$+++++++++########+",
-"+#####################$=%++++++%*=-###########=++++++++%########+",
-"+##########################$$$################*++++++++&########+",
-"+#############################################+++++++++=########+",
-"+############################################$+++++++++$########+",
-"+############################################&++++++++%#########+",
-"+############################################+++++++++=#########+",
-"+###########################################=+++++++++##########+",
-"+###########################################%++++++++*##########+",
-"+##########################################=+++++++++-##########+",
-"+#########-==$############################$+++++++++&###########+",
-"+#######=++++++-##########################*+++++++++############+",
-"+######$++++++++$########################=+++++++++-############+",
-"+######=+++++++++$######################-+++++++++&#############+",
-"+######=+++++++++*#####################$+++++++++&##############.",
-".@#####=++++++++++-###################-+++++++++=##############@.",
-".@@####=++++++++++%##################=+++++++++=#############@@@.",
-".@@@###=+++++++++++*###############$*++++++++%$##############@@@.",
-"....++++++++++++++++++++++++++++++++++++++++++++++++++++++++....."};
-
-const char *recOff_xpm[] = {
+const char* giada_icon[] = {
+"65 65 8 1",
+" c #444643",
+". c #565755",
+"+ c #6C6E6B",
+"@ c #898B88",
+"# c #A3A5A2",
+"$ c #BEC0BD",
+"% c #D7DAD6",
+"& c #FCFEFB",
+" ",
+" &&&&&&&&&&&&&&&&&&&&%$@ .@$%&&&&&&&&&&&&&&&&&&&& ",
+" &&&&&&&&&&&&&&&&&%$@. .#%&&&&&&&&&&&&&&&&&& ",
+" &&&&&&&&&&&&&&&&#+ .@$&&&&&&&&&&&&&&&& ",
+" &&&&&&&&&&&&&&$+ .@%&&&&&&&&&&&&&& ",
+" &&&&&&&&&&&&&@ .#&&&&&&&&&&&&& ",
+" &&&&&&&&&&&%. @&&&&&&&&&&&& ",
+" &&&&&&&&&&$ ...+@@#@@+.. +%&&&&&&&&&& ",
+" &&&&&&&&&# .+#$&&&&&&&&&&%$@+. .%&&&&&&&&& ",
+" &&&&&&&&# +#%&&&&&&&&&&&&&&&&&$@. .%&&&&&&&& ",
+" &&&&&&&# .#%&&&&&&&&&&&&&&&&&&&&&%@ .%&&&&&&& ",
+" &&&&&&$ @%&&&&&&&&&&&&&&&&&&&&&&&&&%+ .&&&&&&& ",
+" &&&&&% $&&&&&&&&&&&&&&&&&&&&&&&&&&&&&@ +&&&&&& ",
+" &&&&&. .%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&# @&&&&& ",
+" &&&&@ +%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&# $&&&& ",
+" &&&$ .%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&# +&&&& ",
+" &&&+ %&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&# #&&& ",
+" &&# $&&&&&&&&&&&&&&&&%$###$%&&&&&&&&&&&&&&&&@ +%&& ",
+" &%+ @&&&&&&&&&&&&&&%@.. ..+#&&&&&&&&&&&&&&%. #&& ",
+" &$ .%&&&&&&&&&&&&%+ #&&&&&&&&&&&&&# +&& ",
+" &@ $&&&&&&&&&&&&# +%&&&&&&&&&&&%+ $& ",
+" %. +%&&&&&&&&&&&@ .$&&&&&&&&&&&# @& ",
+" $ #&&&&&&&&&&&@ .$&&&&&&&&&&&+ & ",
+" @ .%&&&&&&&&&&# .%&&&&&&&&&&@ $ ",
+" . +&&&&&&&&&&% +&&&&&&&&&&%. @ ",
+" #&&&&&&&&&&+ @%&&%$+ #&&&&&&&&&&. . ",
+" .%&&&&&&&&&$ +%&&&&&&&# .&&&&&&&&&&@ ",
+" .&&&&&&&&&&+ +&&&&&&&&&&$ $&&&&&&&&&$ ",
+" +&&&&&&&&&& .%&&&&&&&&&&&# @&&&&&&&&&% ",
+" @&&&&&&&&&$ @&&&&&&&&&&&&&. +&&&&&&&&&& ",
+" @&&&&&&&&&# %&&&&&&&&&&&&&# .%&&&&&&&&&. ",
+" #&&&&&&&&&@ .&&&&&&&&&&&&&&$ .%&&&&&&&&&. ",
+" #&&&&&&&&&@ .&&&&&&&&&&&&&&% .#$%&&&&&&&+ ",
+" #&&&&&&&&&@ .&&&&&&&&&&&&&&$ @%&&&&. ",
+" @&&&&&&&&&# %&&&&&&&&&&&&&# @%&&. ",
+" @&&&&&&&&&$ #&&&&&&&&&&&&&. +@@@@+. .$& ",
+" +&&&&&&&&&& .%&&&&&&&&&&&# +$%&&&&&%#. .$ ",
+" .&&&&&&&&&&+ +&&&&&&&&&&$ $&&&&&&&&&&%@ . ",
+" .%&&&&&&&&&$ +%&&&&&&&#. %&&&&&&&&&&&&&# ",
+" #&&&&&&&&&&+ @%&&&$+ $&&&&&&&&&&&&&&&@ . ",
+" . +&&&&&&&&&&% @&&&&&&&&&&&&&&&&%. @ ",
+" @ .%&&&&&&&&&&# %&&&&&&&&&&&&&&&&&@ $ ",
+" $ #&&&&&&&&&&&@ @&&&&&&&&&&&&&&&&&&%. & ",
+" %. +%&&&&&&&&&&&@ $&&&&&&&&&&&&&&&&&&&. @& ",
+" &@ $&&&&&&&&&&&&# %&&&&&&&&&&&&&&&&&&&+ $& ",
+" &$ .%&&&&&&&&&&&&%+ %&&&&&&&&&&&&&&&&&&&@ +&& ",
+" &%+ @&&&&&&&&&&&&&&%@.. .%&&&&&&&&&&&&&&&&&&&@ #&& ",
+" &&# $&&&&&&&&&&&&&&&&%$###$%&&&&&&&&&&&&&&&&&&&&+ +%&& ",
+" &&&+ .%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&. #&&& ",
+" &&&$ .%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&# +&&&& ",
+" &&&&@ +%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&+ $&&&& ",
+" &&&&&. .%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&# @&&&&& ",
+" &&&&&% .$&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&%. +&&&&&& ",
+" &&&&&&$ @%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&%. .&&&&&&& ",
+" &&&&&&&# .$%&&&&&&&&&&&&&&&&&&&&&&&&&&&&%# .%&&&&&&& ",
+" &&&&&&&&# +#%&&&&&&&&&&&&&&&&&$@#$%%$$@+ .%&&&&&&&& ",
+" &&&&&&&&&# .+#%&&&&&&&&&&&$@+. .... .%&&&&&&&&& ",
+" &&&&&&&&&&$ ..+@@###@+... +%&&&&&&&&&& ",
+" &&&&&&&&&&&%. @&&&&&&&&&&&& ",
+" &&&&&&&&&&&&&@ .#&&&&&&&&&&&&& ",
+" &&&&&&&&&&&&&&$+ .@%&&&&&&&&&&&&&& ",
+" &&&&&&&&&&&&&&&&#+ .@$&&&&&&&&&&&&&&&& ",
+" &&&&&&&&&&&&&&&&&%$@. .#%&&&&&&&&&&&&&&&&&& ",
+" &&&&&&&&&&&&&&&&&&&&%$@ .@$%&&&&&&&&&&&&&&&&&&&& ",
+" "};
+
+
+const char* recOff_xpm[] = {
"23 23 8 1",
" c #242523",
". c #342F2E",
" ",
" "};
-const char *recOn_xpm[] = {
+const char* recOn_xpm[] = {
"23 23 8 1",
" c #4D4F4C",
". c #5F4E50",
" ",
" "};
-const char *inputRecOn_xpm[] = {
+const char* inputRecOn_xpm[] = {
"23 23 8 1",
" c #524D4C",
". c #4D4F4C",
".......................",
"......................."};
-const char *inputRecOff_xpm[] = {
+const char* inputRecOff_xpm[] = {
"23 23 8 1",
" c #242523",
". c #252724",
" ",
" "};
-const char *inputToOutputOn_xpm[] = {
+const char* inputToOutputOn_xpm[] = {
"10 10 8 1",
" c #4D4F4C",
". c #585A57",
" ",
" "};
-const char *inputToOutputOff_xpm[] = {
+const char* inputToOutputOff_xpm[] = {
"10 10 8 1",
" c #242523",
". c #2E302D",
" ",
" "};
-const char *muteOff_xpm[] = {
+const char* muteOff_xpm[] = {
"18 18 8 1",
" c #242523",
". c #2E2F2D",
" ",
" "};
-const char *muteOn_xpm[] = {
+const char* muteOn_xpm[] = {
"18 18 8 1",
" c #4D4F4C",
". c #585A57",
" "};
-const char *readActionOff_xpm[] = {
+const char* readActionOff_xpm[] = {
"18 18 8 1",
" c #242523",
". c #393B38",
" "};
-const char *readActionOn_xpm[] = {
+const char* readActionOn_xpm[] = {
"18 18 8 1",
" c #4D4F4C",
". c #696B68",
" "};
-const char *metronomeOff_xpm[] = {
+const char* metronomeOff_xpm[] = {
"13 13 8 1",
" c #242523",
". c #2D2928",
" "};
-const char *metronomeOn_xpm[] = {
+const char* metronomeOn_xpm[] = {
"13 13 8 1",
" c #4D4F4C",
". c #565150",
" "};
-const char *zoomInOff_xpm[] = {
+const char* zoomInOff_xpm[] = {
"18 18 8 1",
" c None",
". c #252525",
"++++++++++++++++++"};
-const char *zoomInOn_xpm[] = {
+const char* zoomInOn_xpm[] = {
"18 18 8 1",
" c None",
". c #4E4E4E",
".................."};
-const char *zoomOutOff_xpm[] = {
+const char* zoomOutOff_xpm[] = {
"18 18 5 1",
" c None",
". c #252525",
"++++++++++++++++++"};
-const char *zoomOutOn_xpm[] = {
+const char* zoomOutOn_xpm[] = {
"18 18 4 1",
" c None",
". c #4E4E4E",
-const char *scrollRightOff_xpm[] = {
+const char* scrollRightOff_xpm[] = {
"12 12 8 1",
" c #181917",
". c #242523",
"............"};
-const char *scrollLeftOff_xpm[] = {
+const char* scrollLeftOff_xpm[] = {
"12 12 8 1",
" c #181917",
". c #242523",
"............"};
-const char *scrollLeftOn_xpm[] = {
+const char* scrollLeftOn_xpm[] = {
"12 12 8 1",
" c #4D4F4C",
". c #6B6D6A",
" "};
-const char *scrollRightOn_xpm[] = {
+const char* scrollRightOn_xpm[] = {
"12 12 8 1",
" c #4D4F4C",
". c #6B6D6A",
" "};
-const char *soloOn_xpm[] = {
+const char* soloOn_xpm[] = {
"18 18 8 1",
" c #4D4F4C",
". c #616360",
" "};
-const char *soloOff_xpm[] = {
+const char* soloOff_xpm[] = {
"18 18 8 1",
" c #242523",
". c #3D3F3D",
#ifdef WITH_VST
-const char *fxOff_xpm[] = {
+const char* fxOff_xpm[] = {
"18 18 8 1",
" c #242523",
". c #40423F",
" "};
-const char *fxOn_xpm[] = {
+const char* fxOn_xpm[] = {
"18 18 8 1",
" c #4D4F4C",
". c #565855",
" "};
-const char *fxShiftUpOff_xpm[] = {
+const char* fxShiftUpOff_xpm[] = {
"18 18 7 1",
" c #242523",
". c #4D4F4C",
" "};
-const char *fxShiftUpOn_xpm[] = {
+const char* fxShiftUpOn_xpm[] = {
"18 18 5 1",
" c #4D4F4C",
". c #70726F",
" "};
-const char *fxShiftDownOff_xpm[] = {
+const char* fxShiftDownOff_xpm[] = {
"18 18 7 1",
" c #242523",
". c #4D4F4C",
" "};
-const char *fxShiftDownOn_xpm[] = {
+const char* fxShiftDownOn_xpm[] = {
"18 18 5 1",
" c #4D4F4C",
". c #70726F",
" "};
-const char *vstLogo_xpm[] = {
+const char* vstLogo_xpm[] = {
"65 38 8 1",
" c #161715",
". c #2B2D2A",
" .......... .@@@@@@@@ "};
-const char *fxRemoveOff_xpm[] = {
+const char* fxRemoveOff_xpm[] = {
"18 18 9 1",
" c None",
". c #242623",
".................."};
-const char *fxRemoveOn_xpm[] = {
+const char* fxRemoveOn_xpm[] = {
"18 18 9 1",
" c None",
". c #4D4F4C",
#endif // #ifdef WITH_VST
-const char *divideOn_xpm[] = {
+const char* divideOn_xpm[] = {
"18 18 7 1",
" c #5A5A5A",
". c #696969",
" "};
-const char *divideOff_xpm[] = {
+const char* divideOff_xpm[] = {
"18 18 8 1",
" c #252525",
". c #3B3B3B",
" "};
-const char *multiplyOn_xpm[] = {
+const char* multiplyOn_xpm[] = {
"18 18 8 1",
" c #595B58",
". c #737572",
" "};
-const char *multiplyOff_xpm[] = {
+const char* multiplyOff_xpm[] = {
"18 18 8 1",
" c #242523",
". c #4A4C49",
" "};
-const char *channelStop_xpm[] = {
+const char* channelStop_xpm[] = {
"18 18 8 1",
" c #242523",
". c #312D2C",
-const char *channelPlay_xpm[] = {
+const char* channelPlay_xpm[] = {
"18 18 8 1",
" c #4D4F4C",
". c #554E56",
" "};
-const char *armOff_xpm[] = {
+const char* armOff_xpm[] = {
"18 18 8 1",
" c #242523",
". c #4F4445",
" "};
-const char *armOn_xpm[] = {
+const char* armOn_xpm[] = {
"18 18 8 1",
" c #4D4F4C",
". c #6B5077",
#ifndef G_GRAPHICS_H
#define G_GRAPHICS_H
-extern const char *giada_logo_xpm[];
+extern const char* giada_logo_xpm[];
-extern const char *loopRepeat_xpm[];
-extern const char *loopBasic_xpm[];
-extern const char *loopOnce_xpm[];
-extern const char *loopOnceBar_xpm[];
-extern const char *oneshotBasic_xpm[];
-extern const char *oneshotRetrig_xpm[];
-extern const char *oneshotPress_xpm[];
-extern const char *oneshotEndless_xpm[];
+extern const char* loopRepeat_xpm[];
+extern const char* loopBasic_xpm[];
+extern const char* loopOnce_xpm[];
+extern const char* loopOnceBar_xpm[];
+extern const char* oneshotBasic_xpm[];
+extern const char* oneshotRetrig_xpm[];
+extern const char* oneshotPress_xpm[];
+extern const char* oneshotEndless_xpm[];
-extern const char *updirOff_xpm[];
-extern const char *updirOn_xpm[];
+extern const char* updirOff_xpm[];
+extern const char* updirOn_xpm[];
-extern const char *pause_xpm[];
-extern const char *play_xpm[];
+extern const char* pause_xpm[];
+extern const char* play_xpm[];
-extern const char *zoomInOff_xpm[];
-extern const char *zoomInOn_xpm[];
-extern const char *zoomOutOff_xpm[];
-extern const char *zoomOutOn_xpm[];
+extern const char* zoomInOff_xpm[];
+extern const char* zoomInOn_xpm[];
+extern const char* zoomOutOff_xpm[];
+extern const char* zoomOutOn_xpm[];
-extern const char *scrollLeftOff_xpm[];
-extern const char *scrollLeftOn_xpm[];
-extern const char *scrollRightOff_xpm[];
-extern const char *scrollRightOn_xpm[];
+extern const char* scrollLeftOff_xpm[];
+extern const char* scrollLeftOn_xpm[];
+extern const char* scrollRightOff_xpm[];
+extern const char* scrollRightOn_xpm[];
-extern const char *rewindOff_xpm[];
-extern const char *rewindOn_xpm[];
+extern const char* rewindOff_xpm[];
+extern const char* rewindOn_xpm[];
-extern const char *recOff_xpm[];
-extern const char *recOn_xpm[];
+extern const char* recOff_xpm[];
+extern const char* recOn_xpm[];
-extern const char *metronomeOff_xpm[];
-extern const char *metronomeOn_xpm[];
+extern const char* metronomeOff_xpm[];
+extern const char* metronomeOn_xpm[];
-extern const char *inputRecOn_xpm[];
-extern const char *inputRecOff_xpm[];
+extern const char* inputRecOn_xpm[];
+extern const char* inputRecOff_xpm[];
-extern const char *inputToOutputOn_xpm[];
-extern const char *inputToOutputOff_xpm[];
+extern const char* inputToOutputOn_xpm[];
+extern const char* inputToOutputOff_xpm[];
-extern const char *divideOn_xpm[];
-extern const char *divideOff_xpm[];
-extern const char *multiplyOn_xpm[];
-extern const char *multiplyOff_xpm[];
+extern const char* divideOn_xpm[];
+extern const char* divideOff_xpm[];
+extern const char* multiplyOn_xpm[];
+extern const char* multiplyOff_xpm[];
-extern const char *muteOff_xpm[];
-extern const char *muteOn_xpm[];
+extern const char* muteOff_xpm[];
+extern const char* muteOn_xpm[];
-extern const char *soloOff_xpm[];
-extern const char *soloOn_xpm[];
+extern const char* soloOff_xpm[];
+extern const char* soloOn_xpm[];
-extern const char *armOff_xpm[];
-extern const char *armOn_xpm[];
+extern const char* armOff_xpm[];
+extern const char* armOn_xpm[];
-extern const char *readActionOn_xpm[];
-extern const char *readActionOff_xpm[];
+extern const char* readActionOn_xpm[];
+extern const char* readActionOff_xpm[];
-extern const char *channelStop_xpm[];
-extern const char *channelPlay_xpm[];
+extern const char* channelStop_xpm[];
+extern const char* channelPlay_xpm[];
#ifdef WITH_VST
-extern const char *fxOff_xpm[];
-extern const char *fxOn_xpm[];
+extern const char* fxOff_xpm[];
+extern const char* fxOn_xpm[];
-extern const char *fxShiftUpOn_xpm[];
-extern const char *fxShiftUpOff_xpm[];
-extern const char *fxShiftDownOn_xpm[];
-extern const char *fxShiftDownOff_xpm[];
+extern const char* fxShiftUpOn_xpm[];
+extern const char* fxShiftUpOff_xpm[];
+extern const char* fxShiftDownOn_xpm[];
+extern const char* fxShiftDownOff_xpm[];
-extern const char *fxRemoveOff_xpm[];
-extern const char *fxRemoveOn_xpm[];
+extern const char* fxRemoveOff_xpm[];
+extern const char* fxRemoveOn_xpm[];
-extern const char *vstLogo_xpm[];
+extern const char* vstLogo_xpm[];
#endif
-extern const char *giada_icon[];
+extern const char* giada_icon[];
#endif
/* -------------------------------------------------------------------------- */
-void MidiChannel::setMute(bool value, EventType eventType)
+void MidiChannel::setMute(bool value)
{
midiChannelProc::setMute(this, value);
}
while (true) {
if (pthread_mutex_trylock(&pluginHost::mutex_midi) != 0)
continue;
- gu_log("[Channel::processMidi] msg=%X\n", midiEventFlat.getRaw());
+ gu_log("[MidiChannel::processMidi] msg=%X\n", midiEventFlat.getRaw());
addVstMidiEvent(midiEventFlat.getRaw(), 0);
pthread_mutex_unlock(&pluginHost::mutex_midi);
break;
void stopBySeq(bool chansStopOnSeqHalt) override;
void stop() override {};
void rewindBySeq() override;
- void setMute(bool value, giada::EventType eventType) override;
+ void setMute(bool value) override;
void readPatch(const std::string& basePath, int i) override;
void writePatch(int i, bool isProject) override;
void receiveMidi(const giada::m::MidiEvent& midiEvent) override;
void processPlugins(Channel* ch, const MidiEvent& midiEvent)
{
- /* Pure value: if 'noNoteOff' in global config, get the raw value with the
- 'velocy' byte. Otherwise strip it off. */
-
- uint32_t pure = midiEvent.getRaw(conf::noNoteOff);
+ uint32_t pure = midiEvent.getRawNoVelocity();
/* Plugins' parameters layout reflects the structure of the matrix
Channel::midiInPlugins. It is safe to assume then that i (i.e. Plugin*) and k
void processChannels(const MidiEvent& midiEvent)
{
- /* Pure value: if 'noNoteOff' in global config, get the raw value with the
- 'velocy' byte. Otherwise strip it off. */
-
- uint32_t pure = midiEvent.getRaw(conf::noNoteOff);
+ uint32_t pure = midiEvent.getRawNoVelocity();
for (Channel* ch : mixer::channels) {
}
#ifdef WITH_VST
- processPlugins(ch, midiEvent); // Process plugins' parameters
+
+ /* Process learned plugins parameters. */
+ processPlugins(ch, midiEvent);
+
#endif
/* Redirect full midi message (pure + velocity) to plugins. */
-
ch->receiveMidi(midiEvent.getRaw());
}
}
void processMaster(const MidiEvent& midiEvent)
{
- /* Pure value: if 'noNoteOff' in global config, get the raw value with the
- 'velocy' byte. Otherwise strip it off. */
-
- uint32_t pure = midiEvent.getRaw(conf::noNoteOff);
+ uint32_t pure = midiEvent.getRawNoVelocity();
if (pure == conf::midiInRewind) {
gu_log(" >>> rewind (master) (pure=0x%X)\n", pure);
void dispatch(int byte1, int byte2, int byte3)
{
/* Here we want to catch two things: a) note on/note off from a keyboard and
- b) knob/wheel/slider movements from a controller. */
+ b) knob/wheel/slider movements from a controller.
+ We must also fix the velocity zero issue for those devices that sends NOTE
+ OFF events as NOTE ON + velocity zero. Let's make it a real NOTE OFF event. */
MidiEvent midiEvent(byte1, byte2, byte3);
+ midiEvent.fixVelocityZero();
gu_log("[midiDispatcher] MIDI received - 0x%X (chan %d)\n", midiEvent.getRaw(),
midiEvent.getChannel());
/* Start dispatcher. If midi learn is on don't parse channels, just learn
- incoming MIDI signal. Learn callback wants 'pure' MIDI event: if 'noNoteOff'
- in global config, get the raw value with the 'velocy' byte. Otherwise strip it
- off. If midi learn is off process master events first, then each channel
- in the stack. This way incoming signals don't get processed by glue_* when
- MIDI learning is on. */
+ incoming MIDI signal. Learn callback wants 'pure' MIDI event, i.e. with
+ velocity value stripped off. If midi learn is off process master events first,
+ then each channel in the stack. This way incoming signals don't get processed
+ by glue_* when MIDI learning is on. */
if (cb_learn)
- cb_learn(midiEvent.getRaw(conf::noNoteOff), cb_data);
+ cb_learn(midiEvent.getRawNoVelocity(), cb_data);
else {
processMaster(midiEvent);
processChannels(midiEvent);
* -------------------------------------------------------------------------- */
+#include <cassert>
#include "midiEvent.h"
namespace m
{
MidiEvent::MidiEvent()
- : m_raw (0x0),
- m_status (0),
+ : m_status (0),
m_channel (0),
m_note (0),
m_velocity(0),
MidiEvent::MidiEvent(uint32_t raw)
- : m_raw (raw),
- m_status ((raw & 0xF0000000) >> 24),
+ : m_status ((raw & 0xF0000000) >> 24),
m_channel ((raw & 0x0F000000) >> 24),
m_note ((raw & 0x00FF0000) >> 16),
m_velocity((raw & 0x0000FF00) >> 8),
/* -------------------------------------------------------------------------- */
+
MidiEvent::MidiEvent(int byte1, int byte2, int byte3)
: MidiEvent((byte1 << 24) | (byte2 << 16) | (byte3 << 8) | (0x00))
{
void MidiEvent::setChannel(int c)
{
+ assert(c >= 0 && c < G_MAX_MIDI_CHANS);
m_channel = c;
}
+void MidiEvent::setVelocity(int v)
+{
+ assert(v >= 0 && v < G_MAX_VELOCITY);
+ m_velocity = v;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void MidiEvent::fixVelocityZero()
+{
+ if (m_status == NOTE_ON && m_velocity == 0)
+ m_status = NOTE_OFF;
+}
+
+
/* -------------------------------------------------------------------------- */
}
-uint32_t MidiEvent::getRaw(bool velocity) const
+/* -------------------------------------------------------------------------- */
+
+
+uint32_t MidiEvent::getRaw() const
+{
+ return (m_status << 24) | (m_channel << 24) | (m_note << 16) | (m_velocity << 8) | (0x00);
+}
+
+
+uint32_t MidiEvent::getRawNoVelocity() const
{
- if (!velocity)
- return m_raw & 0xFFFF0000;
- return m_raw;
+ return (m_status << 24) | (m_channel << 24) | (m_note << 16) | (0x00 << 8) | (0x00);
}
{
public:
- static const int NOTE_ON = 0x90;
+ static const int NOTE_ON = 0x90;
static const int NOTE_OFF = 0x80;
MidiEvent();
bool isNoteOnOff() const;
int getDelta() const;
- /* getRaw
- Returns the raw message. If 'velocity' is false, velocity byte is stripped
- out. */
+ /* getRaw(), getRawNoVelocity()
+ Returns the raw MIDI message. If getRawNoVelocity(), the velocity value is
+ stripped off (i.e. velocity == 0). */
- uint32_t getRaw(bool velocity=true) const;
+ uint32_t getRaw() const;
+ uint32_t getRawNoVelocity() const;
void resetDelta();
void setChannel(int c);
+ void setVelocity(int v);
+
+ /* fixVelocityZero()
+ According to the MIDI standard, there is a special case if the velocity is
+ set to zero. The NOTE ON message then has the same meaning as a NOTE OFF
+ message, switching the note off. Let's fix it. Sometime however you do want
+ a NOTE ON with velocity zero: setting velocity to 0 in MIDI action editor to
+ mute a specific event. */
+
+ void fixVelocityZero();
private:
- uint32_t m_raw;
int m_status;
int m_channel;
int m_note;
{
namespace
{
-#define TICKSIZE 38
-
+constexpr Frame TICKSIZE = 38;
float tock[TICKSIZE] = {
0.059033, 0.117240, 0.173807, 0.227943, 0.278890, 0.325936,
-0.070862, -0.048844
};
-
float tick[TICKSIZE] = {
0.175860, 0.341914, 0.488904, 0.608633, 0.694426, 0.741500,
0.747229, 0.711293, 0.635697, 0.524656, 0.384362, 0.222636,
0.069639, 0.031320
};
-
AudioBuffer vChanInput; // virtual channel for recording
AudioBuffer vChanInToOut; // virtual channel in->out bridge (hear what you're playin)
-int tickTracker, tockTracker = 0;
-bool tickPlay, tockPlay = false; // 1 = play, 0 = stop
+Frame tickTracker = 0;
+Frame tockTracker = 0;
+bool tickPlay = false;
+bool tockPlay = false;
/* inputTracker
Sample position while recording. */
-int inputTracker = 0;
+Frame inputTracker = 0;
/* -------------------------------------------------------------------------- */
/* computePeak */
-void computePeak(const AudioBuffer& buf, float& peak, unsigned frame)
+void computePeak(const AudioBuffer& buf, float& peak, Frame frame)
{
for (int i=0; i<buf.countChannels(); i++)
if (buf[frame][i] > peak)
/* -------------------------------------------------------------------------- */
-
/* lineInRec
Records from line in. */
/* -------------------------------------------------------------------------- */
-/* test*
-Checks if the sequencer has reached a specific point (bar, first beat or
-last frame). */
-void testBar(unsigned frame)
+void renderMetronome()
{
- if (clock::isOnBar() && metronome)
+ if (!metronome)
+ return;
+ if (clock::isOnBar() || clock::isOnFirstBeat())
tickPlay = true;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void testLastBeat()
-{
- if (clock::isOnBeat() && metronome && !tickPlay)
+ else
+ if (clock::isOnBeat())
tockPlay = true;
}
}; // {anonymous}
/* -------------------------------------------------------------------------- */
-void init(int framesInSeq, int framesInBuffer)
+void init(Frame framesInSeq, Frame framesInBuffer)
{
/* Allocate virtual input channels. vChanInput has variable size: it depends
on how many frames there are in sequencer. */
/* -------------------------------------------------------------------------- */
-void allocVirtualInput(int frames)
+void allocVirtualInput(Frame frames)
{
vChanInput.alloc(frames, G_MAX_IO_CHANS);
}
lineInRec(in, j); // TODO - can go outside this loop
doQuantize(j);
- testBar(j);
- testLastBeat();
+ renderMetronome();
clock::incrCurrentFrame();
clock::sendMIDIsync();
}
#include <pthread.h>
#include <vector>
#include "recorder.h"
+#include "types.h"
#include "../deps/rtaudio-mod/RtAudio.h"
{
struct FrameEvents
{
- int frameLocal;
- int frameGlobal;
+ Frame frameLocal;
+ Frame frameGlobal;
bool doQuantize;
bool onBar;
bool onFirstBeat;
std::vector<recorder::action*> actions;
};
-enum { // const - what to do when a fadeout ends
- DO_STOP = 0x01,
- DO_MUTE = 0x02,
- DO_MUTE_I = 0x04
-};
-
-enum { // const - fade types
- FADEOUT = 0x01,
- XFADE = 0x02
-};
-
extern std::vector<Channel*> channels;
extern bool recording; // is recording something?
extern pthread_mutex_t mutex;
-void init(int framesInSeq, int framesInBuffer);
+void init(Frame framesInSeq, Frame framesInBuffer);
/* allocVirtualInput
Allocates new memory for the virtual input channel. Call this whenever you
shrink or resize the sequencer. */
-void allocVirtualInput(int frames);
+void allocVirtualInput(Frame frames);
void close();
#ifdef WITH_VST
+#include <cassert>
#include <FL/Fl.H>
#include "../utils/log.h"
#include "../utils/time.h"
id (idGenerator++),
bypass(false)
{
+ using namespace juce;
+
/* Init midiInParams. All values are empty (0x0): they will be filled during
midi learning process. */
- for (int i=0; i<plugin->getNumParameters(); i++)
+ const OwnedArray<AudioProcessorParameter>& params = plugin->getParameters();
+ for (int i=0; i<params.size(); i++)
midiInParams.push_back(0x0);
plugin->prepareToPlay(samplerate, buffersize);
gu_log("[Plugin::showEditor] unable to create editor!\n");
return;
}
-
- /* A silly workaround on X: it seems that calling addToDesktop too fast, i.e.
- before the X Window is fully ready screws up the plugin's event dispatcher. */
-
-#ifdef G_OS_LINUX
- time::sleep(500);
-#endif
-
ui->setOpaque(true);
ui->addToDesktop(0, parent);
}
string Plugin::getUniqueId() const
{
- //return plugin->getPluginDescription().fileOrIdentifier.toStdString();
return plugin->getPluginDescription().createIdentifierString().toStdString();
}
int Plugin::getNumParameters() const
{
- return plugin->getNumParameters();
+ return plugin->getParameters().size();
}
float Plugin::getParameter(int paramIndex) const
{
- return plugin->getParameter(paramIndex);
+ return plugin->getParameters()[paramIndex]->getValue();
}
void Plugin::setParameter(int paramIndex, float value) const
{
- return plugin->setParameter(paramIndex, value);
+ plugin->getParameters()[paramIndex]->setValue(value);
}
/* -------------------------------------------------------------------------- */
-int Plugin::getEditorW() const { return ui->getWidth(); }
-int Plugin::getEditorH() const { return ui->getHeight(); }
+int Plugin::getEditorW() const { assert(ui != nullptr); return ui->getWidth(); }
+int Plugin::getEditorH() const { assert(ui != nullptr); return ui->getHeight(); }
/* -------------------------------------------------------------------------- */
string Plugin::getParameterName(int index) const
{
- return plugin->getParameterName(index).toStdString();
+ return plugin->getParameters()[index]->getName(MAX_LABEL_SIZE).toStdString();
}
string Plugin::getParameterText(int index) const
{
- return plugin->getParameterText(index).toStdString();
+ return plugin->getParameters()[index]->getCurrentValueAsText().toStdString();
}
string Plugin::getParameterLabel(int index) const
{
- return plugin->getParameterLabel(index).toStdString();
+ return plugin->getParameters()[index]->getLabel().toStdString();
}
void Plugin::closeEditor()
{
- if (ui == nullptr)
- return;
delete ui;
ui = nullptr;
}
*
* Giada - Your Hardcore Loopmachine
*
- * plugin
- *
* -----------------------------------------------------------------------------
*
* Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
{
private:
+ static const int MAX_LABEL_SIZE = 64;
+
static int idGenerator;
juce::AudioProcessorEditor* ui; // gui
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#ifndef G_RANGE_H
+#define G_RANGE_H
+
+
+#include <cassert>
+
+
+namespace giada
+{
+template<typename T>
+class Range
+{
+private:
+
+ T m_a;
+ T m_b;
+
+public:
+
+ Range() : m_a(0), m_b(0) {}
+ Range(T a, T b) : m_a(a), m_b(b) { assert(a < b); }
+
+ T getBegin() const { return m_a; }
+ T getEnd() const { return m_b; }
+ T getLength() const { return m_b - m_a; }
+};
+} // giada::
+
+
+#endif
/* fixOverdubTruncation
Fixes underlying action truncation when overdubbing over a longer action. I.e.:
- Original: |#############|
- Overdub: ---|#######|---
- fix: |#||#######|--- */
+ Original: |#############|
+ Overdub: ---|#######|---
+ fix: |#||#######|--- */
void fixOverdubTruncation(const Composite& comp, pthread_mutex_t* mixerMutex)
{
- action* next = nullptr;
- int res = getNextAction(comp.a2.chan, comp.a1.type | comp.a2.type, comp.a2.frame,
- &next);
- if (res != 1 || next->type != comp.a2.type)
- return;
- gu_log("[recorder::fixOverdubTruncation] add truncation at frame %d, type=%d\n",
- next->frame, next->type);
- deleteAction(next->chan, next->frame, next->type, false, mixerMutex);
+ action* next = nullptr;
+ int res = getNextAction(comp.a2.chan, comp.a1.type | comp.a2.type, comp.a2.frame,
+ &next);
+ if (res != 1 || next->type != comp.a2.type)
+ return;
+ gu_log("[recorder::fixOverdubTruncation] add truncation at frame %d, type=%d\n",
+ next->frame, next->type);
+ deleteAction(next->chan, next->frame, next->type, false, mixerMutex);
}
}; // {anonymous}
void init()
{
active = false;
- sortedActions = false;
+ sortedActions = false;
clearAll();
}
bool canRec(Channel* ch, bool clockRunning, bool mixerRecording)
{
/* Can record on a channel if:
- - recorder is on
- - mixer is running
- - mixer is not recording a take somewhere
- - channel is SAMPLE type and has data in it */
- return active && clockRunning && !mixerRecording && ch->type == ChannelType::SAMPLE && ch->hasData();
+ - recorder is on
+ - mixer is running
+ - mixer is not recording a take somewhere
+ - channel is MIDI or SAMPLE type with data in it */
+ return active && clockRunning && !mixerRecording &&
+ (ch->type == ChannelType::MIDI || (ch->type == ChannelType::SAMPLE && ch->hasData()));
}
for (unsigned t=0; t<global.at(frameToExpand).size(); t++) {
action* ac = global.at(frameToExpand).at(t);
if (ac->chan == index &&
- ac->type == type &&
- ac->frame == frame &&
- ac->iValue == iValue &&
- ac->fValue == fValue)
+ ac->type == type &&
+ ac->frame == frame &&
+ ac->iValue == iValue &&
+ ac->fValue == fValue)
return;
}
for (unsigned i=0; i<frames.size() && !found; i++) {
if (frames.at(i) != frame)
- continue;
+ continue;
/* find the action in frame i */
if (checkValues)
doit &= (a->iValue == iValue && a->fValue == fValue);
- if (!doit)
- continue;
-
- while (true) {
- if (pthread_mutex_trylock(mixerMutex)) {
- free(a);
- global.at(i).erase(global.at(i).begin() + j);
- pthread_mutex_unlock(mixerMutex);
- found = true;
- break;
- }
- else
- gu_log("[recorder::deleteAction] waiting for mutex...\n");
- }
+ if (!doit)
+ continue;
+
+ while (true) {
+ if (pthread_mutex_trylock(mixerMutex)) {
+ free(a);
+ global.at(i).erase(global.at(i).begin() + j);
+ pthread_mutex_unlock(mixerMutex);
+ found = true;
+ break;
+ }
+ else
+ gu_log("[recorder::deleteAction] waiting for mutex...\n");
+ }
}
}
if (found) {
void deleteActions(int chan, int frame_a, int frame_b, char type,
- pthread_mutex_t* mixerMutex)
+ pthread_mutex_t* mixerMutex)
{
sortActions();
vector<int> dels;
/* -------------------------------------------------------------------------- */
-bool hasActions(int chanIndex)
+bool hasActions(int chanIndex, int type)
{
- if (global.size() == 0)
- return false;
+ if (global.size() == 0)
+ return false;
for (unsigned i=0; i<global.size(); i++) {
for (unsigned j=0; j<global.at(i).size(); j++) {
if (global.at(i).at(j)->chan == chanIndex)
- return true;
+ if (type == -1 || global.at(i).at(j)->type == type)
+ return true;
}
}
return false;
{
/* prepare the composite struct */
- if (actionMask == G_ACTION_KEYS) {
- cmp.a1.type = G_ACTION_KEYPRESS;
- cmp.a2.type = G_ACTION_KEYREL;
- }
- else {
- cmp.a1.type = G_ACTION_MUTEON;
- cmp.a2.type = G_ACTION_MUTEOFF;
- }
+ cmp.a1.type = G_ACTION_KEYPRESS;
+ cmp.a2.type = G_ACTION_KEYREL;
cmp.a1.chan = index;
cmp.a2.chan = index;
cmp.a1.frame = frame;
bool nullLoop = false;
/* Check for ring loops or null loops. Ring loop: a composite action with
- key_press at frame N and key_release at frame M, with M <= N.
- Null loop: a composite action that begins and ends on the very same frame,
- i.e. with 0 size. Very unlikely.
- If ring loop: record the last action at the end of the sequencer (that
- is 'totalFrames').
- If null loop: remove previous action and do nothing. Also make sure to avoid
- underlying action truncation, if the null loop occurs inside a composite
- action. */
+ key_press at frame N and key_release at frame M, with M <= N.
+ Null loop: a composite action that begins and ends on the very same frame,
+ i.e. with 0 size. Very unlikely.
+ If ring loop: record the last action at the end of the sequencer (that
+ is 'totalFrames').
+ If null loop: remove previous action and do nothing. Also make sure to avoid
+ underlying action truncation, if the null loop occurs inside a composite
+ action. */
if (cmp.a2.frame < cmp.a1.frame) { // ring loop
ringLoop = true;
nullLoop = true;
gu_log("[recorder::stopOverdub] null loop! frame1=%d == frame2=%d\n", cmp.a1.frame, cmp.a2.frame);
deleteAction(cmp.a1.chan, cmp.a1.frame, cmp.a1.type, false, mixerMutex); // false == don't check values
- fixOverdubTruncation(cmp, mixerMutex);
- }
+ fixOverdubTruncation(cmp, mixerMutex);
+ }
- if (nullLoop)
- return;
+ if (nullLoop)
+ return;
/* Remove any nested action between keypress----keyrel. */
deleteActions(cmp.a2.chan, cmp.a1.frame, cmp.a2.frame, cmp.a1.type, mixerMutex);
deleteActions(cmp.a2.chan, cmp.a1.frame, cmp.a2.frame, cmp.a2.type, mixerMutex);
- if (ringLoop)
- return;
+ if (ringLoop)
+ return;
- /* Record second part of the composite action. Also make sure to avoid
- underlying action truncation, if keyrel happens inside a composite action. */
+ /* Record second part of the composite action. Also make sure to avoid
+ underlying action truncation, if keyrel happens inside a composite action. */
rec(cmp.a2.chan, cmp.a2.type, cmp.a2.frame);
- fixOverdubTruncation(cmp, mixerMutex);
+ fixOverdubTruncation(cmp, mixerMutex);
}
void forEachAction(std::function<void(const action*)> f)
{
+
for (const vector<action*> actions : recorder::global)
for (const action* action : actions)
f(action);
/* hasActions
Checks if the channel has at least one action recorded. Used after an
-action deletion. */
+action deletion. Type != -1: check if channel has actions of type 'type'.*/
-bool hasActions(int chanIndex);
+bool hasActions(int chanIndex, int type=-1);
/* canRec
* can a channel rec an action? Call this one BEFORE rec(). */
}
-void SampleChannel::recordMute()
-{
- sampleChannelRec::recordMute(this);
-}
-
-
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
-void SampleChannel::setMute(bool value, EventType eventType)
+void SampleChannel::setMute(bool value)
{
- sampleChannelProc::setMute(this, value, eventType);
+ sampleChannelProc::setMute(this, value);
}
bool recordStart(bool canQuantize) override;
bool recordKill() override;
void recordStop() override;
- void recordMute() override;
- void setMute(bool value, giada::EventType eventType) override;
+ void setMute(bool value) override;
void startReadingActions(bool treatRecsAsLoops, bool recsStopOnChanHalt) override;
void stopReadingActions(bool running, bool treatRecsAsLoops,
bool recsStopOnChanHalt) override;
#include <cassert>
#include "../utils/math.h"
+#include "const.h"
#include "pluginHost.h"
#include "sampleChannel.h"
#include "sampleChannelProc.h"
-#include "clock.h"
namespace giada {
void rewind_(SampleChannel* ch, int localFrame)
{
ch->tracker = ch->begin;
- ch->mute_i = false;
ch->qWait = false; // Was in qWait mode? Reset occured, no more qWait now.
/* On rewind, if channel is playing fill again buffer to create something like
for (int i=0; i<out.countFrames(); i++) {
if (running)
ch->calcVolumeEnvelope();
- if (!ch->mute && !ch->mute_i)
+ if (!ch->mute)
for (int j=0; j<out.countChannels(); j++)
out[i][j] += ch->buffer[i][j] * ch->volume * ch->volume_i * ch->calcPanning(j) * ch->boost;
}
/* -------------------------------------------------------------------------- */
-void setMute(SampleChannel* ch, bool value, EventType eventType)
+void setMute(SampleChannel* ch, bool value)
{
- eventType == EventType::MANUAL ? ch->mute = value : ch->mute_i = value;
+ ch->mute = value;
ch->sendMidiLmute();
}
/* For one-shot modes, velocity drives the internal volume. */
if (velocity != 0) {
if (ch->isAnySingleMode() && ch->midiInVeloAsVol)
- ch->volume_i = u::math::map<float>(velocity, 0.0f, 127.0f, 0.0f, 1.0f);
+ ch->volume_i = u::math::map<int, float>(velocity, 0, G_MAX_VELOCITY, 0.0, 1.0);
}
switch (ch->status) {
void start(SampleChannel* ch, int localFrame, bool doQuantize, int velocity);
-void setMute(SampleChannel* ch, bool value, EventType eventType);
+void setMute(SampleChannel* ch, bool value);
}}};
* -------------------------------------------------------------------------- */
+#include <cassert>
#include "const.h"
#include "conf.h"
#include "clock.h"
* is not found. */
res = recorder::getAction(ch->index, G_ACTION_VOLUME, globalFrame, &a0);
- if (res == 0)
- return;
+
+ assert(res != 0);
/* get the action next to this one.
* res == -1: a1 not found, this is the last one. Rewind the search
* res == -2 G_ACTION_VOLUME not found. This should never happen */
res = recorder::getNextAction(ch->index, G_ACTION_VOLUME, globalFrame, &a1);
-
if (res == -1)
res = recorder::getAction(ch->index, G_ACTION_VOLUME, 0, &a1);
+ assert(res != -2);
+
ch->volume_i = a0->fValue;
ch->volume_d = ((a1->fValue - a0->fValue) / (a1->frame - a0->frame)) * 1.003f;
}
if (ch->isAnySingleMode())
ch->kill(localFrame);
break;
- case G_ACTION_MUTEON:
- ch->setMute(true, EventType::AUTO);
- break;
- case G_ACTION_MUTEOFF:
- ch->setMute(false, EventType::AUTO);
- break;
case G_ACTION_VOLUME:
calcVolumeEnv_(ch, globalFrame);
break;
/* -------------------------------------------------------------------------- */
-void recordMute(SampleChannel* ch)
-{
- if (recorderCanRec_(ch)) {
- if (!ch->mute) {
- recorder::startOverdub(ch->index, G_ACTION_MUTES, clock::getCurrentFrame(),
- kernelAudio::getRealBufSize());
- ch->readActions = false; // don't read actions while overdubbing
- }
- else
- recorder::stopOverdub(clock::getCurrentFrame(), clock::getFramesInLoop(),
- &mixer::mutex);
- }
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
bool recordStart(SampleChannel* ch, bool canQuantize)
{
/* Record a 'start' event if the quantizer is off, otherwise let mixer to
void recordStop(SampleChannel* ch);
-void recordMute(SampleChannel* ch);
-
/* setReadActions
If enabled (v == true), Recorder will read actions from channel 'ch'. If
recsStopOnChanHalt == true and v == false, will also kill the channel. */
namespace giada
{
+using Pixel = int;
+using Frame = int;
+
+
enum class ChannelType : int
{
SAMPLE = 1, MIDI
*
* Giada - Your Hardcore Loopmachine
*
- * wave
- *
* -----------------------------------------------------------------------------
*
* Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* Giada - Your Hardcore Loopmachine
*
- * wave
- *
* -----------------------------------------------------------------------------
*
* Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
void toggleMute(Channel* ch, bool gui)
{
- ch->setMute(!ch->mute, EventType::MANUAL);
+ ch->setMute(!ch->mute);
if (!gui) {
Fl::lock();
ch->guiChannel->mute->value(ch->mute);
void keyPress(Channel* ch, bool ctrl, bool shift, int velocity)
{
/* Everything occurs on frame 0 here: they are all user-generated events. */
- if (ctrl) {
- ch->recordMute();
+ if (ctrl)
c::channel::toggleMute(ch);
- }
else
if (shift) {
if (ch->recordKill())
* -------------------------------------------------------------------------- */
+#include <cassert>
#include "../gui/dialogs/gd_warnings.h"
#include "../gui/elems/mainWindow/keyboard/channel.h"
#include "../gui/elems/mainWindow/keyboard/sampleChannel.h"
#include "../core/kernelMidi.h"
#include "../core/channel.h"
#include "../core/recorder.h"
+#include "../core/mixer.h"
+#include "../core/sampleChannel.h"
+#include "../core/midiChannel.h"
#include "../utils/gui.h"
#include "../utils/log.h"
#include "recorder.h"
{
namespace
{
-void updateChannel(geChannel* gch)
+void updateChannel(geChannel* gch, bool refreshActionEditor=true)
{
gch->ch->hasActions = m::recorder::hasActions(gch->ch->index);
- if (gch->ch->type == ChannelType::SAMPLE && !gch->ch->hasActions)
- static_cast<geSampleChannel*>(gch)->hideActionButton();
- /* TODO - set mute=false */
- gu_refreshActionEditor(); // refresh a.editor window, it could be open
+ if (gch->ch->type == ChannelType::SAMPLE) {
+ geSampleChannel* gsch = static_cast<geSampleChannel*>(gch);
+ gsch->ch->hasActions ? gsch->showActionButton() : gsch->hideActionButton();
+ }
+ if (refreshActionEditor)
+ gu_refreshActionEditor(); // refresh a.editor window, it could be open
}
-}; // {namespace}
+}; // {anonymous}
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
-void clearMuteActions(geChannel* gch)
+bool midiActionCanFit(int chan, int note, int frame_a, int frame_b)
{
- if (!gdConfirmWin("Warning", "Clear all mute actions: are you sure?"))
- return;
- m::recorder::clearAction(gch->ch->index, G_ACTION_MUTEON | G_ACTION_MUTEOFF);
- updateChannel(gch);
+ namespace mr = m::recorder;
+
+ /* TODO - This is insane, to say the least. Let's wait for recorder refactoring... */
+
+ vector<mr::Composite> comps = getMidiActions(chan);
+ for (mr::Composite c : comps)
+ if (frame_b >= c.a1.frame && c.a2.frame >= frame_a && m::MidiEvent(c.a1.iValue).getNote() == note)
+ return false;
+ return true;
+}
+
+
+bool sampleActionCanFit(const SampleChannel* ch, int frame_a, int frame_b)
+{
+ namespace mr = m::recorder;
+
+ /* TODO - Even more insanity... Let's wait for recorder refactoring... */
+
+ vector<mr::Composite> comps = getSampleActions(ch);
+ for (mr::Composite c : comps)
+ if (frame_b >= c.a1.frame && c.a2.frame >= frame_a)
+ return false;
+ return true;
}
/* -------------------------------------------------------------------------- */
-void recordMidiAction(int chan, int note, int frame_a, int frame_b)
+void recordMidiAction(int chan, int note, int velocity, int frame_a, int frame_b)
{
if (frame_b == 0)
- frame_b = frame_a + G_DEFAULT_MIDI_ACTION_SIZE;
+ frame_b = frame_a + G_DEFAULT_ACTION_SIZE;
/* Avoid frame overflow. */
frame_a -= overflow;
}
- /* Prepare MIDI events, with maximum velocity (0x3F) for now. */
+ /* Prepare MIDI events. Due to some nasty restrictions on the ancient Recorder,
+ checks for overlapping are done by the caller. TODO ... */
- m::MidiEvent event_a = m::MidiEvent(m::MidiEvent::NOTE_ON, note, 0x3F);
- m::MidiEvent event_b = m::MidiEvent(m::MidiEvent::NOTE_OFF, note, 0x3F);
+ m::MidiEvent event_a = m::MidiEvent(m::MidiEvent::NOTE_ON, note, velocity);
+ m::MidiEvent event_b = m::MidiEvent(m::MidiEvent::NOTE_OFF, note, velocity);
- /* Avoid overlapping actions. Find the next action past frame_a and compare
- its frame: if smaller than frame_b, an overlap occurs. Shrink the new action
- accordingly. */
+ m::recorder::rec(chan, G_ACTION_MIDI, frame_a, event_a.getRaw());
+ m::recorder::rec(chan, G_ACTION_MIDI, frame_b, event_b.getRaw());
+}
- m::recorder::action* next = nullptr;
- m::recorder::getNextAction(chan, G_ACTION_MIDI, frame_a, &next, event_a.getRaw(),
- 0x0000FF00);
- if (next != nullptr && next->frame <= frame_b) {
- frame_b = next->frame - 2;
- gu_log("[recorder::recordMidiAction] Shrink new action, due to overlap\n");
+/* -------------------------------------------------------------------------- */
+
+
+void deleteMidiAction(MidiChannel* ch, m::recorder::action a1, m::recorder::action a2)
+{
+ m::recorder::deleteAction(ch->index, a1.frame, G_ACTION_MIDI, true,
+ &m::mixer::mutex, a1.iValue, 0.0);
+
+ /* If action 1 is not orphaned, send a note-off first in case we are deleting
+ it in a middle of a key_on/key_off sequence. Conversely, orphaned actions
+ should not play, so no need to fire the note-off. */
+
+ if (a2.frame != -1) {
+ ch->sendMidi(a2.iValue);
+ m::recorder::deleteAction(ch->index, a2.frame, G_ACTION_MIDI, true,
+ &m::mixer::mutex, a2.iValue, 0.0);
}
- m::recorder::rec(chan, G_ACTION_MIDI, frame_a, event_a.getRaw());
- m::recorder::rec(chan, G_ACTION_MIDI, frame_b, event_b.getRaw());
+ ch->hasActions = m::recorder::hasActions(ch->index);
+}
+
+/* -------------------------------------------------------------------------- */
+
+
+void recordSampleAction(SampleChannel* ch, int type, int frame_a, int frame_b)
+{
+ if (ch->mode == ChannelMode::SINGLE_PRESS) {
+ m::recorder::rec(ch->index, G_ACTION_KEYPRESS, frame_a);
+ m::recorder::rec(ch->index, G_ACTION_KEYREL, frame_b == 0 ? frame_a + G_DEFAULT_ACTION_SIZE : frame_b);
+ }
+ else
+ m::recorder::rec(ch->index, type, frame_a);
+
+ updateChannel(ch->guiChannel, /*refreshActionEditor=*/false);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void recordEnvelopeAction(Channel* ch, int type, int frame, float fValue)
+{
+ namespace mr = m::recorder;
+
+ if (!mr::hasActions(ch->index, type)) { // First action ever? Add actions at boundaries.
+ mr::rec(ch->index, type, 0, 0, 1.0);
+ mr::rec(ch->index, type, m::clock::getFramesInLoop() - 1, 0, 1.0);
+ }
+ mr::rec(ch->index, type, frame, 0, fValue);
+
+ updateChannel(ch->guiChannel, /*refreshActionEditor=*/false);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void deleteEnvelopeAction(Channel* ch, m::recorder::action a, bool moved)
+{
+ namespace mr = m::recorder;
+
+ /* Deleting first or last action: clear everything. Otherwise delete the
+ selected action only. */
+
+ if (!moved && (a.frame == 0 || a.frame == m::clock::getFramesInLoop() - 1))
+ mr::clearAction(ch->index, a.type);
+ else
+ mr::deleteAction(ch->index, a.frame, a.type, false, &m::mixer::mutex);
+
+ updateChannel(ch->guiChannel, /*refreshActionEditor=*/false);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void deleteSampleAction(SampleChannel* ch, m::recorder::action a1,
+ m::recorder::action a2)
+{
+ namespace mr = m::recorder;
+
+ /* if SINGLE_PRESS delete both the keypress and the keyrelease pair. */
+
+ if (ch->mode == ChannelMode::SINGLE_PRESS) {
+ mr::deleteAction(ch->index, a1.frame, G_ACTION_KEYPRESS, false, &m::mixer::mutex);
+ mr::deleteAction(ch->index, a2.frame, G_ACTION_KEYREL, false, &m::mixer::mutex);
+ }
+ else
+ mr::deleteAction(ch->index, a1.frame, a1.type, false, &m::mixer::mutex);
+
+ updateChannel(ch->guiChannel, /*refreshActionEditor=*/false);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+vector<m::recorder::Composite> getSampleActions(const SampleChannel* ch)
+{
+ namespace mr = m::recorder;
+
+ vector<mr::Composite> out;
+
+ mr::sortActions();
+ mr::forEachAction([&](const mr::action* a1)
+ {
+ /* Exclude:
+ - actions beyond clock::getFramesInLoop();
+ - actions that don't belong to channel ch;
+ - actions != G_ACTION_KEYPRESS, G_ACTION_KEYREL or G_ACTION_KILL;
+ - G_ACTION_KEYREL actions in a SINGLE_PRESS context. */
+
+ if (a1->frame > m::clock::getFramesInLoop() ||
+ a1->chan != ch->index ||
+ a1->type & ~(G_ACTION_KEYPRESS | G_ACTION_KEYREL | G_ACTION_KILL) ||
+ (ch->mode == ChannelMode::SINGLE_PRESS && a1->type == G_ACTION_KEYREL))
+ return;
+
+ mr::Composite cmp;
+ cmp.a1 = *a1;
+ cmp.a2.frame = -1;
+
+ /* If SINGLE_PRESS mode and the current action is G_ACTION_KEYPRESS, let's
+ fetch the corresponding G_ACTION_KEYREL. */
+
+ if (ch->mode == ChannelMode::SINGLE_PRESS && a1->type == G_ACTION_KEYPRESS) {
+ m::recorder::action* a2 = nullptr;
+ mr::getNextAction(ch->index, G_ACTION_KEYREL, a1->frame, &a2);
+ if (a2 != nullptr)
+ cmp.a2 = *a2;
+ }
+
+ out.push_back(cmp);
+ });
+
+ return out;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+vector<m::recorder::action> getEnvelopeActions(const Channel* ch, int type)
+{
+ namespace mr = m::recorder;
+
+ vector<mr::action> out;
+
+ mr::sortActions();
+ mr::forEachAction([&](const mr::action* a)
+ {
+ /* Exclude:
+ - actions beyond clock::getFramesInLoop();
+ - actions that don't belong to channel ch;
+ - actions with wrong type. */
+
+ if (a->frame > m::clock::getFramesInLoop() ||
+ a->chan != ch->index ||
+ a->type != type)
+ return;
+
+ out.push_back(*a);
+ });
+
+ return out;
}
/* -------------------------------------------------------------------------- */
-vector<m::recorder::Composite> getMidiActions(int chan, int frameLimit)
+void setVelocity(const Channel* ch, m::recorder::action a, int value)
+{
+ /* TODO - this is super ugly: delete the action and add a new one with the
+ modified values. This shit will go away as soon as we'll refactor m::recorder
+ for good. */
+
+ m::MidiEvent event = m::MidiEvent(a.iValue);
+ event.setVelocity(value);
+
+ m::recorder::deleteAction(ch->index, a.frame, G_ACTION_MIDI, true,
+ &m::mixer::mutex, a.iValue, 0.0);
+ m::recorder::rec(ch->index, G_ACTION_MIDI, a.frame, event.getRaw());
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+vector<m::recorder::Composite> getMidiActions(int chan)
{
vector<m::recorder::Composite> out;
for (unsigned i=0; i<m::recorder::frames.size(); i++) {
- if (m::recorder::frames.at(i) > frameLimit)
+ if (m::recorder::frames.at(i) > m::clock::getFramesInLoop())
continue;
for (unsigned j=0; j<m::recorder::global.at(i).size(); j++) {
note of a1 and random velocity: we don't care about it (and so we mask it
with 0x0000FF00). */
- m::MidiEvent a2midi(m::MidiEvent::NOTE_OFF, a1midi.getNote(), 0x0);
-
m::recorder::getNextAction(chan, G_ACTION_MIDI, a1->frame, &a2,
- a2midi.getRaw(), 0x0000FF00);
+ m::MidiEvent(m::MidiEvent::NOTE_OFF, a1midi.getNote(), 0x0).getRaw(),
+ 0x0000FF00);
/* If action 2 has been found, add it to the composite duo. Otherwise
set the action 2 frame to -1: it should be intended as "orphaned". */
#include "../core/recorder.h"
+class SampleChannel;
+class MidiChannel;
class geChannel;
namespace recorder
{
void clearAllActions(geChannel* gch);
-void clearMuteActions(geChannel* gch);
void clearVolumeActions(geChannel* gch);
void clearStartStopActions(geChannel* gch);
+
+
+/* MOVE ALL THESE FUNCTIONS TO c::actionEditor*/
+
+
+bool midiActionCanFit(int chan, int note, int frame_a, int frame_b);
+bool sampleActionCanFit(const SampleChannel* ch, int frame_a, int frame_b);
+
/* recordMidiAction
Records a new MIDI action at frame_a. If frame_b == 0, uses the default action
size. This function is designed for the Piano Roll (not for live recording). */
-void recordMidiAction(int chan, int note, int frame_a, int frame_b=0);
+void recordMidiAction(int chan, int note, int velocity, int frame_a, int frame_b=0);
+
+void recordEnvelopeAction(Channel* ch, int type, int frame, float fValue);
+
+void recordSampleAction(SampleChannel* ch, int type, int frame_a, int frame_b=0);
+
+void setVelocity(const Channel* ch, m::recorder::action a, int value);
/* getMidiActions
Returns a list of Composite actions, ready to be displayed in a MIDI note
editor as pairs of NoteOn+NoteOff. */
-std::vector<giada::m::recorder::Composite> getMidiActions(int channel,
- int frameLimit);
+std::vector<m::recorder::Composite> getMidiActions(int channel);
+
+std::vector<m::recorder::action> getEnvelopeActions(const Channel* ch, int type);
+
+/* getSampleActions
+Returns a list of Composite actions, ready to be displayed in a Sample Action
+Editor. If actions are not keypress+keyrelease combos, the second action in
+the Composite struct if left empty (with action2.frame = -1). */
+
+std::vector<m::recorder::Composite> getSampleActions(const SampleChannel* ch);
+
+void deleteMidiAction(MidiChannel* ch, m::recorder::action a1, m::recorder::action a2);
+
+void deleteSampleAction(SampleChannel* ch, m::recorder::action a1,
+ m::recorder::action a2);
+
+void deleteEnvelopeAction(Channel* ch, m::recorder::action a, bool moved);
}}} // giada::c::recorder::
set_modal();
- logo = new geBox(8, 10, 324, 86);
+ logo = new geBox(8, 20, 324, 86);
text = new geBox(8, 120, 324, 145);
close = new geButton(252, h()-28, 80, 20, "Close");
#ifdef WITH_VST
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#include <cassert>
+#include <string>
+#include <FL/Fl.H>
+#include <FL/fl_draw.H>
+#include "../../../utils/gui.h"
+#include "../../../utils/string.h"
+#include "../../../core/conf.h"
+#include "../../../core/const.h"
+#include "../../../core/clock.h"
+#include "../../../core/channel.h"
+#include "../../elems/actionEditor/gridTool.h"
+#include "../../elems/basics/scroll.h"
+#include "../../elems/basics/choice.h"
+#include "baseActionEditor.h"
+
+
+using std::string;
+
+
+namespace giada {
+namespace v
+{
+gdBaseActionEditor::gdBaseActionEditor(Channel* ch)
+: gdWindow (640, 284),
+ ch (ch),
+ ratio (G_DEFAULT_ZOOM_RATIO)
+{
+ using namespace giada::m;
+
+ if (conf::actionEditorW) {
+ resize(conf::actionEditorX, conf::actionEditorY, conf::actionEditorW, conf::actionEditorH);
+ ratio = conf::actionEditorZoom;
+ }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+gdBaseActionEditor::~gdBaseActionEditor()
+{
+ using namespace giada::m;
+
+ conf::actionEditorX = x();
+ conf::actionEditorY = y();
+ conf::actionEditorW = w();
+ conf::actionEditorH = h();
+ conf::actionEditorZoom = ratio;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gdBaseActionEditor::cb_zoomIn(Fl_Widget *w, void *p) { ((gdBaseActionEditor*)p)->zoomIn(); }
+void gdBaseActionEditor::cb_zoomOut(Fl_Widget *w, void *p) { ((gdBaseActionEditor*)p)->zoomOut(); }
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gdBaseActionEditor::computeWidth()
+{
+ fullWidth = frameToPixel(m::clock::getFramesInSeq());
+ loopWidth = frameToPixel(m::clock::getFramesInLoop());
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+Pixel gdBaseActionEditor::frameToPixel(Frame f) const
+{
+ return f / ratio;
+}
+
+
+Frame gdBaseActionEditor::pixelToFrame(Pixel p, bool snap) const
+{
+ return snap ? gridTool->getSnapFrame(p * ratio) : p * ratio;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gdBaseActionEditor::zoomIn()
+{
+ if (ratio / 2 > MIN_RATIO) {
+ ratio /= 2;
+ rebuild();
+ centerViewportIn();
+ redraw();
+ }
+ else
+ ratio = MIN_RATIO;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gdBaseActionEditor::zoomOut()
+{
+ if (ratio * 2 < MAX_RATIO) {
+ ratio *= 2;
+ rebuild();
+ centerViewportOut();
+ redraw();
+ }
+ else
+ ratio = MAX_RATIO;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gdBaseActionEditor::centerViewportIn()
+{
+ Pixel sx = Fl::event_x() + (viewport->xposition() * 2);
+ viewport->scroll_to(sx, viewport->yposition());
+}
+
+
+void gdBaseActionEditor::centerViewportOut()
+{
+ Pixel sx = -((Fl::event_x() + viewport->xposition()) / 2) + viewport->xposition();
+ if (sx < 0) sx = 0;
+ viewport->scroll_to(sx, viewport->yposition());
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int gdBaseActionEditor::getActionType() const
+{
+ if (actionType->value() == 0)
+ return G_ACTION_KEYPRESS;
+ else
+ if (actionType->value() == 1)
+ return G_ACTION_KEYREL;
+ else
+ if (actionType->value() == 2)
+ return G_ACTION_KILL;
+
+ assert(false);
+ return -1;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gdBaseActionEditor::prepareWindow()
+{
+ gu_setFavicon(this);
+
+ string l = "Action Editor";
+ if (ch->name != "") l += " - " + ch->name;
+ copy_label(l.c_str());
+
+ set_non_modal();
+ size_range(640, 284);
+ resizable(viewport);
+
+ show();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int gdBaseActionEditor::handle(int e)
+{
+ switch (e) {
+ case FL_MOUSEWHEEL:
+ Fl::event_dy() == -1 ? zoomIn() : zoomOut();
+ return 1;
+ default:
+ return Fl_Group::handle(e);
+ }
+}
+}} // giada::v::
\ No newline at end of file
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#ifndef GD_BASE_ACTION_EDITOR_H
+#define GD_BASE_ACTION_EDITOR_H
+
+
+#include "../../../core/types.h"
+#include "../window.h"
+
+
+class Channel;
+class geChoice;
+class geButton;
+class geScroll;
+
+
+namespace giada {
+namespace v
+{
+class geGridTool;
+
+
+class gdBaseActionEditor : public gdWindow
+{
+protected:
+
+ static constexpr Pixel RESIZER_BAR_H = 20;
+ static constexpr Pixel MIN_WIDGET_H = 10;
+ static constexpr float MIN_RATIO = 25.0f;
+ static constexpr float MAX_RATIO = 40000.0f;
+
+ gdBaseActionEditor(Channel* ch);
+
+ void zoomIn();
+ void zoomOut();
+ static void cb_zoomIn(Fl_Widget* w, void* p);
+ static void cb_zoomOut(Fl_Widget* w, void* p);
+
+ /* computeWidth
+ Computes total width, in pixel. */
+
+ void computeWidth();
+
+ void centerViewportIn();
+ void centerViewportOut();
+
+ void prepareWindow();
+
+public:
+
+ virtual ~gdBaseActionEditor();
+
+ /* rebuild
+ Forces all internal widgets to rebuild themselves. Used when refreshing the
+ whole Action Editor window. */
+
+ virtual void rebuild() = 0;
+
+ int handle(int e) override;
+
+ Pixel frameToPixel(Frame f) const;
+ Frame pixelToFrame(Pixel p, bool snap=true) const;
+ int getActionType() const;
+
+ geChoice* actionType;
+ geGridTool* gridTool;
+ geButton* zoomInBtn;
+ geButton* zoomOutBtn;
+ geScroll* viewport; // widget container
+
+ Channel* ch;
+
+ float ratio;
+ Pixel fullWidth; // Full widgets width, i.e. scaled-down full sequencer
+ Pixel loopWidth; // Loop width, i.e. scaled-down sequencer range
+};
+}} // giada::v::
+
+
+#endif
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#include <string>
+#include "../../../core/graphics.h"
+#include "../../../core/midiChannel.h"
+#include "../../elems/basics/scroll.h"
+#include "../../elems/basics/button.h"
+#include "../../elems/basics/resizerBar.h"
+#include "../../elems/basics/box.h"
+#include "../../elems/actionEditor/noteEditor.h"
+#include "../../elems/actionEditor/velocityEditor.h"
+#include "../../elems/actionEditor/pianoRoll.h"
+#include "../../elems/actionEditor/gridTool.h"
+#include "midiActionEditor.h"
+
+
+using std::string;
+
+
+namespace giada {
+namespace v
+{
+gdMidiActionEditor::gdMidiActionEditor(MidiChannel* ch)
+: gdBaseActionEditor(ch)
+{
+ computeWidth();
+
+ Fl_Group* upperArea = new Fl_Group(8, 8, w()-16, 20);
+
+ upperArea->begin();
+
+ gridTool = new geGridTool(8, 8);
+
+ geBox *b1 = new geBox(gridTool->x()+gridTool->w()+4, 8, 300, 20); // padding actionType - zoomButtons
+ zoomInBtn = new geButton(w()-8-40-4, 8, 20, 20, "", zoomInOff_xpm, zoomInOn_xpm);
+ zoomOutBtn = new geButton(w()-8-20, 8, 20, 20, "", zoomOutOff_xpm, zoomOutOn_xpm);
+
+ upperArea->end();
+ upperArea->resizable(b1);
+
+ zoomInBtn->callback(cb_zoomIn, (void*)this);
+ zoomOutBtn->callback(cb_zoomOut, (void*)this);
+
+ /* Main viewport: contains all widgets. */
+
+ viewport = new geScroll(8, 36, w()-16, h()-44);
+
+ ne = new geNoteEditor(viewport->x(), viewport->y(), this);
+ viewport->add(ne);
+ viewport->add(new geResizerBar(ne->x(), ne->y()+ne->h(), viewport->w(), RESIZER_BAR_H, MIN_WIDGET_H));
+
+ ve = new geVelocityEditor(viewport->x(), ne->y()+ne->h()+RESIZER_BAR_H, ch);
+ viewport->add(ve);
+ viewport->add(new geResizerBar(ve->x(), ve->y()+ve->h(), viewport->w(), RESIZER_BAR_H, MIN_WIDGET_H));
+
+ end();
+ prepareWindow();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gdMidiActionEditor::rebuild()
+{
+ computeWidth();
+ ne->rebuild();
+ ve->rebuild();
+}
+}} // giada::v::
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#ifndef GD_MIDI_ACTION_EDITOR_H
+#define GD_MIDI_ACTION_EDITOR_H
+
+
+#include "baseActionEditor.h"
+
+
+class MidiChannel;
+
+
+namespace giada {
+namespace v
+{
+class geNoteEditor;
+class geVelocityEditor;
+
+
+class gdMidiActionEditor : public gdBaseActionEditor
+{
+private:
+
+ geNoteEditor* ne;
+ geVelocityEditor* ve;
+
+public:
+
+ gdMidiActionEditor(MidiChannel* ch);
+
+ void rebuild() override;
+};
+}} // giada::v::
+
+
+#endif
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#include <string>
+#include "../../../core/const.h"
+#include "../../../core/graphics.h"
+#include "../../../core/sampleChannel.h"
+#include "../../elems/basics/scroll.h"
+#include "../../elems/basics/button.h"
+#include "../../elems/basics/resizerBar.h"
+#include "../../elems/basics/choice.h"
+#include "../../elems/basics/box.h"
+#include "../../elems/actionEditor/sampleActionEditor.h"
+#include "../../elems/actionEditor/envelopeEditor.h"
+#include "../../elems/actionEditor/gridTool.h"
+#include "sampleActionEditor.h"
+
+
+using std::string;
+
+
+namespace giada {
+namespace v
+{
+gdSampleActionEditor::gdSampleActionEditor(SampleChannel* ch)
+: gdBaseActionEditor(ch)
+{
+ computeWidth();
+
+ /* Container with zoom buttons and the action type selector. Scheme of the
+ resizable boxes: |[--b1--][actionType][--b2--][+][-]| */
+
+ Fl_Group* upperArea = new Fl_Group(8, 8, w()-16, 20);
+
+ upperArea->begin();
+
+ actionType = new geChoice(8, 8, 80, 20);
+ gridTool = new geGridTool(actionType->x()+actionType->w()+4, 8);
+ actionType->add("Key press");
+ actionType->add("Key release");
+ actionType->add("Kill chan");
+ actionType->value(0);
+
+ if (!canChangeActionType())
+ actionType->deactivate();
+
+ geBox* b1 = new geBox(gridTool->x()+gridTool->w()+4, 8, 300, 20); // padding actionType - zoomButtons
+ zoomInBtn = new geButton(w()-8-40-4, 8, 20, 20, "", zoomInOff_xpm, zoomInOn_xpm);
+ zoomOutBtn = new geButton(w()-8-20, 8, 20, 20, "", zoomOutOff_xpm, zoomOutOn_xpm);
+
+ upperArea->end();
+ upperArea->resizable(b1);
+
+ zoomInBtn->callback(cb_zoomIn, (void*)this);
+ zoomOutBtn->callback(cb_zoomOut, (void*)this);
+
+ /* Main viewport: contains all widgets. */
+
+ viewport = new geScroll(8, 36, w()-16, h()-44);
+
+ ac = new geSampleActionEditor(viewport->x(), viewport->y(), ch);
+ viewport->add(ac);
+ viewport->add(new geResizerBar(ac->x(), ac->y()+ac->h(), viewport->w(), RESIZER_BAR_H, MIN_WIDGET_H));
+
+ vc = new geEnvelopeEditor(viewport->x(), ac->y()+ac->h()+RESIZER_BAR_H, G_ACTION_VOLUME, "volume", ch);
+ viewport->add(vc);
+ viewport->add(new geResizerBar(vc->x(), vc->y()+vc->h(), viewport->w(), RESIZER_BAR_H, MIN_WIDGET_H));
+
+ end();
+ prepareWindow();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+bool gdSampleActionEditor::canChangeActionType()
+{
+ SampleChannel* sch = static_cast<SampleChannel*>(ch);
+ return sch->mode != ChannelMode::SINGLE_PRESS && !sch->isAnyLoopMode();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gdSampleActionEditor::rebuild()
+{
+ canChangeActionType() ? actionType->activate() : actionType->deactivate();
+ computeWidth();
+ ac->rebuild();
+ vc->rebuild();
+}
+}} // giada::v::
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#ifndef GD_SAMPLE_ACTION_EDITOR_H
+#define GD_SAMPLE_ACTION_EDITOR_H
+
+
+#include "baseActionEditor.h"
+
+
+class SampleChannel;
+class geSampleActionEditor;
+class geEnvelopeEditor;
+
+
+namespace giada {
+namespace v
+{
+class gdSampleActionEditor : public gdBaseActionEditor
+{
+private:
+
+ geSampleActionEditor* ac;
+ geEnvelopeEditor* vc;
+
+ bool canChangeActionType();
+
+public:
+
+ gdSampleActionEditor(SampleChannel* ch);
+
+ void rebuild() override;
+};
+}} // giada::v::
+
+
+#endif
+++ /dev/null
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
- *
- * This file is part of Giada - Your Hardcore Loopmachine.
- *
- * Giada - Your Hardcore Loopmachine is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General
- * Public License as published by the Free Software Foundation, either
- * version 3 of the License, or (at your option) any later version.
- *
- * Giada - Your Hardcore Loopmachine is distributed in the hope that it
- * will be useful, but WITHOUT ANY WARRANTY; without even the implied
- * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Giada - Your Hardcore Loopmachine. If not, see
- * <http://www.gnu.org/licenses/>.
- *
- * -------------------------------------------------------------------------- */
-
-
-#include <cmath>
-#include "../../utils/gui.h"
-#include "../../utils/string.h"
-#include "../../core/graphics.h"
-#include "../../core/conf.h"
-#include "../../core/const.h"
-#include "../../core/clock.h"
-#include "../../core/sampleChannel.h"
-#include "../elems/basics/scroll.h"
-#include "../elems/basics/button.h"
-#include "../elems/basics/resizerBar.h"
-#include "../elems/basics/choice.h"
-#include "../elems/basics/box.h"
-#include "../elems/actionEditor/actionEditor.h"
-#include "../elems/actionEditor/envelopeEditor.h"
-#include "../elems/actionEditor/muteEditor.h"
-#include "../elems/actionEditor/noteEditor.h"
-#include "../elems/actionEditor/gridTool.h"
-#include "gd_actionEditor.h"
-
-
-using std::string;
-using namespace giada;
-using namespace giada::m;
-
-
-gdActionEditor::gdActionEditor(Channel* chan)
- : gdWindow(640, 284),
- chan (chan),
- zoom (100),
- coverX (0)
-{
- if (conf::actionEditorW) {
- resize(conf::actionEditorX, conf::actionEditorY, conf::actionEditorW, conf::actionEditorH);
- zoom = conf::actionEditorZoom;
- }
-
- totalWidth = (int) std::ceil(clock::getFramesInSeq() / (float) zoom);
-
- /* container with zoom buttons and the action type selector. Scheme of
- * the resizable boxes: |[--b1--][actionType][--b2--][+][-]| */
-
- Fl_Group *upperArea = new Fl_Group(8, 8, w()-16, 20);
-
- upperArea->begin();
-
- if (chan->type == ChannelType::SAMPLE) {
- actionType = new geChoice(8, 8, 80, 20);
- gridTool = new geGridTool(actionType->x()+actionType->w()+4, 8, this);
- actionType->add("key press");
- actionType->add("key release");
- actionType->add("kill chan");
- actionType->value(0);
-
- SampleChannel *ch = static_cast<SampleChannel*>(chan);
- if (ch->mode == ChannelMode::SINGLE_PRESS || ch->isAnyLoopMode())
- actionType->deactivate();
- }
- else {
- gridTool = new geGridTool(8, 8, this);
- }
-
- geBox *b1 = new geBox(gridTool->x()+gridTool->w()+4, 8, 300, 20); // padding actionType - zoomButtons
- zoomIn = new geButton(w()-8-40-4, 8, 20, 20, "", zoomInOff_xpm, zoomInOn_xpm);
- zoomOut = new geButton(w()-8-20, 8, 20, 20, "", zoomOutOff_xpm, zoomOutOn_xpm);
- upperArea->end();
- upperArea->resizable(b1);
-
- zoomIn->callback(cb_zoomIn, (void*)this);
- zoomOut->callback(cb_zoomOut, (void*)this);
-
- /* main scroller: contains all widgets */
-
- scroller = new geScroll(8, 36, w()-16, h()-44);
-
- if (chan->type == ChannelType::SAMPLE) {
-
- SampleChannel *ch = static_cast<SampleChannel*>(chan);
-
- ac = new geActionEditor (scroller->x(), upperArea->y()+upperArea->h()+8, this, ch);
- mc = new geMuteEditor (scroller->x(), ac->y()+ac->h()+8, this);
- vc = new geEnvelopeEditor(scroller->x(), mc->y()+mc->h()+8, this, G_ACTION_VOLUME, G_RANGE_FLOAT, "volume");
- scroller->add(ac);
- //scroller->add(new geResizerBar(ac->x(), ac->y()+ac->h(), scroller->w(), 8));
- scroller->add(mc);
- //scroller->add(new geResizerBar(mc->x(), mc->y()+mc->h(), scroller->w(), 8));
- scroller->add(vc);
- //scroller->add(new geResizerBar(vc->x(), vc->y()+vc->h(), scroller->w(), 8));
-
- /* fill volume envelope with actions from recorder */
-
- vc->fill();
-
- /* if channel is LOOP_ANY, deactivate it: a loop mode channel cannot
- * hold keypress/keyrelease actions */
-
- if (ch->isAnyLoopMode())
- ac->deactivate();
- }
- else {
- pr = new geNoteEditor(scroller->x(), upperArea->y()+upperArea->h()+8, this);
- scroller->add(pr);
- /* TODO - avoid magic number 30 for minimum height */
- scroller->add(new geResizerBar(pr->x(), pr->y()+pr->h(), scroller->w(), 30, 8));
- }
-
- end();
-
- /* compute values */
-
- update();
- gridTool->calc();
-
- gu_setFavicon(this);
-
- string buf = "Edit Actions in Channel " + gu_iToString(chan->index+1);
- label(buf.c_str());
-
- set_non_modal();
- size_range(640, 284);
- resizable(scroller);
-
- show();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-gdActionEditor::~gdActionEditor()
-{
- conf::actionEditorX = x();
- conf::actionEditorY = y();
- conf::actionEditorW = w();
- conf::actionEditorH = h();
- conf::actionEditorZoom = zoom;
-
- /** CHECKME - missing clear() ? */
-
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gdActionEditor::cb_zoomIn(Fl_Widget *w, void *p) { ((gdActionEditor*)p)->__cb_zoomIn(); }
-void gdActionEditor::cb_zoomOut(Fl_Widget *w, void *p) { ((gdActionEditor*)p)->__cb_zoomOut(); }
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gdActionEditor::__cb_zoomIn()
-{
- /* zoom 50: empirical value, to avoid a totalWidth > 16 bit signed
- * (32767 max), unsupported by FLTK 1.3.x */
-
- if (zoom <= 50)
- return;
-
- zoom /= 2;
-
- update();
-
- if (chan->type == ChannelType::SAMPLE) {
- ac->size(totalWidth, ac->h());
- mc->size(totalWidth, mc->h());
- vc->size(totalWidth, vc->h());
- ac->updateActions();
- mc->updateActions();
- vc->updateActions();
- }
- else {
- pr->size(totalWidth, pr->h());
- pr->updateActions();
- }
-
- /* scroll to pointer */
-
- int shift = Fl::event_x() + scroller->xposition();
- scroller->scroll_to(scroller->xposition() + shift, scroller->yposition());
-
- /* update all underlying widgets */
-
- gridTool->calc();
- scroller->redraw();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gdActionEditor::__cb_zoomOut()
-{
- zoom *= 2;
-
- update();
-
- if (chan->type == ChannelType::SAMPLE) {
- ac->size(totalWidth, ac->h());
- mc->size(totalWidth, mc->h());
- vc->size(totalWidth, vc->h());
- ac->updateActions();
- mc->updateActions();
- vc->updateActions();
- }
- else {
- pr->size(totalWidth, pr->h());
- pr->updateActions();
- }
-
- /* scroll to pointer */
-
- int shift = (Fl::event_x() + scroller->xposition()) / -2;
- if (scroller->xposition() + shift < 0)
- shift = 0;
- scroller->scroll_to(scroller->xposition() + shift, scroller->yposition());
-
- /* update all underlying widgets */
-
- gridTool->calc();
- scroller->redraw();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gdActionEditor::update()
-{
- totalWidth = (int) ceilf(clock::getFramesInSeq() / (float) zoom);
- if (totalWidth < scroller->w()) {
- totalWidth = scroller->w();
- zoom = (int) ceilf(clock::getFramesInSeq() / (float) totalWidth);
- scroller->scroll_to(0, scroller->yposition());
- }
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-int gdActionEditor::handle(int e)
-{
- int ret = Fl_Group::handle(e);
- switch (e) {
- case FL_MOUSEWHEEL: {
- Fl::event_dy() == -1 ? __cb_zoomIn() : __cb_zoomOut();
- ret = 1;
- break;
- }
- }
- return ret;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-int gdActionEditor::getActionType()
-{
- if (actionType->value() == 0)
- return G_ACTION_KEYPRESS;
- else
- if (actionType->value() == 1)
- return G_ACTION_KEYREL;
- else
- if (actionType->value() == 2)
- return G_ACTION_KILL;
- else
- return -1;
-}
+++ /dev/null
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
- *
- * This file is part of Giada - Your Hardcore Loopmachine.
- *
- * Giada - Your Hardcore Loopmachine is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General
- * Public License as published by the Free Software Foundation, either
- * version 3 of the License, or (at your option) any later version.
- *
- * Giada - Your Hardcore Loopmachine is distributed in the hope that it
- * will be useful, but WITHOUT ANY WARRANTY; without even the implied
- * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Giada - Your Hardcore Loopmachine. If not, see
- * <http://www.gnu.org/licenses/>.
- *
- * -------------------------------------------------------------------------- */
-
-
-#ifndef GD_ACTIONEDITOR_H
-#define GD_ACTIONEDITOR_H
-
-
-#include <vector>
-#include <FL/Fl.H>
-#include <FL/Fl_Double_Window.H>
-#include <FL/Fl_Scroll.H>
-#include "window.h"
-
-
-class Channel;
-class geChoice;
-class geGridTool;
-class geButton;
-class geButton;
-class geScroll;
-class geActionEditor;
-class geMuteEditor;
-class geEnvelopeEditor;
-class geNoteEditor;
-
-
-/* gActionEditor
-Main window which contains the tools for dealing with actions. This class
-calculates chan, zoom, frames per beat, and so on. Each sub-widget contains a
-pointer to this window to query those data. */
-
-class gdActionEditor : public gdWindow
-{
-private:
-
- /* update
- Computes total width, in pixel. */
-
- void update();
-
-public:
-
- gdActionEditor(Channel *chan);
- ~gdActionEditor();
-
- int handle(int e);
-
- int getActionType();
-
- static void cb_zoomIn(Fl_Widget *w, void *p);
- static void cb_zoomOut(Fl_Widget *w, void *p);
- inline void __cb_zoomIn();
- inline void __cb_zoomOut();
-
- geChoice *actionType;
- geGridTool *gridTool;
- geButton *zoomIn;
- geButton *zoomOut;
- geScroll *scroller; // widget container
-
- geActionEditor *ac;
- geMuteEditor *mc;
- geEnvelopeEditor *vc;
- geNoteEditor *pr;
-
- Channel *chan;
-
- int zoom;
- int totalWidth; // total width of the widget, in pixel (zoom affected)
- int coverX; // x1 of the unused area (x2 = totalWidth)
-};
-
-
-#endif
gdPluginWindowGUI::gdPluginWindowGUI(Plugin* plugin)
- : gdWindow(450, 300), m_plugin(plugin)
+#ifdef G_OS_MAC
+ : gdWindow(Fl::w(), Fl::h()), m_plugin(plugin)
+#else
+ : gdWindow(320, 200), m_plugin(plugin)
+#endif
{
show();
-#ifdef G_OS_LINUX
+#if defined(G_OS_LINUX) || defined(G_OS_MAC)
/* Fl_Window::show() is not guaranteed to show and draw the window on all
platforms immediately. Instead this is done in the background; particularly on
want to have the window instantiated and displayed synchronously. Currently
(as of FLTK 1.3.4) this method has an effect on X11 and Mac OS.
- http://www.fltk.org/doc-1.3/classFl__Window.html#aafbec14ca8ff8abdaff77a35ebb23dd8
-
- NOTE - we should try to macOS as well. */
+ http://www.fltk.org/doc-1.3/classFl__Window.html#aafbec14ca8ff8abdaff77a35ebb23dd8 */
wait_for_expose();
Fl::flush();
-#endif
-
-#ifndef G_OS_MAC
-
- Fl::check();
-
#endif
gu_log("[gdPluginWindowGUI] opening GUI, this=%p, xid=%p\n",
#ifdef G_OS_MAC
void* cocoaWindow = (void*) fl_xid(this);
- cocoa_setWindowSize(cocoaWindow, m_plugin->getEditorW(), m_plugin->getEditorH());
m_plugin->showEditor(cocoa_getViewFromWindow(cocoaWindow));
#else
m_plugin->showEditor((void*) fl_xid(this));
-#endif
-
int pluginW = m_plugin->getEditorW();
int pluginH = m_plugin->getEditorH();
resize((Fl::w() - pluginW) / 2, (Fl::h() - pluginH) / 2, pluginW, pluginH);
+
+#endif
+
Fl::add_timeout(G_GUI_PLUGIN_RATE, cb_refresh, (void*) this);
copy_label(m_plugin->getName().c_str());
+++ /dev/null
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
- *
- * This file is part of Giada - Your Hardcore Loopmachine.
- *
- * Giada - Your Hardcore Loopmachine is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General
- * Public License as published by the Free Software Foundation, either
- * version 3 of the License, or (at your option) any later version.
- *
- * Giada - Your Hardcore Loopmachine is distributed in the hope that it
- * will be useful, but WITHOUT ANY WARRANTY; without even the implied
- * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Giada - Your Hardcore Loopmachine. If not, see
- * <http://www.gnu.org/licenses/>.
- *
- * -------------------------------------------------------------------------- */
-
-
-#include <FL/fl_draw.H>
-#include "../../../core/const.h"
-#include "../../../core/mixer.h"
-#include "../../../core/sampleChannel.h"
-#include "../../../utils/log.h"
-#include "../../dialogs/gd_actionEditor.h"
-#include "actionEditor.h"
-#include "action.h"
-
-
-using namespace giada;
-using namespace giada::m;
-
-
-/** TODO - index is useless?
- * TODO - pass a record::action pointer and let geAction compute values */
-
-geAction::geAction(int X, int Y, int H, int frame_a, unsigned index,
- gdActionEditor *parent, SampleChannel *ch, bool record, char type)
-: Fl_Box (X, Y, MIN_WIDTH, H),
- selected (false),
- index (index),
- parent (parent),
- ch (ch),
- type (type),
- frame_a (frame_a),
- onRightEdge(false),
- onLeftEdge (false)
-{
- /* bool 'record' defines how to understand the action.
- * record = false: don't record it, just show it. It happens when you
- * open the editor with some actions to be shown.
- *
- * record = true: record it AND show it. It happens when you click on
- * an empty area in order to add a new actions. First you record it
- * (addAction()) then you show it (FLTK::Draw()) */
-
- if (record)
- addAction();
-
- /* in order to show a singlepress action we must compute the frame_b. We
- * do that after the possible recording, otherwise we don't know which
- * key_release is associated. */
-
- if (ch->mode == ChannelMode::SINGLE_PRESS && type == G_ACTION_KEYPRESS) {
- recorder::action *a2 = nullptr;
- recorder::getNextAction(ch->index, G_ACTION_KEYREL, frame_a, &a2);
- if (a2) {
- frame_b = a2->frame;
- w((frame_b - frame_a)/parent->zoom);
- }
- else
- gu_log("[geActionEditor] frame_b not found! [%d:???]\n", frame_a);
-
- /* a singlepress action narrower than 8 pixel is useless. So check it.
- * Warning: if an action is 8 px narrow, it has no body space to drag
- * it. It's up to the user to zoom in and drag it. */
-
- if (w() < MIN_WIDTH)
- size(MIN_WIDTH, h());
- }
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void geAction::draw()
-{
- int color;
- if (selected) /// && geActionEditor !disabled
- color = G_COLOR_LIGHT_2;
- else
- color = G_COLOR_LIGHT_1;
-
- if (ch->mode == ChannelMode::SINGLE_PRESS) {
- fl_rectf(x(), y(), w(), h(), (Fl_Color) color);
- }
- else {
- if (type == G_ACTION_KILL)
- fl_rect(x(), y(), MIN_WIDTH, h(), (Fl_Color) color);
- else {
- fl_rectf(x(), y(), MIN_WIDTH, h(), (Fl_Color) color);
- if (type == G_ACTION_KEYPRESS)
- fl_rectf(x()+3, y()+h()-11, 2, 8, G_COLOR_GREY_4);
- else
- if (type == G_ACTION_KEYREL)
- fl_rectf(x()+3, y()+3, 2, 8, G_COLOR_GREY_4);
- }
- }
-
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-int geAction::handle(int e)
-{
- /* ret = 0 sends the event to the parent window. */
-
- int ret = 0;
-
- switch (e) {
-
- case FL_ENTER: {
- selected = true;
- ret = 1;
- redraw();
- break;
- }
- case FL_LEAVE: {
- fl_cursor(FL_CURSOR_DEFAULT, FL_WHITE, FL_BLACK);
- selected = false;
- ret = 1;
- redraw();
- break;
- }
- case FL_MOVE: {
-
- /* handling of the two margins, left & right. 4 pixels are good enough */
-
- if (ch->mode == ChannelMode::SINGLE_PRESS) {
- onLeftEdge = false;
- onRightEdge = false;
- if (Fl::event_x() >= x() && Fl::event_x() < x()+4) {
- onLeftEdge = true;
- fl_cursor(FL_CURSOR_WE, FL_WHITE, FL_BLACK);
- }
- else
- if (Fl::event_x() >= x()+w()-4 && Fl::event_x() <= x()+w()) {
- onRightEdge = true;
- fl_cursor(FL_CURSOR_WE, FL_WHITE, FL_BLACK);
- }
- else
- fl_cursor(FL_CURSOR_DEFAULT, FL_WHITE, FL_BLACK);
- }
- }
- }
-
- return ret;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void geAction::addAction()
-{
- /* anatomy of an action
- * ____[#######]_____ (a) is the left margin, G_ACTION_KEYPRESS. (b) is
- * a b the right margin, the G_ACTION_KEYREL. This is the
- * theory behind the singleshot.press actions; for any other kind the
- * (b) is just a graphical and meaningless point. */
-
- if (ch->mode == ChannelMode::SINGLE_PRESS) {
- recorder::rec(parent->chan->index, G_ACTION_KEYPRESS, frame_a);
- recorder::rec(parent->chan->index, G_ACTION_KEYREL, frame_a+4096);
- //gu_log("action added, [%d, %d]\n", frame_a, frame_a+4096);
- }
- else {
- recorder::rec(parent->chan->index, parent->getActionType(), frame_a);
- //gu_log("action added, [%d]\n", frame_a);
- }
-
- parent->chan->hasActions = true;
-
- recorder::sortActions();
-
- index++; // important!
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void geAction::delAction()
-{
- /* if SINGLE_PRESS you must delete both the keypress and the keyrelease
- * actions. */
-
- if (ch->mode == ChannelMode::SINGLE_PRESS) {
- recorder::deleteAction(parent->chan->index, frame_a, G_ACTION_KEYPRESS,
- false, &mixer::mutex);
- recorder::deleteAction(parent->chan->index, frame_b, G_ACTION_KEYREL,
- false, &mixer::mutex);
- }
- else
- recorder::deleteAction(parent->chan->index, frame_a, type, false,
- &mixer::mutex);
-
- parent->chan->hasActions = recorder::hasActions(parent->chan->index);
-
-
- /* restore the initial cursor shape, in case you delete an action and
- * the double arrow (for resizing) is displayed */
-
- fl_cursor(FL_CURSOR_DEFAULT, FL_WHITE, FL_BLACK);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void geAction::moveAction(int frame_a)
-{
- /* easy one: delete previous action and record the new ones. As usual,
- * SINGLE_PRESS requires two jobs. If frame_a is valid, use that frame
- * value. */
-
- delAction();
-
- if (frame_a != -1)
- this->frame_a = frame_a;
- else
- this->frame_a = xToFrame_a();
-
-
- /* always check frame parity */
-
- if (this->frame_a % 2 != 0)
- this->frame_a++;
-
- recorder::rec(parent->chan->index, type, this->frame_a);
-
- if (ch->mode == ChannelMode::SINGLE_PRESS) {
- frame_b = xToFrame_b();
- recorder::rec(parent->chan->index, G_ACTION_KEYREL, frame_b);
- }
-
- parent->chan->hasActions = true;
-
- recorder::sortActions();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-int geAction::absx()
-{
- return x() - parent->ac->x();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-int geAction::xToFrame_a()
-{
- return (absx()) * parent->zoom;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-int geAction::xToFrame_b()
-{
- return (absx() + w()) * parent->zoom;
-}
+++ /dev/null
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
- *
- * This file is part of Giada - Your Hardcore Loopmachine.
- *
- * Giada - Your Hardcore Loopmachine is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General
- * Public License as published by the Free Software Foundation, either
- * version 3 of the License, or (at your option) any later version.
- *
- * Giada - Your Hardcore Loopmachine is distributed in the hope that it
- * will be useful, but WITHOUT ANY WARRANTY; without even the implied
- * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Giada - Your Hardcore Loopmachine. If not, see
- * <http://www.gnu.org/licenses/>.
- *
- * -------------------------------------------------------------------------- */
-
-
-#ifndef GE_ACTION_H
-#define GE_ACTION_H
-
-
-#include <FL/Fl_Box.H>
-
-
-class gdActionEditor;
-class SampleChannel;
-
-
-class geAction : public Fl_Box
-{
-private:
-
- bool selected;
- unsigned index;
- gdActionEditor *parent; // pointer to parent (geActionEditor)
- SampleChannel *ch;
- char type; // type of action
-
-public:
-
- geAction(int x, int y, int h, int frame_a, unsigned index,
- gdActionEditor *parent, SampleChannel *ch, bool record, char type);
- void draw();
- int handle(int e);
- void addAction();
- void delAction();
-
- /* moveAction
- * shift the action on the x-axis and update Recorder. If frame_a != -1
- * use the new frame in input (used while snapping) */
-
- void moveAction(int frame_a=-1);
-
- /* absx
- * x() is relative to scrolling position. absx() returns the absolute
- * x value of the action, from the leftmost edge. */
-
- int absx();
-
- /* xToFrame_a,b
- * return the real frames of x() position */
-
- int xToFrame_a();
- int xToFrame_b();
-
- int frame_a; // initial frame (KEYPRESS for singlemode.press)
- int frame_b; // terminal frame (KEYREL for singlemode.press, null for others)
-
- bool onRightEdge;
- bool onLeftEdge;
-
- static const int MIN_WIDTH = 8;
-};
-
-
-#endif
+++ /dev/null
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
- *
- * This file is part of Giada - Your Hardcore Loopmachine.
- *
- * Giada - Your Hardcore Loopmachine is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General
- * Public License as published by the Free Software Foundation, either
- * version 3 of the License, or (at your option) any later version.
- *
- * Giada - Your Hardcore Loopmachine is distributed in the hope that it
- * will be useful, but WITHOUT ANY WARRANTY; without even the implied
- * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Giada - Your Hardcore Loopmachine. If not, see
- * <http://www.gnu.org/licenses/>.
- *
- * -------------------------------------------------------------------------- */
-
-
-#include <FL/fl_draw.H>
-#include "../../../core/clock.h"
-#include "../../../core/sampleChannel.h"
-#include "../../dialogs/gd_mainWindow.h"
-#include "../../dialogs/gd_actionEditor.h"
-#include "../mainWindow/keyboard/keyboard.h"
-#include "action.h"
-#include "gridTool.h"
-#include "actionEditor.h"
-
-
-extern gdMainWindow *G_MainWin;
-
-
-using namespace giada;
-using namespace giada::m;
-
-
-geActionEditor::geActionEditor(int x, int y, gdActionEditor *pParent, SampleChannel *ch)
- : geBaseActionEditor(x, y, 200, 40, pParent),
- ch (ch),
- selected (nullptr)
-{
- size(pParent->totalWidth, h());
-
- /* add actions when the window opens. Their position is zoom-based;
- * each frame is / 2 because we don't care about stereo infos. */
-
- for (unsigned i=0; i<recorder::frames.size(); i++) {
- for (unsigned j=0; j<recorder::global.at(i).size(); j++) {
-
- recorder::action *action = recorder::global.at(i).at(j);
-
- /* Don't show actions:
- - that don't belong to the displayed channel (!= pParent->chan->index);
- - that are covered by the grey area (> G_Mixer.totalFrames);
- - of type G_ACTION_KILL in a SINGLE_PRESS channel. They cannot be
- recorded in such mode, but they can exist if you change from another
- mode to singlepress;
- - of type G_ACTION_KEYREL in a SINGLE_PRESS channel. It's up to geAction to
- find the other piece (namely frame_b)
- - not of types G_ACTION_KEYPRESS | G_ACTION_KEYREL | G_ACTION_KILL */
-
- if ((action->chan != pParent->chan->index) ||
- (recorder::frames.at(i) > clock::getFramesInLoop()) ||
- (action->type == G_ACTION_KILL && ch->mode == ChannelMode::SINGLE_PRESS) ||
- (action->type == G_ACTION_KEYREL && ch->mode == ChannelMode::SINGLE_PRESS) ||
- (action->type & ~(G_ACTION_KEYPRESS | G_ACTION_KEYREL | G_ACTION_KILL))
- )
- continue;
-
- int ax = x + (action->frame / pParent->zoom);
- geAction *a = new geAction(
- ax, // x
- y + 4, // y
- h() - 8, // h
- action->frame, // frame_a
- i, // n. of recordings
- pParent, // pointer to the pParent window
- ch, // pointer to SampleChannel
- false, // record = false: don't record it, we are just displaying it!
- action->type); // type of action
- add(a);
- }
- }
- end(); // mandatory when you add widgets to a fl_group, otherwise mega malfunctions
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-geAction *geActionEditor::getSelectedAction()
-{
- for (int i=0; i<children(); i++) {
- int action_x = ((geAction*)child(i))->x();
- int action_w = ((geAction*)child(i))->w();
- if (Fl::event_x() >= action_x && Fl::event_x() <= action_x + action_w)
- return (geAction*)child(i);
- }
- return nullptr;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void geActionEditor::updateActions()
-{
- /* when zooming, don't delete and re-add actions, just MOVE them. This
- * function shifts the action by a zoom factor. Those singlepress are
- * stretched, as well */
-
- geAction *a;
- for (int i=0; i<children(); i++) {
-
- a = (geAction*)child(i);
- int newX = x() + (a->frame_a / pParent->zoom);
-
- if (ch->mode == ChannelMode::SINGLE_PRESS) {
- int newW = ((a->frame_b - a->frame_a) / pParent->zoom);
- if (newW < geAction::MIN_WIDTH)
- newW = geAction::MIN_WIDTH;
- a->resize(newX, a->y(), newW, a->h());
- }
- else
- a->resize(newX, a->y(), geAction::MIN_WIDTH, a->h());
- }
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void geActionEditor::draw()
-{
- /* draw basic boundaries (+ beat bars) and hide the unused area. Then
- * draw the children (the actions) */
-
- baseDraw();
-
- /* print label */
-
- fl_color(G_COLOR_GREY_4);
- fl_font(FL_HELVETICA, 12);
- if (active())
- fl_draw("start/stop", x()+4, y(), w(), h(), (Fl_Align) (FL_ALIGN_LEFT | FL_ALIGN_CENTER)); /// FIXME h() is too much!
- else
- fl_draw("start/stop (disabled)", x()+4, y(), w(), h(), (Fl_Align) (FL_ALIGN_LEFT | FL_ALIGN_CENTER)); /// FIXME h() is too much!
-
- draw_children();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-int geActionEditor::handle(int e)
-{
- int ret = Fl_Group::handle(e);
-
- /* do nothing if the widget is deactivated. It could happen for loopmode
- * channels */
-
- if (!active())
- return 1;
-
- switch (e) {
-
- case FL_DRAG: {
-
- if (selected == nullptr) { // if you drag an empty area
- ret = 1;
- break;
- }
-
- /* if onLeftEdge o onRightEdge are true it means that you're resizing
- * an action. Otherwise move the widget. */
-
- if (selected->onLeftEdge || selected->onRightEdge) {
-
- /* some checks: a) cannot resize an action < N pixels, b) no beyond zero,
- * c) no beyond bar maxwidth. Checks for overlap are done in FL_RELEASE */
-
- if (selected->onRightEdge) {
-
- int aw = Fl::event_x()-selected->x();
- int ah = selected->h();
-
- if (Fl::event_x() < selected->x()+geAction::MIN_WIDTH)
- aw = geAction::MIN_WIDTH;
- else
- if (Fl::event_x() > pParent->coverX)
- aw = pParent->coverX-selected->x();
-
- selected->size(aw, ah);
- }
- else {
-
- int ax = Fl::event_x();
- int ay = selected->y();
- int aw = selected->x()-Fl::event_x()+selected->w();
- int ah = selected->h();
-
- if (Fl::event_x() < x()) {
- ax = x();
- aw = selected->w()+selected->x()-x();
- }
- else
- if (Fl::event_x() > selected->x()+selected->w()-geAction::MIN_WIDTH) {
- ax = selected->x()+selected->w()-geAction::MIN_WIDTH;
- aw = geAction::MIN_WIDTH;
- }
- selected->resize(ax, ay, aw, ah);
- }
- }
-
- /* move the widget around */
-
- else {
- int real_x = Fl::event_x() - actionPickPoint;
- if (real_x < x()) // don't go beyond the left border
- selected->position(x(), selected->y());
- else
- if (real_x+selected->w() > pParent->coverX+x()) // don't go beyond the right border
- selected->position(pParent->coverX+x()-selected->w(), selected->y());
- else {
- if (pParent->gridTool->isOn()) {
- int snpx = pParent->gridTool->getSnapPoint(real_x-x()) + x() -1;
- selected->position(snpx, selected->y());
- }
- else
- selected->position(real_x, selected->y());
- }
- }
- redraw();
- ret = 1;
- break;
- }
-
- case FL_PUSH: {
-
- if (Fl::event_button1()) {
-
- /* avoid at all costs two overlapping actions. We use 'selected' because
- * the selected action can be reused in FL_DRAG, in case you want to move
- * it. */
-
- selected = getSelectedAction();
-
- if (selected == nullptr) {
-
- /* avoid click on grey area */
-
- if (Fl::event_x() >= pParent->coverX) {
- ret = 1;
- break;
- }
-
- /* snap function, if enabled */
-
- int ax = Fl::event_x();
- int fx = (ax - x()) * pParent->zoom;
- if (pParent->gridTool->isOn()) {
- ax = pParent->gridTool->getSnapPoint(ax-x()) + x() -1;
- fx = pParent->gridTool->getSnapFrame(ax-x());
-
- /* with snap=on an action can fall onto another */
-
- if (actionCollides(fx)) {
- ret = 1;
- break;
- }
- }
-
- geAction *a = new geAction(
- ax, // x
- y()+4, // y
- h()-8, // h
- fx, // frame_a
- recorder::frames.size()-1, // n. of actions recorded
- pParent, // pParent window pointer
- ch, // pointer to SampleChannel
- true, // record = true: record it!
- pParent->getActionType()); // type of action
- add(a);
- G_MainWin->keyboard->setChannelWithActions((geSampleChannel*)ch->guiChannel); // mainWindow update
- redraw();
- ret = 1;
- }
- else {
- actionOriginalX = selected->x();
- actionOriginalW = selected->w();
- actionPickPoint = Fl::event_x() - selected->x();
- ret = 1; // for dragging
- }
- }
- else
- if (Fl::event_button3()) {
- geAction *a = getSelectedAction();
- if (a != nullptr) {
- a->delAction();
- remove(a);
- delete a;
- G_MainWin->keyboard->setChannelWithActions((geSampleChannel*)pParent->chan->guiChannel);
- redraw();
- ret = 1;
- }
- }
- break;
- }
- case FL_RELEASE: {
-
- if (selected == nullptr) {
- ret = 1;
- break;
- }
-
- /* noChanges = true when you click on an action without doing anything
- * (dragging or moving it). */
-
- bool noChanges = false;
- if (actionOriginalX == selected->x())
- noChanges = true;
- if (ch->mode == ChannelMode::SINGLE_PRESS &&
- actionOriginalX+actionOriginalW != selected->x()+selected->w())
- noChanges = false;
-
- if (noChanges) {
- ret = 1;
- selected = nullptr;
- break;
- }
-
- /* step 1: check if the action doesn't overlap with another one.
- * In case of overlap the moved action returns to the original X
- * value ("actionOriginalX"). */
-
- bool overlap = false;
- for (int i=0; i<children() && !overlap; i++) {
-
- /* never check against itself. */
-
- if ((geAction*)child(i) == selected)
- continue;
-
- int action_x = ((geAction*)child(i))->x();
- int action_w = ((geAction*)child(i))->w();
- if (ch->mode == ChannelMode::SINGLE_PRESS) {
-
- /* when 2 segments overlap?
- * start = the highest value between the two starting points
- * end = the lowest value between the two ending points
- * if start < end then there's an overlap of end-start pixels. */
-
- int start = action_x >= selected->x() ? action_x : selected->x();
- int end = action_x+action_w < selected->x()+selected->w() ? action_x+action_w : selected->x()+selected->w();
- if (start < end) {
- selected->resize(actionOriginalX, selected->y(), actionOriginalW, selected->h());
- redraw();
- overlap = true; // one overlap: stop checking
- }
- }
- else {
- if (selected->x() == action_x) {
- selected->position(actionOriginalX, selected->y());
- redraw();
- overlap = true; // one overlap: stop checking
- }
- }
- }
-
- /* step 2: no overlap? then update the coordinates of the action, ie
- * delete the previous rec and create a new one.
- * Anyway the selected action becomes nullptr, because when you release
- * the mouse button the dragging process ends. */
-
- if (!overlap) {
- if (pParent->gridTool->isOn()) {
- int f = pParent->gridTool->getSnapFrame(selected->absx());
- selected->moveAction(f);
- }
- else
- selected->moveAction();
- }
- selected = nullptr;
- ret = 1;
- break;
- }
- }
-
- return ret;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-bool geActionEditor::actionCollides(int frame)
-{
- /* if SINGLE_PRESS we check that the tail (frame_b) of the action doesn't
- * overlap the head (frame) of the new one. First the general case, yet. */
-
- bool collision = false;
-
- for (int i=0; i<children() && !collision; i++)
- if (((geAction*) child(i))->frame_a == frame)
- collision = true;
-
- if (ch->mode == ChannelMode::SINGLE_PRESS) {
- for (int i=0; i<children() && !collision; i++) {
- geAction *c = ((geAction*) child(i));
- if (frame <= c->frame_b && frame >= c->frame_a)
- collision = true;
- }
- }
-
- return collision;
-}
+++ /dev/null
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
- *
- * This file is part of Giada - Your Hardcore Loopmachine.
- *
- * Giada - Your Hardcore Loopmachine is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General
- * Public License as published by the Free Software Foundation, either
- * version 3 of the License, or (at your option) any later version.
- *
- * Giada - Your Hardcore Loopmachine is distributed in the hope that it
- * will be useful, but WITHOUT ANY WARRANTY; without even the implied
- * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Giada - Your Hardcore Loopmachine. If not, see
- * <http://www.gnu.org/licenses/>.
- *
- * -------------------------------------------------------------------------- */
-
-
-#ifndef GE_ACTION_EDITOR_H
-#define GE_ACTION_EDITOR_H
-
-
-#include "baseActionEditor.h"
-
-
-class geAction;
-class SampleChannel;
-
-
-class geActionEditor : public geBaseActionEditor
-{
-
-private:
-
- SampleChannel *ch;
-
- /* getSelectedAction
- * get the action under the mouse. nullptr if nothing found. */
-
- geAction *getSelectedAction();
-
- /* selected
- * pointer to the selected action. Useful when dragging around. */
-
- geAction *selected;
-
- /* actionOriginalX, actionOriginalW
- * x and w of the action, when moved. Useful for checking if the action
- * overlaps another one: in that case the moved action returns to
- * actionOriginalX (and to actionOriginalW if resized). */
-
- int actionOriginalX;
- int actionOriginalW;
-
- /* actionPickPoint
- * the precise x point in which the action has been picked with the mouse,
- * before a dragging action. */
-
- int actionPickPoint;
-
-
- /* actionCollides
- * true if an action collides with another. Used while adding new points
- * with snap active.*/
-
- bool actionCollides(int frame);
-
-public:
-
- geActionEditor(int x, int y, gdActionEditor *pParent, SampleChannel *ch);
- void draw();
- int handle(int e);
- void updateActions();
-};
-
-
-#endif
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#include <FL/Fl.H>
+#include <FL/fl_draw.H>
+#include "baseAction.h"
+
+
+namespace giada {
+namespace v
+{
+geBaseAction::geBaseAction(Pixel X, Pixel Y, Pixel W, Pixel H, bool resizable,
+ giada::m::recorder::action a1, giada::m::recorder::action a2)
+: Fl_Box (X, Y, W, H),
+ m_resizable(resizable),
+ onRightEdge(false),
+ onLeftEdge (false),
+ hovered (false),
+ altered (false),
+ pick (0),
+ a1 (a1),
+ a2 (a2)
+{
+ if (w() < MIN_WIDTH)
+ size(MIN_WIDTH, h());
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int geBaseAction::handle(int e)
+{
+ switch (e) {
+ case FL_ENTER: {
+ hovered = true;
+ redraw();
+ return 1;
+ }
+ case FL_LEAVE: {
+ fl_cursor(FL_CURSOR_DEFAULT, FL_WHITE, FL_BLACK);
+ hovered = false;
+ redraw();
+ return 1;
+ }
+ case FL_MOVE: {
+ if (m_resizable) {
+ onLeftEdge = false;
+ onRightEdge = false;
+ if (Fl::event_x() >= x() && Fl::event_x() < x() + HANDLE_WIDTH) {
+ onLeftEdge = true;
+ fl_cursor(FL_CURSOR_WE, FL_WHITE, FL_BLACK);
+ }
+ else
+ if (Fl::event_x() >= x() + w() - HANDLE_WIDTH &&
+ Fl::event_x() <= x() + w()) {
+ onRightEdge = true;
+ fl_cursor(FL_CURSOR_WE, FL_WHITE, FL_BLACK);
+ }
+ else
+ fl_cursor(FL_CURSOR_DEFAULT, FL_WHITE, FL_BLACK);
+ }
+ return 1;
+ }
+ default:
+ return Fl_Widget::handle(e);
+ }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geBaseAction::setLeftEdge(Pixel p)
+{
+ resize(p, y(), x() - p + w(), h());
+ if (w() < MIN_WIDTH)
+ size(MIN_WIDTH, h());
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geBaseAction::setRightEdge(Pixel p)
+{
+ size(p, h());
+ if (w() < MIN_WIDTH)
+ size(MIN_WIDTH, h());
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geBaseAction::setPosition(Pixel p)
+{
+ position(p, y());
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+bool geBaseAction::isOnEdges() const
+{
+ return onLeftEdge || onRightEdge;
+}
+}} // giada::v::
\ No newline at end of file
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#ifndef GE_BASE_ACTION_H
+#define GE_BASE_ACTION_H
+
+
+#include <FL/Fl_Box.H>
+#include "../../../core/recorder.h"
+#include "../../../core/types.h"
+
+
+namespace giada {
+namespace v
+{
+class geBaseAction : public Fl_Box
+{
+private:
+
+ bool m_resizable;
+
+public:
+
+ static const Pixel MIN_WIDTH = 12;
+ static const Pixel HANDLE_WIDTH = 6;
+
+ geBaseAction(Pixel x, Pixel y, Pixel w, Pixel h, bool resizable,
+ m::recorder::action a1, m::recorder::action a2);
+
+ int handle(int e) override;
+
+ bool isOnEdges() const;
+
+ /* setLeftEdge/setRightEdge
+ Set new left/right edges position, relative range. */
+
+ void setLeftEdge(Pixel p);
+ void setRightEdge(Pixel p);
+
+ void setPosition(Pixel p);
+
+ bool onRightEdge;
+ bool onLeftEdge;
+ bool hovered;
+ bool altered;
+ Pixel pick;
+
+ m::recorder::action a1;
+ m::recorder::action a2;
+};
+}} // giada::v::
+
+#endif
*
* Giada - Your Hardcore Loopmachine
*
- * geBaseActionEditor
- * Parent class of any widget inside the action editor.
- *
* -----------------------------------------------------------------------------
*
* Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
* -------------------------------------------------------------------------- */
+#include <FL/Fl.H>
#include <FL/fl_draw.H>
-#include "../../../core/mixer.h"
#include "../../../core/const.h"
-#include "../../dialogs/gd_actionEditor.h"
+#include "../../../core/clock.h"
+#include "../../dialogs/actionEditor/baseActionEditor.h"
#include "gridTool.h"
+#include "baseAction.h"
#include "baseActionEditor.h"
-geBaseActionEditor::geBaseActionEditor(int x, int y, int w, int h,
- gdActionEditor *pParent)
- : Fl_Group(x, y, w, h), pParent(pParent) {}
+namespace giada {
+namespace v
+{
+geBaseActionEditor::geBaseActionEditor(Pixel x, Pixel y, Pixel w, Pixel h, Channel* ch)
+: Fl_Group(x, y, w, h),
+ m_ch (ch),
+ m_base (static_cast<gdBaseActionEditor*>(window())),
+ m_action(nullptr)
+{
+}
/* -------------------------------------------------------------------------- */
-geBaseActionEditor::~geBaseActionEditor() {}
+geBaseAction* geBaseActionEditor::getActionAtCursor() const
+{
+ for (int i=0; i<children(); i++) {
+ geBaseAction* a = static_cast<geBaseAction*>(child(i));
+ if (a->hovered)
+ return a;
+ }
+ return nullptr;
+}
/* -------------------------------------------------------------------------- */
-void geBaseActionEditor::baseDraw(bool clear) {
-
- /* clear the screen */
+void geBaseActionEditor::baseDraw(bool clear) const
+{
+ /* Clear the screen. */
if (clear)
fl_rectf(x(), y(), w(), h(), G_COLOR_GREY_1);
- /* draw the container */
+ /* Draw the outer container. */
fl_color(G_COLOR_GREY_4);
fl_rect(x(), y(), w(), h());
- /* grid drawing, if > 1 */
+ /* Draw grid, beats and bars. A grid set to 1 has a cell size == beat, so
+ painting it is useless. */
- if (pParent->gridTool->getValue() > 1) {
+ if (m_base->gridTool->getValue() > 1) {
+ fl_color(G_COLOR_GREY_3);
+ drawVerticals(m_base->gridTool->getCellSize());
+ }
- fl_color(fl_rgb_color(54, 54, 54));
- fl_line_style(FL_DASH, 0, nullptr);
+ fl_color(G_COLOR_GREY_4);
+ drawVerticals(m::clock::getFramesInBeat());
- for (int i=0; i<(int) pParent->gridTool->points.size(); i++) {
- int px = pParent->gridTool->points.at(i)+x()-1;
- fl_line(px, y()+1, px, y()+h()-2);
- }
- fl_line_style(0);
- }
+ fl_color(G_COLOR_LIGHT_1);
+ drawVerticals(m::clock::getFramesInBar());
- /* bars and beats drawing */
+ /* Cover unused area. Avoid drawing cover if width == 0 (i.e. beats are 32). */
- fl_color(G_COLOR_GREY_4);
- for (int i=0; i<(int) pParent->gridTool->beats.size(); i++) {
- int px = pParent->gridTool->beats.at(i)+x()-1;
- fl_line(px, y()+1, px, y()+h()-2);
+ Pixel coverWidth = m_base->fullWidth - m_base->loopWidth;
+ if (coverWidth != 0)
+ fl_rectf(m_base->loopWidth+x(), y()+1, coverWidth, h()-2, G_COLOR_GREY_4);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geBaseActionEditor::drawVerticals(int steps) const
+{
+ /* Start drawing from steps, not from 0. The zero-th element is always
+ graphically useless. */
+ for (Frame i=steps; i<m::clock::getFramesInLoop(); i+=steps) {
+ Pixel p = m_base->frameToPixel(i) + x();
+ fl_line(p, y()+1, p, y()+h()-2);
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+
+int geBaseActionEditor::handle(int e)
+{
+ switch (e) {
+ case FL_PUSH:
+ return push();
+ case FL_DRAG:
+ return drag();
+ case FL_RELEASE:
+ fl_cursor(FL_CURSOR_DEFAULT, FL_WHITE, FL_BLACK); // Make sure cursor returns normal
+ return release();
+ default:
+ return Fl_Group::handle(e);
}
+}
- fl_color(G_COLOR_LIGHT_1);
- for (int i=0; i<(int) pParent->gridTool->bars.size(); i++) {
- int px = pParent->gridTool->bars.at(i)+x()-1;
- fl_line(px, y()+1, px, y()+h()-2);
+
+/* -------------------------------------------------------------------------- */
+
+
+int geBaseActionEditor::push()
+{
+ m_action = getActionAtCursor();
+
+ if (Fl::event_button1()) { // Left button
+ if (m_action == nullptr) { // No action under cursor: add a new one
+ if (Fl::event_x() < m_base->loopWidth) // Avoid click on grey area
+ onAddAction();
+ }
+ else // Prepare for dragging
+ m_action->pick = Fl::event_x() - m_action->x();
}
+ else
+ if (Fl::event_button3()) { // Right button
+ if (m_action != nullptr) {
+ onDeleteAction();
+ m_action = nullptr;
+ }
+ }
+ return 1;
+}
- /* cover unused area. Avoid drawing cover if width == 0 (i.e. beats
- * are 32) */
- int coverWidth = pParent->totalWidth-pParent->coverX;
- if (coverWidth != 0)
- fl_rectf(pParent->coverX+x(), y()+1, coverWidth, h()-2, G_COLOR_GREY_4);
+/* -------------------------------------------------------------------------- */
+
+
+int geBaseActionEditor::drag()
+{
+ if (m_action == nullptr)
+ return 0;
+ if (m_action->isOnEdges())
+ onResizeAction();
+ else
+ onMoveAction();
+ m_action->altered = true;
+ redraw();
+ return 1;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int geBaseActionEditor::release()
+{
+ int ret = 0;
+ if (m_action != nullptr && m_action->altered) {
+ onRefreshAction();
+ ret = 1;
+ }
+ m_action = nullptr;
+ return ret;
}
+}} // giada::v::
\ No newline at end of file
*
* Giada - Your Hardcore Loopmachine
*
- * ge_actionWidget
- *
- * parent class of any tool inside the action editor.
*
* -----------------------------------------------------------------------------
*
#include <FL/Fl_Group.H>
-class gdActionEditor;
+class Channel;
+
+namespace giada {
+namespace v
+{
+class gdBaseActionEditor;
+class geBaseAction;
class geBaseActionEditor : public Fl_Group
{
+private:
+
+ /* drawVerticals
+ Draws generic vertical lines (beats, bars, grid lines...). */
+
+ void drawVerticals(int steps) const;
+
+ int push();
+ int drag();
+ int release();
+
protected:
- gdActionEditor *pParent;
+ Channel* m_ch;
+
+ gdBaseActionEditor* m_base;
+
+ /* m_action
+ Selected action. Used while dragging. */
+
+ geBaseAction* m_action;
- void baseDraw(bool clear=true);
+ /* baseDraw
+ Draws basic things like borders and grids. Optional background clear. */
+
+ void baseDraw(bool clear=true) const;
+
+ virtual void onAddAction() = 0;
+ virtual void onDeleteAction() = 0;
+ virtual void onMoveAction() = 0;
+ virtual void onResizeAction() = 0;
+ virtual void onRefreshAction() = 0;
public:
- virtual void updateActions() = 0;
+ geBaseActionEditor(Pixel x, Pixel y, Pixel w, Pixel h, Channel* ch);
+
+ /* updateActions
+ Rebuild the actions widgets from scratch. */
+
+ virtual void rebuild() = 0;
- geBaseActionEditor(int x, int y, int w, int h, gdActionEditor *pParent);
- ~geBaseActionEditor();
+ /* handle
+ Override base FL_Group events. */
+
+ int handle(int e) override;
+
+ /* getActionAtCursor
+ Returns the action under the mouse. nullptr if nothing found. Why not using
+ Fl::belowmouse? It would require a boring dynamic_cast. */
+
+ geBaseAction* getActionAtCursor() const;
};
+}} // giada::v::
+
#endif
+++ /dev/null
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
- *
- * This file is part of Giada - Your Hardcore Loopmachine.
- *
- * Giada - Your Hardcore Loopmachine is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General
- * Public License as published by the Free Software Foundation, either
- * version 3 of the License, or (at your option) any later version.
- *
- * Giada - Your Hardcore Loopmachine is distributed in the hope that it
- * will be useful, but WITHOUT ANY WARRANTY; without even the implied
- * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Giada - Your Hardcore Loopmachine. If not, see
- * <http://www.gnu.org/licenses/>.
- *
- * -------------------------------------------------------------------------- */
-
-
-#include "pianoRoll.h"
-#include "basePianoItem.h"
-
-
-geBasePianoItem::geBasePianoItem(int x, int y, int w, gdActionEditor *pParent)
- : Fl_Box (x, y, w, gePianoRoll::CELL_H),
- pParent (pParent),
- selected(false)
-{
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-int geBasePianoItem::handle(int e)
-{
- int ret = 0;
- switch (e) {
- case FL_ENTER:
- selected = true;
- redraw();
- ret = 1;
- break;
- case FL_LEAVE:
- selected = false;
- redraw();
- ret = 1;
- break;
- }
- return ret;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-int geBasePianoItem::getY(int note)
-{
- return (gePianoRoll::MAX_KEYS * gePianoRoll::CELL_H) - (note * gePianoRoll::CELL_H);
-}
+++ /dev/null
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
- *
- * This file is part of Giada - Your Hardcore Loopmachine.
- *
- * Giada - Your Hardcore Loopmachine is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General
- * Public License as published by the Free Software Foundation, either
- * version 3 of the License, or (at your option) any later version.
- *
- * Giada - Your Hardcore Loopmachine is distributed in the hope that it
- * will be useful, but WITHOUT ANY WARRANTY; without even the implied
- * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Giada - Your Hardcore Loopmachine. If not, see
- * <http://www.gnu.org/licenses/>.
- *
- * -------------------------------------------------------------------------- */
-
-
-#ifndef GE_BASE_PIANO_ITEM_H
-#define GE_BASE_PIANO_ITEM_H
-
-
-#include <FL/Fl_Box.H>
-#include "../../../core/recorder.h"
-
-
-class gdActionEditor;
-
-
-class geBasePianoItem : public Fl_Box
-{
-protected:
-
- geBasePianoItem(int x, int y, int w, gdActionEditor *pParent);
-
- /* getY
- * from a note, return the y position on piano roll */
-
- int getY(int note);
-
- gdActionEditor *pParent;
-
- bool selected;
-
-public:
-
- virtual void reposition(int pianoRollX) = 0;
-
- int handle(int e) override;
-};
-
-
-#endif
*
* Giada - Your Hardcore Loopmachine
*
- * envelopeEditor
- *
- * Parent class of any envelope controller, from volume to VST parameter
- * automations.
- *
* -----------------------------------------------------------------------------
*
* Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
* -------------------------------------------------------------------------- */
+#include <FL/Fl.H>
#include <FL/fl_draw.H>
-#include "../../../core/channel.h"
-#include "../../../core/recorder.h"
-#include "../../../core/mixer.h"
-#include "../../../core/clock.h"
-#include "../../dialogs/gd_actionEditor.h"
-#include "../../dialogs/gd_mainWindow.h"
-#include "../mainWindow/keyboard/keyboard.h"
-#include "gridTool.h"
+#include "../../../utils/log.h"
+#include "../../../utils/math.h"
+#include "../../../core/const.h"
+#include "../../../core/conf.h"
+#include "../../../core/sampleChannel.h"
+#include "../../../glue/recorder.h"
+#include "../../dialogs/actionEditor/baseActionEditor.h"
+#include "envelopePoint.h"
#include "envelopeEditor.h"
-extern gdMainWindow *G_MainWin;
-
+using std::vector;
-using namespace giada::m;
-
-geEnvelopeEditor::geEnvelopeEditor(int x, int y, gdActionEditor *pParent,
- int type, int range, const char *l)
- : geBaseActionEditor(x, y, 200, 80, pParent),
- l (l),
- type (type),
- range (range),
- selectedPoint (-1),
- draggedPoint (-1)
+namespace giada {
+namespace v
+{
+geEnvelopeEditor::geEnvelopeEditor(Pixel x, Pixel y, int actionType, const char* l,
+ SampleChannel* ch)
+: geBaseActionEditor(x, y, 200, m::conf::envelopeEditorH, ch),
+ m_actionType (actionType)
{
- size(pParent->totalWidth, h());
+ copy_label(l);
+ rebuild();
}
-/* ------------------------------------------------------------------ */
+/* -------------------------------------------------------------------------- */
-geEnvelopeEditor::~geEnvelopeEditor() {
- clearPoints();
+geEnvelopeEditor::~geEnvelopeEditor()
+{
+ m::conf::envelopeEditorH = h();
}
-/* ------------------------------------------------------------------ */
+/* -------------------------------------------------------------------------- */
-void geEnvelopeEditor::addPoint(int frame, int iValue, float fValue, int px, int py) {
- point p;
- p.frame = frame;
- p.iValue = iValue;
- p.fValue = fValue;
- p.x = px;
- p.y = py;
- points.push_back(p);
-}
-
+void geEnvelopeEditor::draw()
+{
+ baseDraw();
-/* ------------------------------------------------------------------ */
+ /* Print label. */
+ fl_color(G_COLOR_GREY_4);
+ fl_font(FL_HELVETICA, G_GUI_FONT_SIZE_BASE);
+ fl_draw(label(), x()+4, y(), w(), h(), (Fl_Align) (FL_ALIGN_LEFT));
+
+ if (children() == 0)
+ return;
+
+ Pixel side = geEnvelopePoint::SIDE / 2;
+
+ Pixel x1 = child(0)->x() + side;
+ Pixel y1 = child(0)->y() + side;
+ Pixel x2 = 0;
+ Pixel y2 = 0;
+
+ /* For each point:
+ - paint the connecting line with the next one;
+ - reposition it on the y axis, only if there's no point selected (dragged
+ around). */
+
+ for (int i=0; i<children(); i++) {
+ geEnvelopePoint* p = static_cast<geEnvelopePoint*>(child(i));
+ if (m_action == nullptr)
+ p->position(p->x(), valueToY(p->a1.fValue));
+ if (i > 0) {
+ x2 = p->x() + side;
+ y2 = p->y() + side;
+ fl_line(x1, y1, x2, y2);
+ x1 = x2;
+ y1 = y2;
+ }
+ }
-void geEnvelopeEditor::updateActions() {
- for (unsigned i=0; i<points.size(); i++)
- points.at(i).x = points.at(i).frame / pParent->zoom;
+ draw_children();
}
-/* ------------------------------------------------------------------ */
+/* -------------------------------------------------------------------------- */
-void geEnvelopeEditor::draw() {
+void geEnvelopeEditor::rebuild()
+{
+ namespace mr = m::recorder;
+ namespace cr = c::recorder;
- baseDraw();
+ /* Remove all existing actions and set a new width, according to the current
+ zoom level. */
- /* print label */
+ clear();
+ size(m_base->fullWidth, h());
- fl_color(G_COLOR_GREY_4);
- fl_font(FL_HELVETICA, 12);
- fl_draw(l, x()+4, y(), 80, h(), (Fl_Align) (FL_ALIGN_LEFT));
+ vector<mr::action> actions = cr::getEnvelopeActions(m_ch, m_actionType);
- int pxOld = x()-3;
- int pyOld = y()+1;
- int pxNew = 0;
- int pyNew = 0;
+ for (mr::action a : actions) {
+ gu_log("[geEnvelopeEditor::rebuild] Action %d\n", a.frame);
+ add(new geEnvelopePoint(frameToX(a.frame), valueToY(a.fValue), a));
+ }
- fl_color(G_COLOR_LIGHT_1);
+ resizable(nullptr);
- for (unsigned i=0; i<points.size(); i++) {
+ redraw();
+}
- pxNew = points.at(i).x+x()-3;
- pyNew = points.at(i).y+y();
- if (selectedPoint == (int) i) {
- fl_color(G_COLOR_LIGHT_1);
- fl_rectf(pxNew, pyNew, 7, 7);
- fl_color(G_COLOR_LIGHT_1);
- }
- else
- fl_rectf(pxNew, pyNew, 7, 7);
+/* -------------------------------------------------------------------------- */
- if (i > 0)
- fl_line(pxOld+3, pyOld+3, pxNew+3, pyNew+3);
- pxOld = pxNew;
- pyOld = pyNew;
- }
+bool geEnvelopeEditor::isFirstPoint() const
+{
+ return find(m_action) == 0;
}
-/* ------------------------------------------------------------------ */
+bool geEnvelopeEditor::isLastPoint() const
+{
+ return find(m_action) == children() - 1;
+}
-int geEnvelopeEditor::handle(int e) {
+/* -------------------------------------------------------------------------- */
- /* Adding an action: no further checks required, just record it on frame
- * mx*pParent->zoom. Deleting action is trickier: find the active
- * point and derive from it the corresponding frame. */
- int ret = 0;
- int mx = Fl::event_x()-x(); // mouse x
- int my = Fl::event_y()-y(); // mouse y
+Pixel geEnvelopeEditor::frameToX(Frame frame) const
+{
+ return x() + m_base->frameToPixel(frame) - (geEnvelopePoint::SIDE / 2);
+}
- switch (e) {
- case FL_ENTER: {
- ret = 1;
- break;
- }
+Pixel geEnvelopeEditor::valueToY(float value) const
+{
+ return u::math::map<float, Pixel>(value, 0.0, 1.0, y() + (h() - geEnvelopePoint::SIDE), y());
+}
- case FL_MOVE: {
- selectedPoint = getSelectedPoint();
- redraw();
- ret = 1;
- break;
- }
- case FL_LEAVE: {
- draggedPoint = -1;
- selectedPoint = -1;
- redraw();
- ret = 1;
- break;
- }
+float geEnvelopeEditor::yToValue(Pixel pixel) const
+{
+ return u::math::map<Pixel, float>(pixel, h() - geEnvelopePoint::SIDE, 0, 0.0, 1.0);
+}
- case FL_PUSH: {
-
- /* left click on point: drag
- * right click on point: delete
- * left click on void: add */
-
- if (Fl::event_button1()) {
-
- if (selectedPoint != -1) {
- draggedPoint = selectedPoint;
- }
- else {
-
- /* top & border fix */
-
- if (my > h()-8) my = h()-8;
- if (mx > pParent->coverX-x()) mx = pParent->coverX-x();
-
- if (range == G_RANGE_FLOAT) {
-
- /* if this is the first point ever, add other two points at the beginning
- * and the end of the range */
-
- if (points.size() == 0) {
- addPoint(0, 0, 1.0f, 0, 1);
- recorder::rec(pParent->chan->index, type, 0, 0, 1.0f);
- addPoint(clock::getFramesInLoop(), 0, 1.0f, pParent->coverX, 1);
- recorder::rec(pParent->chan->index, type, clock::getFramesInLoop(), 0, 1.0f);
- pParent->chan->hasActions = true;
- }
-
- /* line between 2 points y = (x-a) / (b-a); a = h() - 8; b = 1 */
-
- int frame = mx * pParent->zoom;
- float value = (my - h() + 8) / (float) (1 - h() + 8);
- addPoint(frame, 0, value, mx, my);
- recorder::rec(pParent->chan->index, type, frame, 0, value);
- pParent->chan->hasActions = true;
- recorder::sortActions();
- sortPoints();
- }
- else {
- /// TODO
- }
- G_MainWin->keyboard->setChannelWithActions((geSampleChannel*)pParent->chan->guiChannel); // update mainWindow
- redraw();
- }
- }
- else {
-
- /* right click on point 0 or point size-1 deletes the entire envelope */
-
- if (selectedPoint != -1) {
- if (selectedPoint == 0 || (unsigned) selectedPoint == points.size()-1) {
- recorder::clearAction(pParent->chan->index, type);
- pParent->chan->hasActions = recorder::hasActions(pParent->chan->index);
- points.clear();
- }
- else {
- recorder::deleteAction(pParent->chan->index,
- points.at(selectedPoint).frame, type, false, &mixer::mutex);
- pParent->chan->hasActions = recorder::hasActions(pParent->chan->index);
- recorder::sortActions();
- points.erase(points.begin() + selectedPoint);
- }
- G_MainWin->keyboard->setChannelWithActions((geSampleChannel*)pParent->chan->guiChannel); // update mainWindow
- redraw();
- }
- }
-
- ret = 1;
- break;
- }
- case FL_RELEASE: {
- if (draggedPoint != -1) {
-
- if (points.at(draggedPoint).x == previousXPoint) {
- //gu_log("nothing to do\n");
- }
- else {
- int newFrame = points.at(draggedPoint).x * pParent->zoom;
-
- /* x edge correction */
-
- if (newFrame < 0)
- newFrame = 0;
- else if (newFrame > clock::getFramesInLoop())
- newFrame = clock::getFramesInLoop();
-
- /* vertical line check */
-
- int vp = verticalPoint(points.at(draggedPoint));
- if (vp == 1) newFrame -= 256;
- else if (vp == -1) newFrame += 256;
-
- /* delete previous point and record a new one */
-
- recorder::deleteAction(pParent->chan->index,
- points.at(draggedPoint).frame, type, false, &mixer::mutex);
- pParent->chan->hasActions = recorder::hasActions(pParent->chan->index);
-
- if (range == G_RANGE_FLOAT) {
- float value = (points.at(draggedPoint).y - h() + 8) / (float) (1 - h() + 8);
- recorder::rec(pParent->chan->index, type, newFrame, 0, value);
- pParent->chan->hasActions = true;
- }
- else {
- /// TODO
- }
-
- recorder::sortActions();
- points.at(draggedPoint).frame = newFrame;
- draggedPoint = -1;
- selectedPoint = -1;
- }
- }
- ret = 1;
- break;
- }
+/* -------------------------------------------------------------------------- */
- case FL_DRAG: {
-
- if (draggedPoint != -1) {
-
- /* y constraint */
-
- if (my > h()-8)
- points.at(draggedPoint).y = h()-8;
- else
- if (my < 1)
- points.at(draggedPoint).y = 1;
- else
- points.at(draggedPoint).y = my;
-
- /* x constraint
- * constrain the point between two ends (leftBorder-point, point-point,
- * point-rightBorder). First & last points cannot be shifted on x */
-
- if (draggedPoint == 0)
- points.at(draggedPoint).x = x()-8;
- else
- if ((unsigned) draggedPoint == points.size()-1)
- points.at(draggedPoint).x = pParent->coverX;
- else {
- int prevPoint = points.at(draggedPoint-1).x;
- int nextPoint = points.at(draggedPoint+1).x;
- if (mx <= prevPoint)
- points.at(draggedPoint).x = prevPoint;
- else
- if (mx >= nextPoint)
- points.at(draggedPoint).x = nextPoint;
- //else
- // points.at(draggedPoint).x = mx;
- else {
- if (pParent->gridTool->isOn())
- points.at(draggedPoint).x = pParent->gridTool->getSnapPoint(mx)-1;
- else
- points.at(draggedPoint).x = mx;
- }
- }
- redraw();
- }
-
- ret = 1;
- break;
- }
- }
- return ret;
+void geEnvelopeEditor::onAddAction()
+{
+ Frame f = m_base->pixelToFrame(Fl::event_x() - x());
+ float v = yToValue(Fl::event_y() - y());
+ c::recorder::recordEnvelopeAction(m_ch, m_actionType, f, v);
+ rebuild();
}
-/* ------------------------------------------------------------------ */
+/* -------------------------------------------------------------------------- */
-int geEnvelopeEditor::verticalPoint(const point &p) {
- for (unsigned i=0; i<points.size(); i++) {
- if (&p == &points.at(i)) {
- if (i == 0 || i == points.size()-1) // first or last point
- return 0;
- else {
- if (points.at(i-1).x == p.x) // vertical with point[i-1]
- return -1;
- else
- if (points.at(i+1).x == p.x) // vertical with point[i+1]
- return 1;
- }
- break;
- }
- }
- return 0;
+void geEnvelopeEditor::onDeleteAction()
+{
+ c::recorder::deleteEnvelopeAction(m_ch, m_action->a1, /*moved=*/false);
+ rebuild();
}
-/* ------------------------------------------------------------------ */
-
-
-void geEnvelopeEditor::sortPoints() {
- for (unsigned i=0; i<points.size(); i++)
- for (unsigned j=0; j<points.size(); j++)
- if (points.at(j).x > points.at(i).x)
- std::swap(points.at(j), points.at(i));
-}
+/* -------------------------------------------------------------------------- */
-/* ------------------------------------------------------------------ */
+void geEnvelopeEditor::onMoveAction()
+{
+ Pixel side = geEnvelopePoint::SIDE / 2;
+ Pixel ex = Fl::event_x() - side;
+ Pixel ey = Fl::event_y() - side;
+ Pixel x1 = x() - side;
+ Pixel x2 = m_base->loopWidth + x() - side;
+ Pixel y1 = y();
+ Pixel y2 = y() + h() - geEnvelopePoint::SIDE;
-int geEnvelopeEditor::getSelectedPoint() {
+ /* x-axis constraints. */
+ if (isFirstPoint() || ex < x1) ex = x1;
+ else if (isLastPoint() || ex > x2) ex = x2;
- /* point is a 7x7 dot */
+ /* y-axis constraints. */
+ if (ey < y1) ey = y1; else if (ey > y2) ey = y2;
- for (unsigned i=0; i<points.size(); i++) {
- if (Fl::event_x() >= points.at(i).x+x()-4 &&
- Fl::event_x() <= points.at(i).x+x()+4 &&
- Fl::event_y() >= points.at(i).y+y() &&
- Fl::event_y() <= points.at(i).y+y()+7)
- return i;
- }
- return -1;
+ m_action->position(ex, ey);
+ redraw();
}
-/* ------------------------------------------------------------------ */
-
-
-void geEnvelopeEditor::fill() {
- points.clear();
- for (unsigned i=0; i<recorder::global.size(); i++)
- for (unsigned j=0; j<recorder::global.at(i).size(); j++) {
- recorder::action *a = recorder::global.at(i).at(j);
- if (a->type == type && a->chan == pParent->chan->index) {
- if (range == G_RANGE_FLOAT)
- addPoint(
- a->frame, // frame
- 0, // int value (unused)
- a->fValue, // float value
- a->frame / pParent->zoom, // x
- ((1-h()+8)*a->fValue)+h()-8); // y = (b-a)x + a (line between two points)
- // else: TODO
- }
- }
+/* -------------------------------------------------------------------------- */
+
+void geEnvelopeEditor::onRefreshAction()
+{
+ Frame f = m_base->pixelToFrame((m_action->x() - x()) + geEnvelopePoint::SIDE / 2);
+ float v = yToValue(m_action->y() - y());
+ c::recorder::deleteEnvelopeAction(m_ch, m_action->a1, /*moved=*/true);
+ c::recorder::recordEnvelopeAction(m_ch, m_actionType, f, v);
+ rebuild();
}
+}} // giada::v::
\ No newline at end of file
-/* ---------------------------------------------------------------------
+/* -----------------------------------------------------------------------------
*
* Giada - Your Hardcore Loopmachine
*
- * parent class of any envelope controller, from volume to VST parameter
- * automations.
- *
- * ---------------------------------------------------------------------
+ * -----------------------------------------------------------------------------
*
* Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* along with Giada - Your Hardcore Loopmachine. If not, see
* <http://www.gnu.org/licenses/>.
*
- * ------------------------------------------------------------------ */
+ * -------------------------------------------------------------------------- */
+
#ifndef GE_ENVELOPE_EDITOR_H
#define GE_ENVELOPE_EDITOR_H
-#include <vector>
#include "baseActionEditor.h"
-class geEnvelopeEditor : public geBaseActionEditor
-{
- const char *l; // internal label
- int type; // type of action
- int range;
-
- /* point
- * a single dot in the graph. x = relative frame, y = relative value */
-
- struct point
- {
- int frame;
- int iValue;
- float fValue;
- int x;
- int y;
- };
-
- /* points
- * array of points, filled by fillPoints() */
-
- std::vector<point> points;
-
- /* selectedPoint
- * which point we are selecting? */
-
- int selectedPoint;
+class SampleChannel;
- /* draggedPoint
- * which point we are dragging? */
- int draggedPoint;
-
- /* previousXPoint
- * x coordinate of point at time t-1. Used to check effective shifts */
-
- int previousXPoint;
+namespace giada {
+namespace v
+{
+class geEnvelopePoint;
- void draw();
- int handle(int e);
+class geEnvelopeEditor : public geBaseActionEditor
+{
+private:
- int getSelectedPoint();
+ /* m_actionType
+ What type of action this envelope editor is dealing with. */
+
+ int m_actionType;
- void sortPoints();
+ void onAddAction() override;
+ void onDeleteAction() override;
+ void onMoveAction() override;
+ void onResizeAction() override{}; // Nothing to do here
+ void onRefreshAction() override;
- /* verticalPoint
- * check if two points form a vertical line. In that case the frame value
- * would be the same and recorder would go crazy, so shift by a small value
- * of frames to create a minimal fadein/fadeout level. Return 0: no
- * vertical points; return 1: vertical with the next one, return -1: vertical
- * with the previous one. */
+ Pixel frameToX(Frame frame) const;
+ Pixel valueToY(float value) const;
+ float yToValue(Pixel pixel) const;
- int verticalPoint(const point &p);
+ bool isFirstPoint() const;
+ bool isLastPoint() const;
public:
- geEnvelopeEditor(int x, int y, gdActionEditor *pParent, int type, int range,
- const char *l);
+ geEnvelopeEditor(Pixel x, Pixel y, int actionType, const char* l, SampleChannel* ch);
~geEnvelopeEditor();
- /* addPoint
- * add a point made of frame+value to internal points[]. */
-
- void addPoint(int frame, int iValue=0, float fValue=0.0f, int x=-1, int y=-1);
-
- void updateActions();
-
- /* fill
- * parse recorder's stack and fill the widget with points. It's up to
- * the caller to call this method as initialization. */
-
- void fill();
+ void draw() override;
- inline void clearPoints() { points.clear(); }
+ void rebuild() override;
};
+}} // giada::v::
#endif
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#include <FL/fl_draw.H>
+#include "../../../core/const.h"
+#include "envelopePoint.h"
+
+
+namespace giada {
+namespace v
+{
+geEnvelopePoint::geEnvelopePoint(Pixel X, Pixel Y, m::recorder::action a)
+ : geBaseAction(X, Y, SIDE, SIDE, /*resizable=*/false, a, {})
+{
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geEnvelopePoint::draw()
+{
+ fl_rectf(x(), y(), w(), h(), hovered ? G_COLOR_LIGHT_2 : G_COLOR_LIGHT_1);
+}
+}} // giada::v::
\ No newline at end of file
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#ifndef GE_ENVELOPE_POINT_H
+#define GE_ENVELOPE_POINT_H
+
+
+#include "../../../core/recorder.h"
+#include "baseAction.h"
+
+
+namespace giada {
+namespace v
+{
+class geEnvelopePoint : public geBaseAction
+{
+public:
+
+ static const Pixel SIDE = 12;
+
+ geEnvelopePoint(Pixel x, Pixel y, m::recorder::action a);
+
+ void draw() override;
+};
+}} // giada::v::
+
+
+#endif
* --------------------------------------------------------------------------- */
-#include <cmath>
+#include <FL/Fl_Double_Window.H>
#include "../../../core/conf.h"
-#include "../../../core/const.h"
#include "../../../core/clock.h"
-#include "../../dialogs/gd_actionEditor.h"
+#include "../../../utils/math.h"
#include "../basics/choice.h"
#include "../basics/check.h"
-#include "actionEditor.h"
#include "gridTool.h"
-using namespace giada::m;
-
-
-geGridTool::geGridTool(int x, int y, gdActionEditor *parent)
- : Fl_Group(x, y, 80, 20), parent(parent)
+namespace giada {
+namespace v
+{
+geGridTool::geGridTool(Pixel x, Pixel y)
+: Fl_Group(x, y, 80, 20)
{
gridType = new geChoice(x, y, 40, 20);
gridType->add("1");
gridType->value(0);
gridType->callback(cb_changeType, (void*)this);
- active = new geCheck (x+44, y+4, 12, 12);
+ active = new geCheck(gridType->x() + gridType->w() + 4, y+4, 12, 12);
- gridType->value(conf::actionEditorGridVal);
- active->value(conf::actionEditorGridOn);
+ gridType->value(m::conf::actionEditorGridVal);
+ active->value(m::conf::actionEditorGridOn);
end();
}
geGridTool::~geGridTool()
{
- conf::actionEditorGridVal = gridType->value();
- conf::actionEditorGridOn = active->value();
+ m::conf::actionEditorGridVal = gridType->value();
+ m::conf::actionEditorGridOn = active->value();
}
/* -------------------------------------------------------------------------- */
-void geGridTool::cb_changeType(Fl_Widget *w, void *p) { ((geGridTool*)p)->__cb_changeType(); }
+void geGridTool::cb_changeType(Fl_Widget *w, void *p) { ((geGridTool*)p)->cb_changeType(); }
/* -------------------------------------------------------------------------- */
-void geGridTool::__cb_changeType()
+void geGridTool::cb_changeType()
{
- calc();
- parent->redraw();
+ window()->redraw();
}
/* -------------------------------------------------------------------------- */
-bool geGridTool::isOn()
+bool geGridTool::isOn() const
{
return active->value();
}
/* -------------------------------------------------------------------------- */
-int geGridTool::getValue()
+int geGridTool::getValue() const
{
switch (gridType->value()) {
case 0: return 1;
/* -------------------------------------------------------------------------- */
-void geGridTool::calc()
-{
- points.clear();
- frames.clear();
- bars.clear();
- beats.clear();
-
- /* find beats, bars and grid. The method is the same of the waveform in sample
- * editor. Take totalwidth (the width in pixel of the area to draw), knowing
- * that totalWidth = totalFrames / zoom. Then, for each pixel of totalwidth,
- * put a concentrate of each block (which is totalFrames / zoom) */
-
- int j = 0;
- int fpgc = floor(clock::getFramesInBeat() / getValue()); // frames per grid cell
-
- for (int i=1; i<parent->totalWidth; i++) { // if i=0, step=0 -> useless cycle
- int step = parent->zoom*i;
- while (j < step && j < clock::getFramesInLoop()) {
- if (j % fpgc == 0) {
- points.push_back(i);
- frames.push_back(j);
- }
- if (j % clock::getFramesInBeat() == 0)
- beats.push_back(i);
- if (j % clock::getFramesInBar() == 0 && i != 1)
- bars.push_back(i);
- if (j == clock::getFramesInLoop() - 1)
- parent->coverX = i;
- j++;
- }
- j = step;
- }
-
- /* fix coverX if == 0, which means G_Mixer.beats == G_MAX_BEATS */
-
- if (clock::getBeats() == G_MAX_BEATS)
- parent->coverX = parent->totalWidth;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-int geGridTool::getSnapPoint(int v)
-{
- if (v == 0) return 0;
-
- for (int i=0; i<(int)points.size(); i++) {
-
- if (i == (int) points.size()-1)
- return points.at(i);
-
- int gp = points.at(i);
- int gpn = points.at(i+1);
-
- if (v >= gp && v < gpn)
- return gp;
- }
- return v; // default value
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-int geGridTool::getSnapFrame(int v)
+Frame geGridTool::getSnapFrame(Frame v) const
{
- v *= parent->zoom; // transformation pixel -> frame
-
- for (int i=0; i<(int)frames.size(); i++) {
-
- if (i == (int) frames.size()-1)
- return frames.at(i);
-
- int gf = frames.at(i); // grid frame
- int gfn = frames.at(i+1); // grid frame next
-
- if (v >= gf && v < gfn) {
-
- /* which one is the closest? gf < v < gfn */
-
- if ((gfn - v) < (v - gf))
- return gfn;
- else
- return gf;
- }
- }
- return v; // default value
+ if (!isOn())
+ return v;
+ return u::math::quantize(v, getCellSize());
}
/* -------------------------------------------------------------------------- */
-int geGridTool::getCellSize()
+Frame geGridTool::getCellSize() const
{
- return (parent->coverX - parent->ac->x()) / clock::getBeats() / getValue();
+ return m::clock::getFramesInBeat() / getValue();
}
+}} // giada::v::
\ No newline at end of file
#define GE_GRID_TOOL_H
-#include <vector>
#include <FL/Fl_Group.H>
+#include "../../../core/types.h"
class geChoice;
class geCheck;
-class gdActionEditor;
+namespace giada {
+namespace v
+{
class geGridTool : public Fl_Group
{
private:
- geChoice *gridType;
- geCheck *active;
-
- gdActionEditor *parent;
+ geChoice* gridType;
+ geCheck* active;
- static void cb_changeType(Fl_Widget *w, void *p);
- inline void __cb_changeType();
+ static void cb_changeType(Fl_Widget* w, void* p);
+ inline void cb_changeType();
public:
- geGridTool(int x, int y, gdActionEditor *parent);
+ geGridTool(Pixel x, Pixel y);
~geGridTool();
- int getValue();
- bool isOn();
- void calc();
+ int getValue() const;
+ bool isOn() const;
- /* getSnapPoint
- * given a cursor position in input, return the x coordinates of the
- * nearest snap point (in pixel, clean, ie. not x()-shifted) */
-
- int getSnapPoint(int v);
- int getSnapFrame(int v);
+ Frame getSnapFrame(Frame f) const;
/* getCellSize
- * return the size in pixel of a single cell of the grid. */
-
- int getCellSize();
+ Returns the size in frames of a single cell of the grid. */
- std::vector<int> points; // points of the grid
- std::vector<int> frames; // frames of the grid
+ Frame getCellSize() const;
- std::vector<int> bars;
- std::vector<int> beats;
};
+}} // giada::v::
#endif
+++ /dev/null
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
- *
- * This file is part of Giada - Your Hardcore Loopmachine.
- *
- * Giada - Your Hardcore Loopmachine is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General
- * Public License as published by the Free Software Foundation, either
- * version 3 of the License, or (at your option) any later version.
- *
- * Giada - Your Hardcore Loopmachine is distributed in the hope that it
- * will be useful, but WITHOUT ANY WARRANTY; without even the implied
- * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Giada - Your Hardcore Loopmachine. If not, see
- * <http://www.gnu.org/licenses/>.
- *
- * -------------------------------------------------------------------------- */
-
-
-#include <FL/fl_draw.H>
-#include "../../../core/recorder.h"
-#include "../../../core/mixer.h"
-#include "../../../core/channel.h"
-#include "../../../core/clock.h"
-#include "../../../glue/main.h"
-#include "../../../utils/log.h"
-#include "../../dialogs/gd_actionEditor.h"
-#include "../../dialogs/gd_mainWindow.h"
-#include "../mainWindow/keyboard/keyboard.h"
-#include "gridTool.h"
-#include "muteEditor.h"
-
-
-extern gdMainWindow *G_MainWin;
-
-
-using namespace giada::m;
-
-
-geMuteEditor::geMuteEditor(int x, int y, gdActionEditor *pParent)
- : geBaseActionEditor(x, y, 200, 80, pParent),
- draggedPoint (-1),
- selectedPoint (-1)
-{
- size(pParent->totalWidth, h());
- extractPoints();
-}
-
-
-/* ------------------------------------------------------------------ */
-
-
-void geMuteEditor::draw()
-{
- baseDraw();
-
- /* print label */
-
- fl_color(G_COLOR_GREY_4);
- fl_font(FL_HELVETICA, 12);
- fl_draw("mute", x()+4, y(), w(), h(), (Fl_Align) (FL_ALIGN_LEFT | FL_ALIGN_CENTER));
-
- /* draw "on" and "off" labels. Must stay in background */
-
- fl_color(G_COLOR_GREY_4);
- fl_font(FL_HELVETICA, 9);
- fl_draw("on", x()+4, y(), w(), h(), (Fl_Align) (FL_ALIGN_LEFT | FL_ALIGN_TOP));
- fl_draw("off", x()+4, y()+h()-14, w(), h(), (Fl_Align) (FL_ALIGN_LEFT | FL_ALIGN_TOP));
-
- /* draw on-off points. On = higher rect, off = lower rect. It always
- * starts with a note_off */
-
- fl_color(G_COLOR_LIGHT_1);
-
- int pxOld = x()+1;
- int pxNew = 0;
- int py = y()+h()-5;
- int pyDot = py-6;
-
- for (unsigned i=0; i<points.size(); i++) {
-
- /* next px */
-
- pxNew = points.at(i).x+x();
-
- /* draw line from pxOld to pxNew.
- * i % 2 == 0: first point, mute_on
- * i % 2 != 0: second point, mute_off */
-
- fl_line(pxOld, py, pxNew, py);
- pxOld = pxNew;
-
- py = i % 2 == 0 ? y()+4 : y()+h()-5;
-
- /* draw dots (handles) */
-
- fl_line(pxNew, y()+h()-5, pxNew, y()+4);
-
- if (selectedPoint == (int) i) {
- fl_color(G_COLOR_LIGHT_1);
- fl_rectf(pxNew-3, pyDot, 7, 7);
- fl_color(G_COLOR_LIGHT_1);
- }
- else
- fl_rectf(pxNew-3, pyDot, 7, 7);
- }
-
- /* last section */
-
- py = y()+h()-5;
- fl_line(pxNew+3, py, pParent->coverX+x()-1, py);
-}
-
-
-/* ------------------------------------------------------------------ */
-
-
-void geMuteEditor::extractPoints()
-{
- points.clear();
-
- /* actions are already sorted by recorder::sortActions() */
-
- for (unsigned i=0; i<recorder::frames.size(); i++) {
- for (unsigned j=0; j<recorder::global.at(i).size(); j++) {
- if (recorder::global.at(i).at(j)->chan == pParent->chan->index) {
- if (recorder::global.at(i).at(j)->type & (G_ACTION_MUTEON | G_ACTION_MUTEOFF)) {
- point p;
- p.frame = recorder::frames.at(i);
- p.type = recorder::global.at(i).at(j)->type;
- p.x = p.frame / pParent->zoom;
- points.push_back(p);
- //gu_log("[geMuteEditor::extractPoints] point found, type=%d, frame=%d\n", p.type, p.frame);
- }
- }
- }
- }
-}
-
-
-/* ------------------------------------------------------------------ */
-
-
-void geMuteEditor::updateActions() {
- for (unsigned i=0; i<points.size(); i++)
- points.at(i).x = points.at(i).frame / pParent->zoom;
-}
-
-
-/* ------------------------------------------------------------------ */
-
-
-int geMuteEditor::handle(int e) {
-
- int ret = 0;
- int mouseX = Fl::event_x()-x();
-
- switch (e) {
-
- case FL_ENTER: {
- ret = 1;
- break;
- }
-
- case FL_MOVE: {
- selectedPoint = getSelectedPoint();
- redraw();
- ret = 1;
- break;
- }
-
- case FL_LEAVE: {
- draggedPoint = -1;
- selectedPoint = -1;
- redraw();
- ret = 1;
- break;
- }
-
- case FL_PUSH: {
-
- /* left click on point: drag
- * right click on point: delete
- * left click on void: add */
-
- if (Fl::event_button1()) {
-
- if (selectedPoint != -1) {
- draggedPoint = selectedPoint;
- previousXPoint = points.at(selectedPoint).x;
- }
- else {
-
- /* click on the grey area leads to nowhere */
-
- if (mouseX > pParent->coverX) {
- ret = 1;
- break;
- }
-
- /* click in the middle of a long mute_on (between two points): new actions
- * must be added in reverse: first mute_off then mute_on. Let's find the
- * next point from here. */
-
- unsigned nextPoint = points.size();
- for (unsigned i=0; i<points.size(); i++) {
- if (mouseX < points.at(i).x) {
- nextPoint = i;
- break;
- }
- }
-
- /* next point odd = mute_on [click here] mute_off
- * next point even = mute_off [click here] mute_on */
-
- int frame_a = mouseX * pParent->zoom;
- int frame_b = frame_a+2048;
-
- if (pParent->gridTool->isOn()) {
- frame_a = pParent->gridTool->getSnapFrame(mouseX);
- frame_b = pParent->gridTool->getSnapFrame(mouseX + pParent->gridTool->getCellSize());
-
- /* with snap=on a point can fall onto another */
-
- if (pointCollides(frame_a) || pointCollides(frame_b)) {
- ret = 1;
- break;
- }
- }
-
- /* ensure frame parity */
-
- if (frame_a % 2 != 0) frame_a++;
- if (frame_b % 2 != 0) frame_b++;
-
- /* avoid overflow: frame_b must be within the sequencer range. In that
- * case shift the ON-OFF block */
-
- if (frame_b >= clock::getFramesInLoop()) {
- frame_b = clock::getFramesInLoop();
- frame_a = frame_b-2048;
- }
-
- if (nextPoint % 2 != 0) {
- recorder::rec(pParent->chan->index, G_ACTION_MUTEOFF, frame_a);
- recorder::rec(pParent->chan->index, G_ACTION_MUTEON, frame_b);
- }
- else {
- recorder::rec(pParent->chan->index, G_ACTION_MUTEON, frame_a);
- recorder::rec(pParent->chan->index, G_ACTION_MUTEOFF, frame_b);
- }
- pParent->chan->hasActions = true;
- recorder::sortActions();
-
- G_MainWin->keyboard->setChannelWithActions((geSampleChannel*)pParent->chan->guiChannel); // update mainWindow
- extractPoints();
- redraw();
- }
- }
- else {
-
- /* delete points pair */
-
- if (selectedPoint != -1) {
-
- unsigned a;
- unsigned b;
-
- if (points.at(selectedPoint).type == G_ACTION_MUTEOFF) {
- a = selectedPoint-1;
- b = selectedPoint;
- }
- else {
- a = selectedPoint;
- b = selectedPoint+1;
- }
-
- //gu_log("selected: a=%d, b=%d >>> frame_a=%d, frame_b=%d\n",
- // a, b, points.at(a).frame, points.at(b).frame);
-
- recorder::deleteAction(pParent->chan->index, points.at(a).frame,
- points.at(a).type, false, &mixer::mutex); // false = don't check vals
- recorder::deleteAction(pParent->chan->index, points.at(b).frame,
- points.at(b).type, false, &mixer::mutex); // false = don't check vals
- pParent->chan->hasActions = recorder::hasActions(pParent->chan->index);
-
- recorder::sortActions();
-
- G_MainWin->keyboard->setChannelWithActions((geSampleChannel*)pParent->chan->guiChannel); // update mainWindow
- extractPoints();
- redraw();
- }
- }
- ret = 1;
- break;
- }
-
- case FL_RELEASE: {
-
- if (draggedPoint != -1) {
-
- if (points.at(draggedPoint).x == previousXPoint) {
- //gu_log("nothing to do\n");
- }
- else {
-
- int newFrame = points.at(draggedPoint).x * pParent->zoom;
-
- recorder::deleteAction(pParent->chan->index,
- points.at(draggedPoint).frame, points.at(draggedPoint).type, false,
- &mixer::mutex); // don't check values
- pParent->chan->hasActions = recorder::hasActions(pParent->chan->index);
-
- recorder::rec(
- pParent->chan->index,
- points.at(draggedPoint).type,
- newFrame);
-
- pParent->chan->hasActions = true;
- recorder::sortActions();
-
- points.at(draggedPoint).frame = newFrame;
- }
- }
- draggedPoint = -1;
- selectedPoint = -1;
-
- ret = 1;
- break;
- }
-
- case FL_DRAG: {
-
- if (draggedPoint != -1) {
-
- /* constrain the point between two ends (leftBorder-point,
- * point-point, point-rightBorder) */
-
- int prevPoint;
- int nextPoint;
-
- if (draggedPoint == 0) {
- prevPoint = 0;
- nextPoint = points.at(draggedPoint+1).x - 1;
- if (pParent->gridTool->isOn())
- nextPoint -= pParent->gridTool->getCellSize();
- }
- else
- if ((unsigned) draggedPoint == points.size()-1) {
- prevPoint = points.at(draggedPoint-1).x + 1;
- nextPoint = pParent->coverX-x();
- if (pParent->gridTool->isOn())
- prevPoint += pParent->gridTool->getCellSize();
- }
- else {
- prevPoint = points.at(draggedPoint-1).x + 1;
- nextPoint = points.at(draggedPoint+1).x - 1;
- if (pParent->gridTool->isOn()) {
- prevPoint += pParent->gridTool->getCellSize();
- nextPoint -= pParent->gridTool->getCellSize();
- }
- }
-
- if (mouseX <= prevPoint)
- points.at(draggedPoint).x = prevPoint;
- else
- if (mouseX >= nextPoint)
- points.at(draggedPoint).x = nextPoint;
- else
- if (pParent->gridTool->isOn())
- points.at(draggedPoint).x = pParent->gridTool->getSnapPoint(mouseX)-1;
- else
- points.at(draggedPoint).x = mouseX;
-
- redraw();
- }
- ret = 1;
- break;
- }
- }
-
-
- return ret;
-}
-
-
-/* ------------------------------------------------------------------ */
-
-
-bool geMuteEditor::pointCollides(int frame) {
- for (unsigned i=0; i<points.size(); i++)
- if (frame == points.at(i).frame)
- return true;
- return false;
-}
-
-
-/* ------------------------------------------------------------------ */
-
-
-int geMuteEditor::getSelectedPoint() {
-
- /* point is a 7x7 dot */
-
- for (unsigned i=0; i<points.size(); i++) {
- if (Fl::event_x() >= points.at(i).x+x()-3 &&
- Fl::event_x() <= points.at(i).x+x()+3)
- return i;
- }
- return -1;
-}
+++ /dev/null
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
- *
- * This file is part of Giada - Your Hardcore Loopmachine.
- *
- * Giada - Your Hardcore Loopmachine is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General
- * Public License as published by the Free Software Foundation, either
- * version 3 of the License, or (at your option) any later version.
- *
- * Giada - Your Hardcore Loopmachine is distributed in the hope that it
- * will be useful, but WITHOUT ANY WARRANTY; without even the implied
- * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Giada - Your Hardcore Loopmachine. If not, see
- * <http://www.gnu.org/licenses/>.
- *
- * -------------------------------------------------------------------------- */
-
-
-#ifndef GE_MUTE_TOOL_H
-#define GE_MUTE_TOOL_H
-
-
-#include <vector>
-#include "baseActionEditor.h"
-
-
-class gdActionEditor;
-
-
-class geMuteEditor : public geBaseActionEditor
-{
-private:
-
- /* point
- * a single dot in the graph. */
-
- struct point
- {
- int frame;
- char type;
- int x;
- };
-
- /* points
- * array of on/off points, in frames */
-
- std::vector<point> points;
-
- /* draggedPoint
- * which point we are dragging? */
-
- int draggedPoint;
-
- /* selectedPoint
- * which point we are selecting? */
-
- int selectedPoint;
-
- /* previousXPoint
- * x coordinate of point at time t-1. Used to check effective shifts */
-
- int previousXPoint;
-
- /* extractPoints
- * va a leggere l'array di azioni di Recorder ed estrae tutti i punti
- * interessanti mute_on o mute_off. Li mette poi nel vector points. */
- void extractPoints();
-
- /* getSelectedPoint
- * ritorna l'indice di points[] in base al punto selezionato (quello
- * con il mouse hover). Ritorna -1 se non trova niente. */
- int getSelectedPoint();
-
- /* pointCollides
- * true if a point collides with another. Used while adding new points
- * with snap active.*/
-
- bool pointCollides(int frame);
-
-public:
-
- geMuteEditor(int x, int y, gdActionEditor *pParent);
- void draw();
- int handle(int e);
-
- /* updateActions
- * calculates new points affected by the zoom. Call this one after
- * each zoom update. */
-
- void updateActions();
-};
-
-#endif
* -------------------------------------------------------------------------- */
-#include <FL/fl_draw.H>
+#include <FL/Fl.H>
#include "../../../core/const.h"
#include "../../../core/conf.h"
-#include "../../../utils/log.h"
-#include "../../dialogs/gd_actionEditor.h"
-#include "pianoItem.h"
+#include "../../../core/midiChannel.h"
+#include "../../dialogs/actionEditor/midiActionEditor.h"
#include "pianoRoll.h"
#include "noteEditor.h"
-using namespace giada::m;
-
-
-geNoteEditor::geNoteEditor(int x, int y, gdActionEditor *pParent)
- : Fl_Scroll(x, y, 200, 422),
- pParent (pParent)
+namespace giada {
+namespace v
+{
+geNoteEditor::geNoteEditor(Pixel x, Pixel y, gdMidiActionEditor* base)
+: geScroll(x, y, 200, 422),
+ m_base (base)
{
- size(pParent->totalWidth, conf::pianoRollH);
- pianoRoll = new gePianoRoll(x, y, pParent->totalWidth, pParent);
+ pianoRoll = new gePianoRoll(x, y, m_base->fullWidth, static_cast<MidiChannel*>(m_base->ch));
+
+ rebuild();
+
+ size(m_base->fullWidth, m::conf::pianoRollH);
+
+ type(Fl_Scroll::VERTICAL_ALWAYS);
}
geNoteEditor::~geNoteEditor()
{
- clear();
- conf::pianoRollH = h();
- conf::pianoRollY = pianoRoll->y();
+ m::conf::pianoRollH = h();
+ m::conf::pianoRollY = pianoRoll->y();
}
/* -------------------------------------------------------------------------- */
-void geNoteEditor::updateActions()
+void geNoteEditor::scroll()
{
- pianoRoll->updateActions();
-}
+ Pixel ey = Fl::event_y() - pianoRoll->pick;
+ Pixel y1 = y();
+ Pixel y2 = (y() + h()) - pianoRoll->h();
-/* -------------------------------------------------------------------------- */
+ if (ey > y1) ey = y1; else if (ey < y2) ey = y2;
+ pianoRoll->position(x(), ey);
-void geNoteEditor::draw()
-{
- pianoRoll->size(this->w(), pianoRoll->h()); /// <--- not optimal
-
- /* clear background */
+ redraw();
+}
- fl_rectf(x(), y(), w(), h(), G_COLOR_GREY_1);
- /* clip pianoRoll to pianoRollContainer size */
+/* -------------------------------------------------------------------------- */
- fl_push_clip(x(), y(), w(), h());
- draw_child(*pianoRoll);
- fl_pop_clip();
- fl_color(G_COLOR_GREY_4);
- fl_line_style(0);
- fl_rect(x(), y(), pParent->totalWidth, h());
+void geNoteEditor::rebuild()
+{
+ size(m_base->fullWidth, h());
+ pianoRoll->rebuild();
}
+}} // giada::v::
\ No newline at end of file
#define GE_NOTE_EDITOR_H
-#include <FL/Fl_Scroll.H>
+#include "../basics/scroll.h"
-class gdActionEditor;
+namespace giada {
+namespace v
+{
+class gdMidiActionEditor;
class gePianoRoll;
-class geNoteEditor : public Fl_Scroll
+class geNoteEditor : public geScroll
{
private:
- gdActionEditor *pParent;
- gePianoRoll *pianoRoll;
+ gdMidiActionEditor* m_base;
public:
- geNoteEditor(int x, int y, gdActionEditor *parent);
+ geNoteEditor(Pixel x, Pixel y, gdMidiActionEditor* base);
~geNoteEditor();
- void draw();
- void updateActions();
-};
+ void rebuild();
+ void scroll();
+
+ gePianoRoll* pianoRoll;
+};
+}} // giada::v::
#endif
* -------------------------------------------------------------------------- */
-#include "../../../core/kernelMidi.h"
+#include <FL/fl_draw.H>
#include "../../../core/const.h"
-#include "../../../core/mixer.h"
-#include "../../../core/channel.h"
-#include "../../../core/clock.h"
-#include "../../../core/midiChannel.h"
-#include "../../dialogs/gd_actionEditor.h"
-#include "noteEditor.h"
-#include "pianoRoll.h"
-#include "gridTool.h"
+#include "../../../core/midiEvent.h"
+#include "../../../utils/math.h"
#include "pianoItem.h"
-using namespace giada::m;
-
-
-gePianoItem::gePianoItem(int X, int Y, int rel_x, int rel_y, recorder::action a,
- recorder::action b, gdActionEditor* pParent)
- : geBasePianoItem(X, Y, MIN_WIDTH, pParent),
- a (a),
- b (b),
- changed (false)
+namespace giada {
+namespace v
{
- int newX = rel_x + (a.frame / pParent->zoom);
- int newY = rel_y + getY(kernelMidi::getB2(a.iValue));
- int newW = (b.frame - a.frame) / pParent->zoom;
- resize(newX, newY, newW, h());
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gePianoItem::reposition(int pianoRollX)
+gePianoItem::gePianoItem(Pixel X, Pixel Y, Pixel W, Pixel H, m::recorder::action a1,
+ m::recorder::action a2)
+: geBaseAction(X, Y, W, H, /*resizable=*/true, a1, a2),
+ orphaned (a2.frame == -1)
{
- int newX = pianoRollX + (a.frame / pParent->zoom);
- int newW = ((b.frame - a.frame) / pParent->zoom);
- if (newW < MIN_WIDTH)
- newW = MIN_WIDTH;
- resize(newX, y(), newW, h());
- redraw();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-bool gePianoItem::overlap()
-{
- /* when 2 segments overlap?
- * start = the highest value between the two starting points
- * end = the lowest value between the two ending points
- * if start < end then there's an overlap of end-start pixels. */
-
- geNoteEditor* noteEditor = static_cast<geNoteEditor*>(parent());
-
- for (int i=0; i<noteEditor->children(); i++) {
-
- gePianoItem* pItem = static_cast<gePianoItem*>(noteEditor->child(i));
-
- /* don't check against itself and with different y positions */
-
- if (pItem == this || pItem->y() != y())
- continue;
-
- int start = pItem->x() >= x() ? pItem->x() : x();
- int end = pItem->x()+pItem->w() < x()+w() ? pItem->x()+pItem->w() : x()+w();
- if (start < end)
- return true;
- }
-
- return false;
}
void gePianoItem::draw()
{
- int _w = w() > MIN_WIDTH ? w() : MIN_WIDTH;
- fl_rectf(x(), y()+2, _w, h()-3, (Fl_Color) selected ? G_COLOR_LIGHT_1 : G_COLOR_LIGHT_1);
-}
-
-
-/* -------------------------------------------------------------------------- */
+ Fl_Color color = hovered ? G_COLOR_LIGHT_2 : G_COLOR_LIGHT_1;
+ Pixel by = y() + 2;
+ Pixel bh = h() - 3;
-void gePianoItem::removeAction()
-{
- MidiChannel* ch = static_cast<MidiChannel*>(pParent->chan);
- recorder::deleteAction(ch->index, a.frame, G_ACTION_MIDI, true,
- &mixer::mutex, a.iValue, 0.0);
- recorder::deleteAction(ch->index, b.frame, G_ACTION_MIDI, true,
- &mixer::mutex, b.iValue, 0.0);
-
- /* Send a note-off in case we are deleting it in a middle of a key_on/key_off
- sequence. */
-
- ch->sendMidi(b.iValue);
- ch->hasActions = recorder::hasActions(ch->index);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-int gePianoItem::handle(int e)
-{
- int ret = 0;
-
- switch (e) {
-
- case FL_ENTER: {
- selected = true;
- ret = 1;
- redraw();
- break;
- }
-
- case FL_LEAVE: {
- fl_cursor(FL_CURSOR_DEFAULT, FL_WHITE, FL_BLACK);
- selected = false;
- ret = 1;
- redraw();
- break;
- }
-
- case FL_MOVE: {
- onLeftEdge = false;
- onRightEdge = false;
-
- if (Fl::event_x() >= x() && Fl::event_x() < x()+HANDLE_WIDTH) {
- onLeftEdge = true;
- fl_cursor(FL_CURSOR_WE, FL_WHITE, FL_BLACK);
- }
- else
- if (Fl::event_x() >= x()+w()-HANDLE_WIDTH && Fl::event_x() <= x()+w()) {
- onRightEdge = true;
- fl_cursor(FL_CURSOR_WE, FL_WHITE, FL_BLACK);
- }
- else
- fl_cursor(FL_CURSOR_DEFAULT, FL_WHITE, FL_BLACK);
-
- ret = 1;
- break;
- }
-
- case FL_PUSH: {
- push_x = Fl::event_x() - x();
- old_x = x();
- old_w = w();
-
- if (Fl::event_button3()) {
- fl_cursor(FL_CURSOR_DEFAULT, FL_WHITE, FL_BLACK);
- removeAction();
- hide(); // for Windows
- Fl::delete_widget(this);
- static_cast<geNoteEditor*>(parent())->redraw();
- }
- ret = 1;
- break;
- }
-
- case FL_DRAG: {
-
- changed = true;
-
- geNoteEditor *pr = static_cast<geNoteEditor*>(parent());
- int coverX = pParent->coverX + pr->x(); // relative coverX
- int nx, ny, nw;
-
- if (onLeftEdge) {
- nx = Fl::event_x();
- ny = y();
- nw = x()-Fl::event_x()+w();
- if (nx < pr->x()) {
- nx = pr->x();
- nw = w()+x()-pr->x();
- }
- else
- if (nx > x()+w()-MIN_WIDTH) {
- nx = x()+w()-MIN_WIDTH;
- nw = MIN_WIDTH;
- }
- resize(nx, ny, nw, h());
- }
- else
- if (onRightEdge) {
- nw = Fl::event_x()-x();
- if (Fl::event_x() < x()+MIN_WIDTH)
- nw = MIN_WIDTH;
- else
- if (Fl::event_x() > coverX)
- nw = coverX-x();
- size(nw, h());
- }
- else {
- nx = Fl::event_x() - push_x;
- if (nx < pr->x()+1)
- nx = pr->x()+1;
- else
- if (nx+w() > coverX)
- nx = coverX-w();
-
- /* snapping */
-
- if (pParent->gridTool->isOn())
- nx = pParent->gridTool->getSnapPoint(nx-pr->x()) + pr->x() - 1;
-
- position(nx, y());
- }
-
- /* update screen */
-
- redraw();
- static_cast<geNoteEditor*>(parent())->redraw();
- ret = 1;
- break;
- }
-
- case FL_RELEASE: {
-
-
- gePianoRoll* pianoRoll = static_cast<gePianoRoll*>(parent());
-
- /* Delete and record the action, only if it doesn't overlap with another
- existing one. */
-
- if (overlap()) {
- resize(old_x, y(), old_w, h());
- redraw();
- }
- else
- if (changed) {
- removeAction();
- int note = pianoRoll->yToNote(getRelY());
- int frame_a = getRelX() * pParent->zoom;
- int frame_b = (getRelX()+w()) * pParent->zoom;
- pianoRoll->recordAction(note, frame_a, frame_b);
- changed = false;
- }
-
- pianoRoll->redraw();
-
- ret = 1;
- break;
- }
+ if (orphaned) {
+ fl_rect(x(), by, MIN_WIDTH, bh, color);
+ fl_line(x(), by, x() + MIN_WIDTH, by + bh);
+ }
+ else {
+ Pixel vh = calcVelocityH();
+ fl_rectf(x(), by + (bh - vh), w(), vh, color);
+ fl_rect(x(), by, w(), bh, color);
}
- return ret;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-int gePianoItem::getRelY()
-{
- return y() - parent()->y();
}
/* -------------------------------------------------------------------------- */
-int gePianoItem::getRelX()
+Pixel gePianoItem::calcVelocityH() const
{
- return x() - parent()->x();
+ int v = m::MidiEvent(a1.iValue).getVelocity();
+ return u::math::map<int, Pixel>(v, 0, G_MAX_VELOCITY, 0, h() - 3);
}
+}} // giada::v::
\ No newline at end of file
#include "../../../core/recorder.h"
-#include "../../../core/midiEvent.h"
-#include "basePianoItem.h"
+#include "baseAction.h"
+namespace giada {
+namespace v
+{
class gdActionEditor;
-class gePianoItem : public geBasePianoItem
+class gePianoItem : public geBaseAction
{
private:
- struct giada::m::recorder::action a;
- struct giada::m::recorder::action b;
-
- int push_x;
-
- /* changed
- If Item has been moved or resized: re-recording needed. */
-
- bool changed;
-
- /* onLeft, RightEdge
- If cursor is on a widget's edge. */
-
- bool onLeftEdge;
- bool onRightEdge;
-
- /* old_x, old_w
- Store previous width and position while dragging and moving, in order to
- restore it if overlap. */
-
- int old_x, old_w;
-
- /* getRelX/Y
- Returns x/y point of this item, relative to piano roll (and not to entire
- screen). */
-
- int getRelY();
- int getRelX();
-
- /* overlap
- Checks if this item don't overlap with another one. */
-
- bool overlap();
+ Pixel calcVelocityH() const;
public:
- static const int MIN_WIDTH = 10;
- static const int HANDLE_WIDTH = 5;
-
- gePianoItem(int x, int y, int rel_x, int rel_y,
- struct giada::m::recorder::action a, struct giada::m::recorder::action b,
- gdActionEditor* pParent);
+ gePianoItem(int x, int y, int w, int h, m::recorder::action a1,
+ m::recorder::action a2);
void draw() override;
- int handle(int e) override;
-
- void reposition(int pianoRollX) override;
- void removeAction();
+ bool orphaned;
};
+}} // giada::v::
#endif
+++ /dev/null
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
- *
- * This file is part of Giada - Your Hardcore Loopmachine.
- *
- * Giada - Your Hardcore Loopmachine is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General
- * Public License as published by the Free Software Foundation, either
- * version 3 of the License, or (at your option) any later version.
- *
- * Giada - Your Hardcore Loopmachine is distributed in the hope that it
- * will be useful, but WITHOUT ANY WARRANTY; without even the implied
- * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Giada - Your Hardcore Loopmachine. If not, see
- * <http://www.gnu.org/licenses/>.
- *
- * -------------------------------------------------------------------------- */
-
-
-#include "../../../core/const.h"
-#include "../../../core/kernelMidi.h"
-#include "../../../core/recorder.h"
-#include "../../../core/channel.h"
-#include "../../../core/midiChannel.h"
-#include "../../../core/mixer.h"
-#include "../../dialogs/gd_actionEditor.h"
-#include "pianoRoll.h"
-#include "noteEditor.h"
-#include "pianoItemOrphaned.h"
-
-
-using namespace giada::m;
-
-
-gePianoItemOrphaned::gePianoItemOrphaned(int x, int y, int xRel, int yRel,
- recorder::action action, gdActionEditor* pParent)
- : geBasePianoItem(x, y, WIDTH, pParent)
-{
- note = kernelMidi::getB2(action.iValue);
- frame = action.frame;
- event = action.iValue;
- int newX = xRel + (frame / pParent->zoom);
- int newY = yRel + getY(note);
- resize(newX, newY, w(), h());
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gePianoItemOrphaned::reposition(int pianoRollX)
-{
- int newX = pianoRollX + (frame / pParent->zoom);
- resize(newX, y(), WIDTH, h());
- redraw();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-int gePianoItemOrphaned::handle(int e)
-{
- int ret = geBasePianoItem::handle(e);
- if (e == FL_PUSH) {
- remove();
- ret = 1;
- }
- return ret;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gePianoItemOrphaned::remove()
-{
- MidiChannel *ch = static_cast<MidiChannel*>(pParent->chan);
- recorder::deleteAction(ch->index, frame, G_ACTION_MIDI, true, &mixer::mutex,
- event, 0.0);
- hide(); // for Windows
- Fl::delete_widget(this);
- ch->hasActions = recorder::hasActions(ch->index);
- static_cast<geNoteEditor*>(parent())->redraw();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gePianoItemOrphaned::draw()
-{
- fl_rect(x(), y()+2, WIDTH, h()-3, (Fl_Color) selected ? G_COLOR_LIGHT_1 : G_COLOR_LIGHT_1);
-}
+++ /dev/null
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
- *
- * This file is part of Giada - Your Hardcore Loopmachine.
- *
- * Giada - Your Hardcore Loopmachine is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General
- * Public License as published by the Free Software Foundation, either
- * version 3 of the License, or (at your option) any later version.
- *
- * Giada - Your Hardcore Loopmachine is distributed in the hope that it
- * will be useful, but WITHOUT ANY WARRANTY; without even the implied
- * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Giada - Your Hardcore Loopmachine. If not, see
- * <http://www.gnu.org/licenses/>.
- *
- * -------------------------------------------------------------------------- */
-
-
-#ifndef GE_PIANO_ITEM_ORPHANED_H
-#define GE_PIANO_ITEM_ORPHANED_H
-
-
-#include "../../../core/recorder.h"
-#include "basePianoItem.h"
-
-
-class gdActionEditor;
-
-
-class gePianoItemOrphaned : public geBasePianoItem
-{
-private:
-
- int note;
- int frame;
- int event;
-
-public:
-
- static const int WIDTH = 12;
-
- gePianoItemOrphaned(int x, int y, int xRel, int yRel,
- struct giada::m::recorder::action action, gdActionEditor* pParent);
-
- void draw() override;
- int handle(int e) override;
-
- void reposition(int pianoRollX) override;
-
- void remove();
-};
-
-
-#endif
* -------------------------------------------------------------------------- */
+#include <cassert>
+#include <FL/Fl.H>
#include "../../../core/conf.h"
#include "../../../core/const.h"
-#include "../../../core/mixer.h"
#include "../../../core/clock.h"
-#include "../../../core/channel.h"
-#include "../../../core/recorder.h"
-#include "../../../core/kernelMidi.h"
+#include "../../../core/midiChannel.h"
#include "../../../utils/log.h"
#include "../../../utils/string.h"
+#include "../../../utils/math.h"
#include "../../../glue/recorder.h"
-#include "../../dialogs/gd_actionEditor.h"
+#include "../../dialogs/actionEditor/baseActionEditor.h"
#include "pianoItem.h"
-#include "pianoItemOrphaned.h"
#include "noteEditor.h"
#include "pianoRoll.h"
using std::string;
using std::vector;
-using namespace giada;
-gePianoRoll::gePianoRoll(int X, int Y, int W, gdActionEditor* pParent)
- : geBaseActionEditor(X, Y, W, 40, pParent)
+namespace giada {
+namespace v
{
-
- resizable(nullptr); // don't resize children (i.e. pianoItem)
- size(W, (MAX_KEYS+1) * CELL_H); // 128 MIDI channels * CELL_H height
-
- if (m::conf::pianoRollY == -1)
- position(x(), y()-(h()/2)); // center
- else
- position(x(), m::conf::pianoRollY);
-
- drawSurface1();
- drawSurface2();
-
- build();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gePianoRoll::build()
+gePianoRoll::gePianoRoll(Pixel X, Pixel Y, Pixel W, MidiChannel* ch)
+ : geBaseActionEditor(X, Y, W, 40, ch),
+ pick (0)
{
- using namespace m::recorder;
-
- clear();
-
- int channel = pParent->chan->index;
- int maxSample = m::clock::getFramesInLoop();
-
- vector<Composite> actions = c::recorder::getMidiActions(channel, maxSample);
- for (Composite composite : actions)
- {
- m::MidiEvent e1 = composite.a1.iValue;
- m::MidiEvent e2 = composite.a2.iValue;
-
- gu_log("[gePianoRoll] ((0x%X, 0x%X, f=%d) - (0x%X, 0x%X, f=%d))\n",
- e1.getStatus(), e1.getNote(), composite.a1.frame,
- e2.getStatus(), e2.getNote(), composite.a2.frame
- );
-
- if (composite.a2.frame != -1)
- add(new gePianoItem(0, 0, x(), y(), composite.a1, composite.a2, pParent));
- else
- add(new gePianoItemOrphaned(0, 0, x(), y(), composite.a1, pParent));
- }
-
- redraw();
+ position(x(), m::conf::pianoRollY == -1 ? y()-(h()/2) : m::conf::pianoRollY);
+ rebuild();
}
surface1 = fl_create_offscreen(CELL_W, h());
fl_begin_offscreen(surface1);
- /* warning: only w() and h() come from this widget, x and y coordinates
- * are absolute, since we are writing in a memory chunk */
+ /* Warning: only w() and h() come from this widget, x and y coordinates are
+ absolute, since we are writing in a memory chunk. */
fl_rectf(0, 0, CELL_W, h(), G_COLOR_GREY_1);
void gePianoRoll::drawSurface2()
{
surface2 = fl_create_offscreen(CELL_W, h());
+
fl_begin_offscreen(surface2);
fl_rectf(0, 0, CELL_W, h(), G_COLOR_GREY_1);
fl_color(G_COLOR_GREY_3);
fl_line_style(FL_DASH, 0, nullptr);
+
for (int i=1; i<=MAX_KEYS+1; i++) {
switch (i % KEYS) {
case (int) Notes::G:
fl_line(0, i*CELL_H, CELL_W, i*CELL_H);
}
}
+
fl_line_style(0);
fl_end_offscreen();
}
{
fl_copy_offscreen(x(), y(), CELL_W, h(), surface1, 0, 0);
-#if defined(__APPLE__)
- for (int i=36; i<pParent->totalWidth; i+=36) /// TODO: i < pParent->coverX is faster
+#if defined(__APPLE__) // TODO - is this still useful?
+ for (Pixel i=36; i<m_base->fullWidth; i+=36) /// TODO: i < ae->coverX is faster
fl_copy_offscreen(x()+i, y(), CELL_W, h(), surface2, 1, 0);
#else
- for (int i=CELL_W; i<pParent->totalWidth; i+=CELL_W) /// TODO: i < pParent->coverX is faster
+ for (Pixel i=CELL_W; i<m_base->loopWidth; i+=CELL_W)
fl_copy_offscreen(x()+i, y(), CELL_W, h(), surface2, 0, 0);
#endif
int gePianoRoll::handle(int e)
{
- int ret = Fl_Group::handle(e);
- switch (e) {
- case FL_PUSH: {
+ if (e == FL_PUSH && Fl::event_button3()) {
+ pick = Fl::event_y() - y();
+ return geBaseActionEditor::handle(e);
+ }
+ if (e == FL_DRAG && Fl::event_button3()) {
+ static_cast<geNoteEditor*>(parent())->scroll();
+ return 1;
+ }
+ return geBaseActionEditor::handle(e);
+}
- /* avoid click on grey area */
- if (Fl::event_x() >= pParent->coverX) {
- ret = 1;
- break;
- }
+/* -------------------------------------------------------------------------- */
- push_y = Fl::event_y() - y();
- if (Fl::event_button1()) {
+void gePianoRoll::onAddAction()
+{
+ Frame frame = m_base->pixelToFrame(Fl::event_x() - x());
+ int note = yToNote(Fl::event_y() - y());
+ c::recorder::recordMidiAction(m_ch->index, note, G_MAX_VELOCITY, frame);
+
+ m_base->rebuild(); // Rebuild velocityEditor as well
+}
- /* ax is driven by grid, ay by the height in px of each note. */
- int ax = Fl::event_x();
- int ay = Fl::event_y();
+/* -------------------------------------------------------------------------- */
- /* Vertical snap. */
- int edge = (ay-y()) % CELL_H;
- if (edge != 0) ay -= edge;
+void gePianoRoll::onDeleteAction()
+{
+ c::recorder::deleteMidiAction(static_cast<MidiChannel*>(m_ch), m_action->a1, m_action->a2);
+
+ m_base->rebuild(); // Rebuild velocityEditor as well
+}
- /* If there are no pianoItems below the mouse, add a new one. */
- gePianoItem* pianoItem = dynamic_cast<gePianoItem*>(Fl::belowmouse());
- if (pianoItem == nullptr)
- recordAction(yToNote(ay-y()), (ax-x()) * pParent->zoom);
- }
- ret = 1;
- break;
- }
+/* -------------------------------------------------------------------------- */
- case FL_DRAG: {
- if (Fl::event_button3()) {
+void gePianoRoll::onMoveAction()
+{
+ /* Y computation: - (CELL_H/2) is wrong: we should need the y pick value as
+ done with x. Let's change this when vertical piano zoom will be available. */
- geNoteEditor* prc = static_cast<geNoteEditor*>(parent());
- position(x(), Fl::event_y() - push_y);
+ Pixel ex = Fl::event_x() - m_action->pick;
+ Pixel ey = snapToY(Fl::event_y() - y() - (CELL_H/2)) + y();
- if (y() > prc->y())
- position(x(), prc->y());
- else
- if (y() < prc->y()+prc->h()-h())
- position(x(), prc->y()+prc->h()-h());
+ Pixel x1 = x();
+ Pixel x2 = (m_base->loopWidth + x()) - m_action->w();
+ Pixel y1 = y();
+ Pixel y2 = y() + h();
- prc->redraw();
- }
- ret = 1;
- break;
- }
+ if (ex < x1) ex = x1; else if (ex > x2) ex = x2;
+ if (ey < y1) ey = y1; else if (ey > y2) ey = y2;
- case FL_MOUSEWHEEL: { // nothing to do, just avoid small internal scroll
- ret = 1;
- break;
- }
- }
- return ret;
+ m_action->position(ex, ey);
}
/* -------------------------------------------------------------------------- */
-void gePianoRoll::recordAction(int note, int frame_a, int frame_b)
+void gePianoRoll::onResizeAction()
{
- c::recorder::recordMidiAction(pParent->chan->index, note, frame_a, frame_b);
- pParent->chan->hasActions = true;
- build();
+ if (static_cast<gePianoItem*>(m_action)->orphaned)
+ return;
+
+ Pixel ex = Fl::event_x();
+
+ Pixel x1 = x();
+ Pixel x2 = m_base->loopWidth + x();
+
+ if (ex < x1) ex = x1; else if (ex > x2) ex = x2;
+
+ if (m_action->onRightEdge)
+ m_action->setRightEdge(ex - m_action->x());
+ else
+ m_action->setLeftEdge(ex);
}
/* -------------------------------------------------------------------------- */
-int gePianoRoll::yToNote(int y)
+void gePianoRoll::onRefreshAction()
+{
+ namespace cr = c::recorder;
+
+ if (static_cast<gePianoItem*>(m_action)->orphaned)
+ return;
+
+ Pixel p1 = m_action->x() - x();
+ Pixel p2 = m_action->x() + m_action->w() - x();
+
+ Frame f1 = 0;
+ Frame f2 = 0;
+
+ if (!m_action->isOnEdges()) {
+ f1 = m_base->pixelToFrame(p1);
+ f2 = m_base->pixelToFrame(p2, /*snap=*/false) - (m_base->pixelToFrame(p1, /*snap=*/false) - f1);
+ }
+ else if (m_action->onLeftEdge) {
+ f1 = m_base->pixelToFrame(p1);
+ f2 = m_action->a2.frame;
+ }
+ else if (m_action->onRightEdge) {
+ f1 = m_action->a1.frame;
+ f2 = m_base->pixelToFrame(p2);
+ }
+
+ assert(f1 != 0 && f2 != 0);
+
+ int note = yToNote(m_action->y() - y());
+ int velocity = m::MidiEvent(m_action->a1.iValue).getVelocity();
+
+ /* TODO - less then optimal. Let's wait for recorder refactoring... */
+
+ int oldNote = m::MidiEvent(m_action->a1.iValue).getNote();
+ cr::deleteMidiAction(static_cast<MidiChannel*>(m_ch), m_action->a1, m_action->a2);
+ if (cr::midiActionCanFit(m_ch->index, note, f1, f2))
+ cr::recordMidiAction(m_ch->index, note, velocity, f1, f2);
+ else
+ cr::recordMidiAction(m_ch->index, oldNote, velocity, m_action->a1.frame, m_action->a2.frame);
+
+ m_base->rebuild(); // Rebuild velocityEditor as well
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int gePianoRoll::yToNote(Pixel p) const
+{
+ return gePianoRoll::MAX_KEYS - (p / gePianoRoll::CELL_H);
+}
+
+
+Pixel gePianoRoll::noteToY(int n) const
+{
+ return (MAX_KEYS * CELL_H) - (n * gePianoRoll::CELL_H);
+}
+
+
+Pixel gePianoRoll::snapToY(Pixel p) const
{
- return gePianoRoll::MAX_KEYS - (y / gePianoRoll::CELL_H);
+ return u::math::quantize(p, CELL_H);
}
+
/* -------------------------------------------------------------------------- */
-void gePianoRoll::updateActions()
+void gePianoRoll::rebuild()
{
- for (int k=0; k<children(); k++)
- static_cast<geBasePianoItem*>(child(k))->reposition(x());
+ namespace mr = m::recorder;
+ namespace cr = c::recorder;
+
+ /* Remove all existing actions and set a new width, according to the current
+ zoom level. */
+
+ clear();
+ size(m_base->fullWidth, (MAX_KEYS + 1) * CELL_H);
+
+ vector<mr::Composite> actions = cr::getMidiActions(m_ch->index);
+ for (mr::Composite comp : actions)
+ {
+ m::MidiEvent e1 = comp.a1.iValue;
+ m::MidiEvent e2 = comp.a2.iValue;
+
+ gu_log("[gePianoRoll::rebuild] ((0x%X, 0x%X, f=%d) - (0x%X, 0x%X, f=%d))\n",
+ e1.getStatus(), e1.getNote(), comp.a1.frame,
+ e2.getStatus(), e2.getNote(), comp.a2.frame
+ );
+
+ Pixel px = x() + m_base->frameToPixel(comp.a1.frame);
+ Pixel py = y() + noteToY(e1.getNote());
+ Pixel pw = m_base->frameToPixel(comp.a2.frame - comp.a1.frame);
+ Pixel ph = CELL_H;
+
+ add(new gePianoItem(px, py, pw, ph, comp.a1, comp.a2));
+ }
+
+ drawSurface1();
+ drawSurface2();
+
+ redraw();
}
+}} // giada::v::
\ No newline at end of file
#include "baseActionEditor.h"
-class gdActionEditor;
+class MidiChannel;
+namespace giada {
+namespace v
+{
class gePianoRoll : public geBaseActionEditor
{
private:
A = 11, GS = 0
};
- int push_y;
-
Fl_Offscreen surface1; // notes, no repeat
Fl_Offscreen surface2; // lines, x-repeat
+ void onAddAction() override;
+ void onDeleteAction() override;
+ void onMoveAction() override;
+ void onResizeAction() override;
+ void onRefreshAction() override;
+
/* drawSurface*
Generates a complex drawing in memory first and copy it to the screen at a
later point in time. Fl_Offscreen surface holds the necessary data. The first
void drawSurface1();
void drawSurface2();
-
- void build();
+ Pixel snapToY(Pixel p) const;
+ int yToNote(Pixel y) const;
+ Pixel noteToY(int n) const;
public:
static const int MAX_KEYS = 127;
static const int MAX_OCTAVES = 9;
static const int KEYS = 12;
- static const int CELL_H = 18;
- static const int CELL_W = 40;
+ static const Pixel CELL_H = 20;
+ static const Pixel CELL_W = 40;
- gePianoRoll(int x, int y, int w, gdActionEditor* pParent);
+ gePianoRoll(Pixel x, Pixel y, Pixel w, MidiChannel* ch);
void draw() override;
int handle(int e) override;
- /* updateActions
- Repositions existing actions after a zoom gesture. */
-
- void updateActions() override;
-
- void recordAction(int note, int frame_a, int frame_b=0);
+ void rebuild() override;
- int yToNote(int y);
+ Pixel pick;
};
+}} // giada::v::
#endif
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#include <FL/fl_draw.H>
+#include "../../../core/const.h"
+#include "../../../core/sampleChannel.h"
+#include "sampleAction.h"
+
+
+namespace giada {
+namespace v
+{
+geSampleAction::geSampleAction(Pixel X, Pixel Y, Pixel W, Pixel H,
+ const SampleChannel* ch, m::recorder::action a1, m::recorder::action a2)
+: geBaseAction(X, Y, W, H, ch->mode == ChannelMode::SINGLE_PRESS, a1, a2),
+ m_ch (ch)
+{
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geSampleAction::draw()
+{
+ Fl_Color color = hovered ? G_COLOR_LIGHT_2 : G_COLOR_LIGHT_1;
+
+ if (m_ch->mode == ChannelMode::SINGLE_PRESS) {
+ fl_rectf(x(), y(), w(), h(), color);
+ }
+ else {
+ if (a1.type == G_ACTION_KILL)
+ fl_rect(x(), y(), MIN_WIDTH, h(), color);
+ else {
+ fl_rectf(x(), y(), MIN_WIDTH, h(), color);
+ if (a1.type == G_ACTION_KEYPRESS)
+ fl_rectf(x()+3, y()+h()-11, w()-6, 8, G_COLOR_GREY_4);
+ else
+ if (a1.type == G_ACTION_KEYREL)
+ fl_rectf(x()+3, y()+3, w()-6, 8, G_COLOR_GREY_4);
+ }
+ }
+}
+}} // giada::v::
\ No newline at end of file
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#ifndef GE_SAMPLE_ACTION_H
+#define GE_SAMPLE_ACTION_H
+
+
+#include "../../../core/recorder.h"
+#include "baseAction.h"
+
+
+class SampleChannel;
+
+
+namespace giada {
+namespace v
+{
+class geSampleAction : public geBaseAction
+{
+private:
+
+ const SampleChannel* m_ch;
+
+public:
+
+ geSampleAction(Pixel x, Pixel y, Pixel w, Pixel h, const SampleChannel* ch,
+ m::recorder::action a1, m::recorder::action a2);
+
+ void draw() override;
+};
+}} // giada::v::
+
+
+#endif
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#include <FL/Fl.H>
+#include <FL/fl_draw.H>
+#include "../../../core/const.h"
+#include "../../../core/conf.h"
+#include "../../../core/sampleChannel.h"
+#include "../../../utils/log.h"
+#include "../../../glue/recorder.h"
+#include "../../dialogs/actionEditor/baseActionEditor.h"
+#include "sampleAction.h"
+#include "sampleActionEditor.h"
+
+
+using std::vector;
+
+
+namespace giada {
+namespace v
+{
+geSampleActionEditor::geSampleActionEditor(Pixel x, Pixel y, SampleChannel* ch)
+: geBaseActionEditor(x, y, 200, m::conf::sampleActionEditorH, ch)
+{
+ rebuild();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+geSampleActionEditor::~geSampleActionEditor()
+{
+ m::conf::sampleActionEditorH = h();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geSampleActionEditor::rebuild()
+{
+ namespace mr = m::recorder;
+ namespace cr = c::recorder;
+
+ const SampleChannel* ch = static_cast<const SampleChannel*>(m_ch);
+
+ /* Remove all existing actions and set a new width, according to the current
+ zoom level. */
+
+ clear();
+ size(m_base->fullWidth, h());
+
+ vector<mr::Composite> comps = cr::getSampleActions(ch);
+
+ for (mr::Composite comp : comps) {
+ gu_log("[geSampleActionEditor::rebuild] Action [%d, %d)\n",
+ comp.a1.frame, comp.a2.frame);
+ Pixel px = x() + m_base->frameToPixel(comp.a1.frame);
+ Pixel py = y() + 4;
+ Pixel pw = 0;
+ Pixel ph = h() - 8;
+ if (comp.a2.frame != -1)
+ pw = m_base->frameToPixel(comp.a2.frame - comp.a1.frame);
+
+ geSampleAction* a = new geSampleAction(px, py, pw, ph, ch, comp.a1, comp.a2);
+ add(a);
+ resizable(a);
+ }
+
+ /* If channel is LOOP_ANY, deactivate it: a loop mode channel cannot hold
+ keypress/keyrelease actions. */
+
+ ch->isAnyLoopMode() ? deactivate() : activate();
+
+ redraw();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geSampleActionEditor::draw()
+{
+ /* Draw basic boundaries (+ beat bars) and hide the unused area. Then draw
+ children (the actions). */
+
+ baseDraw();
+
+ /* Print label. */
+
+ fl_color(G_COLOR_GREY_4);
+ fl_font(FL_HELVETICA, G_GUI_FONT_SIZE_BASE);
+ if (active())
+ fl_draw("start/stop", x()+4, y(), w(), h(), (Fl_Align) (FL_ALIGN_LEFT | FL_ALIGN_CENTER));
+ else
+ fl_draw("start/stop (disabled)", x()+4, y(), w(), h(), (Fl_Align) (FL_ALIGN_LEFT | FL_ALIGN_CENTER));
+
+ draw_children();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geSampleActionEditor::onAddAction()
+{
+ Frame f = m_base->pixelToFrame(Fl::event_x() - x());
+ c::recorder::recordSampleAction(static_cast<SampleChannel*>(m_ch),
+ m_base->getActionType(), f);
+ rebuild();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geSampleActionEditor::onDeleteAction()
+{
+ c::recorder::deleteSampleAction(static_cast<SampleChannel*>(m_ch), m_action->a1, m_action->a2);
+ rebuild();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geSampleActionEditor::onMoveAction()
+{
+ Pixel ex = Fl::event_x() - m_action->pick;
+
+ Pixel x1 = x();
+ Pixel x2 = m_base->loopWidth + x() - m_action->w();
+
+ if (ex < x1) ex = x1; else if (ex > x2) ex = x2;
+
+ m_action->setPosition(ex);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geSampleActionEditor::onResizeAction()
+{
+ Pixel ex = Fl::event_x();
+
+ Pixel x1 = x();
+ Pixel x2 = m_base->loopWidth + x();
+
+ if (ex < x1) ex = x1; else if (ex > x2) ex = x2;
+
+ if (m_action->onRightEdge)
+ m_action->setRightEdge(ex - m_action->x());
+ else
+ m_action->setLeftEdge(ex);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geSampleActionEditor::onRefreshAction()
+{
+ namespace cr = c::recorder;
+
+ SampleChannel* ch = static_cast<SampleChannel*>(m_ch);
+
+ Pixel p1 = m_action->x() - x();
+ Pixel p2 = m_action->x() + m_action->w() - x();
+ Frame f1 = 0;
+ Frame f2 = 0;
+ int type = m_action->a1.type;
+
+ if (!m_action->isOnEdges()) {
+ f1 = m_base->pixelToFrame(p1);
+ f2 = m_base->pixelToFrame(p2, /*snap=*/false) - (m_base->pixelToFrame(p1, /*snap=*/false) - f1);
+ }
+ else if (m_action->onLeftEdge) {
+ f1 = m_base->pixelToFrame(p1);
+ f2 = m_action->a2.frame;
+ }
+ else if (m_action->onRightEdge) {
+ f1 = m_action->a1.frame;
+ f2 = m_base->pixelToFrame(p2);
+ }
+
+ /* TODO - less then optimal. Let's wait for recorder refactoring... */
+
+ cr::deleteSampleAction(ch, m_action->a1, m_action->a2);
+ if (cr::sampleActionCanFit(ch, f1, f2))
+ cr::recordSampleAction(ch, type, f1, f2);
+ else
+ cr::recordSampleAction(ch, type, m_action->a1.frame, m_action->a2.frame);
+
+ rebuild();
+}
+}} // giada::v::
\ No newline at end of file
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#ifndef GE_SAMPLE_ACTION_EDITOR_H
+#define GE_SAMPLE_ACTION_EDITOR_H
+
+
+#include "baseActionEditor.h"
+
+
+class SampleChannel;
+
+
+namespace giada {
+namespace v
+{
+class geSampleAction;
+
+
+class geSampleActionEditor : public geBaseActionEditor
+{
+private:
+
+ void onAddAction() override;
+ void onDeleteAction() override;
+ void onMoveAction() override;
+ void onResizeAction() override;
+ void onRefreshAction() override;
+
+public:
+
+ geSampleActionEditor(Pixel x, Pixel y, SampleChannel* ch);
+ ~geSampleActionEditor();
+
+ void draw() override;
+
+ void rebuild() override;
+};
+}} // giada::v::
+
+#endif
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#include <FL/Fl.H>
+#include <FL/fl_draw.H>
+#include "../../../utils/log.h"
+#include "../../../utils/math.h"
+#include "../../../core/const.h"
+#include "../../../core/conf.h"
+#include "../../../core/clock.h"
+#include "../../../core/midiChannel.h"
+#include "../../../glue/recorder.h"
+#include "../../dialogs/actionEditor/baseActionEditor.h"
+#include "envelopePoint.h"
+#include "velocityEditor.h"
+
+
+using std::vector;
+
+
+namespace giada {
+namespace v
+{
+geVelocityEditor::geVelocityEditor(Pixel x, Pixel y, MidiChannel* ch)
+: geBaseActionEditor(x, y, 200, m::conf::velocityEditorH, ch)
+{
+ rebuild();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+geVelocityEditor::~geVelocityEditor()
+{
+ m::conf::velocityEditorH = h();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geVelocityEditor::draw()
+{
+ baseDraw();
+
+ /* Print label. */
+
+ fl_color(G_COLOR_GREY_4);
+ fl_font(FL_HELVETICA, G_GUI_FONT_SIZE_BASE);
+ fl_draw("Velocity", x()+4, y(), w(), h(), (Fl_Align) (FL_ALIGN_LEFT));
+
+ if (children() == 0)
+ return;
+
+ Pixel side = geEnvelopePoint::SIDE / 2;
+
+ for (int i=0; i<children(); i++) {
+ geEnvelopePoint* p = static_cast<geEnvelopePoint*>(child(i));
+ if (m_action == nullptr)
+ p->position(p->x(), valueToY(m::MidiEvent(p->a1.iValue).getVelocity()));
+ Pixel x1 = p->x() + side;
+ Pixel y1 = p->y();
+ Pixel y2 = y() + h();
+ fl_line(x1, y1, x1, y2);
+ }
+
+ draw_children();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+Pixel geVelocityEditor::valueToY(int v) const
+{
+ /* Cast the input type of 'v' to float, to make the mapping more precise. */
+ return u::math::map<float, Pixel>(v, 0, G_MAX_VELOCITY, y() + (h() - geEnvelopePoint::SIDE), y());
+}
+
+
+int geVelocityEditor::yToValue(Pixel px) const
+{
+ return u::math::map<Pixel, int>(px, h() - geEnvelopePoint::SIDE, 1, 0, G_MAX_VELOCITY);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geVelocityEditor::rebuild()
+{
+ namespace mr = m::recorder;
+ namespace cr = c::recorder;
+
+ /* Remove all existing actions and set a new width, according to the current
+ zoom level. */
+
+ clear();
+ size(m_base->fullWidth, h());
+
+ vector<mr::Composite> actions = cr::getMidiActions(m_ch->index);
+ for (mr::Composite comp : actions)
+ {
+ gu_log("[geVelocityEditor::rebuild] f=%d\n", comp.a1.frame);
+
+ Pixel px = x() + m_base->frameToPixel(comp.a1.frame);
+ Pixel py = y() + valueToY(m::MidiEvent(comp.a1.iValue).getVelocity());
+
+ add(new geEnvelopePoint(px, py, comp.a1));
+ }
+
+ resizable(nullptr);
+ redraw();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geVelocityEditor::onMoveAction()
+{
+ Pixel ey = Fl::event_y() - (geEnvelopePoint::SIDE / 2);
+
+ Pixel y1 = y();
+ Pixel y2 = y() + h() - geEnvelopePoint::SIDE;
+
+ if (ey < y1) ey = y1; else if (ey > y2) ey = y2;
+
+ m_action->position(m_action->x(), ey);
+ redraw();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geVelocityEditor::onRefreshAction()
+{
+ c::recorder::setVelocity(m_ch, m_action->a1, yToValue(m_action->y() - y()));
+
+ m_base->rebuild(); // Rebuild pianoRoll as well
+}
+}} // giada::v::
\ No newline at end of file
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#ifndef GE_VELOCITY_EDITOR_H
+#define GE_VELOCITY_EDITOR_H
+
+
+#include "baseActionEditor.h"
+
+
+class MidiChannel;
+
+
+namespace giada {
+namespace v
+{
+class geEnvelopePoint;
+
+
+class geVelocityEditor : public geBaseActionEditor
+{
+private:
+
+ void onMoveAction() override;
+ void onRefreshAction() override;
+ void onAddAction() override{};
+ void onDeleteAction() override{};
+ void onResizeAction() override{};
+
+ Pixel valueToY(int v) const;
+ int yToValue(Pixel y) const;
+
+public:
+
+ geVelocityEditor(Pixel x, Pixel y, MidiChannel* ch);
+ ~geVelocityEditor();
+
+ void draw() override;
+
+ void rebuild() override;
+};
+}} // giada::v::
+
+
+#endif
system = new geChoice(x()+w()-250, y()+9, 250, 20, "System");
portOut = new geChoice(x()+w()-250, system->y()+system->h()+8, 250, 20, "Output port");
portIn = new geChoice(x()+w()-250, portOut->y()+portOut->h()+8, 250, 20, "Input port");
- noNoteOff = new geCheck (x()+w()-250, portIn->y()+portIn->h()+8, 230, 20, "Device does not send NoteOff");
- midiMap = new geChoice(x()+w()-250, noNoteOff->y()+noNoteOff->h(), 250, 20, "Output Midi Map");
+ midiMap = new geChoice(x()+w()-250, portIn->y()+portIn->h()+8, 250, 20, "Output Midi Map");
sync = new geChoice(x()+w()-250, midiMap->y()+midiMap->h()+8, 250, 20, "Sync");
new geBox(x(), sync->y()+sync->h()+8, w(), h()-150, "Restart Giada for the changes to take effect.");
end();
fetchInPorts();
fetchMidiMaps();
- noNoteOff->value(conf::noNoteOff);
-
sync->add("(disabled)");
sync->add("MIDI Clock (master)");
sync->add("MTC (master)");
conf::midiPortOut = portOut->value()-1; // -1 because midiPortOut=-1 is '(disabled)'
conf::midiPortIn = portIn->value()-1; // -1 because midiPortIn=-1 is '(disabled)'
-
- conf::noNoteOff = noNoteOff->value();
conf::midiMapPath = midimap::maps.size() == 0 ? "" : midiMap->text(midiMap->value());
if (sync->value() == 0)
/* -------------------------------------------------------------------------- */
-void geTabMidi::cb_changeSystem(Fl_Widget *w, void *p) { ((geTabMidi*)p)->__cb_changeSystem(); }
+void geTabMidi::cb_changeSystem(Fl_Widget* w, void* p) { ((geTabMidi*)p)->cb_changeSystem(); }
/* -------------------------------------------------------------------------- */
-void geTabMidi::__cb_changeSystem()
+void geTabMidi::cb_changeSystem()
{
/* if the user changes MIDI device (eg ALSA->JACK) device menu deactivates.
* If it returns to the original system, we re-fill the list by
portIn->clear();
fetchInPorts();
portIn->activate();
- noNoteOff->activate();
sync->activate();
}
else {
portIn->clear();
portIn->add("-- restart to fetch device(s) --");
portIn->value(0);
- noNoteOff->deactivate();
sync->deactivate();
}
void fetchInPorts();
void fetchMidiMaps();
- static void cb_changeSystem (Fl_Widget *w, void *p);
- inline void __cb_changeSystem();
+ static void cb_changeSystem(Fl_Widget* w, void* p);
+ inline void cb_changeSystem();
int systemInitValue;
public:
- geChoice *system;
- geChoice *portOut;
- geChoice *portIn;
- geCheck *noNoteOff;
- geChoice *midiMap;
- geChoice *sync;
+ geChoice* system;
+ geChoice* portOut;
+ geChoice* portIn;
+ geChoice* midiMap;
+ geChoice* sync;
geTabMidi(int x, int y, int w, int h);
{
ch->mode = static_cast<ChannelMode>(mode);
- /* what to do when the channel is playing and you change the mode?
- * Nothing, since v0.5.3. Just refresh the action editor window, in
- * case it's open */
+ /* What to do when the channel is playing and you change the mode? Nothing,
+ since v0.5.3. Just refresh the action editor window, in case it's open. */
gu_refreshActionEditor();
}
#include "../../../../glue/recorder.h"
#include "../../../dialogs/gd_mainWindow.h"
#include "../../../dialogs/channelNameInput.h"
-#include "../../../dialogs/gd_actionEditor.h"
#include "../../../dialogs/gd_warnings.h"
#include "../../../dialogs/gd_keyGrabber.h"
#include "../../../dialogs/pluginList.h"
+#include "../../../dialogs/actionEditor/midiActionEditor.h"
#include "../../../dialogs/midiIO/midiInputChannel.h"
#include "../../../dialogs/midiIO/midiOutputMidiCh.h"
#include "../../basics/boxtypes.h"
using namespace giada;
geMidiChannel* gch = static_cast<geMidiChannel*>(w);
+ MidiChannel* ch = static_cast<MidiChannel*>(gch->ch);
+
Menu selectedItem = (Menu) (intptr_t) v;
switch (selectedItem)
case Menu::__END_RESIZE_SUBMENU__:
break;
case Menu::EDIT_ACTIONS:
- gu_openSubWindow(G_MainWin, new gdActionEditor(gch->ch), WID_ACTION_EDITOR);
+ gu_openSubWindow(G_MainWin, new v::gdMidiActionEditor(ch), WID_ACTION_EDITOR);
break;
case Menu::CLEAR_ACTIONS_ALL:
c::recorder::clearAllActions(gch);
gu_openSubWindow(G_MainWin, new gdMidiInputChannel(gch->ch), 0);
break;
case Menu::SETUP_MIDI_OUTPUT:
- gu_openSubWindow(G_MainWin,
- new gdMidiOutputMidiCh(static_cast<MidiChannel*>(gch->ch)), 0);
+ gu_openSubWindow(G_MainWin, new gdMidiOutputMidiCh(ch), 0);
break;
case Menu::RESIZE_H1:
gch->changeSize(G_GUI_CHANNEL_H_1);
#include "../../../dialogs/gd_keyGrabber.h"
#include "../../../dialogs/sampleEditor.h"
#include "../../../dialogs/channelNameInput.h"
-#include "../../../dialogs/gd_actionEditor.h"
#include "../../../dialogs/gd_warnings.h"
+#include "../../../dialogs/actionEditor/sampleActionEditor.h"
#include "../../../dialogs/browser/browserSave.h"
#include "../../../dialogs/browser/browserLoad.h"
#include "../../../dialogs/midiIO/midiOutputSampleCh.h"
EDIT_ACTIONS,
CLEAR_ACTIONS,
CLEAR_ACTIONS_ALL,
- CLEAR_ACTIONS_MUTE,
CLEAR_ACTIONS_VOLUME,
CLEAR_ACTIONS_START_STOP,
__END_CLEAR_ACTIONS_SUBMENU__,
using namespace giada;
geSampleChannel* gch = static_cast<geSampleChannel*>(w);
+ SampleChannel* ch = static_cast<SampleChannel*>(gch->ch);
+
Menu selectedItem = (Menu) (intptr_t) v;
switch (selectedItem) {
break;
}
case Menu::SETUP_MIDI_OUTPUT: {
- gu_openSubWindow(G_MainWin, new gdMidiOutputSampleCh(static_cast<SampleChannel*>(gch->ch)), 0);
+ gu_openSubWindow(G_MainWin, new gdMidiOutputSampleCh(ch), 0);
break;
}
case Menu::EDIT_SAMPLE: {
- gu_openSubWindow(G_MainWin, new gdSampleEditor(static_cast<SampleChannel*>(gch->ch)), WID_SAMPLE_EDITOR);
+ gu_openSubWindow(G_MainWin, new gdSampleEditor(ch), WID_SAMPLE_EDITOR);
break;
}
case Menu::EDIT_ACTIONS: {
- gu_openSubWindow(G_MainWin, new gdActionEditor(gch->ch), WID_ACTION_EDITOR);
+ gu_openSubWindow(G_MainWin, new v::gdSampleActionEditor(ch), WID_ACTION_EDITOR);
break;
}
case Menu::CLEAR_ACTIONS:
c::recorder::clearAllActions(gch);
break;
}
- case Menu::CLEAR_ACTIONS_MUTE: {
- c::recorder::clearMuteActions(gch);
- break;
- }
case Menu::CLEAR_ACTIONS_VOLUME: {
c::recorder::clearVolumeActions(gch);
break;
}; // {namespace}
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
{"Edit actions...", 0, menuCallback, (void*) Menu::EDIT_ACTIONS},
{"Clear actions", 0, menuCallback, (void*) Menu::CLEAR_ACTIONS, FL_SUBMENU},
{"All", 0, menuCallback, (void*) Menu::CLEAR_ACTIONS_ALL},
- {"Mute", 0, menuCallback, (void*) Menu::CLEAR_ACTIONS_MUTE},
{"Volume", 0, menuCallback, (void*) Menu::CLEAR_ACTIONS_VOLUME},
{"Start/Stop", 0, menuCallback, (void*) Menu::CLEAR_ACTIONS_START_STOP},
{0},
/* fl_xid() from FLTK returns a pointer to NSWindow, but plugins on OS X want a
- * pointer to NSView. The function does the hard conversion. */
+pointer to NSView. The function does the hard conversion. */
-void *cocoa_getViewFromWindow(void *p);
+void* cocoa_getViewFromWindow(void* p);
/* A bug on on OS X seems to misalign plugins' UI. The function takes care of
- * fixing the positioning. */
+fixing the positioning.
+TODO temporarily disabled: it does not work. */
-void cocoa_setWindowSize(void *p, int w, int h);
+//void cocoa_setWindowSize(void *p, int w, int h);
#endif
#import "cocoa.h"
-void *cocoa_getViewFromWindow(void *p)
+void* cocoa_getViewFromWindow(void* p)
{
- NSWindow *win = (NSWindow *) p;
- return (void*) [win contentView];
+ NSWindow* win = (NSWindow* ) p;
+ return (void*) win.contentView;
}
-
+/*
void cocoa_setWindowSize(void *p, int w, int h)
{
NSWindow *win = (NSWindow *) p;
[win setContentSize:NSMakeSize(w, h)];
}
+*/
\ No newline at end of file
#include <string>
+#include <FL/Fl.H>
#include <FL/fl_draw.H>
#if defined(_WIN32)
#include "../ext/resource.h"
#include <X11/xpm.h>
#endif
#include "../core/mixer.h"
-#include "../core/recorder.h"
-#include "../core/wave.h"
#include "../core/clock.h"
#include "../core/pluginHost.h"
#include "../core/channel.h"
#include "../core/graphics.h"
#include "../gui/dialogs/gd_warnings.h"
#include "../gui/dialogs/gd_mainWindow.h"
-#include "../gui/dialogs/gd_actionEditor.h"
+#include "../gui/dialogs/actionEditor/baseActionEditor.h"
#include "../gui/dialogs/window.h"
#include "../gui/dialogs/sampleEditor.h"
#include "../gui/elems/mainWindow/mainIO.h"
#include "gui.h"
-extern gdMainWindow *G_MainWin;
+extern gdMainWindow* G_MainWin;
using std::string;
+using namespace giada;
using namespace giada::m;
+using namespace giada::v;
static int blinker = 0;
void gu_refreshActionEditor()
{
- /** TODO - why don't we simply call WID_ACTION_EDITOR->redraw()? */
-
- gdActionEditor* aeditor = (gdActionEditor*) G_MainWin->getChild(WID_ACTION_EDITOR);
- if (aeditor) {
- Channel *chan = aeditor->chan;
- G_MainWin->delSubWindow(WID_ACTION_EDITOR);
- gu_openSubWindow(G_MainWin, new gdActionEditor(chan), WID_ACTION_EDITOR);
- }
+ gdBaseActionEditor* ae = static_cast<gdBaseActionEditor*>(G_MainWin->getChild(WID_ACTION_EDITOR));
+ if (ae != nullptr)
+ ae->rebuild();
}
/* -------------------------------------------------------------------------- */
-gdWindow *gu_getSubwindow(gdWindow *parent, int id)
+gdWindow* gu_getSubwindow(gdWindow* parent, int id)
{
if (parent->hasWindow(id))
return parent->getChild(id);
/* -------------------------------------------------------------------------- */
-int gu_getStringWidth(const std::string &s)
+int gu_getStringWidth(const std::string& s)
{
- int w = 0;
- int h = 0;
- fl_measure(s.c_str(), w, h);
- return w;
+ int w = 0;
+ int h = 0;
+ fl_measure(s.c_str(), w, h);
+ return w;
}
/* -------------------------------------------------------------------------- */
-string gu_removeFltkChars(const string &s)
+string gu_removeFltkChars(const string& s)
{
string out = gu_replace(s, "/", "-");
out = gu_replace(out, "|", "-");
/* -------------------------------------------------------------------------- */
+int quantize(int x, int step)
+{
+ /* Source:
+ https://en.wikipedia.org/wiki/Quantization_(signal_processing)#Rounding_example */
+ return step * floor((x / (float) step) + 0.5f);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
float dBtoLinear(float f)
{
return std::pow(10, f/20.0f);
{
float linearToDB(float f);
float dBtoLinear(float f);
+int quantize(int x, int step);
/* map (template)
Maps 'x' in range [a, b] to a new range [w, z]. Source:
https://en.wikipedia.org/wiki/Linear_equation#Two-point_form*/
-template <typename T>
-T map(T x, T a, T b, T w, T z)
+template <typename Tin, typename Tout>
+Tout map(Tin x, Tin a, Tin b, Tout w, Tout z)
{
- return (((x - a) / (b - a)) * (z - w)) + w;
+ return (((x - a) / (float) (b - a)) * (z - w)) + w;
}
}}} // giada::u::math::
conf::midiSystem = 11;
conf::midiPortOut = 12;
conf::midiPortIn = 13;
- conf::noNoteOff = false;
conf::midiMapPath = "path/to/midi/map";
conf::lastFileMap = "path/to/last/midi/map";
conf::midiSync = 14;
REQUIRE(conf::midiSystem == 11);
REQUIRE(conf::midiPortOut == 12);
REQUIRE(conf::midiPortIn == 13);
- REQUIRE(conf::noNoteOff == false);
REQUIRE(conf::midiMapPath == "path/to/midi/map");
REQUIRE(conf::lastFileMap == "path/to/last/midi/map");
REQUIRE(conf::midiSync == 14);
REQUIRE(recorder::frames.at(0) == 0);
REQUIRE(recorder::frames.at(1) == 80);
}
-
+
SECTION("Test overdub, full overwrite")
{
- recorder::rec(0, G_ACTION_MUTEON, 0, 1, 0.5f);
- recorder::rec(0, G_ACTION_MUTEOFF, 80, 1, 0.5f);
- recorder::rec(0, G_ACTION_MUTEON, 200, 1, 0.5f);
- recorder::rec(0, G_ACTION_MUTEOFF, 400, 1, 0.5f);
+ recorder::rec(0, G_ACTION_KEYPRESS, 0, 1, 0.5f);
+ recorder::rec(0, G_ACTION_KEYREL, 80, 1, 0.5f);
+ recorder::rec(0, G_ACTION_KEYPRESS, 200, 1, 0.5f);
+ recorder::rec(0, G_ACTION_KEYREL, 400, 1, 0.5f);
/* Should delete all actions in between and keep the first one, plus a
new last action on frame 500. */
- recorder::startOverdub(0, G_ACTION_MUTEON | G_ACTION_MUTEOFF, 0, 1024);
+ recorder::startOverdub(0, G_ACTION_KEYPRESS | G_ACTION_KEYREL, 0, 1024);
recorder::stopOverdub(500, 500, &mutex);
REQUIRE(recorder::frames.size() == 2);
REQUIRE(recorder::frames.at(0) == 0);
REQUIRE(recorder::frames.at(1) == 500);
REQUIRE(recorder::global.at(0).at(0)->frame == 0);
- REQUIRE(recorder::global.at(0).at(0)->type == G_ACTION_MUTEON);
+ REQUIRE(recorder::global.at(0).at(0)->type == G_ACTION_KEYPRESS);
REQUIRE(recorder::global.at(1).at(0)->frame == 500);
- REQUIRE(recorder::global.at(1).at(0)->type == G_ACTION_MUTEOFF);
+ REQUIRE(recorder::global.at(1).at(0)->type == G_ACTION_KEYREL);
}
SECTION("Test overdub, left overlap")
{
- recorder::rec(0, G_ACTION_MUTEON, 100, 1, 0.5f);
- recorder::rec(0, G_ACTION_MUTEOFF, 400, 1, 0.5f);
+ recorder::rec(0, G_ACTION_KEYPRESS, 100, 1, 0.5f);
+ recorder::rec(0, G_ACTION_KEYREL, 400, 1, 0.5f);
/* Overdub part of the leftmost part of a composite action. Expected result:
a new composite action.
Original: ----|########|
Overdub: |#######|-----
Result: |#######|----- */
- recorder::startOverdub(0, G_ACTION_MUTEON | G_ACTION_MUTEOFF, 0, 16);
+ recorder::startOverdub(0, G_ACTION_KEYPRESS | G_ACTION_KEYREL, 0, 16);
recorder::stopOverdub(300, 500, &mutex);
REQUIRE(recorder::frames.size() == 2);
REQUIRE(recorder::frames.at(1) == 300);
REQUIRE(recorder::global.at(0).at(0)->frame == 0);
- REQUIRE(recorder::global.at(0).at(0)->type == G_ACTION_MUTEON);
+ REQUIRE(recorder::global.at(0).at(0)->type == G_ACTION_KEYPRESS);
REQUIRE(recorder::global.at(1).at(0)->frame == 300);
- REQUIRE(recorder::global.at(1).at(0)->type == G_ACTION_MUTEOFF);
+ REQUIRE(recorder::global.at(1).at(0)->type == G_ACTION_KEYREL);
}
SECTION("Test overdub, right overlap")
{
- recorder::rec(0, G_ACTION_MUTEON, 000, 1, 0.5f);
- recorder::rec(0, G_ACTION_MUTEOFF, 400, 1, 0.5f);
+ recorder::rec(0, G_ACTION_KEYPRESS, 000, 1, 0.5f);
+ recorder::rec(0, G_ACTION_KEYREL, 400, 1, 0.5f);
/* Overdub part of the rightmost part of a composite action. Expected result:
a new composite action.
Original: |########|------
Overdub: -----|#######|--
Result: |###||#######|-- */
- recorder::startOverdub(0, G_ACTION_MUTEON | G_ACTION_MUTEOFF, 100, 16);
+ recorder::startOverdub(0, G_ACTION_KEYPRESS | G_ACTION_KEYREL, 100, 16);
recorder::stopOverdub(500, 500, &mutex);
REQUIRE(recorder::frames.size() == 4);
REQUIRE(recorder::frames.at(3) == 500);
REQUIRE(recorder::global.at(0).at(0)->frame == 0);
- REQUIRE(recorder::global.at(0).at(0)->type == G_ACTION_MUTEON);
+ REQUIRE(recorder::global.at(0).at(0)->type == G_ACTION_KEYPRESS);
REQUIRE(recorder::global.at(1).at(0)->frame == 84);
- REQUIRE(recorder::global.at(1).at(0)->type == G_ACTION_MUTEOFF);
+ REQUIRE(recorder::global.at(1).at(0)->type == G_ACTION_KEYREL);
REQUIRE(recorder::global.at(2).at(0)->frame == 100);
- REQUIRE(recorder::global.at(2).at(0)->type == G_ACTION_MUTEON);
+ REQUIRE(recorder::global.at(2).at(0)->type == G_ACTION_KEYPRESS);
REQUIRE(recorder::global.at(3).at(0)->frame == 500);
- REQUIRE(recorder::global.at(3).at(0)->type == G_ACTION_MUTEOFF);
+ REQUIRE(recorder::global.at(3).at(0)->type == G_ACTION_KEYREL);
}
SECTION("Test overdub, hole diggin'")
{
- recorder::rec(0, G_ACTION_MUTEON, 0, 1, 0.5f);
- recorder::rec(0, G_ACTION_MUTEOFF, 400, 1, 0.5f);
+ recorder::rec(0, G_ACTION_KEYPRESS, 0, 1, 0.5f);
+ recorder::rec(0, G_ACTION_KEYREL, 400, 1, 0.5f);
/* Overdub in the middle of a long, composite action. Expected result:
original action trimmed down plus anther action next to it. Total frames
Original: |#############|
Overdub: ---|#######|---
Result: |#||#######|--- */
- recorder::startOverdub(0, G_ACTION_MUTEON | G_ACTION_MUTEOFF, 100, 16);
+ recorder::startOverdub(0, G_ACTION_KEYPRESS | G_ACTION_KEYREL, 100, 16);
recorder::stopOverdub(300, 500, &mutex);
REQUIRE(recorder::frames.size() == 4);
REQUIRE(recorder::frames.at(3) == 300);
REQUIRE(recorder::global.at(0).at(0)->frame == 0);
- REQUIRE(recorder::global.at(0).at(0)->type == G_ACTION_MUTEON);
+ REQUIRE(recorder::global.at(0).at(0)->type == G_ACTION_KEYPRESS);
REQUIRE(recorder::global.at(1).at(0)->frame == 84);
- REQUIRE(recorder::global.at(1).at(0)->type == G_ACTION_MUTEOFF);
+ REQUIRE(recorder::global.at(1).at(0)->type == G_ACTION_KEYREL);
REQUIRE(recorder::global.at(2).at(0)->frame == 100);
- REQUIRE(recorder::global.at(2).at(0)->type == G_ACTION_MUTEON);
+ REQUIRE(recorder::global.at(2).at(0)->type == G_ACTION_KEYPRESS);
REQUIRE(recorder::global.at(3).at(0)->frame == 300);
- REQUIRE(recorder::global.at(3).at(0)->type == G_ACTION_MUTEOFF);
+ REQUIRE(recorder::global.at(3).at(0)->type == G_ACTION_KEYREL);
}
SECTION("Test overdub, cover all")
{
- recorder::rec(0, G_ACTION_MUTEON, 0, 1, 0.5f);
- recorder::rec(0, G_ACTION_MUTEOFF, 100, 1, 0.5f);
- recorder::rec(0, G_ACTION_MUTEON, 120, 1, 0.5f);
- recorder::rec(0, G_ACTION_MUTEOFF, 200, 1, 0.5f);
- recorder::rec(0, G_ACTION_MUTEON, 220, 1, 0.5f);
- recorder::rec(0, G_ACTION_MUTEOFF, 300, 1, 0.5f);
+ recorder::rec(0, G_ACTION_KEYPRESS, 0, 1, 0.5f);
+ recorder::rec(0, G_ACTION_KEYREL, 100, 1, 0.5f);
+ recorder::rec(0, G_ACTION_KEYPRESS, 120, 1, 0.5f);
+ recorder::rec(0, G_ACTION_KEYREL, 200, 1, 0.5f);
+ recorder::rec(0, G_ACTION_KEYPRESS, 220, 1, 0.5f);
+ recorder::rec(0, G_ACTION_KEYREL, 300, 1, 0.5f);
/* Overdub all existing actions. Expected result: a single composite one. */
- recorder::startOverdub(0, G_ACTION_MUTEON | G_ACTION_MUTEOFF, 0, 16);
+ recorder::startOverdub(0, G_ACTION_KEYPRESS | G_ACTION_KEYREL, 0, 16);
recorder::stopOverdub(500, 500, &mutex);
REQUIRE(recorder::frames.size() == 2);
REQUIRE(recorder::frames.at(1) == 500);
REQUIRE(recorder::global.at(0).at(0)->frame == 0);
- REQUIRE(recorder::global.at(0).at(0)->type == G_ACTION_MUTEON);
+ REQUIRE(recorder::global.at(0).at(0)->type == G_ACTION_KEYPRESS);
REQUIRE(recorder::global.at(1).at(0)->frame == 500);
- REQUIRE(recorder::global.at(1).at(0)->type == G_ACTION_MUTEOFF);
+ REQUIRE(recorder::global.at(1).at(0)->type == G_ACTION_KEYREL);
}
SECTION("Test overdub, null loop")
{
- recorder::rec(0, G_ACTION_MUTEON, 0, 1, 0.5f);
- recorder::rec(0, G_ACTION_MUTEOFF, 500, 1, 0.5f);
+ recorder::rec(0, G_ACTION_KEYPRESS, 0, 1, 0.5f);
+ recorder::rec(0, G_ACTION_KEYREL, 500, 1, 0.5f);
/* A null loop is a loop that begins and ends on the very same frame. */
- recorder::startOverdub(0, G_ACTION_MUTEON | G_ACTION_MUTEOFF, 300, 16);
+ recorder::startOverdub(0, G_ACTION_KEYPRESS | G_ACTION_KEYREL, 300, 16);
recorder::stopOverdub(300, 700, &mutex);
REQUIRE(recorder::frames.size() == 2);
REQUIRE(recorder::frames.at(1) == 284); // 300 - bufferSize (16)
REQUIRE(recorder::global.at(0).at(0)->frame == 0);
- REQUIRE(recorder::global.at(0).at(0)->type == G_ACTION_MUTEON);
+ REQUIRE(recorder::global.at(0).at(0)->type == G_ACTION_KEYPRESS);
REQUIRE(recorder::global.at(1).at(0)->frame == 284);
- REQUIRE(recorder::global.at(1).at(0)->type == G_ACTION_MUTEOFF);
+ REQUIRE(recorder::global.at(1).at(0)->type == G_ACTION_KEYREL);
}
SECTION("Test overdub, ring loop")
Overdub: #####|------|##
Result: ---|#######||#| */
- recorder::rec(0, G_ACTION_MUTEON, 200, 1, 0.5f);
- recorder::rec(0, G_ACTION_MUTEOFF, 300, 1, 0.5f);
+ recorder::rec(0, G_ACTION_KEYPRESS, 200, 1, 0.5f);
+ recorder::rec(0, G_ACTION_KEYREL, 300, 1, 0.5f);
- recorder::startOverdub(0, G_ACTION_MUTEON | G_ACTION_MUTEOFF, 400, 16);
+ recorder::startOverdub(0, G_ACTION_KEYPRESS | G_ACTION_KEYREL, 400, 16);
recorder::stopOverdub(250, 700, &mutex);
REQUIRE(recorder::frames.size() == 4);