From: Jaromír Mikeš Date: Mon, 21 Nov 2016 08:32:09 +0000 (+0100) Subject: New upstream version 0.13.1~dfsg1 X-Git-Tag: archive/raspbian/0.15.4+ds1-1+rpi1^2~12^2~13 X-Git-Url: https://dgit.raspbian.org/?a=commitdiff_plain;h=ae30bd633d2221840c6db5359a6e97f21d0a074c;p=giada.git New upstream version 0.13.1~dfsg1 --- diff --git a/ChangeLog b/ChangeLog index abcbc3f..22878ce 100644 --- a/ChangeLog +++ b/ChangeLog @@ -12,7 +12,26 @@ -------------------------------------------------------------------------------- -0.13.0 --- +0.13.1 --- 2016 . 11 . 16 +- Input MIDI to MIDI channels/plugins +- Refinements to show/hide 'R' button's dynamics +- Increase piano roll items' height +- Set input volume to max by default +- Start live-recorded sample channels right away +- Avoid potential crashes when loading samples on running channels +- Generate metronome during output post-processing +- Better widgets' layout in Sample Editor +- Lots of source code optimizations and cleanups +- Fix inverted 'R' button's status (GitHub #94) +- Better handling of 'R' button's status when the sequencer is off (GitHub #95) +- Fix non-playing samples if live-recorded and 'R' button is on (GitHub #93) +- Reset button statuses once channels have been freed (GitHub #100) +- Fix missing ASIO and WASAPI APIs on Windows (GitHub #96) +- Missing RtMidi libs on Linux (GitHub #102) +- Fix fade-in/fade-out editing not triggering alert on save (GitHub #101) + + +0.13.0 --- 2016 . 09 . 20 - Deep file browser refactoring - Save browser's scroll position and last item selected on opening - Load patches/projects/samples on double click @@ -25,9 +44,13 @@ - Update JUCE to version 4.2.3 - Don't include JUCE on tests without VST support (GitHub #75) - Fix compilation errors on GCC 6 (GitHub #82) +- Fix includes on OSX (GitHub #92) - Fix wrong channel's actions count that prevented "R" button to be toggled properly - Fixed a bug that prevented actions on frame 0 to being properly reproduced +- Make Recorder a proper class +- Better naming convention for ActionEditor's children classes +- Source code reorganization 0.12.2 --- 2016 . 06 . 02 diff --git a/Makefile.am b/Makefile.am index a78f094..7990158 100644 --- a/Makefile.am +++ b/Makefile.am @@ -47,8 +47,10 @@ src/core/dataStorageIni.h \ src/core/dataStorageIni.cpp \ src/core/dataStorageJson.h \ src/core/dataStorageJson.cpp \ -src/glue/glue.h \ -src/glue/glue.cpp \ +src/glue/main.h \ +src/glue/main.cpp \ +src/glue/io.h \ +src/glue/io.cpp \ src/glue/storage.h \ src/glue/storage.cpp \ src/glue/channel.h \ @@ -89,10 +91,10 @@ src/gui/dialogs/gd_pluginChooser.h \ src/gui/dialogs/gd_pluginChooser.cpp \ src/gui/elems/ge_column.h \ src/gui/elems/ge_column.cpp \ -src/gui/elems/ge_sampleChannel.h \ -src/gui/elems/ge_sampleChannel.cpp \ -src/gui/elems/ge_midiChannel.h \ -src/gui/elems/ge_midiChannel.cpp \ +src/gui/elems/sampleChannel.h \ +src/gui/elems/sampleChannel.cpp \ +src/gui/elems/midiChannel.h \ +src/gui/elems/midiChannel.cpp \ src/gui/elems/ge_midiIoTools.h \ src/gui/elems/ge_midiIoTools.cpp \ src/gui/elems/ge_mixed.h \ @@ -101,18 +103,22 @@ src/gui/elems/ge_waveform.h \ src/gui/elems/ge_waveform.cpp \ src/gui/elems/ge_browser.h \ src/gui/elems/ge_browser.cpp \ -src/gui/elems/ge_actionWidget.h \ -src/gui/elems/ge_actionWidget.cpp \ -src/gui/elems/ge_envelopeChannel.h \ -src/gui/elems/ge_envelopeChannel.cpp \ -src/gui/elems/ge_pianoRoll.h \ -src/gui/elems/ge_pianoRoll.cpp \ -src/gui/elems/ge_channel.h \ -src/gui/elems/ge_channel.cpp \ -src/gui/elems/ge_muteChannel.h \ -src/gui/elems/ge_muteChannel.cpp \ -src/gui/elems/ge_actionChannel.h \ -src/gui/elems/ge_actionChannel.cpp \ +src/gui/elems/baseActionEditor.h \ +src/gui/elems/baseActionEditor.cpp \ +src/gui/elems/envelopeEditor.h \ +src/gui/elems/envelopeEditor.cpp \ +src/gui/elems/pianoRoll.h \ +src/gui/elems/pianoRoll.cpp \ +src/gui/elems/noteEditor.h \ +src/gui/elems/noteEditor.cpp \ +src/gui/elems/pianoItem.h \ +src/gui/elems/pianoItem.cpp \ +src/gui/elems/channel.h \ +src/gui/elems/channel.cpp \ +src/gui/elems/muteEditor.h \ +src/gui/elems/muteEditor.cpp \ +src/gui/elems/actionEditor.h \ +src/gui/elems/actionEditor.cpp \ src/gui/elems/ge_window.h \ src/gui/elems/ge_window.cpp \ src/gui/elems/ge_status.h \ @@ -125,17 +131,17 @@ src/gui/elems/ge_modeBox.h \ src/gui/elems/ge_modeBox.cpp \ src/gui/elems/ge_controller.h \ src/gui/elems/ge_controller.cpp \ -src/gui/elems/ge_channelButton.h \ -src/gui/elems/ge_channelButton.cpp \ +src/gui/elems/channelButton.h \ +src/gui/elems/channelButton.cpp \ src/gui/elems/ge_pluginBrowser.h \ src/gui/elems/ge_pluginBrowser.cpp \ src/utils/log.h \ src/utils/log.cpp \ -src/utils/gui_utils.h \ -src/utils/gui_utils.cpp \ +src/utils/gui.h \ +src/utils/gui.cpp \ src/utils/gvector.h \ -src/utils/utils.h \ -src/utils/utils.cpp \ +src/utils/fs.h \ +src/utils/fs.cpp \ src/utils/string.h \ src/utils/string.cpp @@ -176,13 +182,13 @@ giada_CPPFLAGS += \ -DJUCE_PLUGINHOST_VST=1 \ -DJUCE_PLUGINHOST_VST3=0 \ -DJUCE_PLUGINHOST_AU=0 -giada_CXXFLAGS += -Wno-error=misleading-indentation endif if LINUX giada_SOURCES += src/deps/rtaudio-mod/RtAudio.h src/deps/rtaudio-mod/RtAudio.cpp -# mute rtAudio error on variable length array -giada_CXXFLAGS += -Wno-error=vla +# -Wno-error=vla: mute rtAudio error on variable length array +# -Wno-error=misleading-indentation: mute JUCE warnings on GCC6 +giada_CXXFLAGS += -Wno-error=vla -Wno-error=misleading-indentation giada_CPPFLAGS += -D__LINUX_ALSA__ -D__LINUX_PULSE__ -D__UNIX_JACK__ giada_LDADD = -lsndfile -lfltk -lXext -lX11 -lXft -lXpm -lm -ljack -lasound \ -lpthread -ldl -lpulse-simple -lpulse -lsamplerate -lrtmidi -ljansson \ @@ -190,7 +196,28 @@ giada_LDADD = -lsndfile -lfltk -lXext -lX11 -lXft -lXpm -lm -ljack -lasound \ endif if WINDOWS -giada_LDADD = -lrtaudio -ldsound -lwsock32 -lm -lfltk -lwininet -lgdi32 \ +giada_SOURCES += \ +src/deps/rtaudio-mod/RtAudio.h \ +src/deps/rtaudio-mod/RtAudio.cpp \ +src/deps/rtaudio-mod/include/asio.h \ +src/deps/rtaudio-mod/include/asio.cpp \ +src/deps/rtaudio-mod/include/asiolist.h \ +src/deps/rtaudio-mod/include/asiolist.cpp \ +src/deps/rtaudio-mod/include/asiodrivers.h \ +src/deps/rtaudio-mod/include/asiodrivers.cpp \ +src/deps/rtaudio-mod/include/iasiothiscallresolver.h \ +src/deps/rtaudio-mod/include/iasiothiscallresolver.cpp +# -Wno-error=misleading-indentation: mute JUCE warnings on GCC6 +# -Wno-error=unused-but-set-variable: silence ASIO errors +giada_CXXFLAGS += \ +-Wno-error=misleading-indentation \ +-Wno-error=unused-but-set-variable +giada_CPPFLAGS += \ +-I./src/deps/rtaudio-mod/include \ +-D__WINDOWS_ASIO__ \ +-D__WINDOWS_WASAPI__ \ +-D__WINDOWS_DS__ +giada_LDADD = -ldsound -lwsock32 -lm -lfltk -lwininet -lgdi32 \ -lshell32 -lvfw32 -lrpcrt4 -luuid -lcomctl32 -lole32 -lws2_32 -lsndfile \ -lsamplerate -lrtmidi -lwinmm -lsetupapi -lksuser -lpthreadGC2 -ljansson \ -limm32 -lglu32 -lshell32 -lversion -lopengl32 -loleaut32 -lshlwapi -lcomdlg32 @@ -203,8 +230,9 @@ if OSX # export CXXFLAGS="-m32" # export LDFLAGS="-m32" # -ObjC++: Juce requires to build some Objective C code +# -Wno-unknown-pragmas: shut up Juce even more giada_SOURCES += src/utils/cocoa.mm src/utils/cocoa.h -giada_CXXFLAGS += -ObjC++ +giada_CXXFLAGS += -ObjC++ -Wno-unknown-pragmas giada_LDADD = -lsndfile -lm -lpthread -lfltk -lrtmidi -lrtaudio \ -lsamplerate -ljansson giada_LDFLAGS = -framework CoreAudio -framework Cocoa -framework Carbon \ @@ -234,10 +262,10 @@ src/core/wave.cpp \ src/core/midiMapConf.cpp \ src/core/patch.cpp \ src/core/plugin.cpp \ -src/core/pluginHost.cpp \ src/core/dataStorageIni.cpp \ src/core/dataStorageJson.cpp \ -src/utils/utils.cpp \ +src/utils/fs.cpp \ +src/utils/string.cpp \ src/utils/log.cpp if WITH_VST diff --git a/src/core/channel.cpp b/src/core/channel.cpp index 1a3204a..bcbafeb 100644 --- a/src/core/channel.cpp +++ b/src/core/channel.cpp @@ -28,7 +28,7 @@ #include "../utils/log.h" -#include "../gui/elems/ge_channel.h" +#include "../gui/elems/channel.h" #include "channel.h" #include "pluginHost.h" #include "plugin.h" @@ -44,9 +44,13 @@ #include "midiMapConf.h" +extern Recorder G_Recorder; +extern KernelMidi G_KernelMidi; + + Channel::Channel(int type, int status, int bufferSize, MidiMapConf *midiMapConf) #if defined(WITH_VST) -: pluginHost(NULL), +: pluginHost(nullptr), #else : #endif @@ -65,13 +69,15 @@ Channel::Channel(int type, int status, int bufferSize, MidiMapConf *midiMapConf) mute (false), solo (false), hasActions (false), + armed (false), recStatus (REC_STOPPED), - vChan (NULL), - guiChannel (NULL), + vChan (nullptr), + guiChannel (nullptr), midiIn (true), midiInKeyPress (0x0), midiInKeyRel (0x0), midiInKill (0x0), + midiInArm (0x0), midiInVolume (0x0), midiInMute (0x0), midiInSolo (0x0), @@ -82,7 +88,7 @@ Channel::Channel(int type, int status, int bufferSize, MidiMapConf *midiMapConf) { vChan = (float *) malloc(bufferSize * sizeof(float)); if (!vChan) - gLog("[Channel::allocVchan] unable to alloc memory for vChan\n"); + gu_log("[Channel::allocVchan] unable to alloc memory for vChan\n"); memset(vChan, 0, bufferSize * sizeof(float)); } @@ -119,6 +125,7 @@ void Channel::copy(const Channel *src, pthread_mutex_t *pluginMutex) midiInKeyPress = src->midiInKeyPress; midiInKeyRel = src->midiInKeyRel; midiInKill = src->midiInKill; + midiInArm = src->midiInArm; midiInVolume = src->midiInVolume; midiInMute = src->midiInMute; midiInSolo = src->midiInSolo; @@ -137,11 +144,11 @@ void Channel::copy(const Channel *src, pthread_mutex_t *pluginMutex) /* clone actions */ - for (unsigned i=0; ichan == src->index) - recorder::rec(index, a->type, a->frame, a->iValue, a->fValue); + G_Recorder.rec(index, a->type, a->frame, a->iValue, a->fValue); } } } @@ -152,7 +159,7 @@ void Channel::copy(const Channel *src, pthread_mutex_t *pluginMutex) void Channel::sendMidiLmessage(uint32_t learn, const MidiMapConf::message_t &msg) { - gLog("[channel::sendMidiLmessage] learn=%#X, chan=%d, msg=%#X, offset=%d\n", + gu_log("[channel::sendMidiLmessage] learn=%#X, chan=%d, msg=%#X, offset=%d\n", learn, msg.channel, msg.value, msg.offset); /* isolate 'channel' from learnt message and offset it as requested by 'nn' @@ -164,7 +171,7 @@ void Channel::sendMidiLmessage(uint32_t learn, const MidiMapConf::message_t &msg * send it. */ out |= msg.value | (msg.channel << 24); - kernelMidi::send(out); + G_KernelMidi.send(out); } @@ -220,6 +227,7 @@ int Channel::writePatch(int i, bool isProject, Patch *patch) pch.midiInKeyPress = midiInKeyPress; pch.midiInKeyRel = midiInKeyRel; pch.midiInKill = midiInKill; + pch.midiInArm = midiInArm; pch.midiInVolume = midiInVolume; pch.midiInMute = midiInMute; pch.midiInSolo = midiInSolo; @@ -228,9 +236,9 @@ int Channel::writePatch(int i, bool isProject, Patch *patch) pch.midiOutLmute = midiOutLmute; pch.midiOutLsolo = midiOutLsolo; - for (unsigned i=0; ichan == index) { Patch::action_t pac; pac.type = action->type; @@ -296,7 +304,7 @@ int Channel::readPatch(const string &path, int i, Patch *patch, for (unsigned k=0; kactions.size(); k++) { Patch::action_t *ac = &pch->actions.at(k); - recorder::rec(index, ac->type, ac->frame, ac->iValue, ac->fValue); + G_Recorder.rec(index, ac->type, ac->frame, ac->iValue, ac->fValue); } #ifdef WITH_VST @@ -305,7 +313,7 @@ int Channel::readPatch(const string &path, int i, Patch *patch, Patch::plugin_t *ppl = &pch->plugins.at(k); Plugin *plugin = pluginHost->addPlugin(ppl->path, PluginHost::CHANNEL, pluginMutex, this); - if (plugin != NULL) { + if (plugin != nullptr) { plugin->setBypass(ppl->bypass); for (unsigned j=0; jparams.size(); j++) plugin->setParameter(j, ppl->params.at(j)); @@ -375,9 +383,36 @@ void Channel::sendMidiLplay() /* -------------------------------------------------------------------------- */ +void Channel::receiveMidi(uint32_t msg) +{ +} + +/* -------------------------------------------------------------------------- */ + + #ifdef WITH_VST + +juce::MidiBuffer &Channel::getPluginMidiEvents() +{ + return midiBuffer; +} + + +/* -------------------------------------------------------------------------- */ + + +void Channel::clearMidiBuffer() +{ + midiBuffer.clear(); +} + + +/* -------------------------------------------------------------------------- */ + + void Channel::setPluginHost(PluginHost *pluginHost) { this->pluginHost = pluginHost; } + #endif diff --git a/src/core/channel.h b/src/core/channel.h index e0ee899..df9fe64 100644 --- a/src/core/channel.h +++ b/src/core/channel.h @@ -32,15 +32,15 @@ #include -#include "../utils/utils.h" +#include #include "midiMapConf.h" -#include "const.h" #include "recorder.h" #ifdef WITH_VST #include "../deps/juce/config.h" #endif + using std::vector; using std::string; @@ -91,12 +91,6 @@ public: virtual void copy(const Channel *src, pthread_mutex_t *pluginMutex) = 0; - /* writePatch - * Fill a patch with channel values. Returns the index of the last - * Patch::channel_t added. */ - - virtual int writePatch(int i, bool isProject, class Patch *patch); - /* readPatch * Fill channel with data from patch. */ @@ -106,16 +100,19 @@ public: pthread_mutex_t *pluginMutex, int samplerate, int rsmpQuality); /* process - * merge vChannels into buffer, plus plugin processing (if any). */ + Merges vChannels into buffer, plus plugin processing (if any). Warning: + inBuffer might be nullptr if no input devices are available for recording. */ - virtual void process(float *buffer) = 0; + virtual void process(float *outBuffer, float *inBuffer) = 0; /* start - * action to do when channel starts. doQuantize = false (don't - * quantize) when Mixer is reading actions from Recorder::. */ + Action to do when channel starts. doQuantize = false (don't quantize) + when Mixer is reading actions from Recorder. If isUserGenerated means that + the channel has been started by a human key press and not a pre-recorded + action. */ virtual void start(int frame, bool doQuantize, int quantize, - bool mixerIsRunning) = 0; + bool mixerIsRunning, bool forceStart, bool isUserGenerated) = 0; /* stop * action to do when channel is stopped normally (via key or MIDI). */ @@ -167,7 +164,9 @@ public: * localFrame - frame number of the processed buffer * globalFrame - actual frame in Mixer */ - virtual void parseAction(recorder::action *a, int localFrame, int globalFrame, + // TODO - quantize is useless! + + virtual void parseAction(Recorder::action *a, int localFrame, int globalFrame, int quantize, bool mixerIsRunning) = 0; /* rewind @@ -175,25 +174,49 @@ public: virtual void rewind() = 0; + /* clear + Clears all memory buffers. This is actually useful to sample channels only. */ + + virtual void clear() = 0; + + /* canInputRec + Tells whether a channel can accept and handle input audio. Always false for + Midi channels, true for Sample channels only if they don't contain a + sample yet.*/ + + virtual bool canInputRec() = 0; + + /* writePatch + * Fill a patch with channel values. Returns the index of the last + * Patch::channel_t added. */ + + virtual int writePatch(int i, bool isProject, class Patch *patch); + + /* receiveMidi + * Receives and processes midi messages from external devices. */ + + virtual void receiveMidi(uint32_t msg); + /* ------------------------------------------------------------------------ */ - int index; // unique id - int type; // midi or sample - int status; // status: see const.h - int key; // keyboard button - float volume; // global volume - float volume_i; // internal volume - float volume_d; // delta volume (for envelope) + int index; // unique id + int type; // midi or sample + int status; // status: see const.h + int key; // keyboard button + float volume; // global volume + float volume_i; // internal volume + float volume_d; // delta volume (for envelope) float panLeft; float panRight; - bool mute_i; // internal mute - bool mute_s; // previous mute status after being solo'd - bool mute; // global mute + bool mute_i; // internal mute + bool mute_s; // previous mute status after being solo'd + bool mute; // global mute bool solo; - bool hasActions; // has something recorded - int recStatus; // status of recordings (waiting, ending, ...) - float *vChan; // virtual channel - class gChannel *guiChannel; // pointer to a gChannel object, part of the GUI + bool hasActions; // has something recorded + bool armed; // armed for recording + int recStatus; // status of recordings (waiting, ending, ...) + float *vChan; // virtual channel + class geChannel *guiChannel; // pointer to a gChannel object, part of the GUI // TODO - midi structs, please @@ -201,6 +224,7 @@ public: uint32_t midiInKeyPress; uint32_t midiInKeyRel; uint32_t midiInKill; + uint32_t midiInArm; uint32_t midiInVolume; uint32_t midiInMute; uint32_t midiInSolo; @@ -253,7 +277,9 @@ public: * Return a reference to midiBuffer stack. This is available for any kind of * channel, but it makes sense only for MIDI channels. */ - juce::MidiBuffer &getPluginMidiEvents() { return midiBuffer; }; + juce::MidiBuffer &getPluginMidiEvents(); + + void clearMidiBuffer(); #endif }; diff --git a/src/core/conf.cpp b/src/core/conf.cpp index eadbce2..021549b 100644 --- a/src/core/conf.cpp +++ b/src/core/conf.cpp @@ -30,7 +30,7 @@ #include #include "conf.h" #include "const.h" -#include "../utils/utils.h" +#include "../utils/fs.h" #include "../utils/log.h" @@ -44,8 +44,8 @@ Conf::Conf() #if defined(__linux__) || defined(__APPLE__) - confFilePath = gGetHomePath() + G_SLASH + CONF_FILENAME; - confDirPath = gGetHomePath() + G_SLASH; + confFilePath = gu_getHomePath() + G_SLASH + CONF_FILENAME; + confDirPath = gu_getHomePath() + G_SLASH; #elif defined(_WIN32) @@ -63,17 +63,17 @@ int Conf::createConfigFolder() { #if defined(__linux__) || defined(__APPLE__) - if (gDirExists(confDirPath)) + if (gu_dirExists(confDirPath)) return 1; - gLog("[Conf::createConfigFolder] .giada folder not present. Updating...\n"); + gu_log("[Conf::createConfigFolder] .giada folder not present. Updating...\n"); - if (gMkdir(confDirPath)) { - gLog("[Conf::createConfigFolder] status: ok\n"); + if (gu_mkdir(confDirPath)) { + gu_log("[Conf::createConfigFolder] status: ok\n"); return 1; } else { - gLog("[Conf::createConfigFolder] status: error!\n"); + gu_log("[Conf::createConfigFolder] status: error!\n"); return 0; } @@ -178,7 +178,7 @@ int Conf::read() jRoot = json_load_file(confFilePath.c_str(), 0, &jError); if (!jRoot) { - gLog("[Conf::read] unable to read configuration file! Error on line %d: %s\n", jError.line, jError.text); + gu_log("[Conf::read] unable to read configuration file! Error on line %d: %s\n", jError.line, jError.text); return 0; } @@ -373,7 +373,7 @@ int Conf::write() #endif if (json_dump_file(jRoot, confFilePath.c_str(), JSON_INDENT(2)) != 0) { - gLog("[Conf::write] unable to write configuration file!\n"); + gu_log("[Conf::write] unable to write configuration file!\n"); return 0; } return 1; diff --git a/src/core/const.h b/src/core/const.h index 90b362f..9571b2d 100644 --- a/src/core/const.h +++ b/src/core/const.h @@ -32,11 +32,11 @@ /* -- version --------------------------------------------------------------- */ -#define G_VERSION_STR "0.13.0" +#define G_VERSION_STR "0.13.1" #define G_APP_NAME "Giada" #define G_VERSION_MAJOR 0 #define G_VERSION_MINOR 13 -#define G_VERSION_PATCH 0 +#define G_VERSION_PATCH 1 #define CONF_FILENAME "giada.conf" @@ -75,6 +75,8 @@ #define COLOR_TEXT_0 fl_rgb_color(200, 200, 200) #define COLOR_TEXT_1 fl_rgb_color(25, 25, 25) // TODO - duplicate! #define COLOR_BG_MAIN fl_rgb_color(25, 25, 25) // windows background +#define COLOR_BG_RICH fl_rgb_color(30, 30, 30) // lighter background +#define COLOR_BG_LINE fl_rgb_color(54, 54, 54) // lighter, for bg lines #define COLOR_BG_DARK fl_rgb_color(0, 0, 0) // inputs background @@ -128,11 +130,11 @@ #define DEFAULT_SAMPLERATE 44100 #define DEFAULT_BUFSIZE 1024 #define DEFAULT_DELAYCOMP 0 -#define DEFAULT_VOL 0.0f +#define DEFAULT_VOL 1.0f #define DEFAULT_BOOST 0.0f #define gDEFAULT_PITCH 1.0f // ugly and temporary fix to avoid conflicts with wingdi.h (Windows only). #define DEFAULT_OUT_VOL 1.0f -#define DEFAULT_IN_VOL 0.0f +#define DEFAULT_IN_VOL 1.0f #define DEFAULT_CHANMODE SINGLE_BASIC #define DEFAULT_BPM 120.0f #define DEFAULT_BEATS 4 @@ -140,6 +142,7 @@ #define DEFAULT_QUANTIZE 0 // quantizer off #define DEFAULT_FADEOUT_STEP 0.01f // micro-fadeout speed #define DEFAULT_COLUMN_WIDTH 380 +#define G_DEFAULT_PATCH_NAME "(default patch)" @@ -211,6 +214,7 @@ /* -- browser types --------------------------------------------------------- */ +/* TODO - USELESS, remove them */ #define BROWSER_LOAD_PATCH 0x00 #define BROWSER_LOAD_SAMPLE 0x01 #define BROWSER_SAVE_PATCH 0x02 @@ -268,6 +272,7 @@ #define MIDI_CONTROLLER 0xB0 << 24 #define MIDI_NOTE_ON 0x90 << 24 #define MIDI_NOTE_OFF 0x80 << 24 +#define MIDI_VELOCITY 0x3F << 8 #define MIDI_ALL_NOTES_OFF (MIDI_CONTROLLER) | (0x7B << 16) #define MIDI_VOLUME (MIDI_CONTROLLER) | (0x07 << 16) @@ -350,6 +355,7 @@ const int MIDI_CHANS[16] = { #define PATCH_KEY_CHANNEL_MIDI_IN_KEYPRESS "midi_in_keypress" #define PATCH_KEY_CHANNEL_MIDI_IN_KEYREL "midi_in_keyrel" #define PATCH_KEY_CHANNEL_MIDI_IN_KILL "midi_in_kill" +#define PATCH_KEY_CHANNEL_MIDI_IN_ARM "midi_in_arm" #define PATCH_KEY_CHANNEL_MIDI_IN_VOLUME "midi_in_volume" #define PATCH_KEY_CHANNEL_MIDI_IN_MUTE "midi_in_mute" #define PATCH_KEY_CHANNEL_MIDI_IN_SOLO "midi_in_solo" diff --git a/src/core/dataStorageIni.cpp b/src/core/dataStorageIni.cpp index 5b4a5ec..a759782 100644 --- a/src/core/dataStorageIni.cpp +++ b/src/core/dataStorageIni.cpp @@ -47,7 +47,7 @@ std::string DataStorageIni::getValue(const char *in) char buffer[MAX_LINE_LEN]; if (fgets(buffer, MAX_LINE_LEN, fp) == NULL) { - gLog("[DataStorageIni::getValue] key '%s' not found\n", in); + gu_log("[DataStorageIni::getValue] key '%s' not found\n", in); return ""; } diff --git a/src/core/dataStorageJson.cpp b/src/core/dataStorageJson.cpp index 0a48542..a52f142 100644 --- a/src/core/dataStorageJson.cpp +++ b/src/core/dataStorageJson.cpp @@ -39,7 +39,7 @@ bool DataStorageJson::setString(json_t *jRoot, const char *key, string &output) { json_t *jObject = json_object_get(jRoot, key); if (!json_is_string(jObject)) { - gLog("[dataStorageJson::setString] key '%s' is not a string!\n", key); + gu_log("[dataStorageJson::setString] key '%s' is not a string!\n", key); json_decref(jRoot); return false; } @@ -55,12 +55,12 @@ bool DataStorageJson::setFloat(json_t *jRoot, const char *key, float &output) { json_t *jObject = json_object_get(jRoot, key); if (!jObject) { - gLog("[dataStorageJson::setFloat] key '%s' not found, using default value\n", key); + gu_log("[dataStorageJson::setFloat] key '%s' not found, using default value\n", key); output = 0.0f; return true; } if (!json_is_real(jObject)) { - gLog("[dataStorageJson::setFloat] key '%s' is not a float!\n", key); + gu_log("[dataStorageJson::setFloat] key '%s' is not a float!\n", key); json_decref(jRoot); return false; } @@ -76,12 +76,12 @@ bool DataStorageJson::setUint32(json_t *jRoot, const char *key, uint32_t &output { json_t *jObject = json_object_get(jRoot, key); if (!jObject) { - gLog("[dataStorageJson::setUint32] key '%s' not found, using default value\n", key); + gu_log("[dataStorageJson::setUint32] key '%s' not found, using default value\n", key); output = 0; return true; } if (!json_is_integer(jObject)) { - gLog("[dataStorageJson::setUint32] key '%s' is not an integer!\n", key); + gu_log("[dataStorageJson::setUint32] key '%s' is not an integer!\n", key); json_decref(jRoot); return false; } @@ -97,12 +97,12 @@ bool DataStorageJson::setBool(json_t *jRoot, const char *key, bool &output) { json_t *jObject = json_object_get(jRoot, key); if (!jObject) { - gLog("[dataStorageJson::setBool] key '%s' not found, using default value\n", key); + gu_log("[dataStorageJson::setBool] key '%s' not found, using default value\n", key); output = false; return true; } if (!json_is_boolean(jObject)) { - gLog("[dataStorageJson::setBool] key '%s' is not a boolean!\n", key); + gu_log("[dataStorageJson::setBool] key '%s' is not a boolean!\n", key); json_decref(jRoot); return false; } @@ -126,7 +126,7 @@ bool DataStorageJson::setInt(json_t *jRoot, const char *key, int &output) bool DataStorageJson::checkObject(json_t *jRoot, const char *key) { if (!json_is_object(jRoot)) { - gLog("[DataStorageJson::checkObject] malformed json: %s is not an object!\n", key); + gu_log("[DataStorageJson::checkObject] malformed json: %s is not an object!\n", key); json_decref(jRoot); return false; } @@ -140,7 +140,7 @@ bool DataStorageJson::checkObject(json_t *jRoot, const char *key) bool DataStorageJson::checkArray(json_t *jRoot, const char *key) { if (!json_is_array(jRoot)) { - gLog("[DataStorageJson::checkObject] malformed json: %s is not an array!\n", key); + gu_log("[DataStorageJson::checkObject] malformed json: %s is not an array!\n", key); json_decref(jRoot); return false; } diff --git a/src/core/graphics.cpp b/src/core/graphics.cpp index b5b9402..2826793 100644 --- a/src/core/graphics.cpp +++ b/src/core/graphics.cpp @@ -785,6 +785,48 @@ const char *inputRecOff_xpm[] = { " ", " "}; +const char *inputToOutputOn_xpm[] = { +"10 10 8 1", +" c #4D4F4C", +". c #585A57", +"+ c #666765", +"@ c #6F716E", +"# c #939592", +"$ c #999B98", +"% c #AEB0AD", +"& c #BCBEBB", +" ", +" ", +" .#@ ", +" #&&#+ ", +" @$&%. ", +" @$&%. ", +" #&&#+ ", +" .#@ ", +" ", +" "}; + +const char *inputToOutputOff_xpm[] = { +"10 10 8 1", +" c #242523", +". c #2E302D", +"+ c #3A3B39", +"@ c #4F514E", +"# c #828481", +"$ c #8B8D8A", +"% c #A7A9A6", +"& c #B9BBB7", +" ", +" ", +" +$@ ", +" .#&&#+ ", +" @$&%. ", +" @$&%. ", +" .#&&#+ ", +" +$@ ", +" ", +" "}; + const char *muteOff_xpm[] = { "18 18 8 1", " c #242523", @@ -1512,7 +1554,7 @@ const char *fxRemoveOn_xpm[] = { #endif // #ifdef WITH_VST -const char *beatsDivideOn_xpm[] = { +const char *divideOn_xpm[] = { "18 18 7 1", " c #5A5A5A", ". c #696969", @@ -1541,7 +1583,7 @@ const char *beatsDivideOn_xpm[] = { " "}; -const char *beatsDivideOff_xpm[] = { +const char *divideOff_xpm[] = { "18 18 8 1", " c #252525", ". c #3B3B3B", @@ -1571,7 +1613,7 @@ const char *beatsDivideOff_xpm[] = { " "}; -const char *beatsMultiplyOn_xpm[] = { +const char *multiplyOn_xpm[] = { "18 18 8 1", " c #595B58", ". c #737572", @@ -1601,7 +1643,7 @@ const char *beatsMultiplyOn_xpm[] = { " "}; -const char *beatsMultiplyOff_xpm[] = { +const char *multiplyOff_xpm[] = { "18 18 8 1", " c #242523", ". c #4A4C49", @@ -1690,3 +1732,63 @@ const char *channelPlay_xpm[] = { " ", " ", " "}; + + +const char *armOff_xpm[] = { +"18 18 8 1", +" c #242523", +". c #4F4445", +"+ c #514647", +"@ c #6D5C5E", +"# c #8E7372", +"$ c #AA8889", +"% c #AC898A", +"& c #B18E8F", +" ", +" ", +" ", +" ", +" +#%%#. ", +" @&&&&&&@ ", +" +&&&&&&&&. ", +" #&&&&&&&&# ", +" %&&&&&&&&% ", +" %&&&&&&&&% ", +" #&&&&&&&&# ", +" .&&&&&&&&. ", +" @&&&&&&@ ", +" .#%%#. ", +" ", +" ", +" ", +" "}; + + +const char *armOn_xpm[] = { +"18 18 8 1", +" c #4D4F4C", +". c #6B5077", +"+ c #805191", +"@ c #9950AD", +"# c #9751B3", +"$ c #9553AD", +"% c #AA52C9", +"& c #AE52D1", +" ", +" ", +" ", +" ", +" .#%%#. ", +" +&&&&&&+ ", +" .&&&&&&&&. ", +" #&&&&&&&&@ ", +" %&&&&&&&&% ", +" %&&&&&&&&% ", +" #&&&&&&&&$ ", +" .&&&&&&&&. ", +" +&&&&&&+ ", +" .@%%$. ", +" ", +" ", +" ", +" "}; diff --git a/src/core/graphics.h b/src/core/graphics.h index 39fde45..7d93be8 100644 --- a/src/core/graphics.h +++ b/src/core/graphics.h @@ -68,10 +68,13 @@ extern const char *metronomeOn_xpm[]; extern const char *inputRecOn_xpm[]; extern const char *inputRecOff_xpm[]; -extern const char *beatsDivideOn_xpm[]; -extern const char *beatsDivideOff_xpm[]; -extern const char *beatsMultiplyOn_xpm[]; -extern const char *beatsMultiplyOff_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 *muteOff_xpm[]; extern const char *muteOn_xpm[]; @@ -79,6 +82,9 @@ extern const char *muteOn_xpm[]; extern const char *soloOff_xpm[]; extern const char *soloOn_xpm[]; +extern const char *armOff_xpm[]; +extern const char *armOn_xpm[]; + extern const char *readActionOn_xpm[]; extern const char *readActionOff_xpm[]; diff --git a/src/core/init.cpp b/src/core/init.cpp index d0771c5..e280d46 100644 --- a/src/core/init.cpp +++ b/src/core/init.cpp @@ -29,8 +29,8 @@ #include #include "../utils/log.h" -#include "../utils/utils.h" -#include "../utils/gui_utils.h" +#include "../utils/fs.h" +#include "../utils/gui.h" #include "../gui/dialogs/gd_mainWindow.h" #include "../gui/dialogs/gd_warnings.h" #include "init.h" @@ -47,14 +47,17 @@ #include "kernelMidi.h" +extern KernelAudio G_KernelAudio; extern Mixer G_Mixer; +extern Recorder G_Recorder; +extern KernelMidi G_KernelMidi; extern bool G_audio_status; extern bool G_quit; extern Patch_DEPR_ G_Patch_DEPR_; extern Patch G_Patch; extern Conf G_Conf; extern MidiMapConf G_MidiMap; -extern gdMainWindow *mainWin; +extern gdMainWindow *G_MainWin; #ifdef WITH_VST extern PluginHost G_PluginHost; @@ -65,24 +68,16 @@ void init_prepareParser() { time_t t; time (&t); - gLog("[init] Giada " G_VERSION_STR " - %s", ctime(&t)); + gu_log("[init] Giada " G_VERSION_STR " - %s", ctime(&t)); G_Conf.read(); G_Patch_DEPR_.setDefault(); G_Patch.init(); -#ifdef WITH_VST - - G_PluginHost.init(G_Conf.buffersize, G_Conf.samplerate); - G_PluginHost.sortPlugins(G_Conf.pluginSortMethod); - -#endif + if (!gu_logInit(G_Conf.logMode)) + gu_log("[init] log init failed! Using default stdout\n"); - - if (!gLog_init(G_Conf.logMode)) - gLog("[init] log init failed! Using default stdout\n"); - - gLog("[init] configuration file ready\n"); + gu_log("[init] configuration file ready\n"); } @@ -91,16 +86,25 @@ void init_prepareParser() void init_prepareKernelAudio() { - kernelAudio::openDevice( - G_Conf.soundSystem, - G_Conf.soundDeviceOut, - G_Conf.soundDeviceIn, - G_Conf.channelsOut, - G_Conf.channelsIn, - G_Conf.samplerate, - G_Conf.buffersize); + G_KernelAudio.openDevice(G_Conf.soundSystem, G_Conf.soundDeviceOut, + G_Conf.soundDeviceIn, G_Conf.channelsOut, G_Conf.channelsIn, + G_Conf.samplerate, G_Conf.buffersize); G_Mixer.init(); - recorder::init(); + G_Recorder.init(); + +#ifdef WITH_VST + + /* If with Jack don't use buffer size stored in Conf. Use real buffersize + from the soundcard (G_KernelAudio.realBufsize). */ + + if (G_Conf.soundSystem == SYS_API_JACK) + G_PluginHost.init(G_KernelAudio.realBufsize, G_Conf.samplerate); + else + G_PluginHost.init(G_Conf.buffersize, G_Conf.samplerate); + + G_PluginHost.sortPlugins(G_Conf.pluginSortMethod); + +#endif } @@ -109,9 +113,9 @@ void init_prepareKernelAudio() void init_prepareKernelMIDI() { - kernelMidi::setApi(G_Conf.midiSystem); - kernelMidi::openOutDevice(G_Conf.midiPortOut); - kernelMidi::openInDevice(G_Conf.midiPortIn); + G_KernelMidi.setApi(G_Conf.midiSystem); + G_KernelMidi.openOutDevice(G_Conf.midiPortOut); + G_KernelMidi.openInDevice(G_Conf.midiPortIn); } @@ -128,9 +132,9 @@ void init_prepareMidiMap() // TODO - do the opposite: if json fails, go with deprecated one if (G_MidiMap.read(G_Conf.midiMapPath) != MIDIMAP_READ_OK) { - gLog("[init_prepareMidiMap] JSON-based midimap read failed, trying with the deprecated one...\n"); + gu_log("[init_prepareMidiMap] JSON-based midimap read failed, trying with the deprecated one...\n"); if (G_MidiMap.readMap_DEPR_(G_Conf.midiMapPath) == MIDIMAP_INVALID) - gLog("[init_prepareMidiMap] unable to read deprecated midimap. Nothing to do\n"); + gu_log("[init_prepareMidiMap] unable to read deprecated midimap. Nothing to do\n"); } } @@ -140,25 +144,20 @@ void init_prepareMidiMap() void init_startGUI(int argc, char **argv) { - char win_label[32]; - sprintf(win_label, "%s - %s", - G_APP_NAME, - !strcmp(G_Patch_DEPR_.name, "") ? "(default patch)" : G_Patch_DEPR_.name); + G_MainWin = new gdMainWindow(GUI_WIDTH, GUI_HEIGHT, "", argc, argv); + G_MainWin->resize(G_Conf.mainWindowX, G_Conf.mainWindowY, G_Conf.mainWindowW, + G_Conf.mainWindowH); - mainWin = new gdMainWindow(GUI_WIDTH, GUI_HEIGHT, win_label, argc, argv); - mainWin->resize(G_Conf.mainWindowX, G_Conf.mainWindowY, G_Conf.mainWindowW, G_Conf.mainWindowH); + gu_updateMainWinLabel(G_Patch.name == "" ? G_DEFAULT_PATCH_NAME : G_Patch.name); /* never update the GUI elements if G_audio_status is bad, segfaults * are around the corner */ if (G_audio_status) gu_updateControls(); - - if (!G_audio_status) - gdAlert( - "Your soundcard isn't configured correctly.\n" - "Check the configuration and restart Giada." - ); + else + gdAlert("Your soundcard isn't configured correctly.\n" + "Check the configuration and restart Giada."); } /* -------------------------------------------------------------------------- */ @@ -167,7 +166,7 @@ void init_startGUI(int argc, char **argv) void init_startKernelAudio() { if (G_audio_status) - kernelAudio::startStream(); + G_KernelAudio.startStream(); } @@ -180,41 +179,41 @@ void init_shutdown() /* store position and size of the main window for the next startup */ - G_Conf.mainWindowX = mainWin->x(); - G_Conf.mainWindowY = mainWin->y(); - G_Conf.mainWindowW = mainWin->w(); - G_Conf.mainWindowH = mainWin->h(); + G_Conf.mainWindowX = G_MainWin->x(); + G_Conf.mainWindowY = G_MainWin->y(); + G_Conf.mainWindowW = G_MainWin->w(); + G_Conf.mainWindowH = G_MainWin->h(); /* close any open subwindow, especially before cleaning PluginHost_DEPR_ to * avoid mess */ gu_closeAllSubwindows(); - gLog("[init] all subwindows closed\n"); + gu_log("[init] all subwindows closed\n"); /* write configuration file */ if (!G_Conf.write()) - gLog("[init] error while saving configuration file!\n"); + gu_log("[init] error while saving configuration file!\n"); else - gLog("[init] configuration saved\n"); + gu_log("[init] configuration saved\n"); /* if G_audio_status we close the kernelAudio FIRST, THEN the mixer. * The opposite could cause random segfaults (even now with RtAudio?). */ if (G_audio_status) { - kernelAudio::closeDevice(); + G_KernelAudio.closeDevice(); G_Mixer.close(); - gLog("[init] Mixer closed\n"); + gu_log("[init] Mixer closed\n"); } - recorder::clearAll(); - gLog("[init] Recorder cleaned up\n"); + G_Recorder.clearAll(); + gu_log("[init] Recorder cleaned up\n"); #ifdef WITH_VST G_PluginHost.freeAllStacks(&G_Mixer.channels, &G_Mixer.mutex_plugins); - gLog("[init] PluginHost cleaned up\n"); + gu_log("[init] PluginHost cleaned up\n"); #endif - gLog("[init] Giada " G_VERSION_STR " closed\n\n"); - gLog_close(); + gu_log("[init] Giada " G_VERSION_STR " closed\n\n"); + gu_logClose(); } diff --git a/src/core/kernelAudio.cpp b/src/core/kernelAudio.cpp index d9dbb0e..b487df9 100644 --- a/src/core/kernelAudio.cpp +++ b/src/core/kernelAudio.cpp @@ -27,40 +27,43 @@ * -------------------------------------------------------------------------- */ -#include -#include #include "../utils/log.h" -#include "../glue/glue.h" -#include "kernelAudio.h" +#include "../glue/main.h" +#include "conf.h" #include "mixer.h" +#include "const.h" +#include "kernelAudio.h" -#include "conf.h" + +extern KernelAudio G_KernelAudio; +extern Mixer G_Mixer; +extern Conf G_Conf; +extern bool G_audio_status; -extern Mixer G_Mixer; -extern Conf G_Conf; -extern bool G_audio_status; +using std::string; +using std::vector; -namespace kernelAudio +KernelAudio::KernelAudio() { -RtAudio *system = NULL; -unsigned numDevs = 0; -bool inputEnabled = 0; -unsigned realBufsize = 0; -int api = 0; - -int openDevice( - int _api, - int outDev, - int inDev, - int outChan, - int inChan, - int samplerate, - int buffersize) + system = nullptr; + numDevs = 0; + inputEnabled = 0; + realBufsize = 0; + api = 0; +} + + +/* -------------------------------------------------------------------------- */ + + + +int KernelAudio::openDevice(int _api, int outDev, int inDev, int outChan, + int inChan, int samplerate, int buffersize) { api = _api; - gLog("[KA] using system 0x%x\n", api); + gu_log("[KA] using system 0x%x\n", api); #if defined(__linux__) @@ -88,7 +91,7 @@ int openDevice( if (api == SYS_API_CORE && hasAPI(RtAudio::MACOSX_CORE)) system = new RtAudio(RtAudio::MACOSX_CORE); - + #endif else { @@ -96,27 +99,22 @@ int openDevice( return 0; } - - - //gLog("[KA] %d\n", sizeof(system->rtapi_)); - - gLog("[KA] Opening devices %d (out), %d (in), f=%d...\n", outDev, inDev, samplerate); + gu_log("[KA] Opening devices %d (out), %d (in), f=%d...\n", outDev, inDev, samplerate); numDevs = system->getDeviceCount(); if (numDevs < 1) { - gLog("[KA] no devices found with this API\n"); + gu_log("[KA] no devices found with this API\n"); closeDevice(); G_audio_status = false; return 0; } else { - gLog("[KA] %d device(s) found\n", numDevs); + gu_log("[KA] %d device(s) found\n", numDevs); for (unsigned i=0; iopenStream( - &outParams, // output params - inDev != -1 ? &inParams : NULL, // input params if inDevice is selected - RTAUDIO_FLOAT32, // audio format - samplerate, // sample rate - &realBufsize, // buffer size in byte - &G_Mixer.masterPlay, // audio callback - NULL, // user data (unused) + &outParams, // output params + inDev != -1 ? &inParams : nullptr, // input params if inDevice is selected + RTAUDIO_FLOAT32, // audio format + samplerate, // sample rate + &realBufsize, // buffer size in byte + &G_Mixer.masterPlay, // audio callback + nullptr, // user data (unused) &options); G_audio_status = true; @@ -173,7 +171,7 @@ int openDevice( return 1; } catch (RtAudioError &e) { - gLog("[KA] system init error: %s\n", e.getMessage().c_str()); + gu_log("[KA] system init error: %s\n", e.getMessage().c_str()); closeDevice(); G_audio_status = false; return 0; @@ -184,15 +182,15 @@ int openDevice( /* -------------------------------------------------------------------------- */ -int startStream() +int KernelAudio::startStream() { try { system->startStream(); - gLog("[KA] latency = %lu\n", system->getStreamLatency()); + gu_log("[KA] latency = %lu\n", system->getStreamLatency()); return 1; } catch (RtAudioError &e) { - gLog("[KA] Start stream error: %s\n", e.getMessage().c_str()); + gu_log("[KA] Start stream error: %s\n", e.getMessage().c_str()); return 0; } } @@ -201,14 +199,14 @@ int startStream() /* -------------------------------------------------------------------------- */ -int stopStream() +int KernelAudio::stopStream() { try { system->stopStream(); return 1; } catch (RtAudioError &e) { - gLog("[KA] Stop stream error\n"); + gu_log("[KA] Stop stream error\n"); return 0; } } @@ -217,13 +215,13 @@ int stopStream() /* -------------------------------------------------------------------------- */ -string getDeviceName(unsigned dev) +string KernelAudio::getDeviceName(unsigned dev) { try { return ((RtAudio::DeviceInfo) system->getDeviceInfo(dev)).name; } catch (RtAudioError &e) { - gLog("[KA] invalid device ID = %d\n", dev); + gu_log("[KA] invalid device ID = %d\n", dev); return ""; } } @@ -232,7 +230,7 @@ string getDeviceName(unsigned dev) /* -------------------------------------------------------------------------- */ -int closeDevice() +int KernelAudio::closeDevice() { if (system->isStreamOpen()) { #if defined(__linux__) || defined(__APPLE__) @@ -242,7 +240,7 @@ int closeDevice() #endif system->closeStream(); delete system; - system = NULL; + system = nullptr; } return 1; } @@ -251,7 +249,7 @@ int closeDevice() /* -------------------------------------------------------------------------- */ -unsigned getMaxInChans(int dev) +unsigned KernelAudio::getMaxInChans(int dev) { if (dev == -1) return 0; @@ -259,7 +257,7 @@ unsigned getMaxInChans(int dev) return ((RtAudio::DeviceInfo) system->getDeviceInfo(dev)).inputChannels; } catch (RtAudioError &e) { - gLog("[KA] Unable to get input channels\n"); + gu_log("[KA] Unable to get input channels\n"); return 0; } } @@ -268,13 +266,13 @@ unsigned getMaxInChans(int dev) /* -------------------------------------------------------------------------- */ -unsigned getMaxOutChans(unsigned dev) +unsigned KernelAudio::getMaxOutChans(unsigned dev) { try { return ((RtAudio::DeviceInfo) system->getDeviceInfo(dev)).outputChannels; } catch (RtAudioError &e) { - gLog("[KA] Unable to get output channels\n"); + gu_log("[KA] Unable to get output channels\n"); return 0; } } @@ -283,7 +281,7 @@ unsigned getMaxOutChans(unsigned dev) /* -------------------------------------------------------------------------- */ -bool isProbed(unsigned dev) +bool KernelAudio::isProbed(unsigned dev) { try { return ((RtAudio::DeviceInfo) system->getDeviceInfo(dev)).probed; @@ -297,7 +295,7 @@ bool isProbed(unsigned dev) /* -------------------------------------------------------------------------- */ -unsigned getDuplexChans(unsigned dev) +unsigned KernelAudio::getDuplexChans(unsigned dev) { try { return ((RtAudio::DeviceInfo) system->getDeviceInfo(dev)).duplexChannels; @@ -311,7 +309,7 @@ unsigned getDuplexChans(unsigned dev) /* -------------------------------------------------------------------------- */ -bool isDefaultIn(unsigned dev) +bool KernelAudio::isDefaultIn(unsigned dev) { try { return ((RtAudio::DeviceInfo) system->getDeviceInfo(dev)).isDefaultInput; @@ -325,7 +323,7 @@ bool isDefaultIn(unsigned dev) /* -------------------------------------------------------------------------- */ -bool isDefaultOut(unsigned dev) +bool KernelAudio::isDefaultOut(unsigned dev) { try { return ((RtAudio::DeviceInfo) system->getDeviceInfo(dev)).isDefaultOutput; @@ -339,7 +337,7 @@ bool isDefaultOut(unsigned dev) /* -------------------------------------------------------------------------- */ -int getTotalFreqs(unsigned dev) +int KernelAudio::getTotalFreqs(unsigned dev) { try { return ((RtAudio::DeviceInfo) system->getDeviceInfo(dev)).sampleRates.size(); @@ -353,7 +351,7 @@ int getTotalFreqs(unsigned dev) /* -------------------------------------------------------------------------- */ -int getFreq(unsigned dev, int i) +int KernelAudio::getFreq(unsigned dev, int i) { try { return ((RtAudio::DeviceInfo) system->getDeviceInfo(dev)).sampleRates.at(i); @@ -367,12 +365,12 @@ int getFreq(unsigned dev, int i) /* -------------------------------------------------------------------------- */ -int getDefaultIn() +int KernelAudio::getDefaultIn() { return system->getDefaultInputDevice(); } -int getDefaultOut() +int KernelAudio::getDefaultOut() { return system->getDefaultOutputDevice(); } @@ -381,7 +379,7 @@ int getDefaultOut() /* -------------------------------------------------------------------------- */ -int getDeviceByName(const char *name) +int KernelAudio::getDeviceByName(const char *name) { for (unsigned i=0; i APIs; + vector APIs; RtAudio::getCompiledApi(APIs); for (unsigned i=0; i -#include -#include -jack_client_t *jackGetHandle() +int KernelAudio::jackSyncCb(jack_transport_state_t state, jack_position_t *pos, + void *arg) +{ + return G_KernelAudio.__jackSyncCb(state, pos, arg); +} + + +/* -------------------------------------------------------------------------- */ + + +jack_client_t *KernelAudio::jackGetHandle() { return (jack_client_t*) system->rtapi_->__HACK__getJackClient(); } -void jackStart() + +/* -------------------------------------------------------------------------- */ + + +void KernelAudio::jackStart() { if (api == SYS_API_JACK) { jack_client_t *client = jackGetHandle(); @@ -435,7 +444,10 @@ void jackStart() } -void jackStop() +/* -------------------------------------------------------------------------- */ + + +void KernelAudio::jackStop() { if (api == SYS_API_JACK) { jack_client_t *client = jackGetHandle(); @@ -444,42 +456,46 @@ void jackStop() } -void jackSetSyncCb() +/* -------------------------------------------------------------------------- */ + + +void KernelAudio::jackSetSyncCb() { jack_client_t *client = jackGetHandle(); - jack_set_sync_callback(client, jackSyncCb, NULL); + jack_set_sync_callback(client, jackSyncCb, nullptr); //jack_set_sync_timeout(client, 8); } -int jackSyncCb(jack_transport_state_t state, jack_position_t *pos, +/* -------------------------------------------------------------------------- */ + + +int KernelAudio::__jackSyncCb(jack_transport_state_t state, jack_position_t *pos, void *arg) { switch (state) { case JackTransportStopped: - gLog("[KA] Jack transport stopped, frame=%d\n", pos->frame); + gu_log("[KA] Jack transport stopped, frame=%d\n", pos->frame); glue_stopSeq(false); // false = not from GUI if (pos->frame == 0) glue_rewindSeq(); break; case JackTransportRolling: - gLog("[KA] Jack transport rolling\n"); + gu_log("[KA] Jack transport rolling\n"); break; case JackTransportStarting: - gLog("[KA] Jack transport starting, frame=%d\n", pos->frame); + gu_log("[KA] Jack transport starting, frame=%d\n", pos->frame); glue_startSeq(false); // false = not from GUI if (pos->frame == 0) glue_rewindSeq(); break; default: - gLog("[KA] Jack transport [unknown]\n"); + gu_log("[KA] Jack transport [unknown]\n"); } return 1; } #endif - -} diff --git a/src/core/kernelAudio.h b/src/core/kernelAudio.h index 2ab1e12..ba3667e 100644 --- a/src/core/kernelAudio.h +++ b/src/core/kernelAudio.h @@ -1,10 +1,10 @@ -/* --------------------------------------------------------------------- +/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * KernelAudio * - * --------------------------------------------------------------------- + * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual * @@ -24,7 +24,7 @@ * along with Giada - Your Hardcore Loopmachine. If not, see * . * - * ------------------------------------------------------------------ */ + * -------------------------------------------------------------------------- */ #ifndef KERNELAUDIO_H @@ -32,26 +32,22 @@ #include "../deps/rtaudio-mod/RtAudio.h" -#if defined(__linux__) +#ifdef __linux__ #include #include #include #endif -using std::string; +class KernelAudio +{ +public: + KernelAudio(); -namespace kernelAudio { + int openDevice(int api, int outDev, int inDev, int outChan, int inChan, + int samplerate, int buffersize); - int openDevice( - int api, - int outDev, - int inDev, - int outChan, - int inChan, - int samplerate, - int buffersize); int closeDevice(); int startStream(); @@ -60,7 +56,7 @@ namespace kernelAudio { bool isProbed (unsigned dev); bool isDefaultIn (unsigned dev); bool isDefaultOut (unsigned dev); - string getDeviceName (unsigned dev); + std::string getDeviceName (unsigned dev); unsigned getMaxInChans (int dev); unsigned getMaxOutChans (unsigned dev); unsigned getDuplexChans (unsigned dev); @@ -70,29 +66,27 @@ namespace kernelAudio { int getDefaultOut (); int getDefaultIn (); bool hasAPI (int API); - string getRtAudioVersion(); + std::string getRtAudioVersion(); #ifdef __linux__ + jack_client_t *jackGetHandle(); void jackStart(); void jackStop(); void jackSetSyncCb(); - int jackSyncCb(jack_transport_state_t state, jack_position_t *pos, void *arg); + static int jackSyncCb(jack_transport_state_t state, jack_position_t *pos, void *arg); + int __jackSyncCb(jack_transport_state_t state, jack_position_t *pos, void *arg); + #endif - /* *** how to avoid multiple definition of *** - * When you declare a variable in a header file, every source file that - * includes that header, either directly or indirectly, gets its own - * separate copy of the variable. Then when you go to link all the .o - * files together, the linker sees that the variable is instantiated - * in a bunch of .o files. Make it extern in the header file and - * instantiate it in memory.cpp. */ - - extern RtAudio *system; - extern unsigned numDevs; - extern bool inputEnabled; - extern unsigned realBufsize; // reale bufsize from the soundcard - extern int api; -} + unsigned numDevs; + bool inputEnabled; + unsigned realBufsize; // reale bufsize from the soundcard + int api; + +private: + + RtAudio *system; +}; #endif diff --git a/src/core/kernelMidi.cpp b/src/core/kernelMidi.cpp index 36c99d9..9676143 100644 --- a/src/core/kernelMidi.cpp +++ b/src/core/kernelMidi.cpp @@ -27,44 +27,64 @@ * -------------------------------------------------------------------------- */ -#include +#include #include "../utils/log.h" -#include "../glue/glue.h" -#include "kernelMidi.h" +#include "../glue/channel.h" +#include "../glue/main.h" +#include "../glue/io.h" #include "mixer.h" +#include "const.h" #include "channel.h" #include "sampleChannel.h" +#include "midiChannel.h" #include "conf.h" #include "midiMapConf.h" +#include "pluginHost.h" +#include "kernelMidi.h" extern bool G_midiStatus; extern Conf G_Conf; extern Mixer G_Mixer; +extern KernelMidi G_KernelMidi; extern MidiMapConf G_MidiMap; +extern PluginHost G_PluginHost; using std::string; +using std::vector; + + +KernelMidi::KernelMidi() + : numOutPorts(0), + numInPorts (0), + api (0), // one api for both in & out + midiOut (nullptr), + midiIn (nullptr), + cb_learn (nullptr), + cb_data (nullptr) +{ +} -namespace kernelMidi +/* -------------------------------------------------------------------------- */ + + +void KernelMidi::callback(double t, vector *msg, void *data) { -int api = 0; // one api for both in & out -RtMidiOut *midiOut = NULL; -RtMidiIn *midiIn = NULL; -unsigned numOutPorts = 0; -unsigned numInPorts = 0; + G_KernelMidi.__callback(t, msg, data); +} + -cb_midiLearn *cb_learn = NULL; -void *cb_data = NULL; +/* -------------------------------------------------------------------------- */ -void __sendMidiLightningInitMsgs__() +void KernelMidi::sendMidiLightningInitMsgs() { for(unsigned i=0; igetPortCount(); - gLog("[KM] %d output MIDI ports found\n", numOutPorts); + gu_log("[KM] %d output MIDI ports found\n", numOutPorts); for (unsigned i=0; i 0) { try { midiOut->openPort(port, getOutPortName(port)); - gLog("[KM] MIDI out port %d open\n", port); + gu_log("[KM] MIDI out port %d open\n", port); /* TODO - it shold send midiLightning message only if there is a map loaded and available in G_MidiMap. */ - __sendMidiLightningInitMsgs__(); + sendMidiLightningInitMsgs(); return 1; } catch (RtMidiError &error) { - gLog("[KM] unable to open MIDI out port %d: %s\n", port, error.getMessage().c_str()); + gu_log("[KM] unable to open MIDI out port %d: %s\n", port, error.getMessage().c_str()); G_midiStatus = false; return 0; } @@ -150,14 +170,14 @@ int openOutDevice(int port) /* -------------------------------------------------------------------------- */ -int openInDevice(int port) +int KernelMidi::openInDevice(int port) { try { midiIn = new RtMidiIn((RtMidi::Api) api, "Giada MIDI input"); G_midiStatus = true; } catch (RtMidiError &error) { - gLog("[KM] MIDI in device error: %s\n", error.getMessage().c_str()); + gu_log("[KM] MIDI in device error: %s\n", error.getMessage().c_str()); G_midiStatus = false; return 0; } @@ -165,9 +185,9 @@ int openInDevice(int port) /* print input ports */ numInPorts = midiIn->getPortCount(); - gLog("[KM] %d input MIDI ports found\n", numInPorts); + gu_log("[KM] %d input MIDI ports found\n", numInPorts); for (unsigned i=0; iopenPort(port, getInPortName(port)); midiIn->ignoreTypes(true, false, true); // ignore all system/time msgs, for now - gLog("[KM] MIDI in port %d open\n", port); + gu_log("[KM] MIDI in port %d open\n", port); midiIn->setCallback(&callback); return 1; } catch (RtMidiError &error) { - gLog("[KM] unable to open MIDI in port %d: %s\n", port, error.getMessage().c_str()); + gu_log("[KM] unable to open MIDI in port %d: %s\n", port, error.getMessage().c_str()); G_midiStatus = false; return 0; } @@ -193,9 +213,9 @@ int openInDevice(int port) /* -------------------------------------------------------------------------- */ -bool hasAPI(int API) +bool KernelMidi::hasAPI(int API) { - std::vector APIs; + vector APIs; RtMidi::getCompiledApi(APIs); for (unsigned i=0; igetPortName(p); } catch (RtMidiError &error) { return ""; } } -string getInPortName(unsigned p) +string KernelMidi::getInPortName(unsigned p) { try { return midiIn->getPortName(p); } catch (RtMidiError &error) { return ""; } @@ -223,29 +243,29 @@ string getInPortName(unsigned p) /* -------------------------------------------------------------------------- */ -void send(uint32_t data) +void KernelMidi::send(uint32_t data) { if (!G_midiStatus) return; - std::vector msg(1, getB1(data)); + vector msg(1, getB1(data)); msg.push_back(getB2(data)); msg.push_back(getB3(data)); midiOut->sendMessage(&msg); - gLog("[KM] send msg=0x%X (%X %X %X)\n", data, msg[0], msg[1], msg[2]); + gu_log("[KM] send msg=0x%X (%X %X %X)\n", data, msg[0], msg[1], msg[2]); } /* -------------------------------------------------------------------------- */ -void send(int b1, int b2, int b3) +void KernelMidi::send(int b1, int b2, int b3) { if (!G_midiStatus) return; - std::vector msg(1, b1); + vector msg(1, b1); if (b2 != -1) msg.push_back(b2); @@ -253,23 +273,23 @@ void send(int b1, int b2, int b3) msg.push_back(b3); midiOut->sendMessage(&msg); - //gLog("[KM] send msg=(%X %X %X)\n", b1, b2, b3); + //gu_log("[KM] send msg=(%X %X %X)\n", b1, b2, b3); } /* -------------------------------------------------------------------------- */ -void callback(double t, std::vector *msg, void *data) +void KernelMidi::__callback(double t, vector *msg, void *data) { /* 0.8.0 - for now we handle other midi signals (common and real-time * messages) as unknown, for debugging purposes */ if (msg->size() < 3) { - gLog("[KM] MIDI received - unknown signal - size=%d, value=0x", (int) msg->size()); + gu_log("[KM] MIDI received - unknown signal - size=%d, value=0x", (int) msg->size()); for (unsigned i=0; isize(); i++) - gLog("%X", (int) msg->at(i)); - gLog("\n"); + gu_log("%X", (int) msg->at(i)); + gu_log("\n"); return; } @@ -282,104 +302,122 @@ void callback(double t, std::vector *msg, void *data) uint32_t value = input & 0x0000FF00; uint32_t pure = 0x00; if (!G_Conf.noNoteOff) - pure = input & 0xFFFF0000; // input without 'value' byte + pure = input & 0xFFFF0000; // input without 'value' byte else - pure = input & 0xFFFFFF00; // input with 'value' byte + pure = input & 0xFFFFFF00; // input with 'value' byte - gLog("[KM] MIDI received - 0x%X (chan %d)", input, chan >> 24); + gu_log("[KM] MIDI received - 0x%X (chan %d)\n", input, chan >> 24); /* start dispatcher. If midi learn is on don't parse channels, just * learn incoming midi signal. Otherwise 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) { - gLog("\n"); + if (cb_learn) cb_learn(pure, cb_data); - } else { + processMaster(pure, value); + processChannels(pure, value); + } +} - /* process master events */ - if (pure == G_Conf.midiInRewind) { - gLog(" >>> rewind (global) (pure=0x%X)", pure); - glue_rewindSeq(); - } - else if (pure == G_Conf.midiInStartStop) { - gLog(" >>> startStop (global) (pure=0x%X)", pure); - glue_startStopSeq(); - } - else if (pure == G_Conf.midiInActionRec) { - gLog(" >>> actionRec (global) (pure=0x%X)", pure); - glue_startStopActionRec(); +/* -------------------------------------------------------------------------- */ + + +void KernelMidi::processMaster(uint32_t pure, uint32_t value) +{ + if (pure == G_Conf.midiInRewind) { + gu_log(" >>> rewind (master) (pure=0x%X)\n", pure); + glue_rewindSeq(); + } + else if (pure == G_Conf.midiInStartStop) { + gu_log(" >>> startStop (master) (pure=0x%X)\n", pure); + glue_startStopSeq(false); + } + else if (pure == G_Conf.midiInActionRec) { + gu_log(" >>> actionRec (master) (pure=0x%X)\n", pure); + glue_startStopActionRec(false); + } + else if (pure == G_Conf.midiInInputRec) { + gu_log(" >>> inputRec (master) (pure=0x%X)\n", pure); + glue_startStopInputRec(false); + } + else if (pure == G_Conf.midiInMetronome) { + gu_log(" >>> metronome (master) (pure=0x%X)\n", pure); + glue_startStopMetronome(false); + } + else if (pure == G_Conf.midiInVolumeIn) { + float vf = (value >> 8)/127.0f; + gu_log(" >>> input volume (master) (pure=0x%X, value=%d, float=%f)\n", + pure, value >> 8, vf); + glue_setInVol(vf, false); + } + else if (pure == G_Conf.midiInVolumeOut) { + float vf = (value >> 8)/127.0f; + gu_log(" >>> output volume (master) (pure=0x%X, value=%d, float=%f)\n", + pure, value >> 8, vf); + glue_setOutVol(vf, false); + } + else if (pure == G_Conf.midiInBeatDouble) { + gu_log(" >>> sequencer x2 (master) (pure=0x%X)\n", pure); + glue_beatsMultiply(); + } + else if (pure == G_Conf.midiInBeatHalf) { + gu_log(" >>> sequencer /2 (master) (pure=0x%X)\n", pure); + glue_beatsDivide(); + } +} + + +/* -------------------------------------------------------------------------- */ + + +void KernelMidi::processChannels(uint32_t pure, uint32_t value) +{ + for (unsigned i=0; imidiIn) + continue; + + if (pure == ch->midiInKeyPress) { + gu_log(" >>> keyPress, ch=%d (pure=0x%X)\n", ch->index, pure); + glue_keyPress(ch, false, false); } - else if (pure == G_Conf.midiInInputRec) { - gLog(" >>> inputRec (global) (pure=0x%X)", pure); - glue_startStopInputRec(false, false); // update gui, no popup messages + else if (pure == ch->midiInKeyRel) { + gu_log(" >>> keyRel ch=%d (pure=0x%X)\n", ch->index, pure); + glue_keyRelease(ch, false, false); } - else if (pure == G_Conf.midiInMetronome) { - gLog(" >>> metronome (global) (pure=0x%X)", pure); - glue_startStopMetronome(false); + else if (pure == ch->midiInMute) { + gu_log(" >>> mute ch=%d (pure=0x%X)\n", ch->index, pure); + glue_setMute(ch, false); } - else if (pure == G_Conf.midiInVolumeIn) { - float vf = (value >> 8)/127.0f; - gLog(" >>> input volume (global) (pure=0x%X, value=%d, float=%f)", pure, value >> 8, vf); - glue_setInVol(vf, false); + else if (pure == ch->midiInSolo) { + gu_log(" >>> solo ch=%d (pure=0x%X)\n", ch->index, pure); + ch->solo ? glue_setSoloOn(ch, false) : glue_setSoloOff(ch, false); } - else if (pure == G_Conf.midiInVolumeOut) { + else if (pure == ch->midiInVolume) { float vf = (value >> 8)/127.0f; - gLog(" >>> output volume (global) (pure=0x%X, value=%d, float=%f)", pure, value >> 8, vf); - glue_setOutVol(vf, false); + gu_log(" >>> volume ch=%d (pure=0x%X, value=%d, float=%f)\n", + ch->index, pure, value >> 8, vf); + glue_setChanVol(ch, vf, false); } - else if (pure == G_Conf.midiInBeatDouble) { - gLog(" >>> sequencer x2 (global) (pure=0x%X)", pure); - glue_beatsMultiply(); + else if (pure == ((SampleChannel*)ch)->midiInPitch) { + float vf = (value >> 8)/(127/4.0f); // [0-127] ~> [0.0 4.0] + gu_log(" >>> pitch ch=%d (pure=0x%X, value=%d, float=%f)\n", + ch->index, pure, value >> 8, vf); + glue_setPitch(nullptr, (SampleChannel*)ch, vf, false); } - else if (pure == G_Conf.midiInBeatHalf) { - gLog(" >>> sequencer /2 (global) (pure=0x%X)", pure); - glue_beatsDivide(); + else if (pure == ((SampleChannel*)ch)->midiInReadActions) { + gu_log(" >>> start/stop read actions ch=%d (pure=0x%X)\n", ch->index, pure); + glue_startStopReadingRecs((SampleChannel*)ch, false); } - /* process channels */ - - for (unsigned i=0; imidiIn) continue; - - if (pure == ch->midiInKeyPress) { - gLog(" >>> keyPress, ch=%d (pure=0x%X)", ch->index, pure); - glue_keyPress(ch, false, false); - } - else if (pure == ch->midiInKeyRel) { - gLog(" >>> keyRel ch=%d (pure=0x%X)", ch->index, pure); - glue_keyRelease(ch, false, false); - } - else if (pure == ch->midiInMute) { - gLog(" >>> mute ch=%d (pure=0x%X)", ch->index, pure); - glue_setMute(ch, false); - } - else if (pure == ch->midiInSolo) { - gLog(" >>> solo ch=%d (pure=0x%X)", ch->index, pure); - ch->solo ? glue_setSoloOn(ch, false) : glue_setSoloOff(ch, false); - } - else if (pure == ch->midiInVolume) { - float vf = (value >> 8)/127.0f; - gLog(" >>> volume ch=%d (pure=0x%X, value=%d, float=%f)", ch->index, pure, value >> 8, vf); - glue_setChanVol(ch, vf, false); - } - else if (pure == ((SampleChannel*)ch)->midiInPitch) { - float vf = (value >> 8)/(127/4.0f); // [0-127] ~> [0.0 4.0] - gLog(" >>> pitch ch=%d (pure=0x%X, value=%d, float=%f)", ch->index, pure, value >> 8, vf); - glue_setPitch(NULL, (SampleChannel*)ch, vf, false); - } - else if (pure == ((SampleChannel*)ch)->midiInReadActions) { - gLog(" >>> start/stop read actions ch=%d (pure=0x%X)", ch->index, pure); - glue_startStopReadingRecs((SampleChannel*)ch, false); - } - } - gLog("\n"); + /* redirect full midi message to plugins */ + + ch->receiveMidi(pure | value); } } @@ -387,13 +425,7 @@ void callback(double t, std::vector *msg, void *data) /* -------------------------------------------------------------------------- */ -std::string getRtMidiVersion() +string KernelMidi::getRtMidiVersion() { return midiOut->getVersion(); } - - -/* -------------------------------------------------------------------------- */ - - -} // namespace diff --git a/src/core/kernelMidi.h b/src/core/kernelMidi.h index 161a953..834e0de 100644 --- a/src/core/kernelMidi.h +++ b/src/core/kernelMidi.h @@ -1,10 +1,10 @@ -/* --------------------------------------------------------------------- +/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * KernelMidi * - * --------------------------------------------------------------------- + * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual * @@ -24,45 +24,41 @@ * along with Giada - Your Hardcore Loopmachine. If not, see * . * - * ------------------------------------------------------------------ */ + * -------------------------------------------------------------------------- */ #ifndef KERNELMIDI_H #define KERNELMIDI_H -#include -#include -#include "channel.h" - +#ifdef __APPLE__ // our compiler still doesn't know about cstdint (c++11 stuff) + #include +#else + #include +#endif +#include +#include -using std::string; +class KernelMidi +{ +public: -namespace kernelMidi { + unsigned numOutPorts; + unsigned numInPorts; - extern int api; // one api for both in & out - extern unsigned numOutPorts; - extern unsigned numInPorts; + KernelMidi(); typedef void (cb_midiLearn) (uint32_t, void *); - /* cb_learn - * callback prepared by the gdMidiGrabber window and called by - * kernelMidi. It contains things to do once the midi message has been - * stored. */ - - extern cb_midiLearn *cb_learn; - extern void *cb_data; - void startMidiLearn(cb_midiLearn *cb, void *data); void stopMidiLearn(); - inline int getB1(uint32_t iValue) { return (iValue >> 24) & 0xFF; } - inline int getB2(uint32_t iValue) { return (iValue >> 16) & 0xFF; } - inline int getB3(uint32_t iValue) { return (iValue >> 8) & 0xFF; } + int getB1(uint32_t iValue) { return (iValue >> 24) & 0xFF; } + int getB2(uint32_t iValue) { return (iValue >> 16) & 0xFF; } + int getB3(uint32_t iValue) { return (iValue >> 8) & 0xFF; } - inline uint32_t getIValue(int b1, int b2, int b3) { + uint32_t getIValue(int b1, int b2, int b3) { return (b1 << 24) | (b2 << 16) | (b3 << 8) | (0x00); } @@ -91,17 +87,38 @@ namespace kernelMidi { /* getIn/OutPortName * return the name of the port 'p'. */ - string getInPortName(unsigned p); - string getOutPortName(unsigned p); + std::string getInPortName(unsigned p); + std::string getOutPortName(unsigned p); bool hasAPI(int API); + std::string getRtMidiVersion(); + +private: + + int api; + class RtMidiOut *midiOut; + class RtMidiIn *midiIn; + + /* cb_learn + * callback prepared by the gdMidiGrabber window and called by + * kernelMidi. It contains things to do once the midi message has been + * stored. */ + + cb_midiLearn *cb_learn; + void *cb_data; + /* callback * master callback for input events. */ - void callback(double t, std::vector *msg, void *data); + static void callback(double t, std::vector *msg, void *data); + void __callback(double t, std::vector *msg, void *data); + + void sendMidiLightningInitMsgs(); + - string getRtMidiVersion(); -} + void processMaster(uint32_t pure, uint32_t value); + void processChannels(uint32_t pure, uint32_t value); +}; #endif diff --git a/src/core/midiChannel.cpp b/src/core/midiChannel.cpp index 3dd0cef..cf1f88a 100644 --- a/src/core/midiChannel.cpp +++ b/src/core/midiChannel.cpp @@ -34,17 +34,21 @@ #include "patch_DEPR_.h" #include "patch.h" #include "conf.h" +#include "mixer.h" #include "kernelMidi.h" +extern Recorder G_Recorder; +extern KernelMidi G_KernelMidi; +extern PluginHost G_PluginHost; +extern Mixer G_Mixer; + + MidiChannel::MidiChannel(int bufferSize, MidiMapConf *midiMapConf) : Channel (CHANNEL_MIDI, STATUS_OFF, bufferSize, midiMapConf), midiOut (false), midiOutChan(MIDI_CHANS[0]) { -#ifdef WITH_VST // init VstEvents stack - freeVstMidiEvents(true); -#endif } @@ -69,28 +73,14 @@ void MidiChannel::copy(const Channel *_src, pthread_mutex_t *pluginMutex) /* -------------------------------------------------------------------------- */ -#ifdef WITH_VST - -void MidiChannel::freeVstMidiEvents(bool init) -{ - midiBuffer.clear(); -} - -#endif - - -/* -------------------------------------------------------------------------- */ - - #ifdef WITH_VST void MidiChannel::addVstMidiEvent(uint32_t msg, int localFrame) { juce::MidiMessage message = juce::MidiMessage( - kernelMidi::getB1(msg), - kernelMidi::getB2(msg), - kernelMidi::getB3(msg)); - + G_KernelMidi.getB1(msg), + G_KernelMidi.getB2(msg), + G_KernelMidi.getB3(msg)); midiBuffer.addEvent(message, localFrame); } @@ -124,7 +114,7 @@ void MidiChannel::quantize(int index, int localFrame, int globalFrame) {} /* -------------------------------------------------------------------------- */ -void MidiChannel::parseAction(recorder::action *a, int localFrame, +void MidiChannel::parseAction(Recorder::action *a, int localFrame, int globalFrame, int quantize, bool mixerIsRunning) { if (a->type == ACTION_MIDI) @@ -156,7 +146,7 @@ void MidiChannel::setMute(bool internal) { mute = true; // internal mute does not exist for midi (for now) if (midiOut) - kernelMidi::send(MIDI_ALL_NOTES_OFF); + G_KernelMidi.send(MIDI_ALL_NOTES_OFF); #ifdef WITH_VST addVstMidiEvent(MIDI_ALL_NOTES_OFF, 0); #endif @@ -177,16 +167,16 @@ void MidiChannel::unsetMute(bool internal) /* -------------------------------------------------------------------------- */ -void MidiChannel::process(float *buffer) +void MidiChannel::process(float *outBuffer, float *inBuffer) { #ifdef WITH_VST pluginHost->processStack(vChan, PluginHost::CHANNEL, this); - freeVstMidiEvents(); #endif + /* TODO - isn't this useful only if WITH_VST ? */ for (int j=0; jiValue | MIDI_CHANS[midiOutChan]); + G_KernelMidi.send(a->iValue | MIDI_CHANS[midiOutChan]); #ifdef WITH_VST addVstMidiEvent(a->iValue, localFrame); @@ -302,7 +292,7 @@ void MidiChannel::sendMidi(uint32_t data) { if (status & (STATUS_PLAY | STATUS_ENDING) && !mute) { if (midiOut) - kernelMidi::send(data | MIDI_CHANS[midiOutChan]); + G_KernelMidi.send(data | MIDI_CHANS[midiOutChan]); #ifdef WITH_VST addVstMidiEvent(data, 0); #endif @@ -316,7 +306,7 @@ void MidiChannel::sendMidi(uint32_t data) void MidiChannel::rewind() { if (midiOut) - kernelMidi::send(MIDI_ALL_NOTES_OFF); + G_KernelMidi.send(MIDI_ALL_NOTES_OFF); #ifdef WITH_VST addVstMidiEvent(MIDI_ALL_NOTES_OFF, 0); #endif @@ -336,3 +326,41 @@ int MidiChannel::writePatch(int i, bool isProject, Patch *patch) return 0; } + + +/* -------------------------------------------------------------------------- */ + + +void MidiChannel::receiveMidi(uint32_t msg) +{ + if (!armed) + return; + while (true) { + if (pthread_mutex_trylock(&G_PluginHost.mutex_midi) != 0) + continue; + gu_log("[Channel::processMidi] msg=%X\n", msg); +#ifdef WITH_VST + addVstMidiEvent(msg, 0); +#endif + pthread_mutex_unlock(&G_PluginHost.mutex_midi); + break; + } + + if (G_Recorder.canRec(this)) + G_Recorder.rec(index, ACTION_MIDI, G_Mixer.actualFrame, msg); +} + + +/* -------------------------------------------------------------------------- */ + + +bool MidiChannel::canInputRec() +{ + return false; // midi channels don't handle input audio +} + + +/* -------------------------------------------------------------------------- */ + + +void MidiChannel::clear() {} diff --git a/src/core/midiChannel.h b/src/core/midiChannel.h index 011ae2f..dcaaa27 100644 --- a/src/core/midiChannel.h +++ b/src/core/midiChannel.h @@ -1,10 +1,10 @@ -/* --------------------------------------------------------------------- +/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * channel * - * --------------------------------------------------------------------- + * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual * @@ -24,7 +24,7 @@ * along with Giada - Your Hardcore Loopmachine. If not, see * . * - * ------------------------------------------------------------------ */ + * -------------------------------------------------------------------------- */ #ifndef MIDI_CHANNEL_H @@ -48,43 +48,41 @@ public: bool midiOut; // enable midi output uint8_t midiOutChan; // midi output channel - void copy(const Channel *src, pthread_mutex_t *pluginMutex); - - void process (float *buffer); - void start (int frame, bool doQuantize, int quantize, bool mixerIsRunning); - void kill (int frame); - void empty (); - void stopBySeq (bool chansStopOnSeqHalt); - void stop (); - void rewind (); - void setMute (bool internal); - void unsetMute (bool internal); - int readPatch_DEPR_ (const char *file, int i, class Patch_DEPR_ *patch, - int samplerate, int rsmpQuality); - int readPatch (const string &basePath, int i, class Patch *patch, - pthread_mutex_t *pluginMutex, int samplerate, int rsmpQuality); - int writePatch (int i, bool isProject, class Patch *patch); - void quantize (int index, int localFrame, int globalFrame); - void onZero (int frame, bool recsStopOnChanHalt); - void onBar (int frame); - void parseAction(recorder::action *a, int localFrame, int globalFrame, - int quantize, bool mixerIsRunning); - - /* ---------------------------------------------------------------- */ + void copy(const Channel *src, pthread_mutex_t *pluginMutex) override; + void clear() override; + void process(float *outBuffer, float *inBuffer) override; + void start(int frame, bool doQuantize, int quantize, bool mixerIsRunning, + bool forceStart, bool isUserGenerated) override; + void kill(int frame) override; + void empty() override; + void stopBySeq(bool chansStopOnSeqHalt) override; + void stop() override; + void rewind() override; + void setMute(bool internal) override; + void unsetMute(bool internal) override; + int readPatch_DEPR_(const char *file, int i, class Patch_DEPR_ *patch, + int samplerate, int rsmpQuality) override; + int readPatch(const string &basePath, int i, class Patch *patch, + pthread_mutex_t *pluginMutex, int samplerate, int rsmpQuality) override; + int writePatch(int i, bool isProject, class Patch *patch) override; + void quantize(int index, int localFrame, int globalFrame) override; + void onZero(int frame, bool recsStopOnChanHalt) override; + void onBar(int frame) override; + void parseAction(Recorder::action *a, int localFrame, int globalFrame, + int quantize, bool mixerIsRunning) override; + void receiveMidi(uint32_t msg) override; + bool canInputRec() override; + + /* ------------------------------------------------------------------------ */ /* sendMidi * send Midi event to the outside world. */ - void sendMidi(recorder::action *a, int localFrame); + void sendMidi(Recorder::action *a, int localFrame); void sendMidi(uint32_t data); -#ifdef WITH_VST - - /* freeVstMidiEvents - * empty vstEvents structure. Init: use the method for channel - * initialization. */ - void freeVstMidiEvents(bool init=false); +#ifdef WITH_VST /* addVstMidiEvent * Add a new Midi event to the midiEvent stack fom a composite uint32_t raw diff --git a/src/core/midiMapConf.cpp b/src/core/midiMapConf.cpp index 41d3706..c2b40eb 100644 --- a/src/core/midiMapConf.cpp +++ b/src/core/midiMapConf.cpp @@ -35,7 +35,7 @@ #include #include "midiMapConf.h" #include "const.h" -#include "../utils/utils.h" +#include "../utils/string.h" #include "../utils/log.h" @@ -46,18 +46,18 @@ using std::vector; void MidiMapConf::init() { - midimapsPath = gGetHomePath() + G_SLASH + "midimaps" + G_SLASH; + midimapsPath = gu_getHomePath() + G_SLASH + "midimaps" + G_SLASH; /* scan dir of midi maps and load the filenames into <>maps. */ - gLog("[MidiMapConf::init] scanning midimaps directory...\n"); + gu_log("[MidiMapConf::init] scanning midimaps directory...\n"); DIR *dp; dirent *ep; dp = opendir(midimapsPath.c_str()); if (!dp) { - gLog("[MidiMapConf::init] unable to scan midimaps directory!\n"); + gu_log("[MidiMapConf::init] unable to scan midimaps directory!\n"); return; } @@ -67,12 +67,12 @@ void MidiMapConf::init() // TODO - check if is a valid midimap file (verify headers) - gLog("[MidiMapConf::init] found midimap '%s'\n", ep->d_name); + gu_log("[MidiMapConf::init] found midimap '%s'\n", ep->d_name); maps.push_back(ep->d_name); } - gLog("[MidiMapConf::init] total midimaps found: %d\n", maps.size()); + gu_log("[MidiMapConf::init] total midimaps found: %d\n", maps.size()); closedir(dp); } @@ -125,16 +125,16 @@ void MidiMapConf::setDefault() int MidiMapConf::read(const string &file) { if (file.empty()) { - gLog("[MidiMapConf::read] midimap not specified, nothing to do\n"); + gu_log("[MidiMapConf::read] midimap not specified, nothing to do\n"); return MIDIMAP_NOT_SPECIFIED; } - gLog("[MidiMapConf::read] reading midimap file '%s'\n", file.c_str()); + gu_log("[MidiMapConf::read] reading midimap file '%s'\n", file.c_str()); string path = midimapsPath + file; jRoot = json_load_file(path.c_str(), 0, &jError); if (!jRoot) { - gLog("[MidiMapConf::read] unreadable midimap file. Error on line %d: %s\n", jError.line, jError.text); + gu_log("[MidiMapConf::read] unreadable midimap file. Error on line %d: %s\n", jError.line, jError.text); return MIDIMAP_UNREADABLE; } @@ -178,7 +178,7 @@ bool MidiMapConf::readInitCommands(json_t *jContainer) json_t *jInitCommand; json_array_foreach(jInitCommands, commandIndex, jInitCommand) { - string indexStr = "init command " + gItoa(commandIndex); + string indexStr = "init command " + gu_itoa(commandIndex); if (!checkObject(jInitCommand, indexStr.c_str())) return 0; @@ -238,7 +238,7 @@ void MidiMapConf::parse(message_t *message) message->value = strtoul(output.c_str(), NULL, 16); - gLog("[MidiMapConf::parse] parsed chan=%d valueStr=%s value=%#x, offset=%d\n", + gu_log("[MidiMapConf::parse] parsed chan=%d valueStr=%s value=%#x, offset=%d\n", message->channel, message->valueStr.c_str(), message->value, message->offset); } @@ -296,16 +296,16 @@ void MidiMapConf::setDefault_DEPR_() int MidiMapConf::readMap_DEPR_(string file) { if (file.empty()) { - gLog("[MidiMapConf::readMap_DEPR_] midimap not specified, nothing to do\n"); + gu_log("[MidiMapConf::readMap_DEPR_] midimap not specified, nothing to do\n"); return MIDIMAP_NOT_SPECIFIED; } - gLog("[MidiMapConf::readMap_DEPR_] reading midimap file '%s'\n", file.c_str()); + gu_log("[MidiMapConf::readMap_DEPR_] reading midimap file '%s'\n", file.c_str()); string path = midimapsPath + file; fp = fopen(path.c_str(), "r"); if (!fp) { - gLog("[MidiMapConf::readMap_DEPR_] unreadable midimap file\n"); + gu_log("[MidiMapConf::readMap_DEPR_] unreadable midimap file\n"); return MIDIMAP_UNREADABLE; } @@ -313,20 +313,20 @@ int MidiMapConf::readMap_DEPR_(string file) device = getValue("device"); if (brand.empty() || device.empty()) { - gLog("[MidiMapConf::readMap_DEPR_] invalid midimap file\n"); + gu_log("[MidiMapConf::readMap_DEPR_] invalid midimap file\n"); return MIDIMAP_INVALID; } - gLog("[MidiMapConf::readMap_DEPR_] reading midimap for %s %s\n", + gu_log("[MidiMapConf::readMap_DEPR_] reading midimap for %s %s\n", brand.c_str(), device.c_str()); /* parse init commands */ vector ic; - gSplit(getValue("init_commands"), ";", &ic); + gu_split(getValue("init_commands"), ";", &ic); for (unsigned i=0; i<(unsigned)MAX_INIT_COMMANDS && i #include "dataStorageIni.h" #include "dataStorageJson.h" -#include "../utils/utils.h" +#include "../utils/fs.h" #if defined(__APPLE__) #include #endif diff --git a/src/core/mixer.cpp b/src/core/mixer.cpp index 5ca38cd..7321f1b 100644 --- a/src/core/mixer.cpp +++ b/src/core/mixer.cpp @@ -27,11 +27,7 @@ * -------------------------------------------------------------------------- */ -#include #include "../utils/log.h" -#include "../utils/gui_utils.h" -#include "mixer.h" -#include "init.h" #include "wave.h" #include "recorder.h" #include "pluginHost.h" @@ -42,9 +38,13 @@ #include "sampleChannel.h" #include "midiChannel.h" #include "kernelMidi.h" +#include "mixer.h" +extern KernelAudio G_KernelAudio; extern Mixer G_Mixer; +extern Recorder G_Recorder; +extern KernelMidi G_KernelMidi; extern MidiMapConf G_MidiMap; extern Patch_DEPR_ G_Patch_DEPR_; extern Conf G_Conf; @@ -62,12 +62,6 @@ Mixer::Mixer() /* -------------------------------------------------------------------------- */ -Mixer::~Mixer() {} - - -/* -------------------------------------------------------------------------- */ - - #define TICKSIZE 38 @@ -102,6 +96,7 @@ void Mixer::init() docross = false; rewindWait = false; running = false; + recording = false; ready = true; waitRec = 0; actualFrame = 0; @@ -120,7 +115,6 @@ void Mixer::init() inVol = DEFAULT_IN_VOL; peakOut = 0.0f; peakIn = 0.0f; - chanInput = NULL; inputTracker = 0; actualBeat = 0; @@ -137,7 +131,7 @@ void Mixer::init() /** TODO - set kernelAudio::realBufsize * 2 as private member */ vChanInput = NULL; - vChanInToOut = (float *) malloc(kernelAudio::realBufsize * 2 * sizeof(float)); + vChanInToOut = (float *) malloc(G_KernelAudio.realBufsize * 2 * sizeof(float)); pthread_mutex_init(&mutex_recs, NULL); pthread_mutex_init(&mutex_chans, NULL); @@ -154,7 +148,7 @@ void Mixer::init() Channel *Mixer::addChannel(int type) { Channel *ch; - int bufferSize = kernelAudio::realBufsize*2; + int bufferSize = G_KernelAudio.realBufsize * 2; if (type == CHANNEL_SAMPLE) ch = new SampleChannel(bufferSize, &G_MidiMap); @@ -175,7 +169,7 @@ Channel *Mixer::addChannel(int type) } ch->index = getNewIndex(); - gLog("[mixer] channel index=%d added, type=%d, total=%d\n", ch->index, ch->type, channels.size()); + gu_log("[mixer] channel index=%d added, type=%d, total=%d\n", ch->index, ch->type, channels.size()); return ch; } @@ -214,7 +208,7 @@ int Mixer::deleteChannel(Channel *ch) } if (index == -1) { - gLog("[Mixer::deleteChannel] unable to find Channel %d for deletion!\n", ch->index); + gu_log("[Mixer::deleteChannel] unable to find Channel %d for deletion!\n", ch->index); return 0; } @@ -228,7 +222,7 @@ int Mixer::deleteChannel(Channel *ch) return 1; } //else - // gLog("[mixer::deleteChannel] waiting for mutex...\n"); + // gu_log("[mixer::deleteChannel] waiting for mutex...\n"); } } @@ -241,7 +235,7 @@ Channel *Mixer::getChannelByIndex(int index) for (unsigned i=0; iindex == index) return channels.at(i); - gLog("[mixer::getChannelByIndex] channel at index %d not found!\n", index); + gu_log("[mixer::getChannelByIndex] channel at index %d not found!\n", index); return NULL; } @@ -253,7 +247,7 @@ void Mixer::sendMIDIsync() { if (G_Conf.midiSync == MIDI_SYNC_CLOCK_M) { if (actualFrame % (framesPerBeat/24) == 0) - kernelMidi::send(MIDI_CLOCK, -1, -1); + G_KernelMidi.send(MIDI_CLOCK, -1, -1); } else if (G_Conf.midiSync == MIDI_SYNC_MTC_M) { @@ -271,10 +265,10 @@ void Mixer::sendMIDIsync() * seconds high nibble */ if (midiTCframes % 2 == 0) { - kernelMidi::send(MIDI_MTC_QUARTER, (midiTCframes & 0x0F) | 0x00, -1); - kernelMidi::send(MIDI_MTC_QUARTER, (midiTCframes >> 4) | 0x10, -1); - kernelMidi::send(MIDI_MTC_QUARTER, (midiTCseconds & 0x0F) | 0x20, -1); - kernelMidi::send(MIDI_MTC_QUARTER, (midiTCseconds >> 4) | 0x30, -1); + G_KernelMidi.send(MIDI_MTC_QUARTER, (midiTCframes & 0x0F) | 0x00, -1); + G_KernelMidi.send(MIDI_MTC_QUARTER, (midiTCframes >> 4) | 0x10, -1); + G_KernelMidi.send(MIDI_MTC_QUARTER, (midiTCseconds & 0x0F) | 0x20, -1); + G_KernelMidi.send(MIDI_MTC_QUARTER, (midiTCseconds >> 4) | 0x30, -1); } /* minutes low nibble @@ -283,10 +277,10 @@ void Mixer::sendMIDIsync() * hours high nibble SMPTE frame rate */ else { - kernelMidi::send(MIDI_MTC_QUARTER, (midiTCminutes & 0x0F) | 0x40, -1); - kernelMidi::send(MIDI_MTC_QUARTER, (midiTCminutes >> 4) | 0x50, -1); - kernelMidi::send(MIDI_MTC_QUARTER, (midiTChours & 0x0F) | 0x60, -1); - kernelMidi::send(MIDI_MTC_QUARTER, (midiTChours >> 4) | 0x70, -1); + G_KernelMidi.send(MIDI_MTC_QUARTER, (midiTCminutes & 0x0F) | 0x40, -1); + G_KernelMidi.send(MIDI_MTC_QUARTER, (midiTCminutes >> 4) | 0x50, -1); + G_KernelMidi.send(MIDI_MTC_QUARTER, (midiTChours & 0x0F) | 0x60, -1); + G_KernelMidi.send(MIDI_MTC_QUARTER, (midiTChours >> 4) | 0x70, -1); } midiTCframes++; @@ -305,7 +299,7 @@ void Mixer::sendMIDIsync() midiTCminutes = 0; } } - //gLog("%d:%d:%d:%d\n", midiTChours, midiTCminutes, midiTCseconds, midiTCframes); + //gu_log("%d:%d:%d:%d\n", midiTChours, midiTCminutes, midiTCseconds, midiTCframes); } } } @@ -328,250 +322,64 @@ void Mixer::sendMIDIrewind() * SMPTE time in one message */ if (G_Conf.midiSync == MIDI_SYNC_MTC_M) { - kernelMidi::send(MIDI_SYSEX, 0x7F, 0x00); // send msg on channel 0 - kernelMidi::send(0x01, 0x01, 0x00); // hours 0 - kernelMidi::send(0x00, 0x00, 0x00); // mins, secs, frames 0 - kernelMidi::send(MIDI_EOX, -1, -1); // end of sysex + G_KernelMidi.send(MIDI_SYSEX, 0x7F, 0x00); // send msg on channel 0 + G_KernelMidi.send(0x01, 0x01, 0x00); // hours 0 + G_KernelMidi.send(0x00, 0x00, 0x00); // mins, secs, frames 0 + G_KernelMidi.send(MIDI_EOX, -1, -1); // end of sysex } } /* -------------------------------------------------------------------------- */ -int Mixer::masterPlay( - void *out_buf, void *in_buf, unsigned n_frames, +int Mixer::masterPlay(void *outBuf, void *inBuf, unsigned bufferSize, double streamTime, RtAudioStreamStatus status, void *userData) { - return G_Mixer.__masterPlay(out_buf, in_buf, n_frames); + return G_Mixer.__masterPlay(outBuf, inBuf, bufferSize); } /* -------------------------------------------------------------------------- */ -int Mixer::__masterPlay(void *out_buf, void *in_buf, unsigned bufferFrames) +int Mixer::__masterPlay(void *_outBuf, void *_inBuf, unsigned bufferSize) { if (!ready) return 0; - float *outBuf = ((float *) out_buf); - float *inBuf = ((float *) in_buf); - bufferFrames *= 2; // stereo + float *outBuf = (float *) _outBuf; + float *inBuf = G_KernelAudio.inputEnabled ? (float *) _inBuf : nullptr; + bufferSize *= 2; // stereo peakOut = 0.0f; // reset peak calculator peakIn = 0.0f; // reset peak calculator - /* always clean each buffer */ - - memset(outBuf, 0, sizeof(float) * bufferFrames); // out - memset(vChanInToOut, 0, sizeof(float) * bufferFrames); // inToOut vChan - - pthread_mutex_lock(&mutex_chans); - for (unsigned i=0; itype == CHANNEL_SAMPLE) - ((SampleChannel*)channels.at(i))->clear(); - pthread_mutex_unlock(&mutex_chans); - - for (unsigned j=0; j peakIn) - peakIn = inBuf[j] * inVol; - - /* "hear what you're playing" - process, copy and paste the input buffer - * onto the output buffer */ - - if (inToOut) { - vChanInToOut[j] = inBuf[j] * inVol; - vChanInToOut[j+1] = inBuf[j+1] * inVol; - } - } - - /* operations to do if the sequencer is running: - * - compute quantizer - * - time check for LOOP_REPEAT - * - reset loops at beat 0 - * - read recorded actions - * - reset actualFrame */ + clearAllBuffers(outBuf, bufferSize); + for (unsigned j=0; j= totalFrames) - inputTracker = 0; - } - } - - /* quantizer computations: quantize rewind and all channels. */ - - if (quantize > 0 && quanto > 0) { - if (actualFrame % (quanto) == 0) { // is quanto! - if (rewindWait) { - rewindWait = false; - rewind(); - } - pthread_mutex_lock(&mutex_chans); - for (unsigned k=0; kquantize(k, j, actualFrame); // j == localFrame - pthread_mutex_unlock(&mutex_chans); - } - } - - /* reset LOOP_REPEAT, if a bar has passed */ - - if (actualFrame % framesPerBar == 0 && actualFrame != 0) { - if (metronome) - tickPlay = true; - - pthread_mutex_lock(&mutex_chans); - for (unsigned k=0; konBar(j); - pthread_mutex_unlock(&mutex_chans); - } - - /* reset loops on beat 0 */ - - if (actualFrame == 0) { - pthread_mutex_lock(&mutex_chans); - for (unsigned k=0; konZero(j, G_Conf.recsStopOnChanHalt); - pthread_mutex_unlock(&mutex_chans); - } - - /* reading all actions recorded */ - - pthread_mutex_lock(&mutex_recs); - for (unsigned y=0; ychan; - Channel *ch = getChannelByIndex(index); - ch->parseAction(recorder::global.at(y).at(z), j, actualFrame, quantize, running); - } - break; - } - } - pthread_mutex_unlock(&mutex_recs); - - /* increase actualFrame */ - + lineInRec(inBuf, j); + doQuantize(j); + testBar(j); + testFirstBeat(j); + readActions(j); actualFrame += 2; - - /* if actualFrame > totalFrames the sequencer returns to frame 0, - * beat 0. This must be the last operation. */ - - if (actualFrame > totalFrames) { - actualFrame = 0; - actualBeat = 0; - } - else - if (actualFrame % framesPerBeat == 0 && actualFrame > 0) { - actualBeat++; - - /* avoid tick and tock to overlap when a new bar has passed (which - * is also a beat) */ - - if (metronome && !tickPlay) - tockPlay = true; - } - + testLastBeat(); // this test must be the last one sendMIDIsync(); - - } // if (running) - - /* sum channels, CHANNEL_SAMPLE only */ - - pthread_mutex_lock(&mutex_chans); - for (unsigned k=0; ktype == CHANNEL_SAMPLE) - ((SampleChannel*)channels.at(k))->sum(j, running); } - pthread_mutex_unlock(&mutex_chans); - - /* metronome play */ - /** FIXME - move this one after the peak meter calculation */ - - if (tockPlay) { - outBuf[j] += tock[tockTracker]; - outBuf[j+1] += tock[tockTracker]; - tockTracker++; - if (tockTracker >= TICKSIZE-1) { - tockPlay = false; - tockTracker = 0; - } - } - if (tickPlay) { - outBuf[j] += tick[tickTracker]; - outBuf[j+1] += tick[tickTracker]; - tickTracker++; - if (tickTracker >= TICKSIZE-1) { - tickPlay = false; - tickTracker = 0; - } - } - } // end loop J - - - /* final loop: sum virtual channels and process plugins. */ - - pthread_mutex_lock(&mutex_chans); - for (unsigned k=0; kprocess(outBuf); - pthread_mutex_unlock(&mutex_chans); - - /* processing fxs master in & out, if any. */ - -#ifdef WITH_VST - pthread_mutex_lock(&mutex_plugins); - G_PluginHost.processStack(outBuf, PluginHost::MASTER_OUT); - G_PluginHost.processStack(vChanInToOut, PluginHost::MASTER_IN); - pthread_mutex_unlock(&mutex_plugins); -#endif - - /* post processing master fx + peak calculation. */ - - for (unsigned j=0; j peakOut) - peakOut = outBuf[j]; + renderIO(outBuf, inBuf); - if (G_Conf.limitOutput) { - if (outBuf[j] > 1.0f) - outBuf[j] = 1.0f; - else if (outBuf[j] < -1.0f) - outBuf[j] = -1.0f; + /* post processing */ - if (outBuf[j+1] > 1.0f) - outBuf[j+1] = 1.0f; - else if (outBuf[j+1] < -1.0f) - outBuf[j+1] = -1.0f; - } + for (unsigned j=0; jtype == CHANNEL_MIDI) + continue; + SampleChannel *ch = (SampleChannel*) channels.at(i); + if (ch->armed) + memcpy(ch->wave->data, vChanInput, totalFrames * sizeof(float)); + } + memset(vChanInput, 0, totalFrames * sizeof(float)); // clear vchan +} + + +/* -------------------------------------------------------------------------- */ + + +void Mixer::lineInRec(float *inBuf, unsigned frame) +{ + if (!mh_hasArmedSampleChannels() || !G_KernelAudio.inputEnabled || !recording) + return; + + /* Delay comp: wait until waitRec reaches delayComp. WaitRec + * returns to 0 in mixerHandler, as soon as the recording ends */ + + if (waitRec < G_Conf.delayComp) { + waitRec += 2; + return; + } + + vChanInput[inputTracker] += inBuf[frame] * inVol; + vChanInput[inputTracker+1] += inBuf[frame+1] * inVol; + inputTracker += 2; + if (inputTracker >= totalFrames) + inputTracker = 0; +} + + +/* -------------------------------------------------------------------------- */ + + +void Mixer::processLineIn(float *inBuf, unsigned frame) +{ + if (!G_KernelAudio.inputEnabled) + return; + + /* input peak calculation (left chan only so far). */ + + if (inBuf[frame] * inVol > peakIn) + peakIn = inBuf[frame] * inVol; + + /* "hear what you're playing" - process, copy and paste the input buffer + * onto the output buffer */ + + if (inToOut) { + vChanInToOut[frame] = inBuf[frame] * inVol; + vChanInToOut[frame+1] = inBuf[frame+1] * inVol; + } +} + + +/* -------------------------------------------------------------------------- */ + + +void Mixer::readActions(unsigned frame) +{ + pthread_mutex_lock(&mutex_recs); + for (unsigned i=0; ichan; + Channel *ch = getChannelByIndex(index); + ch->parseAction(G_Recorder.global.at(i).at(j), frame, actualFrame, quantize, running); + } + break; + } } - else { + pthread_mutex_unlock(&mutex_recs); +} + + +/* -------------------------------------------------------------------------- */ + + +void Mixer::doQuantize(unsigned frame) +{ + if (quantize < 0 || quanto <= 0) // if quantizer disabled + return; + if (actualFrame % (quanto) != 0) // if a quanto has not passed yet + return; + + if (rewindWait) { + rewindWait = false; + rewind(); + } + pthread_mutex_lock(&mutex_chans); + for (unsigned i=0; iquantize(i, frame, actualFrame); // j == localFrame + pthread_mutex_unlock(&mutex_chans); +} + + +/* -------------------------------------------------------------------------- */ + + +void Mixer::testBar(unsigned frame) +{ + if (actualFrame % framesPerBar != 0 || actualFrame == 0) + return; + + if (metronome) + tickPlay = true; + + pthread_mutex_lock(&mutex_chans); + for (unsigned k=0; konBar(frame); + pthread_mutex_unlock(&mutex_chans); +} + + +/* -------------------------------------------------------------------------- */ + + +void Mixer::testFirstBeat(unsigned frame) +{ + if (actualFrame != 0) + return; + pthread_mutex_lock(&mutex_chans); + for (unsigned k=0; konZero(frame, G_Conf.recsStopOnChanHalt); + pthread_mutex_unlock(&mutex_chans); +} + + +/* -------------------------------------------------------------------------- */ + + +void Mixer::testLastBeat() +{ + /* if actualFrame > totalFrames the sequencer returns to frame 0, + * beat 0. This must be the last operation. */ + + if (actualFrame > totalFrames) { + actualFrame = 0; + actualBeat = 0; + } + else + if (actualFrame % framesPerBeat == 0 && actualFrame > 0) { + actualBeat++; + + /* avoid tick and tock to overlap when a new bar has passed (which + * is also a beat) */ + + if (metronome && !tickPlay) + tockPlay = true; + } +} + + +/* -------------------------------------------------------------------------- */ + + +void Mixer::sumChannels(unsigned frame) +{ + pthread_mutex_lock(&mutex_chans); + for (unsigned k=0; ktype == CHANNEL_SAMPLE) + ((SampleChannel*)channels.at(k))->sum(frame, running); + } + pthread_mutex_unlock(&mutex_chans); +} + + +/* -------------------------------------------------------------------------- */ + + +void Mixer::renderMetronome(float *outBuf, unsigned frame) +{ + if (tockPlay) { + outBuf[frame] += tock[tockTracker]; + outBuf[frame+1] += tock[tockTracker]; + tockTracker++; + if (tockTracker >= TICKSIZE-1) { + tockPlay = false; + tockTracker = 0; + } + } + if (tickPlay) { + outBuf[frame] += tick[tickTracker]; + outBuf[frame+1] += tick[tickTracker]; + tickTracker++; + if (tickTracker >= TICKSIZE-1) { + tickPlay = false; + tickTracker = 0; + } + } +} + + +/* -------------------------------------------------------------------------- */ + + +void Mixer::renderIO(float *outBuf, float *inBuf) +{ + pthread_mutex_lock(&mutex_chans); + for (unsigned k=0; kprocess(outBuf, inBuf); + pthread_mutex_unlock(&mutex_chans); + #ifdef WITH_VST - G_PluginHost.processStackOffline(vChanInput, PluginHost::MASTER_IN, 0, totalFrames); + pthread_mutex_lock(&mutex_plugins); + G_PluginHost.processStack(outBuf, PluginHost::MASTER_OUT); + G_PluginHost.processStack(vChanInToOut, PluginHost::MASTER_IN); + pthread_mutex_unlock(&mutex_plugins); #endif - int numFrames = totalFrames*sizeof(float); - memcpy(chanInput->wave->data, vChanInput, numFrames); - memset(vChanInput, 0, numFrames); // clear vchan - return true; +} + + +/* -------------------------------------------------------------------------- */ + + +void Mixer::computePeak(float *outBuf, unsigned frame) +{ + /* TODO it takes into account only left channel so far! */ + if (outBuf[frame] > peakOut) + peakOut = outBuf[frame]; +} + + +/* -------------------------------------------------------------------------- */ + + +void Mixer::limitOutput(float *outBuf, unsigned frame) +{ + if (outBuf[frame] > 1.0f) + outBuf[frame] = 1.0f; + else + if (outBuf[frame] < -1.0f) + outBuf[frame] = -1.0f; + + if (outBuf[frame+1] > 1.0f) + outBuf[frame+1] = 1.0f; + else + if (outBuf[frame+1] < -1.0f) + outBuf[frame+1] = -1.0f; +} + + +/* -------------------------------------------------------------------------- */ + + +void Mixer::finalizeOutput(float *outBuf, unsigned frame) +{ + /* merge vChanInToOut, if enabled */ + + if (inToOut) { + outBuf[frame] += vChanInToOut[frame]; + outBuf[frame+1] += vChanInToOut[frame+1]; } + outBuf[frame] *= outVol; + outBuf[frame+1] *= outVol; +} + + +/* -------------------------------------------------------------------------- */ + + +void Mixer::clearAllBuffers(float *outBuf, unsigned bufferSize) +{ + memset(outBuf, 0, sizeof(float) * bufferSize); // out + memset(vChanInToOut, 0, sizeof(float) * bufferSize); // inToOut vChan + + pthread_mutex_lock(&mutex_chans); + for (unsigned i=0; iclear(); + pthread_mutex_unlock(&mutex_chans); } diff --git a/src/core/mixer.h b/src/core/mixer.h index 8387efd..f8afb46 100644 --- a/src/core/mixer.h +++ b/src/core/mixer.h @@ -30,15 +30,10 @@ #ifndef MIXER_H #define MIXER_H -#include + #include #include -#include "const.h" #include "kernelAudio.h" -#include "../utils/utils.h" - - -using std::vector; class Mixer @@ -46,7 +41,6 @@ class Mixer public: Mixer(); - ~Mixer(); void init(); int close(); @@ -64,11 +58,9 @@ public: /* masterPlay * core method (callback) */ - static int masterPlay( - void *out_buf, void *in_buf, unsigned n_frames, - double streamTime, RtAudioStreamStatus status, void *userData - ); - int __masterPlay(void *out_buf, void *in_buf, unsigned n_frames); + static int masterPlay(void *outBuf, void *inBuf, unsigned bufferSize, + double streamTime, RtAudioStreamStatus status, void *userData); + int __masterPlay(void *outBuf, void *inBuf, unsigned bufferSize); /* updateFrameBars * updates bpm, frames, beats and so on. */ @@ -104,7 +96,7 @@ public: * memcpy the virtual channel input in the channel designed for input * recording. Called by mixerHandler on stopInputRec() */ - bool mergeVirtualInput(); + void mergeVirtualInput(); /* getChannelByIndex * return channel with given index 'i'. */ @@ -131,9 +123,10 @@ public: XFADE = 0x02 }; - vector channels; + std::vector channels; bool running; + bool recording; // is recording something? bool ready; float *vChanInput; // virtual channel for recording float *vChanInToOut; // virtual channel in->out bridge (hear what you're playin) @@ -166,11 +159,6 @@ public: int tickTracker, tockTracker; bool tickPlay, tockPlay; // 1 = play, 0 = stop - /* chanInput - * the active channel during a recording. NULL = no channels active */ - - class SampleChannel *chanInput; - /* inputTracker * position of the sample in the input side (recording) */ @@ -210,6 +198,71 @@ private: * the slave */ void sendMIDIrewind(); + + /* lineInRec + Records from line in. */ + + void lineInRec(float *inBuf, unsigned frame); + + /* ProcessLineIn + Computes line in peaks, plus handles "hear what you're playin'" thing. */ + + void processLineIn(float *inBuf, unsigned frame); + + /* clearAllBuffers + Cleans up every buffer, both in Mixer and in channels. */ + + void clearAllBuffers(float *outBuf, unsigned bufferSize); + + /* readActions + Reads all recorded actions. */ + + void readActions(unsigned frame); + + /* doQuantize + Computes quantization on 'rewind' button and all channels. */ + + void doQuantize(unsigned frame); + + /* sumChannels + Sums channels, i.e. lets them add sample frames to their virtual channels. + This is required for CHANNEL_SAMPLE only */ + + void sumChannels(unsigned frame); + + /* renderMetronome + Generates metronome when needed and pastes it to the output buffer. */ + + void renderMetronome(float *outBuf, unsigned frame); + + /* renderIO + Final processing stage. Take each channel and process it (i.e. copy its + content to the output buffer). Process plugins too, if any. */ + + void renderIO(float *outBuf, float *inBuf); + + /* limitOutput + Applies a very dumb hard limiter. */ + + void limitOutput(float *outBuf, unsigned frame); + + /* computePeak */ + + void computePeak(float *outBuf, unsigned frame); + + /* finalizeOutput + Last touches after the output has been rendered: apply inToOut if any, apply + output volume. */ + + void finalizeOutput(float *outBuf, unsigned frame); + + /* test* + Checks if the sequencer has reached a specific point (bar, first beat or + last frame). */ + + void testBar(unsigned frame); + void testFirstBeat(unsigned frame); + void testLastBeat(); }; #endif diff --git a/src/core/mixerHandler.cpp b/src/core/mixerHandler.cpp index f965e02..e3d1945 100644 --- a/src/core/mixerHandler.cpp +++ b/src/core/mixerHandler.cpp @@ -34,9 +34,10 @@ #endif #include -#include "../utils/utils.h" +#include "../utils/fs.h" +#include "../utils/string.h" #include "../utils/log.h" -#include "../glue/glue.h" +#include "../glue/main.h" #include "../glue/channel.h" #include "mixerHandler.h" #include "kernelMidi.h" @@ -201,83 +202,90 @@ void mh_rewindSequencer() /* -------------------------------------------------------------------------- */ -SampleChannel *mh_startInputRec() +bool mh_startInputRec() { - /* search for the next available channel */ + int channelsReady = 0; - SampleChannel *chan = NULL; for (unsigned i=0; itype == CHANNEL_SAMPLE) - if (((SampleChannel*) G_Mixer.channels.at(i))->canInputRec()) { - chan = (SampleChannel*) G_Mixer.channels.at(i); - break; - } - } - /* no chans available? */ + if (!G_Mixer.channels.at(i)->canInputRec()) + continue; - if (chan == NULL) - return NULL; + SampleChannel *ch = (SampleChannel*) G_Mixer.channels.at(i); - Wave *w = new Wave(); - if (!w->allocEmpty(G_Mixer.totalFrames, G_Conf.samplerate)) - return NULL; + /* Allocate empty sample for the current channel. */ + + if (!ch->allocEmpty(G_Mixer.totalFrames, G_Conf.samplerate, G_Patch.lastTakeId)) + { + gu_log("[mh_startInputRec] unable to allocate new Wave in chan %d!\n", + ch->index); + continue; + } + + /* Increase lastTakeId until the sample name TAKE-[n] is unique */ + + while (!mh_uniqueSampleName(ch, ch->wave->name)) { + G_Patch_DEPR_.lastTakeId++; + G_Patch.lastTakeId++; + ch->wave->name = "TAKE-" + gu_itoa(G_Patch.lastTakeId); + } - /* increase lastTakeId until the sample name TAKE-[n] is unique */ + gu_log("[mh_startInputRec] start input recs using chan %d with size %d " + "frame=%d\n", ch->index, G_Mixer.totalFrames, G_Mixer.inputTracker); - char name[32]; - sprintf(name, "TAKE-%d", G_Patch_DEPR_.lastTakeId); - while (!mh_uniqueSamplename(chan, name)) { - G_Patch_DEPR_.lastTakeId++; - G_Patch.lastTakeId++; - sprintf(name, "TAKE-%d", G_Patch_DEPR_.lastTakeId); + channelsReady++; } - chan->allocEmpty(G_Mixer.totalFrames, G_Conf.samplerate, G_Patch_DEPR_.lastTakeId); - G_Mixer.chanInput = chan; + if (channelsReady > 0) { + G_Mixer.recording = true; + /* start to write from the actualFrame, not the beginning */ + /** FIXME: this should be done before wave allocation */ + G_Mixer.inputTracker = G_Mixer.actualFrame; + return true; + } + return false; +} - /* start to write from the actualFrame, not the beginning */ - /** FIXME: move this before wave allocation*/ - G_Mixer.inputTracker = G_Mixer.actualFrame; +/* -------------------------------------------------------------------------- */ - gLog( - "[mh] start input recs using chan %d with size %d, frame=%d\n", - chan->index, G_Mixer.totalFrames, G_Mixer.inputTracker - ); - return chan; +void mh_stopInputRec() +{ + G_Mixer.mergeVirtualInput(); + G_Mixer.recording = false; + G_Mixer.waitRec = 0; // in case delay compensation is in use + gu_log("[mh] stop input recs\n"); } /* -------------------------------------------------------------------------- */ -SampleChannel *mh_stopInputRec() +bool mh_hasArmedSampleChannels() { - gLog("[mh] stop input recs\n"); - G_Mixer.mergeVirtualInput(); - SampleChannel *ch = G_Mixer.chanInput; - G_Mixer.chanInput = NULL; - G_Mixer.waitRec = 0; // if delay compensation is in use - return ch; + for (unsigned i=0; itype == CHANNEL_SAMPLE && ch->armed) + return true; + } + return false; } /* -------------------------------------------------------------------------- */ -bool mh_uniqueSamplename(SampleChannel *ch, const char *name) +bool mh_uniqueSampleName(SampleChannel *ch, const string &name) { for (unsigned i=0; itype == CHANNEL_SAMPLE) { - SampleChannel *other = (SampleChannel*) G_Mixer.channels.at(i); - if (other->wave != NULL) - if (!strcmp(name, other->wave->name.c_str())) - return false; - } - } + if (ch == G_Mixer.channels.at(i)) // skip itself + continue; + if (G_Mixer.channels.at(i)->type != CHANNEL_SAMPLE) + continue; + SampleChannel *other = (SampleChannel*) G_Mixer.channels.at(i); + if (other->wave != NULL && name == other->wave->name) + return false; } return true; } diff --git a/src/core/mixerHandler.h b/src/core/mixerHandler.h index c624d26..9c33b1c 100644 --- a/src/core/mixerHandler.h +++ b/src/core/mixerHandler.h @@ -31,12 +31,7 @@ #define MIXERHANDLER_H -#include -#include "recorder.h" -#include "patch.h" - - -using std::vector; +#include /* stopSequencer @@ -64,14 +59,20 @@ void mh_readPatch(); * the chan number chosen, otherwise -1 if there are no more empty * channels available. */ -SampleChannel *mh_startInputRec(); +bool mh_startInputRec(); -SampleChannel *mh_stopInputRec(); +void mh_stopInputRec(); /* uniqueSamplename * return true if samplename 'n' is unique. Requires SampleChannel *ch * in order to skip check against itself. */ -bool mh_uniqueSamplename(class SampleChannel *ch, const char *name); +bool mh_uniqueSampleName(class SampleChannel *ch, const std::string &s); + +/* hasArmedSampleChannels +Tells whether Mixer has one or more sample channels armed for input +recording. */ + +bool mh_hasArmedSampleChannels(); #endif diff --git a/src/core/patch.cpp b/src/core/patch.cpp index 1cc2653..dbc1aa7 100644 --- a/src/core/patch.cpp +++ b/src/core/patch.cpp @@ -27,24 +27,16 @@ * -------------------------------------------------------------------------- */ -#include #include "../utils/log.h" -#include "../utils/utils.h" -#include "../gui/dialogs/gd_mainWindow.h" -#include "../gui/elems/ge_keyboard.h" -#include "patch.h" +#include "../utils/string.h" #include "const.h" -#include "init.h" -#include "recorder.h" #include "conf.h" -#include "wave.h" #include "mixer.h" -#include "channel.h" +#include "patch.h" -extern Mixer G_Mixer; -extern Conf G_Conf; -extern gdMainWindow *mainWin; +extern Mixer G_Mixer; +extern Conf G_Conf; void Patch::init() @@ -77,7 +69,7 @@ int Patch::write(const string &file) #endif if (json_dump_file(jRoot, file.c_str(), JSON_COMPACT) != 0) { - gLog("[Patch::write] unable to write patch file!\n"); + gu_log("[Patch::write] unable to write patch file!\n"); return 0; } return 1; @@ -91,7 +83,7 @@ int Patch::read(const string &file) { jRoot = json_load_file(file.c_str(), 0, &jError); if (!jRoot) { - gLog("[Patch::read] unable to read patch file! Error on line %d: %s\n", jError.line, jError.text); + gu_log("[Patch::read] unable to read patch file! Error on line %d: %s\n", jError.line, jError.text); return PATCH_UNREADABLE; } @@ -226,6 +218,7 @@ void Patch::writeChannels(json_t *jContainer, vector *channels) json_object_set_new(jChannel, PATCH_KEY_CHANNEL_MIDI_IN_KEYPRESS, json_integer(channel.midiInKeyPress)); json_object_set_new(jChannel, PATCH_KEY_CHANNEL_MIDI_IN_KEYREL, json_integer(channel.midiInKeyRel)); json_object_set_new(jChannel, PATCH_KEY_CHANNEL_MIDI_IN_KILL, json_integer(channel.midiInKill)); + json_object_set_new(jChannel, PATCH_KEY_CHANNEL_MIDI_IN_ARM, json_integer(channel.midiInArm)); json_object_set_new(jChannel, PATCH_KEY_CHANNEL_MIDI_IN_VOLUME, json_integer(channel.midiInVolume)); json_object_set_new(jChannel, PATCH_KEY_CHANNEL_MIDI_IN_MUTE, json_integer(channel.midiInMute)); json_object_set_new(jChannel, PATCH_KEY_CHANNEL_MIDI_IN_SOLO, json_integer(channel.midiInSolo)); @@ -296,7 +289,7 @@ bool Patch::readColumns(json_t *jContainer) json_t *jColumn; json_array_foreach(jColumns, columnIndex, jColumn) { - string columnIndexStr = "column " + gItoa(columnIndex); + string columnIndexStr = "column " + gu_itoa(columnIndex); if (!checkObject(jColumn, columnIndexStr.c_str())) return 0; @@ -323,7 +316,7 @@ bool Patch::readChannels(json_t *jContainer) json_t *jChannel; json_array_foreach(jChannels, channelIndex, jChannel) { - string channelIndexStr = "channel " + gItoa(channelIndex); + string channelIndexStr = "channel " + gu_itoa(channelIndex); if (!checkObject(jChannel, channelIndexStr.c_str())) return 0; @@ -342,6 +335,7 @@ bool Patch::readChannels(json_t *jContainer) if (!setUint32(jChannel, PATCH_KEY_CHANNEL_MIDI_IN_KEYPRESS, channel.midiInKeyPress)) return 0; if (!setUint32(jChannel, PATCH_KEY_CHANNEL_MIDI_IN_KEYREL, channel.midiInKeyRel)) return 0; if (!setUint32(jChannel, PATCH_KEY_CHANNEL_MIDI_IN_KILL, channel.midiInKill)) return 0; + if (!setUint32(jChannel, PATCH_KEY_CHANNEL_MIDI_IN_ARM, channel.midiInArm)) return 0; if (!setUint32(jChannel, PATCH_KEY_CHANNEL_MIDI_IN_VOLUME, channel.midiInVolume)) return 0; if (!setUint32(jChannel, PATCH_KEY_CHANNEL_MIDI_IN_MUTE, channel.midiInMute)) return 0; if (!setUint32(jChannel, PATCH_KEY_CHANNEL_MIDI_IN_SOLO, channel.midiInSolo)) return 0; diff --git a/src/core/patch.h b/src/core/patch.h index ba1cd33..99ef6a1 100644 --- a/src/core/patch.h +++ b/src/core/patch.h @@ -34,9 +34,7 @@ #include #include #include -#include "../utils/utils.h" #include "dataStorageJson.h" -#include "const.h" using std::string; @@ -79,6 +77,7 @@ public: uint32_t midiInKeyPress; uint32_t midiInKeyRel; uint32_t midiInKill; + uint32_t midiInArm; uint32_t midiInVolume; uint32_t midiInMute; uint32_t midiInSolo; diff --git a/src/core/patch_DEPR_.cpp b/src/core/patch_DEPR_.cpp index 7c30284..99010a0 100644 --- a/src/core/patch_DEPR_.cpp +++ b/src/core/patch_DEPR_.cpp @@ -29,7 +29,7 @@ #include #include "../utils/log.h" -#include "../utils/utils.h" +#include "../utils/fs.h" #include "../gui/dialogs/gd_mainWindow.h" #include "../gui/elems/ge_keyboard.h" #include "patch_DEPR_.h" @@ -43,8 +43,9 @@ #include "channel.h" -extern Mixer G_Mixer; -extern Conf G_Conf; +extern Mixer G_Mixer; +extern Conf G_Conf; +extern Recorder G_Recorder; #ifdef WITH_VST extern PluginHost G_PluginHost; #endif @@ -61,7 +62,7 @@ int Patch_DEPR_::open(const char *file) return PATCH_INVALID; version = atof(getValue("versionf").c_str()); - gLog("[patch_DEPR_] open patch version %f\n", version); + gu_log("[patch_DEPR_] open patch version %f\n", version); return PATCH_READ_OK; } @@ -469,7 +470,7 @@ uint32_t Patch_DEPR_::getMidiValue(int i, const char *c) int Patch_DEPR_::readRecs() { - gLog("[patch_DEPR_] Reading recs...\n"); + gu_log("[patch_DEPR_] Reading recs...\n"); unsigned numrecs = atoi(getValue("numrecs").c_str()); @@ -482,7 +483,7 @@ int Patch_DEPR_::readRecs() sprintf(tmpbuf, "recframe%d", i); sscanf(getValue(tmpbuf).c_str(), "%d %d", &frame, &recPerFrame); -//gLog("processing frame=%d, recPerFrame=%d\n", frame, recPerFrame); +//gu_log("processing frame=%d, recPerFrame=%d\n", frame, recPerFrame); for (int k=0; kstatus & ~(STATUS_WRONG | STATUS_MISSING | STATUS_EMPTY)) { if (version < 0.83f) - recorder::rec(ch->index, type, frame, iValue_fix, fValue); + G_Recorder.rec(ch->index, type, frame, iValue_fix, fValue); else - recorder::rec(ch->index, type, frame, iValue, fValue); + G_Recorder.rec(ch->index, type, frame, iValue, fValue); } } } @@ -526,7 +527,7 @@ int Patch_DEPR_::readRecs() #ifdef WITH_VST int Patch_DEPR_::readPlugins() { - gLog("[patch_DEPR_] Reading plugins...\n"); + gu_log("[patch_DEPR_] Reading plugins...\n"); int globalOut = 1; diff --git a/src/core/plugin.cpp b/src/core/plugin.cpp index 740bb70..bf4a1c4 100644 --- a/src/core/plugin.cpp +++ b/src/core/plugin.cpp @@ -49,16 +49,16 @@ void Plugin::init() status = 1; if (getActiveEditor() != NULL) { - gLog("[Plugin::init] plugin has an already active editor!\n"); + gu_log("[Plugin::init] plugin has an already active editor!\n"); return; } ui = createEditorIfNeeded(); if (ui == NULL) { - gLog("[Plugin::init] unable to create editor, the plugin might be GUI-less!\n"); + gu_log("[Plugin::init] unable to create editor, the plugin might be GUI-less!\n"); return; } - gLog("[Plugin::init] editor initialized and ready\n"); + gu_log("[Plugin::init] editor initialized and ready\n"); } @@ -68,7 +68,7 @@ void Plugin::init() void Plugin::showEditor(void *parent) { if (ui == NULL) { - gLog("[Plugin::showEditor] can't show editor!\n"); + gu_log("[Plugin::showEditor] can't show editor!\n"); return; } ui->setOpaque(true); diff --git a/src/core/pluginHost.cpp b/src/core/pluginHost.cpp index 9f31f2e..f95a966 100644 --- a/src/core/pluginHost.cpp +++ b/src/core/pluginHost.cpp @@ -31,10 +31,8 @@ #include "../utils/log.h" -#include "../utils/utils.h" -#include "mixer.h" +#include "const.h" #include "channel.h" -#include "midiChannel.h" #include "plugin.h" #include "pluginHost.h" @@ -53,7 +51,7 @@ PluginHost::~PluginHost() void PluginHost::init(int _buffersize, int _samplerate) { - gLog("[PluginHost::init] initialize with buffersize=%d, samplerate=%d\n", + gu_log("[PluginHost::init] initialize with buffersize=%d, samplerate=%d\n", _buffersize, _samplerate); messageManager = juce::MessageManager::getInstance(); @@ -62,7 +60,9 @@ void PluginHost::init(int _buffersize, int _samplerate) buffersize = _buffersize; missingPlugins = false; //unknownPluginList.empty(); - loadList(gGetHomePath() + G_SLASH + "plugins.xml"); + loadList(gu_getHomePath() + G_SLASH + "plugins.xml"); + + pthread_mutex_init(&mutex_midi, NULL); } @@ -72,8 +72,8 @@ void PluginHost::init(int _buffersize, int _samplerate) int PluginHost::scanDir(const string &dirpath, void (*callback)(float progress, void *p), void *p) { - gLog("[PluginHost::scanDir] requested directory: '%s'\n", dirpath.c_str()); - gLog("[PluginHost::scanDir] current plugins: %d\n", knownPluginList.getNumTypes()); + gu_log("[PluginHost::scanDir] requested directory: '%s'\n", dirpath.c_str()); + gu_log("[PluginHost::scanDir] current plugins: %d\n", knownPluginList.getNumTypes()); knownPluginList.clear(); // clear up previous plugins @@ -85,13 +85,13 @@ int PluginHost::scanDir(const string &dirpath, void (*callback)(float progress, bool cont = true; juce::String name; while (cont) { - gLog("[PluginHost::scanDir] scanning '%s'\n", name.toRawUTF8()); + gu_log("[PluginHost::scanDir] scanning '%s'\n", name.toRawUTF8()); cont = scanner.scanNextFile(false, name); if (callback) callback(scanner.getProgress(), p); } - gLog("[PluginHost::scanDir] %d plugin(s) found\n", knownPluginList.getNumTypes()); + gu_log("[PluginHost::scanDir] %d plugin(s) found\n", knownPluginList.getNumTypes()); return knownPluginList.getNumTypes(); } @@ -103,7 +103,7 @@ int PluginHost::saveList(const string &filepath) { int out = knownPluginList.createXml()->writeToFile(juce::File(filepath), ""); if (!out) - gLog("[PluginHost::saveList] unable to save plugin list to %s\n", filepath.c_str()); + gu_log("[PluginHost::saveList] unable to save plugin list to %s\n", filepath.c_str()); return out; } @@ -138,7 +138,7 @@ Plugin *PluginHost::addPlugin(const string &fid, int stackType, juce::PluginDescription *pd = knownPluginList.getTypeForFile(fid); if (!pd) { - gLog("[PluginHost::addPlugin] no plugin found with fid=%s!\n", fid.c_str()); + gu_log("[PluginHost::addPlugin] no plugin found with fid=%s!\n", fid.c_str()); missingPlugins = true; unknownPluginList.push_back(fid); return NULL; @@ -146,7 +146,7 @@ Plugin *PluginHost::addPlugin(const string &fid, int stackType, Plugin *p = (Plugin *) pluginFormat.createInstanceFromDescription(*pd, samplerate, buffersize); if (!p) { - gLog("[PluginHost::addPlugin] unable to create instance with fid=%s!\n", fid.c_str()); + gu_log("[PluginHost::addPlugin] unable to create instance with fid=%s!\n", fid.c_str()); missingPlugins = true; return NULL; } @@ -157,7 +157,7 @@ Plugin *PluginHost::addPlugin(const string &fid, int stackType, p->init(); p->prepareToPlay(samplerate, buffersize); - gLog("[PluginHost::addPlugin] plugin instance with fid=%s created\n", fid.c_str()); + gu_log("[PluginHost::addPlugin] plugin instance with fid=%s created\n", fid.c_str()); /* Try to inject the plugin as soon as possible. */ @@ -171,7 +171,7 @@ Plugin *PluginHost::addPlugin(const string &fid, int stackType, } } - gLog("[PluginHost::addPlugin] plugin id=%s loaded (%s), stack type=%d, stack size=%d\n", + gu_log("[PluginHost::addPlugin] plugin id=%s loaded (%s), stack type=%d, stack size=%d\n", fid.c_str(), p->getName().toStdString().c_str(), stackType, pStack->size()); return p; @@ -186,12 +186,12 @@ Plugin *PluginHost::addPlugin(int index, int stackType, pthread_mutex_t *mutex, { juce::PluginDescription *pd = knownPluginList.getType(index); if (pd) { - gLog("[PluginHost::addPlugin] plugin found, uid=%s, name=%s...\n", + gu_log("[PluginHost::addPlugin] plugin found, uid=%s, name=%s...\n", pd->fileOrIdentifier.toStdString().c_str(), pd->name.toStdString().c_str()); return addPlugin(pd->fileOrIdentifier.toStdString(), stackType, mutex, ch); } else { - gLog("[PluginHost::addPlugin] no plugins found at index=%d!\n", index); + gu_log("[PluginHost::addPlugin] no plugins found at index=%d!\n", index); return NULL; } } @@ -258,7 +258,7 @@ PluginHost::PluginInfo PluginHost::getAvailablePluginInfo(int i) pi.isInstrument = pd->isInstrument; /* if (!p) { - gLog("[PluginHost::getAvailablePlugin] unable to create plugin instance!\n"); + gu_log("[PluginHost::getAvailablePlugin] unable to create plugin instance!\n"); return NULL; } */ @@ -303,7 +303,7 @@ void PluginHost::freeStack(int stackType, pthread_mutex_t *mutex, Channel *ch) break; } } - gLog("[PluginHost::freeStack] stack type=%d freed\n", stackType); + gu_log("[PluginHost::freeStack] stack type=%d freed\n", stackType); } @@ -326,17 +326,29 @@ void PluginHost::processStack(float *buffer, int stackType, Channel *ch) audioBuffer.setSample(1, i, buffer[(i*2)+1]); } - /* hardcore processing. At the end we swap input and output, so that - * the N-th plugin will process the result of the plugin N-1. */ + /* Hardcore processing. At the end we swap input and output, so that he N-th + plugin will process the result of the plugin N-1. Part of this loop must be + guarded by mutexes, i.e. the MIDI process part. You definitely don't want + a situation like the following one: + processBlock(...) + [a new midi event from kernelMidi thread] + clearMidiBuffer() + The midi event in between would be surely lost, deleted by clearMidiBuffer! */ for (unsigned i=0; isize(); i++) { Plugin *plugin = pStack->at(i); if (plugin->getStatus() != 1 || plugin->isSuspended() || plugin->isBypassed()) continue; - juce::MidiBuffer midiBuffer; - if (ch) // ch might be null if stackType is MASTER_IN or MASTER_OUT - midiBuffer = ch->getPluginMidiEvents(); - plugin->processBlock(audioBuffer, midiBuffer); + if (ch) { // ch might be null if stackType is MASTER_IN/OUT + pthread_mutex_lock(&mutex_midi); + plugin->processBlock(audioBuffer, ch->getPluginMidiEvents()); + ch->clearMidiBuffer(); + pthread_mutex_unlock(&mutex_midi); + } + else { + juce::MidiBuffer midiBuffer; // empty buffer + plugin->processBlock(audioBuffer, midiBuffer); + } } /* converting buffer from Juce to Giada. A note for the future: if we @@ -389,11 +401,11 @@ void PluginHost::swapPlugin(unsigned indexA, unsigned indexB, int stackType, if (lockStatus == 0) { std::swap(pStack->at(indexA), pStack->at(indexB)); pthread_mutex_unlock(mutex); - gLog("[pluginHost::swapPlugin] plugin at index %d and %d swapped\n", indexA, indexB); + gu_log("[pluginHost::swapPlugin] plugin at index %d and %d swapped\n", indexA, indexB); return; } //else - //gLog("[pluginHost] waiting for mutex...\n"); + //gu_log("[pluginHost] waiting for mutex...\n"); } } @@ -406,37 +418,30 @@ void PluginHost::freePlugin(int id, int stackType, pthread_mutex_t *mutex, { vector *pStack = getStack(stackType, ch); - /* try to delete the plugin until succeed. G_Mixer has priority. */ - for (unsigned i=0; isize(); i++) { Plugin *pPlugin = pStack->at(i); - if (pPlugin->getId() == id) { - if (pPlugin->getStatus() == 0) { // no frills if plugin is missing - delete pPlugin; - pStack->erase(pStack->begin() + i); - gLog("[pluginHost::freePlugin] plugin id=%d removed with no frills, since it had status=0\n", id); - return; - } - else { - int lockStatus; - while (true) { - lockStatus = pthread_mutex_trylock(mutex); - if (lockStatus == 0) { - pPlugin->suspendProcessing(true); - pPlugin->releaseResources(); - delete pPlugin; - pStack->erase(pStack->begin() + i); - pthread_mutex_unlock(mutex); - gLog("[pluginHost::freePlugin] plugin id=%d removed\n", id); - return; - } - //else - //gLog("[pluginHost] waiting for mutex...\n"); - } - } + if (pPlugin->getId() != id) + continue; + if (pPlugin->getStatus() == 0) { // no frills if plugin is missing + delete pPlugin; + pStack->erase(pStack->begin() + i); + gu_log("[pluginHost::freePlugin] plugin id=%d removed with no frills, since it had status=0\n", id); + return; + } + + while (true) { + if (pthread_mutex_trylock(mutex) != 0) + continue; + pPlugin->suspendProcessing(true); + pPlugin->releaseResources(); + delete pPlugin; + pStack->erase(pStack->begin() + i); + pthread_mutex_unlock(mutex); + gu_log("[pluginHost::freePlugin] plugin id=%d removed\n", id); + return; } } - gLog("[pluginHost::freePlugin] plugin id=%d not found\n", id); + gu_log("[pluginHost::freePlugin] plugin id=%d not found\n", id); } @@ -446,7 +451,7 @@ void PluginHost::freePlugin(int id, int stackType, pthread_mutex_t *mutex, void PluginHost::runDispatchLoop() { messageManager->runDispatchLoopUntil(10); - //gLog("[PluginHost::runDispatchLoop] %d, hasStopMessageBeenSent=%d\n", r, messageManager->hasStopMessageBeenSent()); + //gu_log("[PluginHost::runDispatchLoop] %d, hasStopMessageBeenSent=%d\n", r, messageManager->hasStopMessageBeenSent()); } @@ -473,7 +478,7 @@ int PluginHost::clonePlugin(Plugin *src, int stackType, pthread_mutex_t *mutex, juce::PluginDescription pd = src->getPluginDescription(); Plugin *p = addPlugin(pd.fileOrIdentifier.toStdString(), stackType, mutex, ch); if (!p) { - gLog("[PluginHost::clonePlugin] unable to add new plugin to stack!\n"); + gu_log("[PluginHost::clonePlugin] unable to add new plugin to stack!\n"); return 0; } for (int k=0; kgetNumParameters(); k++) { @@ -513,36 +518,5 @@ void PluginHost::sortPlugins(int method) } } -/* -------------------------------------------------------------------------- */ - - -void PluginHost::processStackOffline(float *buffer, int stackType, Channel *ch, int size) -{ -#if 0 - /* call processStack on the entire size of the buffer. How many cycles? - * size / (kernelAudio::realBufsize*2) (ie. internal bufsize) */ - - /** FIXME 1 - calling processStack is slow, due to its internal buffer - * conversions. We should also call processOffline from VST sdk */ - - int index = 0; - int step = kernelAudio::realBufsize*2; - - while (index <= size) { - int left = index+step-size; - if (left < 0) - processStack(&buffer[index], stackType, ch); - - /** FIXME 2 - we left out the last part of buffer, because size % step != 0. - * we should process the last chunk in a separate buffer, padded with 0 */ - - //else - // gLog("chunk of buffer left, size=%d\n", left); - - index+=step; - } -#endif -} - #endif // #ifdef WITH_VST diff --git a/src/core/pluginHost.h b/src/core/pluginHost.h index ac741e2..c1f0a51 100644 --- a/src/core/pluginHost.h +++ b/src/core/pluginHost.h @@ -197,11 +197,6 @@ public: void runDispatchLoop(); - /* processStackOffline - * apply the fx list to a longer chunk of data */ - - void processStackOffline(float *buffer, int stackType, class Channel *ch, int size); - /* freeAllStacks * Free everything. */ @@ -219,6 +214,8 @@ public: bool hasMissingPlugins() { return missingPlugins; }; void sortPlugins(int sortMethod); + + pthread_mutex_t mutex_midi; }; #endif diff --git a/src/core/recorder.cpp b/src/core/recorder.cpp index 53864b0..57958c7 100644 --- a/src/core/recorder.cpp +++ b/src/core/recorder.cpp @@ -3,7 +3,6 @@ * Giada - Your Hardcore Loopmachine * * recorder - * Action recorder. * * ----------------------------------------------------------------------------- * @@ -29,7 +28,8 @@ #include -#include "recorder.h" +#include "../utils/log.h" +#include "../utils/fs.h" #include "const.h" #include "mixer.h" #include "mixerHandler.h" @@ -39,31 +39,23 @@ #include "conf.h" #include "channel.h" #include "sampleChannel.h" -#include "../utils/log.h" -#include "../utils/utils.h" +#include "recorder.h" +extern KernelAudio G_KernelAudio; extern Mixer G_Mixer; -extern Patch_DEPR_ f_patch; -extern Conf G_Conf; -namespace recorder +Recorder::Recorder() + : active (false), + sortedActions(false) { -vector frames; -vector< vector > global; -vector actions; - -bool active = false; -bool sortedActions = false; - -composite cmp; - +} /* -------------------------------------------------------------------------- */ -void init() +void Recorder::init() { sortedActions = false; active = false; @@ -74,24 +66,28 @@ void init() /* -------------------------------------------------------------------------- */ -bool canRec(Channel *ch) +bool Recorder::canRec(Channel *ch) { /* NO recording if: * recorder is inactive * mixer is not running - * mixer is recording a take in this channel ch + * mixer is recording a take somewhere * channel is empty */ - if (!active || !G_Mixer.running || G_Mixer.chanInput == ch || (ch->type == CHANNEL_SAMPLE && ((SampleChannel*)ch)->wave == NULL)) - return 0; - return 1; + if (!active || + !G_Mixer.running || + G_Mixer.recording || + (ch->type == CHANNEL_SAMPLE && ((SampleChannel*)ch)->wave == NULL) + ) + return false; + return true; } /* -------------------------------------------------------------------------- */ -void rec(int index, int type, int frame, uint32_t iValue, float fValue) +void Recorder::rec(int index, int type, int frame, uint32_t iValue, float fValue) { /* make sure frame is even */ @@ -149,12 +145,11 @@ void rec(int index, int type, int frame, uint32_t iValue, float fValue) /* don't activate the channel (readActions == false), it's up to * the other layers */ - Channel *ch = G_Mixer.getChannelByIndex(index); - ch->hasActions = true; + G_Mixer.getChannelByIndex(index)->hasActions = true; sortedActions = false; - gLog("[REC] action recorded, type=%d frame=%d chan=%d iValue=%d (%X) fValue=%f\n", + gu_log("[REC] action recorded, type=%d frame=%d chan=%d iValue=%d (0x%X) fValue=%f\n", a->type, a->frame, a->chan, a->iValue, a->iValue, a->fValue); //print(); } @@ -163,9 +158,9 @@ void rec(int index, int type, int frame, uint32_t iValue, float fValue) /* -------------------------------------------------------------------------- */ -void clearChan(int index) +void Recorder::clearChan(int index) { - gLog("[REC] clearing chan %d...\n", index); + gu_log("[REC] clearing chan %d...\n", index); for (unsigned i=0; iiValue == iValue && a->fValue == fValue); if (doit) { + // TODO - wft? just do: while (true); if (pthread_mutex_trylock(&G_Mixer.mutex_recs)) int lockStatus = 0; while (lockStatus == 0) { lockStatus = pthread_mutex_trylock(&G_Mixer.mutex_recs); @@ -255,7 +252,7 @@ void deleteAction(int chan, int frame, char type, bool checkValues, uint32_t iVa break; } else - gLog("[REC] delete action: waiting for mutex...\n"); + gu_log("[REC] delete action: waiting for mutex...\n"); } } } @@ -264,11 +261,11 @@ void deleteAction(int chan, int frame, char type, bool checkValues, uint32_t iVa if (found) { optimize(); setChanHasActionsStatus(chan); - gLog("[REC] action deleted, type=%d frame=%d chan=%d iValue=%d (%X) fValue=%f\n", + gu_log("[REC] action deleted, type=%d frame=%d chan=%d iValue=%d (%X) fValue=%f\n", type, frame, chan, iValue, iValue, fValue); } else - gLog("[REC] unable to delete action, not found! type=%d frame=%d chan=%d iValue=%d (%X) fValue=%f\n", + gu_log("[REC] unable to delete action, not found! type=%d frame=%d chan=%d iValue=%d (%X) fValue=%f\n", type, frame, chan, iValue, iValue, fValue); } @@ -276,7 +273,7 @@ void deleteAction(int chan, int frame, char type, bool checkValues, uint32_t iVa /* -------------------------------------------------------------------------- */ -void deleteActions(int chan, int frame_a, int frame_b, char type) +void Recorder::deleteActions(int chan, int frame_a, int frame_b, char type) { sortActions(); vector dels; @@ -293,19 +290,12 @@ void deleteActions(int chan, int frame_a, int frame_b, char type) /* -------------------------------------------------------------------------- */ -void clearAll() +void Recorder::clearAll() { while (global.size() > 0) { for (unsigned i=0; itype == ACTION_MIDI) - free(global.at(i).at(k)->event); -#endif -#endif + for (unsigned k=0; kchan == chan && (type & a->type) == a->type) { - if (iValue == 0 || (iValue != 0 && a->iValue == iValue)) { + //if (iValue == 0 || (iValue != 0 && a->iValue == iValue)) { + if (iValue == 0 || (iValue != 0 && (iValue & a->iValue) == a->iValue )) { *out = global.at(i).at(j); return 1; } @@ -554,7 +546,7 @@ int getNextAction(int chan, char type, int frame, action **out, uint32_t iValue) /* -------------------------------------------------------------------------- */ -int getAction(int chan, char action, int frame, struct action **out) +int Recorder::getAction(int chan, char action, int frame, struct action **out) { for (unsigned i=0; itype == cmp.a2.type) { - int truncFrame = cmp.a1.frame-kernelAudio::realBufsize; + int truncFrame = cmp.a1.frame - G_KernelAudio.realBufsize; if (truncFrame < 0) truncFrame = 0; - gLog("[REC] add truncation at frame %d, type=%d\n", truncFrame, cmp.a2.type); + gu_log("[REC] add truncation at frame %d, type=%d\n", truncFrame, cmp.a2.type); rec(index, cmp.a2.type, truncFrame); } } @@ -614,7 +606,7 @@ void startOverdub(int index, char actionMask, int frame) /* -------------------------------------------------------------------------- */ -void stopOverdub(int frame) +void Recorder::stopOverdub(int frame) { cmp.a2.frame = frame; bool ringLoop = false; @@ -625,13 +617,13 @@ void stopOverdub(int frame) if (cmp.a2.frame < cmp.a1.frame) { ringLoop = true; - gLog("[REC] ring loop! frame1=%d < frame2=%d\n", cmp.a1.frame, cmp.a2.frame); + gu_log("[REC] ring loop! frame1=%d < frame2=%d\n", cmp.a1.frame, cmp.a2.frame); rec(cmp.a2.chan, cmp.a2.type, G_Mixer.totalFrames); // record at the end of the sequencer } else if (cmp.a2.frame == cmp.a1.frame) { nullLoop = true; - gLog("[REC] null loop! frame1=%d == frame2=%d\n", cmp.a1.frame, cmp.a2.frame); + gu_log("[REC] null loop! frame1=%d == frame2=%d\n", cmp.a1.frame, cmp.a2.frame); deleteAction(cmp.a1.chan, cmp.a1.frame, cmp.a1.type, false); // false == don't check values } @@ -655,7 +647,7 @@ void stopOverdub(int frame) int res = getNextAction(cmp.a2.chan, cmp.a1.type | cmp.a2.type, cmp.a2.frame, &act); if (res == 1) { if (act->type == cmp.a2.type) { - gLog("[REC] add truncation at frame %d, type=%d\n", act->frame, act->type); + gu_log("[REC] add truncation at frame %d, type=%d\n", act->frame, act->type); deleteAction(act->chan, act->frame, act->type, false); // false == don't check values } } @@ -666,15 +658,13 @@ void stopOverdub(int frame) /* -------------------------------------------------------------------------- */ -void print() +void Recorder::print() { - gLog("[REC] ** print debug **\n"); + gu_log("[REC] ** print debug **\n"); for (unsigned i=0; itype, global.at(i).at(j)->chan, global.at(i).at(j)->frame); + gu_log(" action %d | chan %d | frame %d\n", global.at(i).at(j)->type, global.at(i).at(j)->chan, global.at(i).at(j)->frame); } } } - -} // namespace diff --git a/src/core/recorder.h b/src/core/recorder.h index 95a8b65..307b408 100644 --- a/src/core/recorder.h +++ b/src/core/recorder.h @@ -1,10 +1,10 @@ -/* --------------------------------------------------------------------- +/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * recorder * - * --------------------------------------------------------------------- + * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual * @@ -24,156 +24,166 @@ * along with Giada - Your Hardcore Loopmachine. If not, see * . * - * ------------------------------------------------------------------ */ + * -------------------------------------------------------------------------- */ + #ifndef RECORDER_H #define RECORDER_H -#include -#include + +#ifdef __APPLE__ // our compiler still doesn't know about cstdint (c++11 stuff) + #include +#else + #include +#endif #include -#include "../utils/utils.h" -#include "const.h" -#include "mixer.h" using std::vector; -/* - * [global0]-->[vector<_action*>0]-->[a0][a1][a2] 0[frames1] - * [global1]-->[vector<_action*>1]-->[a0][a1][a2] 1[frames2] - * [global2]-->[vector<_action*>2]-->[a0][a1][a2] 2[frames3] - * [global3]-->[vector<_action*>3]-->[a0][a1][a2] 3[frames4] - * */ - -namespace recorder +class Recorder { -/* action - * struct containing fields to describe an atomic action. Note from - * VST sdk: parameter values, like all VST parameters, are declared as - * floats with an inclusive range of 0.0 to 1.0 (fValue). */ +public: -struct action -{ - int chan; // channel index, i.e. Channel->index - int type; - int frame; // redundant info, used by helper functions - float fValue; // used only for envelopes (volumes, vst params). - uint32_t iValue; // used only for MIDI events -}; + Recorder(); -/* composite - * a group of two actions (keypress+keyrel, muteon+muteoff) used during - * the overdub process */ + /* action + * struct containing fields to describe an atomic action. Note from + * VST sdk: parameter values, like all VST parameters, are declared as + * floats with an inclusive range of 0.0 to 1.0 (fValue). */ -struct composite -{ - action a1; - action a2; -}; + struct action + { + int chan; // channel index, i.e. Channel->index + int type; + int frame; // redundant info, used by helper functions + float fValue; // used only for envelopes (volumes, vst params). + uint32_t iValue; // used only for MIDI events + }; + + /* [global0]-->[vector<_action*>0]-->[a0][a1][a2] 0[frames1] + * [global1]-->[vector<_action*>1]-->[a0][a1][a2] 1[frames2] + * [global2]-->[vector<_action*>2]-->[a0][a1][a2] 2[frames3] + * [global3]-->[vector<_action*>3]-->[a0][a1][a2] 3[frames4] */ + + vector frames; // frame counter (sentinel) frames.size == global.size + vector> global; // container of containers of actions + vector actions; // container of actions -extern vector frames; // frame counter (sentinel) frames.size == global.size -extern vector< vector > global; // container of containers of actions -extern vector actions; // container of actions + bool active; + bool sortedActions; // are actions sorted via sortActions()? -extern bool active; -extern bool sortedActions; // are actions sorted via sortActions()? + /* init + * everything starts from here. */ -/* init - * everything starts from here. */ + void init(); -void init(); + /* setChanHasActionsStatus + * Check if the channel has at least one action recorded. If false, sets + * ch->hasActions = false. Used after an action deletion. */ -/* setChanHasActionsStatus - * Check if the channel has at least one action recorded. If false, sets - * ch->hasActions = false. Used after an action deletion. */ + void setChanHasActionsStatus(int chan); -void setChanHasActionsStatus(int chan); + /* canRec + * can a channel rec an action? Call this one BEFORE rec(). */ -/* canRec - * can a channel rec an action? Call this one BEFORE rec(). */ + bool canRec(class Channel *ch); -bool canRec(Channel *ch); + /* rec + * record an action. */ -/* rec - * record an action. */ + void rec(int chan, int action, int frame, uint32_t iValue=0, + float fValue=0.0f); -void rec(int chan, int action, int frame, uint32_t iValue=0, float fValue=0.0f); + /* clearChan + * clear all actions from a channel. */ -/* clearChan - * clear all actions from a channel. */ + void clearChan(int chan); -void clearChan(int chan); + /* clearAction + * clear the 'action' action type from a channel. */ -/* clearAction - * clear the 'action' action type from a channel. */ + void clearAction(int chan, char action); -void clearAction(int chan, char action); + /* deleteAction + * delete ONE action. Useful in the action editor. 'type' can be a mask. */ -/* deleteAction - * delete ONE action. Useful in the action editor. 'type' can be a mask. */ + void deleteAction(int chan, int frame, char type, bool checkValues, + uint32_t iValue=0, float fValue=0.0); -void deleteAction(int chan, int frame, char type, bool checkValues, uint32_t iValue=0, float fValue=0.0); + /* deleteActions + * delete A RANGE of actions from frame_a to frame_b in channel 'chan'. + * 'type' can be a bitmask. Exclusive range (frame_a, frame_b). */ -/* deleteActions - * delete A RANGE of actions from frame_a to frame_b in channel 'chan'. - * 'type' can be a bitmask. Exclusive range (frame_a, frame_b). */ + void deleteActions(int chan, int frame_a, int frame_b, char type); -void deleteActions(int chan, int frame_a, int frame_b, char type); + /* clearAll + * delete everything. */ -/* clearAll - * delete everything. */ + void clearAll(); -void clearAll(); + /* optimize + * clear frames without actions. */ -/* optimize - * clear frames without actions. */ + void optimize(); -void optimize(); + /* sortActions + * sorts actions by frame, asc mode. */ -/* sortActions - * sorts actions by frame, asc mode. */ + void sortActions(); -void sortActions(); + /* updateBpm + * reassign frames by calculating the new bpm value. */ -/* updateBpm - * reassign frames by calculating the new bpm value. */ + void updateBpm(float oldval, float newval, int oldquanto); -void updateBpm(float oldval, float newval, int oldquanto); + /* updateSamplerate + * reassign frames taking in account the samplerate. If f_system == + * f_patch nothing changes, otherwise the conversion is mandatory. */ -/* updateSamplerate - * reassign frames taking in account the samplerate. If f_system == - * f_patch nothing changes, otherwise the conversion is mandatory. */ + void updateSamplerate(int systemRate, int patchRate); -void updateSamplerate(int systemRate, int patchRate); + void expand(int old_fpb, int new_fpb); + void shrink(int new_fpb); -void expand(int old_fpb, int new_fpb); -void shrink(int new_fpb); + /* getNextAction + * Return the nearest action in chan 'chan' of type 'action' starting + * from 'frame'. Action can be a bitmask. If iValue != 0 search for + * next action with iValue == iValue: useful for MIDI key_release. iValue + * can be a bitmask. */ -/* getNextAction - * return the nearest action in chan 'chan' of type 'action' starting - * from 'frame'. Action can be a bitmask. If iValue != -1 search for - * next action with iValue == iValue: useful for MIDI key_release. */ + int getNextAction(int chan, char action, int frame, struct action **out, + uint32_t iValue=0); -int getNextAction(int chan, char action, int frame, struct action **out, uint32_t iValue=0); + /* getAction + * return a pointer to action in chan 'chan' of type 'action' at frame + * 'frame'. */ -/* getAction - * return a pointer to action in chan 'chan' of type 'action' at frame - * 'frame'. */ + int getAction(int chan, char action, int frame, struct action **out); -int getAction(int chan, char action, int frame, struct action **out); + /* start/endOverdub */ -/* start/endOverdub */ + void startOverdub(int chan, char action, int frame); + void stopOverdub(int frame); -void startOverdub(int chan, char action, int frame); -void stopOverdub(int frame); +private: -/* print - * debug of the frame stack. */ + /* composite + * a group of two actions (keypress+keyrel, muteon+muteoff) used during + * the overdub process */ -void print(); + struct composite + { + action a1; + action a2; + } cmp; -} // namespace + /* print + * debug of the frame stack. */ + + void print(); + +}; #endif diff --git a/src/core/sampleChannel.cpp b/src/core/sampleChannel.cpp index 3b7d5e1..4489801 100644 --- a/src/core/sampleChannel.cpp +++ b/src/core/sampleChannel.cpp @@ -29,6 +29,7 @@ #include #include "../utils/log.h" +#include "../utils/string.h" #include "sampleChannel.h" #include "patch_DEPR_.h" #include "patch.h" @@ -38,11 +39,16 @@ #include "waveFx.h" #include "mixerHandler.h" #include "kernelMidi.h" +#include "kernelAudio.h" using std::string; +extern Recorder G_Recorder; +extern KernelAudio G_KernelAudio; + + SampleChannel::SampleChannel(int bufferSize, MidiMapConf *midiMapConf) : Channel (CHANNEL_SAMPLE, STATUS_EMPTY, bufferSize, midiMapConf), frameRewind (-1), @@ -60,12 +66,12 @@ SampleChannel::SampleChannel(int bufferSize, MidiMapConf *midiMapConf) fadeoutVol (1.0f), fadeoutTracker (0), fadeoutStep (DEFAULT_FADEOUT_STEP), - readActions (true), + readActions (false), midiInReadActions(0x0), midiInPitch (0x0) { rsmp_state = src_new(SRC_LINEAR, 2, NULL); - pChan = (float *) malloc(kernelAudio::realBufsize * 2 * sizeof(float)); + pChan = (float *) malloc(G_KernelAudio.realBufsize * 2 * sizeof(float)); } @@ -119,8 +125,8 @@ void SampleChannel::generateUniqueSampleName() { string oldName = wave->name; int k = 1; // Start from k = 1, zero is too nerdy - while (!mh_uniqueSamplename(this, wave->name.c_str())) { - wave->updateName((oldName + "-" + gItoa(k)).c_str()); + while (!mh_uniqueSampleName(this, wave->name)) { + wave->updateName((oldName + "-" + gu_itoa(k)).c_str()); k++; } } @@ -140,7 +146,7 @@ void SampleChannel::clear() if (status & (STATUS_PLAY | STATUS_ENDING)) { tracker = fillChan(vChan, tracker, 0); if (fadeoutOn && fadeoutType == XFADE) { - gLog("[clear] filling pChan fadeoutTracker=%d\n", fadeoutTracker); + gu_log("[clear] filling pChan fadeoutTracker=%d\n", fadeoutTracker); fadeoutTracker = fillChan(pChan, fadeoutTracker, 0); } } @@ -154,14 +160,14 @@ void SampleChannel::calcVolumeEnv(int frame) { /* method: check this frame && next frame, then calculate delta */ - recorder::action *a0 = NULL; - recorder::action *a1 = NULL; + Recorder::action *a0 = NULL; + Recorder::action *a1 = NULL; int res; /* get this action on frame 'frame'. It's unlikely that the action * is not found. */ - res = recorder::getAction(index, ACTION_VOLUME, frame, &a0); + res = G_Recorder.getAction(index, ACTION_VOLUME, frame, &a0); if (res == 0) return; @@ -170,10 +176,10 @@ void SampleChannel::calcVolumeEnv(int frame) * and use action at frame number 0 (actions[0]). * res == -2 ACTION_VOLUME not found. This should never happen */ - res = recorder::getNextAction(index, ACTION_VOLUME, frame, &a1); + res = G_Recorder.getNextAction(index, ACTION_VOLUME, frame, &a1); if (res == -1) - res = recorder::getAction(index, ACTION_VOLUME, 0, &a1); + res = G_Recorder.getAction(index, ACTION_VOLUME, 0, &a1); volume_i = a0->fValue; volume_d = ((a1->fValue - a0->fValue) / ((a1->frame - a0->frame) / 2)) * 1.003f; @@ -278,7 +284,7 @@ void SampleChannel::rewind() /* -------------------------------------------------------------------------- */ -void SampleChannel::parseAction(recorder::action *a, int localFrame, +void SampleChannel::parseAction(Recorder::action *a, int localFrame, int globalFrame, int quantize, bool mixerIsRunning) { if (readActions == false) @@ -287,7 +293,7 @@ void SampleChannel::parseAction(recorder::action *a, int localFrame, switch (a->type) { case ACTION_KEYPRESS: if (mode & SINGLE_ANY) - start(localFrame, false, quantize, mixerIsRunning); + start(localFrame, false, quantize, mixerIsRunning, false, false); break; case ACTION_KEYREL: if (mode & SINGLE_ANY) @@ -494,11 +500,11 @@ void SampleChannel::quantize(int index, int localFrame, int globalFrame) /* this is the moment in which we record the keypress, if the * quantizer is on. SINGLE_PRESS needs overdub */ - if (recorder::canRec(this)) { + if (G_Recorder.canRec(this)) { if (mode == SINGLE_PRESS) - recorder::startOverdub(index, ACTION_KEYS, globalFrame); + G_Recorder.startOverdub(index, ACTION_KEYS, globalFrame); else - recorder::rec(index, ACTION_KEYPRESS, globalFrame); + G_Recorder.rec(index, ACTION_KEYPRESS, globalFrame); } } @@ -603,13 +609,9 @@ void SampleChannel::calcFadeoutStep() void SampleChannel::setReadActions(bool v, bool recsStopOnChanHalt) { - if (v) - readActions = true; - else { - readActions = false; - if (recsStopOnChanHalt) - kill(0); /// FIXME - wrong frame value - } + readActions = v; + if (!readActions && recsStopOnChanHalt) + kill(0); /// FIXME - wrong frame value } @@ -643,7 +645,7 @@ void SampleChannel::setFadeOut(int actionPostFadeout) void SampleChannel::setXFade(int frame) { - gLog("[xFade] frame=%d tracker=%d\n", frame, tracker); + gu_log("[xFade] frame=%d tracker=%d\n", frame, tracker); calcFadeoutStep(); fadeoutOn = true; @@ -697,7 +699,7 @@ void SampleChannel::pushWave(Wave *w) { wave = w; status = STATUS_OFF; - sendMidiLplay(); + sendMidiLplay(); // FIXME - why here?!?! begin = 0; end = wave->size; } @@ -712,14 +714,14 @@ bool SampleChannel::allocEmpty(int frames, int samplerate, int takeId) if (!w->allocEmpty(frames, samplerate)) return false; - w->name = "TAKE-" + gItoa(takeId); - w->pathfile = gGetCurrentPath() + G_SLASH + w->name; + w->name = "TAKE-" + gu_itoa(takeId); + w->pathfile = gu_getCurrentPath() + G_SLASH + w->name; wave = w; status = STATUS_OFF; begin = 0; end = wave->size; - sendMidiLplay(); + sendMidiLplay(); // FIXME - why here?!?! return true; } @@ -728,15 +730,24 @@ bool SampleChannel::allocEmpty(int frames, int samplerate, int takeId) /* -------------------------------------------------------------------------- */ -void SampleChannel::process(float *buffer) +void SampleChannel::process(float *outBuffer, float *inBuffer) { + /* If armed and inbuffer is not null (i.e. input device available), copy input + buffer to vChan: this enables the live recording mode. The vChan will be + overwritten later by PluginHost::processStack, so that you would record "clean" + audio (i.e. not plugin-processed). */ + + if (armed && inBuffer) + for (int i=0; iprocessStack(vChan, PluginHost::CHANNEL, this); #endif for (int j=0; jopen(file)) { - gLog("[SampleChannel] %s: read error\n", file); + gu_log("[SampleChannel] %s: read error\n", file); delete w; return SAMPLE_READ_ERROR; } if (w->channels() > 2) { - gLog("[SampleChannel] %s: unsupported multichannel wave\n", file); + gu_log("[SampleChannel] %s: unsupported multichannel wave\n", file); delete w; return SAMPLE_MULTICHANNEL; } @@ -839,7 +850,7 @@ int SampleChannel::load(const char *file, int samplerate, int rsmpQuality) wfx_monoToStereo(w); if (w->rate() != samplerate) { - gLog("[SampleChannel] input rate (%d) != system rate (%d), conversion needed\n", + gu_log("[SampleChannel] input rate (%d) != system rate (%d), conversion needed\n", w->rate(), samplerate); w->resample(rsmpQuality, samplerate); } @@ -847,7 +858,7 @@ int SampleChannel::load(const char *file, int samplerate, int rsmpQuality) pushWave(w); generateUniqueSampleName(); - gLog("[SampleChannel] %s loaded in channel %d\n", file, index); + gu_log("[SampleChannel] %s loaded in channel %d\n", file, index); return SAMPLE_LOADED_OK; } @@ -944,8 +955,8 @@ int SampleChannel::readPatch(const string &basePath, int i, Patch *patch, bool SampleChannel::canInputRec() - { - return wave == NULL; +{ + return wave == NULL && armed; } @@ -953,7 +964,7 @@ bool SampleChannel::canInputRec() void SampleChannel::start(int frame, bool doQuantize, int quantize, - bool mixerIsRunning) + bool mixerIsRunning, bool forceStart, bool isUserGenerated) { switch (status) { case STATUS_EMPTY: @@ -962,35 +973,34 @@ void SampleChannel::start(int frame, bool doQuantize, int quantize, { return; } - case STATUS_OFF: { if (mode & LOOP_ANY) { - status = STATUS_WAIT; + if (forceStart) { + status = STATUS_PLAY; + tracker = frame; + } + else + status = STATUS_WAIT; sendMidiLplay(); } else { if (quantize > 0 && mixerIsRunning && doQuantize) qWait = true; else { - - /* fillChan only if frame != 0. If you call fillChan on frame == 0 - a duplicate call to fillChan occurs with loss of data. Yeah, but - what happens if an action really occurs on frame 0 (and it happens, - for example when you start the sequencer from the firt beat)? Cheat - time! Shift a little bit the frame, so that it's no longer zero. */ - status = STATUS_PLAY; sendMidiLplay(); - if (frame == 0) - frame = 2; - //if (frame != 0) + + /* Do fillChan only if this is not a user-generated event (i.e. is an + action read by Mixer). Otherwise clear() will take take of calling + fillChan on the next cycle. */ + + if (!isUserGenerated) tracker = fillChan(vChan, tracker, frame); } } break; } - case STATUS_PLAY: { if (mode == SINGLE_BASIC) @@ -1009,14 +1019,12 @@ void SampleChannel::start(int frame, bool doQuantize, int quantize, } break; } - case STATUS_WAIT: { status = STATUS_OFF; sendMidiLplay(); break; } - case STATUS_ENDING: { status = STATUS_PLAY; @@ -1032,13 +1040,15 @@ void SampleChannel::start(int frame, bool doQuantize, int quantize, int SampleChannel::writePatch(int i, bool isProject, Patch *patch) { + // TODO - this code belongs to an upper level (glue) + int pchIndex = Channel::writePatch(i, isProject, patch); Patch::channel_t *pch = &patch->channels.at(pchIndex); if (wave != NULL) { pch->samplePath = wave->pathfile; if (isProject) - pch->samplePath = gBasename(wave->pathfile); // make it portable + pch->samplePath = gu_basename(wave->pathfile); // make it portable } else pch->samplePath = ""; diff --git a/src/core/sampleChannel.h b/src/core/sampleChannel.h index 9158248..751b999 100644 --- a/src/core/sampleChannel.h +++ b/src/core/sampleChannel.h @@ -89,30 +89,33 @@ public: SampleChannel(int bufferSize, class MidiMapConf *midiMapConf); ~SampleChannel(); - void copy(const Channel *src, pthread_mutex_t *pluginMutex); - - void clear (); - void process (float *buffer); - void start (int frame, bool doQuantize, int quantize, bool mixerIsRunning); - void kill (int frame); - void empty (); - void stopBySeq (bool chansStopOnSeqHalt); - void stop (); - void rewind (); - void setMute (bool internal); - void unsetMute (bool internal); - void reset (int frame); - int load (const char *file, int samplerate, int rsmpQuality); - int readPatch_DEPR_ (const char *file, int i, class Patch_DEPR_ *patch, - int samplerate, int rsmpQuality); - int readPatch (const string &basePath, int i, class Patch *patch, - pthread_mutex_t *pluginMutex, int samplerate, int rsmpQuality); - int writePatch (int i, bool isProject, class Patch *patch); - void quantize (int index, int localFrame, int globalFrame); - void onZero (int frame, bool recsStopOnChanHalt); - void onBar (int frame); - void parseAction(recorder::action *a, int localFrame, int globalFrame, - int quantize, bool mixerIsRunning); + void copy(const Channel *src, pthread_mutex_t *pluginMutex) override; + void clear() override; + void process(float *outBuffer, float *inBuffer) override; + void start(int frame, bool doQuantize, int quantize, bool mixerIsRunning, + bool forceStart, bool isUserGenerated) override; + void kill(int frame) override; + void empty() override; + void stopBySeq(bool chansStopOnSeqHalt) override; + void stop() override; + void rewind() override; + void setMute(bool internal) override; + void unsetMute(bool internal) override; + int readPatch_DEPR_(const char *file, int i, class Patch_DEPR_ *patch, + int samplerate, int rsmpQuality) override; + int readPatch(const string &basePath, int i, class Patch *patch, + pthread_mutex_t *pluginMutex, int samplerate, int rsmpQuality) override; + int writePatch(int i, bool isProject, class Patch *patch) override; + void quantize(int index, int localFrame, int globalFrame) override; + void onZero(int frame, bool recsStopOnChanHalt) override; + void onBar(int frame) override; + void parseAction(Recorder::action *a, int localFrame, int globalFrame, + int quantize, bool mixerIsRunning) override; + bool canInputRec() override; + + int load(const char *file, int samplerate, int rsmpQuality); + + void reset(int frame); /* fade methods * prepare channel for fade, mixer will take care of the process @@ -165,13 +168,8 @@ public: bool allocEmpty(int frames, int samplerate, int takeId); - /* canInputRec - * true if channel can host a new wave from input recording. */ - - bool canInputRec(); - /* setReadActions - * if enabled, recorder will read actions from this channel. If + * if enabled (v == true), recorder will read actions from this channel. If * recsStopOnChanHalt == true, stop reading actions right away. */ void setReadActions(bool v, bool recsStopOnChanHalt); diff --git a/src/core/wave.cpp b/src/core/wave.cpp index 4bdffc9..7f89e32 100644 --- a/src/core/wave.cpp +++ b/src/core/wave.cpp @@ -31,7 +31,7 @@ #include #include // memcpy #include -#include "../utils/utils.h" +#include "../utils/fs.h" #include "../utils/log.h" #include "init.h" #include "const.h" @@ -81,11 +81,11 @@ Wave::Wave(const Wave &other) int Wave::open(const char *f) { pathfile = f; - name = gStripExt(gBasename(f).c_str()); + name = gu_stripExt(gu_basename(f)); fileIn = sf_open(f, SFM_READ, &inHeader); if (fileIn == NULL) { - gLog("[wave] unable to read %s. %s\n", f, sf_strerror(fileIn)); + gu_log("[wave] unable to read %s. %s\n", f, sf_strerror(fileIn)); pathfile = ""; name = ""; return 0; @@ -119,12 +119,12 @@ int Wave::readData() size = inHeader.frames * inHeader.channels; data = (float *) malloc(size * sizeof(float)); if (data == NULL) { - gLog("[wave] unable to allocate memory\n"); + gu_log("[wave] unable to allocate memory\n"); return 0; } if (sf_read_float(fileIn, data, size) != size) - gLog("[wave] warning: incomplete read!\n"); + gu_log("[wave] warning: incomplete read!\n"); sf_close(fileIn); return 1; @@ -144,13 +144,13 @@ int Wave::writeData(const char *f) fileOut = sf_open(f, SFM_WRITE, &outHeader); if (fileOut == NULL) { - gLog("[wave] unable to open %s for exporting\n", f); + gu_log("[wave] unable to open %s for exporting\n", f); return 0; } int out = sf_write_float(fileOut, data, size); if (out != (int) size) { - gLog("[wave] error while exporting %s! %s\n", f, sf_strerror(fileOut)); + gu_log("[wave] error while exporting %s! %s\n", f, sf_strerror(fileOut)); return 0; } @@ -186,7 +186,7 @@ int Wave::allocEmpty(unsigned __size, unsigned samplerate) size = __size; data = (float *) malloc(size * sizeof(float)); if (data == NULL) { - gLog("[wave] unable to allocate memory\n"); + gu_log("[wave] unable to allocate memory\n"); return 0; } @@ -213,7 +213,7 @@ int Wave::resample(int quality, int newRate) float *tmp = (float *) malloc(newSize * sizeof(float)); if (!tmp) { - gLog("[wave] unable to allocate memory for resampling\n"); + gu_log("[wave] unable to allocate memory for resampling\n"); return -1; } @@ -224,11 +224,11 @@ int Wave::resample(int quality, int newRate) src_data.output_frames = newSize/2; // in frames, i.e. /2 (stereo) src_data.src_ratio = ratio; - gLog("[wave] resampling: new size=%d (%d frames)\n", newSize, newSize/2); + gu_log("[wave] resampling: new size=%d (%d frames)\n", newSize, newSize/2); int ret = src_simple(&src_data, quality, 2); if (ret != 0) { - gLog("[wave] resampling error: %s\n", src_strerror(ret)); + gu_log("[wave] resampling error: %s\n", src_strerror(ret)); return 0; } @@ -245,12 +245,12 @@ int Wave::resample(int quality, int newRate) string Wave::basename(bool ext) const { - return ext ? gBasename(pathfile) : gStripExt(gBasename(pathfile)); + return ext ? gu_basename(pathfile) : gu_stripExt(gu_basename(pathfile)); } string Wave::extension() const { - return gGetExt(pathfile.c_str()); + return gu_getExt(pathfile); } @@ -259,9 +259,9 @@ string Wave::extension() const void Wave::updateName(const char *n) { - string ext = gGetExt(pathfile.c_str()); - name = gStripExt(gBasename(n).c_str()); - pathfile = gDirname(pathfile.c_str()) + G_SLASH + name + "." + ext; + string ext = gu_getExt(pathfile); + name = gu_stripExt(gu_basename(n)); + pathfile = gu_dirname(pathfile) + G_SLASH + name + "." + ext; isLogical = true; /* a wave with updated name must become logical, since the underlying diff --git a/src/core/waveFx.cpp b/src/core/waveFx.cpp index 52b6f1c..5831968 100644 --- a/src/core/waveFx.cpp +++ b/src/core/waveFx.cpp @@ -1,10 +1,10 @@ -/* --------------------------------------------------------------------- +/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * waveFx * - * --------------------------------------------------------------------- + * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual * @@ -24,21 +24,17 @@ * along with Giada - Your Hardcore Loopmachine. If not, see * . * - * ------------------------------------------------------------------ */ + * -------------------------------------------------------------------------- */ -#include +#include #include "../utils/log.h" -#include "waveFx.h" -#include "channel.h" -#include "mixer.h" #include "wave.h" +#include "waveFx.h" -extern Mixer G_Mixer; - - -float wfx_normalizeSoft(Wave *w) { +float wfx_normalizeSoft(Wave *w) +{ float peak = 0.0f; float abs = 0.0f; for (int i=0; isize; i++) { // i++: both L and R samples @@ -57,15 +53,15 @@ float wfx_normalizeSoft(Wave *w) { } -/* ------------------------------------------------------------------ */ - +/* -------------------------------------------------------------------------- */ -bool wfx_monoToStereo(Wave *w) { +bool wfx_monoToStereo(Wave *w) +{ unsigned newSize = w->size * 2; float *dataNew = (float *) malloc(newSize * sizeof(float)); if (dataNew == NULL) { - gLog("[wfx] unable to allocate memory for mono>stereo conversion\n"); + gu_log("[wfx] unable to allocate memory for mono>stereo conversion\n"); return 0; } @@ -85,16 +81,16 @@ bool wfx_monoToStereo(Wave *w) { } -/* ------------------------------------------------------------------ */ +/* -------------------------------------------------------------------------- */ -void wfx_silence(Wave *w, int a, int b) { - +void wfx_silence(Wave *w, int a, int b) +{ /* stereo values */ a = a * 2; b = b * 2; - gLog("[wfx] silencing from %d to %d\n", a, b); + gu_log("[wfx] silencing from %d to %d\n", a, b); for (int i=a; idata[i] = 0.0f; @@ -107,10 +103,11 @@ void wfx_silence(Wave *w, int a, int b) { } -/* ------------------------------------------------------------------ */ +/* -------------------------------------------------------------------------- */ -int wfx_cut(Wave *w, int a, int b) { +int wfx_cut(Wave *w, int a, int b) +{ a = a * 2; b = b * 2; @@ -123,11 +120,11 @@ int wfx_cut(Wave *w, int a, int b) { unsigned newSize = w->size-(b-a); float *temp = (float *) malloc(newSize * sizeof(float)); if (temp == NULL) { - gLog("[wfx] unable to allocate memory for cutting\n"); + gu_log("[wfx] unable to allocate memory for cutting\n"); return 0; } - gLog("[wfx] cutting from %d to %d, new size=%d (video=%d)\n", a, b, newSize, newSize/2); + gu_log("[wfx] cutting from %d to %d, new size=%d (video=%d)\n", a, b, newSize, newSize/2); for (int i=0, k=0; isize; i++) { if (i < a || i >= b) { // left margin always included, in order to keep @@ -143,16 +140,17 @@ int wfx_cut(Wave *w, int a, int b) { w->frames(w->frames() - b - a); w->isEdited = true; - gLog("[wfx] cutting done\n"); + gu_log("[wfx] cutting done\n"); return 1; } -/* ------------------------------------------------------------------ */ +/* -------------------------------------------------------------------------- */ -int wfx_trim(Wave *w, int a, int b) { +int wfx_trim(Wave *w, int a, int b) +{ a = a * 2; b = b * 2; @@ -162,11 +160,11 @@ int wfx_trim(Wave *w, int a, int b) { int newSize = b - a; float *temp = (float *) malloc(newSize * sizeof(float)); if (temp == NULL) { - gLog("[wfx] unable to allocate memory for trimming\n"); + gu_log("[wfx] unable to allocate memory for trimming\n"); return 0; } - gLog("[wfx] trimming from %d to %d (area = %d)\n", a, b, b-a); + gu_log("[wfx] trimming from %d to %d (area = %d)\n", a, b, b-a); for (int i=a, k=0; idata[i]; @@ -182,11 +180,11 @@ int wfx_trim(Wave *w, int a, int b) { } -/* ------------------------------------------------------------------ */ - +/* -------------------------------------------------------------------------- */ -void wfx_fade(Wave *w, int a, int b, int type) { +void wfx_fade(Wave *w, int a, int b, int type) +{ float m = type == 0 ? 0.0f : 1.0f; float d = 1.0f/(float)(b-a); if (type == 1) @@ -200,14 +198,16 @@ void wfx_fade(Wave *w, int a, int b, int type) { w->data[i+1] *= m; m += d; } -} + w->isEdited = true; +} -/* ------------------------------------------------------------------ */ +/* -------------------------------------------------------------------------- */ -void wfx_smooth(Wave *w, int a, int b) { +void wfx_smooth(Wave *w, int a, int b) +{ int d = 32; // 64 if stereo data /* do nothing if fade edges (both of 32 samples) are > than selected @@ -215,7 +215,7 @@ void wfx_smooth(Wave *w, int a, int b) { * values. */ if (d*2 > (b-a)*2) { - gLog("[WFX] selection is too small, nothing to do\n"); + gu_log("[WFX] selection is too small, nothing to do\n"); return; } diff --git a/src/core/waveFx.h b/src/core/waveFx.h index 491b1af..7eae643 100644 --- a/src/core/waveFx.h +++ b/src/core/waveFx.h @@ -1,10 +1,10 @@ -/* --------------------------------------------------------------------- +/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * waveFx * - * --------------------------------------------------------------------- + * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual * @@ -24,7 +24,7 @@ * along with Giada - Your Hardcore Loopmachine. If not, see * . * - * ------------------------------------------------------------------ */ + * -------------------------------------------------------------------------- */ #ifndef WAVEFX_H diff --git a/src/glue/channel.cpp b/src/glue/channel.cpp index ea4e01b..c1fa9ba 100644 --- a/src/glue/channel.cpp +++ b/src/glue/channel.cpp @@ -29,22 +29,27 @@ #include "../gui/dialogs/gd_mainWindow.h" +#include "../gui/dialogs/gd_editor.h" #include "../gui/elems/ge_keyboard.h" -#include "../gui/elems/ge_channel.h" -#include "../utils/gui_utils.h" +#include "../gui/elems/ge_waveTools.h" +#include "../gui/elems/ge_waveform.h" +#include "../gui/elems/channel.h" +#include "../utils/gui.h" #include "../core/mixerHandler.h" #include "../core/mixer.h" #include "../core/pluginHost.h" #include "../core/conf.h" +#include "../core/wave.h" #include "../core/channel.h" #include "../core/sampleChannel.h" #include "../core/midiChannel.h" -#include "glue.h" +#include "main.h" #include "channel.h" -extern gdMainWindow *mainWin; +extern gdMainWindow *G_MainWin; extern Conf G_Conf; +extern Recorder G_Recorder; extern Mixer G_Mixer; #ifdef WITH_VST extern PluginHost G_PluginHost; @@ -54,17 +59,25 @@ extern PluginHost G_PluginHost; using std::string; -int glue_loadChannel(SampleChannel *ch, const char *fname) +static bool __soloSession__ = false; + + +int glue_loadChannel(SampleChannel *ch, const string &fname) { + /* Always stop a channel before loading a new sample in it. This will prevent + issues if tracker is outside the boundaries of the new sample -> segfault. */ + + ch->hardStop(0); + /* save the patch and take the last browser's dir in order to re-use it * the next time */ - G_Conf.samplePath = gDirname(fname); + G_Conf.samplePath = gu_dirname(fname); - int result = ch->load(fname, G_Conf.samplerate, G_Conf.rsmpQuality); + int result = ch->load(fname.c_str(), G_Conf.samplerate, G_Conf.rsmpQuality); if (result == SAMPLE_LOADED_OK) - mainWin->keyboard->updateChannel(ch->guiChannel); + G_MainWin->keyboard->updateChannel(ch->guiChannel); return result; } @@ -75,10 +88,9 @@ int glue_loadChannel(SampleChannel *ch, const char *fname) Channel *glue_addChannel(int column, int type) { - Channel *ch = G_Mixer.addChannel(type); - gChannel *gch = mainWin->keyboard->addChannel(column, ch); - ch->guiChannel = gch; - glue_setChanVol(ch, 1.0, false); // false = not from gui click + Channel *ch = G_Mixer.addChannel(type); + geChannel *gch = G_MainWin->keyboard->addChannel(column, ch); + ch->guiChannel = gch; return ch; } @@ -88,12 +100,12 @@ Channel *glue_addChannel(int column, int type) void glue_deleteChannel(Channel *ch) { - recorder::clearChan(ch->index); + G_Recorder.clearChan(ch->index); #ifdef WITH_VST G_PluginHost.freeStack(PluginHost::CHANNEL, &G_Mixer.mutex_plugins, ch); #endif Fl::lock(); - mainWin->keyboard->deleteChannel(ch->guiChannel); + G_MainWin->keyboard->deleteChannel(ch->guiChannel); Fl::unlock(); G_Mixer.deleteChannel(ch); gu_closeAllSubwindows(); @@ -106,10 +118,13 @@ void glue_deleteChannel(Channel *ch) void glue_freeChannel(Channel *ch) { #ifdef WITH_VST + G_PluginHost.freeStack(PluginHost::CHANNEL, &G_Mixer.mutex_plugins, ch); + ch->guiChannel->fx->full = false; + #endif - mainWin->keyboard->freeChannel(ch->guiChannel); - recorder::clearChan(ch->index); + G_MainWin->keyboard->freeChannel(ch->guiChannel); + G_Recorder.clearChan(ch->index); ch->empty(); } @@ -117,14 +132,349 @@ void glue_freeChannel(Channel *ch) /* -------------------------------------------------------------------------- */ +void glue_toggleArm(Channel *ch, bool gui) +{ + ch->armed = !ch->armed; + if (!gui) + ch->guiChannel->arm->value(ch->armed); +} + + +/* -------------------------------------------------------------------------- */ + + int glue_cloneChannel(Channel *src) { Channel *ch = G_Mixer.addChannel(src->type); - gChannel *gch = mainWin->keyboard->addChannel(src->guiChannel->getColumnIndex(), ch); + geChannel *gch = G_MainWin->keyboard->addChannel(src->guiChannel->getColumnIndex(), ch); ch->guiChannel = gch; ch->copy(src, &G_Mixer.mutex_plugins); - mainWin->keyboard->updateChannel(ch->guiChannel); + G_MainWin->keyboard->updateChannel(ch->guiChannel); return true; } + + +/* -------------------------------------------------------------------------- */ + + +void glue_setChanVol(Channel *ch, float v, bool gui) +{ + ch->volume = v; + + /* also update wave editor if it's shown */ + + gdEditor *editor = (gdEditor*) gu_getSubwindow(G_MainWin, WID_SAMPLE_EDITOR); + if (editor) { + glue_setVolEditor(editor, (SampleChannel*) ch, v, false); + Fl::lock(); + editor->volume->value(v); + Fl::unlock(); + } + + if (!gui) { + Fl::lock(); + ch->guiChannel->vol->value(v); + Fl::unlock(); + } +} + + +/* -------------------------------------------------------------------------- */ + + +void glue_setPitch(gdEditor *win, SampleChannel *ch, float val, bool numeric) +{ + if (numeric) { + if (val <= 0.0f) + val = 0.1000f; + if (val > 4.0f) + val = 4.0000f; + if (win) + win->pitch->value(val); + } + + ch->setPitch(val); + + if (win) { + char buf[16]; + sprintf(buf, "%.4f", val); + Fl::lock(); + win->pitchNum->value(buf); + win->pitchNum->redraw(); + Fl::unlock(); + } +} + + +/* -------------------------------------------------------------------------- */ + + +void glue_setPanning(gdEditor *win, SampleChannel *ch, float val) +{ + if (val < 1.0f) { + ch->panLeft = 1.0f; + ch->panRight= 0.0f + val; + + char buf[8]; + sprintf(buf, "%d L", (int) std::abs((ch->panRight * 100.0f) - 100)); + win->panNum->value(buf); + } + else if (val == 1.0f) { + ch->panLeft = 1.0f; + ch->panRight= 1.0f; + win->panNum->value("C"); + } + else { + ch->panLeft = 2.0f - val; + ch->panRight= 1.0f; + + char buf[8]; + sprintf(buf, "%d R", (int) std::abs((ch->panLeft * 100.0f) - 100)); + win->panNum->value(buf); + } + win->panNum->redraw(); +} + + +/* -------------------------------------------------------------------------- */ + + +void glue_setMute(Channel *ch, bool gui) +{ + if (G_Recorder.active && G_Recorder.canRec(ch)) { + if (!ch->mute) + G_Recorder.startOverdub(ch->index, ACTION_MUTES, G_Mixer.actualFrame); + else + G_Recorder.stopOverdub(G_Mixer.actualFrame); + } + + ch->mute ? ch->unsetMute(false) : ch->setMute(false); + + if (!gui) { + Fl::lock(); + ch->guiChannel->mute->value(ch->mute); + Fl::unlock(); + } +} + + +/* -------------------------------------------------------------------------- */ + + +void glue_setSoloOn(Channel *ch, bool gui) +{ + /* if there's no solo session, store mute configuration of all chans + * and start the session */ + + if (!__soloSession__) { + for (unsigned i=0; imute_s = och->mute; + } + __soloSession__ = true; + } + + ch->solo = !ch->solo; + ch->sendMidiLsolo(); + + /* mute all other channels and unmute this (if muted) */ + + for (unsigned i=0; isolo && !och->mute) { + och->setMute(false); + Fl::lock(); + och->guiChannel->mute->value(true); + Fl::unlock(); + } + } + + if (ch->mute) { + ch->unsetMute(false); + Fl::lock(); + ch->guiChannel->mute->value(false); + Fl::unlock(); + } + + if (!gui) { + Fl::lock(); + ch->guiChannel->solo->value(1); + Fl::unlock(); + } +} + + +/* -------------------------------------------------------------------------- */ + + +void glue_setSoloOff(Channel *ch, bool gui) +{ + /* if this is uniqueSolo, stop solo session and restore mute status, + * else mute this */ + + if (mh_uniqueSolo(ch)) { + __soloSession__ = false; + for (unsigned i=0; imute_s) { + och->setMute(false); + Fl::lock(); + och->guiChannel->mute->value(true); + Fl::unlock(); + } + else { + och->unsetMute(false); + Fl::lock(); + och->guiChannel->mute->value(false); + Fl::unlock(); + } + och->mute_s = false; + } + } + else { + ch->setMute(false); + Fl::lock(); + ch->guiChannel->mute->value(true); + Fl::unlock(); + } + + ch->solo = !ch->solo; + ch->sendMidiLsolo(); + + if (!gui) { + Fl::lock(); + ch->guiChannel->solo->value(0); + Fl::unlock(); + } +} + + +/* -------------------------------------------------------------------------- */ + + +void glue_setBeginEndChannel(gdEditor *win, SampleChannel *ch, int b, int e, + bool recalc, bool check) +{ + if (check) { + if (e > ch->wave->size) + e = ch->wave->size; + if (b < 0) + b = 0; + if (b > ch->wave->size) + b = ch->wave->size-2; + if (b >= ch->end) + b = ch->begin; + if (e <= ch->begin) + e = ch->end; + } + + /* continue only if new values != old values */ + + if (b == ch->begin && e == ch->end) + return; + + /* print mono values */ + + char tmp[16]; + sprintf(tmp, "%d", b/2); + win->chanStart->value(tmp); + + tmp[0] = '\0'; + sprintf(tmp, "%d", e/2); + win->chanEnd->value(tmp); + + ch->setBegin(b); + ch->setEnd(e); + + /* Recalc is not needed when the user drags the bars directly over the + waveform */ + + if (recalc) { + win->waveTools->waveform->recalcPoints(); + win->waveTools->waveform->redraw(); + } +} + + +/* -------------------------------------------------------------------------- */ + + +void glue_setBoost(gdEditor *win, SampleChannel *ch, float val, bool numeric) +{ + if (numeric) { + if (val > 20.0f) + val = 20.0f; + else if (val < 0.0f) + val = 0.0f; + + float linear = pow(10, (val / 20)); // linear = 10^(dB/20) + + ch->boost = linear; + + char buf[10]; + sprintf(buf, "%.2f", val); + win->boostNum->value(buf); + win->boostNum->redraw(); + + win->boost->value(linear); + win->boost->redraw(); /// inutile + } + else { + ch->boost = val; + char buf[10]; + sprintf(buf, "%.2f", 20*log10(val)); + win->boostNum->value(buf); + win->boostNum->redraw(); + } +} + + +/* -------------------------------------------------------------------------- */ + + +void glue_setVolEditor(class gdEditor *win, SampleChannel *ch, float val, + bool numeric) +{ + if (numeric) { + if (val > 0.0f) + val = 0.0f; + else if (val < -60.0f) + val = -INFINITY; + + float linear = pow(10, (val / 20)); // linear = 10^(dB/20) + + ch->volume = linear; + + win->volume->value(linear); + win->volume->redraw(); + + char buf[10]; + if (val > -INFINITY) + sprintf(buf, "%.2f", val); + else + sprintf(buf, "-inf"); + win->volumeNum->value(buf); + win->volumeNum->redraw(); + + ch->guiChannel->vol->value(linear); + ch->guiChannel->vol->redraw(); + } + else { + ch->volume = val; + + float dbVal = 20 * log10(val); + char buf[10]; + if (dbVal > -INFINITY) + sprintf(buf, "%.2f", dbVal); + else + sprintf(buf, "-inf"); + + win->volumeNum->value(buf); + win->volumeNum->redraw(); + + ch->guiChannel->vol->value(val); + ch->guiChannel->vol->redraw(); + } +} diff --git a/src/glue/channel.h b/src/glue/channel.h index 2da2bf1..25523e0 100644 --- a/src/glue/channel.h +++ b/src/glue/channel.h @@ -32,10 +32,7 @@ #define GLUE_CHANNEL_H -#include "../core/patch.h" - - -using std::string; +#include /* addChannel @@ -46,7 +43,7 @@ class Channel *glue_addChannel(int column, int type); /* loadChannel * fill an existing channel with a wave. */ -int glue_loadChannel(class SampleChannel *ch, const char *fname); +int glue_loadChannel(class SampleChannel *ch, const std::string &fname); /* deleteChannel * Remove a channel from Mixer. */ @@ -60,8 +57,41 @@ void glue_freeChannel(class Channel *ch); /* cloneChannel * Make an exact copy of Channel *ch. */ - + int glue_cloneChannel(class Channel *ch); +/* toggle/set* + * Toggle or set several channel properties. If gui == true the signal comes + * from a manual interaction on the GUI, otherwise it's a MIDI/Jack/external + * signal. */ + +void glue_toggleArm(class Channel *ch, bool gui=true); +void glue_setChanVol(class Channel *ch, float v, bool gui=true); +void glue_setMute(class Channel *ch, bool gui=true); +void glue_setSoloOn (class Channel *ch, bool gui=true); +void glue_setSoloOff(class Channel *ch, bool gui=true); + +void glue_setPitch(class gdEditor *win, class SampleChannel *ch, float val, + bool numeric); + +void glue_setPanning(class gdEditor *win, class SampleChannel *ch, float val); + +/* setBeginEndChannel + * sets start/end points in the sample editor. Recalc=false: don't recalc + * internal position. check=true: check the points' consistency */ + +void glue_setBeginEndChannel(class gdEditor *win, class SampleChannel *ch, + int b, int e, bool recalc=false, bool check=true); + +void glue_setBoost(class gdEditor *win, class SampleChannel *ch, float val, + bool numeric); + +/* setVolEditor + * handles the volume inside the SAMPLE EDITOR (not the main gui). The + * numeric flag tells if we want to handle the dial or the numeric input + * field. */ + +void glue_setVolEditor(class gdEditor *win, class SampleChannel *ch, float val, + bool numeric); #endif diff --git a/src/glue/glue.cpp b/src/glue/glue.cpp deleted file mode 100644 index ff1670c..0000000 --- a/src/glue/glue.cpp +++ /dev/null @@ -1,972 +0,0 @@ -/* ----------------------------------------------------------------------------- - * - * Giada - Your Hardcore Loopmachine - * - * glue - * Intermediate layer GUI <-> CORE. - * - * How to know if you need another glue_ function? Ask yourself if the - * new action will ever be called via MIDI or keyboard/mouse. If yes, - * put it here. - * - * ----------------------------------------------------------------------------- - * - * Copyright (C) 2010-2016 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 "../gui/elems/ge_waveform.h" -#include "../gui/elems/ge_mixed.h" -#include "../gui/elems/ge_channel.h" -#include "../gui/elems/ge_sampleChannel.h" -#include "../gui/elems/ge_waveTools.h" -#include "../gui/elems/ge_keyboard.h" -#include "../gui/dialogs/gd_mainWindow.h" -#include "../gui/dialogs/gd_editor.h" -#include "../gui/dialogs/gd_warnings.h" -#include "../utils/gui_utils.h" -#include "../utils/utils.h" -#include "../utils/log.h" -#include "../core/mixerHandler.h" -#include "../core/mixer.h" -#include "../core/recorder.h" -#include "../core/wave.h" -#include "../core/pluginHost.h" -#include "../core/channel.h" -#include "../core/sampleChannel.h" -#include "../core/midiChannel.h" -#include "../core/kernelMidi.h" -#include "../core/patch_DEPR_.h" -#include "../core/conf.h" -#include "glue.h" - - -extern gdMainWindow *mainWin; -extern Mixer G_Mixer; -extern Patch_DEPR_ G_Patch_DEPR_; -extern Conf G_Conf; -extern bool G_audio_status; -#ifdef WITH_VST -extern PluginHost G_PluginHost; -#endif - - -static bool __soloSession__ = false; - - -void glue_setBpm(const char *v1, const char *v2) -{ - char buf[6]; - float value = atof(v1) + (atof(v2)/10); - if (value < 20.0f) { - value = 20.0f; - sprintf(buf, "20.0"); - } - else - sprintf(buf, "%s.%s", v1, !strcmp(v2, "") ? "0" : v2); - - /* a value such as atof("120.1") will never be 120.1 but 120.0999999, - * because of the rounding error. So we pass the real "wrong" value to - * G_Mixer and we show the nice looking (but fake) one to the GUI. */ - - float old_bpm = G_Mixer.bpm; - G_Mixer.bpm = value; - G_Mixer.updateFrameBars(); - - /* inform recorder and actionEditor of the change */ - - recorder::updateBpm(old_bpm, value, G_Mixer.quanto); - gu_refreshActionEditor(); - - mainWin->timing->setBpm(buf); - gLog("[glue] Bpm changed to %s (real=%f)\n", buf, G_Mixer.bpm); -} - - -/* -------------------------------------------------------------------------- */ - - -void glue_setBeats(int beats, int bars, bool expand) -{ - /* temp vars to store old data (they are necessary) */ - - int oldvalue = G_Mixer.beats; - unsigned oldfpb = G_Mixer.totalFrames; - - if (beats > MAX_BEATS) - G_Mixer.beats = MAX_BEATS; - else if (beats < 1) - G_Mixer.beats = 1; - else - G_Mixer.beats = beats; - - /* update bars - bars cannot be greate than beats and must be a sub - * multiple of beats. If not, approximation to the nearest (and greater) - * value available. */ - - if (bars > G_Mixer.beats) - G_Mixer.bars = G_Mixer.beats; - else if (bars <= 0) - G_Mixer.bars = 1; - else if (beats % bars != 0) { - G_Mixer.bars = bars + (beats % bars); - if (beats % G_Mixer.bars != 0) // it could be an odd value, let's check it (and avoid it) - G_Mixer.bars = G_Mixer.bars - (beats % G_Mixer.bars); - } - else - G_Mixer.bars = bars; - - G_Mixer.updateFrameBars(); - - /* update recorded actions */ - - if (expand) { - if (G_Mixer.beats > oldvalue) - recorder::expand(oldfpb, G_Mixer.totalFrames); - //else if (G_Mixer.beats < oldvalue) - // recorder::shrink(G_Mixer.totalFrames); - } - - mainWin->timing->setMeter(G_Mixer.beats, G_Mixer.bars); - gu_refreshActionEditor(); // in case the action editor is open -} - - -/* -------------------------------------------------------------------------- */ - - -void glue_startStopSeq(bool gui) -{ - G_Mixer.running ? glue_stopSeq(gui) : glue_startSeq(gui); -} - - -/* -------------------------------------------------------------------------- */ - - -void glue_startSeq(bool gui) -{ - G_Mixer.running = true; - - if (gui) { -#ifdef __linux__ - kernelAudio::jackStart(); -#endif - } - - if (G_Conf.midiSync == MIDI_SYNC_CLOCK_M) { - kernelMidi::send(MIDI_START, -1, -1); - kernelMidi::send(MIDI_POSITION_PTR, 0, 0); - } - - if (gui) Fl::lock(); - mainWin->controller->updatePlay(1); - if (gui) Fl::unlock(); -} - - -/* -------------------------------------------------------------------------- */ - - -void glue_stopSeq(bool gui) { - - mh_stopSequencer(); - - if (G_Conf.midiSync == MIDI_SYNC_CLOCK_M) - kernelMidi::send(MIDI_STOP, -1, -1); - -#ifdef __linux__ - if (gui) - kernelAudio::jackStop(); -#endif - - /* what to do if we stop the sequencer and some action recs are active? - * Deactivate the button and delete any 'rec on' status */ - - if (recorder::active) { - recorder::active = false; - if (gui) Fl::lock(); - mainWin->controller->updateRecAction(0); - if (gui) Fl::unlock(); - } - - /* if input recs are active (who knows why) we must deactivate them. - * One might stop the sequencer while an input rec is running. */ - - if (G_Mixer.chanInput != NULL) { - mh_stopInputRec(); - if (gui) Fl::lock(); - mainWin->controller->updateRecInput(0); - if (gui) Fl::unlock(); - } - - if (gui) Fl::lock(); - mainWin->controller->updatePlay(0); - if (gui) Fl::unlock(); -} - - -/* -------------------------------------------------------------------------- */ - - -void glue_rewindSeq() { - mh_rewindSequencer(); - if (G_Conf.midiSync == MIDI_SYNC_CLOCK_M) - kernelMidi::send(MIDI_POSITION_PTR, 0, 0); -} - - -/* -------------------------------------------------------------------------- */ - - -void glue_startStopActionRec() { - recorder::active ? glue_stopActionRec() : glue_startActionRec(); -} - - -/* -------------------------------------------------------------------------- */ - - -void glue_startActionRec() { - if (G_audio_status == false) - return; - if (!G_Mixer.running) - glue_startSeq(); // start the sequencer for convenience - recorder::active = true; - - Fl::lock(); - mainWin->controller->updateRecAction(1); - Fl::unlock(); -} - - -/* -------------------------------------------------------------------------- */ - - -void glue_stopActionRec() { - - /* stop the recorder and sort new actions */ - - recorder::active = false; - recorder::sortActions(); - - for (unsigned i=0; itype == CHANNEL_SAMPLE) { - SampleChannel *ch = (SampleChannel*) G_Mixer.channels.at(i); - if (ch->hasActions) - ch->readActions = true; - else - ch->readActions = false; - mainWin->keyboard->setChannelWithActions((gSampleChannel*)ch->guiChannel); - } - - Fl::lock(); - mainWin->controller->updateRecAction(0); - Fl::unlock(); - - /* in case acton editor is on, refresh it */ - - gu_refreshActionEditor(); -} - - -/* -------------------------------------------------------------------------- */ - - -void glue_startStopReadingRecs(SampleChannel *ch, bool gui) { - if (ch->readActions) - glue_stopReadingRecs(ch, gui); - else - glue_startReadingRecs(ch, gui); -} - - -/* -------------------------------------------------------------------------- */ - - -void glue_startReadingRecs(SampleChannel *ch, bool gui) { - if (G_Conf.treatRecsAsLoops) - ch->recStatus = REC_WAITING; - else - ch->setReadActions(true, G_Conf.recsStopOnChanHalt); - if (!gui) { - gSampleChannel *gch = (gSampleChannel*)ch->guiChannel; - if (gch->readActions) { // if button exists - Fl::lock(); - gch->readActions->value(1); - Fl::unlock(); - } - } -} - - -/* -------------------------------------------------------------------------- */ - - -void glue_stopReadingRecs(SampleChannel *ch, bool gui) { - - /* if "treatRecsAsLoop" wait until the sequencer reaches beat 0, so put - * the channel in REC_ENDING status */ - - if (G_Conf.treatRecsAsLoops) - ch->recStatus = REC_ENDING; - else - ch->setReadActions(false, G_Conf.recsStopOnChanHalt); - if (!gui) { - gSampleChannel *gch = (gSampleChannel*)ch->guiChannel; - if (gch->readActions) { // if button exists - Fl::lock(); - gch->readActions->value(0); - Fl::unlock(); - } - } -} - - -/* -------------------------------------------------------------------------- */ - - -void glue_quantize(int val) { - G_Mixer.quantize = val; - G_Mixer.updateQuanto(); -} - - -/* -------------------------------------------------------------------------- */ - - -void glue_setChanVol(Channel *ch, float v, bool gui) { - - ch->volume = v; - - /* also update wave editor if it's shown */ - - gdEditor *editor = (gdEditor*) gu_getSubwindow(mainWin, WID_SAMPLE_EDITOR); - if (editor) { - glue_setVolEditor(editor, (SampleChannel*) ch, v, false); - Fl::lock(); - editor->volume->value(v); - Fl::unlock(); - } - - if (!gui) { - Fl::lock(); - ch->guiChannel->vol->value(v); - Fl::unlock(); - } -} - - -/* -------------------------------------------------------------------------- */ - - -void glue_setOutVol(float v, bool gui) { - G_Mixer.outVol = v; - if (!gui) { - Fl::lock(); - mainWin->inOut->setOutVol(v); - Fl::unlock(); - } -} - - -/* -------------------------------------------------------------------------- */ - - -void glue_setInVol(float v, bool gui) -{ - G_Mixer.inVol = v; - if (!gui) { - Fl::lock(); - mainWin->inOut->setInVol(v); - Fl::unlock(); - } -} - - -/* -------------------------------------------------------------------------- */ - - -void glue_clearAllSamples() -{ - G_Mixer.running = false; - for (unsigned i=0; iempty(); - G_Mixer.channels.at(i)->guiChannel->reset(); - } - recorder::init(); - return; -} - - -/* -------------------------------------------------------------------------- */ - - -void glue_clearAllRecs() -{ - recorder::init(); - gu_updateControls(); -} - - -/* -------------------------------------------------------------------------- */ - - -void glue_resetToInitState(bool resetGui, bool createColumns) -{ - G_Patch_DEPR_.setDefault(); - G_Mixer.close(); - G_Mixer.init(); - recorder::init(); -#ifdef WITH_VST - G_PluginHost.freeAllStacks(&G_Mixer.channels, &G_Mixer.mutex_plugins); -#endif - - mainWin->keyboard->clear(); - if (createColumns) - mainWin->keyboard->init(); - - if (resetGui) - gu_updateControls(); -} - - -/* -------------------------------------------------------------------------- */ - - -void glue_startStopMetronome(bool gui) -{ - G_Mixer.metronome = !G_Mixer.metronome; - if (!gui) { - Fl::lock(); - mainWin->controller->updateMetronome(G_Mixer.metronome); - Fl::unlock(); - } -} - - -/* -------------------------------------------------------------------------- */ - - -void glue_setBeginEndChannel(gdEditor *win, SampleChannel *ch, int b, int e, bool recalc, bool check) -{ - if (check) { - if (e > ch->wave->size) - e = ch->wave->size; - if (b < 0) - b = 0; - if (b > ch->wave->size) - b = ch->wave->size-2; - if (b >= ch->end) - b = ch->begin; - if (e <= ch->begin) - e = ch->end; - } - - /* continue only if new values != old values */ - - if (b == ch->begin && e == ch->end) - return; - - /* print mono values */ - - char tmp[16]; - sprintf(tmp, "%d", b/2); - win->chanStart->value(tmp); - - tmp[0] = '\0'; - sprintf(tmp, "%d", e/2); - win->chanEnd->value(tmp); - - ch->setBegin(b); - ch->setEnd(e); - - /* recalc is not needed when the user drags the bars directly over the waveform */ - - if (recalc) { - win->waveTools->waveform->recalcPoints(); // importante, altrimenti non si vedono - win->waveTools->waveform->redraw(); - } -} - - -/* -------------------------------------------------------------------------- */ - - -void glue_setBoost(gdEditor *win, SampleChannel *ch, float val, bool numeric) -{ - if (numeric) { - if (val > 20.0f) - val = 20.0f; - else if (val < 0.0f) - val = 0.0f; - - float linear = pow(10, (val / 20)); // linear = 10^(dB/20) - - ch->boost = linear; - - char buf[10]; - sprintf(buf, "%.2f", val); - win->boostNum->value(buf); - win->boostNum->redraw(); - - win->boost->value(linear); - win->boost->redraw(); /// inutile - } - else { - ch->boost = val; - char buf[10]; - sprintf(buf, "%.2f", 20*log10(val)); - win->boostNum->value(buf); - win->boostNum->redraw(); - } -} - - -/* -------------------------------------------------------------------------- */ - - -void glue_setVolEditor(class gdEditor *win, SampleChannel *ch, float val, bool numeric) -{ - if (numeric) { - if (val > 0.0f) - val = 0.0f; - else if (val < -60.0f) - val = -INFINITY; - - float linear = pow(10, (val / 20)); // linear = 10^(dB/20) - - ch->volume = linear; - - win->volume->value(linear); - win->volume->redraw(); - - char buf[10]; - if (val > -INFINITY) - sprintf(buf, "%.2f", val); - else - sprintf(buf, "-inf"); - win->volumeNum->value(buf); - win->volumeNum->redraw(); - - ch->guiChannel->vol->value(linear); - ch->guiChannel->vol->redraw(); - } - else { - ch->volume = val; - - float dbVal = 20 * log10(val); - char buf[10]; - if (dbVal > -INFINITY) - sprintf(buf, "%.2f", dbVal); - else - sprintf(buf, "-inf"); - - win->volumeNum->value(buf); - win->volumeNum->redraw(); - - ch->guiChannel->vol->value(val); - ch->guiChannel->vol->redraw(); - } -} - - -/* -------------------------------------------------------------------------- */ - - -void glue_setMute(Channel *ch, bool gui) -{ - if (recorder::active && recorder::canRec(ch)) { - if (!ch->mute) - recorder::startOverdub(ch->index, ACTION_MUTES, G_Mixer.actualFrame); - else - recorder::stopOverdub(G_Mixer.actualFrame); - } - - ch->mute ? ch->unsetMute(false) : ch->setMute(false); - - if (!gui) { - Fl::lock(); - ch->guiChannel->mute->value(ch->mute); - Fl::unlock(); - } -} - - -/* -------------------------------------------------------------------------- */ - - -void glue_setSoloOn(Channel *ch, bool gui) -{ - /* if there's no solo session, store mute configuration of all chans - * and start the session */ - - if (!__soloSession__) { - for (unsigned i=0; imute_s = och->mute; - } - __soloSession__ = true; - } - - ch->solo = !ch->solo; - ch->sendMidiLsolo(); - - /* mute all other channels and unmute this (if muted) */ - - for (unsigned i=0; isolo && !och->mute) { - och->setMute(false); - Fl::lock(); - och->guiChannel->mute->value(true); - Fl::unlock(); - } - } - - if (ch->mute) { - ch->unsetMute(false); - Fl::lock(); - ch->guiChannel->mute->value(false); - Fl::unlock(); - } - - if (!gui) { - Fl::lock(); - ch->guiChannel->solo->value(1); - Fl::unlock(); - } -} - - -/* -------------------------------------------------------------------------- */ - - -void glue_setSoloOff(Channel *ch, bool gui) -{ - /* if this is uniqueSolo, stop solo session and restore mute status, - * else mute this */ - - if (mh_uniqueSolo(ch)) { - __soloSession__ = false; - for (unsigned i=0; imute_s) { - och->setMute(false); - Fl::lock(); - och->guiChannel->mute->value(true); - Fl::unlock(); - } - else { - och->unsetMute(false); - Fl::lock(); - och->guiChannel->mute->value(false); - Fl::unlock(); - } - och->mute_s = false; - } - } - else { - ch->setMute(false); - Fl::lock(); - ch->guiChannel->mute->value(true); - Fl::unlock(); - } - - ch->solo = !ch->solo; - ch->sendMidiLsolo(); - - if (!gui) { - Fl::lock(); - ch->guiChannel->solo->value(0); - Fl::unlock(); - } -} - - -/* -------------------------------------------------------------------------- */ - - -void glue_setPanning(class gdEditor *win, SampleChannel *ch, float val) -{ - if (val < 1.0f) { - ch->panLeft = 1.0f; - ch->panRight= 0.0f + val; - - char buf[8]; - sprintf(buf, "%d L", (int) abs((ch->panRight * 100.0f) - 100)); - win->panNum->value(buf); - } - else if (val == 1.0f) { - ch->panLeft = 1.0f; - ch->panRight= 1.0f; - win->panNum->value("C"); - } - else { - ch->panLeft = 2.0f - val; - ch->panRight= 1.0f; - - char buf[8]; - sprintf(buf, "%d R", (int) abs((ch->panLeft * 100.0f) - 100)); - win->panNum->value(buf); - } - win->panNum->redraw(); -} - - -/* -------------------------------------------------------------------------- */ - - -void glue_startStopInputRec(bool gui, bool alert) -{ - if (G_Mixer.chanInput == NULL) { - if (!glue_startInputRec(gui)) { - if (alert) gdAlert("No channels available for recording."); - else gLog("[glue] no channels available for recording\n"); - } - } - else - glue_stopInputRec(gui); -} - - -/* -------------------------------------------------------------------------- */ - - -int glue_startInputRec(bool gui) -{ - if (G_audio_status == false) - return -1; - - SampleChannel *ch = mh_startInputRec(); - if (ch == NULL) { // no chans available - Fl::lock(); - mainWin->controller->updateRecInput(0); - Fl::unlock(); - return 0; - } - - if (!G_Mixer.running) { - glue_startSeq(); - Fl::lock(); - mainWin->controller->updatePlay(1); - Fl::unlock(); - } - - glue_setChanVol(ch, 1.0f, false); // false = not from gui click - - ch->guiChannel->mainButton->label(ch->wave->name.c_str()); - - if (!gui) { - Fl::lock(); - mainWin->controller->updateRecInput(1); - Fl::unlock(); - } - - return 1; - -} - - -/* -------------------------------------------------------------------------- */ - - -int glue_stopInputRec(bool gui) -{ - SampleChannel *ch = mh_stopInputRec(); - - if (ch->mode & (LOOP_BASIC | LOOP_ONCE | LOOP_REPEAT)) - ch->start(0, true, G_Mixer.quantize, G_Mixer.running); // on frame 0: user-generated event - - if (!gui) { - Fl::lock(); - mainWin->controller->updateRecInput(0); - Fl::unlock(); - } - - return 1; -} - - -/* -------------------------------------------------------------------------- */ - - -void glue_keyPress(Channel *ch, bool ctrl, bool shift) -{ - if (ch->type == CHANNEL_SAMPLE) - glue_keyPress((SampleChannel*)ch, ctrl, shift); - else - glue_keyPress((MidiChannel*)ch, ctrl, shift); -} - - -/* -------------------------------------------------------------------------- */ - - -void glue_keyRelease(Channel *ch, bool ctrl, bool shift) -{ - if (ch->type == CHANNEL_SAMPLE) - glue_keyRelease((SampleChannel*)ch, ctrl, shift); -} - - -/* -------------------------------------------------------------------------- */ - - -void glue_keyPress(MidiChannel *ch, bool ctrl, bool shift) -{ - if (ctrl) - glue_setMute(ch); - else - if (shift) - ch->kill(0); // on frame 0: user-generated event - else - ch->start(0, true, G_Mixer.quantize, G_Mixer.running); // on frame 0: user-generated event -} - - -/* -------------------------------------------------------------------------- */ - - -void glue_keyPress(SampleChannel *ch, bool ctrl, bool shift) -{ - /* case CTRL */ - - if (ctrl) - glue_setMute(ch); - - /* case SHIFT - * - * action recording on: - * if seq is playing, rec a killchan - * action recording off: - * if chan has recorded events: - * | if seq is playing OR channel 'c' is stopped, de/activate recs - * | else kill chan - * else kill chan */ - - else - if (shift) { - if (recorder::active) { - if (G_Mixer.running) { - ch->kill(0); // on frame 0: user-generated event - if (recorder::canRec(ch) && !(ch->mode & LOOP_ANY)) // don't record killChan actions for LOOP channels - recorder::rec(ch->index, ACTION_KILLCHAN, G_Mixer.actualFrame); - } - } - else { - if (ch->hasActions) { - if (G_Mixer.running || ch->status == STATUS_OFF) - ch->readActions ? glue_stopReadingRecs(ch) : glue_startReadingRecs(ch); - else - ch->kill(0); // on frame 0: user-generated event - } - else - ch->kill(0); // on frame 0: user-generated event - } - } - - /* case no modifier */ - - else { - - /* record now if the quantizer is off, otherwise let mixer to handle it - * when a quantoWait has passed. Moreover, KEYPRESS and KEYREL are - * meaningless for loop modes */ - - if (G_Mixer.quantize == 0 && - recorder::canRec(ch) && - !(ch->mode & LOOP_ANY)) - { - if (ch->mode == SINGLE_PRESS) - recorder::startOverdub(ch->index, ACTION_KEYS, G_Mixer.actualFrame); - else - recorder::rec(ch->index, ACTION_KEYPRESS, G_Mixer.actualFrame); - } - - ch->start(0, true, G_Mixer.quantize, G_Mixer.running); // on frame 0: user-generated event - } - - /* the GUI update is done by gui_refresh() */ -} - - -/* -------------------------------------------------------------------------- */ - - -void glue_keyRelease(SampleChannel *ch, bool ctrl, bool shift) -{ - if (!ctrl && !shift) { - ch->stop(); - - /* record a key release only if channel is single_press. For any - * other mode the KEY REL is meaningless. */ - - if (ch->mode == SINGLE_PRESS && recorder::canRec(ch)) - recorder::stopOverdub(G_Mixer.actualFrame); - } - - /* the GUI update is done by gui_refresh() */ - -} - - -/* -------------------------------------------------------------------------- */ - - -void glue_setPitch(class gdEditor *win, SampleChannel *ch, float val, bool numeric) -{ - if (numeric) { - if (val <= 0.0f) - val = 0.1000f; - if (val > 4.0f) - val = 4.0000f; - if (win) - win->pitch->value(val); - } - - ch->setPitch(val); - - if (win) { - char buf[16]; - sprintf(buf, "%.4f", val); - Fl::lock(); - win->pitchNum->value(buf); - win->pitchNum->redraw(); - Fl::unlock(); - } -} - - -/* -------------------------------------------------------------------------- */ - - -/* never expand or shrink recordings (last param of setBeats = false): - * this is live manipulation */ - -void glue_beatsMultiply() -{ - glue_setBeats(G_Mixer.beats*2, G_Mixer.bars, false); -} - -void glue_beatsDivide() -{ - glue_setBeats(G_Mixer.beats/2, G_Mixer.bars, false); -} diff --git a/src/glue/glue.h b/src/glue/glue.h deleted file mode 100644 index ef7076f..0000000 --- a/src/glue/glue.h +++ /dev/null @@ -1,148 +0,0 @@ -/* --------------------------------------------------------------------- - * - * Giada - Your Hardcore Loopmachine - * - * glue - * Intermediate layer GUI <-> CORE. - * - * How to know if you need another glue_ function? Ask yourself if the - * new action will ever be called via MIDI or keyboard/mouse. If yes, - * put it here. - * - * --------------------------------------------------------------------- - * - * Copyright (C) 2010-2016 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 GLUE_H -#define GLUE_H - - -/* keyPress / keyRelease - * handle the key pressure, either via mouse/keyboard or MIDI. If gui - * is true it means that the event comes from the main window (mouse, - * keyb or MIDI), otherwise the event comes from the action recorder. */ - -void glue_keyPress (class Channel *ch, bool ctrl=0, bool shift=0); -void glue_keyPress (class SampleChannel *ch, bool ctrl=0, bool shift=0); -void glue_keyPress (class MidiChannel *ch, bool ctrl=0, bool shift=0); -void glue_keyRelease(class Channel *ch, bool ctrl=0, bool shift=0); -void glue_keyRelease(class SampleChannel *ch, bool ctrl=0, bool shift=0); - -void glue_setBpm(const char *v1, const char *v2); -void glue_setBeats(int beats, int bars, bool expand); - -/* start, stop, rewind sequencer - * if gui == true the signal comes from an internal interaction on the - * GUI, otherwise it's a MIDI/Jack/external signal. */ - -void glue_startStopSeq(bool gui=true); -void glue_startSeq (bool gui=true); -void glue_stopSeq (bool gui=true); -void glue_rewindSeq (); - -/* start/stopActionRec - * handle the action recording. */ - -void glue_startStopActionRec(); -void glue_startActionRec(); -void glue_stopActionRec(); - -/* start/stopInputRec - * handle the input recording (take). If gui == true the signal comes - * from an internal interaction on the GUI, otherwise it's a - * MIDI/Jack/external signal. Alert displays or not the popup message - * if there are no available channels. */ - -void glue_startStopInputRec(bool gui=true, bool alert=true); -int glue_startInputRec (bool gui=true); -int glue_stopInputRec (bool gui=true); - -/* start/stopReadingRecs - * handle the 'R' button. If gui == true the signal comes from an - * internal interaction on the GUI, otherwise it's a MIDI/Jack/external - * signal. */ - -void glue_startStopReadingRecs(class SampleChannel *ch, bool gui=true); -void glue_startReadingRecs (class SampleChannel *ch, bool gui=true); -void glue_stopReadingRecs (class SampleChannel *ch, bool gui=true); - -void glue_quantize(int val); - -void glue_setChanVol(class Channel *ch, float v, bool gui=true); -void glue_setOutVol (float v, bool gui=true); -void glue_setInVol (float v, bool gui=true); - -void glue_setPanning(class gdEditor *win, class SampleChannel *ch, float val); - -void glue_clearAllSamples(); -void glue_clearAllRecs(); - -/* resetToInitState - * reset Giada to init state. If resetGui also refresh all widgets. If - * createColumns also build initial empty columns. */ - -void glue_resetToInitState(bool resetGui=true, bool createColumns=true); - -void glue_startStopMetronome(bool gui=true); - -/* setBeginEndChannel - * sets start/end points in the sample editor. - * Recalc=false: don't recalc internal position - * check=true: check the points' consistency */ - -/** FIXME - nobody will call this via MIDI/keyb/mouse! */ -void glue_setBeginEndChannel(class gdEditor *win, class SampleChannel *ch, int b, int e, - bool recalc=false, bool check=true); - -/** FIXME - nobody will call this via MIDI/keyb/mouse! */ -void glue_setBoost(class gdEditor *win, class SampleChannel *ch, float val, bool numeric); - -void glue_setPitch(class gdEditor *win, class SampleChannel *ch, float val, bool numeric); - -/* setVolEditor - * handles the volume inside the SAMPLE EDITOR (not the main gui). The - * numeric flag tells if we want to handle the dial or the numeric input - * field. */ - - /** FIXME - nobody will call this via MIDI/keyb/mouse! */ -void glue_setVolEditor(class gdEditor *win, class SampleChannel *ch, float val, bool numeric); - -/* mute - * set mute on or off. If gui == true the signal comes from an internal - * interaction on the GUI, otherwise it's a MIDI/Jack/external signal. */ - -void glue_setMute(class Channel *ch, bool gui=true); - -/* solo on/off - * set solo on and off. If gui == true the signal comes from an internal - * interaction on the GUI, otherwise it's a MIDI/Jack/external signal. */ - -void glue_setSoloOn (class Channel *ch, bool gui=true); -void glue_setSoloOff(class Channel *ch, bool gui=true); - -/* beatsDivide/Multiply - * shrinks or enlarges the number of beats by 2. */ - -void glue_beatsMultiply(); -void glue_beatsDivide(); - -#endif diff --git a/src/glue/io.cpp b/src/glue/io.cpp new file mode 100644 index 0000000..bdc932e --- /dev/null +++ b/src/glue/io.cpp @@ -0,0 +1,327 @@ +/* ----------------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * glue + * Intermediate layer GUI <-> CORE. + * + * How to know if you need another glue_ function? Ask yourself if the + * new action will ever be called via MIDI or keyboard/mouse. If yes, + * put it here. + * + * ----------------------------------------------------------------------------- + * + * Copyright (C) 2010-2016 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 "../gui/dialogs/gd_mainWindow.h" +#include "../gui/dialogs/gd_warnings.h" +#include "../gui/elems/ge_keyboard.h" +#include "../gui/elems/channel.h" +#include "../gui/elems/sampleChannel.h" +#include "../utils/gui.h" +#include "../utils/log.h" +#include "../core/recorder.h" +#include "../core/mixer.h" +#include "../core/mixerHandler.h" +#include "../core/wave.h" +#include "../core/channel.h" +#include "../core/sampleChannel.h" +#include "../core/midiChannel.h" +#include "main.h" +#include "channel.h" +#include "io.h" + + +extern Recorder G_Recorder; +extern bool G_audio_status; +extern Mixer G_Mixer; +extern gdMainWindow *G_MainWin; + + +void glue_keyPress(Channel *ch, bool ctrl, bool shift) +{ + if (ch->type == CHANNEL_SAMPLE) + glue_keyPress((SampleChannel*)ch, ctrl, shift); + else + glue_keyPress((MidiChannel*)ch, ctrl, shift); +} + + +/* -------------------------------------------------------------------------- */ + + +void glue_keyRelease(Channel *ch, bool ctrl, bool shift) +{ + if (ch->type == CHANNEL_SAMPLE) + glue_keyRelease((SampleChannel*)ch, ctrl, shift); +} + + +/* -------------------------------------------------------------------------- */ + + +void glue_keyPress(MidiChannel *ch, bool ctrl, bool shift) +{ + if (ctrl) + glue_setMute(ch); + else + if (shift) + ch->kill(0); // on frame 0: user-generated event + else + ch->start(0, true, G_Mixer.quantize, G_Mixer.running, false, true); // on frame 0: user-generated event +} + + +/* -------------------------------------------------------------------------- */ + + +void glue_keyPress(SampleChannel *ch, bool ctrl, bool shift) +{ + /* case CTRL */ + + if (ctrl) + glue_setMute(ch); + + /* case SHIFT + * + * action recording on: + * if seq is playing, rec a killchan + * action recording off: + * if chan has recorded events: + * | if seq is playing OR channel 'c' is stopped, de/activate recs + * | else kill chan + * else kill chan */ + + else + if (shift) { + if (G_Recorder.active) { + if (G_Mixer.running) { + ch->kill(0); // on frame 0: user-generated event + if (G_Recorder.canRec(ch) && !(ch->mode & LOOP_ANY)) // don't record killChan actions for LOOP channels + G_Recorder.rec(ch->index, ACTION_KILLCHAN, G_Mixer.actualFrame); + } + } + else { + if (ch->hasActions) { + if (G_Mixer.running || ch->status == STATUS_OFF) + ch->readActions ? glue_stopReadingRecs(ch) : glue_startReadingRecs(ch); + else + ch->kill(0); // on frame 0: user-generated event + } + else + ch->kill(0); // on frame 0: user-generated event + } + } + else { /* case no modifier */ + + /* record now if the quantizer is off, otherwise let mixer to handle it + * when a quantoWait has passed. Moreover, KEYPRESS and KEYREL are + * meaningless for loop modes */ + + if (G_Mixer.quantize == 0 && + G_Recorder.canRec(ch) && + !(ch->mode & LOOP_ANY)) + { + if (ch->mode == SINGLE_PRESS) + G_Recorder.startOverdub(ch->index, ACTION_KEYS, G_Mixer.actualFrame); + else { + G_Recorder.rec(ch->index, ACTION_KEYPRESS, G_Mixer.actualFrame); + + /* Why return here? You record an action (as done on line 148) and then + you call ch->start (line 165): Mixer, which is on another thread, reads + your newly recorded action if you have readActions == true, and then + ch->start kicks in right after it (as done on line 165). + The result: Mixer plays the channel (due to the new action) but ch->start + kills it right away (because the sample is playing). Fix: call ch->start + only if you are not recording anything, i.e. let Mixer play it. */ + + if (ch->readActions) + return; + } + } + + /* This is a user-generated event, so it's on frame 0 */ + + ch->start(0, true, G_Mixer.quantize, G_Mixer.running, false, true); + } + + /* the GUI update is done by gui_refresh() */ +} + + +/* -------------------------------------------------------------------------- */ + + +void glue_keyRelease(SampleChannel *ch, bool ctrl, bool shift) +{ + if (ctrl || shift) + return; + + ch->stop(); + + /* record a key release only if channel is single_press. For any + * other mode the KEY REL is meaningless. */ + + if (ch->mode == SINGLE_PRESS && G_Recorder.canRec(ch)) + G_Recorder.stopOverdub(G_Mixer.actualFrame); + + /* the GUI update is done by gui_refresh() */ + +} + + +/* -------------------------------------------------------------------------- */ + + +void glue_startStopActionRec(bool gui) +{ + G_Recorder.active ? glue_stopActionRec(gui) : glue_startActionRec(gui); +} + + +/* -------------------------------------------------------------------------- */ + + +void glue_startActionRec(bool gui) +{ + if (G_audio_status == false) + return; + + G_Recorder.active = true; + + if (!G_Mixer.running) + glue_startSeq(false); // update gui ayway + + if (!gui) { + Fl::lock(); + G_MainWin->controller->updateRecAction(1); + Fl::unlock(); + } +} + + +/* -------------------------------------------------------------------------- */ + + +void glue_stopActionRec(bool gui) +{ + /* stop the recorder and sort new actions */ + + G_Recorder.active = false; + G_Recorder.sortActions(); + + for (unsigned i=0; itype == CHANNEL_MIDI) + continue; + SampleChannel *ch = (SampleChannel*) G_Mixer.channels.at(i); + G_MainWin->keyboard->setChannelWithActions((geSampleChannel*)ch->guiChannel); + if (!ch->readActions && ch->hasActions) + glue_startReadingRecs(ch, false); + } + + if (!gui) { + Fl::lock(); + G_MainWin->controller->updateRecAction(0); + Fl::unlock(); + } + + gu_refreshActionEditor(); // in case it's open +} + + +/* -------------------------------------------------------------------------- */ + + +void glue_startStopInputRec(bool gui) +{ + if (G_Mixer.recording) + glue_stopInputRec(gui); + else + if (!glue_startInputRec(gui)) + gdAlert("No channels armed/available for audio recording."); +} + + +/* -------------------------------------------------------------------------- */ + + +int glue_startInputRec(bool gui) +{ + if (G_audio_status == false) + return false; + + if (!mh_startInputRec()) { + Fl::lock(); + G_MainWin->controller->updateRecInput(0); // set it off, anyway + Fl::unlock(); + return false; + } + + if (!G_Mixer.running) + glue_startSeq(false); // update gui anyway + + if (!gui) { + Fl::lock(); + G_MainWin->controller->updateRecInput(1); + Fl::unlock(); + } + + /* Update sample name inside sample channels' main button. This is useless for + midi channel, but let's do it anyway. */ + + for (unsigned i=0; iguiChannel->update(); + + return true; +} + + +/* -------------------------------------------------------------------------- */ + + +int glue_stopInputRec(bool gui) +{ + mh_stopInputRec(); + + /* Start all sample channels in loop mode that were armed, i.e. that were + recording stuff and not yet in play. They are also started in force mode, i.e. + they must start playing right away at the current frame, not at the next first + beat. */ + + for (unsigned i=0; itype == CHANNEL_MIDI) + continue; + SampleChannel *ch = (SampleChannel*) G_Mixer.channels.at(i); + if (ch->mode & (LOOP_ANY) && ch->status == STATUS_OFF && ch->armed) + ch->start(G_Mixer.actualFrame, true, G_Mixer.quantize, G_Mixer.running, true, true); + } + + if (!gui) { + Fl::lock(); + G_MainWin->controller->updateRecInput(0); + Fl::unlock(); + } + + return 1; +} diff --git a/src/glue/io.h b/src/glue/io.h new file mode 100644 index 0000000..b56160f --- /dev/null +++ b/src/glue/io.h @@ -0,0 +1,67 @@ +/* ----------------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * glue + * Intermediate layer GUI <-> CORE. + * + * How to know if you need another glue_ function? Ask yourself if the + * new action will ever be called via MIDI or keyboard/mouse. If yes, + * put it here. + * + * ----------------------------------------------------------------------------- + * + * Copyright (C) 2010-2016 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 GLUE_IO_H +#define GLUE_IO_H + + +/* keyPress / keyRelease + * handle the key pressure, either via mouse/keyboard or MIDI. If gui + * is true it means that the event comes from the main window (mouse, + * keyb or MIDI), otherwise the event comes from the action recorder. */ + +void glue_keyPress (class Channel *ch, bool ctrl=0, bool shift=0); +void glue_keyPress (class SampleChannel *ch, bool ctrl=0, bool shift=0); +void glue_keyPress (class MidiChannel *ch, bool ctrl=0, bool shift=0); +void glue_keyRelease(class Channel *ch, bool ctrl=0, bool shift=0); +void glue_keyRelease(class SampleChannel *ch, bool ctrl=0, bool shift=0); + +/* start/stopActionRec +Handles the action recording. If gui == true the signal comes from an user +interaction, otherwise it's a MIDI/Jack/external signal. */ + +void glue_startStopActionRec(bool gui=true); +void glue_startActionRec(bool gui=true); +void glue_stopActionRec(bool gui=true); + +/* start/stopInputRec +Handles the input recording (take). If gui == true the signal comes from an +internal interaction on the GUI, otherwise it's a MIDI/Jack/external signal. */ + +void glue_startStopInputRec(bool gui=true); +int glue_startInputRec (bool gui=true); +int glue_stopInputRec (bool gui=true); + + +#endif diff --git a/src/glue/main.cpp b/src/glue/main.cpp new file mode 100644 index 0000000..4c3638f --- /dev/null +++ b/src/glue/main.cpp @@ -0,0 +1,421 @@ +/* ----------------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * glue + * Intermediate layer GUI <-> CORE. + * + * How to know if you need another glue_ function? Ask yourself if the + * new action will ever be called via MIDI or keyboard/mouse. If yes, + * put it here. + * + * ----------------------------------------------------------------------------- + * + * Copyright (C) 2010-2016 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 "../gui/elems/ge_waveform.h" +#include "../gui/elems/ge_mixed.h" +#include "../gui/elems/channel.h" +#include "../gui/elems/sampleChannel.h" +#include "../gui/elems/ge_waveTools.h" +#include "../gui/elems/ge_keyboard.h" +#include "../gui/dialogs/gd_mainWindow.h" +#include "../gui/dialogs/gd_editor.h" +#include "../gui/dialogs/gd_warnings.h" +#include "../utils/gui.h" +#include "../utils/fs.h" +#include "../utils/log.h" +#include "../core/mixerHandler.h" +#include "../core/mixer.h" +#include "../core/recorder.h" +#include "../core/wave.h" +#include "../core/pluginHost.h" +#include "../core/channel.h" +#include "../core/sampleChannel.h" +#include "../core/midiChannel.h" +#include "../core/kernelMidi.h" +#include "../core/patch_DEPR_.h" +#include "../core/conf.h" +#include "main.h" + + +extern gdMainWindow *G_MainWin; +extern Mixer G_Mixer; +extern Recorder G_Recorder; +extern KernelAudio G_KernelAudio; +extern KernelMidi G_KernelMidi; +extern Patch_DEPR_ G_Patch_DEPR_; +extern Conf G_Conf; +extern bool G_audio_status; +#ifdef WITH_VST +extern PluginHost G_PluginHost; +#endif + + +void glue_setBpm(const char *v1, const char *v2) +{ + char buf[6]; + float value = atof(v1) + (atof(v2)/10); + if (value < 20.0f) { + value = 20.0f; + sprintf(buf, "20.0"); + } + else + sprintf(buf, "%s.%s", v1, !strcmp(v2, "") ? "0" : v2); + + /* a value such as atof("120.1") will never be 120.1 but 120.0999999, + * because of the rounding error. So we pass the real "wrong" value to + * G_Mixer and we show the nice looking (but fake) one to the GUI. */ + + float old_bpm = G_Mixer.bpm; + G_Mixer.bpm = value; + G_Mixer.updateFrameBars(); + + /* inform recorder and actionEditor of the change */ + + G_Recorder.updateBpm(old_bpm, value, G_Mixer.quanto); + gu_refreshActionEditor(); + + G_MainWin->timing->setBpm(buf); + gu_log("[glue] Bpm changed to %s (real=%f)\n", buf, G_Mixer.bpm); +} + + +/* -------------------------------------------------------------------------- */ + + +void glue_setBeats(int beats, int bars, bool expand) +{ + /* temp vars to store old data (they are necessary) */ + + int oldvalue = G_Mixer.beats; + unsigned oldfpb = G_Mixer.totalFrames; + + if (beats > MAX_BEATS) + G_Mixer.beats = MAX_BEATS; + else if (beats < 1) + G_Mixer.beats = 1; + else + G_Mixer.beats = beats; + + /* update bars - bars cannot be greate than beats and must be a sub + * multiple of beats. If not, approximation to the nearest (and greater) + * value available. */ + + if (bars > G_Mixer.beats) + G_Mixer.bars = G_Mixer.beats; + else if (bars <= 0) + G_Mixer.bars = 1; + else if (beats % bars != 0) { + G_Mixer.bars = bars + (beats % bars); + if (beats % G_Mixer.bars != 0) // it could be an odd value, let's check it (and avoid it) + G_Mixer.bars = G_Mixer.bars - (beats % G_Mixer.bars); + } + else + G_Mixer.bars = bars; + + G_Mixer.updateFrameBars(); + + /* update recorded actions */ + + if (expand) { + if (G_Mixer.beats > oldvalue) + G_Recorder.expand(oldfpb, G_Mixer.totalFrames); + //else if (G_Mixer.beats < oldvalue) + // G_Recorder.shrink(G_Mixer.totalFrames); + } + + G_MainWin->timing->setMeter(G_Mixer.beats, G_Mixer.bars); + gu_refreshActionEditor(); // in case the action editor is open +} + + +/* -------------------------------------------------------------------------- */ + + +void glue_startStopSeq(bool gui) +{ + G_Mixer.running ? glue_stopSeq(gui) : glue_startSeq(gui); +} + + +/* -------------------------------------------------------------------------- */ + + +void glue_startSeq(bool gui) +{ + G_Mixer.running = true; + + if (gui) { +#ifdef __linux__ + G_KernelAudio.jackStart(); +#endif + } + + if (G_Conf.midiSync == MIDI_SYNC_CLOCK_M) { + G_KernelMidi.send(MIDI_START, -1, -1); + G_KernelMidi.send(MIDI_POSITION_PTR, 0, 0); + } + + if (!gui) { + Fl::lock(); + G_MainWin->controller->updatePlay(1); + Fl::unlock(); + } +} + + +/* -------------------------------------------------------------------------- */ + + +void glue_stopSeq(bool gui) +{ + mh_stopSequencer(); + + if (G_Conf.midiSync == MIDI_SYNC_CLOCK_M) + G_KernelMidi.send(MIDI_STOP, -1, -1); + +#ifdef __linux__ + if (gui) + G_KernelAudio.jackStop(); +#endif + + /* what to do if we stop the sequencer and some action recs are active? + * Deactivate the button and delete any 'rec on' status */ + + if (G_Recorder.active) { + G_Recorder.active = false; + Fl::lock(); + G_MainWin->controller->updateRecAction(0); + Fl::unlock(); + } + + /* if input recs are active (who knows why) we must deactivate them. + * One might stop the sequencer while an input rec is running. */ + + if (G_Mixer.recording) { + mh_stopInputRec(); + Fl::lock(); + G_MainWin->controller->updateRecInput(0); + Fl::unlock(); + } + + if (!gui) { + Fl::lock(); + G_MainWin->controller->updatePlay(0); + Fl::unlock(); + } +} + + +/* -------------------------------------------------------------------------- */ + + +void glue_rewindSeq() +{ + mh_rewindSequencer(); + if (G_Conf.midiSync == MIDI_SYNC_CLOCK_M) + G_KernelMidi.send(MIDI_POSITION_PTR, 0, 0); +} + + +/* -------------------------------------------------------------------------- */ + + +void glue_startStopReadingRecs(SampleChannel *ch, bool gui) +{ + /* When you call glue_startReadingRecs with G_Conf.treatRecsAsLoops, the + member value ch->readActions actually is not set to true immediately, because + the channel is in wait mode (REC_WAITING). ch->readActions will become true on + the next first beat. So a 'stop rec' command should occur also when + ch->readActions is false but the channel is in wait mode; this check will + handle the case of when you press 'R', the channel goes into REC_WAITING and + then you press 'R' again to undo the status. */ + + if (ch->readActions || (!ch->readActions && ch->recStatus == REC_WAITING)) + glue_stopReadingRecs(ch, gui); + else + glue_startReadingRecs(ch, gui); +} + + +/* -------------------------------------------------------------------------- */ + + +void glue_startReadingRecs(SampleChannel *ch, bool gui) +{ + if (G_Conf.treatRecsAsLoops) + ch->recStatus = REC_WAITING; + else + ch->setReadActions(true, G_Conf.recsStopOnChanHalt); + if (!gui) { + Fl::lock(); + ((geSampleChannel*)ch->guiChannel)->readActions->value(1); + Fl::unlock(); + } +} + + +/* -------------------------------------------------------------------------- */ + + +void glue_stopReadingRecs(SampleChannel *ch, bool gui) +{ + /* First of all, if the mixer is not running just stop and disable everything. + Then if "treatRecsAsLoop" wait until the sequencer reaches beat 0, so put the + channel in REC_ENDING status. */ + + if (!G_Mixer.running) { + ch->recStatus = REC_STOPPED; + ch->readActions = false; + } + else + if (G_Conf.treatRecsAsLoops) + ch->recStatus = REC_ENDING; + else + ch->setReadActions(false, G_Conf.recsStopOnChanHalt); + + if (!gui) { + Fl::lock(); + ((geSampleChannel*)ch->guiChannel)->readActions->value(0); + Fl::unlock(); + } +} + + +/* -------------------------------------------------------------------------- */ + + +void glue_quantize(int val) +{ + G_Mixer.quantize = val; + G_Mixer.updateQuanto(); +} + + +/* -------------------------------------------------------------------------- */ + + +void glue_setOutVol(float v, bool gui) +{ + G_Mixer.outVol = v; + if (!gui) { + Fl::lock(); + G_MainWin->inOut->setOutVol(v); + Fl::unlock(); + } +} + + +/* -------------------------------------------------------------------------- */ + + +void glue_setInVol(float v, bool gui) +{ + G_Mixer.inVol = v; + if (!gui) { + Fl::lock(); + G_MainWin->inOut->setInVol(v); + Fl::unlock(); + } +} + + +/* -------------------------------------------------------------------------- */ + + +void glue_clearAllSamples() +{ + G_Mixer.running = false; + for (unsigned i=0; iempty(); + G_Mixer.channels.at(i)->guiChannel->reset(); + } + G_Recorder.init(); + return; +} + + +/* -------------------------------------------------------------------------- */ + + +void glue_clearAllRecs() +{ + G_Recorder.init(); + gu_updateControls(); +} + + +/* -------------------------------------------------------------------------- */ + + +void glue_resetToInitState(bool resetGui, bool createColumns) +{ + G_Patch_DEPR_.setDefault(); + G_Mixer.close(); + G_Mixer.init(); + G_Recorder.init(); +#ifdef WITH_VST + G_PluginHost.freeAllStacks(&G_Mixer.channels, &G_Mixer.mutex_plugins); +#endif + + G_MainWin->keyboard->clear(); + if (createColumns) + G_MainWin->keyboard->init(); + + gu_updateMainWinLabel(G_DEFAULT_PATCH_NAME); + + if (resetGui) + gu_updateControls(); +} + + +/* -------------------------------------------------------------------------- */ + + +void glue_startStopMetronome(bool gui) +{ + G_Mixer.metronome = !G_Mixer.metronome; + if (!gui) { + Fl::lock(); + G_MainWin->controller->updateMetronome(G_Mixer.metronome); + Fl::unlock(); + } +} + + +/* -------------------------------------------------------------------------- */ + + +/* never expand or shrink recordings (last param of setBeats = false): + * this is live manipulation */ + +void glue_beatsMultiply() +{ + glue_setBeats(G_Mixer.beats*2, G_Mixer.bars, false); +} + +void glue_beatsDivide() +{ + glue_setBeats(G_Mixer.beats/2, G_Mixer.bars, false); +} diff --git a/src/glue/main.h b/src/glue/main.h new file mode 100644 index 0000000..422f422 --- /dev/null +++ b/src/glue/main.h @@ -0,0 +1,81 @@ +/* --------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * glue + * Intermediate layer GUI <-> CORE. + * + * How to know if you need another glue_ function? Ask yourself if the + * new action will ever be called via MIDI or keyboard/mouse. If yes, + * put it here. + * + * --------------------------------------------------------------------- + * + * Copyright (C) 2010-2016 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 GLUE_H +#define GLUE_H + + +void glue_setBpm(const char *v1, const char *v2); +void glue_setBeats(int beats, int bars, bool expand); + +/* start, stop, rewind sequencer +If gui == true the signal comes from an user interaction on the GUI, +otherwise it's a MIDI/Jack/external signal. */ + +void glue_startStopSeq(bool gui=true); +void glue_startSeq (bool gui=true); +void glue_stopSeq (bool gui=true); +void glue_rewindSeq (); + +/* start/stopReadingRecs +Handles the 'R' button. If gui == true the signal comes from an user interaction +on the GUI, otherwise it's a MIDI/Jack/external signal. */ + +void glue_startStopReadingRecs(class SampleChannel *ch, bool gui=true); +void glue_startReadingRecs (class SampleChannel *ch, bool gui=true); +void glue_stopReadingRecs (class SampleChannel *ch, bool gui=true); + +void glue_quantize(int val); + +void glue_setOutVol (float v, bool gui=true); +void glue_setInVol (float v, bool gui=true); + +void glue_clearAllSamples(); +void glue_clearAllRecs(); + +/* resetToInitState + * reset Giada to init state. If resetGui also refresh all widgets. If + * createColumns also build initial empty columns. */ + +void glue_resetToInitState(bool resetGui=true, bool createColumns=true); + +void glue_startStopMetronome(bool gui=true); + +/* beatsDivide/Multiply + * shrinks or enlarges the number of beats by 2. */ + +void glue_beatsMultiply(); +void glue_beatsDivide(); + +#endif diff --git a/src/glue/storage.cpp b/src/glue/storage.cpp index d517736..41924d9 100644 --- a/src/glue/storage.cpp +++ b/src/glue/storage.cpp @@ -44,17 +44,20 @@ #include "../core/sampleChannel.h" #include "../core/midiChannel.h" #include "../core/wave.h" -#include "../utils/gui_utils.h" -#include "glue.h" // TODO - remove, used only for DEPR calls +#include "../utils/gui.h" +#include "../utils/log.h" +#include "main.h" // TODO - remove, used only for DEPR calls #include "channel.h" #include "storage.h" using std::string; +using std::vector; -extern gdMainWindow *mainWin; +extern gdMainWindow *G_MainWin; extern Mixer G_Mixer; +extern Recorder G_Recorder; extern Patch G_Patch; extern Conf G_Conf; extern Patch_DEPR_ G_Patch_DEPR_; // TODO - remove, used only for DEPR calls @@ -87,8 +90,8 @@ static void __glue_fillPatchGlobalsPlugins__(vector *host, vectorkeyboard->getTotalColumns(); i++) { - gColumn *gCol = mainWin->keyboard->getColumn(i); + for (unsigned i=0; ikeyboard->getTotalColumns(); i++) { + gColumn *gCol = G_MainWin->keyboard->getColumn(i); Patch::column_t pCol; pCol.index = gCol->getIndex(); pCol.width = gCol->w(); @@ -160,8 +163,8 @@ static bool __glue_savePatch__(const string &fullPath, const string &name, __glue_fillPatchColumns__(); if (G_Patch.write(fullPath)) { - gu_update_win_label(name.c_str()); - gLog("[glue_savePatch] patch saved as %s\n", fullPath.c_str()); + gu_updateMainWinLabel(name); + gu_log("[glue_savePatch] patch saved as %s\n", fullPath.c_str()); return true; } return false; @@ -175,19 +178,19 @@ void glue_savePatch(void *data) { gdSaveBrowser *browser = (gdSaveBrowser*) data; string name = browser->getName(); - string fullPath = browser->getCurrentPath() + G_SLASH + gStripExt(name) + ".gptc"; + string fullPath = browser->getCurrentPath() + G_SLASH + gu_stripExt(name) + ".gptc"; if (name == "") { gdAlert("Please choose a file name."); return; } - if (gFileExists(fullPath.c_str())) + if (gu_fileExists(fullPath)) if (!gdConfirmWin("Warning", "File exists: overwrite?")) return; if (__glue_savePatch__(fullPath, name, false)) { // false == not a project - G_Conf.patchPath = gDirname(fullPath); + G_Conf.patchPath = gu_dirname(fullPath); browser->do_callback(); } else @@ -202,16 +205,16 @@ void glue_loadPatch(void *data) { gdLoadBrowser *browser = (gdLoadBrowser*) data; string fullPath = browser->getSelectedItem(); - bool isProject = gIsProject(browser->getSelectedItem()); + bool isProject = gu_isProject(browser->getSelectedItem()); browser->showStatusBar(); - gLog("[glue] loading %s...\n", fullPath.c_str()); + gu_log("[glue] loading %s...\n", fullPath.c_str()); string fileToLoad = fullPath; // patch file to read from string basePath = ""; // base path, in case of reading from a project if (isProject) { - fileToLoad = fullPath + G_SLASH + gStripExt(gBasename(fullPath)) + ".gptc"; + fileToLoad = fullPath + G_SLASH + gu_stripExt(gu_basename(fullPath)) + ".gptc"; basePath = fullPath + G_SLASH; } @@ -222,9 +225,9 @@ void glue_loadPatch(void *data) bool deprecated = false; if (res == PATCH_UNREADABLE) { - gLog("[glue] failed reading JSON-based patch. Trying with the deprecated method\n"); + gu_log("[glue] failed reading JSON-based patch. Trying with the deprecated method\n"); deprecated = true; - res = glue_loadPatch__DEPR__(gBasename(fileToLoad).c_str(), fileToLoad.c_str(), + res = glue_loadPatch__DEPR__(gu_basename(fileToLoad).c_str(), fileToLoad.c_str(), browser->getStatusBar(), isProject); } @@ -263,7 +266,7 @@ void glue_loadPatch(void *data) float steps = 0.8 / G_Patch.channels.size(); for (unsigned i=0; ikeyboard->addColumn(col->width); + G_MainWin->keyboard->addColumn(col->width); for (unsigned k=0; kindex) { Channel *ch = glue_addChannel(G_Patch.channels.at(k).column, @@ -283,22 +286,22 @@ void glue_loadPatch(void *data) /* let recorder recompute the actions' positions if the current * samplerate != patch samplerate */ - recorder::updateSamplerate(G_Conf.samplerate, G_Patch.samplerate); + G_Recorder.updateSamplerate(G_Conf.samplerate, G_Patch.samplerate); /* save patchPath by taking the last dir of the broswer, in order to * reuse it the next time */ - G_Conf.patchPath = gDirname(fullPath); + G_Conf.patchPath = gu_dirname(fullPath); /* refresh GUI */ gu_updateControls(); - gu_update_win_label(G_Patch.name.c_str()); + gu_updateMainWinLabel(G_Patch.name); browser->setStatusBar(0.1f); //__glue_setProgressBar__(status, 1.0f); - gLog("[glue] patch loaded successfully\n"); + gu_log("[glue] patch loaded successfully\n"); #ifdef WITH_VST @@ -346,12 +349,12 @@ int glue_loadPatch__DEPR__(const char *fname, const char *fpath, gProgress *stat /* mixerHandler will update the samples inside Mixer */ - mh_loadPatch_DEPR_(isProject, gDirname(fpath).c_str()); + mh_loadPatch_DEPR_(isProject, gu_dirname(fpath).c_str()); /* take the patch name and update the main window's title */ G_Patch_DEPR_.getName(); - gu_update_win_label(G_Patch_DEPR_.name); + gu_updateMainWinLabel(G_Patch_DEPR_.name); status->value(0.4f); // progress status: 0.4 //Fl::check(); @@ -372,7 +375,7 @@ int glue_loadPatch__DEPR__(const char *fname, const char *fpath, gProgress *stat /* this one is vital: let recorder recompute the actions' positions if * the current samplerate != patch samplerate */ - recorder::updateSamplerate(G_Conf.samplerate, G_Patch_DEPR_.samplerate); + G_Recorder.updateSamplerate(G_Conf.samplerate, G_Patch_DEPR_.samplerate); /* update gui */ @@ -385,9 +388,9 @@ int glue_loadPatch__DEPR__(const char *fname, const char *fpath, gProgress *stat /* save patchPath by taking the last dir of the broswer, in order to * reuse it the next time */ - G_Conf.patchPath = gDirname(fpath).c_str(); + G_Conf.patchPath = gu_dirname(fpath).c_str(); - gLog("[glue] patch %s loaded\n", fname); + gu_log("[glue] patch %s loaded\n", fname); #ifdef WITH_VST if (resPlugins != 1) @@ -408,22 +411,22 @@ void glue_saveProject(void *data) gdSaveBrowser *browser = (gdSaveBrowser*) data; string name = browser->getName(); string folderPath = browser->getCurrentPath(); //browser->getSelectedItem(); - string fullPath = folderPath + G_SLASH + gStripExt(name) + ".gprj"; + string fullPath = folderPath + G_SLASH + gu_stripExt(name) + ".gprj"; if (name == "") { gdAlert("Please choose a project name."); return; } - if (gIsProject(fullPath.c_str()) && !gdConfirmWin("Warning", "Project exists: overwrite?")) + if (gu_isProject(fullPath) && !gdConfirmWin("Warning", "Project exists: overwrite?")) return; - if (!gDirExists(fullPath.c_str()) && !gMkdir(fullPath.c_str())) { - gLog("[glue_saveProject] unable to make project directory!\n"); + if (!gu_dirExists(fullPath) && !gu_mkdir(fullPath)) { + gu_log("[glue_saveProject] unable to make project directory!\n"); return; } - gLog("[glue_saveProject] project dir created: %s\n", fullPath.c_str()); + gu_log("[glue_saveProject] project dir created: %s\n", fullPath.c_str()); /* copy all samples inside the folder. Takes and logical ones are saved * via glue_saveSample() */ @@ -443,13 +446,13 @@ void glue_saveProject(void *data) string samplePath = fullPath + G_SLASH + ch->wave->basename(true); - if (gFileExists(samplePath.c_str())) + if (gu_fileExists(samplePath)) remove(samplePath.c_str()); if (ch->save(samplePath.c_str())) ch->wave->pathfile = samplePath; } - string gptcPath = fullPath + G_SLASH + gStripExt(name.c_str()) + ".gptc"; + string gptcPath = fullPath + G_SLASH + gu_stripExt(name) + ".gptc"; if (__glue_savePatch__(gptcPath, name, true)) // true == it's a project browser->do_callback(); else @@ -471,12 +474,12 @@ void glue_loadSample(void *data) int res = glue_loadChannel((SampleChannel*) browser->getChannel(), fullPath.c_str()); if (res == SAMPLE_LOADED_OK) { - G_Conf.samplePath = gDirname(fullPath); + G_Conf.samplePath = gu_dirname(fullPath); browser->do_callback(); - mainWin->delSubWindow(WID_SAMPLE_EDITOR); // if editor is open + G_MainWin->delSubWindow(WID_SAMPLE_EDITOR); // if editor is open } else - mainWin->keyboard->printChannelMessage(res); + G_MainWin->keyboard->printChannelMessage(res); } @@ -496,14 +499,14 @@ void glue_saveSample(void *data) /* bruteforce check extension. */ - string filePath = folderPath + G_SLASH + gStripExt(name) + ".wav"; + string filePath = folderPath + G_SLASH + gu_stripExt(name) + ".wav"; - if (gFileExists(filePath)) + if (gu_fileExists(filePath)) if (!gdConfirmWin("Warning", "File exists: overwrite?")) return; if (((SampleChannel*)browser->getChannel())->save(filePath.c_str())) { - G_Conf.samplePath = gDirname(folderPath); + G_Conf.samplePath = gu_dirname(folderPath); browser->do_callback(); } else diff --git a/src/glue/storage.h b/src/glue/storage.h index 98be8e8..a95ec0a 100644 --- a/src/glue/storage.h +++ b/src/glue/storage.h @@ -32,14 +32,6 @@ #define GLUE_STORAGE_H -#include -#include "../core/patch.h" - - -using std::string; -using std::vector; - - void glue_loadPatch (void *data); int glue_loadPatch__DEPR__(const char *fname, const char *fpath, class gProgress *status, bool isProject); void glue_savePatch (void *data); @@ -47,4 +39,5 @@ void glue_saveProject(void *data); void glue_saveSample (void *data); void glue_loadSample (void *data); + #endif diff --git a/src/gui/dialogs/gd_about.cpp b/src/gui/dialogs/gd_about.cpp index 3e0e614..5c42d26 100644 --- a/src/gui/dialogs/gd_about.cpp +++ b/src/gui/dialogs/gd_about.cpp @@ -33,12 +33,15 @@ #include "../../core/kernelAudio.h" #include "../../core/kernelMidi.h" #include "../../core/graphics.h" -#include "../../utils/gui_utils.h" +#include "../../deps/juce/config.h" +#include "../../utils/gui.h" #include "../elems/ge_mixed.h" #include "gd_about.h" -extern Conf G_Conf; +extern Conf G_Conf; +extern KernelAudio G_KernelAudio; +extern KernelMidi G_KernelMidi; gdAbout::gdAbout() @@ -85,8 +88,8 @@ gdAbout::gdAbout() "News, infos, contacts and documentation:\n" "www.giadamusic.com", FL_MAJOR_VERSION, FL_MINOR_VERSION, FL_PATCH_VERSION, - kernelAudio::getRtAudioVersion().c_str(), - kernelMidi::getRtMidiVersion().c_str(), + G_KernelAudio.getRtAudioVersion().c_str(), + G_KernelMidi.getRtMidiVersion().c_str(), JANSSON_VERSION #ifdef WITH_VST , JUCE_MAJOR_VERSION, JUCE_MINOR_VERSION, JUCE_BUILDNUMBER); diff --git a/src/gui/dialogs/gd_about.h b/src/gui/dialogs/gd_about.h index 2416740..2b8b6d3 100644 --- a/src/gui/dialogs/gd_about.h +++ b/src/gui/dialogs/gd_about.h @@ -1,10 +1,10 @@ -/* --------------------------------------------------------------------- +/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * gd_about * - * --------------------------------------------------------------------- + * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual * @@ -24,19 +24,20 @@ * along with Giada - Your Hardcore Loopmachine. If not, see * . * - * ------------------------------------------------------------------ */ + * -------------------------------------------------------------------------- */ #ifndef GD_ABOUT_H #define GD_ABOUT_H -#include -#include + #include "../elems/ge_window.h" -class gdAbout : public gWindow { +class gdAbout : public gWindow +{ private: + class gBox *logo; class gBox *text; class gClick *close; @@ -47,12 +48,12 @@ private: #endif public: + gdAbout(); ~gdAbout(); static void cb_close(Fl_Widget *w, void *p); inline void __cb_close(); - }; #endif diff --git a/src/gui/dialogs/gd_actionEditor.cpp b/src/gui/dialogs/gd_actionEditor.cpp index ddfcdaf..9d70506 100644 --- a/src/gui/dialogs/gd_actionEditor.cpp +++ b/src/gui/dialogs/gd_actionEditor.cpp @@ -28,17 +28,17 @@ #include -#include "../../utils/gui_utils.h" +#include "../../utils/gui.h" #include "../../core/graphics.h" #include "../../core/mixer.h" #include "../../core/recorder.h" #include "../../core/conf.h" #include "../../core/channel.h" #include "../../core/sampleChannel.h" -#include "../elems/ge_actionChannel.h" -#include "../elems/ge_muteChannel.h" -#include "../elems/ge_envelopeChannel.h" -#include "../elems/ge_pianoRoll.h" +#include "../elems/actionEditor.h" +#include "../elems/envelopeEditor.h" +#include "../elems/muteEditor.h" +#include "../elems/noteEditor.h" #include "../elems/ge_mixed.h" #include "gd_actionEditor.h" @@ -100,9 +100,9 @@ gdActionEditor::gdActionEditor(Channel *chan) SampleChannel *ch = (SampleChannel*) chan; - ac = new gActionChannel (scroller->x(), upperArea->y()+upperArea->h()+8, this, ch); - mc = new gMuteChannel (scroller->x(), ac->y()+ac->h()+8, this); - vc = new gEnvelopeChannel (scroller->x(), mc->y()+mc->h()+8, this, ACTION_VOLUME, RANGE_FLOAT, "volume"); + 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, ACTION_VOLUME, RANGE_FLOAT, "volume"); scroller->add(ac); //scroller->add(new gResizerBar(ac->x(), ac->y()+ac->h(), scroller->w(), 8)); scroller->add(mc); @@ -121,7 +121,7 @@ gdActionEditor::gdActionEditor(Channel *chan) ac->deactivate(); } else { - pr = new gPianoRollContainer(scroller->x(), upperArea->y()+upperArea->h()+8, this); + pr = new geNoteEditor(scroller->x(), upperArea->y()+upperArea->h()+8, this); scroller->add(pr); scroller->add(new gResizerBar(pr->x(), pr->y()+pr->h(), scroller->w(), 8)); } diff --git a/src/gui/dialogs/gd_actionEditor.h b/src/gui/dialogs/gd_actionEditor.h index 1f7eb40..435619d 100644 --- a/src/gui/dialogs/gd_actionEditor.h +++ b/src/gui/dialogs/gd_actionEditor.h @@ -45,8 +45,8 @@ using std::vector; * 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 gWindow { - +class gdActionEditor : public gWindow +{ private: /* update @@ -74,10 +74,10 @@ public: class gClick *zoomOut; class gScroll *scroller; // widget container - class gActionChannel *ac; - class gMuteChannel *mc; - class gEnvelopeChannel *vc; - class gPianoRollContainer *pr; + class geActionEditor *ac; + class geMuteEditor *mc; + class geEnvelopeEditor *vc; + class geNoteEditor *pr; vector widgets; diff --git a/src/gui/dialogs/gd_beatsInput.cpp b/src/gui/dialogs/gd_beatsInput.cpp index 36599eb..e77e839 100644 --- a/src/gui/dialogs/gd_beatsInput.cpp +++ b/src/gui/dialogs/gd_beatsInput.cpp @@ -27,10 +27,11 @@ * ------------------------------------------------------------------ */ -#include "../../utils/gui_utils.h" +#include "../../utils/gui.h" #include "../../core/mixer.h" #include "../../core/conf.h" -#include "../../glue/glue.h" +#include "../../core/const.h" +#include "../../glue/main.h" #include "gd_beatsInput.h" #include "gd_mainWindow.h" diff --git a/src/gui/dialogs/gd_bpmInput.cpp b/src/gui/dialogs/gd_bpmInput.cpp index d0186bf..4ad1da2 100644 --- a/src/gui/dialogs/gd_bpmInput.cpp +++ b/src/gui/dialogs/gd_bpmInput.cpp @@ -28,9 +28,10 @@ #include "../../core/conf.h" +#include "../../core/const.h" #include "../../core/mixer.h" -#include "../../glue/glue.h" -#include "../../utils/gui_utils.h" +#include "../../glue/main.h" +#include "../../utils/gui.h" #include "../elems/ge_mixed.h" #include "gd_bpmInput.h" #include "gd_mainWindow.h" diff --git a/src/gui/dialogs/gd_browser.cpp b/src/gui/dialogs/gd_browser.cpp index 448ad74..0c45a18 100644 --- a/src/gui/dialogs/gd_browser.cpp +++ b/src/gui/dialogs/gd_browser.cpp @@ -32,12 +32,13 @@ #include "../../core/channel.h" #include "../../core/sampleChannel.h" #include "../../core/conf.h" -#include "../../glue/glue.h" +#include "../../core/const.h" +#include "../../glue/main.h" #include "../../glue/channel.h" #include "../../glue/storage.h" -#include "../../utils/gui_utils.h" +#include "../../utils/gui.h" #include "../elems/ge_browser.h" -#include "../elems/ge_channel.h" +#include "../elems/channel.h" #include "gd_browser.h" @@ -102,7 +103,7 @@ gdBaseBrowser::~gdBaseBrowser() G_Conf.browserW = w(); G_Conf.browserH = h(); G_Conf.browserPosition = browser->position(); - G_Conf.browserLastPath = gDirname(browser->getSelectedItem()); + G_Conf.browserLastPath = gu_dirname(browser->getSelectedItem()); G_Conf.browserLastValue = browser->value(); } @@ -197,7 +198,7 @@ void gdSaveBrowser::__cb_down() /* if the selected item is a directory just load its content. If it's a file * use it as the file name (i.e. fill name->value()). */ - if (gIsDir(path)) { + if (gu_isDir(path)) { browser->loadDir(path); where->value(browser->getCurrentDir().c_str()); } @@ -261,7 +262,7 @@ void gdLoadBrowser::__cb_down() { string path = browser->getSelectedItem(); - if (path.empty() || !gIsDir(path)) // when click on an empty area or not a dir + if (path.empty() || !gu_isDir(path)) // when click on an empty area or not a dir return; browser->loadDir(path); diff --git a/src/gui/dialogs/gd_config.cpp b/src/gui/dialogs/gd_config.cpp index 5d7da03..832b75a 100644 --- a/src/gui/dialogs/gd_config.cpp +++ b/src/gui/dialogs/gd_config.cpp @@ -27,14 +27,17 @@ * -------------------------------------------------------------------------- */ +#include +#include #include "../../core/conf.h" #include "../../core/midiMapConf.h" #include "../../core/patch_DEPR_.h" #include "../../core/kernelAudio.h" #include "../../core/kernelMidi.h" #include "../../core/pluginHost.h" -#include "../../utils/gui_utils.h" +#include "../../utils/gui.h" #include "../../utils/log.h" +#include "../../utils/string.h" #include "../elems/ge_mixed.h" #include "gd_config.h" #include "gd_keyGrabber.h" @@ -44,6 +47,8 @@ extern Patch_DEPR_ G_Patch_DEPR_; extern Conf G_Conf; +extern KernelAudio G_KernelAudio; +extern KernelMidi G_KernelMidi; extern bool G_audio_status; extern MidiMapConf G_MidiMap; @@ -131,11 +136,11 @@ gTabAudio::gTabAudio(int X, int Y, int W, int H) #if defined(__linux__) - if (kernelAudio::hasAPI(RtAudio::LINUX_ALSA)) + if (G_KernelAudio.hasAPI(RtAudio::LINUX_ALSA)) soundsys->add("ALSA"); - if (kernelAudio::hasAPI(RtAudio::UNIX_JACK)) + if (G_KernelAudio.hasAPI(RtAudio::UNIX_JACK)) soundsys->add("Jack"); - if (kernelAudio::hasAPI(RtAudio::LINUX_PULSE)) + if (G_KernelAudio.hasAPI(RtAudio::LINUX_PULSE)) soundsys->add("PulseAudio"); switch (G_Conf.soundSystem) { @@ -157,11 +162,11 @@ gTabAudio::gTabAudio(int X, int Y, int W, int H) #elif defined(_WIN32) - if (kernelAudio::hasAPI(RtAudio::WINDOWS_DS)) + if (G_KernelAudio.hasAPI(RtAudio::WINDOWS_DS)) soundsys->add("DirectSound"); - if (kernelAudio::hasAPI(RtAudio::WINDOWS_ASIO)) + if (G_KernelAudio.hasAPI(RtAudio::WINDOWS_ASIO)) soundsys->add("ASIO"); - if (kernelAudio::hasAPI(RtAudio::WINDOWS_WASAPI)) + if (G_KernelAudio.hasAPI(RtAudio::WINDOWS_WASAPI)) soundsys->add("WASAPI"); switch (G_Conf.soundSystem) { @@ -181,7 +186,7 @@ gTabAudio::gTabAudio(int X, int Y, int W, int H) #elif defined (__APPLE__) - if (kernelAudio::hasAPI(RtAudio::MACOSX_CORE)) + if (G_KernelAudio.hasAPI(RtAudio::MACOSX_CORE)) soundsys->add("CoreAudio"); switch (G_Conf.soundSystem) { @@ -213,10 +218,10 @@ gTabAudio::gTabAudio(int X, int Y, int W, int H) /* fill frequency dropdown menu */ /* TODO - add fetchFrequencies() */ - int nfreq = kernelAudio::getTotalFreqs(sounddevOut->value()); + int nfreq = G_KernelAudio.getTotalFreqs(sounddevOut->value()); for (int i=0; ivalue(), i); - samplerate->add(gItoa(freq).c_str()); + int freq = G_KernelAudio.getFreq(sounddevOut->value(), i); + samplerate->add(gu_itoa(freq).c_str()); if (freq == G_Conf.samplerate) samplerate->value(i); } @@ -241,7 +246,7 @@ gTabAudio::gTabAudio(int X, int Y, int W, int H) buffersize->add("1024"); buffersize->add("2048"); buffersize->add("4096"); - buffersize->showItem(gItoa(G_Conf.buffersize).c_str()); + buffersize->showItem(gu_itoa(G_Conf.buffersize).c_str()); rsmpQuality->add("Sinc best quality (very slow)"); rsmpQuality->add("Sinc medium quality (slow)"); @@ -250,7 +255,7 @@ gTabAudio::gTabAudio(int X, int Y, int W, int H) rsmpQuality->add("Linear (very fast)"); rsmpQuality->value(G_Conf.rsmpQuality); - delayComp->value(gItoa(G_Conf.delayComp).c_str()); + delayComp->value(gu_itoa(G_Conf.delayComp).c_str()); delayComp->type(FL_INT_INPUT); delayComp->maximum_size(5); @@ -293,7 +298,7 @@ void gTabAudio::__cb_fetchOutChans() void gTabAudio::__cb_showInputInfo() { - unsigned dev = kernelAudio::getDeviceByName(sounddevIn->text(sounddevIn->value())); + unsigned dev = G_KernelAudio.getDeviceByName(sounddevIn->text(sounddevIn->value())); new gdDevInfo(dev); } @@ -303,7 +308,7 @@ void gTabAudio::__cb_showInputInfo() void gTabAudio::__cb_showOutputInfo() { - unsigned dev = kernelAudio::getDeviceByName(sounddevOut->text(sounddevOut->value())); + unsigned dev = G_KernelAudio.getDeviceByName(sounddevOut->text(sounddevOut->value())); new gdDevInfo(dev); } @@ -374,8 +379,8 @@ void gTabAudio::fetchInChans(int menuItem) channelsIn->clear(); - unsigned dev = kernelAudio::getDeviceByName(sounddevIn->text(sounddevIn->value())); - unsigned chs = kernelAudio::getMaxInChans(dev); + unsigned dev = G_KernelAudio.getDeviceByName(sounddevIn->text(sounddevIn->value())); + unsigned chs = G_KernelAudio.getMaxInChans(dev); if (chs == 0) { channelsIn->add("none"); @@ -398,8 +403,8 @@ void gTabAudio::fetchOutChans(int menuItem) { channelsOut->clear(); - unsigned dev = kernelAudio::getDeviceByName(sounddevOut->text(sounddevOut->value())); - unsigned chs = kernelAudio::getMaxOutChans(dev); + unsigned dev = G_KernelAudio.getDeviceByName(sounddevOut->text(sounddevOut->value())); + unsigned chs = G_KernelAudio.getMaxOutChans(dev); if (chs == 0) { channelsOut->add("none"); @@ -427,11 +432,11 @@ int gTabAudio::findMenuDevice(gChoice *m, int device) return 0; for (int i=0; isize(); i++) { - if (kernelAudio::getDeviceName(device) == "") + if (G_KernelAudio.getDeviceName(device) == "") continue; if (m->text(i) == NULL) continue; - if (m->text(i) == kernelAudio::getDeviceName(device)) + if (m->text(i) == G_KernelAudio.getDeviceName(device)) return i; } @@ -444,7 +449,7 @@ int gTabAudio::findMenuDevice(gChoice *m, int device) void gTabAudio::fetchSoundDevs() { - if (kernelAudio::numDevs == 0) { + if (G_KernelAudio.numDevs == 0) { sounddevOut->add("-- no devices found --"); sounddevOut->value(0); sounddevIn->add("-- no devices found --"); @@ -462,11 +467,11 @@ void gTabAudio::fetchSoundDevs() sounddevIn->add("(disabled)"); - for (unsigned i=0; i 0) + if (G_KernelAudio.getMaxOutChans(i) > 0) sounddevOut->add(tmp.c_str()); - if (kernelAudio::getMaxInChans(i) > 0) + if (G_KernelAudio.getMaxInChans(i) > 0) sounddevIn->add(tmp.c_str()); } @@ -534,6 +539,8 @@ void gTabAudio::save() G_Conf.soundSystem = SYS_API_DS; else if (text == "ASIO") G_Conf.soundSystem = SYS_API_ASIO; + else if (text == "WASAPI") + G_Conf.soundSystem = SYS_API_WASAPI; #elif defined (__APPLE__) @@ -544,8 +551,8 @@ void gTabAudio::save() /* use the device name to search into the drop down menu's */ - G_Conf.soundDeviceOut = kernelAudio::getDeviceByName(sounddevOut->text(sounddevOut->value())); - G_Conf.soundDeviceIn = kernelAudio::getDeviceByName(sounddevIn->text(sounddevIn->value())); + G_Conf.soundDeviceOut = G_KernelAudio.getDeviceByName(sounddevOut->text(sounddevOut->value())); + G_Conf.soundDeviceIn = G_KernelAudio.getDeviceByName(sounddevIn->text(sounddevIn->value())); G_Conf.channelsOut = channelsOut->value(); G_Conf.channelsIn = channelsIn->value(); G_Conf.limitOutput = limitOutput->value(); @@ -620,7 +627,7 @@ gTabMidi::gTabMidi(int X, int Y, int W, int H) void gTabMidi::fetchOutPorts() { - if (kernelMidi::numOutPorts == 0) { + if (G_KernelMidi.numOutPorts == 0) { portOut->add("-- no ports found --"); portOut->value(0); portOut->deactivate(); @@ -629,8 +636,8 @@ void gTabMidi::fetchOutPorts() { portOut->add("(disabled)"); - for (unsigned i=0; iadd(gu_removeFltkChars(kernelMidi::getOutPortName(i)).c_str()); + for (unsigned i=0; iadd(gu_removeFltkChars(G_KernelMidi.getOutPortName(i)).c_str()); portOut->value(G_Conf.midiPortOut+1); // +1 because midiPortOut=-1 is '(disabled)' } @@ -641,7 +648,7 @@ void gTabMidi::fetchOutPorts() { void gTabMidi::fetchInPorts() { - if (kernelMidi::numInPorts == 0) { + if (G_KernelMidi.numInPorts == 0) { portIn->add("-- no ports found --"); portIn->value(0); portIn->deactivate(); @@ -650,8 +657,8 @@ void gTabMidi::fetchInPorts() portIn->add("(disabled)"); - for (unsigned i=0; iadd(gu_removeFltkChars(kernelMidi::getInPortName(i)).c_str()); + for (unsigned i=0; iadd(gu_removeFltkChars(G_KernelMidi.getInPortName(i)).c_str()); portIn->value(G_Conf.midiPortIn+1); // +1 because midiPortIn=-1 is '(disabled)' } @@ -678,7 +685,7 @@ void gTabMidi::fetchMidiMaps() } /* Preselect the 0 midimap if nothing is selected but midimaps exist. */ - + if (midiMap->value() == -1 && G_MidiMap.maps.size() > 0) midiMap->value(0); } @@ -722,14 +729,14 @@ void gTabMidi::fetchSystems() { #if defined(__linux__) - if (kernelMidi::hasAPI(RtMidi::LINUX_ALSA)) + if (G_KernelMidi.hasAPI(RtMidi::LINUX_ALSA)) system->add("ALSA"); - if (kernelMidi::hasAPI(RtMidi::UNIX_JACK)) + if (G_KernelMidi.hasAPI(RtMidi::UNIX_JACK)) system->add("Jack"); #elif defined(_WIN32) - if (kernelMidi::hasAPI(RtMidi::WINDOWS_MM)) + if (G_KernelMidi.hasAPI(RtMidi::WINDOWS_MM)) system->add("Multimedia MIDI"); #elif defined (__APPLE__) @@ -887,7 +894,7 @@ gTabPlugins::gTabPlugins(int X, int Y, int W, int H) void gTabPlugins::updateCount() { - string scanLabel = "Scan (" + gItoa(G_PluginHost.countAvailablePlugins()) + " found)"; + string scanLabel = "Scan (" + gu_itoa(G_PluginHost.countAvailablePlugins()) + " found)"; scanButton->label(scanLabel.c_str()); } @@ -903,7 +910,7 @@ void gTabPlugins::cb_scan(Fl_Widget *w, void *p) { ((gTabPlugins*)p)->__cb_scan( void gTabPlugins::cb_onScan(float progress, void *p) { - string l = "Scan in progress (" + gItoa((int)(progress*100)) + "%). Please wait..."; + string l = "Scan in progress (" + gu_itoa((int)(progress*100)) + "%). Please wait..."; ((gTabPlugins *)p)->info->label(l.c_str()); Fl::wait(); } @@ -916,7 +923,7 @@ void gTabPlugins::__cb_scan(Fl_Widget *w) { info->show(); G_PluginHost.scanDir(folderPath->value(), cb_onScan, (void*) this); - G_PluginHost.saveList(gGetHomePath() + G_SLASH + "plugins.xml"); + G_PluginHost.saveList(gu_getHomePath() + G_SLASH + "plugins.xml"); info->hide(); updateCount(); } diff --git a/src/gui/dialogs/gd_config.h b/src/gui/dialogs/gd_config.h index d2e7580..fc7b64a 100644 --- a/src/gui/dialogs/gd_config.h +++ b/src/gui/dialogs/gd_config.h @@ -1,10 +1,10 @@ -/* --------------------------------------------------------------------- +/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * gd_config * - * --------------------------------------------------------------------- + * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual * @@ -24,18 +24,14 @@ * along with Giada - Your Hardcore Loopmachine. If not, see * . * - * ------------------------------------------------------------------ */ + * -------------------------------------------------------------------------- */ + #ifndef GD_CONFIG_H #define GD_CONFIG_H -#include -#include -#include -#include "../elems/ge_window.h" - -using std::string; +#include "../elems/ge_window.h" class gdConfig : public gWindow diff --git a/src/gui/dialogs/gd_devInfo.cpp b/src/gui/dialogs/gd_devInfo.cpp index 5055928..3f901a3 100644 --- a/src/gui/dialogs/gd_devInfo.cpp +++ b/src/gui/dialogs/gd_devInfo.cpp @@ -28,17 +28,21 @@ #include "../../core/kernelAudio.h" -#include "../../utils/gui_utils.h" -#include "../../utils/utils.h" +#include "../../utils/gui.h" +#include "../../utils/string.h" #include "../elems/ge_mixed.h" #include "gd_devInfo.h" +extern KernelAudio G_KernelAudio; + + using std::string; gdDevInfo::gdDevInfo(unsigned dev) -: Fl_Window(340, 300, "Device information") { + : Fl_Window(340, 300, "Device information") +{ set_modal(); text = new gBox(8, 8, 320, 200, "", (Fl_Align) (FL_ALIGN_LEFT | FL_ALIGN_TOP)); @@ -48,22 +52,22 @@ gdDevInfo::gdDevInfo(unsigned dev) string body = ""; int lines = 7; - body = "Device name: " + kernelAudio::getDeviceName(dev) + "\n"; - body += "Total output(s): " + gItoa(kernelAudio::getMaxOutChans(dev)) + "\n"; - body += "Total intput(s): " + gItoa(kernelAudio::getMaxInChans(dev)) + "\n"; - body += "Duplex channel(s): " + gItoa(kernelAudio::getDuplexChans(dev)) + "\n"; - body += "Default output: " + string(kernelAudio::isDefaultOut(dev) ? "yes" : "no") + "\n"; - body += "Default input: " + string(kernelAudio::isDefaultIn(dev) ? "yes" : "no") + "\n"; + body = "Device name: " + G_KernelAudio.getDeviceName(dev) + "\n"; + body += "Total output(s): " + gu_itoa(G_KernelAudio.getMaxOutChans(dev)) + "\n"; + body += "Total intput(s): " + gu_itoa(G_KernelAudio.getMaxInChans(dev)) + "\n"; + body += "Duplex channel(s): " + gu_itoa(G_KernelAudio.getDuplexChans(dev)) + "\n"; + body += "Default output: " + string(G_KernelAudio.isDefaultOut(dev) ? "yes" : "no") + "\n"; + body += "Default input: " + string(G_KernelAudio.isDefaultIn(dev) ? "yes" : "no") + "\n"; - int totalFreq = kernelAudio::getTotalFreqs(dev); - body += "Supported frequencies: " + gItoa(totalFreq); + int totalFreq = G_KernelAudio.getTotalFreqs(dev); + body += "Supported frequencies: " + gu_itoa(totalFreq); for (int i=0; icopy_label(body.c_str()); diff --git a/src/gui/dialogs/gd_editor.cpp b/src/gui/dialogs/gd_editor.cpp index 501edfe..af5d55b 100644 --- a/src/gui/dialogs/gd_editor.cpp +++ b/src/gui/dialogs/gd_editor.cpp @@ -1,10 +1,10 @@ -/* --------------------------------------------------------------------- +/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * gd_editor * - * --------------------------------------------------------------------- + * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual * @@ -24,32 +24,27 @@ * along with Giada - Your Hardcore Loopmachine. If not, see * . * - * ------------------------------------------------------------------ */ + * -------------------------------------------------------------------------- */ -#include "../../utils/gui_utils.h" -#include "../../glue/glue.h" +#include "../../glue/channel.h" #include "../../core/waveFx.h" #include "../../core/conf.h" +#include "../../core/const.h" #include "../../core/graphics.h" -#include "../../core/mixerHandler.h" -#include "../../core/channel.h" #include "../../core/sampleChannel.h" #include "../../core/mixer.h" #include "../../core/wave.h" +#include "../elems/channel.h" #include "../elems/ge_waveform.h" #include "../elems/ge_mixed.h" -#include "../elems/ge_channel.h" #include "../elems/ge_waveTools.h" -#include "../elems/ge_keyboard.h" -#include "gd_editor.h" -#include "gd_mainWindow.h" #include "gd_warnings.h" +#include "gd_editor.h" -extern Mixer G_Mixer; -extern gdMainWindow *mainWin; -extern Conf G_Conf; +extern Mixer G_Mixer; +extern Conf G_Conf; gdEditor::gdEditor(SampleChannel *ch) @@ -81,28 +76,28 @@ gdEditor::gdEditor(SampleChannel *ch) Fl_Group *tools = new Fl_Group(8, waveTools->y()+waveTools->h()+8, w()-16, 130); tools->begin(); - volume = new gDial (tools->x()+42, tools->y(), 20, 20, "Volume"); + volume = new gDial (tools->x()+50, tools->y(), 20, 20, "Volume"); volumeNum = new gInput(volume->x()+volume->w()+4, tools->y(), 46, 20, "dB"); - boost = new gDial (volumeNum->x()+volumeNum->w()+80, tools->y(), 20, 20, "Boost"); - boostNum = new gInput(boost->x()+boost->w()+4, tools->y(), 46, 20, "dB"); + boost = new gDial (volumeNum->x()+volumeNum->w()+108, tools->y(), 20, 20, "Boost"); + boostNum = new gInput(boost->x()+boost->w()+4, tools->y(), 44, 20, "dB"); normalize = new gClick(boostNum->x()+boostNum->w()+54, tools->y(), 70, 20, "Normalize"); pan = new gDial (normalize->x()+normalize->w()+40, tools->y(), 20, 20, "Pan"); panNum = new gInput(pan->x()+pan->w()+4, tools->y(), 45, 20, "%"); - pitch = new gDial (tools->x()+42, volume->y()+volume->h()+4, 20, 20, "Pitch"); + pitch = new gDial (tools->x()+50, volume->y()+volume->h()+4, 20, 20, "Pitch"); pitchNum = new gInput(pitch->x()+pitch->w()+4, volume->y()+volume->h()+4, 46, 20); - pitchToBar = new gClick(pitchNum->x()+pitchNum->w()+4, volume->y()+volume->h()+4, 46, 20, "To bar"); - pitchToSong = new gClick(pitchToBar->x()+pitchToBar->w()+4, volume->y()+volume->h()+4, 46, 20, "To song"); - pitchHalf = new gClick(pitchToSong->x()+pitchToSong->w()+4, volume->y()+volume->h()+4, 21, 20, "÷"); - pitchDouble = new gClick(pitchHalf->x()+pitchHalf->w()+4, volume->y()+volume->h()+4, 21, 20, "×"); + pitchToBar = new gClick(pitchNum->x()+pitchNum->w()+4, volume->y()+volume->h()+4, 60, 20, "To bar"); + pitchToSong = new gClick(pitchToBar->x()+pitchToBar->w()+4, volume->y()+volume->h()+4, 60, 20, "To song"); + pitchHalf = new gClick(pitchToSong->x()+pitchToSong->w()+4, volume->y()+volume->h()+4, 20, 20, "", divideOff_xpm, divideOn_xpm); + pitchDouble = new gClick(pitchHalf->x()+pitchHalf->w()+4, volume->y()+volume->h()+4, 20, 20, "", multiplyOff_xpm, multiplyOn_xpm); pitchReset = new gClick(pitchDouble->x()+pitchDouble->w()+4, volume->y()+volume->h()+4, 46, 20, "Reset"); reload = new gClick(pitchReset->x()+pitchReset->w()+4, volume->y()+volume->h()+4, 70, 20, "Reload"); - chanStart = new gInput(tools->x()+52, pitch->y()+pitch->h()+4, 60, 20, "Start"); - chanEnd = new gInput(chanStart->x()+chanStart->w()+40, pitch->y()+pitch->h()+4, 60, 20, "End"); - resetStartEnd = new gClick(chanEnd->x()+chanEnd->w()+4, pitch->y()+pitch->h()+4, 46, 20, "Reset"); + chanStart = new gInput(tools->x()+60, pitch->y()+pitch->h()+4, 60, 20, "Range"); + chanEnd = new gInput(chanStart->x()+chanStart->w()+4, pitch->y()+pitch->h()+4, 60, 20, ""); + resetStartEnd = new gClick(chanEnd->x()+chanEnd->w()+4, pitch->y()+pitch->h()+4, 60, 20, "Reset"); tools->end(); tools->resizable(new gBox(panNum->x()+panNum->w()+4, tools->y(), 80, tools->h())); @@ -202,7 +197,7 @@ gdEditor::gdEditor(SampleChannel *ch) if (ch->panRight < 1.0f) { char buf[8]; - sprintf(buf, "%d L", (int) abs((ch->panRight * 100.0f) - 100)); + sprintf(buf, "%d L", (int) std::abs((ch->panRight * 100.0f) - 100)); pan->value(ch->panRight); panNum->value(buf); } @@ -212,7 +207,7 @@ gdEditor::gdEditor(SampleChannel *ch) } else { char buf[8]; - sprintf(buf, "%d R", (int) abs((ch->panLeft * 100.0f) - 100)); + sprintf(buf, "%d R", (int) std::abs((ch->panLeft * 100.0f) - 100)); pan->value(2.0f - ch->panLeft); panNum->value(buf); } @@ -231,7 +226,7 @@ gdEditor::gdEditor(SampleChannel *ch) } -/* ------------------------------------------------------------------ */ +/* -------------------------------------------------------------------------- */ gdEditor::~gdEditor() @@ -245,7 +240,7 @@ gdEditor::~gdEditor() } -/* ------------------------------------------------------------------ */ +/* -------------------------------------------------------------------------- */ void gdEditor::cb_setChanPos (Fl_Widget *w, void *p) { ((gdEditor*)p)->__cb_setChanPos(); } @@ -270,7 +265,7 @@ void gdEditor::cb_changeGrid (Fl_Widget *w, void *p) { ((gdEditor*)p)->__cb void gdEditor::cb_enableSnap (Fl_Widget *w, void *p) { ((gdEditor*)p)->__cb_enableSnap(); } -/* ------------------------------------------------------------------ */ +/* -------------------------------------------------------------------------- */ void gdEditor::__cb_enableSnap() @@ -279,7 +274,7 @@ void gdEditor::__cb_enableSnap() } -/* ------------------------------------------------------------------ */ +/* -------------------------------------------------------------------------- */ void gdEditor::__cb_setPitchToBar() @@ -288,7 +283,7 @@ void gdEditor::__cb_setPitchToBar() } -/* ------------------------------------------------------------------ */ +/* -------------------------------------------------------------------------- */ void gdEditor::__cb_setPitchToSong() @@ -297,7 +292,7 @@ void gdEditor::__cb_setPitchToSong() } -/* ------------------------------------------------------------------ */ +/* -------------------------------------------------------------------------- */ void gdEditor::__cb_resetPitch() @@ -306,7 +301,7 @@ void gdEditor::__cb_resetPitch() } -/* ------------------------------------------------------------------ */ +/* -------------------------------------------------------------------------- */ void gdEditor::__cb_setChanPos() @@ -321,7 +316,7 @@ void gdEditor::__cb_setChanPos() } -/* ------------------------------------------------------------------ */ +/* -------------------------------------------------------------------------- */ void gdEditor::__cb_resetStartEnd() @@ -330,7 +325,7 @@ void gdEditor::__cb_resetStartEnd() } -/* ------------------------------------------------------------------ */ +/* -------------------------------------------------------------------------- */ void gdEditor::__cb_setVolume() @@ -339,7 +334,7 @@ void gdEditor::__cb_setVolume() } -/* ------------------------------------------------------------------ */ +/* -------------------------------------------------------------------------- */ void gdEditor::__cb_setVolumeNum() @@ -348,7 +343,7 @@ void gdEditor::__cb_setVolumeNum() } -/* ------------------------------------------------------------------ */ +/* -------------------------------------------------------------------------- */ void gdEditor::__cb_setBoost() @@ -362,7 +357,7 @@ void gdEditor::__cb_setBoost() } -/* ------------------------------------------------------------------ */ +/* -------------------------------------------------------------------------- */ void gdEditor::__cb_setBoostNum() @@ -372,7 +367,7 @@ void gdEditor::__cb_setBoostNum() } -/* ------------------------------------------------------------------ */ +/* -------------------------------------------------------------------------- */ void gdEditor::__cb_normalize() @@ -390,7 +385,7 @@ void gdEditor::__cb_normalize() } -/* ------------------------------------------------------------------ */ +/* -------------------------------------------------------------------------- */ void gdEditor::__cb_panning() @@ -399,7 +394,7 @@ void gdEditor::__cb_panning() } -/* ------------------------------------------------------------------ */ +/* -------------------------------------------------------------------------- */ void gdEditor::__cb_reload() @@ -426,7 +421,7 @@ void gdEditor::__cb_reload() } -/* ------------------------------------------------------------------ */ +/* -------------------------------------------------------------------------- */ void gdEditor::__cb_setPitch() @@ -435,7 +430,7 @@ void gdEditor::__cb_setPitch() } -/* ------------------------------------------------------------------ */ +/* -------------------------------------------------------------------------- */ void gdEditor::__cb_setPitchNum() @@ -444,7 +439,7 @@ void gdEditor::__cb_setPitchNum() } -/* ------------------------------------------------------------------ */ +/* -------------------------------------------------------------------------- */ void gdEditor::__cb_setPitchHalf() @@ -453,7 +448,7 @@ void gdEditor::__cb_setPitchHalf() } -/* ------------------------------------------------------------------ */ +/* -------------------------------------------------------------------------- */ void gdEditor::__cb_setPitchDouble() @@ -462,7 +457,7 @@ void gdEditor::__cb_setPitchDouble() } -/* ------------------------------------------------------------------ */ +/* -------------------------------------------------------------------------- */ void gdEditor::__cb_zoomIn() @@ -472,7 +467,7 @@ void gdEditor::__cb_zoomIn() } -/* ------------------------------------------------------------------ */ +/* -------------------------------------------------------------------------- */ void gdEditor::__cb_zoomOut() @@ -482,7 +477,7 @@ void gdEditor::__cb_zoomOut() } -/* ------------------------------------------------------------------ */ +/* -------------------------------------------------------------------------- */ void gdEditor::__cb_changeGrid() diff --git a/src/gui/dialogs/gd_editor.h b/src/gui/dialogs/gd_editor.h index cd7b9ae..c9ffe74 100644 --- a/src/gui/dialogs/gd_editor.h +++ b/src/gui/dialogs/gd_editor.h @@ -1,10 +1,10 @@ -/* --------------------------------------------------------------------- +/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * gd_editor * - * --------------------------------------------------------------------- + * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual * @@ -24,19 +24,18 @@ * along with Giada - Your Hardcore Loopmachine. If not, see * . * - * ------------------------------------------------------------------ */ + * -------------------------------------------------------------------------- */ + #ifndef GD_EDITOR_H #define GD_EDITOR_H -#include -#include -#include -#include "../elems/ge_window.h" +#include "../elems/ge_window.h" -class gdEditor : public gWindow { +class gdEditor : public gWindow +{ private: static void cb_setChanPos (Fl_Widget *w, void *p); @@ -113,4 +112,5 @@ public: class SampleChannel *ch; }; + #endif diff --git a/src/gui/dialogs/gd_keyGrabber.cpp b/src/gui/dialogs/gd_keyGrabber.cpp index 47a8a54..508867b 100644 --- a/src/gui/dialogs/gd_keyGrabber.cpp +++ b/src/gui/dialogs/gd_keyGrabber.cpp @@ -27,7 +27,7 @@ * -------------------------------------------------------------------------- */ -#include "../../utils/gui_utils.h" +#include "../../utils/gui.h" #include "../../core/conf.h" #include "../../core/channel.h" #include "../../core/sampleChannel.h" @@ -35,8 +35,8 @@ #include "../../utils/log.h" #include "../elems/ge_keyboard.h" #include "../elems/ge_mixed.h" -#include "../elems/ge_channel.h" -#include "../elems/ge_channelButton.h" +#include "../elems/channel.h" +#include "../elems/channelButton.h" #include "gd_keyGrabber.h" #include "gd_config.h" #include "gd_mainWindow.h" @@ -132,13 +132,13 @@ int gdKeyGrabber::handle(int e) && x != FL_End && x != ' ') { - gLog("set key '%c' (%d) for channel %d\n", x, x, ch->index); + gu_log("set key '%c' (%d) for channel %d\n", x, x, ch->index); setButtonLabel(x); updateText(x); break; } else - gLog("invalid key\n"); + gu_log("invalid key\n"); } } return(ret); diff --git a/src/gui/dialogs/gd_mainWindow.cpp b/src/gui/dialogs/gd_mainWindow.cpp index b2012c2..dba5764 100644 --- a/src/gui/dialogs/gd_mainWindow.cpp +++ b/src/gui/dialogs/gd_mainWindow.cpp @@ -1,10 +1,10 @@ -/* --------------------------------------------------------------------- +/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * gd_mainWindow * - * --------------------------------------------------------------------- + * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual * @@ -24,7 +24,7 @@ * along with Giada - Your Hardcore Loopmachine. If not, see * . * - * ------------------------------------------------------------------ */ + * -------------------------------------------------------------------------- */ #ifdef __linux__ @@ -40,9 +40,10 @@ #include "../../core/sampleChannel.h" #include "../../core/init.h" #include "../../core/patch_DEPR_.h" +#include "../../core/patch.h" #include "../../core/conf.h" #include "../../core/pluginHost.h" -#include "../../glue/glue.h" +#include "../../glue/main.h" #include "../../glue/storage.h" #include "../elems/ge_keyboard.h" #include "gd_warnings.h" @@ -60,7 +61,7 @@ extern Mixer G_Mixer; extern Patch_DEPR_ G_Patch_DEPR_; extern Patch G_Patch; extern Conf G_Conf; -extern gdMainWindow *mainWin; +extern gdMainWindow *G_MainWin; extern bool G_quit; extern bool G_audio_status; @@ -76,7 +77,7 @@ gdMainWindow::gdMainWindow(int W, int H, const char *title, int argc, char **arg size_range(GUI_WIDTH, GUI_HEIGHT); menu = new gMenu(8, -1); - inOut = new gInOut(414, 8); + inOut = new gInOut(412, 8); controller = new gController(8, 39); timing = new gTiming(628, 44); beatMeter = new gBeatMeter(100, 83, 609, 20); @@ -117,13 +118,13 @@ gdMainWindow::gdMainWindow(int W, int H, const char *title, int argc, char **arg } -/* ------------------------------------------------------------------ */ +/* -------------------------------------------------------------------------- */ -void gdMainWindow::cb_endprogram(Fl_Widget *v, void *p) { mainWin->__cb_endprogram(); } +void gdMainWindow::cb_endprogram(Fl_Widget *v, void *p) { G_MainWin->__cb_endprogram(); } -/* ------------------------------------------------------------------ */ +/* -------------------------------------------------------------------------- */ void gdMainWindow::__cb_endprogram() @@ -136,28 +137,28 @@ void gdMainWindow::__cb_endprogram() } -/* ------------------------------------------------------------------ */ -/* ------------------------------------------------------------------ */ -/* ------------------------------------------------------------------ */ +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ gInOut::gInOut(int x, int y) - : Fl_Group(x, y, 394, 20) + : Fl_Group(x, y, 396, 20) { begin(); #if defined(WITH_VST) masterFxIn = new gFxButton (x, y, 20, 20, fxOff_xpm, fxOn_xpm); inVol = new gDial (masterFxIn->x()+masterFxIn->w()+4, y, 20, 20); - inMeter = new gSoundMeter(inVol->x()+inVol->w()+4, y+5, 140, 10); - inToOut = new gClick (inMeter->x()+inMeter->w()+4, y+5, 10, 10, ""); - outMeter = new gSoundMeter(inToOut->x()+inToOut->w()+4, y+5, 140, 10); + inMeter = new gSoundMeter(inVol->x()+inVol->w()+4, y+4, 140, 12); + inToOut = new gClick (inMeter->x()+inMeter->w()+4, y+4, 12, 12, "", inputToOutputOff_xpm, inputToOutputOn_xpm); + outMeter = new gSoundMeter(inToOut->x()+inToOut->w()+4, y+4, 140, 12); outVol = new gDial (outMeter->x()+outMeter->w()+4, y, 20, 20); masterFxOut = new gFxButton (outVol->x()+outVol->w()+4, y, 20, 20, fxOff_xpm, fxOn_xpm); #else inVol = new gDial (x+62, y, 20, 20); - inMeter = new gSoundMeter(inVol->x()+inVol->w()+4, y+5, 140, 10); - outMeter = new gSoundMeter(inMeter->x()+inMeter->w()+4, y+5, 140, 10); + inMeter = new gSoundMeter(inVol->x()+inVol->w()+4, y+5, 140, 12); + outMeter = new gSoundMeter(inMeter->x()+inMeter->w()+4, y+5, 140, 12); outVol = new gDial (outMeter->x()+outMeter->w()+4, y, 20, 20); #endif @@ -179,7 +180,7 @@ gInOut::gInOut(int x, int y) } -/* ------------------------------------------------------------------ */ +/* -------------------------------------------------------------------------- */ void gInOut::cb_outVol (Fl_Widget *v, void *p) { ((gInOut*)p)->__cb_outVol(); } @@ -191,7 +192,7 @@ void gInOut::cb_inToOut (Fl_Widget *v, void *p) { ((gInOut*)p)->__cb_inToO #endif -/* ------------------------------------------------------------------ */ +/* -------------------------------------------------------------------------- */ void gInOut::__cb_outVol() @@ -200,7 +201,7 @@ void gInOut::__cb_outVol() } -/* ------------------------------------------------------------------ */ +/* -------------------------------------------------------------------------- */ void gInOut::__cb_inVol() @@ -209,18 +210,18 @@ void gInOut::__cb_inVol() } -/* ------------------------------------------------------------------ */ +/* -------------------------------------------------------------------------- */ #ifdef WITH_VST void gInOut::__cb_masterFxOut() { - gu_openSubWindow(mainWin, new gdPluginList(PluginHost::MASTER_OUT), WID_FX_LIST); + gu_openSubWindow(G_MainWin, new gdPluginList(PluginHost::MASTER_OUT), WID_FX_LIST); } void gInOut::__cb_masterFxIn() { - gu_openSubWindow(mainWin, new gdPluginList(PluginHost::MASTER_IN), WID_FX_LIST); + gu_openSubWindow(G_MainWin, new gdPluginList(PluginHost::MASTER_IN), WID_FX_LIST); } void gInOut::__cb_inToOut() @@ -230,7 +231,7 @@ void gInOut::__cb_inToOut() #endif -/* ------------------------------------------------------------------ */ +/* -------------------------------------------------------------------------- */ void gInOut::refresh() @@ -242,9 +243,9 @@ void gInOut::refresh() } -/* ------------------------------------------------------------------ */ -/* ------------------------------------------------------------------ */ -/* ------------------------------------------------------------------ */ +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ gMenu::gMenu(int x, int y) @@ -268,7 +269,7 @@ gMenu::gMenu(int x, int y) } -/* ------------------------------------------------------------------ */ +/* -------------------------------------------------------------------------- */ void gMenu::cb_about (Fl_Widget *v, void *p) { ((gMenu*)p)->__cb_about(); } @@ -277,25 +278,25 @@ void gMenu::cb_file (Fl_Widget *v, void *p) { ((gMenu*)p)->__cb_file(); } void gMenu::cb_edit (Fl_Widget *v, void *p) { ((gMenu*)p)->__cb_edit(); } -/* ------------------------------------------------------------------ */ +/* -------------------------------------------------------------------------- */ void gMenu::__cb_about() { - gu_openSubWindow(mainWin, new gdAbout(), WID_ABOUT); + gu_openSubWindow(G_MainWin, new gdAbout(), WID_ABOUT); } -/* ------------------------------------------------------------------ */ +/* -------------------------------------------------------------------------- */ void gMenu::__cb_config() { - gu_openSubWindow(mainWin, new gdConfig(380, 370), WID_CONFIG); + gu_openSubWindow(G_MainWin, new gdConfig(380, 370), WID_CONFIG); } -/* ------------------------------------------------------------------ */ +/* -------------------------------------------------------------------------- */ void gMenu::__cb_file() @@ -322,11 +323,11 @@ void gMenu::__cb_file() if (strcmp(m->label(), "Open patch or project...") == 0) { //gWindow *childWin = new gdBrowser("Load Patch", G_Conf.patchPath.c_str(), 0, BROWSER_LOAD_PATCH); - //gu_openSubWindow(mainWin, childWin, WID_FILE_BROWSER); + //gu_openSubWindow(G_MainWin, childWin, WID_FILE_BROWSER); gWindow *childWin = new gdLoadBrowser(G_Conf.browserX, G_Conf.browserY, G_Conf.browserW, G_Conf.browserH, "Load patch or project", G_Conf.patchPath, glue_loadPatch, NULL); - gu_openSubWindow(mainWin, childWin, WID_FILE_BROWSER); + gu_openSubWindow(G_MainWin, childWin, WID_FILE_BROWSER); return; } if (strcmp(m->label(), "Save patch...") == 0) { @@ -336,24 +337,24 @@ void gMenu::__cb_file() gWindow *childWin = new gdSaveBrowser(G_Conf.browserX, G_Conf.browserY, G_Conf.browserW, G_Conf.browserH, "Save patch", G_Conf.patchPath, G_Patch.name, glue_savePatch, NULL); - gu_openSubWindow(mainWin, childWin, WID_FILE_BROWSER); + gu_openSubWindow(G_MainWin, childWin, WID_FILE_BROWSER); return; } if (strcmp(m->label(), "Save project...") == 0) { gWindow *childWin = new gdSaveBrowser(G_Conf.browserX, G_Conf.browserY, G_Conf.browserW, G_Conf.browserH, "Save project", G_Conf.patchPath, G_Patch.name, glue_saveProject, NULL); - gu_openSubWindow(mainWin, childWin, WID_FILE_BROWSER); + gu_openSubWindow(G_MainWin, childWin, WID_FILE_BROWSER); return; } if (strcmp(m->label(), "Quit Giada") == 0) { - mainWin->do_callback(); + G_MainWin->do_callback(); return; } } -/* ------------------------------------------------------------------ */ +/* -------------------------------------------------------------------------- */ void gMenu::__cb_edit() @@ -396,14 +397,14 @@ void gMenu::__cb_edit() if (strcmp(m->label(), "Clear all samples") == 0) { if (!gdConfirmWin("Warning", "Clear all samples: are you sure?")) return; - mainWin->delSubWindow(WID_SAMPLE_EDITOR); + G_MainWin->delSubWindow(WID_SAMPLE_EDITOR); glue_clearAllSamples(); return; } if (strcmp(m->label(), "Clear all actions") == 0) { if (!gdConfirmWin("Warning", "Clear all actions: are you sure?")) return; - mainWin->delSubWindow(WID_ACTION_EDITOR); + G_MainWin->delSubWindow(WID_ACTION_EDITOR); glue_clearAllRecs(); return; } @@ -415,19 +416,19 @@ void gMenu::__cb_edit() return; } if (strcmp(m->label(), "Remove empty columns") == 0) { - mainWin->keyboard->organizeColumns(); + G_MainWin->keyboard->organizeColumns(); return; } if (strcmp(m->label(), "Setup global MIDI input...") == 0) { - gu_openSubWindow(mainWin, new gdMidiInputMaster(), 0); + gu_openSubWindow(G_MainWin, new gdMidiInputMaster(), 0); return; } } -/* ------------------------------------------------------------------ */ -/* ------------------------------------------------------------------ */ -/* ------------------------------------------------------------------ */ +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ gTiming::gTiming(int x, int y) @@ -438,8 +439,8 @@ gTiming::gTiming(int x, int y) quantizer = new gChoice(x, y, 40, 20, "", false); bpm = new gClick (quantizer->x()+quantizer->w()+4, y, 40, 20); meter = new gClick (bpm->x()+bpm->w()+8, y, 40, 20, "4/1"); - multiplier = new gClick (meter->x()+meter->w()+4, y, 20, 20, "", beatsMultiplyOff_xpm, beatsMultiplyOn_xpm); - divider = new gClick (multiplier->x()+multiplier->w()+4, y, 20, 20, "", beatsDivideOff_xpm, beatsDivideOn_xpm); + multiplier = new gClick (meter->x()+meter->w()+4, y, 20, 20, "", multiplyOff_xpm, multiplyOn_xpm); + divider = new gClick (multiplier->x()+multiplier->w()+4, y, 20, 20, "", divideOff_xpm, divideOn_xpm); end(); @@ -464,7 +465,7 @@ gTiming::gTiming(int x, int y) } -/* ------------------------------------------------------------------ */ +/* -------------------------------------------------------------------------- */ void gTiming::cb_bpm (Fl_Widget *v, void *p) { ((gTiming*)p)->__cb_bpm(); } @@ -474,25 +475,25 @@ void gTiming::cb_multiplier(Fl_Widget *v, void *p) { ((gTiming*)p)->__cb_multipl void gTiming::cb_divider (Fl_Widget *v, void *p) { ((gTiming*)p)->__cb_divider(); } -/* ------------------------------------------------------------------ */ +/* -------------------------------------------------------------------------- */ void gTiming::__cb_bpm() { - gu_openSubWindow(mainWin, new gdBpmInput(bpm->label()), WID_BPM); + gu_openSubWindow(G_MainWin, new gdBpmInput(bpm->label()), WID_BPM); } -/* ------------------------------------------------------------------ */ +/* -------------------------------------------------------------------------- */ void gTiming::__cb_meter() { - gu_openSubWindow(mainWin, new gdBeatsInput(), WID_BEATS); + gu_openSubWindow(G_MainWin, new gdBeatsInput(), WID_BEATS); } -/* ------------------------------------------------------------------ */ +/* -------------------------------------------------------------------------- */ void gTiming::__cb_quantizer() @@ -501,7 +502,7 @@ void gTiming::__cb_quantizer() } -/* ------------------------------------------------------------------ */ +/* -------------------------------------------------------------------------- */ void gTiming::__cb_multiplier() @@ -510,7 +511,7 @@ void gTiming::__cb_multiplier() } -/* ------------------------------------------------------------------ */ +/* -------------------------------------------------------------------------- */ void gTiming::__cb_divider() @@ -519,7 +520,7 @@ void gTiming::__cb_divider() } -/* ------------------------------------------------------------------ */ +/* -------------------------------------------------------------------------- */ void gTiming::setBpm(const char *v) @@ -536,7 +537,7 @@ void gTiming::setBpm(float v) } -/* ------------------------------------------------------------------ */ +/* -------------------------------------------------------------------------- */ void gTiming::setMeter(int beats, int bars) diff --git a/src/gui/dialogs/gd_mainWindow.h b/src/gui/dialogs/gd_mainWindow.h index 7adc52d..a0a0489 100644 --- a/src/gui/dialogs/gd_mainWindow.h +++ b/src/gui/dialogs/gd_mainWindow.h @@ -1,9 +1,9 @@ -/* --------------------------------------------------------------------- +/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * gd_mainWindow * - * --------------------------------------------------------------------- + * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual * @@ -23,7 +23,7 @@ * along with Giada - Your Hardcore Loopmachine. If not, see * . * - * ------------------------------------------------------------------ */ + * -------------------------------------------------------------------------- */ #ifndef GD_MAINWINDOW_H @@ -37,7 +37,7 @@ #include "../elems/ge_controller.h" -/* ------------------------------------------------------------------ */ +/* -------------------------------------------------------------------------- */ class gdMainWindow : public gWindow @@ -60,7 +60,7 @@ public: }; -/* ------------------------------------------------------------------ */ +/* -------------------------------------------------------------------------- */ class gInOut : public Fl_Group @@ -108,7 +108,7 @@ public: }; -/* ------------------------------------------------------------------ */ +/* -------------------------------------------------------------------------- */ class gMenu : public Fl_Group @@ -136,7 +136,7 @@ public: }; -/* ------------------------------------------------------------------ */ +/* -------------------------------------------------------------------------- */ class gTiming : public Fl_Group diff --git a/src/gui/dialogs/gd_midiInput.cpp b/src/gui/dialogs/gd_midiInput.cpp index ce7f468..e713416 100644 --- a/src/gui/dialogs/gd_midiInput.cpp +++ b/src/gui/dialogs/gd_midiInput.cpp @@ -27,17 +27,23 @@ * -------------------------------------------------------------------------- */ -#include "../../utils/gui_utils.h" +#include "../../utils/gui.h" #include "../../core/kernelMidi.h" #include "../../core/conf.h" +#include "../../core/const.h" #include "../../core/sampleChannel.h" #include "../../utils/log.h" +#include "../../utils/string.h" #include "../elems/ge_mixed.h" #include "../elems/ge_midiIoTools.h" #include "gd_midiInput.h" -extern Conf G_Conf; +extern Conf G_Conf; +extern KernelMidi G_KernelMidi; + + +using std::string; gdMidiInput::gdMidiInput(int w, int h, const char *title) @@ -49,16 +55,18 @@ gdMidiInput::gdMidiInput(int w, int h, const char *title) /* -------------------------------------------------------------------------- */ -gdMidiInput::~gdMidiInput() { - kernelMidi::stopMidiLearn(); +gdMidiInput::~gdMidiInput() +{ + G_KernelMidi.stopMidiLearn(); } /* -------------------------------------------------------------------------- */ -void gdMidiInput::stopMidiLearn(gLearner *learner) { - kernelMidi::stopMidiLearn(); +void gdMidiInput::stopMidiLearn(gLearner *learner) +{ + G_KernelMidi.stopMidiLearn(); learner->updateValue(); } @@ -66,17 +74,19 @@ void gdMidiInput::stopMidiLearn(gLearner *learner) { /* -------------------------------------------------------------------------- */ -void gdMidiInput::__cb_learn(uint32_t *param, uint32_t msg, gLearner *l) { +void gdMidiInput::__cb_learn(uint32_t *param, uint32_t msg, gLearner *l) +{ *param = msg; stopMidiLearn(l); - gLog("[gdMidiGrabber] MIDI learn done - message=0x%X\n", msg); + gu_log("[gdMidiGrabber] MIDI learn done - message=0x%X\n", msg); } /* -------------------------------------------------------------------------- */ -void gdMidiInput::cb_learn(uint32_t msg, void *d) { +void gdMidiInput::cb_learn(uint32_t msg, void *d) +{ cbData *data = (cbData*) d; gdMidiInput *window = (gdMidiInput*) data->window; gLearner *learner = data->learner; @@ -95,7 +105,8 @@ void gdMidiInput::cb_close(Fl_Widget *w, void *p) { ((gdMidiInput*)p)->__cb_clo /* -------------------------------------------------------------------------- */ -void gdMidiInput::__cb_close() { +void gdMidiInput::__cb_close() +{ do_callback(); } @@ -106,12 +117,11 @@ void gdMidiInput::__cb_close() { gdMidiInputChannel::gdMidiInputChannel(Channel *ch) - : gdMidiInput(300, 206, "MIDI Input Setup"), + : gdMidiInput(300, 230, "MIDI Input Setup"), ch(ch) { - char title[64]; - sprintf(title, "MIDI Input Setup (channel %d)", ch->index+1); - label(title); + string title = "MIDI Input Setup (channel " + gu_itoa(ch->index+1) + ")"; + label(title.c_str()); set_modal(); @@ -119,16 +129,17 @@ gdMidiInputChannel::gdMidiInputChannel(Channel *ch) new gLearner(8, 30, w()-16, "key press", cb_learn, &ch->midiInKeyPress); new gLearner(8, 54, w()-16, "key release", cb_learn, &ch->midiInKeyRel); new gLearner(8, 78, w()-16, "key kill", cb_learn, &ch->midiInKill); - new gLearner(8, 102, w()-16, "mute", cb_learn, &ch->midiInMute); - new gLearner(8, 126, w()-16, "solo", cb_learn, &ch->midiInSolo); - new gLearner(8, 150, w()-16, "volume", cb_learn, &ch->midiInVolume); - int yy = 178; + new gLearner(8, 102, w()-16, "arm", cb_learn, &ch->midiInArm); + new gLearner(8, 126, w()-16, "mute", cb_learn, &ch->midiInMute); + new gLearner(8, 150, w()-16, "solo", cb_learn, &ch->midiInSolo); + new gLearner(8, 174, w()-16, "volume", cb_learn, &ch->midiInVolume); + int yy = 202; if (ch->type == CHANNEL_SAMPLE) { - size(300, 254); - new gLearner(8, 174, w()-16, "pitch", cb_learn, &((SampleChannel*)ch)->midiInPitch); - new gLearner(8, 198, w()-16, "read actions", cb_learn, &((SampleChannel*)ch)->midiInReadActions); - yy = 226; + size(300, 278); + new gLearner(8, 198, w()-16, "pitch", cb_learn, &((SampleChannel*)ch)->midiInPitch); + new gLearner(8, 222, w()-16, "read actions", cb_learn, &((SampleChannel*)ch)->midiInReadActions); + yy = 250; } ok = new gButton(w()-88, yy, 80, 20, "Close"); @@ -151,7 +162,8 @@ void gdMidiInputChannel::cb_enable(Fl_Widget *w, void *p) { ((gdMidiInputChanne /* -------------------------------------------------------------------------- */ -void gdMidiInputChannel::__cb_enable() { +void gdMidiInputChannel::__cb_enable() +{ ch->midiIn = enable->value(); } diff --git a/src/gui/dialogs/gd_midiInput.h b/src/gui/dialogs/gd_midiInput.h index 0ade193..eb236e7 100644 --- a/src/gui/dialogs/gd_midiInput.h +++ b/src/gui/dialogs/gd_midiInput.h @@ -31,17 +31,14 @@ #define GD_MIDI_INPUT_H -#include "../../core/kernelMidi.h" -#include "../../utils/utils.h" #include "../elems/ge_window.h" -#include "../elems/ge_mixed.h" class gdMidiInput : public gWindow { protected: - gClick *ok; + class gClick *ok; void stopMidiLearn(class gLearner *l); @@ -70,8 +67,7 @@ private: class Channel *ch; - gCheck *enable; - + class gCheck *enable; //gVector items; for future use, with vst parameters diff --git a/src/gui/dialogs/gd_midiOutput.cpp b/src/gui/dialogs/gd_midiOutput.cpp index 69aea2c..5b2be1f 100644 --- a/src/gui/dialogs/gd_midiOutput.cpp +++ b/src/gui/dialogs/gd_midiOutput.cpp @@ -31,19 +31,20 @@ #include "../../core/sampleChannel.h" #include "../../core/conf.h" #include "../../core/midiChannel.h" -#include "../../utils/gui_utils.h" +#include "../../utils/gui.h" +#include "../../utils/log.h" #include "../elems/ge_mixed.h" -#include "../elems/ge_channel.h" +#include "../elems/channel.h" #include "../elems/ge_midiIoTools.h" #include "../elems/ge_keyboard.h" #include "gd_midiOutput.h" -extern Conf G_Conf; +extern Conf G_Conf; +extern KernelMidi G_KernelMidi; gdMidiOutput::gdMidiOutput(int w, int h) - //: gWindow(300, 64, "Midi Output Setup") : gWindow(w, h, "Midi Output Setup") { } @@ -52,8 +53,9 @@ gdMidiOutput::gdMidiOutput(int w, int h) /* -------------------------------------------------------------------------- */ -void gdMidiOutput::stopMidiLearn(gLearner *learner) { - kernelMidi::stopMidiLearn(); +void gdMidiOutput::stopMidiLearn(gLearner *learner) +{ + G_KernelMidi.stopMidiLearn(); learner->updateValue(); } @@ -61,17 +63,19 @@ void gdMidiOutput::stopMidiLearn(gLearner *learner) { /* -------------------------------------------------------------------------- */ -void gdMidiOutput::__cb_learn(uint32_t *param, uint32_t msg, gLearner *l) { +void gdMidiOutput::__cb_learn(uint32_t *param, uint32_t msg, gLearner *l) +{ *param = msg; stopMidiLearn(l); - gLog("[gdMidiGrabber] MIDI learn done - message=0x%X\n", msg); + gu_log("[gdMidiGrabber] MIDI learn done - message=0x%X\n", msg); } /* -------------------------------------------------------------------------- */ -void gdMidiOutput::cb_learn(uint32_t msg, void *d) { +void gdMidiOutput::cb_learn(uint32_t msg, void *d) +{ cbData *data = (cbData*) d; gdMidiOutput *window = (gdMidiOutput*) data->window; gLearner *learner = data->learner; @@ -90,7 +94,8 @@ void gdMidiOutput::cb_close(Fl_Widget *w, void *p) { ((gdMidiOutput*)p)->__cb_c /* -------------------------------------------------------------------------- */ -void gdMidiOutput::__cb_close() { +void gdMidiOutput::__cb_close() +{ do_callback(); } @@ -98,7 +103,8 @@ void gdMidiOutput::__cb_close() { /* -------------------------------------------------------------------------- */ -void gdMidiOutput::cb_enableLightning(Fl_Widget *w, void *p) { +void gdMidiOutput::cb_enableLightning(Fl_Widget *w, void *p) +{ ((gdMidiOutput*)p)->__cb_enableLightning(); } @@ -190,7 +196,8 @@ void gdMidiOutputMidiCh::cb_enableChanList(Fl_Widget *w, void *p) { ((gdMidiOutp /* -------------------------------------------------------------------------- */ -void gdMidiOutputMidiCh::__cb_enableChanList() { +void gdMidiOutputMidiCh::__cb_enableChanList() +{ enableOut->value() ? chanListOut->activate() : chanListOut->deactivate(); } @@ -198,7 +205,8 @@ void gdMidiOutputMidiCh::__cb_enableChanList() { /* -------------------------------------------------------------------------- */ -void gdMidiOutputMidiCh::__cb_close() { +void gdMidiOutputMidiCh::__cb_close() +{ ch->midiOut = enableOut->value(); ch->midiOutChan = chanListOut->value(); ch->midiOutL = enableLightning->value(); @@ -243,7 +251,8 @@ void gdMidiOutputSampleCh::cb_close(Fl_Widget *w, void *p) { ((gdMidiOutputSampl /* -------------------------------------------------------------------------- */ -void gdMidiOutputSampleCh::__cb_close() { +void gdMidiOutputSampleCh::__cb_close() +{ ch->midiOutL = enableLightning->value(); do_callback(); } diff --git a/src/gui/dialogs/gd_pluginChooser.cpp b/src/gui/dialogs/gd_pluginChooser.cpp index c090caa..d868108 100644 --- a/src/gui/dialogs/gd_pluginChooser.cpp +++ b/src/gui/dialogs/gd_pluginChooser.cpp @@ -30,7 +30,7 @@ #ifdef WITH_VST -#include "../../utils/gui_utils.h" +#include "../../utils/gui.h" #include "../../core/channel.h" #include "../../core/mixer.h" #include "../../core/conf.h" diff --git a/src/gui/dialogs/gd_pluginList.cpp b/src/gui/dialogs/gd_pluginList.cpp index 433284f..003ca63 100644 --- a/src/gui/dialogs/gd_pluginList.cpp +++ b/src/gui/dialogs/gd_pluginList.cpp @@ -30,16 +30,19 @@ #ifdef WITH_VST -#include "../../utils/gui_utils.h" -#include "../../utils/utils.h" +#include "../../utils/gui.h" +#include "../../utils/fs.h" #include "../../core/conf.h" +#include "../../core/const.h" #include "../../core/graphics.h" #include "../../core/pluginHost.h" #include "../../core/plugin.h" #include "../../core/mixer.h" #include "../../core/channel.h" +#include "../../utils/log.h" +#include "../../utils/string.h" #include "../elems/ge_mixed.h" -#include "../elems/ge_channel.h" +#include "../elems/channel.h" #include "gd_pluginList.h" #include "gd_pluginChooser.h" #include "gd_pluginWindow.h" @@ -51,7 +54,7 @@ extern Conf G_Conf; extern Mixer G_Mixer; extern PluginHost G_PluginHost; -extern gdMainWindow *mainWin; +extern gdMainWindow *G_MainWin; using std::string; @@ -86,7 +89,7 @@ gdPluginList::gdPluginList(int stackType, Channel *ch) if (stackType == PluginHost::MASTER_IN) label("Master In Plugins"); else { - string l = "Channel " + gItoa(ch->index+1) + " Plugins"; + string l = "Channel " + gu_itoa(ch->index+1) + " Plugins"; copy_label(l.c_str()); } @@ -195,12 +198,12 @@ void gdPluginList::refreshList() { gdPluginListMaster */ if (stackType == PluginHost::MASTER_OUT) { - mainWin->inOut->setMasterFxOutFull( + G_MainWin->inOut->setMasterFxOutFull( G_PluginHost.countPlugins(stackType, ch) > 0); } else if (stackType == PluginHost::MASTER_IN) { - mainWin->inOut->setMasterFxInFull( + G_MainWin->inOut->setMasterFxInFull( G_PluginHost.countPlugins(stackType, ch) > 0); } else { @@ -340,13 +343,13 @@ void gdPlugin::__cb_openPluginWindow() gWindow *w; if (pPlugin->hasEditor()) { if (pPlugin->isEditorOpen()) { - gLog("[gdPlugin::__cb_openPluginWindow] plugin has editor but it's already visible\n"); + gu_log("[gdPlugin::__cb_openPluginWindow] plugin has editor but it's already visible\n"); return; } int pwid = pPlugin->getId()+1; - gLog("[gdPlugin::__cb_openPluginWindow] plugin has editor, open window id=%d\n", pwid); + gu_log("[gdPlugin::__cb_openPluginWindow] plugin has editor, open window id=%d\n", pwid); if (pParent->hasWindow(pwid)) pParent->delSubWindow(pwid); diff --git a/src/gui/dialogs/gd_pluginWindow.cpp b/src/gui/dialogs/gd_pluginWindow.cpp index cc4348a..0c0be5f 100644 --- a/src/gui/dialogs/gd_pluginWindow.cpp +++ b/src/gui/dialogs/gd_pluginWindow.cpp @@ -31,7 +31,7 @@ #include -#include "../../utils/gui_utils.h" +#include "../../utils/gui.h" #include "../../core/plugin.h" #include "../elems/ge_mixed.h" #include "gd_pluginWindow.h" diff --git a/src/gui/dialogs/gd_pluginWindowGUI.cpp b/src/gui/dialogs/gd_pluginWindowGUI.cpp index b50b1f4..e8ba698 100644 --- a/src/gui/dialogs/gd_pluginWindowGUI.cpp +++ b/src/gui/dialogs/gd_pluginWindowGUI.cpp @@ -31,7 +31,7 @@ #include "../../utils/log.h" -#include "../../utils/gui_utils.h" +#include "../../utils/gui.h" #include "../../core/pluginHost.h" #include "../../core/plugin.h" #include "../../core/const.h" @@ -57,7 +57,7 @@ gdPluginWindowGUI::gdPluginWindowGUI(Plugin *pPlugin) #endif - gLog("[gdPluginWindowGUI] opening GUI, this=%p, xid=%p\n", + gu_log("[gdPluginWindowGUI] opening GUI, this=%p, xid=%p\n", (void*) this, (void*) fl_xid(this)); #if defined(__APPLE__) @@ -75,8 +75,6 @@ gdPluginWindowGUI::gdPluginWindowGUI(Plugin *pPlugin) int pluginW = pPlugin->getEditorW(); int pluginH = pPlugin->getEditorH(); - printf("w=%d h=%d\n", Fl::w(), Fl::h()); - resize((Fl::w() - pluginW) / 2, (Fl::h() - pluginH) / 2, pluginW, pluginH); Fl::add_timeout(GUI_PLUGIN_RATE, cb_refresh, (void*) this); @@ -100,7 +98,7 @@ void gdPluginWindowGUI::__cb_close() { Fl::remove_timeout(cb_refresh); pPlugin->closeEditor(); - gLog("[gdPluginWindowGUI::__cb_close] GUI closed, this=%p\n", (void*) this); + gu_log("[gdPluginWindowGUI::__cb_close] GUI closed, this=%p\n", (void*) this); } @@ -109,7 +107,7 @@ void gdPluginWindowGUI::__cb_close() void gdPluginWindowGUI::__cb_refresh() { - //gLog("[gdPluginWindowGUI::__cb_refresh] refresh!\n"); + //gu_log("[gdPluginWindowGUI::__cb_refresh] refresh!\n"); G_PluginHost.runDispatchLoop(); Fl::repeat_timeout(GUI_PLUGIN_RATE, cb_refresh, (void*) this); } diff --git a/src/gui/dialogs/gd_warnings.h b/src/gui/dialogs/gd_warnings.h index c2350cf..13d3b4e 100644 --- a/src/gui/dialogs/gd_warnings.h +++ b/src/gui/dialogs/gd_warnings.h @@ -33,7 +33,7 @@ #include #include #include "../elems/ge_mixed.h" -#include "../../utils/gui_utils.h" +#include "../../utils/gui.h" void gdAlert(const char *c); diff --git a/src/gui/elems/actionEditor.cpp b/src/gui/elems/actionEditor.cpp new file mode 100644 index 0000000..4a88413 --- /dev/null +++ b/src/gui/elems/actionEditor.cpp @@ -0,0 +1,679 @@ +/* ----------------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * ge_actionChannel + * + * ----------------------------------------------------------------------------- + * + * Copyright (C) 2010-2016 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/conf.h" +#include "../../core/channel.h" +#include "../../core/sampleChannel.h" +#include "../../glue/main.h" +#include "../../utils/log.h" +#include "../dialogs/gd_mainWindow.h" +#include "../dialogs/gd_actionEditor.h" +#include "ge_keyboard.h" +#include "actionEditor.h" + + +extern gdMainWindow *G_MainWin; +extern Mixer G_Mixer; +extern Conf G_Conf; +extern Recorder G_Recorder; + + +/* -------------------------------------------------------------------------- */ + + +geActionEditor::geActionEditor(int x, int y, gdActionEditor *pParent, SampleChannel *ch) + : geBaseActionEditor(x, y, 200, 40, pParent), + ch (ch), + selected (NULL) +{ + 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 ACTION_KILLCHAN 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 ACTION_KEYREL in a SINGLE_PRESS channel. It's up to gAction to + find the other piece (namely frame_b) + - not of types ACTION_KEYPRESS | ACTION_KEYREL | ACTION_KILLCHAN */ + + if ((action->chan != pParent->chan->index) || + (G_Recorder.frames.at(i) > G_Mixer.totalFrames) || + (action->type == ACTION_KILLCHAN && ch->mode == SINGLE_PRESS) || + (action->type == ACTION_KEYREL && ch->mode == SINGLE_PRESS) || + (action->type & ~(ACTION_KEYPRESS | ACTION_KEYREL | ACTION_KILLCHAN)) + ) + continue; + + int ax = x + (action->frame / pParent->zoom); + gAction *a = new gAction( + 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 +} + + +/* -------------------------------------------------------------------------- */ + + +gAction *geActionEditor::getSelectedAction() +{ + for (int i=0; ix(); + int action_w = ((gAction*)child(i))->w(); + if (Fl::event_x() >= action_x && Fl::event_x() <= action_x + action_w) + return (gAction*)child(i); + } + return NULL; +} + + +/* -------------------------------------------------------------------------- */ + + +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 */ + + gAction *a; + for (int i=0; iframe_a / pParent->zoom); + + if (ch->mode == SINGLE_PRESS) { + int newW = ((a->frame_b - a->frame_a) / pParent->zoom); + if (newW < gAction::MIN_WIDTH) + newW = gAction::MIN_WIDTH; + a->resize(newX, a->y(), newW, a->h()); + } + else + a->resize(newX, a->y(), gAction::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(COLOR_BG_1); + 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 == NULL) { // 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()+gAction::MIN_WIDTH) + aw = gAction::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()-gAction::MIN_WIDTH) { + ax = selected->x()+selected->w()-gAction::MIN_WIDTH; + aw = gAction::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 == NULL) { + + /* 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; + } + } + + gAction *a = new gAction( + ax, // x + y()+4, // y + h()-8, // h + fx, // frame_a + G_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()) { + gAction *a = getSelectedAction(); + if (a != NULL) { + a->delAction(); + remove(a); + delete a; + G_MainWin->keyboard->setChannelWithActions((geSampleChannel*)pParent->chan->guiChannel); + redraw(); + ret = 1; + } + } + break; + } + case FL_RELEASE: { + + if (selected == NULL) { + 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 == SINGLE_PRESS && + actionOriginalX+actionOriginalW != selected->x()+selected->w()) + noChanges = false; + + if (noChanges) { + ret = 1; + selected = NULL; + 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 = ((gAction*)child(i))->w(); + if (ch->mode == 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 NULL, 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 = NULL; + 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 == SINGLE_PRESS) { + for (int i=0; iframe_b && frame >= c->frame_a) + collision = true; + } + } + + return collision; +} + + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + + +/** TODO - index is useless? + * TODO - pass a record::action pointer and let gAction compute values */ + +gAction::gAction(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 == SINGLE_PRESS && type == ACTION_KEYPRESS) { + Recorder::action *a2 = NULL; + G_Recorder.getNextAction(ch->index, 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 gAction::draw() +{ + int color; + if (selected) /// && geActionEditor !disabled + color = COLOR_BD_1; + else + color = COLOR_BG_2; + + if (ch->mode == SINGLE_PRESS) { + fl_rectf(x(), y(), w(), h(), (Fl_Color) color); + } + else { + if (type == ACTION_KILLCHAN) + fl_rect(x(), y(), MIN_WIDTH, h(), (Fl_Color) color); + else { + fl_rectf(x(), y(), MIN_WIDTH, h(), (Fl_Color) color); + if (type == ACTION_KEYPRESS) + fl_rectf(x()+3, y()+h()-11, 2, 8, COLOR_BD_0); + else + if (type == ACTION_KEYREL) + fl_rectf(x()+3, y()+3, 2, 8, COLOR_BD_0); + } + } + +} + + +/* -------------------------------------------------------------------------- */ + + +int gAction::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 == 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 gAction::addAction() +{ + /* always check frame parity */ + + if (frame_a % 2 != 0) + frame_a++; + + /* anatomy of an action + * ____[#######]_____ (a) is the left margin, ACTION_KEYPRESS. (b) is + * a b the right margin, the 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 == SINGLE_PRESS) { + G_Recorder.rec(parent->chan->index, ACTION_KEYPRESS, frame_a); + G_Recorder.rec(parent->chan->index, ACTION_KEYREL, frame_a+4096); + //gu_log("action added, [%d, %d]\n", frame_a, frame_a+4096); + } + else { + G_Recorder.rec(parent->chan->index, parent->getActionType(), frame_a); + //gu_log("action added, [%d]\n", frame_a); + } + + G_Recorder.sortActions(); + + index++; // important! +} + + +/* -------------------------------------------------------------------------- */ + + +void gAction::delAction() +{ + /* if SINGLE_PRESS you must delete both the keypress and the keyrelease + * actions. */ + + if (ch->mode == SINGLE_PRESS) { + G_Recorder.deleteAction(parent->chan->index, frame_a, ACTION_KEYPRESS, false); + G_Recorder.deleteAction(parent->chan->index, frame_b, ACTION_KEYREL, false); + } + else + G_Recorder.deleteAction(parent->chan->index, frame_a, type, false); + + /* 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 gAction::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++; + + G_Recorder.rec(parent->chan->index, type, this->frame_a); + + if (ch->mode == SINGLE_PRESS) { + frame_b = xToFrame_b(); + G_Recorder.rec(parent->chan->index, ACTION_KEYREL, frame_b); + } + + G_Recorder.sortActions(); +} + + +/* -------------------------------------------------------------------------- */ + + +int gAction::absx() +{ + return x() - parent->ac->x(); +} + + +/* -------------------------------------------------------------------------- */ + + +int gAction::xToFrame_a() +{ + return (absx()) * parent->zoom; +} + + +/* -------------------------------------------------------------------------- */ + + +int gAction::xToFrame_b() +{ + return (absx() + w()) * parent->zoom; +} diff --git a/src/gui/elems/actionEditor.h b/src/gui/elems/actionEditor.h new file mode 100644 index 0000000..56f0ce8 --- /dev/null +++ b/src/gui/elems/actionEditor.h @@ -0,0 +1,140 @@ +/* ----------------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * ge_actionChannel + * + * ----------------------------------------------------------------------------- + * + * Copyright (C) 2010-2016 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_ACTIONCHANNEL_H +#define GE_ACTIONCHANNEL_H + + +#include +#include +#include "../../utils/gui.h" +#include "../../core/mixer.h" +#include "../../core/recorder.h" +#include "baseActionEditor.h" + + +class gAction : public Fl_Box +{ +private: + + bool selected; + unsigned index; + class gdActionEditor *parent; // pointer to parent (gActionEditor) + class SampleChannel *ch; + char type; // type of action + +public: + + gAction(int x, int y, int h, int frame_a, unsigned index, + gdActionEditor *parent, class 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; +}; + + +/* -------------------------------------------------------------------------- */ + + +class geActionEditor : public geBaseActionEditor +{ + +private: + + class SampleChannel *ch; + + /* getSelectedAction + * get the action under the mouse. NULL if nothing found. */ + + gAction *getSelectedAction(); + + /* selected + * pointer to the selected action. Useful when dragging around. */ + + gAction *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, class SampleChannel *ch); + void draw(); + int handle(int e); + void updateActions(); +}; + + +#endif diff --git a/src/gui/elems/baseActionEditor.cpp b/src/gui/elems/baseActionEditor.cpp new file mode 100644 index 0000000..bc7a412 --- /dev/null +++ b/src/gui/elems/baseActionEditor.cpp @@ -0,0 +1,102 @@ +/* ----------------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * ge_actionWidget + * + * pParent class of any widget inside the action editor. + * + * ----------------------------------------------------------------------------- + * + * Copyright (C) 2010-2016 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/mixer.h" +#include "../dialogs/gd_actionEditor.h" +#include "baseActionEditor.h" +#include "ge_mixed.h" + + +extern Mixer G_Mixer; + + +geBaseActionEditor::geBaseActionEditor(int x, int y, int w, int h, + gdActionEditor *pParent) + : Fl_Group(x, y, w, h), pParent(pParent) {} + + +/* -------------------------------------------------------------------------- */ + + +geBaseActionEditor::~geBaseActionEditor() {} + + +/* -------------------------------------------------------------------------- */ + + +void geBaseActionEditor::baseDraw(bool clear) { + + /* clear the screen */ + + if (clear) + fl_rectf(x(), y(), w(), h(), COLOR_BG_MAIN); + + /* draw the container */ + + fl_color(COLOR_BD_0); + fl_rect(x(), y(), w(), h()); + + /* grid drawing, if > 1 */ + + if (pParent->gridTool->getValue() > 1) { + + fl_color(fl_rgb_color(54, 54, 54)); + fl_line_style(FL_DASH, 0, NULL); + + 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); + } + + /* bars and beats drawing */ + + fl_color(COLOR_BD_0); + 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); + } + + fl_color(COLOR_BG_2); + 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); + } + + /* 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, COLOR_BG_1); +} diff --git a/src/gui/elems/baseActionEditor.h b/src/gui/elems/baseActionEditor.h new file mode 100644 index 0000000..49c74b1 --- /dev/null +++ b/src/gui/elems/baseActionEditor.h @@ -0,0 +1,55 @@ +/* ----------------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * ge_actionWidget + * + * parent class of any tool inside the action editor. + * + * ----------------------------------------------------------------------------- + * + * Copyright (C) 2010-2016 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_ACTIONWIDGET_H__ +#define __GE_ACTIONWIDGET_H__ + +#include +#include +#include "../../core/const.h" + + +class geBaseActionEditor : public Fl_Group +{ +protected: + + class gdActionEditor *pParent; + void baseDraw(bool clear=true); + +public: + + virtual void updateActions() = 0; + + geBaseActionEditor(int x, int y, int w, int h, gdActionEditor *pParent); + ~geBaseActionEditor(); +}; + +#endif diff --git a/src/gui/elems/channel.cpp b/src/gui/elems/channel.cpp new file mode 100644 index 0000000..a33f121 --- /dev/null +++ b/src/gui/elems/channel.cpp @@ -0,0 +1,243 @@ +/* ----------------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * ge_channel + * + * ----------------------------------------------------------------------------- + * + * Copyright (C) 2010-2016 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/mixer.h" +#include "../../core/conf.h" +#include "../../core/channel.h" +#include "../../core/patch_DEPR_.h" +#include "../../core/graphics.h" +#include "../../core/pluginHost.h" +#include "../../utils/gui.h" +#include "../../glue/channel.h" +#include "../../glue/main.h" +#include "../dialogs/gd_mainWindow.h" +#include "../dialogs/gd_pluginList.h" +#include "ge_column.h" +#include "channelButton.h" +#include "channel.h" + + +extern Mixer G_Mixer; +extern Conf G_Conf; +extern Patch_DEPR_ G_Patch_DEPR_; +extern gdMainWindow *G_MainWin; + + +geChannel::geChannel(int X, int Y, int W, int H, int type, Channel *ch) + : Fl_Group(X, Y, W, H, NULL), + ch (ch), + type (type) +{ +} + + +/* -------------------------------------------------------------------------- */ + + +void geChannel::cb_arm(Fl_Widget *v, void *p) { ((geChannel*)p)->__cb_arm(); } +void geChannel::cb_mute(Fl_Widget *v, void *p) { ((geChannel*)p)->__cb_mute(); } +void geChannel::cb_solo(Fl_Widget *v, void *p) { ((geChannel*)p)->__cb_solo(); } +void geChannel::cb_changeVol(Fl_Widget *v, void *p) { ((geChannel*)p)->__cb_changeVol(); } +#ifdef WITH_VST +void geChannel::cb_openFxWindow(Fl_Widget *v, void *p) { ((geChannel*)p)->__cb_openFxWindow(); } +#endif + + +/* -------------------------------------------------------------------------- */ + + +void geChannel::__cb_arm() +{ + glue_toggleArm(ch, true); +} + + +/* -------------------------------------------------------------------------- */ + + +void geChannel::__cb_mute() +{ + glue_setMute(ch); +} + + +/* -------------------------------------------------------------------------- */ + + +void geChannel::__cb_solo() +{ + solo->value() ? glue_setSoloOn(ch) : glue_setSoloOff(ch); +} + + +/* -------------------------------------------------------------------------- */ + + +void geChannel::__cb_changeVol() +{ + glue_setChanVol(ch, vol->value()); +} + + +/* -------------------------------------------------------------------------- */ + + +#ifdef WITH_VST +void geChannel::__cb_openFxWindow() +{ + gu_openSubWindow(G_MainWin, new gdPluginList(PluginHost::CHANNEL, ch), WID_FX_LIST); +} +#endif + + +/* -------------------------------------------------------------------------- */ + + +int geChannel::keyPress(int e) +{ + return handleKey(e, ch->key); +} + + +/* -------------------------------------------------------------------------- */ + + + +int geChannel::getColumnIndex() +{ + return ((gColumn*)parent())->getIndex(); +} + + +/* -------------------------------------------------------------------------- */ + + +void geChannel::blink() +{ + if (gu_getBlinker() > 6) + mainButton->setPlayMode(); + else + mainButton->setDefaultMode(); +} + + +/* -------------------------------------------------------------------------- */ + + +void geChannel::setColorsByStatus(int playStatus, int recStatus) +{ + switch (playStatus) { + case STATUS_OFF: + case STATUS_EMPTY: + mainButton->setDefaultMode(); + button->imgOn = channelPlay_xpm; + button->imgOff = channelStop_xpm; + button->redraw(); + break; + case STATUS_PLAY: + mainButton->setPlayMode(); + button->imgOn = channelStop_xpm; + button->imgOff = channelPlay_xpm; + button->redraw(); + break; + case STATUS_WAIT: + blink(); + break; + case STATUS_ENDING: + mainButton->setEndingMode(); + break; + } + + switch (recStatus) { + case REC_WAITING: + blink(); + break; + case REC_ENDING: + mainButton->setEndingMode(); + break; + } +} + + +/* -------------------------------------------------------------------------- */ + + +void geChannel::packWidgets() +{ + /* Count visible widgets and resize mainButton according to how many widgets + are visible. */ + + int visibles = 0; + for (int i=0; isize(20, 20); // also normalize widths + if (child(i)->visible()) + visibles++; + } + mainButton->size(w() - ((visibles - 1) * (24)), 20); // -1: exclude itself + + /* Reposition everything else */ + + for (int i=1, p=0; ivisible()) + continue; + for (int k=i-1; k>=0; k--) // Get the first visible item prior to i + if (child(k)->visible()) { + p = k; + break; + } + child(i)->position(child(p)->x() + child(p)->w() + 4, y()); + } + + init_sizes(); // Resets the internal array of widget sizes and positions +} + + +/* -------------------------------------------------------------------------- */ + + +int geChannel::handleKey(int e, int key) +{ + int ret; + if (e == FL_KEYDOWN && button->value()) // key already pressed! skip it + ret = 1; + else + if (Fl::event_key() == key && !button->value()) { + button->take_focus(); // move focus to this button + button->value((e == FL_KEYDOWN || e == FL_SHORTCUT) ? 1 : 0); // change the button's state + button->do_callback(); // invoke the button's callback + ret = 1; + } + else + ret = 0; + + if (Fl::event_key() == key) + button->value((e == FL_KEYDOWN || e == FL_SHORTCUT) ? 1 : 0); // change the button's state + + return ret; +} diff --git a/src/gui/elems/channel.h b/src/gui/elems/channel.h new file mode 100644 index 0000000..33dc00d --- /dev/null +++ b/src/gui/elems/channel.h @@ -0,0 +1,139 @@ +/* ----------------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * ge_channel + * + * ----------------------------------------------------------------------------- + * + * Copyright (C) 2010-2016 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_CHANNEL_H +#define GE_CHANNEL_H + + +#include + + +class geChannel : public Fl_Group +{ +protected: + + /* Define some breakpoints for dynamic resize. BREAK_DELTA: base amount of + pixels to shrink sampleButton. */ + +#ifdef WITH_VST + static const int BREAK_READ_ACTIONS = 240; + static const int BREAK_MODE_BOX = 216; + static const int BREAK_FX = 192; + static const int BREAK_ARM = 168; +#else + static const int BREAK_READ_ACTIONS = 216; + static const int BREAK_MODE_BOX = 192; + static const int BREAK_ARM = 168; +#endif + + static void cb_arm (Fl_Widget *v, void *p); + static void cb_mute (Fl_Widget *v, void *p); + static void cb_solo (Fl_Widget *v, void *p); + static void cb_changeVol (Fl_Widget *v, void *p); +#ifdef WITH_VST + static void cb_openFxWindow(Fl_Widget *v, void *p); +#endif + + inline void __cb_mute(); + inline void __cb_arm(); + inline void __cb_solo(); + inline void __cb_changeVol(); +#ifdef WITH_VST + inline void __cb_openFxWindow(); +#endif + + /* blink + * blink button when channel is in wait/ending status. */ + + void blink(); + + /* setColorByStatus + * update colors depending on channel status. */ + + void setColorsByStatus(int playStatus, int recStatus); + + /* handleKey + * method wrapped by virtual handle(int e). */ + + int handleKey(int e, int key); + + /* packWidgets + Spread widgets across available space. */ + + void packWidgets(); + +public: + + geChannel(int x, int y, int w, int h, int type, class Channel *ch); + + /* reset + * reset channel to initial status. */ + + virtual void reset() = 0; + + /* update + * update the label of sample button and everything else such as 'R' + * button, key box and so on, according to global values. */ + + virtual void update() = 0; + + /* refresh + * update graphics. */ + + virtual void refresh() = 0; + + /* keypress + * what to do when the corresponding key is pressed. */ + + int keyPress(int event); + + /* getColumnIndex + * return the numeric index of the column in which this channel is + * located. */ + + int getColumnIndex(); + + class Channel *ch; + + class gButton *button; + class gStatus *status; + class gClick *arm; + class geChannelButton *mainButton; + class gClick *mute; + class gClick *solo; + class gDial *vol; +#ifdef WITH_VST + class gFxButton *fx; +#endif + + int type; +}; + + +#endif diff --git a/src/gui/elems/channelButton.cpp b/src/gui/elems/channelButton.cpp new file mode 100644 index 0000000..69d262d --- /dev/null +++ b/src/gui/elems/channelButton.cpp @@ -0,0 +1,137 @@ +/* ----------------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * ge_channelButton + * + * ----------------------------------------------------------------------------- + * + * Copyright (C) 2010-2016 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 "../../utils/fs.h" +#include "channelButton.h" + + +using std::string; + + +geChannelButton::geChannelButton(int x, int y, int w, int h, const char *l) + : gClick(x, y, w, h, l), key("") {} + + +/* -------------------------------------------------------------------------- */ + + +void geChannelButton::setKey(const string &k) +{ + key = k; +} + + +/* -------------------------------------------------------------------------- */ + + +void geChannelButton::setKey(int k) +{ + if (k == 0) + key = ""; + else { + // FIXME - this crap won't work with unicode/utf-8 + char c = (char) k; + key = c; + } +} + + +/* -------------------------------------------------------------------------- */ + + +void geChannelButton::draw() +{ + gClick::draw(); + + if (key == "") + return; + + /* draw background */ + + fl_rectf(x()+1, y()+1, 18, h()-2, bgColor0); + + /* draw key */ + + fl_color(COLOR_TEXT_0); + fl_font(FL_HELVETICA, 11); + fl_draw(key.c_str(), x(), y(), 18, h(), FL_ALIGN_CENTER); +} + + +/* -------------------------------------------------------------------------- */ + + +void geChannelButton::setInputRecordMode() +{ + bgColor0 = COLOR_BG_3; +} + + +/* -------------------------------------------------------------------------- */ + + +void geChannelButton::setActionRecordMode() +{ + bgColor0 = COLOR_BG_4; + txtColor = COLOR_TEXT_0; +} + + +/* -------------------------------------------------------------------------- */ + + +void geChannelButton::setDefaultMode(const char *l) +{ + bgColor0 = COLOR_BG_0; + bdColor = COLOR_BD_0; + txtColor = COLOR_TEXT_0; + if (l) + label(l); +} + + +/* -------------------------------------------------------------------------- */ + + +void geChannelButton::setPlayMode() +{ + bgColor0 = COLOR_BG_2; + bdColor = COLOR_BD_1; + txtColor = COLOR_TEXT_1; +} + + +/* -------------------------------------------------------------------------- */ + + +void geChannelButton::setEndingMode() +{ + bgColor0 = COLOR_BD_0; +} diff --git a/src/gui/elems/channelButton.h b/src/gui/elems/channelButton.h new file mode 100644 index 0000000..da08cd8 --- /dev/null +++ b/src/gui/elems/channelButton.h @@ -0,0 +1,63 @@ +/* ----------------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * ge_channelButton + * + * ----------------------------------------------------------------------------- + * + * Copyright (C) 2010-2016 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_CHANNEL_BUTTON_H +#define GE_CHANNEL_BUTTON_H + + +#include "ge_mixed.h" + + +using std::string; + + +class geChannelButton : public gClick +{ +private: + + string key; + +public: + + geChannelButton(int x, int y, int w, int h, const char *l=0); + + virtual int handle(int e) = 0; + + void draw(); + void setKey(const string &k); + void setKey(int k); + void setPlayMode(); + void setEndingMode(); + void setDefaultMode(const char *l=0); + void setInputRecordMode(); + void setActionRecordMode(); +}; + + +#endif diff --git a/src/gui/elems/envelopeEditor.cpp b/src/gui/elems/envelopeEditor.cpp new file mode 100644 index 0000000..7ade209 --- /dev/null +++ b/src/gui/elems/envelopeEditor.cpp @@ -0,0 +1,405 @@ +/* --------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * ge_envelopeWidget + * + * Parent class of any envelope controller, from volume to VST parameter + * automations. + * + * --------------------------------------------------------------------- + * + * Copyright (C) 2010-2016 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/channel.h" +#include "../../core/recorder.h" +#include "../../core/mixer.h" +#include "../dialogs/gd_actionEditor.h" +#include "../dialogs/gd_mainWindow.h" +#include "ge_keyboard.h" +#include "envelopeEditor.h" + + +extern Mixer G_Mixer; +extern Recorder G_Recorder; +extern gdMainWindow *G_MainWin; + + +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) +{ + size(pParent->totalWidth, h()); +} + + +/* ------------------------------------------------------------------ */ + + +geEnvelopeEditor::~geEnvelopeEditor() { + clearPoints(); +} + + +/* ------------------------------------------------------------------ */ + + +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::updateActions() { + for (unsigned i=0; izoom; +} + + +/* ------------------------------------------------------------------ */ + + +void geEnvelopeEditor::draw() { + + baseDraw(); + + /* print label */ + + fl_color(COLOR_BG_1); + fl_font(FL_HELVETICA, 12); + fl_draw(l, x()+4, y(), 80, h(), (Fl_Align) (FL_ALIGN_LEFT)); + + int pxOld = x()-3; + int pyOld = y()+1; + int pxNew = 0; + int pyNew = 0; + + fl_color(COLOR_BG_2); + + for (unsigned i=0; i 0) + fl_line(pxOld+3, pyOld+3, pxNew+3, pyNew+3); + + pxOld = pxNew; + pyOld = pyNew; + } +} + + +/* ------------------------------------------------------------------ */ + + +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 + + 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; + } + else { + + /* top & border fix */ + + if (my > h()-8) my = h()-8; + if (mx > pParent->coverX-x()) mx = pParent->coverX-x(); + + if (range == 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); + G_Recorder.rec(pParent->chan->index, type, 0, 0, 1.0f); + addPoint(G_Mixer.totalFrames, 0, 1.0f, pParent->coverX, 1); + G_Recorder.rec(pParent->chan->index, type, G_Mixer.totalFrames, 0, 1.0f); + } + + /* 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); + G_Recorder.rec(pParent->chan->index, type, frame, 0, value); + G_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) { + G_Recorder.clearAction(pParent->chan->index, type); + points.clear(); + } + else { + G_Recorder.deleteAction(pParent->chan->index, points.at(selectedPoint).frame, type, false); + G_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 > G_Mixer.totalFrames) + newFrame = G_Mixer.totalFrames; + + /* 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 */ + + G_Recorder.deleteAction(pParent->chan->index, points.at(draggedPoint).frame, type, false); + + if (range == RANGE_FLOAT) { + float value = (points.at(draggedPoint).y - h() + 8) / (float) (1 - h() + 8); + G_Recorder.rec(pParent->chan->index, type, newFrame, 0, value); + } + else { + /// TODO + } + + G_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; +} + + +/* ------------------------------------------------------------------ */ + + +int geEnvelopeEditor::verticalPoint(const point &p) { + for (unsigned i=0; i points.at(i).x) + std::swap(points.at(j), points.at(i)); +} + + +/* ------------------------------------------------------------------ */ + + +int geEnvelopeEditor::getSelectedPoint() { + + /* point is a 7x7 dot */ + + 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; +} + + +/* ------------------------------------------------------------------ */ + + +void geEnvelopeEditor::fill() { + points.clear(); + for (unsigned i=0; itype == type && a->chan == pParent->chan->index) { + if (range == 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 + } + } + +} diff --git a/src/gui/elems/envelopeEditor.h b/src/gui/elems/envelopeEditor.h new file mode 100644 index 0000000..f7dade4 --- /dev/null +++ b/src/gui/elems/envelopeEditor.h @@ -0,0 +1,122 @@ +/* --------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * ge_envelopeWidget + * + * parent class of any envelope controller, from volume to VST parameter + * automations. + * + * --------------------------------------------------------------------- + * + * Copyright (C) 2010-2016 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_ENVELOPECHANNEL_H__ +#define __GE_ENVELOPECHANNEL_H__ + + +#include +#include +#include +#include "../../utils/fs.h" +#include "baseActionEditor.h" + + +using std::vector; + + +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() */ + + vector points; + + /* selectedPoint + * which point we are selecting? */ + + int selectedPoint; + + /* draggedPoint + * which point we are dragging? */ + + int draggedPoint; + + /* previousXPoint + * x coordinate of point at time t-1. Used to check effective shifts */ + + int previousXPoint; + + void draw(); + + int handle(int e); + + int getSelectedPoint(); + + void sortPoints(); + + /* 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. */ + + int verticalPoint(const point &p); + +public: + + geEnvelopeEditor(int x, int y, gdActionEditor *pParent, int type, int range, const char *l); + ~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(); + + inline void clearPoints() { points.clear(); } +}; + +#endif diff --git a/src/gui/elems/ge_actionChannel.cpp b/src/gui/elems/ge_actionChannel.cpp deleted file mode 100644 index 81b453a..0000000 --- a/src/gui/elems/ge_actionChannel.cpp +++ /dev/null @@ -1,678 +0,0 @@ -/* ----------------------------------------------------------------------------- - * - * Giada - Your Hardcore Loopmachine - * - * ge_actionChannel - * - * ----------------------------------------------------------------------------- - * - * Copyright (C) 2010-2016 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/conf.h" -#include "../../core/channel.h" -#include "../../core/sampleChannel.h" -#include "../../glue/glue.h" -#include "../../utils/log.h" -#include "../dialogs/gd_mainWindow.h" -#include "../dialogs/gd_actionEditor.h" -#include "ge_keyboard.h" -#include "ge_actionChannel.h" - - -extern gdMainWindow *mainWin; -extern Mixer G_Mixer; -extern Conf G_Conf; - - -/* -------------------------------------------------------------------------- */ - - -gActionChannel::gActionChannel(int x, int y, gdActionEditor *pParent, SampleChannel *ch) - : gActionWidget(x, y, 200, 40, pParent), - ch (ch), - selected (NULL) -{ - 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 ACTION_KILLCHAN 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 ACTION_KEYREL in a SINGLE_PRESS channel. It's up to gAction to - find the other piece (namely frame_b) - - not of types ACTION_KEYPRESS | ACTION_KEYREL | ACTION_KILLCHAN */ - - if ((action->chan != pParent->chan->index) || - (recorder::frames.at(i) > G_Mixer.totalFrames) || - (action->type == ACTION_KILLCHAN && ch->mode == SINGLE_PRESS) || - (action->type == ACTION_KEYREL && ch->mode == SINGLE_PRESS) || - (action->type & ~(ACTION_KEYPRESS | ACTION_KEYREL | ACTION_KILLCHAN)) - ) - continue; - - int ax = x + (action->frame / pParent->zoom); - gAction *a = new gAction( - 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 -} - - -/* -------------------------------------------------------------------------- */ - - -gAction *gActionChannel::getSelectedAction() -{ - for (int i=0; ix(); - int action_w = ((gAction*)child(i))->w(); - if (Fl::event_x() >= action_x && Fl::event_x() <= action_x + action_w) - return (gAction*)child(i); - } - return NULL; -} - - -/* -------------------------------------------------------------------------- */ - - -void gActionChannel::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 */ - - gAction *a; - for (int i=0; iframe_a / pParent->zoom); - - if (ch->mode == SINGLE_PRESS) { - int newW = ((a->frame_b - a->frame_a) / pParent->zoom); - if (newW < gAction::MIN_WIDTH) - newW = gAction::MIN_WIDTH; - a->resize(newX, a->y(), newW, a->h()); - } - else - a->resize(newX, a->y(), gAction::MIN_WIDTH, a->h()); - } -} - - -/* -------------------------------------------------------------------------- */ - - -void gActionChannel::draw() -{ - /* draw basic boundaries (+ beat bars) and hide the unused area. Then - * draw the children (the actions) */ - - baseDraw(); - - /* print label */ - - fl_color(COLOR_BG_1); - 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 gActionChannel::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 == NULL) { // 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()+gAction::MIN_WIDTH) - aw = gAction::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()-gAction::MIN_WIDTH) { - ax = selected->x()+selected->w()-gAction::MIN_WIDTH; - aw = gAction::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 == NULL) { - - /* 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; - } - } - - gAction *a = new gAction( - 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); - mainWin->keyboard->setChannelWithActions((gSampleChannel*)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()) { - gAction *a = getSelectedAction(); - if (a != NULL) { - a->delAction(); - remove(a); - delete a; - mainWin->keyboard->setChannelWithActions((gSampleChannel*)pParent->chan->guiChannel); - redraw(); - ret = 1; - } - } - break; - } - case FL_RELEASE: { - - if (selected == NULL) { - 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 == SINGLE_PRESS && - actionOriginalX+actionOriginalW != selected->x()+selected->w()) - noChanges = false; - - if (noChanges) { - ret = 1; - selected = NULL; - 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 = ((gAction*)child(i))->w(); - if (ch->mode == 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 NULL, 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 = NULL; - ret = 1; - break; - } - } - - return ret; -} - - -/* -------------------------------------------------------------------------- */ - - -bool gActionChannel::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 == SINGLE_PRESS) { - for (int i=0; iframe_b && frame >= c->frame_a) - collision = true; - } - } - - return collision; -} - - -/* -------------------------------------------------------------------------- */ -/* -------------------------------------------------------------------------- */ -/* -------------------------------------------------------------------------- */ - - -/** TODO - index is useless? - * TODO - pass a record::action pointer and let gAction compute values */ - -gAction::gAction(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 == SINGLE_PRESS && type == ACTION_KEYPRESS) { - recorder::action *a2 = NULL; - recorder::getNextAction(ch->index, ACTION_KEYREL, frame_a, &a2); - if (a2) { - frame_b = a2->frame; - w((frame_b - frame_a)/parent->zoom); - } - else - gLog("[gActionChannel] 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 gAction::draw() -{ - int color; - if (selected) /// && gActionChannel !disabled - color = COLOR_BD_1; - else - color = COLOR_BG_2; - - if (ch->mode == SINGLE_PRESS) { - fl_rectf(x(), y(), w(), h(), (Fl_Color) color); - } - else { - if (type == ACTION_KILLCHAN) - fl_rect(x(), y(), MIN_WIDTH, h(), (Fl_Color) color); - else { - fl_rectf(x(), y(), MIN_WIDTH, h(), (Fl_Color) color); - if (type == ACTION_KEYPRESS) - fl_rectf(x()+3, y()+h()-11, 2, 8, COLOR_BD_0); - else - if (type == ACTION_KEYREL) - fl_rectf(x()+3, y()+3, 2, 8, COLOR_BD_0); - } - } - -} - - -/* -------------------------------------------------------------------------- */ - - -int gAction::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 == 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 gAction::addAction() -{ - /* always check frame parity */ - - if (frame_a % 2 != 0) - frame_a++; - - /* anatomy of an action - * ____[#######]_____ (a) is the left margin, ACTION_KEYPRESS. (b) is - * a b the right margin, the 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 == SINGLE_PRESS) { - recorder::rec(parent->chan->index, ACTION_KEYPRESS, frame_a); - recorder::rec(parent->chan->index, ACTION_KEYREL, frame_a+4096); - //gLog("action added, [%d, %d]\n", frame_a, frame_a+4096); - } - else { - recorder::rec(parent->chan->index, parent->getActionType(), frame_a); - //gLog("action added, [%d]\n", frame_a); - } - - recorder::sortActions(); - - index++; // important! -} - - -/* -------------------------------------------------------------------------- */ - - -void gAction::delAction() -{ - /* if SINGLE_PRESS you must delete both the keypress and the keyrelease - * actions. */ - - if (ch->mode == SINGLE_PRESS) { - recorder::deleteAction(parent->chan->index, frame_a, ACTION_KEYPRESS, false); - recorder::deleteAction(parent->chan->index, frame_b, ACTION_KEYREL, false); - } - else - recorder::deleteAction(parent->chan->index, frame_a, type, false); - - /* 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 gAction::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 == SINGLE_PRESS) { - frame_b = xToFrame_b(); - recorder::rec(parent->chan->index, ACTION_KEYREL, frame_b); - } - - recorder::sortActions(); -} - - -/* -------------------------------------------------------------------------- */ - - -int gAction::absx() -{ - return x() - parent->ac->x(); -} - - -/* -------------------------------------------------------------------------- */ - - -int gAction::xToFrame_a() -{ - return (absx()) * parent->zoom; -} - - -/* -------------------------------------------------------------------------- */ - - -int gAction::xToFrame_b() -{ - return (absx() + w()) * parent->zoom; -} diff --git a/src/gui/elems/ge_actionChannel.h b/src/gui/elems/ge_actionChannel.h deleted file mode 100644 index 051feea..0000000 --- a/src/gui/elems/ge_actionChannel.h +++ /dev/null @@ -1,140 +0,0 @@ -/* ----------------------------------------------------------------------------- - * - * Giada - Your Hardcore Loopmachine - * - * ge_actionChannel - * - * ----------------------------------------------------------------------------- - * - * Copyright (C) 2010-2016 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_ACTIONCHANNEL_H -#define GE_ACTIONCHANNEL_H - - -#include -#include -#include "../../utils/gui_utils.h" -#include "../../core/mixer.h" -#include "../../core/recorder.h" -#include "ge_actionWidget.h" - - -class gAction : public Fl_Box -{ -private: - - bool selected; - unsigned index; - class gdActionEditor *parent; // pointer to parent (gActionEditor) - class SampleChannel *ch; - char type; // type of action - -public: - - gAction(int x, int y, int h, int frame_a, unsigned index, - gdActionEditor *parent, class 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; -}; - - -/* -------------------------------------------------------------------------- */ - - -class gActionChannel : public gActionWidget -{ - -private: - - class SampleChannel *ch; - - /* getSelectedAction - * get the action under the mouse. NULL if nothing found. */ - - gAction *getSelectedAction(); - - /* selected - * pointer to the selected action. Useful when dragging around. */ - - gAction *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: - - gActionChannel(int x, int y, gdActionEditor *pParent, class SampleChannel *ch); - void draw(); - int handle(int e); - void updateActions(); -}; - - -#endif diff --git a/src/gui/elems/ge_actionWidget.cpp b/src/gui/elems/ge_actionWidget.cpp deleted file mode 100644 index 562a591..0000000 --- a/src/gui/elems/ge_actionWidget.cpp +++ /dev/null @@ -1,101 +0,0 @@ -/* --------------------------------------------------------------------- - * - * Giada - Your Hardcore Loopmachine - * - * ge_actionWidget - * - * pParent class of any widget inside the action editor. - * - * --------------------------------------------------------------------- - * - * Copyright (C) 2010-2016 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/mixer.h" -#include "../dialogs/gd_actionEditor.h" -#include "ge_actionWidget.h" -#include "ge_mixed.h" - - -extern Mixer G_Mixer; - - -gActionWidget::gActionWidget(int x, int y, int w, int h, gdActionEditor *pParent) - : Fl_Group(x, y, w, h), pParent(pParent) {} - - -/* ------------------------------------------------------------------ */ - - -gActionWidget::~gActionWidget() {} - - -/* ------------------------------------------------------------------ */ - - -void gActionWidget::baseDraw(bool clear) { - - /* clear the screen */ - - if (clear) - fl_rectf(x(), y(), w(), h(), COLOR_BG_MAIN); - - /* draw the container */ - - fl_color(COLOR_BD_0); - fl_rect(x(), y(), w(), h()); - - /* grid drawing, if > 1 */ - - if (pParent->gridTool->getValue() > 1) { - - fl_color(fl_rgb_color(54, 54, 54)); - fl_line_style(FL_DASH, 0, NULL); - - 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); - } - - /* bars and beats drawing */ - - fl_color(COLOR_BD_0); - 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); - } - - fl_color(COLOR_BG_2); - 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); - } - - /* 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, COLOR_BG_1); -} diff --git a/src/gui/elems/ge_actionWidget.h b/src/gui/elems/ge_actionWidget.h deleted file mode 100644 index 1536b29..0000000 --- a/src/gui/elems/ge_actionWidget.h +++ /dev/null @@ -1,53 +0,0 @@ -/* --------------------------------------------------------------------- - * - * Giada - Your Hardcore Loopmachine - * - * ge_actionWidget - * - * parent class of any widget inside the action editor. - * - * --------------------------------------------------------------------- - * - * Copyright (C) 2010-2016 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_ACTIONWIDGET_H__ -#define __GE_ACTIONWIDGET_H__ - -#include -#include -#include "../../core/const.h" - - -class gActionWidget : public Fl_Group { - -protected: - class gdActionEditor *pParent; - void baseDraw(bool clear=true); - -public: - virtual void updateActions() = 0; - - gActionWidget(int x, int y, int w, int h, gdActionEditor *pParent); - ~gActionWidget(); -}; - -#endif diff --git a/src/gui/elems/ge_browser.cpp b/src/gui/elems/ge_browser.cpp index be64cc8..9ae3235 100644 --- a/src/gui/elems/ge_browser.cpp +++ b/src/gui/elems/ge_browser.cpp @@ -29,7 +29,7 @@ #include #include "../../core/const.h" -#include "../../utils/utils.h" +#include "../../utils/fs.h" #include "../../utils/string.h" #include "../../utils/log.h" #include "../dialogs/gd_browser.h" @@ -122,7 +122,7 @@ int gBrowser::handle(int e) string gBrowser::getCurrentDir() { - return normalize(gGetRealPath(currentDir)); + return normalize(gu_getRealPath(currentDir)); } @@ -137,7 +137,7 @@ string gBrowser::getSelectedItem(bool fullPath) if (value() == 0) // no rows selected? return current directory return normalize(currentDir); else - return normalize(gGetRealPath(currentDir + G_SLASH + normalize(text(value())))); + return normalize(gu_getRealPath(currentDir + G_SLASH + normalize(text(value())))); } @@ -158,12 +158,14 @@ string gBrowser::normalize(const string &s) { string out = s; - /* our crappy version of Clang doesn't seem to support std::string::back() */ + /* If string ends with G_SLASH, remove it. Don't do it if has length > 1, it + means that the string is just '/'. Note: our crappy version of Clang doesn't + seem to support std::string::back() */ #ifdef __APPLE__ - if (out[out.length() - 1] == G_SLASH) + if (out[out.length() - 1] == G_SLASH && out.length() > 1) #else - if (out.back() == G_SLASH) + if (out.back() == G_SLASH && out.length() > 1) #endif out = out.substr(0, out.size()-1); diff --git a/src/gui/elems/ge_channel.cpp b/src/gui/elems/ge_channel.cpp deleted file mode 100644 index 63dd719..0000000 --- a/src/gui/elems/ge_channel.cpp +++ /dev/null @@ -1,143 +0,0 @@ -/* ----------------------------------------------------------------------------- - * - * Giada - Your Hardcore Loopmachine - * - * ge_channel - * - * ----------------------------------------------------------------------------- - * - * Copyright (C) 2010-2016 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/mixer.h" -#include "../../core/conf.h" -#include "../../core/patch_DEPR_.h" -#include "../../core/graphics.h" -#include "../../core/channel.h" -#include "../../core/wave.h" -#include "../../core/sampleChannel.h" -#include "../../core/midiChannel.h" -#include "../../glue/glue.h" -#include "../../utils/gui_utils.h" -#include "../dialogs/gd_mainWindow.h" -#include "../dialogs/gd_keyGrabber.h" -#include "../dialogs/gd_midiInput.h" -#include "../dialogs/gd_editor.h" -#include "../dialogs/gd_actionEditor.h" -#include "../dialogs/gd_warnings.h" -#include "../dialogs/gd_browser.h" -#include "../dialogs/gd_midiOutput.h" -#include "ge_keyboard.h" -#include "ge_channel.h" -#include "ge_sampleChannel.h" - - -extern Mixer G_Mixer; -extern Conf G_Conf; -extern Patch_DEPR_ G_Patch_DEPR_; -extern gdMainWindow *mainWin; - - -gChannel::gChannel(int X, int Y, int W, int H, int type) - : Fl_Group(X, Y, W, H, NULL), type(type) {} - - -/* -------------------------------------------------------------------------- */ - - -int gChannel::getColumnIndex() -{ - return ((gColumn*)parent())->getIndex(); -} - - -/* -------------------------------------------------------------------------- */ - - -void gChannel::blink() -{ - if (gu_getBlinker() > 6) - mainButton->setPlayMode(); - else - mainButton->setDefaultMode(); -} - - -/* -------------------------------------------------------------------------- */ - - -void gChannel::setColorsByStatus(int playStatus, int recStatus) -{ - switch (playStatus) { - case STATUS_OFF: - mainButton->setDefaultMode(); - button->imgOn = channelPlay_xpm; - button->imgOff = channelStop_xpm; - button->redraw(); - break; - case STATUS_PLAY: - mainButton->setPlayMode(); - button->imgOn = channelStop_xpm; - button->imgOff = channelPlay_xpm; - button->redraw(); - break; - case STATUS_WAIT: - blink(); - break; - case STATUS_ENDING: - mainButton->setEndingMode(); - break; - } - - switch (recStatus) { - case REC_WAITING: - blink(); - break; - case REC_ENDING: - mainButton->setEndingMode(); - break; - } -} - - -/* -------------------------------------------------------------------------- */ - - -int gChannel::handleKey(int e, int key) -{ - int ret; - if (e == FL_KEYDOWN && button->value()) // key already pressed! skip it - ret = 1; - else - if (Fl::event_key() == key && !button->value()) { - button->take_focus(); // move focus to this button - button->value((e == FL_KEYDOWN || e == FL_SHORTCUT) ? 1 : 0); // change the button's state - button->do_callback(); // invoke the button's callback - ret = 1; - } - else - ret = 0; - - if (Fl::event_key() == key) - button->value((e == FL_KEYDOWN || e == FL_SHORTCUT) ? 1 : 0); // change the button's state - - return ret; -} diff --git a/src/gui/elems/ge_channel.h b/src/gui/elems/ge_channel.h deleted file mode 100644 index e0c99ac..0000000 --- a/src/gui/elems/ge_channel.h +++ /dev/null @@ -1,119 +0,0 @@ -/* ----------------------------------------------------------------------------- - * - * Giada - Your Hardcore Loopmachine - * - * ge_channel - * - * ----------------------------------------------------------------------------- - * - * Copyright (C) 2010-2016 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_CHANNEL_H -#define GE_CHANNEL_H - - -#include -#include -#include -#include "ge_mixed.h" - - -class gChannel : public Fl_Group -{ -protected: - - /* define some breakpoints for dynamic resize */ - -#ifdef WITH_VST - static const int BREAK_READ_ACTIONS = 212; - static const int BREAK_MODE_BOX = 188; - static const int BREAK_FX = 164; - static const int BREAK_DELTA = 120; -#else - static const int BREAK_READ_ACTIONS = 188; - static const int BREAK_MODE_BOX = 164; - static const int BREAK_FX = 140; - static const int BREAK_DELTA = 96; -#endif - static const int BREAK_UNIT = 24; - - /* blink - * blink button when channel is in wait/ending status. */ - - void blink(); - - /* setColorByStatus - * update colors depending on channel status. */ - - void setColorsByStatus(int playStatus, int recStatus); - - /* handleKey - * method wrapped by virtual handle(int e). */ - - int handleKey(int e, int key); - -public: - - gChannel(int x, int y, int w, int h, int type); - - /* reset - * reset channel to initial status. */ - - virtual void reset() = 0; - - /* update - * update the label of sample button and everything else such as 'R' - * button, key box and so on, according to global values. */ - - virtual void update() = 0; - - /* refresh - * update graphics. */ - - virtual void refresh() = 0; - - /* keypress - * what to do when the corresponding key is pressed. */ - - virtual int keyPress(int event) = 0; - - /* getColumnIndex - * return the numeric index of the column in which this channel is - * located. */ - - int getColumnIndex(); - - class gButton *button; - class gStatus *status; - class gChannelButton *mainButton; - class gDial *vol; - class gClick *mute; - class gClick *solo; -#ifdef WITH_VST - class gFxButton *fx; -#endif - - int type; -}; - - -#endif diff --git a/src/gui/elems/ge_channelButton.cpp b/src/gui/elems/ge_channelButton.cpp deleted file mode 100644 index ab6e306..0000000 --- a/src/gui/elems/ge_channelButton.cpp +++ /dev/null @@ -1,137 +0,0 @@ -/* ----------------------------------------------------------------------------- - * - * Giada - Your Hardcore Loopmachine - * - * ge_channelButton - * - * ----------------------------------------------------------------------------- - * - * Copyright (C) 2010-2016 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 "../../utils/utils.h" -#include "ge_channelButton.h" - - -using std::string; - - -gChannelButton::gChannelButton(int x, int y, int w, int h, const char *l) - : gClick(x, y, w, h, l), key("") {} - - -/* -------------------------------------------------------------------------- */ - - -void gChannelButton::setKey(const string &k) -{ - key = k; -} - - -/* -------------------------------------------------------------------------- */ - - -void gChannelButton::setKey(int k) -{ - if (k == 0) - key = ""; - else { - // FIXME - this crap won't work with unicode/utf-8 - char c = (char) k; - key = c; - } -} - - -/* -------------------------------------------------------------------------- */ - - -void gChannelButton::draw() -{ - gClick::draw(); - - if (key == "") - return; - - /* draw background */ - - fl_rectf(x()+1, y()+1, 18, h()-2, bgColor0); - - /* draw key */ - - fl_color(COLOR_TEXT_0); - fl_font(FL_HELVETICA, 11); - fl_draw(key.c_str(), x(), y(), 18, h(), FL_ALIGN_CENTER); -} - - -/* -------------------------------------------------------------------------- */ - - -void gChannelButton::setInputRecordMode() -{ - bgColor0 = COLOR_BG_3; -} - - -/* -------------------------------------------------------------------------- */ - - -void gChannelButton::setActionRecordMode() -{ - bgColor0 = COLOR_BG_4; - txtColor = COLOR_TEXT_0; -} - - -/* -------------------------------------------------------------------------- */ - - -void gChannelButton::setDefaultMode(const char *l) -{ - bgColor0 = COLOR_BG_0; - bdColor = COLOR_BD_0; - txtColor = COLOR_TEXT_0; - if (l) - label(l); -} - - -/* -------------------------------------------------------------------------- */ - - -void gChannelButton::setPlayMode() -{ - bgColor0 = COLOR_BG_2; - bdColor = COLOR_BD_1; - txtColor = COLOR_TEXT_1; -} - - -/* -------------------------------------------------------------------------- */ - - -void gChannelButton::setEndingMode() -{ - bgColor0 = COLOR_BD_0; -} diff --git a/src/gui/elems/ge_channelButton.h b/src/gui/elems/ge_channelButton.h deleted file mode 100644 index 9a1718d..0000000 --- a/src/gui/elems/ge_channelButton.h +++ /dev/null @@ -1,63 +0,0 @@ -/* ----------------------------------------------------------------------------- - * - * Giada - Your Hardcore Loopmachine - * - * ge_channelButton - * - * ----------------------------------------------------------------------------- - * - * Copyright (C) 2010-2016 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_CHANNEL_BUTTON_H -#define GE_CHANNEL_BUTTON_H - - -#include "ge_mixed.h" - - -using std::string; - - -class gChannelButton : public gClick -{ -private: - - string key; - -public: - - gChannelButton(int x, int y, int w, int h, const char *l=0); - - virtual int handle(int e) = 0; - - void draw(); - void setKey(const string &k); - void setKey(int k); - void setPlayMode(); - void setEndingMode(); - void setDefaultMode(const char *l=0); - void setInputRecordMode(); - void setActionRecordMode(); -}; - - -#endif diff --git a/src/gui/elems/ge_column.cpp b/src/gui/elems/ge_column.cpp index 5f0dc4c..7f8a046 100644 --- a/src/gui/elems/ge_column.cpp +++ b/src/gui/elems/ge_column.cpp @@ -33,16 +33,17 @@ #include "../../core/channel.h" #include "../../core/sampleChannel.h" #include "../../core/midiChannel.h" -#include "../../glue/glue.h" +#include "../../glue/main.h" #include "../../glue/channel.h" #include "../../utils/log.h" +#include "../../utils/string.h" #include "../dialogs/gd_mainWindow.h" #include "../dialogs/gd_warnings.h" #include "../elems/ge_keyboard.h" #include "ge_column.h" -#include "ge_channel.h" -#include "ge_sampleChannel.h" -#include "ge_midiChannel.h" +#include "channel.h" +#include "sampleChannel.h" +#include "midiChannel.h" extern Mixer G_Mixer; @@ -107,13 +108,13 @@ int gColumn::handle(int e) } case FL_PASTE: { // handle actual drop (paste) operation vector paths; - gSplit(Fl::event_text(), "\n", &paths); + gu_split(Fl::event_text(), "\n", &paths); bool fails = false; int result = 0; for (unsigned i=0; iguiChannel); fails = true; @@ -165,7 +166,7 @@ void gColumn::resize(int X, int Y, int W, int H) void gColumn::refreshChannels() { for (int i=1; irefresh(); + ((geChannel*) child(i))->refresh(); } @@ -196,14 +197,14 @@ void gColumn::cb_addChannel(Fl_Widget *v, void *p) { ((gColumn*)p)->__cb_addChan /* -------------------------------------------------------------------------- */ -gChannel *gColumn::addChannel(class Channel *ch) +geChannel *gColumn::addChannel(Channel *ch) { int currentY = y() + children() * 24; - gChannel *gch = NULL; + geChannel *gch = NULL; if (ch->type == CHANNEL_SAMPLE) - gch = (gSampleChannel*) new gSampleChannel(x(), currentY, w(), 20, (SampleChannel*) ch); + gch = (geSampleChannel*) new geSampleChannel(x(), currentY, w(), 20, (SampleChannel*) ch); else - gch = (gMidiChannel*) new gMidiChannel(x(), currentY, w(), 20, (MidiChannel*) ch); + gch = (geMidiChannel*) new geMidiChannel(x(), currentY, w(), 20, (MidiChannel*) ch); add(gch); resize(x(), y(), w(), (children() * 24) + 66); // evil space for drag n drop @@ -216,7 +217,7 @@ gChannel *gColumn::addChannel(class Channel *ch) /* -------------------------------------------------------------------------- */ -void gColumn::deleteChannel(gChannel *gch) +void gColumn::deleteChannel(geChannel *gch) { gch->hide(); remove(gch); @@ -228,7 +229,7 @@ void gColumn::deleteChannel(gChannel *gch) * parameter to skip the operation */ for (int i=0; iposition(gch->x(), y()+(i*24)); } size(w(), children() * 24 + 66); // evil space for drag n drop @@ -241,7 +242,7 @@ void gColumn::deleteChannel(gChannel *gch) void gColumn::__cb_addChannel() { - gLog("[gColumn::__cb_addChannel] index = %d\n", index); + gu_log("[gColumn::__cb_addChannel] index = %d\n", index); int type = openTypeMenu(); if (type) glue_addChannel(index, type); @@ -286,7 +287,7 @@ void gColumn::clear(bool full) else { while (children() >= 2) { // skip "add new channel" btn int i = children()-1; - deleteChannel((gChannel*)child(i)); + deleteChannel((geChannel*)child(i)); } } } @@ -297,9 +298,9 @@ void gColumn::clear(bool full) Channel *gColumn::getChannel(int i) { - gChannel *gch = (gChannel*) child(i); + geChannel *gch = (geChannel*) child(i); if (gch->type == CHANNEL_SAMPLE) - return ((gSampleChannel*) child(i))->ch; + return ((geSampleChannel*) child(i))->ch; else - return ((gMidiChannel*) child(i))->ch; + return ((geMidiChannel*) child(i))->ch; } diff --git a/src/gui/elems/ge_column.h b/src/gui/elems/ge_column.h index 93e8a1c..bcaa2ca 100644 --- a/src/gui/elems/ge_column.h +++ b/src/gui/elems/ge_column.h @@ -59,7 +59,7 @@ public: * add a new channel in this column and set the internal pointer * to channel to 'ch'. */ - class gChannel *addChannel(class Channel *ch); + class geChannel *addChannel(class Channel *ch); /* handle */ @@ -73,7 +73,7 @@ public: /* deleteChannel * remove the channel 'gch' from this column. */ - void deleteChannel(gChannel *gch); + void deleteChannel(geChannel *gch); /* refreshChannels * update channels' graphical statues. Called on each GUI cycle. */ diff --git a/src/gui/elems/ge_controller.cpp b/src/gui/elems/ge_controller.cpp index ed9f738..9ff5f25 100644 --- a/src/gui/elems/ge_controller.cpp +++ b/src/gui/elems/ge_controller.cpp @@ -27,7 +27,8 @@ #include "../../core/graphics.h" -#include "../../glue/glue.h" +#include "../../glue/main.h" +#include "../../glue/io.h" #include "ge_mixed.h" #include "ge_controller.h" @@ -87,7 +88,7 @@ void gController::__cb_rewind() void gController::__cb_play() { - glue_startStopSeq(); + glue_startStopSeq(true); } @@ -96,7 +97,7 @@ void gController::__cb_play() void gController::__cb_recAction() { - glue_startStopActionRec(); + glue_startStopActionRec(true); } @@ -105,7 +106,7 @@ void gController::__cb_recAction() void gController::__cb_recInput() { - glue_startStopInputRec(); + glue_startStopInputRec(true); } @@ -114,7 +115,7 @@ void gController::__cb_recInput() void gController::__cb_metronome() { - glue_startStopMetronome(); + glue_startStopMetronome(true); } diff --git a/src/gui/elems/ge_envelopeChannel.cpp b/src/gui/elems/ge_envelopeChannel.cpp deleted file mode 100644 index b6be16e..0000000 --- a/src/gui/elems/ge_envelopeChannel.cpp +++ /dev/null @@ -1,399 +0,0 @@ -/* --------------------------------------------------------------------- - * - * Giada - Your Hardcore Loopmachine - * - * ge_envelopeWidget - * - * Parent class of any envelope controller, from volume to VST parameter - * automations. - * - * --------------------------------------------------------------------- - * - * Copyright (C) 2010-2016 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/channel.h" -#include "../../core/recorder.h" -#include "../../core/mixer.h" -#include "../dialogs/gd_actionEditor.h" -#include "../dialogs/gd_mainWindow.h" -#include "ge_keyboard.h" -#include "ge_envelopeChannel.h" - - -extern Mixer G_Mixer; -extern gdMainWindow *mainWin; - - -gEnvelopeChannel::gEnvelopeChannel(int x, int y, gdActionEditor *pParent, int type, int range, const char *l) - : gActionWidget(x, y, 200, 80, pParent), l(l), type(type), range(range), - selectedPoint(-1), draggedPoint(-1) -{ - size(pParent->totalWidth, h()); -} - - -/* ------------------------------------------------------------------ */ - - -gEnvelopeChannel::~gEnvelopeChannel() { - clearPoints(); -} - - -/* ------------------------------------------------------------------ */ - - -void gEnvelopeChannel::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 gEnvelopeChannel::updateActions() { - for (unsigned i=0; izoom; -} - - -/* ------------------------------------------------------------------ */ - - -void gEnvelopeChannel::draw() { - - baseDraw(); - - /* print label */ - - fl_color(COLOR_BG_1); - fl_font(FL_HELVETICA, 12); - fl_draw(l, x()+4, y(), 80, h(), (Fl_Align) (FL_ALIGN_LEFT)); - - int pxOld = x()-3; - int pyOld = y()+1; - int pxNew = 0; - int pyNew = 0; - - fl_color(COLOR_BG_2); - - for (unsigned i=0; i 0) - fl_line(pxOld+3, pyOld+3, pxNew+3, pyNew+3); - - pxOld = pxNew; - pyOld = pyNew; - } -} - - -/* ------------------------------------------------------------------ */ - - -int gEnvelopeChannel::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 - - 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; - } - else { - - /* top & border fix */ - - if (my > h()-8) my = h()-8; - if (mx > pParent->coverX-x()) mx = pParent->coverX-x(); - - if (range == 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(G_Mixer.totalFrames, 0, 1.0f, pParent->coverX, 1); - recorder::rec(pParent->chan->index, type, G_Mixer.totalFrames, 0, 1.0f); - } - - /* 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); - recorder::sortActions(); - sortPoints(); - } - else { - /// TODO - } - mainWin->keyboard->setChannelWithActions((gSampleChannel*)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); - points.clear(); - } - else { - recorder::deleteAction(pParent->chan->index, points.at(selectedPoint).frame, type, false); - recorder::sortActions(); - points.erase(points.begin() + selectedPoint); - } - mainWin->keyboard->setChannelWithActions((gSampleChannel*)pParent->chan->guiChannel); // update mainWindow - redraw(); - } - } - - ret = 1; - break; - } - - case FL_RELEASE: { - if (draggedPoint != -1) { - - if (points.at(draggedPoint).x == previousXPoint) { - //gLog("nothing to do\n"); - } - else { - int newFrame = points.at(draggedPoint).x * pParent->zoom; - - /* x edge correction */ - - if (newFrame < 0) - newFrame = 0; - else if (newFrame > G_Mixer.totalFrames) - newFrame = G_Mixer.totalFrames; - - /* 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); - - if (range == RANGE_FLOAT) { - float value = (points.at(draggedPoint).y - h() + 8) / (float) (1 - h() + 8); - recorder::rec(pParent->chan->index, type, newFrame, 0, value); - } - 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; -} - - -/* ------------------------------------------------------------------ */ - - -int gEnvelopeChannel::verticalPoint(const point &p) { - for (unsigned i=0; i points.at(i).x) - std::swap(points.at(j), points.at(i)); -} - - -/* ------------------------------------------------------------------ */ - - -int gEnvelopeChannel::getSelectedPoint() { - - /* point is a 7x7 dot */ - - 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; -} - - -/* ------------------------------------------------------------------ */ - - -void gEnvelopeChannel::fill() { - points.clear(); - for (unsigned i=0; itype == type && a->chan == pParent->chan->index) { - if (range == 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 - } - } - -} diff --git a/src/gui/elems/ge_envelopeChannel.h b/src/gui/elems/ge_envelopeChannel.h deleted file mode 100644 index 8afe11b..0000000 --- a/src/gui/elems/ge_envelopeChannel.h +++ /dev/null @@ -1,120 +0,0 @@ -/* --------------------------------------------------------------------- - * - * Giada - Your Hardcore Loopmachine - * - * ge_envelopeWidget - * - * parent class of any envelope controller, from volume to VST parameter - * automations. - * - * --------------------------------------------------------------------- - * - * Copyright (C) 2010-2016 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_ENVELOPECHANNEL_H__ -#define __GE_ENVELOPECHANNEL_H__ - - -#include -#include -#include -#include "../../utils/utils.h" -#include "ge_actionWidget.h" - - -using std::vector; - - -class gEnvelopeChannel : public gActionWidget -{ - 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() */ - - vector points; - - /* selectedPoint - * which point we are selecting? */ - - int selectedPoint; - - /* draggedPoint - * which point we are dragging? */ - - int draggedPoint; - - /* previousXPoint - * x coordinate of point at time t-1. Used to check effective shifts */ - - int previousXPoint; - - void draw(); - - int handle(int e); - - int getSelectedPoint(); - - void sortPoints(); - - /* 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. */ - - int verticalPoint(const point &p); - -public: - gEnvelopeChannel(int x, int y, gdActionEditor *pParent, int type, int range, const char *l); - ~gEnvelopeChannel(); - - /* 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(); - - inline void clearPoints() { points.clear(); } -}; - -#endif diff --git a/src/gui/elems/ge_keyboard.cpp b/src/gui/elems/ge_keyboard.cpp index 5d5fd37..68a8228 100644 --- a/src/gui/elems/ge_keyboard.cpp +++ b/src/gui/elems/ge_keyboard.cpp @@ -33,14 +33,15 @@ #include "../../core/patch_DEPR_.h" #include "../../core/channel.h" #include "../../core/sampleChannel.h" -#include "../../glue/glue.h" +#include "../../glue/main.h" +#include "../../glue/io.h" #include "../../utils/log.h" #include "../dialogs/gd_browser.h" #include "../dialogs/gd_mainWindow.h" #include "../dialogs/gd_editor.h" #include "../dialogs/gd_warnings.h" -#include "ge_channel.h" -#include "ge_sampleChannel.h" +#include "channel.h" +#include "sampleChannel.h" #include "ge_keyboard.h" @@ -101,7 +102,7 @@ void gKeyboard::init() /* -------------------------------------------------------------------------- */ -void gKeyboard::freeChannel(gChannel *gch) +void gKeyboard::freeChannel(geChannel *gch) { gch->reset(); } @@ -110,7 +111,7 @@ void gKeyboard::freeChannel(gChannel *gch) /* -------------------------------------------------------------------------- */ -void gKeyboard::deleteChannel(gChannel *gch) +void gKeyboard::deleteChannel(geChannel *gch) { for (unsigned i=0; ifind(gch); @@ -125,7 +126,7 @@ void gKeyboard::deleteChannel(gChannel *gch) /* -------------------------------------------------------------------------- */ -void gKeyboard::updateChannel(gChannel *gch) +void gKeyboard::updateChannel(geChannel *gch) { gch->update(); } @@ -180,7 +181,7 @@ void gKeyboard::cb_addColumn(Fl_Widget *v, void *p) /* -------------------------------------------------------------------------- */ -gChannel *gKeyboard::addChannel(int colIndex, Channel *ch, bool build) +geChannel *gKeyboard::addChannel(int colIndex, Channel *ch, bool build) { gColumn *col = getColumnByIndex(colIndex); @@ -191,10 +192,10 @@ gChannel *gKeyboard::addChannel(int colIndex, Channel *ch, bool build) __cb_addColumn(); col = columns.back(); col->setIndex(colIndex); - gLog("[gKeyboard::addChannel] created new column with index=%d\n", colIndex); + gu_log("[gKeyboard::addChannel] created new column with index=%d\n", colIndex); } - gLog("[gKeyboard::addChannel] add to column with index = %d\n", col->getIndex()); + gu_log("[gKeyboard::addChannel] add to column with index = %d\n", col->getIndex()); return col->addChannel(ch); } @@ -223,6 +224,8 @@ gColumn *gKeyboard::getColumnByIndex(int index) /* -------------------------------------------------------------------------- */ +/* TODO - the following event handling for play, stop, rewind, start rec and +so on should be moved to the proper widget: gdMainWindow or (better) geController. */ int gKeyboard::handle(int e) { @@ -254,13 +257,13 @@ int gKeyboard::handle(int e) } else if (Fl::event_key() == FL_Enter && !enterPressed) { enterPressed = true; - glue_startStopActionRec(); + glue_startStopActionRec(false); // update gui ret = 1; break; } else if (Fl::event_key() == ' ' && !spacePressed) { spacePressed = true; - G_Mixer.running ? glue_stopSeq() : glue_startSeq(); // TODO - glue_startStopSeq, no core logic here + glue_startStopSeq(false); // update gui ret = 1; break; } @@ -282,7 +285,7 @@ int gKeyboard::handle(int e) for (unsigned i=0; ichildren(); k++) - ret &= ((gChannel*)columns.at(i)->child(k))->keyPress(e); + ret &= ((geChannel*)columns.at(i)->child(k))->keyPress(e); break; } } @@ -306,12 +309,12 @@ void gKeyboard::clear() /* -------------------------------------------------------------------------- */ -void gKeyboard::setChannelWithActions(gSampleChannel *gch) +void gKeyboard::setChannelWithActions(geSampleChannel *gch) { if (gch->ch->hasActions) - gch->addActionButton(); + gch->showActionButton(); else - gch->delActionButton(); + gch->hideActionButton(); } @@ -369,7 +372,7 @@ void gKeyboard::__cb_addColumn(int width) addColumnBtn->position(colxw + gap, y()); redraw(); - gLog("[gKeyboard::__cb_addColumn] new column added (index=%d, w=%d), total count=%d, addColumn(x)=%d\n", + gu_log("[gKeyboard::__cb_addColumn] new column added (index=%d, w=%d), total count=%d, addColumn(x)=%d\n", gc->getIndex(), width, columns.size(), addColumnBtn->x()); /* recompute col indexes */ diff --git a/src/gui/elems/ge_keyboard.h b/src/gui/elems/ge_keyboard.h index 967758c..c5558b9 100644 --- a/src/gui/elems/ge_keyboard.h +++ b/src/gui/elems/ge_keyboard.h @@ -39,7 +39,7 @@ #include #include "../elems/ge_column.h" #include "../../core/const.h" -#include "../../utils/utils.h" +#include "../../utils/fs.h" using std::vector; @@ -87,12 +87,12 @@ public: void init(); /* addChannel - * add a new channel to gChannels. Used by callbacks and during - * patch loading. Requires Channel (and not gChannel). If build is + * add a new channel to geChannels. Used by callbacks and during + * patch loading. Requires Channel (and not geChannel). If build is * set to true, also generate the corresponding column if column (index) does * not exist yet. */ - gChannel *addChannel(int column, class Channel *ch, bool build=false); + class geChannel *addChannel(int column, class Channel *ch, bool build=false); /* addColumn * add a new column to the top of the stack. */ @@ -100,21 +100,21 @@ public: void addColumn(int width=380); /* deleteChannel - * delete a channel from gChannels<> where gChannel->ch == ch and remove + * delete a channel from geChannels<> where geChannel->ch == ch and remove * it from the stack. */ - void deleteChannel(gChannel *gch); + void deleteChannel(geChannel *gch); /* freeChannel - * free a channel from gChannels<> where gChannel->ch == ch. No channels + * free a channel from geChannels<> where geChannel->ch == ch. No channels * are deleted */ - void freeChannel(gChannel *gch); + void freeChannel(geChannel *gch); /* updateChannel * wrapper function to call gch->update(). */ - void updateChannel(gChannel *gch); + void updateChannel(geChannel *gch); /* organizeColumns * reorganize columns layout by removing empty gaps. */ @@ -144,7 +144,7 @@ public: /* setChannelWithActions * add 'R' button if channel has actions, and set recorder to active. */ - void setChannelWithActions(class gSampleChannel *gch); + void setChannelWithActions(class geSampleChannel *gch); /* printChannelMessage * given any output by glue_loadChannel, print the message on screen diff --git a/src/gui/elems/ge_midiChannel.cpp b/src/gui/elems/ge_midiChannel.cpp deleted file mode 100644 index bd139c3..0000000 --- a/src/gui/elems/ge_midiChannel.cpp +++ /dev/null @@ -1,348 +0,0 @@ -/* ----------------------------------------------------------------------------- - * - * Giada - Your Hardcore Loopmachine - * - * ge_midiChannel - * - * ----------------------------------------------------------------------------- - * - * Copyright (C) 2010-2016 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/pluginHost.h" -#include "../../core/mixer.h" -#include "../../core/conf.h" -#include "../../core/patch_DEPR_.h" -#include "../../core/graphics.h" -#include "../../core/channel.h" -#include "../../core/wave.h" -#include "../../core/sampleChannel.h" -#include "../../core/midiChannel.h" -#include "../../glue/channel.h" -#include "../../glue/glue.h" -#include "../../utils/gui_utils.h" -#include "../dialogs/gd_mainWindow.h" -#include "../dialogs/gd_keyGrabber.h" -#include "../dialogs/gd_midiInput.h" -#include "../dialogs/gd_editor.h" -#include "../dialogs/gd_actionEditor.h" -#include "../dialogs/gd_warnings.h" -#include "../dialogs/gd_browser.h" -#include "../dialogs/gd_keyGrabber.h" -#include "../dialogs/gd_midiOutput.h" -#include "ge_keyboard.h" -#include "ge_midiChannel.h" -#include "ge_channel.h" -#include "ge_sampleChannel.h" -#include "../dialogs/gd_pluginList.h" - - -extern Mixer G_Mixer; -extern Conf G_Conf; -extern Patch_DEPR_ G_Patch_DEPR_; -extern gdMainWindow *mainWin; - - -gMidiChannel::gMidiChannel(int X, int Y, int W, int H, class MidiChannel *ch) - : gChannel(X, Y, W, H, CHANNEL_MIDI), ch(ch) -{ - begin(); - -#if defined(WITH_VST) - int delta = 120; // (5 widgets * 20) + (5 paddings * 4) -#else - int delta = 96; // (4 widgets * 20) + (4 paddings * 4) -#endif - - button = new gButton(x(), y(), 20, 20, "", channelStop_xpm, channelPlay_xpm); - mainButton = new gMidiChannelButton(button->x()+button->w()+4, y(), w() - delta, 20, "-- MIDI --"); - mute = new gClick(mainButton->x()+mainButton->w()+4, y(), 20, 20, "", muteOff_xpm, muteOn_xpm); - solo = new gClick(mute->x()+mute->w()+4, y(), 20, 20, "", soloOff_xpm, soloOn_xpm); -#if defined(WITH_VST) - fx = new gFxButton(solo->x()+solo->w()+4, y(), 20, 20, fxOff_xpm, fxOn_xpm); - vol = new gDial(fx->x()+fx->w()+4, y(), 20, 20); -#else - vol = new gDial(solo->x()+solo->w()+4, y(), 20, 20); -#endif - - end(); - - resizable(mainButton); - - update(); - - button->callback(cb_button, (void*)this); - button->when(FL_WHEN_CHANGED); // do callback on keypress && on keyrelease - -#ifdef WITH_VST - fx->callback(cb_openFxWindow, (void*)this); -#endif - - mute->type(FL_TOGGLE_BUTTON); - mute->callback(cb_mute, (void*)this); - - solo->type(FL_TOGGLE_BUTTON); - solo->callback(cb_solo, (void*)this); - - mainButton->callback(cb_openMenu, (void*)this); - - vol->callback(cb_changeVol, (void*)this); - - ch->guiChannel = this; -} - - -/* -------------------------------------------------------------------------- */ - - -void gMidiChannel::cb_button (Fl_Widget *v, void *p) { ((gMidiChannel*)p)->__cb_button(); } -void gMidiChannel::cb_mute (Fl_Widget *v, void *p) { ((gMidiChannel*)p)->__cb_mute(); } -void gMidiChannel::cb_solo (Fl_Widget *v, void *p) { ((gMidiChannel*)p)->__cb_solo(); } -void gMidiChannel::cb_openMenu (Fl_Widget *v, void *p) { ((gMidiChannel*)p)->__cb_openMenu(); } -void gMidiChannel::cb_changeVol (Fl_Widget *v, void *p) { ((gMidiChannel*)p)->__cb_changeVol(); } -#ifdef WITH_VST -void gMidiChannel::cb_openFxWindow(Fl_Widget *v, void *p) { ((gMidiChannel*)p)->__cb_openFxWindow(); } -#endif - - -/* -------------------------------------------------------------------------- */ - - -void gMidiChannel::__cb_mute() -{ - glue_setMute(ch); -} - - -/* -------------------------------------------------------------------------- */ - - -void gMidiChannel::__cb_solo() -{ - solo->value() ? glue_setSoloOn(ch) : glue_setSoloOff(ch); -} - - -/* -------------------------------------------------------------------------- */ - - -void gMidiChannel::__cb_changeVol() -{ - glue_setChanVol(ch, vol->value()); -} - - -/* -------------------------------------------------------------------------- */ - - -#ifdef WITH_VST -void gMidiChannel::__cb_openFxWindow() -{ - gu_openSubWindow(mainWin, new gdPluginList(PluginHost::CHANNEL, ch), WID_FX_LIST); -} -#endif - -/* -------------------------------------------------------------------------- */ - - -void gMidiChannel::__cb_button() -{ - if (button->value()) - glue_keyPress(ch, Fl::event_ctrl(), Fl::event_shift()); -} - - -/* -------------------------------------------------------------------------- */ - - -void gMidiChannel::__cb_openMenu() -{ - Fl_Menu_Item rclick_menu[] = { - {"Edit actions..."}, // 0 - {"Clear actions", 0, 0, 0, FL_SUBMENU}, // 1 - {"All"}, // 2 - {0}, // 3 - {"Setup keyboard input..."}, // 5 - {"Setup MIDI input..."}, // 6 - {"Setup MIDI output..."}, // 7 - {"Clone channel"}, // 8 - {"Delete channel"}, // 9 - {0} - }; - - /* no 'clear actions' if there are no actions */ - - if (!ch->hasActions) - rclick_menu[1].deactivate(); - - Fl_Menu_Button *b = new Fl_Menu_Button(0, 0, 100, 50); - b->box(G_BOX); - b->textsize(GUI_FONT_SIZE_BASE); - b->textcolor(COLOR_TEXT_0); - b->color(COLOR_BG_0); - - const Fl_Menu_Item *m = rclick_menu->popup(Fl::event_x(), Fl::event_y(), 0, 0, b); - if (!m) return; - - if (strcmp(m->label(), "Delete channel") == 0) { - if (!gdConfirmWin("Warning", "Delete channel: are you sure?")) - return; - glue_deleteChannel(ch); - return; - } - - if (strcmp(m->label(), "Clone channel") == 0) { - glue_cloneChannel(ch); - return; - } - - if (strcmp(m->label(), "Setup keyboard input...") == 0) { - gu_openSubWindow(mainWin, new gdKeyGrabber(ch), 0); - //new gdKeyGrabber(ch); - return; - } - - if (strcmp(m->label(), "All") == 0) { - if (!gdConfirmWin("Warning", "Clear all actions: are you sure?")) - return; - recorder::clearChan(ch->index); - gu_refreshActionEditor(); // refresh a.editor window, it could be open - return; - } - - if (strcmp(m->label(), "Edit actions...") == 0) { - gu_openSubWindow(mainWin, new gdActionEditor(ch), WID_ACTION_EDITOR); - return; - } - - if (strcmp(m->label(), "Setup MIDI input...") == 0) { - gu_openSubWindow(mainWin, new gdMidiInputChannel(ch), 0); - return; - } - - if (strcmp(m->label(), "Setup MIDI output...") == 0) { - //gu_openSubWindow(mainWin, new gdMidiGrabberChannel(ch, GrabForOutput), 0); - gu_openSubWindow(mainWin, new gdMidiOutputMidiCh(ch), 0); - return; - } -} - - -/* -------------------------------------------------------------------------- */ - - -void gMidiChannel::refresh() -{ - setColorsByStatus(ch->status, ch->recStatus); - mainButton->redraw(); -} - - -/* -------------------------------------------------------------------------- */ - - -void gMidiChannel::reset() -{ - mainButton->setDefaultMode("-- MIDI --"); - mainButton->redraw(); -} - - -/* -------------------------------------------------------------------------- */ - - -void gMidiChannel::update() -{ - if (ch->midiOut) { - char tmp[32]; - sprintf(tmp, "-- MIDI (channel %d) --", ch->midiOutChan+1); - mainButton->copy_label(tmp); - } - else - mainButton->label("-- MIDI --"); - - vol->value(ch->volume); - mute->value(ch->mute); - solo->value(ch->solo); - - mainButton->setKey(ch->key); - -#ifdef WITH_VST - fx->full = ch->plugins.size() > 0; - fx->redraw(); -#endif -} - - -/* -------------------------------------------------------------------------- */ - - -void gMidiChannel::resize(int X, int Y, int W, int H) -{ - gChannel::resize(X, Y, W, H); - - /* this stuff makes sense only with FX button available. Do nothing - * otherwise */ - -#ifdef WITH_VST - if (w() < BREAK_FX) { - fx->hide(); - - mainButton->size(w() - (BREAK_DELTA - BREAK_UNIT), mainButton->h()); - } - else { - fx->show(); - mainButton->size(w() - BREAK_DELTA, mainButton->h()); - } - mute->resize(mainButton->x()+mainButton->w()+4, y(), 20, 20); - solo->resize(mute->x()+mute->w()+4, y(), 20, 20); - - gChannel::init_sizes(); -#endif -} - - -/* -------------------------------------------------------------------------- */ - - -int gMidiChannel::keyPress(int e) -{ - return handleKey(e, ch->key); -} - - -/* -------------------------------------------------------------------------- */ -/* -------------------------------------------------------------------------- */ -/* -------------------------------------------------------------------------- */ - - -gMidiChannelButton::gMidiChannelButton(int x, int y, int w, int h, const char *l) - : gChannelButton(x, y, w, h, l) {} - - -/* -------------------------------------------------------------------------- */ - - -int gMidiChannelButton::handle(int e) -{ - // MIDI drag-n-drop does nothing so far. - return gClick::handle(e); -} diff --git a/src/gui/elems/ge_midiChannel.h b/src/gui/elems/ge_midiChannel.h deleted file mode 100644 index ac1141a..0000000 --- a/src/gui/elems/ge_midiChannel.h +++ /dev/null @@ -1,90 +0,0 @@ -/* ----------------------------------------------------------------------------- - * - * Giada - Your Hardcore Loopmachine - * - * ge_midiChannel - * - * ----------------------------------------------------------------------------- - * - * Copyright (C) 2010-2016 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_MIDI_CHANNEL_H -#define GE_MIDI_CHANNEL_H - - -#include -#include -#include -#include "ge_channel.h" -#include "ge_channelButton.h" -#include "ge_mixed.h" - - -class gMidiChannel : public gChannel -{ -private: - - static void cb_button (Fl_Widget *v, void *p); - static void cb_mute (Fl_Widget *v, void *p); - static void cb_solo (Fl_Widget *v, void *p); - static void cb_openMenu (Fl_Widget *v, void *p); - static void cb_changeVol (Fl_Widget *v, void *p); -#ifdef WITH_VST - static void cb_openFxWindow (Fl_Widget *v, void *p); -#endif - - inline void __cb_mute (); - inline void __cb_solo (); - inline void __cb_changeVol (); - inline void __cb_button (); - inline void __cb_openMenu (); - inline void __cb_readActions (); -#ifdef WITH_VST - inline void __cb_openFxWindow(); -#endif - -public: - - gMidiChannel(int x, int y, int w, int h, class MidiChannel *ch); - - void reset (); - void update (); - void refresh (); - int keyPress(int event); - void resize (int x, int y, int w, int h); - - class MidiChannel *ch; -}; - - -/* -------------------------------------------------------------------------- */ - - -class gMidiChannelButton : public gChannelButton -{ -public: - gMidiChannelButton(int x, int y, int w, int h, const char *l=0); - int handle(int e); -}; - - -#endif diff --git a/src/gui/elems/ge_midiIoTools.cpp b/src/gui/elems/ge_midiIoTools.cpp index 9057f3f..8c20101 100644 --- a/src/gui/elems/ge_midiIoTools.cpp +++ b/src/gui/elems/ge_midiIoTools.cpp @@ -27,11 +27,15 @@ * -------------------------------------------------------------------------- */ -#include "ge_midiIoTools.h" #include "ge_mixed.h" +#include "ge_midiIoTools.h" + + +extern KernelMidi G_KernelMidi; -gLearner::gLearner(int X, int Y, int W, const char *l, kernelMidi::cb_midiLearn *cb, uint32_t *param) +gLearner::gLearner(int X, int Y, int W, const char *l, KernelMidi::cb_midiLearn *cb, + uint32_t *param) : Fl_Group(X, Y, W, 20), callback(cb), param (param) @@ -98,8 +102,8 @@ void gLearner::__cb_button() { cbData *data = (cbData*) malloc(sizeof(cbData)); data->window = (gdMidiInput*) parent(); // parent = gdMidiGrabberChannel data->learner = this; - kernelMidi::startMidiLearn(callback, (void*)data); + G_KernelMidi.startMidiLearn(callback, (void*)data); } else - kernelMidi::stopMidiLearn(); + G_KernelMidi.stopMidiLearn(); } diff --git a/src/gui/elems/ge_midiIoTools.h b/src/gui/elems/ge_midiIoTools.h index 2a795e1..0712937 100644 --- a/src/gui/elems/ge_midiIoTools.h +++ b/src/gui/elems/ge_midiIoTools.h @@ -31,12 +31,14 @@ #define GE_LEARNER_H -#include #include #include "../../core/kernelMidi.h" #include "../dialogs/gd_midiInput.h" +extern KernelMidi G_KernelMidi; + + class gLearner : public Fl_Group { private: @@ -46,7 +48,7 @@ private: * uint32_t msg - MIDI message * void *data - extra data */ - kernelMidi::cb_midiLearn *callback; + KernelMidi::cb_midiLearn *callback; class gBox *text; class gClick *value; @@ -64,7 +66,7 @@ public: uint32_t *param; - gLearner(int x, int y, int w, const char *l, kernelMidi::cb_midiLearn *cb, uint32_t *param); + gLearner(int x, int y, int w, const char *l, KernelMidi::cb_midiLearn *cb, uint32_t *param); void updateValue(); }; diff --git a/src/gui/elems/ge_mixed.cpp b/src/gui/elems/ge_mixed.cpp index 60abf9e..0969a8a 100644 --- a/src/gui/elems/ge_mixed.cpp +++ b/src/gui/elems/ge_mixed.cpp @@ -34,7 +34,7 @@ #include "../../core/recorder.h" #include "../../core/channel.h" #include "../../core/sampleChannel.h" -#include "../../utils/gui_utils.h" +#include "../../utils/gui.h" #include "../dialogs/gd_mainWindow.h" #include "ge_mixed.h" @@ -452,7 +452,7 @@ gResizerBar::gResizerBar(int X,int Y,int W,int H, bool vertical) /* gResizerBar::~gResizerBar() { - gLog("------ resizerbar %p destroyed\n", (void*)this); + gu_log("------ resizerbar %p destroyed\n", (void*)this); } */ diff --git a/src/gui/elems/ge_mixed.h b/src/gui/elems/ge_mixed.h index b41be44..d454497 100644 --- a/src/gui/elems/ge_mixed.h +++ b/src/gui/elems/ge_mixed.h @@ -120,6 +120,7 @@ public: /* gButton * exactly as gClick but with a unique id inside of it. Used for the buttons in * channels and for FXs. */ + /* TODO - is this really useful? */ class gButton : public gClick { diff --git a/src/gui/elems/ge_modeBox.cpp b/src/gui/elems/ge_modeBox.cpp index 27afbea..4a6915b 100644 --- a/src/gui/elems/ge_modeBox.cpp +++ b/src/gui/elems/ge_modeBox.cpp @@ -27,7 +27,7 @@ * -------------------------------------------------------------------------- */ -#include "../../utils/gui_utils.h" +#include "../../utils/gui.h" #include "../../core/graphics.h" #include "../../core/sampleChannel.h" #include "../../core/const.h" diff --git a/src/gui/elems/ge_muteChannel.cpp b/src/gui/elems/ge_muteChannel.cpp deleted file mode 100644 index 7b996b8..0000000 --- a/src/gui/elems/ge_muteChannel.cpp +++ /dev/null @@ -1,411 +0,0 @@ -/* --------------------------------------------------------------------- - * - * Giada - Your Hardcore Loopmachine - * - * ge_muteChannel - * a widget that represents mute actions inside the action editor. - * - * --------------------------------------------------------------------- - * - * Copyright (C) 2010-2016 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/recorder.h" -#include "../../core/mixer.h" -#include "../../core/channel.h" -#include "../../glue/glue.h" -#include "../../utils/log.h" -#include "../dialogs/gd_actionEditor.h" -#include "../dialogs/gd_mainWindow.h" -#include "ge_keyboard.h" -#include "ge_actionWidget.h" -#include "ge_muteChannel.h" - - -extern gdMainWindow *mainWin; -extern Mixer G_Mixer; - - -gMuteChannel::gMuteChannel(int x, int y, gdActionEditor *pParent) - : gActionWidget(x, y, 200, 80, pParent), draggedPoint(-1), selectedPoint(-1) -{ - size(pParent->totalWidth, h()); - extractPoints(); -} - - -/* ------------------------------------------------------------------ */ - - -void gMuteChannel::draw() { - - baseDraw(); - - /* print label */ - - fl_color(COLOR_BG_1); - 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(COLOR_BG_1); - 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(COLOR_BG_2); - - 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 gMuteChannel::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 & (ACTION_MUTEON | 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); - //gLog("[gMuteChannel::extractPoints] point found, type=%d, frame=%d\n", p.type, p.frame); - } - } - } - } -} - - -/* ------------------------------------------------------------------ */ - - -void gMuteChannel::updateActions() { - for (unsigned i=0; izoom; -} - - -/* ------------------------------------------------------------------ */ - - -int gMuteChannel::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 >= G_Mixer.totalFrames) { - frame_b = G_Mixer.totalFrames; - frame_a = frame_b-2048; - } - - if (nextPoint % 2 != 0) { - recorder::rec(pParent->chan->index, ACTION_MUTEOFF, frame_a); - recorder::rec(pParent->chan->index, ACTION_MUTEON, frame_b); - } - else { - recorder::rec(pParent->chan->index, ACTION_MUTEON, frame_a); - recorder::rec(pParent->chan->index, ACTION_MUTEOFF, frame_b); - } - recorder::sortActions(); - - mainWin->keyboard->setChannelWithActions((gSampleChannel*)pParent->chan->guiChannel); // update mainWindow - extractPoints(); - redraw(); - } - } - else { - - /* delete points pair */ - - if (selectedPoint != -1) { - - unsigned a; - unsigned b; - - if (points.at(selectedPoint).type == ACTION_MUTEOFF) { - a = selectedPoint-1; - b = selectedPoint; - } - else { - a = selectedPoint; - b = selectedPoint+1; - } - - //gLog("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); // false = don't check vals - recorder::deleteAction(pParent->chan->index, points.at(b).frame, points.at(b).type, false); // false = don't check vals - recorder::sortActions(); - - mainWin->keyboard->setChannelWithActions((gSampleChannel*)pParent->chan->guiChannel); // update mainWindow - extractPoints(); - redraw(); - } - } - ret = 1; - break; - } - - case FL_RELEASE: { - - if (draggedPoint != -1) { - - if (points.at(draggedPoint).x == previousXPoint) { - //gLog("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); // don't check values - - recorder::rec( - pParent->chan->index, - points.at(draggedPoint).type, - newFrame); - - 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 gMuteChannel::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/ge_muteChannel.h b/src/gui/elems/ge_muteChannel.h deleted file mode 100644 index 2e74453..0000000 --- a/src/gui/elems/ge_muteChannel.h +++ /dev/null @@ -1,108 +0,0 @@ -/* --------------------------------------------------------------------- - * - * Giada - Your Hardcore Loopmachine - * - * ge_muteChannel - * a widget representing mute actions inside the action editor. - * - * --------------------------------------------------------------------- - * - * Copyright (C) 2010-2016 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_MUTECHANNEL_H -#define GE_MUTECHANNEL_H - - -#include -#include -#include -#include -#include "../../utils/utils.h" -#include "ge_actionWidget.h" - - -using std::vector; - - -class gMuteChannel : public gActionWidget -{ -private: - - /* point - * a single dot in the graph. */ - - struct point { - int frame; - char type; - int x; - }; - - /* points - * array of on/off points, in frames */ - - 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: - - gMuteChannel(int x, int y, class 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/ge_pianoRoll.cpp b/src/gui/elems/ge_pianoRoll.cpp deleted file mode 100644 index bd3dd5b..0000000 --- a/src/gui/elems/ge_pianoRoll.cpp +++ /dev/null @@ -1,730 +0,0 @@ -/* --------------------------------------------------------------------- - * - * Giada - Your Hardcore Loopmachine - * - * ge_pianoRoll - * - * --------------------------------------------------------------------- - * - * Copyright (C) 2010-2016 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/channel.h" -#include "../../core/midiChannel.h" -#include "../../core/const.h" -#include "../../core/kernelMidi.h" -#include "../../core/conf.h" -#include "../../utils/log.h" -#include "../dialogs/gd_mainWindow.h" -#include "../dialogs/gd_actionEditor.h" -#include "ge_pianoRoll.h" - - -extern gdMainWindow *mainWin; -extern Mixer G_Mixer; -extern Conf G_Conf; - - -gPianoRollContainer::gPianoRollContainer(int x, int y, class gdActionEditor *pParent) - : Fl_Scroll(x, y, 200, 422), pParent(pParent) -{ - size(pParent->totalWidth, G_Conf.pianoRollH); - pianoRoll = new gPianoRoll(x, y, pParent->totalWidth, pParent); -} - - -/* ------------------------------------------------------------------ */ - - -gPianoRollContainer::~gPianoRollContainer() { - clear(); - G_Conf.pianoRollH = h(); - G_Conf.pianoRollY = pianoRoll->y(); -} - - -/* ------------------------------------------------------------------ */ - - -void gPianoRollContainer::updateActions() { - pianoRoll->updateActions(); -} - - -/* ------------------------------------------------------------------ */ - - -void gPianoRollContainer::draw() { - - pianoRoll->size(this->w(), pianoRoll->h()); /// <--- not optimal - - /* clear background */ - - fl_rectf(x(), y(), w(), h(), COLOR_BG_MAIN); - - /* clip pianoRoll to pianoRollContainer size */ - - fl_push_clip(x(), y(), w(), h()); - draw_child(*pianoRoll); - fl_pop_clip(); - - fl_color(COLOR_BD_0); - fl_line_style(0); - fl_rect(x(), y(), pParent->totalWidth, h()); -} - - -/* ------------------------------------------------------------------ */ -/* ------------------------------------------------------------------ */ -/* ------------------------------------------------------------------ */ - - -gPianoRoll::gPianoRoll(int X, int Y, int W, class gdActionEditor *pParent) - : gActionWidget(X, Y, W, 40, pParent) -{ - resizable(NULL); // don't resize children (i.e. pianoItem) - size(W, (MAX_NOTES+1) * CELL_H); // 128 MIDI channels * 15 px height - - if (G_Conf.pianoRollY == -1) - position(x(), y()-(h()/2)); // center - else - position(x(), G_Conf.pianoRollY); - - drawSurface1(); - drawSurface2(); - - /* add actions when the window is opened. Position is zoom-based. MIDI - * actions come always in pair: start + end. */ - - recorder::sortActions(); - - recorder::action *a2 = NULL; - recorder::action *prev = NULL; - - for (unsigned i=0; i than the grey area */ - /** FIXME - can we move this to the outer cycle? */ - - if (recorder::frames.at(i) > G_Mixer.totalFrames) - continue; - - recorder::action *a1 = recorder::global.at(i).at(j); - - if (a1->chan != pParent->chan->index) - continue; - - if (a1->type == ACTION_MIDI) { - - /* if this action is == to previous one: skip it, we have already - * checked it */ - - if (a1 == prev) { - //gLog("[gPianoRoll] ACTION_MIDI found, but skipping - was previous\n"); - continue; - } - - /* extract MIDI infos from a1: if is note off skip it, we are looking - * for note on only */ - - int a1_type = kernelMidi::getB1(a1->iValue); - int a1_note = kernelMidi::getB2(a1->iValue); - int a1_velo = kernelMidi::getB3(a1->iValue); - - if (a1_type == 0x80) { - //gLog("[gPianoRoll] ACTION_MIDI found, but skipping - was note off\n"); - continue; - } - - /* search for the next action. Must have: same channel, ACTION_MIDI, greater - * than a1->frame and with MIDI properties of note_off (0x80), same note - * of a1, same velocity of a1 */ - - recorder::getNextAction( - a1->chan, - ACTION_MIDI, - a1->frame, - &a2, - kernelMidi::getIValue(0x80, a1_note, a1_velo)); - - /* next action note off found: add a new gPianoItem to piano roll */ - - if (a2) { - //gLog("[gPianoRoll] ACTION_MIDI pair found, frame_a=%d frame_b=%d, note_a=%d, note_b=%d, type_a=%d, type_b=%d\n", - // a1->frame, a2->frame, kernelMidi::getNoteValue(a1->iValue), kernelMidi::getNoteValue(a2->iValue), - // kernelMidi::getNoteOnOff(a1->iValue), kernelMidi::getNoteOnOff(a2->iValue)); - new gPianoItem(0, 0, x(), y()+3, a1, a2, pParent); - prev = a2; - a2 = NULL; - } - else - gLog("[gPianoRoll] recorder didn't find action!\n"); - - } - } - } - - end(); -} - - -/* ------------------------------------------------------------------ */ - - -void gPianoRoll::drawSurface1() { - - surface1 = fl_create_offscreen(40, 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 */ - - fl_rectf(0, 0, 40, h(), COLOR_BG_MAIN); - - fl_line_style(FL_DASH, 0, NULL); - fl_font(FL_HELVETICA, 11); - - int octave = 9; - - for (int i=1; i<=MAX_NOTES+1; i++) { - - /* print key note label. C C# D D# E F F# G G# A A# B */ - - char note[6]; - int step = i % 12; - - switch (step) { - case 1: - fl_rectf(0, i*CELL_H, 40, CELL_H, 30, 30, 30); - sprintf(note, "%dG", octave); - break; - case 2: - sprintf(note, "%dF#", octave); - break; - case 3: - sprintf(note, "%dF", octave); - break; - case 4: - fl_rectf(0, i*CELL_H, 40, CELL_H, 30, 30, 30); - sprintf(note, "%dE", octave); - break; - case 5: - sprintf(note, "%dD#", octave); - break; - case 6: - fl_rectf(0, i*CELL_H, 40, CELL_H, 30, 30, 30); - sprintf(note, "%dD", octave); - break; - case 7: - sprintf(note, "%dC#", octave); - break; - case 8: - sprintf(note, "%dC", octave); - break; - case 9: - fl_rectf(0, i*CELL_H, 40, CELL_H, 30, 30, 30); - sprintf(note, "%dB", octave); - break; - case 10: - sprintf(note, "%dA#", octave); - break; - case 11: - fl_rectf(0, i*CELL_H, 40, CELL_H, 30, 30, 30); - sprintf(note, "%dA", octave); - break; - case 0: - sprintf(note, "%dG#", octave); - octave--; - break; - } - - fl_color(fl_rgb_color(54, 54, 54)); - fl_draw(note, 4, ((i-1)*CELL_H)+1, 30, CELL_H, (Fl_Align) (FL_ALIGN_LEFT | FL_ALIGN_CENTER)); - - /* print horizontal line */ - - if (i < 128) - fl_line(0, i*CELL_H, 40, +i*CELL_H); - } - - fl_line_style(0); - fl_end_offscreen(); -} - - -/* ------------------------------------------------------------------ */ - - -void gPianoRoll::drawSurface2() { - surface2 = fl_create_offscreen(40, h()); - fl_begin_offscreen(surface2); - fl_rectf(0, 0, 40, h(), COLOR_BG_MAIN); - fl_color(fl_rgb_color(54, 54, 54)); - fl_line_style(FL_DASH, 0, NULL); - for (int i=1; i<=MAX_NOTES+1; i++) { - int step = i % 12; - switch (step) { - case 1: - case 4: - case 6: - case 9: - case 11: - fl_rectf(0, i*CELL_H, 40, CELL_H, 30, 30, 30); - break; - } - if (i < 128) { - fl_color(fl_rgb_color(54, 54, 54)); - fl_line(0, i*CELL_H, 40, +i*CELL_H); - } - } - fl_line_style(0); - fl_end_offscreen(); -} - - -/* ------------------------------------------------------------------ */ - - -void gPianoRoll::draw() { - - fl_copy_offscreen(x(), y(), 40, h(), surface1, 0, 0); - -#if defined(__APPLE__) - for (int i=36; itotalWidth; i+=36) /// TODO: i < pParent->coverX is faster - fl_copy_offscreen(x()+i, y(), 40, h(), surface2, 1, 0); -#else - for (int i=40; itotalWidth; i+=40) /// TODO: i < pParent->coverX is faster - fl_copy_offscreen(x()+i, y(), 40, h(), surface2, 0, 0); -#endif - - baseDraw(false); - draw_children(); -} - - -/* ------------------------------------------------------------------ */ - - -int gPianoRoll::handle(int e) { - - int ret = Fl_Group::handle(e); - - switch (e) { - case FL_PUSH: { - - /* avoid click on grey area */ - - if (Fl::event_x() >= pParent->coverX) { - ret = 1; - break; - } - - - push_y = Fl::event_y() - y(); - - if (Fl::event_button1()) { - - /* 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()-3) % 15; - if (edge != 0) ay -= edge; - - /* if no overlap, add new piano item. Also check that it doesn't - * overflow on the grey area, by shifting it to the left if - * necessary. */ - - if (!onItem(ax, ay-y()-3)) { - int greyover = ax+20 - pParent->coverX-x(); - if (greyover > 0) - ax -= greyover; - add(new gPianoItem(ax, ay, ax-x(), ay-y()-3, NULL, NULL, pParent)); - redraw(); - } - } - ret = 1; - break; - } - case FL_DRAG: { - - if (Fl::event_button3()) { - - gPianoRollContainer *prc = (gPianoRollContainer*) parent(); - position(x(), Fl::event_y() - push_y); - - if (y() > prc->y()) - position(x(), prc->y()); - else - if (y() < prc->y()+prc->h()-h()) - position(x(), prc->y()+prc->h()-h()); - - prc->redraw(); - } - ret = 1; - break; - } - case FL_MOUSEWHEEL: { // nothing to do, just avoid small internal scroll - ret = 1; - break; - } - } - return ret; -} - - -/* ------------------------------------------------------------------ */ - - -void gPianoRoll::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 */ - - gPianoItem *i; - for (int k=0; kgetFrame_a(), i->getFrame_b(), i->x()); - - int newX = x() + (i->getFrame_a() / pParent->zoom); - int newW = ((i->getFrame_b() - i->getFrame_a()) / pParent->zoom); - if (newW < 8) - newW = 8; - i->resize(newX, i->y(), newW, i->h()); - i->redraw(); - - //gLog("update point %p, frame_a=%d frame_b=%d, x()=%d\n", (void*) i, i->getFrame_a(), i->getFrame_b(), i->x()); - } -} - - -/* ------------------------------------------------------------------ */ - - -bool gPianoRoll::onItem(int rel_x, int rel_y) { - - if (!pParent->chan->hasActions) - return false; - - int note = MAX_NOTES - (rel_y / CELL_H); - - int n = children(); - for (int i=0; igetNote() != note) - continue; - - /* 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. We - * also add 1 px to the edges in order to gain some space: - * [ ][ ] ---> no - * [ ] [ ] ---> yes! */ - - int start = p->x() > rel_x ? p->x() : rel_x-1; - int end = p->x()+p->w() < rel_x + 20 ? p->x()+p->w() : rel_x + 21; - if (start < end) - return true; - } - return false; -} - -/* ------------------------------------------------------------------ */ -/* ------------------------------------------------------------------ */ -/* ------------------------------------------------------------------ */ - - -gPianoItem::gPianoItem(int X, int Y, int rel_x, int rel_y, recorder::action *_a, recorder::action *_b, gdActionEditor *pParent) - : Fl_Box (X, Y, 20, gPianoRoll::CELL_H-5), - a (_a), - b (_b), - pParent (pParent), - selected(false), - event_a (0x00), - event_b (0x00), - changed (false) -{ - - /* a is a pointer: action exists, needs to be displayed */ - - if (a) { - note = kernelMidi::getB2(a->iValue); - frame_a = a->frame; - frame_b = b->frame; - event_a = a->iValue; - event_b = b->iValue; - int newX = rel_x + (frame_a / pParent->zoom); - int newY = rel_y + getY(note); - int newW = (frame_b - frame_a) / pParent->zoom; - resize(newX, newY, newW, h()); - } - - /* a is null: action needs to be recorded from scratch */ - - else { - note = getNote(rel_y); - frame_a = rel_x * pParent->zoom; - frame_b = (rel_x + 20) * pParent->zoom; - record(); - size((frame_b - frame_a) / pParent->zoom, h()); - } -} - - -/* ------------------------------------------------------------------ */ - - -bool gPianoItem::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. */ - - gPianoRoll *pPiano = (gPianoRoll*) parent(); - - for (int i=0; ichildren(); i++) { - - gPianoItem *pItem = (gPianoItem*) pPiano->child(i); - - /* don't check against itself and with different y positions */ - - if (pItem == this || pItem->y() != y()) - continue; - - int start = pItem->x() >= x() ? pItem->x() : x(); - int end = pItem->x()+pItem->w() < x()+w() ? pItem->x()+pItem->w() : x()+w(); - if (start < end) - return true; - } - - return false; -} - - -/* ------------------------------------------------------------------ */ - - -void gPianoItem::draw() { - int _w = w() > 4 ? w() : 4; - //gLog("[gPianoItem] draw me (%p) at x=%d\n", (void*)this, x()); - fl_rectf(x(), y(), _w, h(), (Fl_Color) selected ? COLOR_BD_1 : COLOR_BG_2); -} - - -/* ------------------------------------------------------------------ */ - - -void gPianoItem::record() { - - /* avoid frame overflow */ - - int overflow = frame_b - G_Mixer.totalFrames; - if (overflow > 0) { - frame_b -= overflow; - frame_a -= overflow; - } - - /* note off */ - /** FIXME - use constants */ - event_a |= (0x90 << 24); // note on - event_a |= (note << 16); // note value - event_a |= (0x3F << 8); // velocity - event_a |= (0x00); - - event_b |= (0x80 << 24); // note off - event_b |= (note << 16); // note value - event_b |= (0x3F << 8); // velocity - event_b |= (0x00); - - recorder::rec(pParent->chan->index, ACTION_MIDI, frame_a, event_a); - recorder::rec(pParent->chan->index, ACTION_MIDI, frame_b, event_b); -} - - -/* ------------------------------------------------------------------ */ - - -void gPianoItem::remove() { - recorder::deleteAction(pParent->chan->index, frame_a, ACTION_MIDI, true, event_a, 0.0); - recorder::deleteAction(pParent->chan->index, frame_b, ACTION_MIDI, true, event_b, 0.0); - - /* send a note-off in case we are deleting it in a middle of a key_on - * key_off sequence. */ - - ((MidiChannel*) pParent->chan)->sendMidi(event_b); -} - - -/* ------------------------------------------------------------------ */ - - -int gPianoItem::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()+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); - - 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); - remove(); - hide(); // for Windows - Fl::delete_widget(this); - ((gPianoRoll*)parent())->redraw(); - } - ret = 1; - break; - } - - case FL_DRAG: { - - changed = true; - - gPianoRoll *pr = (gPianoRoll*) 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()-8) { - nx = x()+w()-8; - nw = 8; - } - resize(nx, ny, nw, h()); - } - else - if (onRightEdge) { - nw = Fl::event_x()-x(); - if (Fl::event_x() < x()+8) - nw = 8; - 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(); - ((gPianoRoll*)parent())->redraw(); - ret = 1; - break; - } - - case FL_RELEASE: { - - /* delete & record the action, only if it doesn't overlap with - * another one */ - - if (overlap()) { - resize(old_x, y(), old_w, h()); - redraw(); - } - else - if (changed) { - remove(); - note = getNote(getRelY()); - frame_a = getRelX() * pParent->zoom; - frame_b = (getRelX()+w()) * pParent->zoom; - record(); - changed = false; - } - - ((gPianoRoll*)parent())->redraw(); - - ret = 1; - break; - } - } - return ret; -} diff --git a/src/gui/elems/ge_pianoRoll.h b/src/gui/elems/ge_pianoRoll.h deleted file mode 100644 index 509fd32..0000000 --- a/src/gui/elems/ge_pianoRoll.h +++ /dev/null @@ -1,183 +0,0 @@ -/* --------------------------------------------------------------------- - * - * Giada - Your Hardcore Loopmachine - * - * ge_pianoRoll - * - * --------------------------------------------------------------------- - * - * Copyright (C) 2010-2016 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_PIANOROLL_H -#define GE_PIANOROLL_H - -#include -#include -#include -#include "../../core/recorder.h" -#include "ge_actionWidget.h" - - -class gPianoRollContainer : public Fl_Scroll { - -private: - class gdActionEditor *pParent; - class gPianoRoll *pianoRoll; - -public: - gPianoRollContainer(int x, int y, class gdActionEditor *parent); - ~gPianoRollContainer(); - void draw(); - void updateActions(); -}; - - -/* ------------------------------------------------------------------ */ - - -class gPianoRoll : public gActionWidget { - -private: - - /* onItem - * is curson on a gPianoItem? */ - - bool onItem(int rel_x, int rel_y); - - /* drawSurface* - * generate 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. */ - - /* drawSurface1 - * draw first tile of note values. */ - - void drawSurface1(); - - /* drawSurface2 - * draw the rest of the piano roll. */ - - void drawSurface2(); - - int push_y; - Fl_Offscreen surface1; // notes, no repeat - Fl_Offscreen surface2; // lines, x-repeat - - -public: - gPianoRoll(int x, int y, int w, class gdActionEditor *pParent); - - void draw(); - int handle(int e); - void updateActions(); - - enum { MAX_NOTES = 127, CELL_H = 15 }; -}; - - -/* ------------------------------------------------------------------ */ - - -class gPianoItem : public Fl_Box { - -private: - - /* getRelX/Y - * return x/y point of this item, relative to piano roll (and not to - * entire screen) */ - - inline int getRelY() { return y() - parent()->y() - 3; }; - inline int getRelX() { return x() - parent()->x(); }; - - /* getNote - * from a relative_y return the real MIDI note, range 0-127. 15 is - * the hardcoded value for note height in pixels */ - - inline int getNote(int rel_y) { - return gPianoRoll::MAX_NOTES - (rel_y / gPianoRoll::CELL_H); - }; - - /* getY - * from a note, return the y position on piano roll */ - - inline int getY(int note) { - return (gPianoRoll::MAX_NOTES * gPianoRoll::CELL_H) - (note * gPianoRoll::CELL_H); - }; - - /* overlap - * check if this item don't overlap with another one. */ - - bool overlap(); - - recorder::action *a; - recorder::action *b; - class gdActionEditor *pParent; - - bool selected; - int push_x; - - /* MIDI note, start frame, end frame - Used only if it's a newly added - * action */ /** FIXME - is it true? */ - - int note; - int frame_a; - int frame_b; - - /* event - bitmasked MIDI events, generated by record() or by ctor if - * not newly added action */ - - int event_a; - int event_b; - - /* 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; - -public: - - /* pianoItem ctor - * if action *a == NULL, record a new action */ - - gPianoItem(int x, int y, int rel_x, int rel_y, recorder::action *a, recorder::action *b, class gdActionEditor *pParent); - - void draw(); - int handle(int e); - void record(); - void remove(); - - inline int getFrame_a() { return frame_a; } - inline int getFrame_b() { return frame_b; } - inline int getNote() { return note; } - -}; - -#endif diff --git a/src/gui/elems/ge_sampleChannel.cpp b/src/gui/elems/ge_sampleChannel.cpp deleted file mode 100644 index 3838a9e..0000000 --- a/src/gui/elems/ge_sampleChannel.cpp +++ /dev/null @@ -1,617 +0,0 @@ -/* ----------------------------------------------------------------------------- - * - * Giada - Your Hardcore Loopmachine - * - * ge_sampleChannel - * - * ----------------------------------------------------------------------------- - * - * Copyright (C) 2010-2016 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/pluginHost.h" -#include "../../core/mixer.h" -#include "../../core/conf.h" -#include "../../core/patch_DEPR_.h" -#include "../../core/graphics.h" -#include "../../core/channel.h" -#include "../../core/wave.h" -#include "../../core/sampleChannel.h" -#include "../../core/midiChannel.h" -#include "../../glue/glue.h" -#include "../../glue/channel.h" -#include "../../glue/storage.h" -#include "../../utils/gui_utils.h" -#include "../dialogs/gd_mainWindow.h" -#include "../dialogs/gd_keyGrabber.h" -#include "../dialogs/gd_midiInput.h" -#include "../dialogs/gd_editor.h" -#include "../dialogs/gd_actionEditor.h" -#include "../dialogs/gd_warnings.h" -#include "../dialogs/gd_browser.h" -#include "../dialogs/gd_midiOutput.h" -#include "../dialogs/gd_pluginList.h" -#include "../dialogs/gd_pluginChooser.h" -#include "ge_keyboard.h" -#include "ge_sampleChannel.h" -#include "ge_status.h" -#include "ge_modeBox.h" - - -extern Mixer G_Mixer; -extern Conf G_Conf; -extern Patch_DEPR_ G_Patch_DEPR_; -extern gdMainWindow *mainWin; - - -gSampleChannel::gSampleChannel(int X, int Y, int W, int H, class SampleChannel *ch) - : gChannel(X, Y, W, H, CHANNEL_SAMPLE), ch(ch) -{ - begin(); - -#if defined(WITH_VST) - int delta = 168; // (7 widgets * 20) + (7 paddings * 4) -#else - int delta = 144; // (6 widgets * 20) + (6 paddings * 4) -#endif - - button = new gButton(x(), y(), 20, 20, "", channelStop_xpm, channelPlay_xpm); - status = new gStatus(button->x()+button->w()+4, y(), 20, 20, ch); - mainButton = new gSampleChannelButton(status->x()+status->w()+4, y(), w() - delta, 20, "-- no sample --"); - modeBox = new gModeBox(mainButton->x()+mainButton->w()+4, y(), 20, 20, ch); - mute = new gClick(modeBox->x()+modeBox->w()+4, y(), 20, 20, "", muteOff_xpm, muteOn_xpm); - solo = new gClick(mute->x()+mute->w()+4, y(), 20, 20, "", soloOff_xpm, soloOn_xpm); - readActions = NULL; // no 'R' button - -#if defined(WITH_VST) - fx = new gFxButton(solo->x()+solo->w()+4, y(), 20, 20, fxOff_xpm, fxOn_xpm); - vol = new gDial(fx->x()+fx->w()+4, y(), 20, 20); -#else - vol = new gDial(solo->x()+solo->w()+4, y(), 20, 20); -#endif - - end(); - - resizable(mainButton); - - update(); - - button->callback(cb_button, (void*)this); - button->when(FL_WHEN_CHANGED); // do callback on keypress && on keyrelease - -#ifdef WITH_VST - fx->callback(cb_openFxWindow, (void*)this); -#endif - - mute->type(FL_TOGGLE_BUTTON); - mute->callback(cb_mute, (void*)this); - - solo->type(FL_TOGGLE_BUTTON); - solo->callback(cb_solo, (void*)this); - - mainButton->callback(cb_openMenu, (void*)this); - - vol->callback(cb_changeVol, (void*)this); - - ch->guiChannel = this; -} - - -/* -------------------------------------------------------------------------- */ - - -void gSampleChannel::cb_button (Fl_Widget *v, void *p) { ((gSampleChannel*)p)->__cb_button(); } -void gSampleChannel::cb_mute (Fl_Widget *v, void *p) { ((gSampleChannel*)p)->__cb_mute(); } -void gSampleChannel::cb_solo (Fl_Widget *v, void *p) { ((gSampleChannel*)p)->__cb_solo(); } -void gSampleChannel::cb_openMenu (Fl_Widget *v, void *p) { ((gSampleChannel*)p)->__cb_openMenu(); } -void gSampleChannel::cb_changeVol (Fl_Widget *v, void *p) { ((gSampleChannel*)p)->__cb_changeVol(); } -void gSampleChannel::cb_readActions (Fl_Widget *v, void *p) { ((gSampleChannel*)p)->__cb_readActions(); } -#ifdef WITH_VST -void gSampleChannel::cb_openFxWindow(Fl_Widget *v, void *p) { ((gSampleChannel*)p)->__cb_openFxWindow(); } -#endif - - -/* -------------------------------------------------------------------------- */ - - -void gSampleChannel::__cb_mute() -{ - glue_setMute(ch); -} - - -/* -------------------------------------------------------------------------- */ - - -void gSampleChannel::__cb_solo() -{ - solo->value() ? glue_setSoloOn(ch) : glue_setSoloOff(ch); -} - - -/* -------------------------------------------------------------------------- */ - - -void gSampleChannel::__cb_changeVol() -{ - glue_setChanVol(ch, vol->value()); -} - - -/* -------------------------------------------------------------------------- */ - - -#ifdef WITH_VST -void gSampleChannel::__cb_openFxWindow() -{ - gu_openSubWindow(mainWin, new gdPluginList(PluginHost::CHANNEL, ch), WID_FX_LIST); -} -#endif - - -/* -------------------------------------------------------------------------- */ - - - -void gSampleChannel::__cb_button() -{ - if (button->value()) // pushed - glue_keyPress(ch, Fl::event_ctrl(), Fl::event_shift()); - else // released - glue_keyRelease(ch, Fl::event_ctrl(), Fl::event_shift()); -} - - -/* -------------------------------------------------------------------------- */ - - -void gSampleChannel::__cb_openMenu() -{ - /* if you're recording (actions or input) no menu is allowed; you can't - * do anything, especially deallocate the channel */ - - if (G_Mixer.chanInput == ch || recorder::active) - return; - - /* the following is a trash workaround for a FLTK menu. We need a gMenu - * widget asap */ - - Fl_Menu_Item rclick_menu[] = { - {"Load new sample..."}, // 0 - {"Export sample to file..."}, // 1 - {"Setup keyboard input..."}, // 2 - {"Setup MIDI input..."}, // 3 - {"Setup MIDI output..."}, // 4 - {"Edit sample..."}, // 5 - {"Edit actions..."}, // 6 - {"Clear actions", 0, 0, 0, FL_SUBMENU}, // 7 - {"All"}, // 8 - {"Mute"}, // 9 - {"Volume"}, // 10 - {"Start/Stop"}, // 11 - {0}, // 12 - {"Clone channel"}, // 13 - {"Free channel"}, // 14 - {"Delete channel"}, // 15 - {0} - }; - - if (ch->status & (STATUS_EMPTY | STATUS_MISSING)) { - rclick_menu[1].deactivate(); - rclick_menu[5].deactivate(); - rclick_menu[14].deactivate(); - } - - /* no 'clear actions' if there are no actions */ - - if (!ch->hasActions) - rclick_menu[7].deactivate(); - - /* no 'clear start/stop actions' for those channels in loop mode: - * they cannot have start/stop actions. */ - - if (ch->mode & LOOP_ANY) - rclick_menu[11].deactivate(); - - Fl_Menu_Button *b = new Fl_Menu_Button(0, 0, 100, 50); - b->box(G_BOX); - b->textsize(GUI_FONT_SIZE_BASE); - b->textcolor(COLOR_TEXT_0); - b->color(COLOR_BG_0); - - const Fl_Menu_Item *m = rclick_menu->popup(Fl::event_x(), Fl::event_y(), 0, 0, b); - if (!m) return; - - if (strcmp(m->label(), "Load new sample...") == 0) { - openBrowser(BROWSER_LOAD_SAMPLE); - return; - } - - if (strcmp(m->label(), "Setup keyboard input...") == 0) { - new gdKeyGrabber(ch); /// FIXME - use gu_openSubWindow - return; - } - - if (strcmp(m->label(), "Setup MIDI input...") == 0) { - gu_openSubWindow(mainWin, new gdMidiInputChannel(ch), 0); - return; - } - - if (strcmp(m->label(), "Setup MIDI output...") == 0) { - gu_openSubWindow(mainWin, new gdMidiOutputSampleCh(ch), 0); - return; - } - - if (strcmp(m->label(), "Edit sample...") == 0) { - gu_openSubWindow(mainWin, new gdEditor(ch), WID_SAMPLE_EDITOR); /// FIXME title it's up to gdEditor - return; - } - - if (strcmp(m->label(), "Export sample to file...") == 0) { - openBrowser(BROWSER_SAVE_SAMPLE); - return; - } - - if (strcmp(m->label(), "Delete channel") == 0) { - if (!gdConfirmWin("Warning", "Delete channel: are you sure?")) - return; - glue_deleteChannel(ch); - return; - } - - if (strcmp(m->label(), "Free channel") == 0) { - if (ch->status == STATUS_PLAY) { - if (!gdConfirmWin("Warning", "This action will stop the channel: are you sure?")) - return; - } - else if (!gdConfirmWin("Warning", "Free channel: are you sure?")) - return; - - glue_freeChannel(ch); - - /* delete any related subwindow */ - - /** FIXME - use gu_closeAllSubwindows() */ - - mainWin->delSubWindow(WID_FILE_BROWSER); - mainWin->delSubWindow(WID_ACTION_EDITOR); - mainWin->delSubWindow(WID_SAMPLE_EDITOR); - mainWin->delSubWindow(WID_FX_LIST); - - return; - } - - if (strcmp(m->label(), "Clone channel") == 0) { - glue_cloneChannel(ch); - return; - } - - if (strcmp(m->label(), "Mute") == 0) { - if (!gdConfirmWin("Warning", "Clear all mute actions: are you sure?")) - return; - recorder::clearAction(ch->index, ACTION_MUTEON | ACTION_MUTEOFF); - if (!ch->hasActions) - delActionButton(); - - /* TODO - set mute=false */ - - gu_refreshActionEditor(); // refresh a.editor window, it could be open - return; - } - - if (strcmp(m->label(), "Start/Stop") == 0) { - if (!gdConfirmWin("Warning", "Clear all start/stop actions: are you sure?")) - return; - recorder::clearAction(ch->index, ACTION_KEYPRESS | ACTION_KEYREL | ACTION_KILLCHAN); - if (!ch->hasActions) - delActionButton(); - gu_refreshActionEditor(); // refresh a.editor window, it could be open - return; - } - - if (strcmp(m->label(), "Volume") == 0) { - if (!gdConfirmWin("Warning", "Clear all volume actions: are you sure?")) - return; - recorder::clearAction(ch->index, ACTION_VOLUME); - if (!ch->hasActions) - delActionButton(); - gu_refreshActionEditor(); // refresh a.editor window, it could be open - return; - } - - if (strcmp(m->label(), "All") == 0) { - if (!gdConfirmWin("Warning", "Clear all actions: are you sure?")) - return; - recorder::clearChan(ch->index); - delActionButton(); - gu_refreshActionEditor(); // refresh a.editor window, it could be open - return; - } - - if (strcmp(m->label(), "Edit actions...") == 0) { - gu_openSubWindow(mainWin, new gdActionEditor(ch), WID_ACTION_EDITOR); - return; - } -} - - -/* -------------------------------------------------------------------------- */ - - -void gSampleChannel::__cb_readActions() -{ - ch->readActions ? glue_stopReadingRecs(ch) : glue_startReadingRecs(ch); -} - - -/* -------------------------------------------------------------------------- */ - - -void gSampleChannel::openBrowser(int type) -{ - gWindow *childWin = NULL; - switch (type) { - case BROWSER_LOAD_SAMPLE: - childWin = new gdLoadBrowser(G_Conf.browserX, G_Conf.browserY, - G_Conf.browserW, G_Conf.browserH, "Browse sample", - G_Conf.samplePath.c_str(), glue_loadSample, ch); - break; - case BROWSER_SAVE_SAMPLE: - childWin = new gdSaveBrowser(G_Conf.browserX, G_Conf.browserY, - G_Conf.browserW, G_Conf.browserH, "Save sample", \ - G_Conf.samplePath.c_str(), "", glue_saveSample, ch); - break; - } - if (childWin) - gu_openSubWindow(mainWin, childWin, WID_FILE_BROWSER); -} - - -/* -------------------------------------------------------------------------- */ - - -void gSampleChannel::refresh() -{ - if (!mainButton->visible()) // mainButton invisible? status too (see below) - return; - - setColorsByStatus(ch->status, ch->recStatus); - - if (ch->wave != NULL) { - if (G_Mixer.chanInput == ch) - mainButton->setInputRecordMode(); - if (recorder::active) { - if (recorder::canRec(ch)) - mainButton->setActionRecordMode(); - } - status->redraw(); // status invisible? sampleButton too (see below) - } - mainButton->redraw(); -} - - -/* -------------------------------------------------------------------------- */ - - -void gSampleChannel::reset() -{ - delActionButton(true); // force==true, don't check, just remove it - mainButton->setDefaultMode("-- no sample --"); - mainButton->redraw(); - status->redraw(); -} - - -/* -------------------------------------------------------------------------- */ - - -void gSampleChannel::update() -{ - /* update sample button's label */ - - switch (ch->status) { - case STATUS_EMPTY: - mainButton->label("-- no sample --"); - break; - case STATUS_MISSING: - case STATUS_WRONG: - mainButton->label("* file not found! *"); - break; - default: - mainButton->label(ch->wave->name.c_str()); - break; - } - - /* update channels. If you load a patch with recorded actions, the 'R' - * button must be shown. Moreover if the actions are active, the 'R' - * button must be activated accordingly. */ - - if (ch->hasActions) - addActionButton(); - else - delActionButton(); - - /* updates modebox */ - - modeBox->value(ch->mode); - modeBox->redraw(); - - /* update volumes+mute+solo */ - - vol->value(ch->volume); - mute->value(ch->mute); - solo->value(ch->solo); - - mainButton->setKey(ch->key); - -#ifdef WITH_VST - fx->full = ch->plugins.size() > 0; - fx->redraw(); -#endif -} - - -/* -------------------------------------------------------------------------- */ - - -int gSampleChannel::keyPress(int e) -{ - return handleKey(e, ch->key); -} - - -/* -------------------------------------------------------------------------- */ - - -void gSampleChannel::addActionButton() -{ - /* quit if 'R' exists yet. */ - - if (readActions != NULL) - return; - - mainButton->size(mainButton->w()-24, mainButton->h()); - - redraw(); - - readActions = new gClick(mainButton->x() + mainButton->w() + 4, - mainButton->y(), 20, 20, "", readActionOff_xpm, - readActionOn_xpm); - readActions->type(FL_TOGGLE_BUTTON); - readActions->value(ch->readActions); - readActions->callback(cb_readActions, (void*)this); - add(readActions); - - /* hard redraw: there's no other way to avoid glitches when moving - * the 'R' button */ - - mainWin->keyboard->redraw(); -} - - -/* -------------------------------------------------------------------------- */ - - -void gSampleChannel::delActionButton(bool force) -{ - if (readActions == NULL) - return; - - /* TODO - readActions check is useless here */ - - if (!force && (readActions == NULL || ch->hasActions)) - return; - - remove(readActions); // delete from Keyboard group (FLTK) - delete readActions; // delete (C++) - readActions = NULL; - - mainButton->size(mainButton->w()+24, mainButton->h()); - mainButton->redraw(); -} - - -/* -------------------------------------------------------------------------- */ - - -void gSampleChannel::resize(int X, int Y, int W, int H) -{ - gChannel::resize(X, Y, W, H); - - if (w() < BREAK_FX) { -#ifdef WITH_VST - fx->hide(); -#endif - mainButton->size(w() - BREAK_DELTA, mainButton->h()); - mute->resize(mainButton->x()+mainButton->w()+4, y(), 20, 20); - solo->resize(mute->x()+mute->w()+4, y(), 20, 20); - - /* The followings are useless on manual resizing, but useful when a channel - * is added from a patch with a small width. */ - - modeBox->hide(); - if (readActions) - readActions->hide(); - } - else - if (w() < BREAK_MODE_BOX) { -#ifdef WITH_VST - fx->show(); -#endif - mainButton->size(w() - (BREAK_DELTA + BREAK_UNIT), mainButton->h()); - mute->resize(mainButton->x()+mainButton->w()+4, y(), 20, 20); - solo->resize(mute->x()+mute->w()+4, y(), 20, 20); - modeBox->hide(); - } - else - if (w() < BREAK_READ_ACTIONS) { - modeBox->show(); - mainButton->size(w() - (BREAK_DELTA + (BREAK_UNIT * 2)), mainButton->h()); - modeBox->resize(mainButton->x()+mainButton->w()+4, y(), 20, 20); - if (readActions) - readActions->hide(); - } - else { - if (readActions) { - mainButton->size(w() - (BREAK_DELTA + (BREAK_UNIT * 3)), mainButton->h()); - readActions->resize(mainButton->x()+mainButton->w()+4, y(), 20, 20); - readActions->show(); - } - } - - gChannel::init_sizes(); -} - - -/* -------------------------------------------------------------------------- */ -/* -------------------------------------------------------------------------- */ -/* -------------------------------------------------------------------------- */ - - -gSampleChannelButton::gSampleChannelButton(int x, int y, int w, int h, const char *l) - : gChannelButton(x, y, w, h, l) {} - - -/* -------------------------------------------------------------------------- */ - - -int gSampleChannelButton::handle(int e) -{ - int ret = gClick::handle(e); - switch (e) { - case FL_DND_ENTER: - case FL_DND_DRAG: - case FL_DND_RELEASE: { - ret = 1; - break; - } - case FL_PASTE: { - gSampleChannel *gch = (gSampleChannel*) parent(); // parent is gSampleChannel - SampleChannel *ch = gch->ch; - int result = glue_loadChannel(ch, gTrim(gStripFileUrl(Fl::event_text())).c_str()); - if (result != SAMPLE_LOADED_OK) - mainWin->keyboard->printChannelMessage(result); - ret = 1; - break; - } - } - return ret; -} diff --git a/src/gui/elems/ge_sampleChannel.h b/src/gui/elems/ge_sampleChannel.h deleted file mode 100644 index db5beb2..0000000 --- a/src/gui/elems/ge_sampleChannel.h +++ /dev/null @@ -1,104 +0,0 @@ -/* ----------------------------------------------------------------------------- - * - * Giada - Your Hardcore Loopmachine - * - * ge_sampleChannel - * - * ----------------------------------------------------------------------------- - * - * Copyright (C) 2010-2016 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_CHANNEL_H -#define GE_SAMPLE_CHANNEL_H - - -#include -#include -#include -#include "ge_channel.h" -#include "ge_channelButton.h" -#include "ge_mixed.h" - - -class gSampleChannel : public gChannel -{ -private: - - static void cb_button (Fl_Widget *v, void *p); - static void cb_mute (Fl_Widget *v, void *p); - static void cb_solo (Fl_Widget *v, void *p); - static void cb_openMenu (Fl_Widget *v, void *p); - static void cb_changeVol (Fl_Widget *v, void *p); - static void cb_readActions (Fl_Widget *v, void *p); -#ifdef WITH_VST - static void cb_openFxWindow (Fl_Widget *v, void *p); -#endif - - inline void __cb_mute (); - inline void __cb_solo (); - inline void __cb_changeVol (); - inline void __cb_button (); - inline void __cb_openMenu (); - inline void __cb_readActions (); -#ifdef WITH_VST - inline void __cb_openFxWindow(); -#endif - - void openBrowser(int type); - -public: - - gSampleChannel(int x, int y, int w, int h, class SampleChannel *ch); - - void reset (); - void update (); - void refresh (); - int keyPress(int event); - void resize (int x, int y, int w, int h); - - /* add/delActionButton - * add or remove 'R' button when actions are available. 'Status' is - * the initial status of the button: on or off. - * If force==true remove the button with no further checks. */ - - void addActionButton(); - void delActionButton(bool force=false); - - class gModeBox *modeBox; - class gClick *readActions; - - class SampleChannel *ch; -}; - - -/* -------------------------------------------------------------------------- */ - - -class gSampleChannelButton : public gChannelButton -{ -public: - gSampleChannelButton(int x, int y, int w, int h, const char *l=0); - int handle(int e); -}; - - -#endif diff --git a/src/gui/elems/ge_status.cpp b/src/gui/elems/ge_status.cpp index 8acaf0f..80cbac7 100644 --- a/src/gui/elems/ge_status.cpp +++ b/src/gui/elems/ge_status.cpp @@ -28,10 +28,12 @@ #include "../../core/mixer.h" +#include "../../core/const.h" #include "ge_status.h" -extern Mixer G_Mixer; +extern Mixer G_Mixer; +extern Recorder G_Recorder; gStatus::gStatus(int x, int y, int w, int h, SampleChannel *ch, const char *L) @@ -59,10 +61,10 @@ void gStatus::draw() fl_rectf(x()+1, y()+1, w()-2, h()-2, COLOR_BG_0); // status empty - if (G_Mixer.chanInput == ch) + if (G_Mixer.recording && ch->armed) fl_rectf(x()+1, y()+1, w()-2, h()-2, COLOR_BG_3); // take in progress else - if (recorder::active && recorder::canRec(ch)) + if (G_Recorder.active && G_Recorder.canRec(ch)) fl_rectf(x()+1, y()+1, w()-2, h()-2, COLOR_BG_4); // action record /* equation for the progress bar: diff --git a/src/gui/elems/ge_waveTools.cpp b/src/gui/elems/ge_waveTools.cpp index 49f9735..7e7b197 100644 --- a/src/gui/elems/ge_waveTools.cpp +++ b/src/gui/elems/ge_waveTools.cpp @@ -29,6 +29,7 @@ #include "../../core/graphics.h" #include "../../core/mixer.h" +#include "../../core/const.h" #include "../elems/ge_mixed.h" #include "../elems/ge_waveform.h" #include "ge_waveTools.h" @@ -54,7 +55,7 @@ gWaveTools::gWaveTools(int x, int y, int w, int h, SampleChannel *ch, const char /* ------------------------------------------------------------------ */ -void gWaveTools::updateWaveform() +void gWaveTools::updateWaveform() { waveform->alloc(w()); waveform->redraw(); @@ -64,7 +65,7 @@ void gWaveTools::updateWaveform() /* ------------------------------------------------------------------ */ -void gWaveTools::resize(int x, int y, int w, int h) +void gWaveTools::resize(int x, int y, int w, int h) { if (this->w() == w || (this->w() != w && this->h() != h)) { // vertical or both resize Fl_Widget::resize(x, y, w, h); @@ -87,7 +88,7 @@ void gWaveTools::resize(int x, int y, int w, int h) /* ------------------------------------------------------------------ */ -int gWaveTools::handle(int e) +int gWaveTools::handle(int e) { int ret = Fl_Group::handle(e); switch (e) { @@ -100,4 +101,3 @@ int gWaveTools::handle(int e) } return ret; } - diff --git a/src/gui/elems/ge_waveform.cpp b/src/gui/elems/ge_waveform.cpp index 5d8a48f..dd893ce 100644 --- a/src/gui/elems/ge_waveform.cpp +++ b/src/gui/elems/ge_waveform.cpp @@ -33,11 +33,12 @@ #include #include "../../core/wave.h" #include "../../core/conf.h" +#include "../../core/const.h" #include "../../core/mixer.h" #include "../../core/waveFx.h" #include "../../core/channel.h" #include "../../core/sampleChannel.h" -#include "../../glue/glue.h" +#include "../../glue/channel.h" #include "../dialogs/gd_editor.h" #include "ge_waveTools.h" #include "ge_mixed.h" @@ -250,7 +251,7 @@ void gWaveform::draw() for (unsigned k=0; k #include #include -#include "../../utils/utils.h" +#include "../../utils/fs.h" using std::vector; diff --git a/src/gui/elems/ge_window.cpp b/src/gui/elems/ge_window.cpp index 30c26cb..e14fcb8 100644 --- a/src/gui/elems/ge_window.cpp +++ b/src/gui/elems/ge_window.cpp @@ -75,7 +75,7 @@ void gWindow::addSubWindow(gWindow *w) { /** TODO - useless: delete ---------------------------------------- */ for (unsigned i=0; igetId() == subWindows.at(i)->getId()) { - //gLog("[gWindow] window %p (id=%d) exists, not added (and deleted)\n", (void*)w, w->getId()); + //gu_log("[gWindow] window %p (id=%d) exists, not added (and deleted)\n", (void*)w, w->getId()); delete w; return; } @@ -138,10 +138,10 @@ void gWindow::setId(int id) { void gWindow::debug() { - gLog("---- window stack (id=%d): ----\n", getId()); + gu_log("---- window stack (id=%d): ----\n", getId()); for (unsigned i=0; igetId()); - gLog("----\n"); + gu_log("[gWindow] %p (id=%d)\n", (void*)subWindows.at(i), subWindows.at(i)->getId()); + gu_log("----\n"); } diff --git a/src/gui/elems/ge_window.h b/src/gui/elems/ge_window.h index 4991b0f..e986d8d 100644 --- a/src/gui/elems/ge_window.h +++ b/src/gui/elems/ge_window.h @@ -34,7 +34,7 @@ #include #include -#include "../../utils/utils.h" +#include "../../utils/fs.h" using std::vector; diff --git a/src/gui/elems/midiChannel.cpp b/src/gui/elems/midiChannel.cpp new file mode 100644 index 0000000..bfdbea5 --- /dev/null +++ b/src/gui/elems/midiChannel.cpp @@ -0,0 +1,291 @@ +/* ----------------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * ge_midiChannel + * + * ----------------------------------------------------------------------------- + * + * Copyright (C) 2010-2016 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/mixer.h" +#include "../../core/conf.h" +#include "../../core/patch_DEPR_.h" +#include "../../core/graphics.h" +#include "../../core/channel.h" +#include "../../core/midiChannel.h" +#include "../../glue/channel.h" +#include "../../glue/main.h" +#include "../../glue/io.h" +#include "../../utils/gui.h" +#include "../dialogs/gd_mainWindow.h" +#include "../dialogs/gd_keyGrabber.h" +#include "../dialogs/gd_midiInput.h" +#include "../dialogs/gd_editor.h" +#include "../dialogs/gd_actionEditor.h" +#include "../dialogs/gd_warnings.h" +#include "../dialogs/gd_browser.h" +#include "../dialogs/gd_keyGrabber.h" +#include "../dialogs/gd_midiOutput.h" +#include "../dialogs/gd_pluginList.h" +#include "midiChannel.h" + + +extern Mixer G_Mixer; +extern Conf G_Conf; +extern Recorder G_Recorder; +extern Patch_DEPR_ G_Patch_DEPR_; +extern gdMainWindow *G_MainWin; + + +geMidiChannel::geMidiChannel(int X, int Y, int W, int H, MidiChannel *ch) + : geChannel(X, Y, W, H, CHANNEL_MIDI, ch) +{ + begin(); + +#if defined(WITH_VST) + int delta = 144; // (6 widgets * 20) + (6 paddings * 4) +#else + int delta = 120; // (5 widgets * 20) + (5 paddings * 4) +#endif + + button = new gButton(x(), y(), 20, 20, "", channelStop_xpm, channelPlay_xpm); + arm = new gClick(button->x()+button->w()+4, y(), 20, 20, "", armOff_xpm, armOn_xpm); + mainButton = new geMidiChannelButton(arm->x()+arm->w()+4, y(), w() - delta, 20, "-- MIDI --"); + mute = new gClick(mainButton->x()+mainButton->w()+4, y(), 20, 20, "", muteOff_xpm, muteOn_xpm); + solo = new gClick(mute->x()+mute->w()+4, y(), 20, 20, "", soloOff_xpm, soloOn_xpm); +#if defined(WITH_VST) + fx = new gFxButton(solo->x()+solo->w()+4, y(), 20, 20, fxOff_xpm, fxOn_xpm); + vol = new gDial(fx->x()+fx->w()+4, y(), 20, 20); +#else + vol = new gDial(solo->x()+solo->w()+4, y(), 20, 20); +#endif + + end(); + + resizable(mainButton); + + update(); + + button->callback(cb_button, (void*)this); + button->when(FL_WHEN_CHANGED); // do callback on keypress && on keyrelease + + arm->type(FL_TOGGLE_BUTTON); + arm->callback(cb_arm, (void*)this); + +#ifdef WITH_VST + fx->callback(cb_openFxWindow, (void*)this); +#endif + + mute->type(FL_TOGGLE_BUTTON); + mute->callback(cb_mute, (void*)this); + + solo->type(FL_TOGGLE_BUTTON); + solo->callback(cb_solo, (void*)this); + + mainButton->callback(cb_openMenu, (void*)this); + + vol->callback(cb_changeVol, (void*)this); + + ch->guiChannel = this; +} + + +/* -------------------------------------------------------------------------- */ + + +void geMidiChannel::cb_button (Fl_Widget *v, void *p) { ((geMidiChannel*)p)->__cb_button(); } +void geMidiChannel::cb_openMenu (Fl_Widget *v, void *p) { ((geMidiChannel*)p)->__cb_openMenu(); } + + +/* -------------------------------------------------------------------------- */ + + +void geMidiChannel::__cb_button() +{ + if (button->value()) + glue_keyPress(ch, Fl::event_ctrl(), Fl::event_shift()); +} + + +/* -------------------------------------------------------------------------- */ + + +void geMidiChannel::__cb_openMenu() +{ + Fl_Menu_Item rclick_menu[] = { + {"Edit actions..."}, // 0 + {"Clear actions", 0, 0, 0, FL_SUBMENU}, // 1 + {"All"}, // 2 + {0}, // 3 + {"Setup keyboard input..."}, // 5 + {"Setup MIDI input..."}, // 6 + {"Setup MIDI output..."}, // 7 + {"Clone channel"}, // 8 + {"Delete channel"}, // 9 + {0} + }; + + /* no 'clear actions' if there are no actions */ + + if (!ch->hasActions) + rclick_menu[1].deactivate(); + + Fl_Menu_Button *b = new Fl_Menu_Button(0, 0, 100, 50); + b->box(G_BOX); + b->textsize(GUI_FONT_SIZE_BASE); + b->textcolor(COLOR_TEXT_0); + b->color(COLOR_BG_0); + + const Fl_Menu_Item *m = rclick_menu->popup(Fl::event_x(), Fl::event_y(), 0, 0, b); + if (!m) return; + + if (strcmp(m->label(), "Delete channel") == 0) { + if (!gdConfirmWin("Warning", "Delete channel: are you sure?")) + return; + glue_deleteChannel(ch); + return; + } + + if (strcmp(m->label(), "Clone channel") == 0) { + glue_cloneChannel(ch); + return; + } + + if (strcmp(m->label(), "Setup keyboard input...") == 0) { + gu_openSubWindow(G_MainWin, new gdKeyGrabber(ch), 0); + //new gdKeyGrabber(ch); + return; + } + + if (strcmp(m->label(), "All") == 0) { + if (!gdConfirmWin("Warning", "Clear all actions: are you sure?")) + return; + G_Recorder.clearChan(ch->index); + gu_refreshActionEditor(); // refresh a.editor window, it could be open + return; + } + + if (strcmp(m->label(), "Edit actions...") == 0) { + gu_openSubWindow(G_MainWin, new gdActionEditor(ch), WID_ACTION_EDITOR); + return; + } + + if (strcmp(m->label(), "Setup MIDI input...") == 0) { + gu_openSubWindow(G_MainWin, new gdMidiInputChannel(ch), 0); + return; + } + + if (strcmp(m->label(), "Setup MIDI output...") == 0) { + //gu_openSubWindow(G_MainWin, new gdMidiGrabberChannel(ch, GrabForOutput), 0); + gu_openSubWindow(G_MainWin, new gdMidiOutputMidiCh((MidiChannel*) ch), 0); + return; + } +} + + +/* -------------------------------------------------------------------------- */ + + +void geMidiChannel::refresh() +{ + setColorsByStatus(ch->status, ch->recStatus); + mainButton->redraw(); +} + + +/* -------------------------------------------------------------------------- */ + + +void geMidiChannel::reset() +{ + mainButton->setDefaultMode("-- MIDI --"); + mainButton->redraw(); +} + + +/* -------------------------------------------------------------------------- */ + + +void geMidiChannel::update() +{ + if (((MidiChannel*) ch)->midiOut) { + char tmp[32]; + sprintf(tmp, "-- MIDI (channel %d) --", ((MidiChannel*) ch)->midiOutChan+1); + mainButton->copy_label(tmp); + } + else + mainButton->label("-- MIDI --"); + + vol->value(ch->volume); + mute->value(ch->mute); + solo->value(ch->solo); + + mainButton->setKey(ch->key); + +#ifdef WITH_VST + fx->full = ch->plugins.size() > 0; + fx->redraw(); +#endif +} + + +/* -------------------------------------------------------------------------- */ + + +void geMidiChannel::resize(int X, int Y, int W, int H) +{ + geChannel::resize(X, Y, W, H); + + arm->hide(); +#ifdef WITH_VST + fx->hide(); +#endif + + if (w() > BREAK_ARM) + arm->show(); +#ifdef WITH_VST + if (w() > BREAK_FX) + fx->show(); +#endif + + packWidgets(); +} + + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + + +geMidiChannelButton::geMidiChannelButton(int x, int y, int w, int h, const char *l) + : geChannelButton(x, y, w, h, l) {} + + +/* -------------------------------------------------------------------------- */ + + +int geMidiChannelButton::handle(int e) +{ + // MIDI drag-n-drop does nothing so far. + return gClick::handle(e); +} diff --git a/src/gui/elems/midiChannel.h b/src/gui/elems/midiChannel.h new file mode 100644 index 0000000..cd06ddc --- /dev/null +++ b/src/gui/elems/midiChannel.h @@ -0,0 +1,72 @@ +/* ----------------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * ge_midiChannel + * + * ----------------------------------------------------------------------------- + * + * Copyright (C) 2010-2016 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_MIDI_CHANNEL_H +#define GE_MIDI_CHANNEL_H + + +#include "channel.h" +#include "channelButton.h" + + +class geMidiChannel : public geChannel +{ +private: + + static void cb_button (Fl_Widget *v, void *p); + static void cb_openMenu (Fl_Widget *v, void *p); + + inline void __cb_button (); + inline void __cb_openMenu (); + inline void __cb_readActions (); + +public: + + geMidiChannel(int x, int y, int w, int h, class MidiChannel *ch); + + void reset (); + void update (); + void refresh (); + int keyPress(int event); // TODO - move to base class + void resize (int x, int y, int w, int h); +}; + + +/* -------------------------------------------------------------------------- */ + + +class geMidiChannelButton : public geChannelButton +{ +public: + geMidiChannelButton(int x, int y, int w, int h, const char *l=0); + int handle(int e); +}; + + +#endif diff --git a/src/gui/elems/muteEditor.cpp b/src/gui/elems/muteEditor.cpp new file mode 100644 index 0000000..e2861b2 --- /dev/null +++ b/src/gui/elems/muteEditor.cpp @@ -0,0 +1,410 @@ +/* ----------------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * ----------------------------------------------------------------------------- + * + * Copyright (C) 2010-2016 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/recorder.h" +#include "../../core/mixer.h" +#include "../../core/channel.h" +#include "../../glue/main.h" +#include "../../utils/log.h" +#include "../dialogs/gd_actionEditor.h" +#include "../dialogs/gd_mainWindow.h" +#include "ge_keyboard.h" +#include "muteEditor.h" + + +extern gdMainWindow *G_MainWin; +extern Mixer G_Mixer; +extern Recorder G_Recorder; + + +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(COLOR_BG_1); + 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(COLOR_BG_1); + 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(COLOR_BG_2); + + 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 G_Recorder.sortActions() */ + + for (unsigned i=0; ichan == pParent->chan->index) { + if (G_Recorder.global.at(i).at(j)->type & (ACTION_MUTEON | ACTION_MUTEOFF)) { + point p; + p.frame = G_Recorder.frames.at(i); + p.type = G_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 >= G_Mixer.totalFrames) { + frame_b = G_Mixer.totalFrames; + frame_a = frame_b-2048; + } + + if (nextPoint % 2 != 0) { + G_Recorder.rec(pParent->chan->index, ACTION_MUTEOFF, frame_a); + G_Recorder.rec(pParent->chan->index, ACTION_MUTEON, frame_b); + } + else { + G_Recorder.rec(pParent->chan->index, ACTION_MUTEON, frame_a); + G_Recorder.rec(pParent->chan->index, ACTION_MUTEOFF, frame_b); + } + G_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 == 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); + + G_Recorder.deleteAction(pParent->chan->index, points.at(a).frame, points.at(a).type, false); // false = don't check vals + G_Recorder.deleteAction(pParent->chan->index, points.at(b).frame, points.at(b).type, false); // false = don't check vals + G_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; + + G_Recorder.deleteAction( + pParent->chan->index, + points.at(draggedPoint).frame, + points.at(draggedPoint).type, + false); // don't check values + + G_Recorder.rec( + pParent->chan->index, + points.at(draggedPoint).type, + newFrame); + + G_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/muteEditor.h b/src/gui/elems/muteEditor.h new file mode 100644 index 0000000..620f82a --- /dev/null +++ b/src/gui/elems/muteEditor.h @@ -0,0 +1,106 @@ +/* ----------------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * ----------------------------------------------------------------------------- + * + * Copyright (C) 2010-2016 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_MUTECHANNEL_H +#define GE_MUTECHANNEL_H + + +#include +#include +#include +#include +#include "../../utils/fs.h" +#include "baseActionEditor.h" + + +using std::vector; + + +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 */ + + 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, class 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/noteEditor.cpp b/src/gui/elems/noteEditor.cpp new file mode 100644 index 0000000..19940a2 --- /dev/null +++ b/src/gui/elems/noteEditor.cpp @@ -0,0 +1,90 @@ +/* ----------------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * ----------------------------------------------------------------------------- + * + * Copyright (C) 2010-2016 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/conf.h" +#include "../../utils/log.h" +#include "../dialogs/gd_actionEditor.h" +#include "pianoItem.h" +#include "pianoRoll.h" +#include "noteEditor.h" + + +extern Conf G_Conf; + + +geNoteEditor::geNoteEditor(int x, int y, gdActionEditor *pParent) + : Fl_Scroll(x, y, 200, 422), + pParent (pParent) +{ + size(pParent->totalWidth, G_Conf.pianoRollH); + pianoRoll = new gePianoRoll(x, y, pParent->totalWidth, pParent); +} + + +/* -------------------------------------------------------------------------- */ + + +geNoteEditor::~geNoteEditor() +{ + clear(); + G_Conf.pianoRollH = h(); + G_Conf.pianoRollY = pianoRoll->y(); +} + + +/* -------------------------------------------------------------------------- */ + + +void geNoteEditor::updateActions() +{ + pianoRoll->updateActions(); +} + + +/* -------------------------------------------------------------------------- */ + + +void geNoteEditor::draw() +{ + pianoRoll->size(this->w(), pianoRoll->h()); /// <--- not optimal + + /* clear background */ + + fl_rectf(x(), y(), w(), h(), COLOR_BG_MAIN); + + /* clip pianoRoll to pianoRollContainer size */ + + fl_push_clip(x(), y(), w(), h()); + draw_child(*pianoRoll); + fl_pop_clip(); + + fl_color(COLOR_BD_0); + fl_line_style(0); + fl_rect(x(), y(), pParent->totalWidth, h()); +} diff --git a/src/gui/elems/noteEditor.h b/src/gui/elems/noteEditor.h new file mode 100644 index 0000000..0ca600f --- /dev/null +++ b/src/gui/elems/noteEditor.h @@ -0,0 +1,54 @@ +/* ----------------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * ----------------------------------------------------------------------------- + * + * Copyright (C) 2010-2016 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_NOTE_EDITOR_H +#define GE_NOTE_EDITOR_H + +#include +#include +#include +#include +#include "../../core/recorder.h" + + +class geNoteEditor : public Fl_Scroll +{ +private: + + class gdActionEditor *pParent; + class gePianoRoll *pianoRoll; + +public: + + geNoteEditor(int x, int y, class gdActionEditor *parent); + ~geNoteEditor(); + void draw(); + void updateActions(); +}; + + +#endif diff --git a/src/gui/elems/pianoItem.cpp b/src/gui/elems/pianoItem.cpp new file mode 100644 index 0000000..870b61a --- /dev/null +++ b/src/gui/elems/pianoItem.cpp @@ -0,0 +1,348 @@ +/* ----------------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * ----------------------------------------------------------------------------- + * + * Copyright (C) 2010-2016 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/kernelMidi.h" +#include "../../core/mixer.h" +#include "../../core/channel.h" +#include "../../core/midiChannel.h" +#include "../dialogs/gd_actionEditor.h" +#include "noteEditor.h" +#include "pianoRoll.h" +#include "pianoItem.h" + + +extern KernelMidi G_KernelMidi; +extern Mixer G_Mixer; +extern Recorder G_Recorder; + + +gePianoItem::gePianoItem(int X, int Y, int rel_x, int rel_y, Recorder::action *_a, + Recorder::action *_b, gdActionEditor *pParent) + : Fl_Box (X, Y, MIN_WIDTH, gePianoRoll::CELL_H), + a (_a), + b (_b), + pParent (pParent), + selected(false), + event_a (0x00), + event_b (0x00), + changed (false) +{ + /* a is a pointer: action exists, needs to be displayed */ + + if (a) { + note = G_KernelMidi.getB2(a->iValue); + frame_a = a->frame; + frame_b = b->frame; + event_a = a->iValue; + event_b = b->iValue; + int newX = rel_x + (frame_a / pParent->zoom); + int newY = rel_y + getY(note); + int newW = (frame_b - frame_a) / pParent->zoom; + resize(newX, newY, newW, h()); + } + + /* a is null: action needs to be recorded from scratch */ + + else { + note = getNote(rel_y); + frame_a = rel_x * pParent->zoom; + frame_b = (rel_x + 20) * pParent->zoom; + record(); + size((frame_b - frame_a) / pParent->zoom, h()); + } +} + + +/* -------------------------------------------------------------------------- */ + + +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 *pPiano = (geNoteEditor*) parent(); + + for (int i=0; ichildren(); i++) { + + gePianoItem *pItem = (gePianoItem*) pPiano->child(i); + + /* don't check against itself and with different y positions */ + + if (pItem == this || pItem->y() != y()) + continue; + + int start = pItem->x() >= x() ? pItem->x() : x(); + int end = pItem->x()+pItem->w() < x()+w() ? pItem->x()+pItem->w() : x()+w(); + if (start < end) + return true; + } + + return false; +} + + +/* -------------------------------------------------------------------------- */ + + +void gePianoItem::draw() +{ + int _w = w() > MIN_WIDTH ? w() : MIN_WIDTH; + fl_rectf(x(), y()+2, _w, h()-3, (Fl_Color) selected ? COLOR_BD_1 : COLOR_BG_2); +} + + +/* -------------------------------------------------------------------------- */ + + +void gePianoItem::record() +{ + /* avoid frame overflow */ + + int overflow = frame_b - G_Mixer.totalFrames; + if (overflow > 0) { + frame_b -= overflow; + frame_a -= overflow; + } + + event_a |= (MIDI_NOTE_ON); + event_a |= (note << 16); // note value + event_a |= (MIDI_VELOCITY); + event_a |= (0x00); + + event_b |= (MIDI_NOTE_OFF); + event_b |= (note << 16); // note value + event_b |= (MIDI_VELOCITY); + event_b |= (0x00); + + G_Recorder.rec(pParent->chan->index, ACTION_MIDI, frame_a, event_a); + G_Recorder.rec(pParent->chan->index, ACTION_MIDI, frame_b, event_b); +} + + +/* -------------------------------------------------------------------------- */ + + +void gePianoItem::remove() +{ + G_Recorder.deleteAction(pParent->chan->index, frame_a, ACTION_MIDI, true, event_a, 0.0); + G_Recorder.deleteAction(pParent->chan->index, frame_b, ACTION_MIDI, true, event_b, 0.0); + + /* send a note-off in case we are deleting it in a middle of a key_on + * key_off sequence. */ + + ((MidiChannel*) pParent->chan)->sendMidi(event_b); + + ((gePianoRoll*) parent())->cursorOnItem = false; +} + + +/* -------------------------------------------------------------------------- */ + + +int gePianoItem::handle(int e) +{ + int ret = 0; + + switch (e) { + + case FL_ENTER: { + ((gePianoRoll*) parent())->cursorOnItem = true; + selected = true; + ret = 1; + redraw(); + break; + } + + case FL_LEAVE: { + fl_cursor(FL_CURSOR_DEFAULT, FL_WHITE, FL_BLACK); + ((gePianoRoll*) parent())->cursorOnItem = false; + 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); + remove(); + hide(); // for Windows + Fl::delete_widget(this); + ((geNoteEditor*)parent())->redraw(); + } + ret = 1; + break; + } + + case FL_DRAG: { + + changed = true; + + geNoteEditor *pr = (geNoteEditor*) parent(); + int coverX = pParent->coverX + pr->x(); // relative coverX + int nx, ny, nw; + + if (onLeftEdge) { + nx = Fl::event_x(); + ny = y(); + nw = x()-Fl::event_x()+w(); + if (nx < pr->x()) { + nx = pr->x(); + nw = w()+x()-pr->x(); + } + else + if (nx > x()+w()-MIN_WIDTH) { + nx = x()+w()-MIN_WIDTH; + nw = MIN_WIDTH; + } + resize(nx, ny, nw, h()); + } + else + if (onRightEdge) { + nw = Fl::event_x()-x(); + if (Fl::event_x() < x()+MIN_WIDTH) + nw = MIN_WIDTH; + else + if (Fl::event_x() > coverX) + nw = coverX-x(); + size(nw, h()); + } + else { + nx = Fl::event_x() - push_x; + if (nx < pr->x()+1) + nx = pr->x()+1; + else + if (nx+w() > coverX) + nx = coverX-w(); + + /* snapping */ + + if (pParent->gridTool->isOn()) + nx = pParent->gridTool->getSnapPoint(nx-pr->x()) + pr->x() - 1; + + position(nx, y()); + } + + /* update screen */ + + redraw(); + ((geNoteEditor*)parent())->redraw(); + ret = 1; + break; + } + + case FL_RELEASE: { + + /* delete & record the action, only if it doesn't overlap with + * another one */ + + if (overlap()) { + resize(old_x, y(), old_w, h()); + redraw(); + } + else + if (changed) { + remove(); + note = getNote(getRelY()); + frame_a = getRelX() * pParent->zoom; + frame_b = (getRelX()+w()) * pParent->zoom; + record(); + changed = false; + } + + ((geNoteEditor*)parent())->redraw(); + + ret = 1; + break; + } + } + return ret; +} + + +/* -------------------------------------------------------------------------- */ + + +int gePianoItem::getNote(int rel_y) +{ + return gePianoRoll::MAX_KEYS - (rel_y / gePianoRoll::CELL_H); +} + + +/* -------------------------------------------------------------------------- */ + + +int gePianoItem::getRelY() +{ + return y() - parent()->y(); +} + + +/* -------------------------------------------------------------------------- */ + + +int gePianoItem::getRelX() +{ + return x() - parent()->x(); +} + + +/* -------------------------------------------------------------------------- */ + + +int gePianoItem::getY(int note) +{ + return (gePianoRoll::MAX_KEYS * gePianoRoll::CELL_H) - (note * gePianoRoll::CELL_H); +} diff --git a/src/gui/elems/pianoItem.h b/src/gui/elems/pianoItem.h new file mode 100644 index 0000000..0aa4b2b --- /dev/null +++ b/src/gui/elems/pianoItem.h @@ -0,0 +1,119 @@ +/* ----------------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * ----------------------------------------------------------------------------- + * + * Copyright (C) 2010-2016 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_H +#define GE_PIANO_ITEM_H + + +#include +#include "../../core/recorder.h" + + +class gePianoItem : public Fl_Box +{ +private: + + /* getRelX/Y + * return x/y point of this item, relative to piano roll (and not to + * entire screen) */ + + int getRelY(); + int getRelX(); + + /* getNote + * from a relative_y return the real MIDI note, range 0-127. 15 is + * the hardcoded value for note height in pixels */ + + int getNote(int rel_y); + + /* getY + * from a note, return the y position on piano roll */ + + int getY(int note); + + /* overlap + * check if this item don't overlap with another one. */ + + bool overlap(); + + struct Recorder::action *a; + struct Recorder::action *b; + class gdActionEditor *pParent; + + bool selected; + int push_x; + + /* MIDI note, start frame, end frame - Used only if it's a newly added + * action */ /** FIXME - is it true? */ + + int note; + int frame_a; + int frame_b; + + /* event - bitmasked MIDI events, generated by record() or by ctor if + * not newly added action */ + + int event_a; + int event_b; + + /* 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; + +public: + + static const int MIN_WIDTH = 10; + static const int HANDLE_WIDTH = 5; + + /* pianoItem ctor + * if action *a == NULL, record a new action */ + + gePianoItem(int x, int y, int rel_x, int rel_y, struct Recorder::action *a, + struct Recorder::action *b, class gdActionEditor *pParent); + + void draw(); + int handle(int e); + void record(); + void remove(); + + int getFrame_a() { return frame_a; } + int getFrame_b() { return frame_b; } + int getNote() { return note; } +}; + + +#endif diff --git a/src/gui/elems/pianoRoll.cpp b/src/gui/elems/pianoRoll.cpp new file mode 100644 index 0000000..b8f776b --- /dev/null +++ b/src/gui/elems/pianoRoll.cpp @@ -0,0 +1,360 @@ +/* ----------------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * ----------------------------------------------------------------------------- + * + * Copyright (C) 2010-2016 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/conf.h" +#include "../../core/mixer.h" +#include "../../core/channel.h" +#include "../../core/recorder.h" +#include "../../core/kernelMidi.h" +#include "../../utils/log.h" +#include "../dialogs/gd_actionEditor.h" +#include "pianoItem.h" +#include "noteEditor.h" +#include "pianoRoll.h" + + +extern Conf G_Conf; +extern Recorder G_Recorder; +extern Mixer G_Mixer; +extern KernelMidi G_KernelMidi; + + +gePianoRoll::gePianoRoll(int X, int Y, int W, gdActionEditor *pParent) + : geBaseActionEditor(X, Y, W, 40, pParent), + cursorOnItem (false) +{ + resizable(nullptr); // don't resize children (i.e. pianoItem) + size(W, (MAX_KEYS+1) * CELL_H); // 128 MIDI channels * CELL_H height + + if (G_Conf.pianoRollY == -1) + position(x(), y()-(h()/2)); // center + else + position(x(), G_Conf.pianoRollY); + + drawSurface1(); + drawSurface2(); + + /* add actions when the window is opened. Position is zoom-based. MIDI + * actions come always in pair: start + end. */ + + G_Recorder.sortActions(); + + Recorder::action *a2 = nullptr; + Recorder::action *prev = nullptr; + + for (unsigned i=0; i than the grey area */ + /** FIXME - can we move this to the outer cycle? */ + + if (G_Recorder.frames.at(i) > G_Mixer.totalFrames) + continue; + + Recorder::action *a1 = G_Recorder.global.at(i).at(j); + + /* Skip action if: + - does not belong to this channel + - is not a MIDI action (we only want MIDI things here) + - is the previous one (we have already checked it) + - (later on) if it's NOTE_OFF (0x80): we want note on only */ + + if (a1->chan != pParent->chan->index) + continue; + if (a1->type != ACTION_MIDI) + continue; + if (a1 == prev) + continue; + + /* extract MIDI infos from a1: if is note off skip it, we are looking + * for note on only */ + + int a1_type = G_KernelMidi.getB1(a1->iValue); + int a1_note = G_KernelMidi.getB2(a1->iValue); + + if (a1_type == 0x80) // NOTE_OFF + continue; + + /* Search for the next action. Must have: same channel, ACTION_MIDI, + greater than a1->frame and with MIDI properties of note_off (0x80), same + note of a1, any velocity value (0xFF) because we just don't care about the + velocity of a note_off. */ + + G_Recorder.getNextAction(a1->chan, ACTION_MIDI, a1->frame, &a2, + G_KernelMidi.getIValue(0x80, a1_note, 0xFF)); + + /* next action note_off found: add a new gePianoItem to piano roll */ + + if (a2) { + new gePianoItem(0, 0, x(), y(), a1, a2, pParent); + prev = a2; + a2 = nullptr; + } + else + gu_log("[geNoteEditor] recorder didn't find requested action!\n"); + // TODO - create new gOrphanedPianoItem + } + } + + end(); +} + + +/* -------------------------------------------------------------------------- */ + + +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 */ + + fl_rectf(0, 0, CELL_W, h(), COLOR_BG_MAIN); + + fl_line_style(FL_DASH, 0, nullptr); + fl_font(FL_HELVETICA, GUI_FONT_SIZE_BASE); + + int octave = MAX_OCTAVES; + + for (int i=1; i<=MAX_KEYS+1; i++) { + + /* print key note label. C C# D D# E F F# G G# A A# B */ + + char note[6]; + switch (i % KEYS) { + case (int) Notes::G: + fl_rectf(0, i*CELL_H, CELL_W, CELL_H, COLOR_BG_RICH); + sprintf(note, "%dG", octave); + break; + case (int) Notes::FS: + sprintf(note, "%dF#", octave); + break; + case (int) Notes::F: + sprintf(note, "%dF", octave); + break; + case (int) Notes::E: + fl_rectf(0, i*CELL_H, CELL_W, CELL_H, COLOR_BG_RICH); + sprintf(note, "%dE", octave); + break; + case (int) Notes::DS: + sprintf(note, "%dD#", octave); + break; + case (int) Notes::D: + fl_rectf(0, i*CELL_H, CELL_W, CELL_H, COLOR_BG_RICH); + sprintf(note, "%dD", octave); + break; + case (int) Notes::CS: + sprintf(note, "%dC#", octave); + break; + case (int) Notes::C: + sprintf(note, "%dC", octave); + break; + case (int) Notes::B: + fl_rectf(0, i*CELL_H, CELL_W, CELL_H, COLOR_BG_RICH); + sprintf(note, "%dB", octave); + break; + case (int) Notes::AS: + sprintf(note, "%dA#", octave); + break; + case (int) Notes::A: + fl_rectf(0, i*CELL_H, CELL_W, CELL_H, COLOR_BG_RICH); + sprintf(note, "%dA", octave); + break; + case (int) Notes::GS: + sprintf(note, "%dG#", octave); + octave--; + break; + } + + /* Print note name */ + + fl_color(COLOR_BG_LINE); + fl_draw(note, 4, ((i-1)*CELL_H)+1, CELL_W, CELL_H, + (Fl_Align) (FL_ALIGN_LEFT | FL_ALIGN_CENTER)); + + /* Print horizontal line */ + + if (i < MAX_KEYS+1) + fl_line(0, i*CELL_H, CELL_W, +i*CELL_H); + } + + fl_line_style(0); + fl_end_offscreen(); +} + + +/* -------------------------------------------------------------------------- */ + + +void gePianoRoll::drawSurface2() +{ + surface2 = fl_create_offscreen(CELL_W, h()); + fl_begin_offscreen(surface2); + fl_rectf(0, 0, CELL_W, h(), COLOR_BG_MAIN); + fl_color(COLOR_BG_LINE); + fl_line_style(FL_DASH, 0, nullptr); + for (int i=1; i<=MAX_KEYS+1; i++) { + switch (i % KEYS) { + case (int) Notes::G: + case (int) Notes::E: + case (int) Notes::D: + case (int) Notes::B: + case (int) Notes::A: + fl_rectf(0, i*CELL_H, CELL_W, CELL_H, COLOR_BG_RICH); + break; + } + if (i < MAX_KEYS+1) { + fl_color(COLOR_BG_LINE); + fl_line(0, i*CELL_H, CELL_W, i*CELL_H); + } + } + fl_line_style(0); + fl_end_offscreen(); +} + + +/* -------------------------------------------------------------------------- */ + + +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 + 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 + fl_copy_offscreen(x()+i, y(), CELL_W, h(), surface2, 0, 0); +#endif + + baseDraw(false); + draw_children(); +} + + +/* -------------------------------------------------------------------------- */ + + +int gePianoRoll::handle(int e) +{ + int ret = Fl_Group::handle(e); + + switch (e) { + case FL_PUSH: { + + /* avoid click on grey area */ + + if (Fl::event_x() >= pParent->coverX) { + ret = 1; + break; + } + + + push_y = Fl::event_y() - y(); + + if (Fl::event_button1()) { + + /* 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; + + /* if no overlap, add new piano item. Also check that it doesn't + * overflow on the grey area, by shifting it to the left if + * necessary. */ + + if (!cursorOnItem) { + int greyover = ax+20 - pParent->coverX-x(); + if (greyover > 0) + ax -= greyover; + add(new gePianoItem(ax, ay, ax-x(), ay-y(), nullptr, nullptr, pParent)); + redraw(); + } + } + ret = 1; + break; + } + case FL_DRAG: { + + if (Fl::event_button3()) { + + geNoteEditor *prc = (geNoteEditor*) parent(); + position(x(), Fl::event_y() - push_y); + + if (y() > prc->y()) + position(x(), prc->y()); + else + if (y() < prc->y()+prc->h()-h()) + position(x(), prc->y()+prc->h()-h()); + + prc->redraw(); + } + ret = 1; + break; + } + case FL_MOUSEWHEEL: { // nothing to do, just avoid small internal scroll + ret = 1; + break; + } + } + return ret; +} + + +/* -------------------------------------------------------------------------- */ + + +void gePianoRoll::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 */ + + gePianoItem *i; + for (int k=0; kgetFrame_a(), i->getFrame_b(), i->x()); + + int newX = x() + (i->getFrame_a() / pParent->zoom); + int newW = ((i->getFrame_b() - i->getFrame_a()) / pParent->zoom); + if (newW < 8) + newW = 8; + i->resize(newX, i->y(), newW, i->h()); + i->redraw(); + + //gu_log("update point %p, frame_a=%d frame_b=%d, x()=%d\n", (void*) i, i->getFrame_a(), i->getFrame_b(), i->x()); + } +} diff --git a/src/gui/elems/pianoRoll.h b/src/gui/elems/pianoRoll.h new file mode 100644 index 0000000..449d4d0 --- /dev/null +++ b/src/gui/elems/pianoRoll.h @@ -0,0 +1,84 @@ +/* ----------------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * ----------------------------------------------------------------------------- + * + * Copyright (C) 2010-2016 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_ROLL_H +#define GE_PIANO_ROLL_H + + +#include +#include "baseActionEditor.h" + + +class gePianoRoll : public geBaseActionEditor +{ +private: + + enum class Notes + { + G = 1, FS = 2, F = 3, E = 4, DS = 5, D = 6, CS = 7, C = 8, B = 9, AS = 10, + A = 11, GS = 0 + }; + + /* 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 + call creates an offscreen surface of CELL_W pixel wide containing note values. + The second call creates another offscreen surface of CELL_W pixels wide + containing the rest of the piano roll. The latter will then be tiled during + the ::draw() call. */ + + void drawSurface1(); + void drawSurface2(); + + int push_y; + + Fl_Offscreen surface1; // notes, no repeat + Fl_Offscreen surface2; // lines, x-repeat + +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; + + gePianoRoll(int x, int y, int w, class gdActionEditor *pParent); + + void draw(); + int handle(int e); + void updateActions(); + + /* cursorOnItem + Defines wheter the cursor is over a piano item. This value is updated by each + gePianoItem sub-widget. */ + + bool cursorOnItem; +}; + + +#endif diff --git a/src/gui/elems/sampleChannel.cpp b/src/gui/elems/sampleChannel.cpp new file mode 100644 index 0000000..5b7e4c9 --- /dev/null +++ b/src/gui/elems/sampleChannel.cpp @@ -0,0 +1,512 @@ +/* ----------------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * ge_sampleChannel + * + * ----------------------------------------------------------------------------- + * + * Copyright (C) 2010-2016 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/pluginHost.h" +#include "../../core/mixer.h" +#include "../../core/conf.h" +#include "../../core/patch_DEPR_.h" +#include "../../core/recorder.h" +#include "../../core/graphics.h" +#include "../../core/wave.h" +#include "../../glue/main.h" +#include "../../glue/io.h" +#include "../../glue/channel.h" +#include "../../glue/storage.h" +#include "../../utils/gui.h" +#include "../../utils/string.h" +#include "../dialogs/gd_mainWindow.h" +#include "../dialogs/gd_keyGrabber.h" +#include "../dialogs/gd_midiOutput.h" +#include "../dialogs/gd_midiInput.h" +#include "../dialogs/gd_editor.h" +#include "../dialogs/gd_actionEditor.h" +#include "../dialogs/gd_warnings.h" +#include "../dialogs/gd_browser.h" +#include "ge_status.h" +#include "ge_modeBox.h" +#include "ge_keyboard.h" +#include "sampleChannel.h" + + +extern Mixer G_Mixer; +extern Conf G_Conf; +extern Recorder G_Recorder; +extern Patch_DEPR_ G_Patch_DEPR_; +extern gdMainWindow *G_MainWin; + + +geSampleChannel::geSampleChannel(int X, int Y, int W, int H, SampleChannel *ch) + : geChannel(X, Y, W, H, CHANNEL_SAMPLE, ch) +{ + begin(); + + button = new gButton(x(), y(), 20, 20, "", channelStop_xpm, channelPlay_xpm); + arm = new gClick(button->x()+button->w()+4, y(), 20, 20, "", armOff_xpm, armOn_xpm); + status = new gStatus(arm->x()+arm->w()+4, y(), 20, 20, ch); + mainButton = new geSampleChannelButton(status->x()+status->w()+4, y(), 20, 20, "-- no sample --"); + readActions = new gClick(mainButton->x()+mainButton->w()+4, y(), 20, 20, "", readActionOff_xpm, readActionOn_xpm); + modeBox = new gModeBox(readActions->x()+readActions->w()+4, y(), 20, 20, ch); + mute = new gClick(modeBox->x()+modeBox->w()+4, y(), 20, 20, "", muteOff_xpm, muteOn_xpm); + solo = new gClick(mute->x()+mute->w()+4, y(), 20, 20, "", soloOff_xpm, soloOn_xpm); +#ifdef WITH_VST + fx = new gFxButton(solo->x()+solo->w()+4, y(), 20, 20, fxOff_xpm, fxOn_xpm); + vol = new gDial(fx->x()+fx->w()+4, y(), 20, 20); +#else + vol = new gDial(solo->x()+solo->w()+4, y(), 20, 20); +#endif + + end(); + + resizable(mainButton); + + update(); + + button->callback(cb_button, (void*)this); + button->when(FL_WHEN_CHANGED); // do callback on keypress && on keyrelease + + arm->type(FL_TOGGLE_BUTTON); + arm->callback(cb_arm, (void*)this); + +#ifdef WITH_VST + fx->callback(cb_openFxWindow, (void*)this); +#endif + + mute->type(FL_TOGGLE_BUTTON); + mute->callback(cb_mute, (void*)this); + + solo->type(FL_TOGGLE_BUTTON); + solo->callback(cb_solo, (void*)this); + + mainButton->callback(cb_openMenu, (void*)this); + + readActions->type(FL_TOGGLE_BUTTON); + readActions->value(ch->readActions); + readActions->callback(cb_readActions, (void*)this); + + vol->callback(cb_changeVol, (void*)this); + + ch->guiChannel = this; +} + + +/* -------------------------------------------------------------------------- */ + + +void geSampleChannel::cb_button (Fl_Widget *v, void *p) { ((geSampleChannel*)p)->__cb_button(); } +void geSampleChannel::cb_openMenu (Fl_Widget *v, void *p) { ((geSampleChannel*)p)->__cb_openMenu(); } +void geSampleChannel::cb_readActions (Fl_Widget *v, void *p) { ((geSampleChannel*)p)->__cb_readActions(); } + + +/* -------------------------------------------------------------------------- */ + + +void geSampleChannel::__cb_button() +{ + if (button->value()) // pushed + glue_keyPress(ch, Fl::event_ctrl(), Fl::event_shift()); + else // released + glue_keyRelease(ch, Fl::event_ctrl(), Fl::event_shift()); +} + + +/* -------------------------------------------------------------------------- */ + + +void geSampleChannel::__cb_openMenu() +{ + /* If you're recording (input or actions) no menu is allowed; you can't do + anything, especially deallocate the channel */ + + if (G_Mixer.recording || G_Recorder.active) + return; + + /* the following is a trash workaround for a FLTK menu. We need a gMenu + * widget asap */ + + Fl_Menu_Item rclick_menu[] = { + {"Load new sample..."}, // 0 + {"Export sample to file..."}, // 1 + {"Setup keyboard input..."}, // 2 + {"Setup MIDI input..."}, // 3 + {"Setup MIDI output..."}, // 4 + {"Edit sample..."}, // 5 + {"Edit actions..."}, // 6 + {"Clear actions", 0, 0, 0, FL_SUBMENU}, // 7 + {"All"}, // 8 + {"Mute"}, // 9 + {"Volume"}, // 10 + {"Start/Stop"}, // 11 + {0}, // 12 + {"Clone channel"}, // 13 + {"Free channel"}, // 14 + {"Delete channel"}, // 15 + {0} + }; + + if (ch->status & (STATUS_EMPTY | STATUS_MISSING)) { + rclick_menu[1].deactivate(); + rclick_menu[5].deactivate(); + rclick_menu[14].deactivate(); + } + + /* no 'clear actions' if there are no actions */ + + if (!ch->hasActions) + rclick_menu[7].deactivate(); + + /* no 'clear start/stop actions' for those channels in loop mode: + * they cannot have start/stop actions. */ + + if (((SampleChannel*)ch)->mode & LOOP_ANY) + rclick_menu[11].deactivate(); + + Fl_Menu_Button *b = new Fl_Menu_Button(0, 0, 100, 50); + b->box(G_BOX); + b->textsize(GUI_FONT_SIZE_BASE); + b->textcolor(COLOR_TEXT_0); + b->color(COLOR_BG_0); + + const Fl_Menu_Item *m = rclick_menu->popup(Fl::event_x(), Fl::event_y(), 0, 0, b); + if (!m) return; + + if (strcmp(m->label(), "Load new sample...") == 0) { + openBrowser(BROWSER_LOAD_SAMPLE); + return; + } + + if (strcmp(m->label(), "Setup keyboard input...") == 0) { + new gdKeyGrabber(ch); /// FIXME - use gu_openSubWindow + return; + } + + if (strcmp(m->label(), "Setup MIDI input...") == 0) { + gu_openSubWindow(G_MainWin, new gdMidiInputChannel(ch), 0); + return; + } + + if (strcmp(m->label(), "Setup MIDI output...") == 0) { + gu_openSubWindow(G_MainWin, new gdMidiOutputSampleCh((SampleChannel*) ch), 0); + return; + } + + if (strcmp(m->label(), "Edit sample...") == 0) { + gu_openSubWindow(G_MainWin, new gdEditor((SampleChannel*) ch), WID_SAMPLE_EDITOR); /// FIXME title it's up to gdEditor + return; + } + + if (strcmp(m->label(), "Export sample to file...") == 0) { + openBrowser(BROWSER_SAVE_SAMPLE); + return; + } + + if (strcmp(m->label(), "Delete channel") == 0) { + if (!gdConfirmWin("Warning", "Delete channel: are you sure?")) + return; + glue_deleteChannel(ch); + return; + } + + if (strcmp(m->label(), "Free channel") == 0) { + if (ch->status == STATUS_PLAY) { + if (!gdConfirmWin("Warning", "This action will stop the channel: are you sure?")) + return; + } + else if (!gdConfirmWin("Warning", "Free channel: are you sure?")) + return; + + glue_freeChannel(ch); + + /* delete any related subwindow */ + + /** FIXME - use gu_closeAllSubwindows() */ + + G_MainWin->delSubWindow(WID_FILE_BROWSER); + G_MainWin->delSubWindow(WID_ACTION_EDITOR); + G_MainWin->delSubWindow(WID_SAMPLE_EDITOR); + G_MainWin->delSubWindow(WID_FX_LIST); + + return; + } + + if (strcmp(m->label(), "Clone channel") == 0) { + glue_cloneChannel(ch); + return; + } + + if (strcmp(m->label(), "Mute") == 0) { + if (!gdConfirmWin("Warning", "Clear all mute actions: are you sure?")) + return; + G_Recorder.clearAction(ch->index, ACTION_MUTEON | ACTION_MUTEOFF); + if (!ch->hasActions) + hideActionButton(); + + /* TODO - set mute=false */ + + gu_refreshActionEditor(); // refresh a.editor window, it could be open + return; + } + + if (strcmp(m->label(), "Start/Stop") == 0) { + if (!gdConfirmWin("Warning", "Clear all start/stop actions: are you sure?")) + return; + G_Recorder.clearAction(ch->index, ACTION_KEYPRESS | ACTION_KEYREL | ACTION_KILLCHAN); + if (!ch->hasActions) + hideActionButton(); + gu_refreshActionEditor(); // refresh a.editor window, it could be open + return; + } + + if (strcmp(m->label(), "Volume") == 0) { + if (!gdConfirmWin("Warning", "Clear all volume actions: are you sure?")) + return; + G_Recorder.clearAction(ch->index, ACTION_VOLUME); + if (!ch->hasActions) + hideActionButton(); + gu_refreshActionEditor(); // refresh a.editor window, it could be open + return; + } + + if (strcmp(m->label(), "All") == 0) { + if (!gdConfirmWin("Warning", "Clear all actions: are you sure?")) + return; + G_Recorder.clearChan(ch->index); + hideActionButton(); + gu_refreshActionEditor(); // refresh a.editor window, it could be open + return; + } + + if (strcmp(m->label(), "Edit actions...") == 0) { + gu_openSubWindow(G_MainWin, new gdActionEditor(ch), WID_ACTION_EDITOR); + return; + } +} + + +/* -------------------------------------------------------------------------- */ + + +void geSampleChannel::__cb_readActions() +{ + glue_startStopReadingRecs((SampleChannel*) ch); +} + + +/* -------------------------------------------------------------------------- */ + + +void geSampleChannel::openBrowser(int type) +{ + gWindow *childWin = NULL; + switch (type) { + case BROWSER_LOAD_SAMPLE: + childWin = new gdLoadBrowser(G_Conf.browserX, G_Conf.browserY, + G_Conf.browserW, G_Conf.browserH, "Browse sample", + G_Conf.samplePath.c_str(), glue_loadSample, ch); + break; + case BROWSER_SAVE_SAMPLE: + childWin = new gdSaveBrowser(G_Conf.browserX, G_Conf.browserY, + G_Conf.browserW, G_Conf.browserH, "Save sample", \ + G_Conf.samplePath.c_str(), "", glue_saveSample, ch); + break; + } + if (childWin) + gu_openSubWindow(G_MainWin, childWin, WID_FILE_BROWSER); +} + + +/* -------------------------------------------------------------------------- */ + + +void geSampleChannel::refresh() +{ + if (!mainButton->visible()) // mainButton invisible? status too (see below) + return; + + setColorsByStatus(ch->status, ch->recStatus); + + if (((SampleChannel*) ch)->wave != NULL) { + if (G_Mixer.recording && ch->armed) + mainButton->setInputRecordMode(); + if (G_Recorder.active) { + if (G_Recorder.canRec(ch)) + mainButton->setActionRecordMode(); + } + status->redraw(); // status invisible? sampleButton too (see below) + } + mainButton->redraw(); +} + + +/* -------------------------------------------------------------------------- */ + + +void geSampleChannel::reset() +{ + hideActionButton(); + mainButton->setDefaultMode("-- no sample --"); + mainButton->redraw(); + status->redraw(); +} + + +/* -------------------------------------------------------------------------- */ + + +void geSampleChannel::update() +{ + /* update sample button's label */ + + switch (ch->status) { + case STATUS_EMPTY: + mainButton->label("-- no sample --"); + break; + case STATUS_MISSING: + case STATUS_WRONG: + mainButton->label("* file not found! *"); + break; + default: + mainButton->label(((SampleChannel*) ch)->wave->name.c_str()); + break; + } + + /* update channels. If you load a patch with recorded actions, the 'R' + * button must be shown. Moreover if the actions are active, the 'R' + * button must be activated accordingly. */ + + if (ch->hasActions) + showActionButton(); + else + hideActionButton(); + + /* updates modebox */ + + modeBox->value(((SampleChannel*) ch)->mode); + modeBox->redraw(); + + /* update volumes+mute+solo */ + + vol->value(ch->volume); + mute->value(ch->mute); + solo->value(ch->solo); + + mainButton->setKey(ch->key); + +#ifdef WITH_VST + fx->full = ch->plugins.size() > 0; + fx->redraw(); +#endif +} + + +/* -------------------------------------------------------------------------- */ + + +void geSampleChannel::showActionButton() +{ + readActions->value(((SampleChannel*) ch)->readActions); + readActions->show(); + packWidgets(); + redraw(); +} + + +/* -------------------------------------------------------------------------- */ + + +void geSampleChannel::hideActionButton() +{ + readActions->hide(); + packWidgets(); + redraw(); +} + + +/* -------------------------------------------------------------------------- */ + + +void geSampleChannel::resize(int X, int Y, int W, int H) +{ + geChannel::resize(X, Y, W, H); + + arm->hide(); + modeBox->hide(); + readActions->hide(); +#ifdef WITH_VST + fx->hide(); +#endif + + if (w() > BREAK_ARM) + arm->show(); +#ifdef WITH_VST + if (w() > BREAK_FX) + fx->show(); +#endif + if (w() > BREAK_MODE_BOX) + modeBox->show(); + if (w() > BREAK_READ_ACTIONS && ch->hasActions) + readActions->show(); + + packWidgets(); +} + + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + + +geSampleChannelButton::geSampleChannelButton(int x, int y, int w, int h, const char *l) + : geChannelButton(x, y, w, h, l) {} + + +/* -------------------------------------------------------------------------- */ + + +int geSampleChannelButton::handle(int e) +{ + int ret = gClick::handle(e); + switch (e) { + case FL_DND_ENTER: + case FL_DND_DRAG: + case FL_DND_RELEASE: { + ret = 1; + break; + } + case FL_PASTE: { + geSampleChannel *gch = (geSampleChannel*) parent(); // parent is geSampleChannel + SampleChannel *ch = (SampleChannel*) gch->ch; + int result = glue_loadChannel(ch, gu_trim(gu_stripFileUrl(Fl::event_text())).c_str()); + if (result != SAMPLE_LOADED_OK) + G_MainWin->keyboard->printChannelMessage(result); + ret = 1; + break; + } + } + return ret; +} diff --git a/src/gui/elems/sampleChannel.h b/src/gui/elems/sampleChannel.h new file mode 100644 index 0000000..efde2a6 --- /dev/null +++ b/src/gui/elems/sampleChannel.h @@ -0,0 +1,83 @@ +/* ----------------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * ge_sampleChannel + * + * ----------------------------------------------------------------------------- + * + * Copyright (C) 2010-2016 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_CHANNEL_H +#define GE_SAMPLE_CHANNEL_H + + +#include "channel.h" +#include "channelButton.h" + + +class geSampleChannel : public geChannel +{ +private: + + static void cb_button (Fl_Widget *v, void *p); + static void cb_openMenu (Fl_Widget *v, void *p); + static void cb_readActions (Fl_Widget *v, void *p); + + inline void __cb_button (); + inline void __cb_openMenu (); + inline void __cb_readActions (); + + void openBrowser(int type); + +public: + + geSampleChannel(int x, int y, int w, int h, class SampleChannel *ch); + + void reset (); + void update (); + void refresh (); + void resize (int x, int y, int w, int h); + + /* show/hideActionButton + Adds or removes 'R' button when actions are available. */ + + void showActionButton(); + void hideActionButton(); + + class gModeBox *modeBox; + class gClick *readActions; +}; + + +/* -------------------------------------------------------------------------- */ + + +class geSampleChannelButton : public geChannelButton +{ +public: + geSampleChannelButton(int x, int y, int w, int h, const char *l=0); + int handle(int e); +}; + + +#endif diff --git a/src/main.cpp b/src/main.cpp index d619c5a..a80e63b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -38,16 +38,20 @@ #include "core/mixer.h" #include "core/mixerHandler.h" #include "core/kernelAudio.h" +#include "core/kernelMidi.h" #include "core/recorder.h" -#include "utils/gui_utils.h" +#include "utils/gui.h" #include "gui/dialogs/gd_mainWindow.h" #include "core/pluginHost.h" /* global variables. Yeah, we are nasty */ -pthread_t t_video; +pthread_t G_videoThread; +KernelAudio G_KernelAudio; Mixer G_Mixer; +Recorder G_Recorder; +KernelMidi G_KernelMidi; bool G_quit; bool G_audio_status; bool G_midiStatus; @@ -55,18 +59,18 @@ Patch_DEPR_ G_Patch_DEPR_; Patch G_Patch; Conf G_Conf; MidiMapConf G_MidiMap; -gdMainWindow *mainWin; +gdMainWindow *G_MainWin; #ifdef WITH_VST PluginHost G_PluginHost; #endif -void *thread_video(void *arg); +void *videoThreadCb(void *arg); -int main(int argc, char **argv) { - +int main(int argc, char **argv) +{ G_quit = false; init_prepareParser(); @@ -75,7 +79,7 @@ int main(int argc, char **argv) { init_prepareKernelMIDI(); init_startGUI(argc, argv); Fl::lock(); - pthread_create(&t_video, NULL, thread_video, NULL); + pthread_create(&G_videoThread, NULL, videoThreadCb, NULL); init_startKernelAudio(); #ifdef WITH_VST @@ -88,16 +92,16 @@ int main(int argc, char **argv) { juce::shutdownJuce_GUI(); #endif - pthread_join(t_video, NULL); + pthread_join(G_videoThread, NULL); return ret; } - -void *thread_video(void *arg) { +void *videoThreadCb(void *arg) +{ if (G_audio_status) while (!G_quit) { - gu_refresh(); + gu_refreshUI(); #ifdef _WIN32 Sleep(GUI_SLEEP); #else diff --git a/src/utils/fs.cpp b/src/utils/fs.cpp new file mode 100644 index 0000000..23b2384 --- /dev/null +++ b/src/utils/fs.cpp @@ -0,0 +1,267 @@ +/* ----------------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * utils + * + * ----------------------------------------------------------------------------- + * + * Copyright (C) 2010-2016 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 + * . + * + * -------------------------------------------------------------------------- */ + + +#if defined(_WIN32) // getcwd (unix) or __getcwd (win) + #include + #include +#else + #include +#endif + +#include +#include // stat (gu_dirExists) +#include +#include +#include +#include +#include +#include +#if defined(__APPLE__) + #include // basename unix + #include // getpwuid +#endif +#include "../core/const.h" +#include "string.h" +#include "log.h" +#include "fs.h" + + +using std::string; +using std::vector; + + +bool gu_fileExists(const string &filename) +{ + FILE *fh = fopen(filename.c_str(), "rb"); + if (!fh) { + return 0; + } + else { + fclose(fh); + return 1; + } +} + + +/* -------------------------------------------------------------------------- */ + + +bool gu_isDir(const string &path) +{ + bool ret; + +#if defined(__linux__) + + struct stat s1; + stat(path.c_str(), &s1); + ret = S_ISDIR(s1.st_mode); + +#elif defined(__APPLE__) + + if (strcmp(path.c_str(), "") == 0) + ret = false; + else { + struct stat s1; + stat(path.c_str(), &s1); + ret = S_ISDIR(s1.st_mode); + + /* check if ret is a bundle, a special OS X folder which must be + * shown as a regular file (VST). + * FIXME - consider native functions CFBundle... */ + + if (ret) { + if (gu_fileExists(path + "/Contents/Info.plist")) + ret = false; + } + } + +#elif defined(__WIN32) + + unsigned dwAttrib = GetFileAttributes(path.c_str()); + ret = (dwAttrib != INVALID_FILE_ATTRIBUTES && + (dwAttrib & FILE_ATTRIBUTE_DIRECTORY)); +#endif + + return ret & !gu_isProject(path); +} + + +/* -------------------------------------------------------------------------- */ + + +bool gu_dirExists(const string &path) +{ + struct stat st; + if (stat(path.c_str(), &st) != 0 && errno == ENOENT) + return false; + return true; +} + + +/* -------------------------------------------------------------------------- */ + + +bool gu_mkdir(const string &path) +{ +#if defined(__linux__) || defined(__APPLE__) + if (mkdir(path.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) == 0) +#else + if (_mkdir(path.c_str()) == 0) +#endif + return true; + return false; +} + + +/* -------------------------------------------------------------------------- */ + + +string gu_basename(const string &s) +{ + string out = s; + out.erase(0, out.find_last_of(G_SLASH_STR) + 1); + return out; +} + + +/* -------------------------------------------------------------------------- */ + + +string gu_dirname(const string &path) +{ + if (path.empty()) + return ""; + string out = path; + out.erase(out.find_last_of(G_SLASH_STR)); + return out; +} + + +/* -------------------------------------------------------------------------- */ + + +string gu_getCurrentPath() +{ + char buf[PATH_MAX]; +#if defined(__WIN32) + if (_getcwd(buf, PATH_MAX) != NULL) +#else + if (getcwd(buf, PATH_MAX) != NULL) +#endif + return buf; + else + return ""; +} + + +/* -------------------------------------------------------------------------- */ + + +string gu_getExt(const string &file) +{ + // TODO - use std functions + int len = strlen(file.c_str()); + int pos = len; + while (pos>0) { + if (file[pos] == '.') + break; + pos--; + } + if (pos==0) + return ""; + string out = file; + return out.substr(pos+1, len); +} + + +/* -------------------------------------------------------------------------- */ + + +string gu_stripExt(const string &s) +{ + return s.substr(0, s.find_last_of(".")); +} + + +/* -------------------------------------------------------------------------- */ + + +bool gu_isProject(const string &path) +{ + /** FIXME - checks too weak */ + + if (gu_getExt(path.c_str()) == "gprj" && gu_dirExists(path)) + return 1; + return 0; +} + + +/* -------------------------------------------------------------------------- */ + + +string gu_stripFileUrl(const string &f) +{ + string out = f; + out = gu_replace(out, "file://", ""); + out = gu_replace(out, "%20", " "); + return out; +} + + +/* -------------------------------------------------------------------------- */ + + +string gu_getHomePath() +{ + char path[PATH_MAX]; + +#if defined(__linux__) + + snprintf(path, PATH_MAX, "%s/.giada", getenv("HOME")); + +#elif defined(_WIN32) + + snprintf(path, PATH_MAX, "."); + +#elif defined(__APPLE__) + + struct passwd *p = getpwuid(getuid()); + if (p == NULL) { + gu_log("[gu_getHomePath] unable to fetch user infos\n"); + return ""; + } + else { + const char *home = p->pw_dir; + snprintf(path, PATH_MAX, "%s/Library/Application Support/Giada", home); + } + +#endif + + return string(path); +} diff --git a/src/utils/fs.h b/src/utils/fs.h new file mode 100644 index 0000000..abb9861 --- /dev/null +++ b/src/utils/fs.h @@ -0,0 +1,67 @@ +/* ----------------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * utils + * + * ----------------------------------------------------------------------------- + * + * Copyright (C) 2010-2016 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 UTILS_H +#define UTILS_H + + +#include +#include + + +using std::string; +using std::vector; + + +bool gu_fileExists(const string &path); + +bool gu_dirExists(const string &path); + +bool gu_isDir(const string &path); + +bool gu_isProject(const string &path); + +bool gu_mkdir(const string &path); + +string gu_getCurrentPath(); + +string gu_getHomePath(); + +string gu_basename(const string &s); + +string gu_dirname(const string &s); + +string gu_getExt(const string &s); + +string gu_stripExt(const string &s); + +string gu_stripFileUrl(const string &s); + + +#endif diff --git a/src/utils/gui.cpp b/src/utils/gui.cpp new file mode 100644 index 0000000..5daf5d5 --- /dev/null +++ b/src/utils/gui.cpp @@ -0,0 +1,218 @@ +/* ----------------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * gui_utils + * + * ----------------------------------------------------------------------------- + * + * Copyright (C) 2010-2016 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/mixer.h" +#include "../core/patch_DEPR_.h" +#include "../core/recorder.h" +#include "../core/wave.h" +#include "../core/pluginHost.h" +#include "../core/channel.h" +#include "../core/conf.h" +#include "../core/graphics.h" +#include "../gui/dialogs/gd_warnings.h" +#include "../gui/dialogs/gd_mainWindow.h" +#include "../gui/dialogs/gd_actionEditor.h" +#include "../gui/elems/ge_keyboard.h" +#include "../gui/elems/ge_window.h" +#include "../gui/elems/channel.h" +#include "log.h" +#include "string.h" +#include "gui.h" + + +extern Mixer G_Mixer; +extern unsigned G_beats; +extern bool G_audio_status; +extern Patch_DEPR_ G_patch; +extern Conf G_conf; +extern uint32_t G_time; +extern gdMainWindow *G_MainWin; +#ifdef WITH_VST +extern PluginHost G_PluginHost; +#endif + + +static int blinker = 0; + + +void gu_refreshUI() +{ + Fl::lock(); + + /* update dynamic elements: in and out meters, beat meter and + * each channel */ + + G_MainWin->inOut->refresh(); + G_MainWin->beatMeter->redraw(); + G_MainWin->keyboard->refreshColumns(); + + /* compute timer for blinker */ + + blinker++; + if (blinker > 12) + blinker = 0; + + /* redraw GUI */ + + Fl::unlock(); + Fl::awake(); +} + + +/* -------------------------------------------------------------------------- */ + + +int gu_getBlinker() +{ + return blinker; +} + + +/* -------------------------------------------------------------------------- */ + + +void gu_updateControls() +{ + for (unsigned i=0; iguiChannel->update(); + + G_MainWin->inOut->setOutVol(G_Mixer.outVol); + G_MainWin->inOut->setInVol(G_Mixer.inVol); +#ifdef WITH_VST + G_MainWin->inOut->setMasterFxOutFull(G_PluginHost.getStack(PluginHost::MASTER_OUT)->size() > 0); + G_MainWin->inOut->setMasterFxInFull(G_PluginHost.getStack(PluginHost::MASTER_IN)->size() > 0); +#endif + + G_MainWin->timing->setMeter(G_Mixer.beats, G_Mixer.bars); + G_MainWin->timing->setBpm(G_Mixer.bpm); + + G_MainWin->controller->updatePlay(G_Mixer.running); + G_MainWin->controller->updateMetronome(G_Mixer.metronome); +} + + +/* -------------------------------------------------------------------------- */ + + +void gu_updateMainWinLabel(const string &s) +{ + std::string out = std::string(G_APP_NAME) + " - " + s; + G_MainWin->copy_label(out.c_str()); +} + + +/* -------------------------------------------------------------------------- */ + + +void gu_setFavicon(Fl_Window *w) +{ +#if defined(__linux__) + + fl_open_display(); + Pixmap p, mask; + XpmCreatePixmapFromData(fl_display, DefaultRootWindow(fl_display), + (char **) giada_icon, &p, &mask, NULL); + w->icon((char *)p); + +#elif defined(_WIN32) + + w->icon((char *)LoadIcon(fl_display, MAKEINTRESOURCE(IDI_ICON1))); + +#endif +} + + +/* -------------------------------------------------------------------------- */ + + +void gu_openSubWindow(gWindow *parent, gWindow *child, int id) +{ + if (parent->hasWindow(id)) { + gu_log("[GU] parent has subwindow with id=%d, deleting\n", id); + parent->delSubWindow(id); + } + child->setId(id); + parent->addSubWindow(child); +} + + +/* -------------------------------------------------------------------------- */ + + +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); + } +} + + +/* -------------------------------------------------------------------------- */ + + +gWindow *gu_getSubwindow(gWindow *parent, int id) +{ + if (parent->hasWindow(id)) + return parent->getChild(id); + else + return NULL; +} + + +/* -------------------------------------------------------------------------- */ + + +void gu_closeAllSubwindows() +{ + /* don't close WID_FILE_BROWSER, because it's the caller of this + * function */ + + G_MainWin->delSubWindow(WID_ACTION_EDITOR); + G_MainWin->delSubWindow(WID_SAMPLE_EDITOR); + G_MainWin->delSubWindow(WID_FX_LIST); + G_MainWin->delSubWindow(WID_FX); +} + + +/* -------------------------------------------------------------------------- */ + + +string gu_removeFltkChars(const string &s) +{ + string out = gu_replace(s, "/", "-"); + out = gu_replace(out, "|", "-"); + out = gu_replace(out, "&", "-"); + out = gu_replace(out, "_", "-"); + return out; +} diff --git a/src/utils/gui.h b/src/utils/gui.h new file mode 100644 index 0000000..921c9ea --- /dev/null +++ b/src/utils/gui.h @@ -0,0 +1,99 @@ +/* ----------------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * ----------------------------------------------------------------------------- + * + * Copyright (C) 2010-2016 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 GUI_UTILS_H +#define GUI_UTILS_H + +#include +#include +#include +#include +#ifdef __APPLE__ + #include // in osx, for basename() (but linux?) +#endif + +/* including stuff for the favicon */ + +#if defined(_WIN32) + #include "../ext/resource.h" +#elif defined(__linux__) + #include +#endif + + +using std::string; + + +/* refresh + * refresh all GUI elements. */ + +void gu_refreshUI(); + +/* getBlinker +* return blinker value, used to make widgets blink. */ + +int gu_getBlinker(); + +/* updateControls + * update attributes of control elements (sample names, volumes, ...). + * Useful when loading a new patch. */ + +void gu_updateControls(); + +/* update_win_label + * update the name of the main window */ + +void gu_updateMainWinLabel(const string &s); + +void gu_setFavicon(Fl_Window *w); + +void gu_openSubWindow(class gWindow *parent, gWindow *child, int id); + +/* refreshActionEditor + * reload the action editor window by closing and reopening it. It's used + * when you delete some actions from the mainWindow and the action editor + * window is open. */ + +void gu_refreshActionEditor(); + + +/* closeAllSubwindows + * close all subwindows attached to mainWin. */ + +void gu_closeAllSubwindows(); + + +/* getSubwindow + * return a pointer to an open subwindow, otherwise NULL. */ + +gWindow *gu_getSubwindow(class gWindow *parent, int id); + +/* removeFltkChars + * Strip special chars used by FLTK to split menus into sub-menus. */ + +string gu_removeFltkChars(const string &s); + +#endif diff --git a/src/utils/gui_utils.cpp b/src/utils/gui_utils.cpp deleted file mode 100644 index ec711d5..0000000 --- a/src/utils/gui_utils.cpp +++ /dev/null @@ -1,224 +0,0 @@ -/* ----------------------------------------------------------------------------- - * - * Giada - Your Hardcore Loopmachine - * - * gui_utils - * - * ----------------------------------------------------------------------------- - * - * Copyright (C) 2010-2016 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/mixer.h" -#include "../core/patch_DEPR_.h" -#include "../core/recorder.h" -#include "../core/wave.h" -#include "../core/pluginHost.h" -#include "../core/channel.h" -#include "../core/conf.h" -#include "../core/graphics.h" -#include "../gui/dialogs/gd_warnings.h" -#include "../gui/dialogs/gd_mainWindow.h" -#include "../gui/dialogs/gd_actionEditor.h" -#include "../gui/elems/ge_keyboard.h" -#include "../gui/elems/ge_window.h" -#include "../gui/elems/ge_channel.h" -#include "gui_utils.h" -#include "log.h" - - -extern Mixer G_Mixer; -extern unsigned G_beats; -extern bool G_audio_status; -extern Patch_DEPR_ G_patch; -extern Conf G_conf; -extern uint32_t G_time; -extern gdMainWindow *mainWin; -#ifdef WITH_VST -extern PluginHost G_PluginHost; -#endif - - -static int blinker = 0; - - -void gu_refresh() -{ - Fl::lock(); - - /* update dynamic elements: in and out meters, beat meter and - * each channel */ - - mainWin->inOut->refresh(); - mainWin->beatMeter->redraw(); - mainWin->keyboard->refreshColumns(); - - /* compute timer for blinker */ - - blinker++; - if (blinker > 12) - blinker = 0; - - /* redraw GUI */ - - Fl::unlock(); - Fl::awake(); -} - - -/* -------------------------------------------------------------------------- */ - - -int gu_getBlinker() -{ - return blinker; -} - - -/* -------------------------------------------------------------------------- */ - - -void gu_updateControls() -{ - for (unsigned i=0; iguiChannel->update(); - } - - mainWin->inOut->setOutVol(G_Mixer.outVol); - mainWin->inOut->setInVol(G_Mixer.inVol); -#ifdef WITH_VST - mainWin->inOut->setMasterFxOutFull(G_PluginHost.getStack(PluginHost::MASTER_OUT)->size() > 0); - mainWin->inOut->setMasterFxInFull(G_PluginHost.getStack(PluginHost::MASTER_IN)->size() > 0); -#endif - - mainWin->timing->setMeter(G_Mixer.beats, G_Mixer.bars); - mainWin->timing->setBpm(G_Mixer.bpm); - - /* if you reset to init state while the seq is in play: it's better to - * update the button status */ - - mainWin->controller->updatePlay(G_Mixer.running); - mainWin->controller->updateMetronome(0); -} - - -/* -------------------------------------------------------------------------- */ - - -void gu_update_win_label(const char *c) -{ - std::string out = G_APP_NAME; - out += " - "; - out += c; - mainWin->copy_label(out.c_str()); -} - - -/* -------------------------------------------------------------------------- */ - - -void gu_setFavicon(Fl_Window *w) -{ -#if defined(__linux__) - fl_open_display(); - Pixmap p, mask; - XpmCreatePixmapFromData( - fl_display, - DefaultRootWindow(fl_display), - (char **)giada_icon, - &p, - &mask, - NULL); - w->icon((char *)p); -#elif defined(_WIN32) - w->icon((char *)LoadIcon(fl_display, MAKEINTRESOURCE(IDI_ICON1))); -#endif -} - - -/* -------------------------------------------------------------------------- */ - - -void gu_openSubWindow(gWindow *parent, gWindow *child, int id) -{ - if (parent->hasWindow(id)) { - gLog("[GU] parent has subwindow with id=%d, deleting\n", id); - parent->delSubWindow(id); - } - child->setId(id); - parent->addSubWindow(child); -} - - -/* -------------------------------------------------------------------------- */ - - -void gu_refreshActionEditor() -{ - /** TODO - why don't we simply call WID_ACTION_EDITOR->redraw()? */ - - gdActionEditor *aeditor = (gdActionEditor*) mainWin->getChild(WID_ACTION_EDITOR); - if (aeditor) { - Channel *chan = aeditor->chan; - mainWin->delSubWindow(WID_ACTION_EDITOR); - gu_openSubWindow(mainWin, new gdActionEditor(chan), WID_ACTION_EDITOR); - } -} - - -/* -------------------------------------------------------------------------- */ - - -gWindow *gu_getSubwindow(gWindow *parent, int id) -{ - if (parent->hasWindow(id)) - return parent->getChild(id); - else - return NULL; -} - - -/* -------------------------------------------------------------------------- */ - - -void gu_closeAllSubwindows() -{ - /* don't close WID_FILE_BROWSER, because it's the caller of this - * function */ - - mainWin->delSubWindow(WID_ACTION_EDITOR); - mainWin->delSubWindow(WID_SAMPLE_EDITOR); - mainWin->delSubWindow(WID_FX_LIST); - mainWin->delSubWindow(WID_FX); -} - - -/* -------------------------------------------------------------------------- */ - - -string gu_removeFltkChars(const string &s) -{ - string out = gReplace(s, "/", "-"); - out = gReplace(out, "|", "-"); - out = gReplace(out, "&", "-"); - out = gReplace(out, "_", "-"); - return out; -} diff --git a/src/utils/gui_utils.h b/src/utils/gui_utils.h deleted file mode 100644 index 605b196..0000000 --- a/src/utils/gui_utils.h +++ /dev/null @@ -1,101 +0,0 @@ -/* --------------------------------------------------------------------- - * - * Giada - Your Hardcore Loopmachine - * - * gui_utils - * - * --------------------------------------------------------------------- - * - * Copyright (C) 2010-2016 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 GUI_UTILS_H -#define GUI_UTILS_H - -#include -#include -#include -#include -#ifdef __APPLE__ - #include // in osx, for basename() (but linux?) -#endif - -/* including stuff for the favicon */ - -#if defined(_WIN32) - #include "../ext/resource.h" -#elif defined(__linux__) - #include -#endif - - -using std::string; - - -/* refresh - * refresh all GUI elements. */ - -void gu_refresh(); - -/* getBlinker -* return blinker value, used to make widgets blink. */ - -int gu_getBlinker(); - -/* updateControls - * update attributes of control elements (sample names, volumes, ...). - * Useful when loading a new patch. */ - -void gu_updateControls(); - -/* update_win_label - * update the name of the main window */ - -void gu_update_win_label(const char *c); - -void gu_setFavicon(Fl_Window *w); - -void gu_openSubWindow(class gWindow *parent, gWindow *child, int id); - -/* refreshActionEditor - * reload the action editor window by closing and reopening it. It's used - * when you delete some actions from the mainWindow and the action editor - * window is open. */ - -void gu_refreshActionEditor(); - - -/* closeAllSubwindows - * close all subwindows attached to mainWin. */ - -void gu_closeAllSubwindows(); - - -/* getSubwindow - * return a pointer to an open subwindow, otherwise NULL. */ - -gWindow *gu_getSubwindow(class gWindow *parent, int id); - -/* removeFltkChars - * Strip special chars used by FLTK to split menus into sub-menus. */ - -string gu_removeFltkChars(const string &s); - -#endif diff --git a/src/utils/log.cpp b/src/utils/log.cpp index 1ebef9f..3724f03 100644 --- a/src/utils/log.cpp +++ b/src/utils/log.cpp @@ -1,10 +1,10 @@ -/* --------------------------------------------------------------------- +/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * log * - * --------------------------------------------------------------------- + * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual * @@ -24,27 +24,31 @@ * along with Giada - Your Hardcore Loopmachine. If not, see * . * - * ------------------------------------------------------------------ */ + * -------------------------------------------------------------------------- */ #include #include #include -#include "../utils/utils.h" +#include "../utils/fs.h" #include "../core/const.h" #include "log.h" +using std::string; + + static FILE *f; static int mode; static bool stat; -int gLog_init(int m) { +int gu_logInit(int m) +{ mode = m; stat = true; if (mode == LOG_MODE_FILE) { - std::string fpath = gGetHomePath() + "/giada.log"; + string fpath = gu_getHomePath() + G_SLASH + "giada.log"; f = fopen(fpath.c_str(), "a"); if (!f) { stat = false; @@ -55,19 +59,21 @@ int gLog_init(int m) { } -/* ------------------------------------------------------------------ */ +/* -------------------------------------------------------------------------- */ -void gLog_close() { +void gu_logClose() +{ if (mode == LOG_MODE_FILE) fclose(f); } -/* ------------------------------------------------------------------ */ +/* -------------------------------------------------------------------------- */ -void gLog(const char *format, ...) { +void gu_log(const char *format, ...) +{ if (mode == LOG_MODE_MUTE) return; va_list args; diff --git a/src/utils/log.h b/src/utils/log.h index 285a7d6..14ece29 100644 --- a/src/utils/log.h +++ b/src/utils/log.h @@ -1,10 +1,10 @@ -/* --------------------------------------------------------------------- +/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * log * - * --------------------------------------------------------------------- + * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual * @@ -24,7 +24,7 @@ * along with Giada - Your Hardcore Loopmachine. If not, see * . * - * ------------------------------------------------------------------ */ + * -------------------------------------------------------------------------- */ #ifndef __LOG_H__ @@ -34,12 +34,12 @@ /* init * init logger. Mode defines where to write the output: LOG_MODE_STDOUT, * LOG_MODE_FILE and LOG_MODE_MUTE. */ - -int gLog_init (int mode); -void gLog_close(); +int gu_logInit(int mode); -void gLog(const char *format, ...); +void gu_logClose(); + +void gu_log(const char *format, ...); #endif diff --git a/src/utils/string.cpp b/src/utils/string.cpp index 167ed83..603693e 100644 --- a/src/utils/string.cpp +++ b/src/utils/string.cpp @@ -27,14 +27,15 @@ * -------------------------------------------------------------------------- */ -#include "string.h" #include +#include +#include "string.h" using std::string; -string gGetRealPath(const string &path) +string gu_getRealPath(const string &path) { string out = ""; @@ -54,3 +55,59 @@ string gGetRealPath(const string &path) } return out; } + + +/* -------------------------------------------------------------------------- */ + + +string gu_itoa(int i) +{ + std::stringstream out; + out << i; + return out.str(); +} + + +/* -------------------------------------------------------------------------- */ + + +string gu_trim(const string &s) +{ + std::size_t first = s.find_first_not_of(" \n\t"); + std::size_t last = s.find_last_not_of(" \n\t"); + return s.substr(first, last-first+1); +} + + +/* -------------------------------------------------------------------------- */ + + +string gu_replace(string in, const string &search, const string &replace) +{ + size_t pos = 0; + while ((pos = in.find(search, pos)) != string::npos) { + in.replace(pos, search.length(), replace); + pos += replace.length(); + } + return in; +} + + +/* -------------------------------------------------------------------------- */ + + +void gu_split(string in, string sep, vector *v) +{ + string full = in; + string token = ""; + size_t curr = 0; + size_t next = -1; + do { + curr = next + 1; + next = full.find_first_of(sep, curr); + token = full.substr(curr, next - curr); + if (token != "") + v->push_back(token); + } + while (next != string::npos); +} diff --git a/src/utils/string.h b/src/utils/string.h index 758b742..88df1af 100644 --- a/src/utils/string.h +++ b/src/utils/string.h @@ -32,14 +32,22 @@ #include -#include #include -#include "log.h" using std::string; +using std::vector; -string gGetRealPath(const string &path); +string gu_getRealPath(const string &path); + +string gu_replace(string in, const string &search, const string &replace); + +string gu_trim(const string &s); + +string gu_itoa(int i); + +void gu_split(string in, string sep, vector *v); + #endif diff --git a/src/utils/utils.cpp b/src/utils/utils.cpp deleted file mode 100644 index 16e00f4..0000000 --- a/src/utils/utils.cpp +++ /dev/null @@ -1,403 +0,0 @@ -/* ----------------------------------------------------------------------------- - * - * Giada - Your Hardcore Loopmachine - * - * utils - * - * ----------------------------------------------------------------------------- - * - * Copyright (C) 2010-2016 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 - * . - * - * -------------------------------------------------------------------------- */ - - -#if defined(_WIN32) // getcwd (unix) or __getcwd (win) - #include - #include -#else - #include -#endif - -#include -#include // stat (gDirExists) -#include -#include -#include -#include -#include -#include -#include -#if defined(__APPLE__) - #include // basename unix - #include // getpwuid -#endif -#include "../core/const.h" -#include "utils.h" - - -using std::string; -using std::vector; - - -bool gFileExists(const char *filename) -{ - FILE *fh = fopen(filename, "rb"); - if (!fh) { - return 0; - } - else { - fclose(fh); - return 1; - } -} - - -bool gFileExists(const string &filename) -{ - return gFileExists(filename.c_str()); -} - - -/* -------------------------------------------------------------------------- */ - - -bool gIsDir(const char *path) -{ - bool ret; - -#if defined(__linux__) - - struct stat s1; - stat(path, &s1); - ret = S_ISDIR(s1.st_mode); - -#elif defined(__APPLE__) - - if (strcmp(path, "")==0) - ret = false; - else { - struct stat s1; - stat(path, &s1); - ret = S_ISDIR(s1.st_mode); - - /* check if ret is a bundle, a special OS X folder which must be - * shown as a regular file (VST). - * FIXME - consider native functions CFBundle... */ - - if (ret) { - string tmp = path; - tmp += "/Contents/Info.plist"; - if (gFileExists(tmp.c_str())) - ret = false; - } - } - -#elif defined(__WIN32) - - unsigned dwAttrib = GetFileAttributes(path); - ret = (dwAttrib != INVALID_FILE_ATTRIBUTES && - (dwAttrib & FILE_ATTRIBUTE_DIRECTORY)); -#endif - - return ret & !gIsProject(path); -} - - -bool gIsDir(const string &path) -{ - return gIsDir(path.c_str()); -} - -/* -------------------------------------------------------------------------- */ - - -bool gDirExists(const char *path) -{ - struct stat st; - if (stat(path, &st) != 0 && errno == ENOENT) - return false; - return true; -} - - -bool gDirExists(const string &path) -{ - return gDirExists(path.c_str()); -} - - -/* -------------------------------------------------------------------------- */ - - -bool gMkdir(const char *path) -{ -#if defined(__linux__) || defined(__APPLE__) - if (mkdir(path, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) == 0) -#else - if (_mkdir(path) == 0) -#endif - return true; - return false; -} - - -bool gMkdir(const string &path) -{ - return gMkdir(path.c_str()); -} - - -/* -------------------------------------------------------------------------- */ - - -string gBasename(const string &s) -{ - string out = s; - out.erase(0, out.find_last_of(G_SLASH_STR) + 1); - return out; -} - - -/* -------------------------------------------------------------------------- */ - - -string gDirname(const string &path) -{ - if (path.empty()) - return ""; - string out = path; - out.erase(out.find_last_of(G_SLASH_STR)); - return out; -} - - -/* -------------------------------------------------------------------------- */ - - -string gGetCurrentPath() -{ - char buf[PATH_MAX]; -#if defined(__WIN32) - if (_getcwd(buf, PATH_MAX) != NULL) -#else - if (getcwd(buf, PATH_MAX) != NULL) -#endif - return buf; - else - return ""; -} - - -/* -------------------------------------------------------------------------- */ - - -string gGetExt(const char *file) -{ - int len = strlen(file); - int pos = len; - while (pos>0) { - if (file[pos] == '.') - break; - pos--; - } - if (pos==0) - return ""; - string out = file; - return out.substr(pos+1, len); -} - - -/* -------------------------------------------------------------------------- */ - - -string gStripExt(const char *file) -{ - int len = strlen(file); - int pos = -1; - for (int i=0; i=0) { /// TODO - use gGetSlash() -#if defined(__linux__) || defined(__APPLE__) - if (out[i] == '/') -#elif defined(_WIN32) - if (out[i] == '\\') -#endif - break; - i--; - } - - out.erase(0, i+1); // includes the '/' (or '\' on windows) - return out; -} - - - -/* -------------------------------------------------------------------------- */ - - -string gItoa(int i) -{ - std::stringstream out; - out << i; - return out.str(); -} - - -/* -------------------------------------------------------------------------- */ - - -string gTrim(const char *f) -{ - string out = f; - return gTrim(out); -} - - -string gTrim(const string &s) -{ - std::size_t first = s.find_first_not_of(" \n\t"); - std::size_t last = s.find_last_not_of(" \n\t"); - return s.substr(first, last-first+1); -} - - -/* -------------------------------------------------------------------------- */ - - -string gReplace(string in, const string& search, const string& replace) -{ - size_t pos = 0; - while ((pos = in.find(search, pos)) != string::npos) { - in.replace(pos, search.length(), replace); - pos += replace.length(); - } - return in; -} - - -/* -------------------------------------------------------------------------- */ - - -string gStripFileUrl(const char *f) -{ - string out = f; - out = gReplace(out, "file://", ""); - out = gReplace(out, "%20", " "); - return out; -} - - -/* -------------------------------------------------------------------------- */ - - -string gGetHomePath() -{ - char path[PATH_MAX]; - -#if defined(__linux__) - - snprintf(path, PATH_MAX, "%s/.giada", getenv("HOME")); - -#elif defined(_WIN32) - - snprintf(path, PATH_MAX, "."); - -#elif defined(__APPLE__) - - struct passwd *p = getpwuid(getuid()); - if (p == NULL) { - gLog("[gGetHomePath] unable to fetch user infos\n"); - return ""; - } - else { - const char *home = p->pw_dir; - snprintf(path, PATH_MAX, "%s/Library/Application Support/Giada", home); - } - -#endif - - return string(path); -} - - -/* -------------------------------------------------------------------------- */ - - -void gSplit(string in, string sep, vector *v) -{ - string full = in; - string token = ""; - size_t curr = 0; - size_t next = -1; - do { - curr = next + 1; - next = full.find_first_of(sep, curr); - token = full.substr(curr, next - curr); - if (token != "") - v->push_back(token); - } - while (next != string::npos); -} diff --git a/src/utils/utils.h b/src/utils/utils.h deleted file mode 100644 index dd6ecd0..0000000 --- a/src/utils/utils.h +++ /dev/null @@ -1,87 +0,0 @@ -/* ----------------------------------------------------------------------------- - * - * Giada - Your Hardcore Loopmachine - * - * utils - * - * ----------------------------------------------------------------------------- - * - * Copyright (C) 2010-2016 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 UTILS_H -#define UTILS_H - - -#include -#include -#include -#include "log.h" - - -using std::string; -using std::vector; - - -bool gFileExists(const char *path); -bool gFileExists(const string &path); - -bool gDirExists(const char *path); -bool gDirExists(const string &path); - -bool gIsDir(const char *path); -bool gIsDir(const string &path); - -bool gIsProject(const string &path); - -bool gIsPatch(const char *path); -bool gIsPatch(const string &path); - -bool gMkdir(const char *path); -bool gMkdir(const string &path); - -string gBasename(const string &s); - -string gReplace(string in, const string& search, const string& replace); - -string gDirname(const string &s); - -string gTrim(const char *path); -string gTrim(const string &s); - -string gGetCurrentPath(); - -string gGetHomePath(); - -string gStripFileUrl(const char *path); - -string gGetExt(const char *path); - -string gStripExt(const char *path); -string gStripExt(const string &s); - -string gGetProjectName(const char *path); // TODO - useless! - -string gItoa(int i); - -void gSplit(string in, string sep, vector *v); - -#endif diff --git a/tests/patch.cpp b/tests/patch.cpp index 8ff9942..991ff1f 100644 --- a/tests/patch.cpp +++ b/tests/patch.cpp @@ -69,6 +69,7 @@ TEST_CASE("Test Patch class") channel1.midiInKeyPress = UINT32_MAX; // check maximum value channel1.midiInKeyRel = 1; channel1.midiInKill = 2; + channel1.midiInArm = 11; channel1.midiInVolume = 3; channel1.midiInMute = 4; channel1.midiInSolo = 5; @@ -157,6 +158,7 @@ TEST_CASE("Test Patch class") REQUIRE(channel0.midiInKeyPress == UINT32_MAX); REQUIRE(channel0.midiInKeyRel == 1); REQUIRE(channel0.midiInKill == 2); + REQUIRE(channel0.midiInArm == 11); REQUIRE(channel0.midiInVolume == 3); REQUIRE(channel0.midiInMute == 4); REQUIRE(channel0.midiInSolo == 5); diff --git a/tests/pluginHost.cpp b/tests/pluginHost.cpp index e1f071b..3ae679c 100644 --- a/tests/pluginHost.cpp +++ b/tests/pluginHost.cpp @@ -1,6 +1,8 @@ #ifdef WITH_VST #ifdef RUN_TESTS_WITH_LOCAL_FILES +// temporarily disabled due to entangled deps (WIP) +#if 0 #include "../src/core/pluginHost.h" #include "catch.hpp" @@ -26,5 +28,7 @@ TEST_CASE("Test PluginHost class") } } +#endif + #endif #endif diff --git a/tests/utils.cpp b/tests/utils.cpp index b7c88b3..0f54920 100644 --- a/tests/utils.cpp +++ b/tests/utils.cpp @@ -1,4 +1,5 @@ -#include "../src/utils/utils.h" +#include "../src/utils/fs.h" +#include "../src/utils/string.h" #include "catch.hpp" @@ -7,27 +8,27 @@ using std::vector; TEST_CASE("Test filesystem utils") { - REQUIRE(gFileExists("giada_tests") == true); - REQUIRE(gFileExists("ghost_file") == false); - REQUIRE(gDirExists("src/") == true); - REQUIRE(gDirExists("ghost_dir/") == false); - REQUIRE(gIsDir("src/") == true); - REQUIRE(gIsDir("giada_tests") == false); - REQUIRE(gBasename("tests/utils.cpp") == "utils.cpp"); - REQUIRE(gDirname("tests/utils.cpp") == "tests"); - REQUIRE(gGetExt("tests/utils.cpp") == "cpp"); - REQUIRE(gStripExt("tests/utils.cpp") == "tests/utils"); + REQUIRE(gu_fileExists("giada_tests") == true); + REQUIRE(gu_fileExists("ghost_file") == false); + REQUIRE(gu_dirExists("src/") == true); + REQUIRE(gu_dirExists("ghost_dir/") == false); + REQUIRE(gu_isDir("src/") == true); + REQUIRE(gu_isDir("giada_tests") == false); + REQUIRE(gu_basename("tests/utils.cpp") == "utils.cpp"); + REQUIRE(gu_dirname("tests/utils.cpp") == "tests"); + REQUIRE(gu_getExt("tests/utils.cpp") == "cpp"); + REQUIRE(gu_stripExt("tests/utils.cpp") == "tests/utils"); } TEST_CASE("Test string utils") { - REQUIRE(gReplace("Giada is cool", "cool", "hot") == "Giada is hot"); - REQUIRE(gTrim(" Giada is cool ") == "Giada is cool"); - REQUIRE(gItoa(666) == "666"); + REQUIRE(gu_replace("Giada is cool", "cool", "hot") == "Giada is hot"); + REQUIRE(gu_trim(" Giada is cool ") == "Giada is cool"); + REQUIRE(gu_itoa(666) == "666"); vector v; - gSplit("Giada is cool", " ", &v); + gu_split("Giada is cool", " ", &v); REQUIRE(v.size() == 3); REQUIRE(v.at(0) == "Giada"); REQUIRE(v.at(1) == "is");