From: IOhannes m zmölnig Date: Sat, 8 Sep 2018 16:20:45 +0000 (+0200) Subject: New upstream version 0.15.2+ds1 X-Git-Tag: archive/raspbian/0.15.4+ds1-1+rpi1^2~12^2~2 X-Git-Url: https://dgit.raspbian.org/?a=commitdiff_plain;h=4f1e9c5241972249d756def6cdb5560293d7b489;p=giada.git New upstream version 0.15.2+ds1 --- diff --git a/ChangeLog b/ChangeLog index bc3ffe3..01eb682 100644 --- a/ChangeLog +++ b/ChangeLog @@ -12,6 +12,20 @@ -------------------------------------------------------------------------------- +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 diff --git a/Makefile.am b/Makefile.am index 2ce25d6..e101bce 100644 --- a/Makefile.am +++ b/Makefile.am @@ -14,6 +14,7 @@ sourcesMain = src/main.cpp 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 \ @@ -112,18 +113,22 @@ sourcesCore = \ 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 \ @@ -166,24 +171,24 @@ sourcesCore = \ 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 \ diff --git a/README.md b/README.md index 1fa51c3..a5af7a7 100644 --- a/README.md +++ b/README.md @@ -39,23 +39,19 @@ Take a look at the COPYING file for further informations. ## 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 diff --git a/src/core/channel.cpp b/src/core/channel.cpp index 741e1eb..6185d74 100644 --- a/src/core/channel.cpp +++ b/src/core/channel.cpp @@ -65,7 +65,6 @@ Channel::Channel(ChannelType type, ChannelStatus status, int bufferSize) solo (false), volume_i (1.0f), volume_d (0.0f), - mute_i (false), hasActions (false), readActions (false), midiIn (true), @@ -98,7 +97,6 @@ void Channel::copy(const Channel* src, pthread_mutex_t* pluginMutex) 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; diff --git a/src/core/channel.h b/src/core/channel.h index 904bdb4..a827d54 100644 --- a/src/core/channel.h +++ b/src/core/channel.h @@ -44,9 +44,6 @@ #endif -#undef Status - - class Plugin; class MidiMapConf; class geChannel; @@ -94,7 +91,7 @@ public: /* 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). */ @@ -123,9 +120,8 @@ public: 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 @@ -227,8 +223,6 @@ public: float volume_i; float volume_d; - - bool mute_i; // internal mute bool hasActions; // has something recorded bool readActions; // read what's recorded diff --git a/src/core/conf.cpp b/src/core/conf.cpp index b24b06d..1185eda 100644 --- a/src/core/conf.cpp +++ b/src/core/conf.cpp @@ -76,6 +76,9 @@ void sanitize() 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; @@ -162,7 +165,6 @@ int rsmpQuality = 0; 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; @@ -226,6 +228,10 @@ int midiInputH = G_DEFAULT_MIDI_INPUT_UI_H; int pianoRollY = -1; int pianoRollH = 422; +int sampleActionEditorH = 40; +int velocityEditorH = 40; +int envelopeEditorH = 40; + int pluginListX = 0; int pluginListY = 0; @@ -321,7 +327,6 @@ int read() 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; @@ -371,6 +376,9 @@ int read() 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; @@ -431,7 +439,6 @@ int write() 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)); @@ -481,6 +488,9 @@ int write() 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)); diff --git a/src/core/conf.h b/src/core/conf.h index b881d6d..7b9a83b 100644 --- a/src/core/conf.h +++ b/src/core/conf.h @@ -63,7 +63,6 @@ extern int rsmpQuality; 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 @@ -107,6 +106,9 @@ extern int sampleEditorGridOn; 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; diff --git a/src/core/const.h b/src/core/const.h index 6d2e6ba..2e244b8 100644 --- a/src/core/const.h +++ b/src/core/const.h @@ -46,10 +46,10 @@ /* -- 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" @@ -110,6 +110,8 @@ #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 @@ -164,7 +166,8 @@ #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 @@ -172,13 +175,10 @@ #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) @@ -280,7 +280,7 @@ it drives knobs, volume, faders and such. */ #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, @@ -384,7 +384,6 @@ const int MIDI_CHANS[16] = { #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" @@ -434,6 +433,9 @@ const int MIDI_CHANS[16] = { #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" diff --git a/src/core/graphics.cpp b/src/core/graphics.cpp index c6be10f..b73474b 100644 --- a/src/core/graphics.cpp +++ b/src/core/graphics.cpp @@ -28,101 +28,109 @@ #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", @@ -152,7 +160,7 @@ const char *loopRepeat_xpm[] = { ".................."}; -const char *loopBasic_xpm[] = { +const char* loopBasic_xpm[] = { "18 18 8 1", " c #181917", ". c #242523", @@ -182,7 +190,7 @@ const char *loopBasic_xpm[] = { ".................."}; -const char *loopOnce_xpm[] = { +const char* loopOnce_xpm[] = { "18 18 8 1", " c #181917", ". c #242523", @@ -212,7 +220,7 @@ const char *loopOnce_xpm[] = { ".................."}; -const char *loopOnceBar_xpm[] = { +const char* loopOnceBar_xpm[] = { "18 18 8 1", " c #242523", ". c #393A38", @@ -242,7 +250,7 @@ const char *loopOnceBar_xpm[] = { " "}; -const char *oneshotBasic_xpm[] = { +const char* oneshotBasic_xpm[] = { "18 18 8 1", " c #181917", ". c #242523", @@ -272,7 +280,7 @@ const char *oneshotBasic_xpm[] = { ".................."}; -const char *oneshotRetrig_xpm[] = { +const char* oneshotRetrig_xpm[] = { "18 18 8 1", " c #181917", ". c #242523", @@ -302,7 +310,7 @@ const char *oneshotRetrig_xpm[] = { ".................."}; -const char *oneshotPress_xpm[] = { +const char* oneshotPress_xpm[] = { "18 18 8 1", " c #181917", ". c #242523", @@ -332,7 +340,7 @@ const char *oneshotPress_xpm[] = { ".................."}; -const char *oneshotEndless_xpm[] = { +const char* oneshotEndless_xpm[] = { "18 18 6 1", " c #242523", ". c #464745", @@ -360,7 +368,7 @@ const char *oneshotEndless_xpm[] = { " "}; -const char *updirOff_xpm[] = { +const char* updirOff_xpm[] = { "18 18 8 1", " c #242523", ". c #332F2E", @@ -390,7 +398,7 @@ const char *updirOff_xpm[] = { " "}; -const char *updirOn_xpm[] = { +const char* updirOn_xpm[] = { "18 18 8 1", " c #4D4F4C", ". c #555150", @@ -420,7 +428,7 @@ const char *updirOn_xpm[] = { " "}; -const char *pause_xpm[] = { +const char* pause_xpm[] = { "23 23 8 1", " c #4D4F4C", ". c #514E53", @@ -455,7 +463,7 @@ const char *pause_xpm[] = { " "}; -const char *play_xpm[] = { +const char* play_xpm[] = { "23 23 8 1", " c #242523", ". c #393534", @@ -490,7 +498,7 @@ const char *play_xpm[] = { " "}; -const char *rewindOff_xpm[] = { +const char* rewindOff_xpm[] = { "23 23 8 1", " c #242523", ". c #393534", @@ -525,7 +533,7 @@ const char *rewindOff_xpm[] = { " "}; -const char *rewindOn_xpm[] = { +const char* rewindOn_xpm[] = { "23 23 8 1", " c #4D4F4C", ". c #514E53", @@ -560,96 +568,84 @@ const char *rewindOn_xpm[] = { " "}; -// 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", @@ -683,7 +679,7 @@ const char *recOff_xpm[] = { " ", " "}; -const char *recOn_xpm[] = { +const char* recOn_xpm[] = { "23 23 8 1", " c #4D4F4C", ". c #5F4E50", @@ -717,7 +713,7 @@ const char *recOn_xpm[] = { " ", " "}; -const char *inputRecOn_xpm[] = { +const char* inputRecOn_xpm[] = { "23 23 8 1", " c #524D4C", ". c #4D4F4C", @@ -751,7 +747,7 @@ const char *inputRecOn_xpm[] = { ".......................", "......................."}; -const char *inputRecOff_xpm[] = { +const char* inputRecOff_xpm[] = { "23 23 8 1", " c #242523", ". c #252724", @@ -785,7 +781,7 @@ const char *inputRecOff_xpm[] = { " ", " "}; -const char *inputToOutputOn_xpm[] = { +const char* inputToOutputOn_xpm[] = { "10 10 8 1", " c #4D4F4C", ". c #585A57", @@ -806,7 +802,7 @@ const char *inputToOutputOn_xpm[] = { " ", " "}; -const char *inputToOutputOff_xpm[] = { +const char* inputToOutputOff_xpm[] = { "10 10 8 1", " c #242523", ". c #2E302D", @@ -827,7 +823,7 @@ const char *inputToOutputOff_xpm[] = { " ", " "}; -const char *muteOff_xpm[] = { +const char* muteOff_xpm[] = { "18 18 8 1", " c #242523", ". c #2E2F2D", @@ -856,7 +852,7 @@ const char *muteOff_xpm[] = { " ", " "}; -const char *muteOn_xpm[] = { +const char* muteOn_xpm[] = { "18 18 8 1", " c #4D4F4C", ". c #585A57", @@ -886,7 +882,7 @@ const char *muteOn_xpm[] = { " "}; -const char *readActionOff_xpm[] = { +const char* readActionOff_xpm[] = { "18 18 8 1", " c #242523", ". c #393B38", @@ -916,7 +912,7 @@ const char *readActionOff_xpm[] = { " "}; -const char *readActionOn_xpm[] = { +const char* readActionOn_xpm[] = { "18 18 8 1", " c #4D4F4C", ". c #696B68", @@ -946,7 +942,7 @@ const char *readActionOn_xpm[] = { " "}; -const char *metronomeOff_xpm[] = { +const char* metronomeOff_xpm[] = { "13 13 8 1", " c #242523", ". c #2D2928", @@ -971,7 +967,7 @@ const char *metronomeOff_xpm[] = { " "}; -const char *metronomeOn_xpm[] = { +const char* metronomeOn_xpm[] = { "13 13 8 1", " c #4D4F4C", ". c #565150", @@ -996,7 +992,7 @@ const char *metronomeOn_xpm[] = { " "}; -const char *zoomInOff_xpm[] = { +const char* zoomInOff_xpm[] = { "18 18 8 1", " c None", ". c #252525", @@ -1026,7 +1022,7 @@ const char *zoomInOff_xpm[] = { "++++++++++++++++++"}; -const char *zoomInOn_xpm[] = { +const char* zoomInOn_xpm[] = { "18 18 8 1", " c None", ". c #4E4E4E", @@ -1056,7 +1052,7 @@ const char *zoomInOn_xpm[] = { ".................."}; -const char *zoomOutOff_xpm[] = { +const char* zoomOutOff_xpm[] = { "18 18 5 1", " c None", ". c #252525", @@ -1083,7 +1079,7 @@ const char *zoomOutOff_xpm[] = { "++++++++++++++++++"}; -const char *zoomOutOn_xpm[] = { +const char* zoomOutOn_xpm[] = { "18 18 4 1", " c None", ". c #4E4E4E", @@ -1110,7 +1106,7 @@ const char *zoomOutOn_xpm[] = { -const char *scrollRightOff_xpm[] = { +const char* scrollRightOff_xpm[] = { "12 12 8 1", " c #181917", ". c #242523", @@ -1134,7 +1130,7 @@ const char *scrollRightOff_xpm[] = { "............"}; -const char *scrollLeftOff_xpm[] = { +const char* scrollLeftOff_xpm[] = { "12 12 8 1", " c #181917", ". c #242523", @@ -1158,7 +1154,7 @@ const char *scrollLeftOff_xpm[] = { "............"}; -const char *scrollLeftOn_xpm[] = { +const char* scrollLeftOn_xpm[] = { "12 12 8 1", " c #4D4F4C", ". c #6B6D6A", @@ -1182,7 +1178,7 @@ const char *scrollLeftOn_xpm[] = { " "}; -const char *scrollRightOn_xpm[] = { +const char* scrollRightOn_xpm[] = { "12 12 8 1", " c #4D4F4C", ". c #6B6D6A", @@ -1206,7 +1202,7 @@ const char *scrollRightOn_xpm[] = { " "}; -const char *soloOn_xpm[] = { +const char* soloOn_xpm[] = { "18 18 8 1", " c #4D4F4C", ". c #616360", @@ -1236,7 +1232,7 @@ const char *soloOn_xpm[] = { " "}; -const char *soloOff_xpm[] = { +const char* soloOff_xpm[] = { "18 18 8 1", " c #242523", ". c #3D3F3D", @@ -1269,7 +1265,7 @@ const char *soloOff_xpm[] = { #ifdef WITH_VST -const char *fxOff_xpm[] = { +const char* fxOff_xpm[] = { "18 18 8 1", " c #242523", ". c #40423F", @@ -1299,7 +1295,7 @@ const char *fxOff_xpm[] = { " "}; -const char *fxOn_xpm[] = { +const char* fxOn_xpm[] = { "18 18 8 1", " c #4D4F4C", ". c #565855", @@ -1329,7 +1325,7 @@ const char *fxOn_xpm[] = { " "}; -const char *fxShiftUpOff_xpm[] = { +const char* fxShiftUpOff_xpm[] = { "18 18 7 1", " c #242523", ". c #4D4F4C", @@ -1358,7 +1354,7 @@ const char *fxShiftUpOff_xpm[] = { " "}; -const char *fxShiftUpOn_xpm[] = { +const char* fxShiftUpOn_xpm[] = { "18 18 5 1", " c #4D4F4C", ". c #70726F", @@ -1385,7 +1381,7 @@ const char *fxShiftUpOn_xpm[] = { " "}; -const char *fxShiftDownOff_xpm[] = { +const char* fxShiftDownOff_xpm[] = { "18 18 7 1", " c #242523", ". c #4D4F4C", @@ -1414,7 +1410,7 @@ const char *fxShiftDownOff_xpm[] = { " "}; -const char *fxShiftDownOn_xpm[] = { +const char* fxShiftDownOn_xpm[] = { "18 18 5 1", " c #4D4F4C", ". c #70726F", @@ -1441,7 +1437,7 @@ const char *fxShiftDownOn_xpm[] = { " "}; -const char *vstLogo_xpm[] = { +const char* vstLogo_xpm[] = { "65 38 8 1", " c #161715", ". c #2B2D2A", @@ -1491,7 +1487,7 @@ const char *vstLogo_xpm[] = { " .......... .@@@@@@@@ "}; -const char *fxRemoveOff_xpm[] = { +const char* fxRemoveOff_xpm[] = { "18 18 9 1", " c None", ". c #242623", @@ -1522,7 +1518,7 @@ const char *fxRemoveOff_xpm[] = { ".................."}; -const char *fxRemoveOn_xpm[] = { +const char* fxRemoveOn_xpm[] = { "18 18 9 1", " c None", ". c #4D4F4C", @@ -1554,7 +1550,7 @@ const char *fxRemoveOn_xpm[] = { #endif // #ifdef WITH_VST -const char *divideOn_xpm[] = { +const char* divideOn_xpm[] = { "18 18 7 1", " c #5A5A5A", ". c #696969", @@ -1583,7 +1579,7 @@ const char *divideOn_xpm[] = { " "}; -const char *divideOff_xpm[] = { +const char* divideOff_xpm[] = { "18 18 8 1", " c #252525", ". c #3B3B3B", @@ -1613,7 +1609,7 @@ const char *divideOff_xpm[] = { " "}; -const char *multiplyOn_xpm[] = { +const char* multiplyOn_xpm[] = { "18 18 8 1", " c #595B58", ". c #737572", @@ -1643,7 +1639,7 @@ const char *multiplyOn_xpm[] = { " "}; -const char *multiplyOff_xpm[] = { +const char* multiplyOff_xpm[] = { "18 18 8 1", " c #242523", ". c #4A4C49", @@ -1673,7 +1669,7 @@ const char *multiplyOff_xpm[] = { " "}; -const char *channelStop_xpm[] = { +const char* channelStop_xpm[] = { "18 18 8 1", " c #242523", ". c #312D2C", @@ -1704,7 +1700,7 @@ const char *channelStop_xpm[] = { -const char *channelPlay_xpm[] = { +const char* channelPlay_xpm[] = { "18 18 8 1", " c #4D4F4C", ". c #554E56", @@ -1734,7 +1730,7 @@ const char *channelPlay_xpm[] = { " "}; -const char *armOff_xpm[] = { +const char* armOff_xpm[] = { "18 18 8 1", " c #242523", ". c #4F4445", @@ -1764,7 +1760,7 @@ const char *armOff_xpm[] = { " "}; -const char *armOn_xpm[] = { +const char* armOn_xpm[] = { "18 18 8 1", " c #4D4F4C", ". c #6B5077", diff --git a/src/core/graphics.h b/src/core/graphics.h index a09cdb7..5fae68b 100644 --- a/src/core/graphics.h +++ b/src/core/graphics.h @@ -29,83 +29,83 @@ #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 diff --git a/src/core/midiChannel.cpp b/src/core/midiChannel.cpp index 4c83b02..3dd4867 100644 --- a/src/core/midiChannel.cpp +++ b/src/core/midiChannel.cpp @@ -121,7 +121,7 @@ void MidiChannel::rewindBySeq() /* -------------------------------------------------------------------------- */ -void MidiChannel::setMute(bool value, EventType eventType) +void MidiChannel::setMute(bool value) { midiChannelProc::setMute(this, value); } @@ -221,7 +221,7 @@ void MidiChannel::receiveMidi(const MidiEvent& midiEvent) 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; diff --git a/src/core/midiChannel.h b/src/core/midiChannel.h index 08f2729..7185397 100644 --- a/src/core/midiChannel.h +++ b/src/core/midiChannel.h @@ -55,7 +55,7 @@ public: 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; diff --git a/src/core/midiDispatcher.cpp b/src/core/midiDispatcher.cpp index e5d93ea..201a385 100644 --- a/src/core/midiDispatcher.cpp +++ b/src/core/midiDispatcher.cpp @@ -66,10 +66,7 @@ void* cb_data = nullptr; 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 @@ -99,10 +96,7 @@ void processPlugins(Channel* ch, const MidiEvent& midiEvent) 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) { @@ -158,11 +152,13 @@ void processChannels(const MidiEvent& midiEvent) } #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()); } } @@ -173,10 +169,7 @@ void processChannels(const MidiEvent& midiEvent) 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); @@ -251,22 +244,24 @@ void stopMidiLearn() 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); diff --git a/src/core/midiEvent.cpp b/src/core/midiEvent.cpp index ae11319..ac3aabc 100644 --- a/src/core/midiEvent.cpp +++ b/src/core/midiEvent.cpp @@ -25,6 +25,7 @@ * -------------------------------------------------------------------------- */ +#include #include "midiEvent.h" @@ -32,8 +33,7 @@ namespace giada { namespace m { MidiEvent::MidiEvent() - : m_raw (0x0), - m_status (0), + : m_status (0), m_channel (0), m_note (0), m_velocity(0), @@ -46,8 +46,7 @@ MidiEvent::MidiEvent() 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), @@ -58,6 +57,7 @@ MidiEvent::MidiEvent(uint32_t raw) /* -------------------------------------------------------------------------- */ + MidiEvent::MidiEvent(int byte1, int byte2, int byte3) : MidiEvent((byte1 << 24) | (byte2 << 16) | (byte3 << 8) | (0x00)) { @@ -78,10 +78,28 @@ void MidiEvent::resetDelta() 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; +} + + /* -------------------------------------------------------------------------- */ @@ -121,11 +139,18 @@ int MidiEvent::getDelta() const } -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); } diff --git a/src/core/midiEvent.h b/src/core/midiEvent.h index fb31369..4c29e52 100644 --- a/src/core/midiEvent.h +++ b/src/core/midiEvent.h @@ -39,7 +39,7 @@ class MidiEvent { public: - static const int NOTE_ON = 0x90; + static const int NOTE_ON = 0x90; static const int NOTE_OFF = 0x80; MidiEvent(); @@ -53,18 +53,28 @@ public: 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; diff --git a/src/core/mixer.cpp b/src/core/mixer.cpp index 1931ed7..05d64e0 100644 --- a/src/core/mixer.cpp +++ b/src/core/mixer.cpp @@ -50,8 +50,7 @@ namespace mixer { namespace { -#define TICKSIZE 38 - +constexpr Frame TICKSIZE = 38; float tock[TICKSIZE] = { 0.059033, 0.117240, 0.173807, 0.227943, 0.278890, 0.325936, @@ -63,7 +62,6 @@ float tock[TICKSIZE] = { -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, @@ -74,24 +72,25 @@ float tick[TICKSIZE] = { 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 peak) @@ -101,7 +100,6 @@ void computePeak(const AudioBuffer& buf, float& peak, unsigned frame) /* -------------------------------------------------------------------------- */ - /* lineInRec Records from line in. */ @@ -262,23 +260,15 @@ void finalizeOutput(AudioBuffer& outBuf, unsigned frame) /* -------------------------------------------------------------------------- */ -/* 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} @@ -309,7 +299,7 @@ pthread_mutex_t mutex; /* -------------------------------------------------------------------------- */ -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. */ @@ -331,7 +321,7 @@ void init(int framesInSeq, int framesInBuffer) /* -------------------------------------------------------------------------- */ -void allocVirtualInput(int frames) +void allocVirtualInput(Frame frames) { vChanInput.alloc(frames, G_MAX_IO_CHANS); } @@ -380,8 +370,7 @@ int masterPlay(void* outBuf, void* inBuf, unsigned bufferSize, lineInRec(in, j); // TODO - can go outside this loop doQuantize(j); - testBar(j); - testLastBeat(); + renderMetronome(); clock::incrCurrentFrame(); clock::sendMIDIsync(); } diff --git a/src/core/mixer.h b/src/core/mixer.h index 4f1f341..3a1f81e 100644 --- a/src/core/mixer.h +++ b/src/core/mixer.h @@ -32,6 +32,7 @@ #include #include #include "recorder.h" +#include "types.h" #include "../deps/rtaudio-mod/RtAudio.h" @@ -44,8 +45,8 @@ namespace mixer { struct FrameEvents { - int frameLocal; - int frameGlobal; + Frame frameLocal; + Frame frameGlobal; bool doQuantize; bool onBar; bool onFirstBeat; @@ -53,17 +54,6 @@ struct FrameEvents std::vector 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 channels; extern bool recording; // is recording something? @@ -85,13 +75,13 @@ extern bool inToOut; 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(); diff --git a/src/core/plugin.cpp b/src/core/plugin.cpp index afcb4c9..b3c4e80 100644 --- a/src/core/plugin.cpp +++ b/src/core/plugin.cpp @@ -28,6 +28,7 @@ #ifdef WITH_VST +#include #include #include "../utils/log.h" #include "../utils/time.h" @@ -52,10 +53,13 @@ Plugin::Plugin(juce::AudioPluginInstance* plugin, double samplerate, 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; igetNumParameters(); i++) + const OwnedArray& params = plugin->getParameters(); + for (int i=0; iprepareToPlay(samplerate, buffersize); @@ -86,14 +90,6 @@ void Plugin::showEditor(void* parent) 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); } @@ -113,7 +109,6 @@ bool Plugin::isEditorOpen() const string Plugin::getUniqueId() const { - //return plugin->getPluginDescription().fileOrIdentifier.toStdString(); return plugin->getPluginDescription().createIdentifierString().toStdString(); } @@ -123,7 +118,7 @@ string Plugin::getUniqueId() const int Plugin::getNumParameters() const { - return plugin->getNumParameters(); + return plugin->getParameters().size(); } @@ -132,7 +127,7 @@ int Plugin::getNumParameters() const float Plugin::getParameter(int paramIndex) const { - return plugin->getParameter(paramIndex); + return plugin->getParameters()[paramIndex]->getValue(); } @@ -141,7 +136,7 @@ float Plugin::getParameter(int paramIndex) const void Plugin::setParameter(int paramIndex, float value) const { - return plugin->setParameter(paramIndex, value); + plugin->getParameters()[paramIndex]->setValue(value); } @@ -198,8 +193,8 @@ int Plugin::getId() const { return id; } /* -------------------------------------------------------------------------- */ -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(); } /* -------------------------------------------------------------------------- */ @@ -261,7 +256,7 @@ string Plugin::getProgramName(int index) const string Plugin::getParameterName(int index) const { - return plugin->getParameterName(index).toStdString(); + return plugin->getParameters()[index]->getName(MAX_LABEL_SIZE).toStdString(); } @@ -270,7 +265,7 @@ string Plugin::getParameterName(int index) const string Plugin::getParameterText(int index) const { - return plugin->getParameterText(index).toStdString(); + return plugin->getParameters()[index]->getCurrentValueAsText().toStdString(); } @@ -279,7 +274,7 @@ string Plugin::getParameterText(int index) const string Plugin::getParameterLabel(int index) const { - return plugin->getParameterLabel(index).toStdString(); + return plugin->getParameters()[index]->getLabel().toStdString(); } @@ -288,8 +283,6 @@ string Plugin::getParameterLabel(int index) const void Plugin::closeEditor() { - if (ui == nullptr) - return; delete ui; ui = nullptr; } diff --git a/src/core/plugin.h b/src/core/plugin.h index 988c20a..62633c4 100644 --- a/src/core/plugin.h +++ b/src/core/plugin.h @@ -2,8 +2,6 @@ * * Giada - Your Hardcore Loopmachine * - * plugin - * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual @@ -40,6 +38,8 @@ class Plugin { private: + static const int MAX_LABEL_SIZE = 64; + static int idGenerator; juce::AudioProcessorEditor* ui; // gui diff --git a/src/core/range.h b/src/core/range.h new file mode 100644 index 0000000..296dac0 --- /dev/null +++ b/src/core/range.h @@ -0,0 +1,57 @@ +/* ----------------------------------------------------------------------------- + * + * 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 + * . + * + * -------------------------------------------------------------------------- */ + + +#ifndef G_RANGE_H +#define G_RANGE_H + + +#include + + +namespace giada +{ +template +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 diff --git a/src/core/recorder.cpp b/src/core/recorder.cpp index 7e068f4..a54524d 100644 --- a/src/core/recorder.cpp +++ b/src/core/recorder.cpp @@ -54,20 +54,20 @@ Composite cmp; /* 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} @@ -92,7 +92,7 @@ bool sortedActions = false; void init() { active = false; - sortedActions = false; + sortedActions = false; clearAll(); } @@ -103,11 +103,12 @@ void init() 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())); } @@ -154,10 +155,10 @@ void rec(int index, int type, int frame, uint32_t iValue, float fValue) for (unsigned t=0; tchan == 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; } @@ -234,7 +235,7 @@ void deleteAction(int chan, int frame, char type, bool checkValues, for (unsigned i=0; iiValue == 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) { @@ -278,7 +279,7 @@ void deleteAction(int chan, int frame, char type, bool checkValues, void deleteActions(int chan, int frame_a, int frame_b, char type, - pthread_mutex_t* mixerMutex) + pthread_mutex_t* mixerMutex) { sortActions(); vector dels; @@ -484,14 +485,15 @@ void shrink(int new_fpb) /* -------------------------------------------------------------------------- */ -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; ichan == chanIndex) - return true; + if (type == -1 || global.at(i).at(j)->type == type) + return true; } } return false; @@ -568,14 +570,8 @@ void startOverdub(int index, char actionMask, int frame, unsigned bufferSize) { /* 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; @@ -610,14 +606,14 @@ void stopOverdub(int currentFrame, int totalFrames, pthread_mutex_t* mixerMutex) 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; @@ -629,25 +625,25 @@ void stopOverdub(int currentFrame, int totalFrames, pthread_mutex_t* mixerMutex) 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); } @@ -670,6 +666,7 @@ vector getActionsOnFrame(int frame) void forEachAction(std::function f) { + for (const vector actions : recorder::global) for (const action* action : actions) f(action); diff --git a/src/core/recorder.h b/src/core/recorder.h index cdbcc2c..d62017b 100644 --- a/src/core/recorder.h +++ b/src/core/recorder.h @@ -92,9 +92,9 @@ void init(); /* 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(). */ diff --git a/src/core/sampleChannel.cpp b/src/core/sampleChannel.cpp index 0ce44f6..8139a7c 100644 --- a/src/core/sampleChannel.cpp +++ b/src/core/sampleChannel.cpp @@ -182,12 +182,6 @@ void SampleChannel::recordStop() } -void SampleChannel::recordMute() -{ - sampleChannelRec::recordMute(this); -} - - /* -------------------------------------------------------------------------- */ @@ -220,9 +214,9 @@ void SampleChannel::stopInputRec(int globalFrame) /* -------------------------------------------------------------------------- */ -void SampleChannel::setMute(bool value, EventType eventType) +void SampleChannel::setMute(bool value) { - sampleChannelProc::setMute(this, value, eventType); + sampleChannelProc::setMute(this, value); } diff --git a/src/core/sampleChannel.h b/src/core/sampleChannel.h index 87aca99..857e8e1 100644 --- a/src/core/sampleChannel.h +++ b/src/core/sampleChannel.h @@ -60,8 +60,7 @@ public: 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; diff --git a/src/core/sampleChannelProc.cpp b/src/core/sampleChannelProc.cpp index 546adf7..aa23584 100644 --- a/src/core/sampleChannelProc.cpp +++ b/src/core/sampleChannelProc.cpp @@ -27,10 +27,10 @@ #include #include "../utils/math.h" +#include "const.h" #include "pluginHost.h" #include "sampleChannel.h" #include "sampleChannelProc.h" -#include "clock.h" namespace giada { @@ -42,7 +42,6 @@ namespace 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 @@ -210,7 +209,7 @@ void processData_(SampleChannel* ch, m::AudioBuffer& out, const m::AudioBuffer& for (int i=0; icalcVolumeEnvelope(); - if (!ch->mute && !ch->mute_i) + if (!ch->mute) for (int j=0; jbuffer[i][j] * ch->volume * ch->volume_i * ch->calcPanning(j) * ch->boost; } @@ -354,9 +353,9 @@ void rewindBySeq(SampleChannel* ch) /* -------------------------------------------------------------------------- */ -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(); } @@ -369,7 +368,7 @@ void start(SampleChannel* ch, int localFrame, bool doQuantize, int velocity) /* For one-shot modes, velocity drives the internal volume. */ if (velocity != 0) { if (ch->isAnySingleMode() && ch->midiInVeloAsVol) - ch->volume_i = u::math::map(velocity, 0.0f, 127.0f, 0.0f, 1.0f); + ch->volume_i = u::math::map(velocity, 0, G_MAX_VELOCITY, 0.0, 1.0); } switch (ch->status) { diff --git a/src/core/sampleChannelProc.h b/src/core/sampleChannelProc.h index 341cbfd..0c95a69 100644 --- a/src/core/sampleChannelProc.h +++ b/src/core/sampleChannelProc.h @@ -84,7 +84,7 @@ actions from Recorder. */ void start(SampleChannel* ch, int localFrame, bool doQuantize, int velocity); -void setMute(SampleChannel* ch, bool value, EventType eventType); +void setMute(SampleChannel* ch, bool value); }}}; diff --git a/src/core/sampleChannelRec.cpp b/src/core/sampleChannelRec.cpp index ec28bb9..91db541 100644 --- a/src/core/sampleChannelRec.cpp +++ b/src/core/sampleChannelRec.cpp @@ -25,6 +25,7 @@ * -------------------------------------------------------------------------- */ +#include #include "const.h" #include "conf.h" #include "clock.h" @@ -90,8 +91,8 @@ void calcVolumeEnv_(SampleChannel* ch, int globalFrame) * 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 @@ -99,10 +100,11 @@ void calcVolumeEnv_(SampleChannel* ch, int globalFrame) * 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; } @@ -136,12 +138,6 @@ void parseAction_(SampleChannel* ch, const recorder::action* a, int localFrame, 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; @@ -204,24 +200,6 @@ void parseEvents(SampleChannel* ch, mixer::FrameEvents fe) /* -------------------------------------------------------------------------- */ -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 diff --git a/src/core/sampleChannelRec.h b/src/core/sampleChannelRec.h index 6fe5d48..6694b2e 100644 --- a/src/core/sampleChannelRec.h +++ b/src/core/sampleChannelRec.h @@ -55,8 +55,6 @@ Ends overdub mode SINGLE_PRESS channels. */ 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. */ diff --git a/src/core/types.h b/src/core/types.h index d83fd3c..4754a10 100644 --- a/src/core/types.h +++ b/src/core/types.h @@ -31,6 +31,10 @@ namespace giada { +using Pixel = int; +using Frame = int; + + enum class ChannelType : int { SAMPLE = 1, MIDI diff --git a/src/core/wave.cpp b/src/core/wave.cpp index 9512308..1276dc6 100644 --- a/src/core/wave.cpp +++ b/src/core/wave.cpp @@ -2,8 +2,6 @@ * * Giada - Your Hardcore Loopmachine * - * wave - * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual diff --git a/src/core/wave.h b/src/core/wave.h index 5ea0e71..f285bf3 100644 --- a/src/core/wave.h +++ b/src/core/wave.h @@ -2,8 +2,6 @@ * * Giada - Your Hardcore Loopmachine * - * wave - * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual diff --git a/src/ext/giada.ico b/src/ext/giada.ico index e0a90bd..06ac502 100644 Binary files a/src/ext/giada.ico and b/src/ext/giada.ico differ diff --git a/src/glue/channel.cpp b/src/glue/channel.cpp index cdfe12d..aaa805c 100644 --- a/src/glue/channel.cpp +++ b/src/glue/channel.cpp @@ -271,7 +271,7 @@ void setPanning(SampleChannel* ch, float val) 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); diff --git a/src/glue/io.cpp b/src/glue/io.cpp index d978803..767473f 100644 --- a/src/glue/io.cpp +++ b/src/glue/io.cpp @@ -61,10 +61,8 @@ namespace io 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()) diff --git a/src/glue/recorder.cpp b/src/glue/recorder.cpp index f193285..47e5b56 100644 --- a/src/glue/recorder.cpp +++ b/src/glue/recorder.cpp @@ -25,6 +25,7 @@ * -------------------------------------------------------------------------- */ +#include #include "../gui/dialogs/gd_warnings.h" #include "../gui/elems/mainWindow/keyboard/channel.h" #include "../gui/elems/mainWindow/keyboard/sampleChannel.h" @@ -33,6 +34,9 @@ #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" @@ -48,17 +52,21 @@ namespace recorder { 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(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(gch); + gsch->ch->hasActions ? gsch->showActionButton() : gsch->hideActionButton(); + } + if (refreshActionEditor) + gu_refreshActionEditor(); // refresh a.editor window, it could be open } -}; // {namespace} +}; // {anonymous} +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ @@ -98,22 +106,41 @@ void clearStartStopActions(geChannel* gch) /* -------------------------------------------------------------------------- */ -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 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 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. */ @@ -123,33 +150,207 @@ void recordMidiAction(int chan, int note, int frame_a, int frame_b) 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 getSampleActions(const SampleChannel* ch) +{ + namespace mr = m::recorder; + + vector 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 getEnvelopeActions(const Channel* ch, int type) +{ + namespace mr = m::recorder; + + vector 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 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 getMidiActions(int chan) { vector out; @@ -157,7 +358,7 @@ vector getMidiActions(int chan, int frameLimit) for (unsigned i=0; i frameLimit) + if (m::recorder::frames.at(i) > m::clock::getFramesInLoop()) continue; for (unsigned j=0; j getMidiActions(int chan, int frameLimit) 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". */ diff --git a/src/glue/recorder.h b/src/glue/recorder.h index ab417db..1bf4a40 100644 --- a/src/glue/recorder.h +++ b/src/glue/recorder.h @@ -33,6 +33,8 @@ #include "../core/recorder.h" +class SampleChannel; +class MidiChannel; class geChannel; @@ -41,22 +43,50 @@ namespace c { 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 getMidiActions(int channel, - int frameLimit); +std::vector getMidiActions(int channel); + +std::vector 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 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:: diff --git a/src/gui/dialogs/about.cpp b/src/gui/dialogs/about.cpp index 79f7996..dfe503a 100644 --- a/src/gui/dialogs/about.cpp +++ b/src/gui/dialogs/about.cpp @@ -59,7 +59,7 @@ gdAbout::gdAbout() 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 diff --git a/src/gui/dialogs/actionEditor/baseActionEditor.cpp b/src/gui/dialogs/actionEditor/baseActionEditor.cpp new file mode 100644 index 0000000..b85a30e --- /dev/null +++ b/src/gui/dialogs/actionEditor/baseActionEditor.cpp @@ -0,0 +1,212 @@ +/* ----------------------------------------------------------------------------- + * + * 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 + * . + * + * -------------------------------------------------------------------------- */ + + +#include +#include +#include +#include +#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 diff --git a/src/gui/dialogs/actionEditor/baseActionEditor.h b/src/gui/dialogs/actionEditor/baseActionEditor.h new file mode 100644 index 0000000..1b35d79 --- /dev/null +++ b/src/gui/dialogs/actionEditor/baseActionEditor.h @@ -0,0 +1,105 @@ +/* ----------------------------------------------------------------------------- + * + * 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 + * . + * + * -------------------------------------------------------------------------- */ + + +#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 diff --git a/src/gui/dialogs/actionEditor/midiActionEditor.cpp b/src/gui/dialogs/actionEditor/midiActionEditor.cpp new file mode 100644 index 0000000..ab65cef --- /dev/null +++ b/src/gui/dialogs/actionEditor/midiActionEditor.cpp @@ -0,0 +1,95 @@ +/* ----------------------------------------------------------------------------- + * + * 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 + * . + * + * -------------------------------------------------------------------------- */ + + +#include +#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:: diff --git a/src/gui/dialogs/actionEditor/midiActionEditor.h b/src/gui/dialogs/actionEditor/midiActionEditor.h new file mode 100644 index 0000000..15da033 --- /dev/null +++ b/src/gui/dialogs/actionEditor/midiActionEditor.h @@ -0,0 +1,61 @@ +/* ----------------------------------------------------------------------------- + * + * 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 + * . + * + * -------------------------------------------------------------------------- */ + + +#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 diff --git a/src/gui/dialogs/actionEditor/sampleActionEditor.cpp b/src/gui/dialogs/actionEditor/sampleActionEditor.cpp new file mode 100644 index 0000000..2f2e268 --- /dev/null +++ b/src/gui/dialogs/actionEditor/sampleActionEditor.cpp @@ -0,0 +1,118 @@ +/* ----------------------------------------------------------------------------- + * + * 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 + * . + * + * -------------------------------------------------------------------------- */ + + +#include +#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(ch); + return sch->mode != ChannelMode::SINGLE_PRESS && !sch->isAnyLoopMode(); +} + + +/* -------------------------------------------------------------------------- */ + + +void gdSampleActionEditor::rebuild() +{ + canChangeActionType() ? actionType->activate() : actionType->deactivate(); + computeWidth(); + ac->rebuild(); + vc->rebuild(); +} +}} // giada::v:: diff --git a/src/gui/dialogs/actionEditor/sampleActionEditor.h b/src/gui/dialogs/actionEditor/sampleActionEditor.h new file mode 100644 index 0000000..9832f21 --- /dev/null +++ b/src/gui/dialogs/actionEditor/sampleActionEditor.h @@ -0,0 +1,61 @@ +/* ----------------------------------------------------------------------------- + * + * 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 + * . + * + * -------------------------------------------------------------------------- */ + + +#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 diff --git a/src/gui/dialogs/gd_actionEditor.cpp b/src/gui/dialogs/gd_actionEditor.cpp deleted file mode 100644 index 499f9a3..0000000 --- a/src/gui/dialogs/gd_actionEditor.cpp +++ /dev/null @@ -1,299 +0,0 @@ -/* ----------------------------------------------------------------------------- - * - * 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 - * . - * - * -------------------------------------------------------------------------- */ - - -#include -#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(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(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; -} diff --git a/src/gui/dialogs/gd_actionEditor.h b/src/gui/dialogs/gd_actionEditor.h deleted file mode 100644 index f82a6f7..0000000 --- a/src/gui/dialogs/gd_actionEditor.h +++ /dev/null @@ -1,98 +0,0 @@ -/* ----------------------------------------------------------------------------- - * - * 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 - * . - * - * -------------------------------------------------------------------------- */ - - -#ifndef GD_ACTIONEDITOR_H -#define GD_ACTIONEDITOR_H - - -#include -#include -#include -#include -#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 diff --git a/src/gui/dialogs/pluginWindowGUI.cpp b/src/gui/dialogs/pluginWindowGUI.cpp index 35c9ec6..0e64c3a 100644 --- a/src/gui/dialogs/pluginWindowGUI.cpp +++ b/src/gui/dialogs/pluginWindowGUI.cpp @@ -45,11 +45,15 @@ using namespace giada::m; 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 @@ -58,19 +62,11 @@ gdPluginWindowGUI::gdPluginWindowGUI(Plugin* plugin) 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", @@ -79,20 +75,20 @@ gdPluginWindowGUI::gdPluginWindowGUI(Plugin* plugin) #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()); diff --git a/src/gui/elems/actionEditor/action.cpp b/src/gui/elems/actionEditor/action.cpp deleted file mode 100644 index 9a85940..0000000 --- a/src/gui/elems/actionEditor/action.cpp +++ /dev/null @@ -1,288 +0,0 @@ -/* ----------------------------------------------------------------------------- - * - * 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 - * . - * - * -------------------------------------------------------------------------- */ - - -#include -#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; -} diff --git a/src/gui/elems/actionEditor/action.h b/src/gui/elems/actionEditor/action.h deleted file mode 100644 index f681d56..0000000 --- a/src/gui/elems/actionEditor/action.h +++ /dev/null @@ -1,86 +0,0 @@ -/* ----------------------------------------------------------------------------- - * - * 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 - * . - * - * -------------------------------------------------------------------------- */ - - -#ifndef GE_ACTION_H -#define GE_ACTION_H - - -#include - - -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 diff --git a/src/gui/elems/actionEditor/actionEditor.cpp b/src/gui/elems/actionEditor/actionEditor.cpp deleted file mode 100644 index 72a23fc..0000000 --- a/src/gui/elems/actionEditor/actionEditor.cpp +++ /dev/null @@ -1,426 +0,0 @@ -/* ----------------------------------------------------------------------------- - * - * 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 - * . - * - * -------------------------------------------------------------------------- */ - - -#include -#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; ichan->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; ix(); - 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; iframe_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; ix(); - 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; iframe_a == frame) - collision = true; - - if (ch->mode == ChannelMode::SINGLE_PRESS) { - for (int i=0; iframe_b && frame >= c->frame_a) - collision = true; - } - } - - return collision; -} diff --git a/src/gui/elems/actionEditor/actionEditor.h b/src/gui/elems/actionEditor/actionEditor.h deleted file mode 100644 index 5870cca..0000000 --- a/src/gui/elems/actionEditor/actionEditor.h +++ /dev/null @@ -1,86 +0,0 @@ -/* ----------------------------------------------------------------------------- - * - * 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 - * . - * - * -------------------------------------------------------------------------- */ - - -#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 diff --git a/src/gui/elems/actionEditor/baseAction.cpp b/src/gui/elems/actionEditor/baseAction.cpp new file mode 100644 index 0000000..ba0d5f4 --- /dev/null +++ b/src/gui/elems/actionEditor/baseAction.cpp @@ -0,0 +1,133 @@ +/* ----------------------------------------------------------------------------- + * + * 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 + * . + * + * -------------------------------------------------------------------------- */ + + +#include +#include +#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 diff --git a/src/gui/elems/actionEditor/baseAction.h b/src/gui/elems/actionEditor/baseAction.h new file mode 100644 index 0000000..d8bf4fd --- /dev/null +++ b/src/gui/elems/actionEditor/baseAction.h @@ -0,0 +1,77 @@ +/* ----------------------------------------------------------------------------- + * + * 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 + * . + * + * -------------------------------------------------------------------------- */ + + +#ifndef GE_BASE_ACTION_H +#define GE_BASE_ACTION_H + + +#include +#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 diff --git a/src/gui/elems/actionEditor/baseActionEditor.cpp b/src/gui/elems/actionEditor/baseActionEditor.cpp index 0f11b31..c59b12d 100644 --- a/src/gui/elems/actionEditor/baseActionEditor.cpp +++ b/src/gui/elems/actionEditor/baseActionEditor.cpp @@ -2,9 +2,6 @@ * * Giada - Your Hardcore Loopmachine * - * geBaseActionEditor - * Parent class of any widget inside the action editor. - * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual @@ -28,72 +25,165 @@ * -------------------------------------------------------------------------- */ +#include #include -#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(window())), + m_action(nullptr) +{ +} /* -------------------------------------------------------------------------- */ -geBaseActionEditor::~geBaseActionEditor() {} +geBaseAction* geBaseActionEditor::getActionAtCursor() const +{ + for (int i=0; i(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; iframeToPixel(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 diff --git a/src/gui/elems/actionEditor/baseActionEditor.h b/src/gui/elems/actionEditor/baseActionEditor.h index d0862de..0f3d2ed 100644 --- a/src/gui/elems/actionEditor/baseActionEditor.h +++ b/src/gui/elems/actionEditor/baseActionEditor.h @@ -2,9 +2,6 @@ * * Giada - Your Hardcore Loopmachine * - * ge_actionWidget - * - * parent class of any tool inside the action editor. * * ----------------------------------------------------------------------------- * @@ -36,23 +33,71 @@ #include -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 diff --git a/src/gui/elems/actionEditor/basePianoItem.cpp b/src/gui/elems/actionEditor/basePianoItem.cpp deleted file mode 100644 index 439c91a..0000000 --- a/src/gui/elems/actionEditor/basePianoItem.cpp +++ /dev/null @@ -1,68 +0,0 @@ -/* ----------------------------------------------------------------------------- - * - * 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 - * . - * - * -------------------------------------------------------------------------- */ - - -#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); -} diff --git a/src/gui/elems/actionEditor/basePianoItem.h b/src/gui/elems/actionEditor/basePianoItem.h deleted file mode 100644 index 41d7de4..0000000 --- a/src/gui/elems/actionEditor/basePianoItem.h +++ /dev/null @@ -1,62 +0,0 @@ -/* ----------------------------------------------------------------------------- - * - * 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 - * . - * - * -------------------------------------------------------------------------- */ - - -#ifndef GE_BASE_PIANO_ITEM_H -#define GE_BASE_PIANO_ITEM_H - - -#include -#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 diff --git a/src/gui/elems/actionEditor/envelopeEditor.cpp b/src/gui/elems/actionEditor/envelopeEditor.cpp index 4e032a8..b1bba63 100644 --- a/src/gui/elems/actionEditor/envelopeEditor.cpp +++ b/src/gui/elems/actionEditor/envelopeEditor.cpp @@ -2,11 +2,6 @@ * * 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 @@ -30,387 +25,209 @@ * -------------------------------------------------------------------------- */ +#include #include -#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(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; izoom; + 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 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 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(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, 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; ia1, /*moved=*/false); + rebuild(); } -/* ------------------------------------------------------------------ */ - - -void geEnvelopeEditor::sortPoints() { - for (unsigned i=0; i 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.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; itype == 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 diff --git a/src/gui/elems/actionEditor/envelopeEditor.h b/src/gui/elems/actionEditor/envelopeEditor.h index b2218ab..0bdc8b5 100644 --- a/src/gui/elems/actionEditor/envelopeEditor.h +++ b/src/gui/elems/actionEditor/envelopeEditor.h @@ -1,11 +1,8 @@ -/* --------------------------------------------------------------------- +/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * - * parent class of any envelope controller, from volume to VST parameter - * automations. - * - * --------------------------------------------------------------------- + * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual * @@ -25,91 +22,56 @@ * along with Giada - Your Hardcore Loopmachine. If not, see * . * - * ------------------------------------------------------------------ */ + * -------------------------------------------------------------------------- */ + #ifndef GE_ENVELOPE_EDITOR_H #define GE_ENVELOPE_EDITOR_H -#include #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 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 diff --git a/src/gui/elems/actionEditor/envelopePoint.cpp b/src/gui/elems/actionEditor/envelopePoint.cpp new file mode 100644 index 0000000..a1f7e9e --- /dev/null +++ b/src/gui/elems/actionEditor/envelopePoint.cpp @@ -0,0 +1,49 @@ +/* ----------------------------------------------------------------------------- + * + * 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 + * . + * + * -------------------------------------------------------------------------- */ + + +#include +#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 diff --git a/src/gui/elems/actionEditor/envelopePoint.h b/src/gui/elems/actionEditor/envelopePoint.h new file mode 100644 index 0000000..3e040ff --- /dev/null +++ b/src/gui/elems/actionEditor/envelopePoint.h @@ -0,0 +1,52 @@ +/* ----------------------------------------------------------------------------- + * + * 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 + * . + * + * -------------------------------------------------------------------------- */ + + +#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 diff --git a/src/gui/elems/actionEditor/gridTool.cpp b/src/gui/elems/actionEditor/gridTool.cpp index 4f17dbb..52cf3f1 100644 --- a/src/gui/elems/actionEditor/gridTool.cpp +++ b/src/gui/elems/actionEditor/gridTool.cpp @@ -25,22 +25,20 @@ * --------------------------------------------------------------------------- */ -#include +#include #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"); @@ -54,10 +52,10 @@ geGridTool::geGridTool(int x, int y, gdActionEditor *parent) 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(); } @@ -68,31 +66,30 @@ geGridTool::geGridTool(int x, int y, gdActionEditor *parent) 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(); } @@ -101,7 +98,7 @@ bool geGridTool::isOn() /* -------------------------------------------------------------------------- */ -int geGridTool::getValue() +int geGridTool::getValue() const { switch (gridType->value()) { case 0: return 1; @@ -120,101 +117,19 @@ int geGridTool::getValue() /* -------------------------------------------------------------------------- */ -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; itotalWidth; 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 diff --git a/src/gui/elems/actionEditor/gridTool.h b/src/gui/elems/actionEditor/gridTool.h index 2ca3eac..f86e3d9 100644 --- a/src/gui/elems/actionEditor/gridTool.h +++ b/src/gui/elems/actionEditor/gridTool.h @@ -29,54 +29,44 @@ #define GE_GRID_TOOL_H -#include #include +#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 points; // points of the grid - std::vector frames; // frames of the grid + Frame getCellSize() const; - std::vector bars; - std::vector beats; }; +}} // giada::v:: #endif diff --git a/src/gui/elems/actionEditor/muteEditor.cpp b/src/gui/elems/actionEditor/muteEditor.cpp deleted file mode 100644 index 5ee61f2..0000000 --- a/src/gui/elems/actionEditor/muteEditor.cpp +++ /dev/null @@ -1,419 +0,0 @@ -/* ----------------------------------------------------------------------------- - * - * 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 - * . - * - * -------------------------------------------------------------------------- */ - - -#include -#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; icoverX+x()-1, py); -} - - -/* ------------------------------------------------------------------ */ - - -void geMuteEditor::extractPoints() -{ - points.clear(); - - /* actions are already sorted by recorder::sortActions() */ - - for (unsigned i=0; ichan == 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; izoom; -} - - -/* ------------------------------------------------------------------ */ - - -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; izoom; - 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.at(i).x+x()-3 && - Fl::event_x() <= points.at(i).x+x()+3) - return i; - } - return -1; -} diff --git a/src/gui/elems/actionEditor/muteEditor.h b/src/gui/elems/actionEditor/muteEditor.h deleted file mode 100644 index d70bdd8..0000000 --- a/src/gui/elems/actionEditor/muteEditor.h +++ /dev/null @@ -1,102 +0,0 @@ -/* ----------------------------------------------------------------------------- - * - * 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 - * . - * - * -------------------------------------------------------------------------- */ - - -#ifndef GE_MUTE_TOOL_H -#define GE_MUTE_TOOL_H - - -#include -#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 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 diff --git a/src/gui/elems/actionEditor/noteEditor.cpp b/src/gui/elems/actionEditor/noteEditor.cpp index b0608b2..0614b35 100644 --- a/src/gui/elems/actionEditor/noteEditor.cpp +++ b/src/gui/elems/actionEditor/noteEditor.cpp @@ -25,25 +25,29 @@ * -------------------------------------------------------------------------- */ -#include +#include #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(m_base->ch)); + + rebuild(); + + size(m_base->fullWidth, m::conf::pianoRollH); + + type(Fl_Scroll::VERTICAL_ALWAYS); } @@ -52,39 +56,35 @@ geNoteEditor::geNoteEditor(int x, int y, gdActionEditor *pParent) 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 diff --git a/src/gui/elems/actionEditor/noteEditor.h b/src/gui/elems/actionEditor/noteEditor.h index 26fbaf0..130e925 100644 --- a/src/gui/elems/actionEditor/noteEditor.h +++ b/src/gui/elems/actionEditor/noteEditor.h @@ -29,27 +29,32 @@ #define GE_NOTE_EDITOR_H -#include +#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 diff --git a/src/gui/elems/actionEditor/pianoItem.cpp b/src/gui/elems/actionEditor/pianoItem.cpp index 01e787f..5d0077a 100644 --- a/src/gui/elems/actionEditor/pianoItem.cpp +++ b/src/gui/elems/actionEditor/pianoItem.cpp @@ -25,78 +25,21 @@ * -------------------------------------------------------------------------- */ -#include "../../../core/kernelMidi.h" +#include #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(parent()); - - for (int i=0; ichildren(); i++) { - - gePianoItem* pItem = static_cast(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; } @@ -105,192 +48,29 @@ bool gePianoItem::overlap() 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(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(parent())->redraw(); - } - ret = 1; - break; - } - - case FL_DRAG: { - - changed = true; - - geNoteEditor *pr = static_cast(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(parent())->redraw(); - ret = 1; - break; - } - - case FL_RELEASE: { - - - gePianoRoll* pianoRoll = static_cast(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(v, 0, G_MAX_VELOCITY, 0, h() - 3); } +}} // giada::v:: \ No newline at end of file diff --git a/src/gui/elems/actionEditor/pianoItem.h b/src/gui/elems/actionEditor/pianoItem.h index a3a3f3a..da7c614 100644 --- a/src/gui/elems/actionEditor/pianoItem.h +++ b/src/gui/elems/actionEditor/pianoItem.h @@ -30,67 +30,31 @@ #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 diff --git a/src/gui/elems/actionEditor/pianoItemOrphaned.cpp b/src/gui/elems/actionEditor/pianoItemOrphaned.cpp deleted file mode 100644 index 261d3b7..0000000 --- a/src/gui/elems/actionEditor/pianoItemOrphaned.cpp +++ /dev/null @@ -1,102 +0,0 @@ -/* ----------------------------------------------------------------------------- - * - * 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 - * . - * - * -------------------------------------------------------------------------- */ - - -#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(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(parent())->redraw(); -} - - -/* -------------------------------------------------------------------------- */ - - -void gePianoItemOrphaned::draw() -{ - fl_rect(x(), y()+2, WIDTH, h()-3, (Fl_Color) selected ? G_COLOR_LIGHT_1 : G_COLOR_LIGHT_1); -} diff --git a/src/gui/elems/actionEditor/pianoItemOrphaned.h b/src/gui/elems/actionEditor/pianoItemOrphaned.h deleted file mode 100644 index b7d214c..0000000 --- a/src/gui/elems/actionEditor/pianoItemOrphaned.h +++ /dev/null @@ -1,63 +0,0 @@ -/* ----------------------------------------------------------------------------- - * - * 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 - * . - * - * -------------------------------------------------------------------------- */ - - -#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 diff --git a/src/gui/elems/actionEditor/pianoRoll.cpp b/src/gui/elems/actionEditor/pianoRoll.cpp index 3466852..c1b416b 100644 --- a/src/gui/elems/actionEditor/pianoRoll.cpp +++ b/src/gui/elems/actionEditor/pianoRoll.cpp @@ -25,77 +25,35 @@ * -------------------------------------------------------------------------- */ +#include +#include #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 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(); } @@ -107,8 +65,8 @@ void gePianoRoll::drawSurface1() 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); @@ -190,10 +148,12 @@ void gePianoRoll::drawSurface1() 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: @@ -209,6 +169,7 @@ void gePianoRoll::drawSurface2() fl_line(0, i*CELL_H, CELL_W, i*CELL_H); } } + fl_line_style(0); fl_end_offscreen(); } @@ -221,11 +182,11 @@ void gePianoRoll::draw() { fl_copy_offscreen(x(), y(), CELL_W, h(), surface1, 0, 0); -#if defined(__APPLE__) - for (int i=36; itotalWidth; i+=36) /// TODO: i < pParent->coverX is faster +#if defined(__APPLE__) // TODO - is this still useful? + for (Pixel i=36; ifullWidth; 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; itotalWidth; i+=CELL_W) /// TODO: i < pParent->coverX is faster + for (Pixel i=CELL_W; iloopWidth; i+=CELL_W) fl_copy_offscreen(x()+i, y(), CELL_W, h(), surface2, 0, 0); #endif @@ -239,93 +200,191 @@ void gePianoRoll::draw() 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(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(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(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(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(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(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(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(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 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 diff --git a/src/gui/elems/actionEditor/pianoRoll.h b/src/gui/elems/actionEditor/pianoRoll.h index 59cf219..1fdfbbc 100644 --- a/src/gui/elems/actionEditor/pianoRoll.h +++ b/src/gui/elems/actionEditor/pianoRoll.h @@ -33,9 +33,12 @@ #include "baseActionEditor.h" -class gdActionEditor; +class MidiChannel; +namespace giada { +namespace v +{ class gePianoRoll : public geBaseActionEditor { private: @@ -46,11 +49,15 @@ 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 @@ -62,31 +69,28 @@ private: 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 diff --git a/src/gui/elems/actionEditor/sampleAction.cpp b/src/gui/elems/actionEditor/sampleAction.cpp new file mode 100644 index 0000000..f4e7d9b --- /dev/null +++ b/src/gui/elems/actionEditor/sampleAction.cpp @@ -0,0 +1,68 @@ +/* ----------------------------------------------------------------------------- + * + * 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 + * . + * + * -------------------------------------------------------------------------- */ + + +#include +#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 diff --git a/src/gui/elems/actionEditor/sampleAction.h b/src/gui/elems/actionEditor/sampleAction.h new file mode 100644 index 0000000..47a8116 --- /dev/null +++ b/src/gui/elems/actionEditor/sampleAction.h @@ -0,0 +1,58 @@ +/* ----------------------------------------------------------------------------- + * + * 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 + * . + * + * -------------------------------------------------------------------------- */ + + +#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 diff --git a/src/gui/elems/actionEditor/sampleActionEditor.cpp b/src/gui/elems/actionEditor/sampleActionEditor.cpp new file mode 100644 index 0000000..25bdd91 --- /dev/null +++ b/src/gui/elems/actionEditor/sampleActionEditor.cpp @@ -0,0 +1,222 @@ +/* ----------------------------------------------------------------------------- + * + * 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 + * . + * + * -------------------------------------------------------------------------- */ + + +#include +#include +#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(m_ch); + + /* Remove all existing actions and set a new width, according to the current + zoom level. */ + + clear(); + size(m_base->fullWidth, h()); + + vector 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(m_ch), + m_base->getActionType(), f); + rebuild(); +} + + +/* -------------------------------------------------------------------------- */ + + +void geSampleActionEditor::onDeleteAction() +{ + c::recorder::deleteSampleAction(static_cast(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(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 diff --git a/src/gui/elems/actionEditor/sampleActionEditor.h b/src/gui/elems/actionEditor/sampleActionEditor.h new file mode 100644 index 0000000..197973f --- /dev/null +++ b/src/gui/elems/actionEditor/sampleActionEditor.h @@ -0,0 +1,65 @@ +/* ----------------------------------------------------------------------------- + * + * 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 + * . + * + * -------------------------------------------------------------------------- */ + + +#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 diff --git a/src/gui/elems/actionEditor/velocityEditor.cpp b/src/gui/elems/actionEditor/velocityEditor.cpp new file mode 100644 index 0000000..faaefeb --- /dev/null +++ b/src/gui/elems/actionEditor/velocityEditor.cpp @@ -0,0 +1,168 @@ +/* ----------------------------------------------------------------------------- + * + * 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 + * . + * + * -------------------------------------------------------------------------- */ + + +#include +#include +#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(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(v, 0, G_MAX_VELOCITY, y() + (h() - geEnvelopePoint::SIDE), y()); +} + + +int geVelocityEditor::yToValue(Pixel px) const +{ + return u::math::map(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 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 diff --git a/src/gui/elems/actionEditor/velocityEditor.h b/src/gui/elems/actionEditor/velocityEditor.h new file mode 100644 index 0000000..40120c2 --- /dev/null +++ b/src/gui/elems/actionEditor/velocityEditor.h @@ -0,0 +1,69 @@ +/* ----------------------------------------------------------------------------- + * + * 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 + * . + * + * -------------------------------------------------------------------------- */ + + +#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 diff --git a/src/gui/elems/config/tabMidi.cpp b/src/gui/elems/config/tabMidi.cpp index b35e0d0..527bfeb 100644 --- a/src/gui/elems/config/tabMidi.cpp +++ b/src/gui/elems/config/tabMidi.cpp @@ -53,8 +53,7 @@ geTabMidi::geTabMidi(int X, int Y, int W, int H) 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(); @@ -69,8 +68,6 @@ geTabMidi::geTabMidi(int X, int Y, int W, int H) fetchInPorts(); fetchMidiMaps(); - noNoteOff->value(conf::noNoteOff); - sync->add("(disabled)"); sync->add("MIDI Clock (master)"); sync->add("MTC (master)"); @@ -172,8 +169,6 @@ void geTabMidi::save() 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) @@ -221,13 +216,13 @@ void geTabMidi::fetchSystems() /* -------------------------------------------------------------------------- */ -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 @@ -240,7 +235,6 @@ void geTabMidi::__cb_changeSystem() portIn->clear(); fetchInPorts(); portIn->activate(); - noNoteOff->activate(); sync->activate(); } else { @@ -252,7 +246,6 @@ void geTabMidi::__cb_changeSystem() portIn->clear(); portIn->add("-- restart to fetch device(s) --"); portIn->value(0); - noNoteOff->deactivate(); sync->deactivate(); } diff --git a/src/gui/elems/config/tabMidi.h b/src/gui/elems/config/tabMidi.h index 8f0ee70..9b48a92 100644 --- a/src/gui/elems/config/tabMidi.h +++ b/src/gui/elems/config/tabMidi.h @@ -45,19 +45,18 @@ private: 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); diff --git a/src/gui/elems/mainWindow/keyboard/channelMode.cpp b/src/gui/elems/mainWindow/keyboard/channelMode.cpp index 44f8a08..e47efb0 100644 --- a/src/gui/elems/mainWindow/keyboard/channelMode.cpp +++ b/src/gui/elems/mainWindow/keyboard/channelMode.cpp @@ -106,9 +106,8 @@ void geChannelMode::__cb_changeMode(int mode) { ch->mode = static_cast(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(); } diff --git a/src/gui/elems/mainWindow/keyboard/midiChannel.cpp b/src/gui/elems/mainWindow/keyboard/midiChannel.cpp index fb38604..9c42a8c 100644 --- a/src/gui/elems/mainWindow/keyboard/midiChannel.cpp +++ b/src/gui/elems/mainWindow/keyboard/midiChannel.cpp @@ -36,10 +36,10 @@ #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" @@ -88,6 +88,8 @@ void menuCallback(Fl_Widget* w, void* v) using namespace giada; geMidiChannel* gch = static_cast(w); + MidiChannel* ch = static_cast(gch->ch); + Menu selectedItem = (Menu) (intptr_t) v; switch (selectedItem) @@ -98,7 +100,7 @@ void menuCallback(Fl_Widget* w, void* v) 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); @@ -110,8 +112,7 @@ void menuCallback(Fl_Widget* w, void* v) gu_openSubWindow(G_MainWin, new gdMidiInputChannel(gch->ch), 0); break; case Menu::SETUP_MIDI_OUTPUT: - gu_openSubWindow(G_MainWin, - new gdMidiOutputMidiCh(static_cast(gch->ch)), 0); + gu_openSubWindow(G_MainWin, new gdMidiOutputMidiCh(ch), 0); break; case Menu::RESIZE_H1: gch->changeSize(G_GUI_CHANNEL_H_1); diff --git a/src/gui/elems/mainWindow/keyboard/sampleChannel.cpp b/src/gui/elems/mainWindow/keyboard/sampleChannel.cpp index b97d0f8..e82446f 100644 --- a/src/gui/elems/mainWindow/keyboard/sampleChannel.cpp +++ b/src/gui/elems/mainWindow/keyboard/sampleChannel.cpp @@ -40,8 +40,8 @@ #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" @@ -78,7 +78,6 @@ enum class Menu EDIT_ACTIONS, CLEAR_ACTIONS, CLEAR_ACTIONS_ALL, - CLEAR_ACTIONS_MUTE, CLEAR_ACTIONS_VOLUME, CLEAR_ACTIONS_START_STOP, __END_CLEAR_ACTIONS_SUBMENU__, @@ -103,6 +102,8 @@ void menuCallback(Fl_Widget* w, void* v) using namespace giada; geSampleChannel* gch = static_cast(w); + SampleChannel* ch = static_cast(gch->ch); + Menu selectedItem = (Menu) (intptr_t) v; switch (selectedItem) { @@ -133,15 +134,15 @@ void menuCallback(Fl_Widget* w, void* v) break; } case Menu::SETUP_MIDI_OUTPUT: { - gu_openSubWindow(G_MainWin, new gdMidiOutputSampleCh(static_cast(gch->ch)), 0); + gu_openSubWindow(G_MainWin, new gdMidiOutputSampleCh(ch), 0); break; } case Menu::EDIT_SAMPLE: { - gu_openSubWindow(G_MainWin, new gdSampleEditor(static_cast(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: @@ -153,10 +154,6 @@ void menuCallback(Fl_Widget* w, void* v) 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; @@ -207,6 +204,8 @@ void menuCallback(Fl_Widget* w, void* v) }; // {namespace} +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ @@ -313,7 +312,6 @@ void geSampleChannel::cb_openMenu() {"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}, diff --git a/src/utils/cocoa.h b/src/utils/cocoa.h index 7e8bb19..69b9628 100644 --- a/src/utils/cocoa.h +++ b/src/utils/cocoa.h @@ -32,14 +32,15 @@ /* 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 diff --git a/src/utils/cocoa.mm b/src/utils/cocoa.mm index 69519e9..10792b1 100644 --- a/src/utils/cocoa.mm +++ b/src/utils/cocoa.mm @@ -32,15 +32,16 @@ #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 diff --git a/src/utils/gui.cpp b/src/utils/gui.cpp index 39a856e..a481d69 100644 --- a/src/utils/gui.cpp +++ b/src/utils/gui.cpp @@ -26,6 +26,7 @@ #include +#include #include #if defined(_WIN32) #include "../ext/resource.h" @@ -33,8 +34,6 @@ #include #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" @@ -42,7 +41,7 @@ #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" @@ -57,11 +56,13 @@ #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; @@ -180,21 +181,16 @@ void gu_openSubWindow(gdWindow* parent, gdWindow* child, int id) 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(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); @@ -221,19 +217,19 @@ void gu_closeAllSubwindows() /* -------------------------------------------------------------------------- */ -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, "|", "-"); diff --git a/src/utils/math.cpp b/src/utils/math.cpp index 9bb7ca0..324816b 100644 --- a/src/utils/math.cpp +++ b/src/utils/math.cpp @@ -42,6 +42,17 @@ float linearToDB(float f) /* -------------------------------------------------------------------------- */ +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); diff --git a/src/utils/math.h b/src/utils/math.h index c17d82d..16e6f9f 100644 --- a/src/utils/math.h +++ b/src/utils/math.h @@ -35,15 +35,16 @@ namespace math { 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 -T map(T x, T a, T b, T w, T z) +template +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:: diff --git a/tests/conf.cpp b/tests/conf.cpp index a51f216..c392b81 100644 --- a/tests/conf.cpp +++ b/tests/conf.cpp @@ -28,7 +28,6 @@ TEST_CASE("conf") 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; @@ -104,7 +103,6 @@ TEST_CASE("conf") 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); diff --git a/tests/recorder.cpp b/tests/recorder.cpp index c42e6b1..85deb89 100644 --- a/tests/recorder.cpp +++ b/tests/recorder.cpp @@ -326,17 +326,17 @@ TEST_CASE("recorder") 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); @@ -344,22 +344,22 @@ TEST_CASE("recorder") 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); @@ -368,22 +368,22 @@ TEST_CASE("recorder") 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); @@ -394,20 +394,20 @@ TEST_CASE("recorder") 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 @@ -415,7 +415,7 @@ TEST_CASE("recorder") 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); @@ -426,27 +426,27 @@ TEST_CASE("recorder") 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); @@ -455,18 +455,18 @@ TEST_CASE("recorder") 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); @@ -474,9 +474,9 @@ TEST_CASE("recorder") 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") @@ -487,10 +487,10 @@ TEST_CASE("recorder") 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);