From 118ce961714dc6205bc58c1ce2d699d30b001497 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Jarom=C3=ADr=20Mike=C5=A1?= Date: Sat, 25 Nov 2017 15:57:44 +0100 Subject: [PATCH] New upstream version 0.14.4~dfsg1 --- ChangeLog | 22 + Makefile.am | 73 +-- README.md | 4 +- src/core/channel.cpp | 72 ++- src/core/channel.h | 144 +++--- src/core/conf.cpp | 9 +- src/core/conf.h | 1 + src/core/const.h | 9 +- src/core/init.cpp | 1 + src/core/kernelMidi.cpp | 102 ++-- src/core/midiChannel.h | 18 +- src/core/mixer.cpp | 2 +- src/core/mixerHandler.cpp | 29 +- src/core/mixerHandler.h | 28 +- src/core/patch.cpp | 4 + src/core/patch.h | 2 + src/core/plugin.cpp | 155 +++--- src/core/plugin.h | 109 +++-- src/core/pluginHost.cpp | 450 ++++++++++-------- src/core/pluginHost.h | 41 +- src/core/sampleChannel.cpp | 49 +- src/core/sampleChannel.h | 70 ++- src/core/storager.cpp | 139 +++--- src/core/wave.cpp | 47 +- src/core/wave.h | 11 +- src/core/waveFx.cpp | 20 +- src/core/waveFx.h | 2 + src/core/waveManager.cpp | 3 - src/deps/juce-config.h | 4 +- src/glue/channel.cpp | 68 ++- src/glue/channel.h | 54 +-- src/glue/io.cpp | 6 +- src/glue/plugin.cpp | 47 +- src/glue/plugin.h | 16 +- src/glue/sampleEditor.cpp | 23 +- src/glue/sampleEditor.h | 1 + src/glue/storage.cpp | 138 +++--- .../{gd_beatsInput.cpp => beatsInput.cpp} | 37 +- .../dialogs/{gd_beatsInput.h => beatsInput.h} | 22 +- .../dialogs/{gd_bpmInput.cpp => bpmInput.cpp} | 37 +- src/gui/dialogs/{gd_bpmInput.h => bpmInput.h} | 12 +- src/gui/dialogs/browser/browserBase.cpp | 33 +- src/gui/dialogs/browser/browserBase.h | 43 +- src/gui/dialogs/browser/browserLoad.cpp | 17 +- src/gui/dialogs/browser/browserLoad.h | 13 +- src/gui/dialogs/browser/browserSave.cpp | 18 +- src/gui/dialogs/browser/browserSave.h | 17 +- src/gui/dialogs/channelNameInput.cpp | 101 ++++ src/gui/dialogs/channelNameInput.h | 61 +++ src/gui/dialogs/gd_pluginWindow.cpp | 127 ----- ...gd_pluginChooser.cpp => pluginChooser.cpp} | 7 +- .../{gd_pluginChooser.h => pluginChooser.h} | 0 .../{gd_pluginList.cpp => pluginList.cpp} | 136 +++--- .../dialogs/{gd_pluginList.h => pluginList.h} | 0 src/gui/dialogs/pluginWindow.cpp | 97 ++++ .../{gd_pluginWindow.h => pluginWindow.h} | 35 +- ...luginWindowGUI.cpp => pluginWindowGUI.cpp} | 2 +- ...gd_pluginWindowGUI.h => pluginWindowGUI.h} | 6 +- src/gui/dialogs/sampleEditor.cpp | 50 +- src/gui/dialogs/sampleEditor.h | 17 +- src/gui/dialogs/window.h | 22 +- src/gui/elems/basics/liquidScroll.cpp | 28 +- src/gui/elems/config/tabAudio.cpp | 24 +- src/gui/elems/config/tabMidi.cpp | 14 +- src/gui/elems/config/tabMisc.cpp | 2 +- src/gui/elems/mainWindow/keyboard/channel.cpp | 30 +- src/gui/elems/mainWindow/keyboard/channel.h | 19 +- .../mainWindow/keyboard/channelButton.cpp | 65 +-- .../elems/mainWindow/keyboard/channelButton.h | 8 +- .../mainWindow/keyboard/channelStatus.cpp | 2 +- .../elems/mainWindow/keyboard/midiChannel.cpp | 82 ++-- .../elems/mainWindow/keyboard/midiChannel.h | 23 +- .../mainWindow/keyboard/midiChannelButton.cpp | 44 ++ .../mainWindow/keyboard/midiChannelButton.h | 43 ++ .../mainWindow/keyboard/sampleChannel.cpp | 46 +- .../keyboard/sampleChannelButton.cpp | 10 +- .../mainWindow/keyboard/sampleChannelButton.h | 2 +- src/gui/elems/mainWindow/mainIO.cpp | 2 +- src/gui/elems/mainWindow/mainMenu.cpp | 2 +- src/gui/elems/mainWindow/mainTimer.cpp | 4 +- src/gui/elems/{ => plugin}/pluginBrowser.cpp | 8 +- src/gui/elems/{ => plugin}/pluginBrowser.h | 0 src/gui/elems/plugin/pluginParameter.cpp | 99 ++++ src/gui/elems/plugin/pluginParameter.h | 69 +++ src/gui/elems/sampleEditor/rangeTool.cpp | 52 +- src/gui/elems/sampleEditor/shiftTool.cpp | 107 +++++ src/gui/elems/sampleEditor/shiftTool.h | 66 +++ src/gui/elems/sampleEditor/waveform.cpp | 2 +- src/utils/fs.h | 37 +- src/utils/string.cpp | 8 +- src/utils/string.h | 10 +- src/utils/time.cpp | 13 +- tests/wave.cpp | 89 ++-- 93 files changed, 2398 insertions(+), 1495 deletions(-) rename src/gui/dialogs/{gd_beatsInput.cpp => beatsInput.cpp} (76%) rename src/gui/dialogs/{gd_beatsInput.h => beatsInput.h} (84%) rename src/gui/dialogs/{gd_bpmInput.cpp => bpmInput.cpp} (77%) rename src/gui/dialogs/{gd_bpmInput.h => bpmInput.h} (86%) create mode 100644 src/gui/dialogs/channelNameInput.cpp create mode 100644 src/gui/dialogs/channelNameInput.h delete mode 100644 src/gui/dialogs/gd_pluginWindow.cpp rename src/gui/dialogs/{gd_pluginChooser.cpp => pluginChooser.cpp} (96%) rename src/gui/dialogs/{gd_pluginChooser.h => pluginChooser.h} (100%) rename src/gui/dialogs/{gd_pluginList.cpp => pluginList.cpp} (72%) rename src/gui/dialogs/{gd_pluginList.h => pluginList.h} (100%) create mode 100644 src/gui/dialogs/pluginWindow.cpp rename src/gui/dialogs/{gd_pluginWindow.h => pluginWindow.h} (73%) rename src/gui/dialogs/{gd_pluginWindowGUI.cpp => pluginWindowGUI.cpp} (98%) rename src/gui/dialogs/{gd_pluginWindowGUI.h => pluginWindowGUI.h} (97%) create mode 100644 src/gui/elems/mainWindow/keyboard/midiChannelButton.cpp create mode 100644 src/gui/elems/mainWindow/keyboard/midiChannelButton.h rename src/gui/elems/{ => plugin}/pluginBrowser.cpp (96%) rename src/gui/elems/{ => plugin}/pluginBrowser.h (100%) create mode 100644 src/gui/elems/plugin/pluginParameter.cpp create mode 100644 src/gui/elems/plugin/pluginParameter.h create mode 100644 src/gui/elems/sampleEditor/shiftTool.cpp create mode 100644 src/gui/elems/sampleEditor/shiftTool.h diff --git a/ChangeLog b/ChangeLog index 01102c8..cf11e2b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -12,10 +12,32 @@ -------------------------------------------------------------------------------- +0.14.4 --- 2017 . 10 . 28 +- Renameable channels +- Portable VST path +- [Sample Editor] Sample shift tool +- [Linux/Mac] Don't skip '/' path when navigating to upper folders +- Ability to process more than one plug-in instrument at once +- Beautify Configuration Window +- Bring VST window to front when opening UI +- Save 'arm' status to patch/project file +- Revamped Beats and Bpm input windows +- Simplified audio samples' storage in project folders +- Update JUCE to version 5.1.2 +- UI-less plug-in window refinements +- Update UI-less plug-in window on MIDI parameter's change +- Strip .gptc/.gprj extention from patch name +- [Sample Editor] Fix non-working 'cut' operation +- Fix missed MIDI events with more than 1 plug-in in the stack +- Fix File Browser path widget drawn incorrectly in OS X +- Fix missing MIDI learn for 'Arm channel' and 'Kill channel' + + 0.14.3 --- 2017 . 09 . 18 - [Sample Editor] New "reverse selection" function - [Sample Editor] New "normalize hard" function - [Sample Editor] New "copy to channel" function +- [Sample Editor] New "copy & paste" function - [Sample Editor] Double click on waveform selects all - [Sample Editor] Fix garbled characters in window's title - [Sample Editor] Fix wrong result on "set pitch to song/bar" diff --git a/Makefile.am b/Makefile.am index 2e2a260..e008f62 100644 --- a/Makefile.am +++ b/Makefile.am @@ -71,28 +71,30 @@ src/gui/dialogs/gd_about.h \ src/gui/dialogs/gd_about.cpp \ src/gui/dialogs/gd_mainWindow.h \ src/gui/dialogs/gd_mainWindow.cpp \ -src/gui/dialogs/gd_beatsInput.h \ -src/gui/dialogs/gd_beatsInput.cpp \ +src/gui/dialogs/beatsInput.h \ +src/gui/dialogs/beatsInput.cpp \ src/gui/dialogs/gd_warnings.h \ src/gui/dialogs/gd_warnings.cpp \ -src/gui/dialogs/gd_bpmInput.h \ -src/gui/dialogs/gd_bpmInput.cpp \ +src/gui/dialogs/bpmInput.h \ +src/gui/dialogs/bpmInput.cpp \ +src/gui/dialogs/channelNameInput.h \ +src/gui/dialogs/channelNameInput.cpp \ src/gui/dialogs/gd_config.h \ src/gui/dialogs/gd_config.cpp \ src/gui/dialogs/gd_devInfo.h \ src/gui/dialogs/gd_devInfo.cpp \ -src/gui/dialogs/gd_pluginList.h \ -src/gui/dialogs/gd_pluginList.cpp \ -src/gui/dialogs/gd_pluginWindow.h \ -src/gui/dialogs/gd_pluginWindow.cpp \ +src/gui/dialogs/pluginList.h \ +src/gui/dialogs/pluginList.cpp \ +src/gui/dialogs/pluginWindow.h \ +src/gui/dialogs/pluginWindow.cpp \ src/gui/dialogs/sampleEditor.h \ src/gui/dialogs/sampleEditor.cpp \ -src/gui/dialogs/gd_pluginWindowGUI.h \ -src/gui/dialogs/gd_pluginWindowGUI.cpp \ +src/gui/dialogs/pluginWindowGUI.h \ +src/gui/dialogs/pluginWindowGUI.cpp \ src/gui/dialogs/gd_actionEditor.h \ src/gui/dialogs/gd_actionEditor.cpp \ -src/gui/dialogs/gd_pluginChooser.h \ -src/gui/dialogs/gd_pluginChooser.cpp \ +src/gui/dialogs/pluginChooser.h \ +src/gui/dialogs/pluginChooser.cpp \ src/gui/dialogs/browser/browserBase.h \ src/gui/dialogs/browser/browserBase.cpp \ src/gui/dialogs/browser/browserLoad.h \ @@ -117,8 +119,10 @@ src/gui/elems/browser.h \ src/gui/elems/browser.cpp \ src/gui/elems/soundMeter.h \ src/gui/elems/soundMeter.cpp \ -src/gui/elems/pluginBrowser.h \ -src/gui/elems/pluginBrowser.cpp \ +src/gui/elems/plugin/pluginBrowser.h \ +src/gui/elems/plugin/pluginBrowser.cpp \ +src/gui/elems/plugin/pluginParameter.h \ +src/gui/elems/plugin/pluginParameter.cpp \ src/gui/elems/sampleEditor/waveform.h \ src/gui/elems/sampleEditor/waveform.cpp \ src/gui/elems/sampleEditor/waveTools.h \ @@ -133,6 +137,8 @@ src/gui/elems/sampleEditor/pitchTool.h \ src/gui/elems/sampleEditor/pitchTool.cpp \ src/gui/elems/sampleEditor/rangeTool.h \ src/gui/elems/sampleEditor/rangeTool.cpp \ +src/gui/elems/sampleEditor/shiftTool.h \ +src/gui/elems/sampleEditor/shiftTool.cpp \ src/gui/elems/actionEditor/baseActionEditor.h \ src/gui/elems/actionEditor/baseActionEditor.cpp \ src/gui/elems/actionEditor/envelopeEditor.h \ @@ -165,24 +171,26 @@ src/gui/elems/mainWindow/mainTransport.h \ src/gui/elems/mainWindow/mainTransport.cpp \ src/gui/elems/mainWindow/beatMeter.h \ src/gui/elems/mainWindow/beatMeter.cpp \ -src/gui/elems/mainWindow/keyboard/channelMode.h \ -src/gui/elems/mainWindow/keyboard/channelMode.cpp \ -src/gui/elems/mainWindow/keyboard/channelButton.h \ -src/gui/elems/mainWindow/keyboard/channelButton.cpp \ -src/gui/elems/mainWindow/keyboard/channelStatus.h \ -src/gui/elems/mainWindow/keyboard/channelStatus.cpp \ -src/gui/elems/mainWindow/keyboard/keyboard.h \ -src/gui/elems/mainWindow/keyboard/keyboard.cpp \ -src/gui/elems/mainWindow/keyboard/column.h \ -src/gui/elems/mainWindow/keyboard/column.cpp \ -src/gui/elems/mainWindow/keyboard/sampleChannel.h \ -src/gui/elems/mainWindow/keyboard/sampleChannel.cpp \ -src/gui/elems/mainWindow/keyboard/midiChannel.h \ -src/gui/elems/mainWindow/keyboard/midiChannel.cpp \ -src/gui/elems/mainWindow/keyboard/channel.h \ -src/gui/elems/mainWindow/keyboard/channel.cpp \ -src/gui/elems/mainWindow/keyboard/sampleChannelButton.h \ -src/gui/elems/mainWindow/keyboard/sampleChannelButton.cpp \ +src/gui/elems/mainWindow/keyboard/channelMode.h \ +src/gui/elems/mainWindow/keyboard/channelMode.cpp \ +src/gui/elems/mainWindow/keyboard/channelButton.h \ +src/gui/elems/mainWindow/keyboard/channelButton.cpp \ +src/gui/elems/mainWindow/keyboard/channelStatus.h \ +src/gui/elems/mainWindow/keyboard/channelStatus.cpp \ +src/gui/elems/mainWindow/keyboard/keyboard.h \ +src/gui/elems/mainWindow/keyboard/keyboard.cpp \ +src/gui/elems/mainWindow/keyboard/column.h \ +src/gui/elems/mainWindow/keyboard/column.cpp \ +src/gui/elems/mainWindow/keyboard/sampleChannel.h \ +src/gui/elems/mainWindow/keyboard/sampleChannel.cpp \ +src/gui/elems/mainWindow/keyboard/midiChannel.h \ +src/gui/elems/mainWindow/keyboard/midiChannel.cpp \ +src/gui/elems/mainWindow/keyboard/channel.h \ +src/gui/elems/mainWindow/keyboard/channel.cpp \ +src/gui/elems/mainWindow/keyboard/sampleChannelButton.h \ +src/gui/elems/mainWindow/keyboard/sampleChannelButton.cpp \ +src/gui/elems/mainWindow/keyboard/midiChannelButton.h \ +src/gui/elems/mainWindow/keyboard/midiChannelButton.cpp \ src/gui/elems/config/tabMisc.h \ src/gui/elems/config/tabMisc.cpp \ src/gui/elems/config/tabMidi.h \ @@ -359,6 +367,7 @@ src/core/storager.cpp \ src/core/recorder.cpp \ src/utils/fs.cpp \ src/utils/string.cpp \ +src/utils/time.cpp \ src/utils/log.cpp if WITH_VST diff --git a/README.md b/README.md index 4b36cda..9da5e2a 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ Giada is a free, minimal, hardcore audio tool for DJs, live performers and elect * unlimited number of channels (controllable via computer keyboard); * several playback modes and combinations; * BPM and beat sync with sample-accurate loop engine; -* VST and VSTi (instrument) plugin support; +* VST and VSTi (instrument) plug-in support; * MIDI input and output support, featuring custom [MIDI lightning messages](https://github.com/monocasual/giada-midimaps); * super-sleek, built-in wave editor; * live sampler from external inputs; @@ -49,6 +49,8 @@ Found a typo or a terrible mistake? Feel free to clone the [website repository]( We do our best to make the compilation process as simple as possible. You can find all the information in the [official docs page](http://giadamusic.com/documentation/show/compiling-from-source). +Something went wrong? Try our new [Docker image](https://github.com/monocasual/giada-docker) for building and running Giada without hurdles. + ## Bugs, requests and questions for non-developers Feel free to ask anything on our end-user forum: diff --git a/src/core/channel.cpp b/src/core/channel.cpp index d4bded3..95aa43e 100644 --- a/src/core/channel.cpp +++ b/src/core/channel.cpp @@ -52,8 +52,9 @@ using namespace giada::m; Channel::Channel(int type, int status, int bufferSize) : bufferSize (bufferSize), midiFilter (-1), - pan (0.5f), previewMode (G_PREVIEW_NONE), + pan (0.5f), + armed (false), type (type), status (status), key (0), @@ -66,7 +67,6 @@ Channel::Channel(int type, int status, int bufferSize) solo (false), hasActions (false), readActions (false), - armed (false), recStatus (REC_STOPPED), vChan (nullptr), guiChannel (nullptr), @@ -187,7 +187,7 @@ void Channel::sendMidiLmessage(uint32_t learn, const midimap::message_t &msg) /* -------------------------------------------------------------------------- */ -bool Channel::isPlaying() +bool Channel::isPlaying() const { return status & (STATUS_PLAY | STATUS_ENDING); } @@ -202,7 +202,9 @@ int Channel::writePatch(int i, bool isProject) pch.type = type; pch.index = index; pch.size = guiChannel->getSize(); + pch.name = name; pch.key = key; + pch.armed = armed; pch.column = guiChannel->getColumnIndex(); pch.mute = mute; pch.mute_s = mute_s; @@ -224,7 +226,7 @@ int Channel::writePatch(int i, bool isProject) for (unsigned i=0; ichan == index) { patch::action_t pac; pac.type = action->type; @@ -240,7 +242,7 @@ int Channel::writePatch(int i, bool isProject) unsigned numPlugs = pluginHost::countPlugins(pluginHost::CHANNEL, this); for (unsigned i=0; igetUniqueId(); pp.bypass = pPlugin->isBypassed(); @@ -262,13 +264,15 @@ int Channel::writePatch(int i, bool isProject) /* -------------------------------------------------------------------------- */ -int Channel::readPatch(const string &path, int i, pthread_mutex_t *pluginMutex, +int Channel::readPatch(const string& path, int i, pthread_mutex_t* pluginMutex, int samplerate, int rsmpQuality) { int ret = 1; - patch::channel_t *pch = &patch::channels.at(i); + patch::channel_t* pch = &patch::channels.at(i); key = pch->key; + armed = pch->armed; type = pch->type; + name = pch->name; index = pch->index; mute = pch->mute; mute_s = pch->mute_s; @@ -287,28 +291,26 @@ int Channel::readPatch(const string &path, int i, pthread_mutex_t *pluginMutex, midiOutLmute = pch->midiOutLmute; midiOutLsolo = pch->midiOutLsolo; - 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); + for (const patch::action_t& ac : pch->actions) { + recorder::rec(index, ac.type, ac.frame, ac.iValue, ac.fValue); hasActions = true; } #ifdef WITH_VST - for (unsigned k=0; kplugins.size(); k++) { - patch::plugin_t *ppl = &pch->plugins.at(k); - Plugin *plugin = pluginHost::addPlugin(ppl->path, pluginHost::CHANNEL, + for (const patch::plugin_t& ppl : pch->plugins) { + Plugin* plugin = pluginHost::addPlugin(ppl.path, pluginHost::CHANNEL, pluginMutex, this); if (plugin == nullptr) { ret &= 0; continue; } - plugin->setBypass(ppl->bypass); - for (unsigned j=0; jparams.size(); j++) - plugin->setParameter(j, ppl->params.at(j)); + plugin->setBypass(ppl.bypass); + for (unsigned j=0; jsetParameter(j, ppl.params.at(j)); plugin->midiInParams.clear(); - for (unsigned j=0; jmidiInParams.size(); j++) - plugin->midiInParams.push_back(ppl->midiInParams.at(j)); + for (uint32_t midiInParam : ppl.midiInParams) + plugin->midiInParams.push_back(midiInParam); ret &= 1; } @@ -391,7 +393,7 @@ void Channel::setPan(float v) } -float Channel::getPan() +float Channel::getPan() const { return pan; } @@ -420,7 +422,7 @@ void Channel::setPreviewMode(int m) } -bool Channel::isPreview() +bool Channel::isPreview() const { return previewMode != G_PREVIEW_NONE; } @@ -429,6 +431,36 @@ bool Channel::isPreview() /* -------------------------------------------------------------------------- */ +void Channel::setArmed(bool b) +{ + armed = b; +} + + +bool Channel::isArmed() const +{ + return armed; +} + + +/* -------------------------------------------------------------------------- */ + + +std::string Channel::getName() const +{ + return name; +} + + +void Channel::setName(const std::string& s) +{ + name = s; +} + + +/* -------------------------------------------------------------------------- */ + + #ifdef WITH_VST juce::MidiBuffer &Channel::getPluginMidiEvents() diff --git a/src/core/channel.h b/src/core/channel.h index 6f96257..72e6f2d 100644 --- a/src/core/channel.h +++ b/src/core/channel.h @@ -49,18 +49,29 @@ class Channel { protected: + /* sendMidiLMessage + Composes a MIDI message by merging bytes from MidiMap conf class, and sends it + to KernelMidi. */ + + void sendMidiLmessage(uint32_t learn, const giada::m::midimap::message_t& msg); + + /* calcPanning + Given an audio channel (stereo: 0 or 1) computes the current panning value. */ + + float calcPanning(int ch); + #ifdef WITH_VST - /* MidiBuffer contains MIDI events. When ready, events are sent to - * each plugin in the channel. This is available for any kind of - * channel, but it makes sense only for MIDI channels. */ + /* MidiBuffer contains MIDI events. When ready, events are sent to each plugin + in the channel. This is available for any kind of channel, but it makes sense + only for MIDI channels. */ juce::MidiBuffer midiBuffer; #endif /* bufferSize - * size of every buffer in this channel (vChan, pChan) */ + Size of every buffer in this channel (vChan, pChan) */ int bufferSize; @@ -70,23 +81,14 @@ protected: int midiFilter; - float pan; - /* previewMode Whether the channel is in audio preview mode or not. */ int previewMode; - /* sendMidiLMessage - Composes a MIDI message by merging bytes from MidiMap conf class, and sends it - to KernelMidi. */ - - void sendMidiLmessage(uint32_t learn, const giada::m::midimap::message_t& msg); - - /* calcPanning - Given an audio channel (stereo: 0 or 1) computes the current panning value. */ - - float calcPanning(int ch); + float pan; + bool armed; + std::string name; public: @@ -95,12 +97,12 @@ public: virtual ~Channel(); /* copy - * Make a shallow copy (no vChan/pChan allocation) of another channel. */ + Makes a shallow copy (no vChan/pChan allocation) of another channel. */ virtual void copy(const Channel* src, pthread_mutex_t* pluginMutex) = 0; /* readPatch - * Fill channel with data from patch. */ + Fills channel with data from patch. */ virtual int readPatch(const std::string& basePath, int i, pthread_mutex_t* pluginMutex, int samplerate, int rsmpQuality); @@ -127,54 +129,53 @@ public: bool mixerIsRunning, bool forceStart, bool isUserGenerated) = 0; /* stop - * action to do when channel is stopped normally (via key or MIDI). */ + What to do when channel is stopped normally (via key or MIDI). */ virtual void stop() = 0; /* kill - * action to do when channel stops abruptly. */ + What to do when channel stops abruptly. */ virtual void kill(int frame) = 0; /* mute - * action to do when channel is muted. If internal == true, set - * internal mute without altering main mute. */ + What to do when channel is muted. If internal == true, set internal mute + without altering main mute. */ virtual void setMute (bool internal) = 0; virtual void unsetMute(bool internal) = 0; /* empty - * free any associated resources (e.g. waveform for SAMPLE). */ + Frees any associated resources (e.g. waveform for SAMPLE). */ virtual void empty() = 0; /* stopBySeq - * action to do when channel is stopped by sequencer. */ + What to do when channel is stopped by sequencer. */ virtual void stopBySeq(bool chansStopOnSeqHalt) = 0; /* quantize - * start channel according to quantizer. Index = array index of - * mixer::channels, used by recorder. LocalFrame = frame within the current - * buffer. */ + Starts channel according to quantizer. Index = array index of mixer::channels, + used by recorder. LocalFrame = frame within the current buffer. */ virtual void quantize(int index, int localFrame) = 0; /* onZero - * action to do when frame goes to zero, i.e. sequencer restart. */ + What to do when frame goes to zero, i.e. sequencer restart. */ virtual void onZero(int frame, bool recsStopOnChanHalt) = 0; /* onBar - * action to do when a bar has passed. */ + What to do when a bar has passed. */ virtual void onBar(int frame) = 0; /* parseAction - * do something on a recorded action. Parameters: - * action *a - action to parse - * localFrame - frame number of the processed buffer - * globalFrame - actual frame in Mixer */ + Does something on a recorded action. Parameters: + - action *a - action to parse + - localFrame - frame number of the processed buffer + - globalFrame - actual frame in Mixer */ // TODO - quantize is useless! @@ -182,7 +183,7 @@ public: int globalFrame, int quantize, bool mixerIsRunning) = 0; /* rewind - * rewind channel when rewind button is pressed. */ + Rewinds channel when rewind button is pressed. */ virtual void rewind() = 0; @@ -199,13 +200,13 @@ public: virtual bool canInputRec() = 0; /* writePatch - * Fill a patch with channel values. Returns the index of the last - * Patch::channel_t added. */ + Fills a patch with channel values. Returns the index of the last + Patch::channel_t added. */ virtual int writePatch(int i, bool isProject); /* receiveMidi - * Receives and processes midi messages from external devices. */ + Receives and processes midi messages from external devices. */ virtual void receiveMidi(uint32_t msg); @@ -215,7 +216,41 @@ public: virtual bool allocBuffers(); - /* ------------------------------------------------------------------------ */ + /* isPlaying + * tell wether the channel is playing or is stopped. */ + + bool isPlaying() const; + + /* sendMidiL* + * send MIDI lightning events to a physical device. */ + + void sendMidiLmute(); + void sendMidiLsolo(); + void sendMidiLplay(); + + void setPan(float v); + float getPan() const; + + void setArmed(bool b); + bool isArmed() const; + + std::string getName() const; + void setName(const std::string& s); + + void setPreviewMode(int m); + bool isPreview() const; + +#ifdef WITH_VST + + /* getPluginMidiEvents + * 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(); + + void clearMidiBuffer(); + +#endif int index; // unique id int type; // midi or sample @@ -230,7 +265,6 @@ public: bool solo; bool hasActions; // has something recorded bool readActions; // read what's recorded - bool armed; // armed for recording int recStatus; // status of recordings (waiting, ending, ...) float* vChan; // virtual channel geChannel* guiChannel; // pointer to a gChannel object, part of the GUI @@ -260,38 +294,6 @@ public: std::vector plugins; #endif - - /* ------------------------------------------------------------------------ */ - - /* isPlaying - * tell wether the channel is playing or is stopped. */ - - bool isPlaying(); - - /* sendMidiL* - * send MIDI lightning events to a physical device. */ - - void sendMidiLmute(); - void sendMidiLsolo(); - void sendMidiLplay(); - - void setPan(float v); - float getPan(); - - void setPreviewMode(int m); - bool isPreview(); - -#ifdef WITH_VST - - /* getPluginMidiEvents - * 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(); - - void clearMidiBuffer(); - -#endif }; diff --git a/src/core/conf.cpp b/src/core/conf.cpp index 39bc9ec..e32c289 100644 --- a/src/core/conf.cpp +++ b/src/core/conf.cpp @@ -238,6 +238,9 @@ int beatsY = 0; int aboutX = 0; int aboutY = 0; +int nameX = 0; +int nameY = 0; + #ifdef WITH_VST int pluginChooserX = 0; @@ -365,6 +368,8 @@ int read() if (!storager::setInt(jRoot, CONF_KEY_BEATS_Y, beatsY)) return 0; if (!storager::setInt(jRoot, CONF_KEY_ABOUT_X, aboutX)) return 0; if (!storager::setInt(jRoot, CONF_KEY_ABOUT_Y, aboutY)) return 0; + if (!storager::setInt(jRoot, CONF_KEY_NAME_X, nameX)) return 0; + if (!storager::setInt(jRoot, CONF_KEY_NAME_Y, nameY)) return 0; if (!storager::setInt(jRoot, CONF_KEY_MIDI_INPUT_X, midiInputX)) return 0; if (!storager::setInt(jRoot, CONF_KEY_MIDI_INPUT_Y, midiInputY)) return 0; if (!storager::setInt(jRoot, CONF_KEY_MIDI_INPUT_W, midiInputW)) return 0; @@ -470,7 +475,9 @@ int write() json_object_set_new(jRoot, CONF_KEY_BEATS_X, json_integer(beatsX)); json_object_set_new(jRoot, CONF_KEY_BEATS_Y, json_integer(beatsY)); json_object_set_new(jRoot, CONF_KEY_ABOUT_X, json_integer(aboutX)); - json_object_set_new(jRoot, CONF_KEY_ABOUT_Y, json_integer(aboutY)); + json_object_set_new(jRoot, CONF_KEY_ABOUT_Y, json_integer(aboutY)); + json_object_set_new(jRoot, CONF_KEY_NAME_X, json_integer(nameX)); + json_object_set_new(jRoot, CONF_KEY_NAME_Y, json_integer(nameY)); json_object_set_new(jRoot, CONF_KEY_MIDI_INPUT_X, json_integer(midiInputX)); json_object_set_new(jRoot, CONF_KEY_MIDI_INPUT_Y, json_integer(midiInputY)); json_object_set_new(jRoot, CONF_KEY_MIDI_INPUT_W, json_integer(midiInputW)); diff --git a/src/core/conf.h b/src/core/conf.h index 138579d..024c734 100644 --- a/src/core/conf.h +++ b/src/core/conf.h @@ -104,6 +104,7 @@ extern int configX, configY; extern int bpmX, bpmY; extern int beatsX, beatsY; extern int aboutX, aboutY; +extern int nameX, nameY; #ifdef WITH_VST diff --git a/src/core/const.h b/src/core/const.h index 3d3c223..7e48358 100644 --- a/src/core/const.h +++ b/src/core/const.h @@ -46,10 +46,10 @@ /* -- version --------------------------------------------------------------- */ #define G_APP_NAME "Giada" -#define G_VERSION_STR "0.14.3" +#define G_VERSION_STR "0.14.4" #define G_VERSION_MAJOR 0 #define G_VERSION_MINOR 14 -#define G_VERSION_PATCH 3 +#define G_VERSION_PATCH 4 #define CONF_FILENAME "giada.conf" @@ -257,6 +257,7 @@ #define WID_SAMPLE_EDITOR -8 #define WID_FX -9 #define WID_KEY_GRABBER -10 +#define WID_SAMPLE_NAME -11 @@ -359,6 +360,7 @@ const int MIDI_CHANS[16] = { #define PATCH_KEY_CHANNEL_TYPE "type" #define PATCH_KEY_CHANNEL_INDEX "index" #define PATCH_KEY_CHANNEL_SIZE "size" +#define PATCH_KEY_CHANNEL_NAME "name" #define PATCH_KEY_CHANNEL_COLUMN "column" #define PATCH_KEY_CHANNEL_MUTE "mute" #define PATCH_KEY_CHANNEL_MUTE_S "mute_s" @@ -392,6 +394,7 @@ const int MIDI_CHANS[16] = { #define PATCH_KEY_CHANNEL_MIDI_OUT_CHAN "midi_out_chan" #define PATCH_KEY_CHANNEL_PLUGINS "plugins" #define PATCH_KEY_CHANNEL_ACTIONS "actions" +#define PATCH_KEY_CHANNEL_ARMED "armed" #define PATCH_KEY_ACTION_TYPE "type" #define PATCH_KEY_ACTION_FRAME "frame" #define PATCH_KEY_ACTION_F_VALUE "f_value" @@ -479,6 +482,8 @@ const int MIDI_CHANS[16] = { #define CONF_KEY_BEATS_Y "beats_y" #define CONF_KEY_ABOUT_X "about_x" #define CONF_KEY_ABOUT_Y "about_y" +#define CONF_KEY_NAME_X "name_x" +#define CONF_KEY_NAME_Y "name_y" #define CONF_KEY_PLUGIN_CHOOSER_X "plugin_chooser_x" #define CONF_KEY_PLUGIN_CHOOSER_Y "plugin_chooser_y" #define CONF_KEY_PLUGIN_CHOOSER_W "plugin_chooser_w" diff --git a/src/core/init.cpp b/src/core/init.cpp index db18cbc..6104874 100644 --- a/src/core/init.cpp +++ b/src/core/init.cpp @@ -189,6 +189,7 @@ void init_shutdown() if (kernelAudio::getStatus()) { kernelAudio::closeDevice(); + gu_log("[init] KernelAudio closed\n"); mixer::close(); gu_log("[init] Mixer closed\n"); } diff --git a/src/core/kernelMidi.cpp b/src/core/kernelMidi.cpp index 41d57ad..8a19181 100644 --- a/src/core/kernelMidi.cpp +++ b/src/core/kernelMidi.cpp @@ -28,6 +28,7 @@ #include #include "../utils/log.h" #include "../glue/channel.h" +#include "../glue/plugin.h" #include "../glue/main.h" #include "../glue/transport.h" #include "../glue/io.h" @@ -57,8 +58,8 @@ namespace { bool status = false; int api = 0; -RtMidiOut *midiOut = nullptr; -RtMidiIn *midiIn = nullptr; +RtMidiOut* midiOut = nullptr; +RtMidiIn* midiIn = nullptr; unsigned numOutPorts = 0; unsigned numInPorts = 0; @@ -67,8 +68,8 @@ unsigned numInPorts = 0; * kernelMidi. It contains things to do once the midi message has been * stored. */ -cb_midiLearn *cb_learn = nullptr; -void *cb_data = nullptr; +cb_midiLearn* cb_learn = nullptr; +void* cb_data = nullptr; /* -------------------------------------------------------------------------- */ @@ -76,25 +77,24 @@ void *cb_data = nullptr; #ifdef WITH_VST -void processPlugins(Channel *ch, uint32_t pure, uint32_t value) +void processPlugins(Channel* ch, uint32_t pure, uint32_t value) { - /* Plugins' parameters layout reflect the structure of the matrix - Channel::midiInPlugins. It is safe to assume then that i and k indexes match - both the structure of Channel::midiInPlugins and vector *plugins. */ + /* Plugins' parameters layout reflects the structure of the matrix + Channel::midiInPlugins. It is safe to assume then that i (i.e. Plugin*) and k + indexes match both the structure of Channel::midiInPlugins and + vector* plugins. */ - vector *plugins = pluginHost::getStack(pluginHost::CHANNEL, ch); + vector* plugins = pluginHost::getStack(pluginHost::CHANNEL, ch); - for (unsigned i=0; isize(); i++) - { - Plugin *plugin = plugins->at(i); + for (Plugin* plugin : *plugins) { for (unsigned k=0; kmidiInParams.size(); k++) { uint32_t midiInParam = plugin->midiInParams.at(k); if (pure != midiInParam) continue; - float vf = (value >> 8)/127.0f; - plugin->setParameter(k, vf); + float vf = (value >> 8) / 127.0f; + c::plugin::setParameter(plugin, k, vf, false); // false: not from GUI gu_log(" >>> [plugin %d parameter %d] ch=%d (pure=0x%X, value=%d, float=%f)\n", - i, k, ch->index, pure, value >> 8, vf); + plugin->getId(), k, ch->index, pure, value >> 8, vf); } } } @@ -107,9 +107,7 @@ void processPlugins(Channel *ch, uint32_t pure, uint32_t value) void processChannels(uint32_t pure, uint32_t value) { - for (unsigned i=0; i(mixer::channels.at(i)); + for (Channel* ch : mixer::channels) { if (!ch->midiIn) continue; @@ -124,11 +122,19 @@ void processChannels(uint32_t pure, uint32_t value) } else if (pure == ch->midiInMute) { gu_log(" >>> mute ch=%d (pure=0x%X)\n", ch->index, pure); - glue_setMute(ch, false); + glue_toggleMute(ch, false); + } + else if (pure == ch->midiInKill) { + gu_log(" >>> kill ch=%d (pure=0x%X)\n", ch->index, pure); + glue_kill(ch); + } + else if (pure == ch->midiInArm) { + gu_log(" >>> arm ch=%d (pure=0x%X)\n", ch->index, pure); + glue_toggleArm(ch, 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); + glue_toggleSolo(ch, false); } else if (pure == ch->midiInVolume) { float vf = (value >> 8)/127.0f; @@ -136,22 +142,27 @@ void processChannels(uint32_t pure, uint32_t value) ch->index, pure, value >> 8, vf); glue_setVolume(ch, vf, false); } - 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(static_cast(ch), vf); - } - else if (pure == ((SampleChannel*)ch)->midiInReadActions) { - gu_log(" >>> start/stop read actions ch=%d (pure=0x%X)\n", ch->index, pure); - glue_startStopReadingRecs(static_cast(ch), false); + else { + SampleChannel* sch = static_cast(ch); + if (pure == sch->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", + sch->index, pure, value >> 8, vf); + glue_setPitch(sch, vf); + } + else + if (pure == sch->midiInReadActions) { + gu_log(" >>> toggle read actions ch=%d (pure=0x%X)\n", sch->index, pure); + glue_toggleReadingRecs(sch, false); + } } #ifdef WITH_VST processPlugins(ch, pure, value); // Process plugins' parameters #endif - /* Redirect full midi message (pure + value) to plugins */ + /* Redirect full midi message (pure + value) to plugins. */ + ch->receiveMidi(pure | value); } } @@ -208,38 +219,37 @@ void processMaster(uint32_t pure, uint32_t value) /* -------------------------------------------------------------------------- */ -static void callback(double t, std::vector *msg, void *data) +static void callback(double t, std::vector* msg, void* data) { - /* 0.8.0 - for now we handle other midi signals (common and real-time - * messages) as unknown, for debugging purposes */ + /* 0.8.0 - for now we handle other MIDI signals (common and real-time + messages) as unknown, for debugging purposes. */ if (msg->size() < 3) { - gu_log("[KM] MIDI received - unknown signal - size=%d, value=0x", (int) msg->size()); - for (unsigned i=0; isize(); i++) - gu_log("%X", (int) msg->at(i)); - gu_log("\n"); + //gu_log("[KM] MIDI received - unknown signal - size=%d, value=0x", (int) msg->size()); + //for (unsigned i=0; isize(); i++) + // gu_log("%X", (int) msg->at(i)); + //gu_log("\n"); return; } - /* in this place we want to catch two things: a) note on/note off - * from a keyboard and b) knob/wheel/slider movements from a - * controller */ + /* Here we want to catch two things: a) note on/note off from a keyboard and + b) knob/wheel/slider movements from a controller. */ uint32_t input = getIValue(msg->at(0), msg->at(1), msg->at(2)); uint32_t chan = input & 0x0F000000; uint32_t value = input & 0x0000FF00; uint32_t pure = 0x00; if (!conf::noNoteOff) - pure = input & 0xFFFF0000; // input without 'value' byte + pure = input & 0xFFFF0000; // input without 'value' (i.e. velocity) byte else - pure = input & 0xFFFFFF00; // input with 'value' byte + pure = input & 0xFFFFFF00; // input with 'value' (i.e. velocity) byte 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. */ + /* 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) cb_learn(pure, cb_data); diff --git a/src/core/midiChannel.h b/src/core/midiChannel.h index feec2b1..8b2cd56 100644 --- a/src/core/midiChannel.h +++ b/src/core/midiChannel.h @@ -49,12 +49,12 @@ public: bool midiOut; // enable midi output uint8_t midiOutChan; // midi output channel - void copy(const Channel *src, pthread_mutex_t *pluginMutex) override; + void copy(const Channel* src, pthread_mutex_t* pluginMutex) override; void clear() override; - void process(float *outBuffer, float *inBuffer) override; - void preview(float *outBuffer) override; + void process(float* outBuffer, float *inBuffer) override; + void preview(float* outBuffer) override; void start(int frame, bool doQuantize, int quantize, bool mixerIsRunning, - bool forceStart, bool isUserGenerated) override; + bool forceStart, bool isUserGenerated) override; void kill(int frame) override; void empty() override; void stopBySeq(bool chansStopOnSeqHalt) override; @@ -62,23 +62,21 @@ public: void rewind() override; void setMute(bool internal) override; void unsetMute(bool internal) override; - int readPatch(const std::string &basePath, int i, pthread_mutex_t *pluginMutex, + int readPatch(const std::string& basePath, int i, pthread_mutex_t* pluginMutex, int samplerate, int rsmpQuality) override; int writePatch(int i, bool isProject) override; void quantize(int index, int localFrame) override; void onZero(int frame, bool recsStopOnChanHalt) override; void onBar(int frame) override; - void parseAction(giada::m::recorder::action *a, int localFrame, int globalFrame, - int quantize, bool mixerIsRunning) override; + void parseAction(giada::m::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(giada::m::recorder::action *a, int localFrame); + void sendMidi(giada::m::recorder::action* a, int localFrame); void sendMidi(uint32_t data); #ifdef WITH_VST diff --git a/src/core/mixer.cpp b/src/core/mixer.cpp index e86595d..350769a 100644 --- a/src/core/mixer.cpp +++ b/src/core/mixer.cpp @@ -542,7 +542,7 @@ void mergeVirtualInput() if (channels.at(i)->type == CHANNEL_MIDI) continue; SampleChannel *ch = static_cast(channels.at(i)); - if (ch->armed) + if (ch->isArmed()) memcpy(ch->wave->getData(), vChanInput, clock::getTotalFrames() * sizeof(float)); } memset(vChanInput, 0, clock::getTotalFrames() * sizeof(float)); // clear vchan diff --git a/src/core/mixerHandler.cpp b/src/core/mixerHandler.cpp index 623ed27..40220a7 100644 --- a/src/core/mixerHandler.cpp +++ b/src/core/mixerHandler.cpp @@ -64,7 +64,7 @@ namespace { #ifdef WITH_VST -int readPatchPlugins(vector *list, int type) +int readPatchPlugins(vector* list, int type) { int ret = 1; for (unsigned i=0; isize(); i++) { @@ -115,15 +115,13 @@ int getNewChanIndex() /* -------------------------------------------------------------------------- */ -bool uniqueSampleName(SampleChannel* ch, const string& name) +bool uniqueSamplePath(const SampleChannel* skip, const string& path) { - for (unsigned i=0; itype != CHANNEL_SAMPLE) + for (const Channel* ch : mixer::channels) { + if (skip == ch || ch->type != CHANNEL_SAMPLE) // skip itself and MIDI channels continue; - SampleChannel* other = (SampleChannel*) mixer::channels.at(i); - if (other->wave != nullptr && name == other->wave->getName()) + const SampleChannel* sch = static_cast(ch); + if (sch->wave != nullptr && path == sch->wave->getPath()) return false; } return true; @@ -330,19 +328,12 @@ bool startInputRec() continue; } - ch->pushWave(wave, false); // false: don't generate name, we provide it - - /* Increase lastTakeId until the sample name TAKE-[n] is unique. */ - - while (!uniqueSampleName(ch, ch->wave->getName())) { - patch::lastTakeId++; - ch->wave->setName("TAKE-" + gu_toString(patch::lastTakeId)); - } + ch->pushWave(wave); + ch->setName("TAKE-" + gu_toString(patch::lastTakeId++)); // Increase lastTakeId + channelsReady++; gu_log("[startInputRec] start input recs using chan %d with size %d " "frame=%d\n", ch->index, clock::getTotalFrames(), mixer::inputTracker); - - channelsReady++; } if (channelsReady > 0) { @@ -375,7 +366,7 @@ bool hasArmedSampleChannels() { for (unsigned i=0; itype == CHANNEL_SAMPLE && ch->armed) + if (ch->type == CHANNEL_SAMPLE && ch->isArmed()) return true; } return false; diff --git a/src/core/mixerHandler.h b/src/core/mixerHandler.h index 362c346..18b0283 100644 --- a/src/core/mixerHandler.h +++ b/src/core/mixerHandler.h @@ -43,17 +43,17 @@ namespace mh /* addChannel Adds a new channel of type 'type' into mixer's stack. */ -Channel *addChannel(int type); +Channel* addChannel(int type); /* deleteChannel Completely removes a channel from the stack. */ -int deleteChannel(Channel *ch); +int deleteChannel(Channel* ch); /* getChannelByIndex Returns channel with given index 'i'. */ -Channel *getChannelByIndex(int i); +Channel* getChannelByIndex(int i); /* hasLogicalSamples True if 1 or more samples are logical (memory only, such as takes) */ @@ -66,8 +66,7 @@ True if 1 or more samples was edited via gEditor */ bool hasEditedSamples(); /* stopSequencer - * stop the sequencer, with special case if samplesStopOnSeqHalt is - * true. */ +Stops the sequencer, with special case if samplesStopOnSeqHalt is true. */ void stopSequencer(); @@ -76,28 +75,27 @@ void rewindSequencer(); /* uniqueSolo * true if ch is the only solo'd channel in mixer. */ -bool uniqueSolo(Channel *ch); +bool uniqueSolo(Channel* ch); /* loadPatch - * load a path or a project (if isProject) into Mixer. If isProject, path - * must contain the address of the project folder. */ +Loads a path or a project (if isProject) into Mixer. If isProject, path must +contain the address of the project folder. */ void readPatch(); /* startInputRec - record from line in - * creates a new empty wave in the first available channels and returns - * the chan number chosen, otherwise -1 if there are no more empty - * channels available. */ +Creates a new empty wave in the first available channels. Returns false if +something went wrong. */ bool startInputRec(); void stopInputRec(); -/* uniqueSamplename - * return true if samplename 'n' is unique. Requires SampleChannel *ch - * in order to skip check against itself. */ +/* uniqueSamplePath +Returns true if path 'p' is unique. Requires SampleChannel 'skip' in order +to skip check against itself. */ -bool uniqueSampleName(SampleChannel *ch, const std::string &s); +bool uniqueSamplePath(const SampleChannel* skip, const std::string& p); /* hasArmedSampleChannels Tells whether Mixer has one or more sample channels armed for input diff --git a/src/core/patch.cpp b/src/core/patch.cpp index 495134d..b70ea9e 100644 --- a/src/core/patch.cpp +++ b/src/core/patch.cpp @@ -208,6 +208,7 @@ bool readChannels(json_t* jContainer) if (!storager::setInt (jChannel, PATCH_KEY_CHANNEL_TYPE, channel.type)) return 0; if (!storager::setInt (jChannel, PATCH_KEY_CHANNEL_INDEX, channel.index)) return 0; if (!storager::setInt (jChannel, PATCH_KEY_CHANNEL_SIZE, channel.size)) return 0; + if (!storager::setString(jChannel, PATCH_KEY_CHANNEL_NAME, channel.name)) return 0; if (!storager::setInt (jChannel, PATCH_KEY_CHANNEL_COLUMN, channel.column)) return 0; if (!storager::setInt (jChannel, PATCH_KEY_CHANNEL_MUTE, channel.mute)) return 0; if (!storager::setInt (jChannel, PATCH_KEY_CHANNEL_MUTE_S, channel.mute_s)) return 0; @@ -239,6 +240,7 @@ bool readChannels(json_t* jContainer) if (!storager::setUint32(jChannel, PATCH_KEY_CHANNEL_MIDI_IN_PITCH, channel.midiInPitch)) return 0; if (!storager::setUint32(jChannel, PATCH_KEY_CHANNEL_MIDI_OUT, channel.midiOut)) return 0; if (!storager::setUint32(jChannel, PATCH_KEY_CHANNEL_MIDI_OUT_CHAN, channel.midiOutChan)) return 0; + if (!storager::setBool (jChannel, PATCH_KEY_CHANNEL_ARMED, channel.armed)) return 0; readActions(jChannel, &channel); @@ -383,6 +385,7 @@ void writeChannels(json_t* jContainer, vector* channels) json_object_set_new(jChannel, PATCH_KEY_CHANNEL_TYPE, json_integer(channel.type)); json_object_set_new(jChannel, PATCH_KEY_CHANNEL_INDEX, json_integer(channel.index)); json_object_set_new(jChannel, PATCH_KEY_CHANNEL_SIZE, json_integer(channel.size)); + json_object_set_new(jChannel, PATCH_KEY_CHANNEL_NAME, json_string(channel.name.c_str())); json_object_set_new(jChannel, PATCH_KEY_CHANNEL_COLUMN, json_integer(channel.column)); json_object_set_new(jChannel, PATCH_KEY_CHANNEL_MUTE, json_integer(channel.mute)); json_object_set_new(jChannel, PATCH_KEY_CHANNEL_MUTE_S, json_integer(channel.mute_s)); @@ -414,6 +417,7 @@ void writeChannels(json_t* jContainer, vector* channels) json_object_set_new(jChannel, PATCH_KEY_CHANNEL_MIDI_IN_PITCH, json_integer(channel.midiInPitch)); json_object_set_new(jChannel, PATCH_KEY_CHANNEL_MIDI_OUT, json_integer(channel.midiOut)); json_object_set_new(jChannel, PATCH_KEY_CHANNEL_MIDI_OUT_CHAN, json_integer(channel.midiOutChan)); + json_object_set_new(jChannel, PATCH_KEY_CHANNEL_ARMED, json_boolean(channel.armed)); json_array_append_new(jChannels, jChannel); writeActions(jChannel, &channel.actions); diff --git a/src/core/patch.h b/src/core/patch.h index 7018dba..bfc757e 100644 --- a/src/core/patch.h +++ b/src/core/patch.h @@ -65,6 +65,7 @@ struct channel_t int type; int index; int size; + std::string name; int column; int mute; int mute_s; @@ -83,6 +84,7 @@ struct channel_t uint32_t midiOutLplaying; uint32_t midiOutLmute; uint32_t midiOutLsolo; + bool armed; // sample channel std::string samplePath; int key; diff --git a/src/core/plugin.cpp b/src/core/plugin.cpp index 83361f9..ce2b58e 100644 --- a/src/core/plugin.cpp +++ b/src/core/plugin.cpp @@ -46,21 +46,21 @@ int Plugin::idGenerator = 1; Plugin::Plugin(juce::AudioPluginInstance *plugin, double samplerate, - int buffersize) - : ui (nullptr), - plugin(plugin), - id (idGenerator++), - bypass(false) + int buffersize) + : ui (nullptr), + plugin(plugin), + id (idGenerator++), + bypass(false) { - /* Init midiInParams. All values are empty (0x0): they will be filled during - midi learning process. */ + /* Init midiInParams. All values are empty (0x0): they will be filled during + midi learning process. */ - for (int i=0; igetNumParameters(); i++) - midiInParams.push_back(0x0); - - plugin->prepareToPlay(samplerate, buffersize); + for (int i=0; igetNumParameters(); i++) + midiInParams.push_back(0x0); + + plugin->prepareToPlay(samplerate, buffersize); - gu_log("[Plugin] plugin initialized and ready\n"); + gu_log("[Plugin] plugin initialized and ready\n"); } @@ -70,8 +70,8 @@ Plugin::Plugin(juce::AudioPluginInstance *plugin, double samplerate, Plugin::~Plugin() { closeEditor(); - plugin->suspendProcessing(true); - plugin->releaseResources(); + plugin->suspendProcessing(true); + plugin->releaseResources(); } @@ -80,174 +80,205 @@ Plugin::~Plugin() void Plugin::showEditor(void* parent) { - ui = plugin->createEditorIfNeeded(); - if (ui == nullptr) { - gu_log("[Plugin::showEditor] unable to create editor!\n"); - return; - } + ui = plugin->createEditorIfNeeded(); + if (ui == nullptr) { + gu_log("[Plugin::showEditor] unable to create editor!\n"); + return; + } - /* A silly workaround on X: it seems that calling addToDesktop too fast, i.e. - before the X Window is fully ready screws up the plugin's event dispatcher. */ + /* A silly workaround on X: it seems that calling addToDesktop too fast, i.e. + before the X Window is fully ready screws up the plugin's event dispatcher. */ #ifdef G_OS_LINUX - time::sleep(500); + time::sleep(500); #endif - ui->setOpaque(true); - ui->addToDesktop(0, parent); + ui->setOpaque(true); + ui->addToDesktop(0, parent); } /* -------------------------------------------------------------------------- */ -bool Plugin::isEditorOpen() +bool Plugin::isEditorOpen() const { - return ui != nullptr && ui->isVisible() && ui->isOnDesktop(); + return ui != nullptr && ui->isVisible() && ui->isOnDesktop(); } /* -------------------------------------------------------------------------- */ -string Plugin::getUniqueId() +string Plugin::getUniqueId() const { - return plugin->getPluginDescription().fileOrIdentifier.toStdString(); + //return plugin->getPluginDescription().fileOrIdentifier.toStdString(); + return plugin->getPluginDescription().createIdentifierString().toStdString(); } /* -------------------------------------------------------------------------- */ -int Plugin::getNumParameters() +int Plugin::getNumParameters() const { - return plugin->getNumParameters(); + return plugin->getNumParameters(); } /* -------------------------------------------------------------------------- */ -float Plugin::getParameter(int paramIndex) +float Plugin::getParameter(int paramIndex) const { - return plugin->getParameter(paramIndex); + return plugin->getParameter(paramIndex); } /* -------------------------------------------------------------------------- */ -void Plugin::setParameter(int paramIndex, float value) +void Plugin::setParameter(int paramIndex, float value) const { - return plugin->setParameter(paramIndex, value); + return plugin->setParameter(paramIndex, value); } /* -------------------------------------------------------------------------- */ -void Plugin::prepareToPlay(double samplerate, int buffersize) +void Plugin::prepareToPlay(double samplerate, int buffersize) const { - plugin->prepareToPlay(samplerate, buffersize); + plugin->prepareToPlay(samplerate, buffersize); } /* -------------------------------------------------------------------------- */ -string Plugin::getName() +string Plugin::getName() const { - return plugin->getName().toStdString(); + return plugin->getName().toStdString(); } /* -------------------------------------------------------------------------- */ -bool Plugin::isSuspended() +bool Plugin::isSuspended() const { - return plugin->isSuspended(); + return plugin->isSuspended(); } /* -------------------------------------------------------------------------- */ -void Plugin::process(juce::AudioBuffer &b, juce::MidiBuffer &m) +bool Plugin::acceptsMidi() const { - plugin->processBlock(b, m); + return plugin->acceptsMidi(); } /* -------------------------------------------------------------------------- */ -int Plugin::getNumPrograms() +bool Plugin::isBypassed() const { return bypass; } +void Plugin::toggleBypass() { bypass = !bypass; } +void Plugin::setBypass(bool b) { bypass = b; } + + +/* -------------------------------------------------------------------------- */ + + +int Plugin::getId() const { return id; } + + +/* -------------------------------------------------------------------------- */ + + +int Plugin::getEditorW() const { return ui->getWidth(); } +int Plugin::getEditorH() const { return ui->getHeight(); } + + +/* -------------------------------------------------------------------------- */ + + +void Plugin::process(juce::AudioBuffer& b, juce::MidiBuffer m) const +{ + plugin->processBlock(b, m); +} + + +/* -------------------------------------------------------------------------- */ + + +int Plugin::getNumPrograms() const { - return plugin->getNumPrograms(); + return plugin->getNumPrograms(); } /* -------------------------------------------------------------------------- */ -int Plugin::getCurrentProgram() +int Plugin::getCurrentProgram() const { - return plugin->getCurrentProgram(); + return plugin->getCurrentProgram(); } /* -------------------------------------------------------------------------- */ -void Plugin::setCurrentProgram(int index) +void Plugin::setCurrentProgram(int index) const { - plugin->setCurrentProgram(index); + plugin->setCurrentProgram(index); } /* -------------------------------------------------------------------------- */ -bool Plugin::hasEditor() +bool Plugin::hasEditor() const { - return plugin->hasEditor(); + return plugin->hasEditor(); } /* -------------------------------------------------------------------------- */ -string Plugin::getProgramName(int index) +string Plugin::getProgramName(int index) const { - return plugin->getProgramName(index).toStdString(); + return plugin->getProgramName(index).toStdString(); } /* -------------------------------------------------------------------------- */ -string Plugin::getParameterName(int index) +string Plugin::getParameterName(int index) const { - return plugin->getParameterName(index).toStdString(); + return plugin->getParameterName(index).toStdString(); } /* -------------------------------------------------------------------------- */ -string Plugin::getParameterText(int index) +string Plugin::getParameterText(int index) const { - return plugin->getParameterText(index).toStdString(); + return plugin->getParameterText(index).toStdString(); } /* -------------------------------------------------------------------------- */ -string Plugin::getParameterLabel(int index) +string Plugin::getParameterLabel(int index) const { - return plugin->getParameterLabel(index).toStdString(); + return plugin->getParameterLabel(index).toStdString(); } @@ -256,10 +287,10 @@ string Plugin::getParameterLabel(int index) void Plugin::closeEditor() { - if (ui == nullptr) - return; - delete ui; - ui = nullptr; + if (ui == nullptr) + return; + delete ui; + ui = nullptr; } #endif diff --git a/src/core/plugin.h b/src/core/plugin.h index 9a52473..8091d8e 100644 --- a/src/core/plugin.h +++ b/src/core/plugin.h @@ -40,63 +40,68 @@ class Plugin { private: - static int idGenerator; + static int idGenerator; - juce::AudioProcessorEditor *ui; // gui - juce::AudioPluginInstance *plugin; // core + juce::AudioProcessorEditor* ui; // gui + juce::AudioPluginInstance* plugin; // core - int id; - bool bypass; + int id; + bool bypass; public: - Plugin(juce::AudioPluginInstance *p, double samplerate, int buffersize); - ~Plugin(); - - void showEditor(void *parent); - - /* closeEditor - * Shut down plugin GUI. */ - - void closeEditor(); - - /* isEditorOpen */ - - bool isEditorOpen(); - - /* getUniqueId - * Return a string-based UID. */ - - std::string getUniqueId(); - - std::string getName(); - - bool hasEditor(); - int getNumParameters(); - float getParameter(int index); - void setParameter(int index, float value); - std::string getParameterName(int index); - std::string getParameterText(int index); - std::string getParameterLabel(int index); - void prepareToPlay(double samplerate, int buffersize); - bool isSuspended(); - void process(juce::AudioBuffer &b, juce::MidiBuffer &m); - int getNumPrograms(); - int getCurrentProgram(); - void setCurrentProgram(int index); - std::string getProgramName(int index); - - int getId() { return id; } - bool isBypassed() { return bypass; } - int getEditorW() { return ui->getWidth(); } - int getEditorH() { return ui->getHeight(); } - void toggleBypass() { bypass = !bypass; } - void setBypass(bool b) { bypass = b; } - - /* midiInParams - A list of midiIn hex values for parameter automation. */ - - std::vector midiInParams; + Plugin(juce::AudioPluginInstance* p, double samplerate, int buffersize); + ~Plugin(); + + /* getUniqueId + Returns a string-based UID. */ + + std::string getUniqueId() const; + + /* process + Process the plug-in with audio and MIDI data. The audio buffer is a reference: + it has to be altered by the plug-in itself. Conversely, the MIDI buffer must + be passed by copy: each plug-in must receive its own copy of the event set, so + that any attempt to change/clear the MIDI buffer will only modify the local + copy. */ + + void process(juce::AudioBuffer& b, juce::MidiBuffer m) const; + + std::string getName() const; + bool isEditorOpen() const; + bool hasEditor() const; + int getNumParameters() const; + float getParameter(int index) const; + std::string getParameterName(int index) const; + std::string getParameterText(int index) const; + std::string getParameterLabel(int index) const; + bool isSuspended() const; + bool isBypassed() const; + int getNumPrograms() const; + int getCurrentProgram() const; + std::string getProgramName(int index) const; + int getId() const; + int getEditorW() const; + int getEditorH() const; + void setParameter(int index, float value) const; + void prepareToPlay(double samplerate, int buffersize) const; + void setCurrentProgram(int index) const; + bool acceptsMidi() const; + + void showEditor(void* parent); + + /* closeEditor + Shuts down plugin GUI. */ + + void closeEditor(); + + void toggleBypass(); + void setBypass(bool b); + + /* midiInParams + A list of midiIn hex values for parameter automation. */ + + std::vector midiInParams; }; #endif diff --git a/src/core/pluginHost.cpp b/src/core/pluginHost.cpp index 188d41a..6ca0006 100644 --- a/src/core/pluginHost.cpp +++ b/src/core/pluginHost.cpp @@ -30,6 +30,7 @@ #include "../utils/log.h" #include "../utils/fs.h" +#include "../utils/string.h" #include "const.h" #include "channel.h" #include "plugin.h" @@ -46,7 +47,7 @@ namespace pluginHost { namespace { -juce::MessageManager *messageManager; +juce::MessageManager* messageManager; /* pluginFormat * Plugin format manager. */ @@ -61,10 +62,10 @@ juce::KnownPluginList knownPluginList; /* unknownPluginList * List of unrecognized plugins found in a patch. */ -std::vector unknownPluginList; +vector unknownPluginList; -std::vector masterOut; -std::vector masterIn; +vector masterOut; +vector masterIn; /* Audio|MidiBuffer * Dynamic buffers. */ @@ -79,6 +80,49 @@ int buffersize; bool missingPlugins; +void splitPluginDescription(const string& descr, vector& out) +{ + // input: VST-mda-Ambience-18fae2d2-6d646141 string + // output: [2-------------] [1-----] [0-----] vector.size() == 3 + + string chunk = ""; + int count = 2; + for (int i=descr.length()-1; i >= 0; i--) { + if (descr[i] == '-' && count != 0) { + out.push_back(chunk); + count--; + chunk = ""; + } + else + chunk += descr[i]; + } + out.push_back(chunk); +} + + +/* findPluginDescription +Browses the list of known plug-ins until plug-in with id == 'id' is found. +Unfortunately knownPluginList.getTypeForIdentifierString(id) doesn't work for +VSTs: their ID is based on the plug-in file location. E.g.: + + /home/vst/mdaAmbience.so -> VST-mdaAmbience-18fae2d2-6d646141 + /home/vst-test/mdaAmbience.so -> VST-mdaAmbience-b328b2f6-6d646141 + +The following function simply drops the first hash code during comparison. */ + +const juce::PluginDescription* findPluginDescription(const string& id) +{ + vector idParts; + splitPluginDescription(id, idParts); + + for (const juce::PluginDescription* pd : knownPluginList) { + vector tmpIdParts; + splitPluginDescription(pd->createIdentifierString().toStdString(), tmpIdParts); + if (idParts[0] == tmpIdParts[0] && idParts[2] == tmpIdParts[2]) + return pd; + } + return nullptr; +} }; // {anonymous} @@ -95,7 +139,7 @@ pthread_mutex_t mutex_midi; void close() { - messageManager->deleteInstance(); + messageManager->deleteInstance(); } @@ -104,149 +148,150 @@ void close() void init(int _buffersize, int _samplerate) { - gu_log("[pluginHost::init] initialize with buffersize=%d, samplerate=%d\n", - _buffersize, _samplerate); - - messageManager = juce::MessageManager::getInstance(); - audioBuffer.setSize(2, _buffersize); - samplerate = _samplerate; - buffersize = _buffersize; - missingPlugins = false; - //unknownPluginList.empty(); - loadList(gu_getHomePath() + G_SLASH + "plugins.xml"); - - pthread_mutex_init(&mutex_midi, nullptr); + gu_log("[pluginHost::init] initialize with buffersize=%d, samplerate=%d\n", + _buffersize, _samplerate); + + messageManager = juce::MessageManager::getInstance(); + audioBuffer.setSize(2, _buffersize); + samplerate = _samplerate; + buffersize = _buffersize; + missingPlugins = false; + //unknownPluginList.empty(); + loadList(gu_getHomePath() + G_SLASH + "plugins.xml"); + + pthread_mutex_init(&mutex_midi, nullptr); } /* -------------------------------------------------------------------------- */ -int scanDir(const string &dirpath, void (*callback)(float progress, void *p), - void *p) +int scanDir(const string& dirpath, void (*callback)(float progress, void* p), + void* p) { - 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 - - juce::VSTPluginFormat format; - juce::FileSearchPath path(dirpath); - juce::PluginDirectoryScanner scanner(knownPluginList, format, path, - true, juce::File::nonexistent); // true: recursive - - bool cont = true; - juce::String name; - while (cont) { - gu_log("[pluginHost::scanDir] scanning '%s'\n", name.toRawUTF8()); - cont = scanner.scanNextFile(false, name); - if (callback) - callback(scanner.getProgress(), p); - } - - gu_log("[pluginHost::scanDir] %d plugin(s) found\n", knownPluginList.getNumTypes()); - return 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 + + juce::VSTPluginFormat format; + juce::FileSearchPath path(dirpath); + juce::PluginDirectoryScanner scanner(knownPluginList, format, path, + true, juce::File::nonexistent); // true: recursive + + bool cont = true; + juce::String name; + while (cont) { + gu_log("[pluginHost::scanDir] scanning '%s'\n", name.toRawUTF8()); + cont = scanner.scanNextFile(false, name); + if (callback) + callback(scanner.getProgress(), p); + } + + gu_log("[pluginHost::scanDir] %d plugin(s) found\n", knownPluginList.getNumTypes()); + return knownPluginList.getNumTypes(); } /* -------------------------------------------------------------------------- */ -int saveList(const string &filepath) +int saveList(const string& filepath) { - int out = knownPluginList.createXml()->writeToFile(juce::File(filepath), ""); - if (!out) - gu_log("[pluginHost::saveList] unable to save plugin list to %s\n", filepath.c_str()); - return out; + int out = knownPluginList.createXml()->writeToFile(juce::File(filepath), ""); + if (!out) + gu_log("[pluginHost::saveList] unable to save plugin list to %s\n", filepath.c_str()); + return out; } /* -------------------------------------------------------------------------- */ -int loadList(const string &filepath) +int loadList(const string& filepath) { - juce::XmlElement *elem = juce::XmlDocument::parse(juce::File(filepath)); - if (elem) { - knownPluginList.recreateFromXml(*elem); - delete elem; - return 1; - } - return 0; + juce::XmlElement* elem = juce::XmlDocument::parse(juce::File(filepath)); + if (elem) { + knownPluginList.recreateFromXml(*elem); + delete elem; + return 1; + } + return 0; } /* -------------------------------------------------------------------------- */ -Plugin *addPlugin(const string &fid, int stackType, - pthread_mutex_t *mutex, Channel *ch) +Plugin* addPlugin(const string& fid, int stackType, pthread_mutex_t* mutex, + Channel* ch) { - /* Get the proper stack to add the plugin to */ - - vector *pStack; - pStack = getStack(stackType, ch); - - /* Initialize plugin */ - - juce::PluginDescription *pd = knownPluginList.getTypeForFile(fid); - if (!pd) { - gu_log("[pluginHost::addPlugin] no plugin found with fid=%s!\n", fid.c_str()); - missingPlugins = true; - unknownPluginList.push_back(fid); - return nullptr; - } + vector* pStack = getStack(stackType, ch); + + /* Initialize plugin. The default mode uses getTypeForIdentifierString, + falling back to getTypeForFile (deprecated) for old patches (< 0.14.4). */ + + const juce::PluginDescription* pd = findPluginDescription(fid); + if (pd == nullptr) { + gu_log("[pluginHost::addPlugin] no plugin found with fid=%s! Trying with " + "deprecated mode...\n", fid.c_str()); + pd = knownPluginList.getTypeForFile(fid); + if (pd == nullptr) { + gu_log("[pluginHost::addPlugin] still nothing to do, returning unknown plugin\n"); + missingPlugins = true; + unknownPluginList.push_back(fid); + return nullptr; + } + } - juce::AudioPluginInstance *pi = pluginFormat.createInstanceFromDescription(*pd, samplerate, buffersize); - if (!pi) { - gu_log("[pluginHost::addPlugin] unable to create instance with fid=%s!\n", fid.c_str()); - missingPlugins = true; - return nullptr; - } - gu_log("[pluginHost::addPlugin] plugin instance with fid=%s created\n", fid.c_str()); + juce::AudioPluginInstance* pi = pluginFormat.createInstanceFromDescription(*pd, samplerate, buffersize); + if (!pi) { + gu_log("[pluginHost::addPlugin] unable to create instance with fid=%s!\n", fid.c_str()); + missingPlugins = true; + return nullptr; + } + gu_log("[pluginHost::addPlugin] plugin instance with fid=%s created\n", fid.c_str()); - Plugin *p = new Plugin(pi, samplerate, buffersize); + Plugin* p = new Plugin(pi, samplerate, buffersize); - /* Try to inject the plugin as soon as possible. */ + /* Try to inject the plugin as soon as possible. */ - while (true) { - if (pthread_mutex_trylock(mutex) != 0) - continue; - pStack->push_back(p); - pthread_mutex_unlock(mutex); - break; - } + while (true) { + if (pthread_mutex_trylock(mutex) != 0) + continue; + pStack->push_back(p); + pthread_mutex_unlock(mutex); + break; + } - gu_log("[pluginHost::addPlugin] plugin id=%s loaded (%s), stack type=%d, stack size=%d\n", - fid.c_str(), p->getName().c_str(), stackType, pStack->size()); + gu_log("[pluginHost::addPlugin] plugin id=%s loaded (%s), stack type=%d, stack size=%d\n", + fid.c_str(), p->getName().c_str(), stackType, pStack->size()); - return p; + return p; } /* -------------------------------------------------------------------------- */ -Plugin *addPlugin(int index, int stackType, pthread_mutex_t *mutex, - Channel *ch) +Plugin* addPlugin(int index, int stackType, pthread_mutex_t* mutex, + Channel* ch) { - juce::PluginDescription *pd = knownPluginList.getType(index); - if (pd) { - 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 { - gu_log("[pluginHost::addPlugin] no plugins found at index=%d!\n", index); - return nullptr; - } + juce::PluginDescription* pd = knownPluginList.getType(index); + if (pd) { + gu_log("[pluginHost::addPlugin] plugin found, uid=%s, name=%s...\n", + pd->createIdentifierString().toRawUTF8(), pd->name.toRawUTF8()); + return addPlugin(pd->createIdentifierString().toStdString(), stackType, mutex, ch); + } + gu_log("[pluginHost::addPlugin] no plugins found at index=%d!\n", index); + return nullptr; } /* -------------------------------------------------------------------------- */ -vector *getStack(int stackType, Channel *ch) +vector* getStack(int stackType, Channel* ch) { switch(stackType) { case MASTER_OUT: @@ -264,9 +309,9 @@ vector *getStack(int stackType, Channel *ch) /* -------------------------------------------------------------------------- */ -unsigned countPlugins(int stackType, Channel *ch) +unsigned countPlugins(int stackType, Channel* ch) { - vector *pStack = getStack(stackType, ch); + vector* pStack = getStack(stackType, ch); return pStack->size(); } @@ -276,7 +321,7 @@ unsigned countPlugins(int stackType, Channel *ch) int countAvailablePlugins() { - return knownPluginList.getNumTypes(); + return knownPluginList.getNumTypes(); } @@ -285,7 +330,7 @@ int countAvailablePlugins() unsigned countUnknownPlugins() { - return unknownPluginList.size(); + return unknownPluginList.size(); } @@ -294,15 +339,15 @@ unsigned countUnknownPlugins() pluginHost::PluginInfo getAvailablePluginInfo(int i) { - juce::PluginDescription *pd = knownPluginList.getType(i); - PluginInfo pi; - pi.uid = pd->fileOrIdentifier.toStdString(); - pi.name = pd->name.toStdString(); - pi.category = pd->category.toStdString(); - pi.manufacturerName = pd->manufacturerName.toStdString(); - pi.format = pd->pluginFormatName.toStdString(); - pi.isInstrument = pd->isInstrument; - return pi; + juce::PluginDescription* pd = knownPluginList.getType(i); + PluginInfo pi; + pi.uid = pd->fileOrIdentifier.toStdString(); + pi.name = pd->name.toStdString(); + pi.category = pd->category.toStdString(); + pi.manufacturerName = pd->manufacturerName.toStdString(); + pi.format = pd->pluginFormatName.toStdString(); + pi.isInstrument = pd->isInstrument; + return pi; } @@ -311,7 +356,7 @@ pluginHost::PluginInfo getAvailablePluginInfo(int i) bool hasMissingPlugins() { - return missingPlugins; + return missingPlugins; }; @@ -320,80 +365,100 @@ bool hasMissingPlugins() string getUnknownPluginInfo(int i) { - return unknownPluginList.at(i); + return unknownPluginList.at(i); } /* -------------------------------------------------------------------------- */ -void freeStack(int stackType, pthread_mutex_t *mutex, Channel *ch) +void freeStack(int stackType, pthread_mutex_t* mutex, Channel* ch) { - vector *pStack; - pStack = getStack(stackType, ch); + vector* pStack = getStack(stackType, ch); if (pStack->size() == 0) return; while (true) { if (pthread_mutex_trylock(mutex) != 0) - continue; + continue; for (unsigned i=0; isize(); i++) - delete pStack->at(i); + delete pStack->at(i); pStack->clear(); pthread_mutex_unlock(mutex); break; } - gu_log("[pluginHost::freeStack] stack type=%d freed\n", stackType); + gu_log("[pluginHost::freeStack] stack type=%d freed\n", stackType); } /* -------------------------------------------------------------------------- */ -void processStack(float *buffer, int stackType, Channel *ch) +void processStack(float* buffer, int stackType, Channel* ch) { - vector *pStack = getStack(stackType, ch); + vector* pStack = getStack(stackType, ch); - /* empty stack, stack not found or mixer not ready: do nothing */ + /* Empty stack, stack not found or mixer not ready: do nothing. */ if (pStack == nullptr || pStack->size() == 0) return; - /* converting buffer from Giada to Juce */ + /* MIDI channels must not process the current buffer: give them an empty one. + Sample channels and Master in/out want audio data instead: let's convert the + internal buffer from Giada to Juce. */ - for (int i=0; itype == CHANNEL_MIDI) + audioBuffer.clear(); + else + for (int i=0; isize(); i++) { - Plugin *plugin = pStack->at(i); + 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: + this::processStack() + [a new midi event comes in from kernelMidi thread] + channel::clearMidiBuffer() + The midi event in between would be surely lost, deleted by the last call to + channel::clearMidiBuffer()! */ + + if (ch != nullptr) + pthread_mutex_lock(&mutex_midi); + + for (const Plugin* plugin : *pStack) { if (plugin->isSuspended() || plugin->isBypassed()) continue; - if (ch) { // ch might be null if stackType is MASTER_IN/OUT - pthread_mutex_lock(&mutex_midi); - plugin->process(audioBuffer, ch->getPluginMidiEvents()); - ch->clearMidiBuffer(); - pthread_mutex_unlock(&mutex_midi); - } - else { - juce::MidiBuffer midiBuffer; // empty buffer - plugin->process(audioBuffer, midiBuffer); - } - } - - /* converting buffer from Juce to Giada. A note for the future: if we - * overwrite (=) (as we do now) it's SEND, if we add (+) it's INSERT. */ + + /* If this is a Channel (ch != nullptr) and the current plugin is an + instrument (i.e. accepts MIDI), don't let it fill the current audio buffer: + create a new temporary one instead and then merge the result into the main + one when done. This way each plug-in generates its own audio data and we can + play more than one plug-in instrument in the same stack, driven by the same + set of MIDI events. */ + + if (ch != nullptr && plugin->acceptsMidi()) { + juce::AudioBuffer tmp(2, buffersize); + plugin->process(tmp, ch->getPluginMidiEvents()); + for (int i=0; iprocess(audioBuffer, juce::MidiBuffer()); // Empty MIDI buffer + } + + if (ch != nullptr) { + ch->clearMidiBuffer(); + pthread_mutex_unlock(&mutex_midi); + } + + /* Converting buffer from Juce to Giada. A note for the future: if we + overwrite (=) (as we do now) it's SEND, if we add (+) it's INSERT. */ for (int i=0; i *pStack = getStack(stackType, ch); + vector* pStack = getStack(stackType, ch); if (pStack->size() == 0) return nullptr; if ((unsigned) index >= pStack->size()) @@ -419,9 +484,9 @@ Plugin *getPluginByIndex(int index, int stackType, Channel *ch) /* -------------------------------------------------------------------------- */ -int getPluginIndex(int id, int stackType, Channel *ch) +int getPluginIndex(int id, int stackType, Channel* ch) { - vector *pStack = getStack(stackType, ch); + vector* pStack = getStack(stackType, ch); for (unsigned i=0; isize(); i++) if (pStack->at(i)->getId() == id) return i; @@ -433,12 +498,12 @@ int getPluginIndex(int id, int stackType, Channel *ch) void swapPlugin(unsigned indexA, unsigned indexB, int stackType, - pthread_mutex_t *mutex, Channel *ch) + pthread_mutex_t* mutex, Channel* ch) { - vector *pStack = getStack(stackType, ch); + vector* pStack = getStack(stackType, ch); while (true) { if (pthread_mutex_trylock(mutex) != 0) - continue; + continue; std::swap(pStack->at(indexA), pStack->at(indexB)); pthread_mutex_unlock(mutex); gu_log("[pluginHost::swapPlugin] plugin at index %d and %d swapped\n", indexA, indexB); @@ -450,26 +515,25 @@ void swapPlugin(unsigned indexA, unsigned indexB, int stackType, /* -------------------------------------------------------------------------- */ -int freePlugin(int id, int stackType, pthread_mutex_t *mutex, - Channel *ch) +int freePlugin(int id, int stackType, pthread_mutex_t* mutex, Channel* ch) { - vector *pStack = getStack(stackType, ch); + vector* pStack = getStack(stackType, ch); for (unsigned i=0; isize(); i++) { - Plugin *pPlugin = pStack->at(i); + Plugin *pPlugin = pStack->at(i); if (pPlugin->getId() != id) - continue; + continue; while (true) { if (pthread_mutex_trylock(mutex) != 0) - continue; + continue; delete pPlugin; pStack->erase(pStack->begin() + i); pthread_mutex_unlock(mutex); gu_log("[pluginHost::freePlugin] plugin id=%d removed\n", id); return i; } - } + } gu_log("[pluginHost::freePlugin] plugin id=%d not found\n", id); - return -1; + return -1; } @@ -478,32 +542,32 @@ int freePlugin(int id, int stackType, pthread_mutex_t *mutex, void runDispatchLoop() { - messageManager->runDispatchLoopUntil(10); - //gu_log("[pluginHost::runDispatchLoop] %d, hasStopMessageBeenSent=%d\n", r, messageManager->hasStopMessageBeenSent()); + messageManager->runDispatchLoopUntil(10); + //gu_log("[pluginHost::runDispatchLoop] %d, hasStopMessageBeenSent=%d\n", r, messageManager->hasStopMessageBeenSent()); } /* -------------------------------------------------------------------------- */ -void freeAllStacks(vector *channels, pthread_mutex_t *mutex) +void freeAllStacks(vector* channels, pthread_mutex_t* mutex) { freeStack(pluginHost::MASTER_OUT, mutex); freeStack(pluginHost::MASTER_IN, mutex); for (unsigned i=0; isize(); i++) freeStack(pluginHost::CHANNEL, mutex, channels->at(i)); - missingPlugins = false; - unknownPluginList.clear(); + missingPlugins = false; + unknownPluginList.clear(); } /* -------------------------------------------------------------------------- */ -int clonePlugin(Plugin *src, int stackType, pthread_mutex_t *mutex, - Channel *ch) +int clonePlugin(Plugin* src, int stackType, pthread_mutex_t* mutex, + Channel* ch) { - Plugin *p = addPlugin(src->getUniqueId(), stackType, mutex, ch); + Plugin* p = addPlugin(src->getUniqueId(), stackType, mutex, ch); if (!p) { gu_log("[pluginHost::clonePlugin] unable to add new plugin to stack!\n"); return 0; @@ -519,9 +583,9 @@ int clonePlugin(Plugin *src, int stackType, pthread_mutex_t *mutex, /* -------------------------------------------------------------------------- */ -bool doesPluginExist(const string &fid) +bool doesPluginExist(const string& fid) { - return pluginFormat.doesPluginStillExist(*knownPluginList.getTypeForFile(fid)); + return pluginFormat.doesPluginStillExist(*knownPluginList.getTypeForFile(fid)); } @@ -530,20 +594,20 @@ bool doesPluginExist(const string &fid) void sortPlugins(int method) { - switch (method) { - case sortMethod::NAME: - knownPluginList.sort(juce::KnownPluginList::SortMethod::sortAlphabetically, true); - break; - case sortMethod::CATEGORY: - knownPluginList.sort(juce::KnownPluginList::SortMethod::sortByCategory, true); - break; - case sortMethod::MANUFACTURER: - knownPluginList.sort(juce::KnownPluginList::SortMethod::sortByManufacturer, true); - break; - case sortMethod::FORMAT: - knownPluginList.sort(juce::KnownPluginList::SortMethod::sortByFormat, true); - break; - } + switch (method) { + case sortMethod::NAME: + knownPluginList.sort(juce::KnownPluginList::SortMethod::sortAlphabetically, true); + break; + case sortMethod::CATEGORY: + knownPluginList.sort(juce::KnownPluginList::SortMethod::sortByCategory, true); + break; + case sortMethod::MANUFACTURER: + knownPluginList.sort(juce::KnownPluginList::SortMethod::sortByManufacturer, true); + break; + case sortMethod::FORMAT: + knownPluginList.sort(juce::KnownPluginList::SortMethod::sortByFormat, true); + break; + } } }}}; // giada::m::pluginHost:: diff --git a/src/core/pluginHost.h b/src/core/pluginHost.h index 13ee170..f36bf41 100644 --- a/src/core/pluginHost.h +++ b/src/core/pluginHost.h @@ -76,14 +76,14 @@ void close(); * called on each plugin found. Used to update the main window from the GUI * thread. */ -int scanDir(const std::string &path, void (*callback)(float progress, void *p)=nullptr, - void *p=nullptr); +int scanDir(const std::string& path, void (*callback)(float progress, void* p)=nullptr, + void* p=nullptr); /* (save|load)List * (Save|Load) knownPluginList (in|from) an XML file. */ -int saveList(const std::string &path); -int loadList(const std::string &path); +int saveList(const std::string& path); +int loadList(const std::string& path); /* addPlugin * Add a new plugin to 'stackType' by unique id or by index in knownPluginList @@ -95,15 +95,15 @@ int loadList(const std::string &path); * bufSize - buffer size * ch - if stackType == CHANNEL. */ -Plugin *addPlugin(const std::string &fid, int stackType, pthread_mutex_t *mutex, - Channel *ch=nullptr); -Plugin *addPlugin(int index, int stackType, pthread_mutex_t *mutex, - Channel *ch=nullptr); +Plugin* addPlugin(const std::string& fid, int stackType, pthread_mutex_t* mutex, + Channel* ch=nullptr); +Plugin *addPlugin(int index, int stackType, pthread_mutex_t* mutex, + Channel* ch=nullptr); /* countPlugins * Return size of 'stackType'. */ -unsigned countPlugins(int stackType, Channel *ch=nullptr); +unsigned countPlugins(int stackType, Channel* ch=nullptr); /* countAvailablePlugins * Return size of knownPluginList. */ @@ -126,37 +126,37 @@ std::string getUnknownPluginInfo(int index); /* freeStack * free plugin stack of type 'stackType'. */ -void freeStack(int stackType, pthread_mutex_t *mutex, Channel *ch=nullptr); +void freeStack(int stackType, pthread_mutex_t* mutex, Channel* ch=nullptr); /* processStack * apply the fx list to the buffer. */ -void processStack(float *buffer, int stackType, Channel *ch=nullptr); +void processStack(float* buffer, int stackType, Channel* ch=nullptr); /* getStack * Return a std::vector given the stackType. If stackType == CHANNEL * a pointer to Channel is also required. */ -std::vector *getStack(int stackType, Channel *ch=nullptr); +std::vector* getStack(int stackType, Channel* ch=nullptr); /* getPluginByIndex */ -Plugin *getPluginByIndex(int index, int stackType, Channel *ch=nullptr); +Plugin* getPluginByIndex(int index, int stackType, Channel* ch=nullptr); /* getPluginIndex */ -int getPluginIndex(int id, int stackType, Channel *ch=nullptr); +int getPluginIndex(int id, int stackType, Channel* ch=nullptr); /* swapPlugin */ void swapPlugin(unsigned indexA, unsigned indexB, int stackType, - pthread_mutex_t *mutex, Channel *ch=nullptr); + pthread_mutex_t* mutex, Channel* ch=nullptr); /* freePlugin. Returns the internal stack index of the deleted plugin. */ int freePlugin(int id, int stackType, pthread_mutex_t *mutex, - Channel *ch=nullptr); + Channel* ch=nullptr); /* runDispatchLoop * Wakes up plugins' GUI manager for N milliseconds. */ @@ -166,16 +166,15 @@ void runDispatchLoop(); /* freeAllStacks * Frees everything. */ -void freeAllStacks(std::vector *channels, pthread_mutex_t *mutex); +void freeAllStacks(std::vector* channels, pthread_mutex_t* mutex); /* clonePlugin */ -int clonePlugin(Plugin *src, int stackType, pthread_mutex_t *mutex, - Channel *ch); - +int clonePlugin(Plugin* src, int stackType, pthread_mutex_t* mutex, Channel* ch); + /* doesPluginExist */ -bool doesPluginExist(const std::string &fid); +bool doesPluginExist(const std::string& fid); bool hasMissingPlugins(); diff --git a/src/core/sampleChannel.cpp b/src/core/sampleChannel.cpp index 6ffdd80..38dad90 100644 --- a/src/core/sampleChannel.cpp +++ b/src/core/sampleChannel.cpp @@ -61,6 +61,7 @@ SampleChannel::SampleChannel(int bufferSize, bool inputMonitor) boost (G_DEFAULT_BOOST), pitch (G_DEFAULT_PITCH), trackerPreview (0), + shift (0), wave (nullptr), tracker (0), mode (G_DEFAULT_CHANMODE), @@ -155,23 +156,9 @@ void SampleChannel::copy(const Channel* _src, pthread_mutex_t* pluginMutex) /* -------------------------------------------------------------------------- */ -void SampleChannel::generateUniqueSampleName() -{ - string oldName = wave->getName(); - int k = 0; - while (!mh::uniqueSampleName(this, wave->getName())) { - wave->setName(oldName + "-" + gu_toString(k)); - k++; - } -} - - -/* -------------------------------------------------------------------------- */ - - void SampleChannel::clear() { - /** TODO - these memsets can be done only if status PLAY (if below), + /** TODO - these memsets may be done only if status PLAY (if below), * but it would require extra clearPChan calls when samples stop */ std::memset(vChan, 0, sizeof(float) * bufferSize); @@ -261,15 +248,6 @@ void SampleChannel::onBar(int frame) /* -------------------------------------------------------------------------- */ -int SampleChannel::save(const char* path) -{ - return waveManager::save(wave, path); -} - - -/* -------------------------------------------------------------------------- */ - - void SampleChannel::setBegin(int f) { /* TODO - Opaque channel's count - everything in SampleChannel should be @@ -357,6 +335,13 @@ int SampleChannel::getTrackerPreview() const /* -------------------------------------------------------------------------- */ +int SampleChannel::getShift() const { return shift; } +void SampleChannel::setShift(int s) { shift = s; } + + +/* -------------------------------------------------------------------------- */ + + void SampleChannel::setPitch(float v) { if (v > G_MAX_PITCH) @@ -852,15 +837,14 @@ void SampleChannel::empty() /* -------------------------------------------------------------------------- */ -void SampleChannel::pushWave(Wave* w, bool generateName) +void SampleChannel::pushWave(Wave* w) { sendMidiLplay(); // FIXME - why here?!?! wave = w; status = STATUS_OFF; begin = 0; end = (wave->getSize() - 1) * wave->getChannels(); // TODO - Opaque channels' count - if (generateName) - generateUniqueSampleName(); + name = wave->getBasename(); } @@ -990,15 +974,15 @@ void SampleChannel::stop() /* -------------------------------------------------------------------------- */ -int SampleChannel::readPatch(const string &basePath, int i, - pthread_mutex_t *pluginMutex, int samplerate, int rsmpQuality) +int SampleChannel::readPatch(const string& basePath, int i, + pthread_mutex_t* pluginMutex, int samplerate, int rsmpQuality) { /* load channel's data first: if the sample is missing or wrong, the channel * is not completely blank. */ Channel::readPatch("", i, pluginMutex, samplerate, rsmpQuality); - patch::channel_t *pch = &patch::channels.at(i); + const patch::channel_t* pch = &patch::channels.at(i); mode = pch->mode; boost = pch->boost; @@ -1008,13 +992,14 @@ int SampleChannel::readPatch(const string &basePath, int i, midiInPitch = pch->midiInPitch; inputMonitor = pch->inputMonitor; - Wave *w = nullptr; + Wave* w = nullptr; int res = waveManager::create(basePath + pch->samplePath, &w); if (res == G_RES_OK) { pushWave(w); + setName(pch->name); setBegin(pch->begin); - setEnd (pch->end); + setEnd(pch->end); setPitch(pch->pitch); } else { diff --git a/src/core/sampleChannel.h b/src/core/sampleChannel.h index 8435dfe..783c9ae 100644 --- a/src/core/sampleChannel.h +++ b/src/core/sampleChannel.h @@ -42,6 +42,29 @@ class SampleChannel : public Channel { private: + /* fillChan + Fills 'dest' buffer at point 'offset' with wave data taken from 'start'. If + rewind=false don't rewind internal tracker. Returns new sample position, + in frames. It resamples data if pitch != 1.0f. */ + + int fillChan(float* dest, int start, int offset, bool rewind=true); + + /* clearChan + * set data to zero from start to bufferSize-1. */ + + void clearChan(float* dest, int start); + + /* calcFadeoutStep + * how many frames are left before the end of the sample? Is there + * enough room for a complete fadeout? Should we shorten it? */ + + void calcFadeoutStep(); + + /* calcVolumeEnv + * compute any changes in volume done via envelope tool */ + + void calcVolumeEnv(int frame); + /* rsmp_state, rsmp_data * structs from libsamplerate */ @@ -68,41 +91,13 @@ private: float boost; float pitch; int trackerPreview; // chan position for audio preview + int shift; /* onPreviewEnd A callback fired when audio preview ends. */ std::function onPreviewEnd; - /* fillChan - Fills 'dest' buffer at point 'offset' with wave data taken from 'start'. If - rewind=false don't rewind internal tracker. Returns new sample position, - in frames. It resamples data if pitch != 1.0f. */ - - int fillChan(float* dest, int start, int offset, bool rewind=true); - - /* clearChan - * set data to zero from start to bufferSize-1. */ - - void clearChan(float* dest, int start); - - /* calcFadeoutStep - * how many frames are left before the end of the sample? Is there - * enough room for a complete fadeout? Should we shorten it? */ - - void calcFadeoutStep(); - - /* calcVolumeEnv - * compute any changes in volume done via envelope tool */ - - void calcVolumeEnv(int frame); - - /* generateUniqueSampleName - * Sample name must be unique. Generate a new samplename with the "-[n]" - * suffix. */ - - void generateUniqueSampleName(); - public: SampleChannel(int bufferSize, bool inputMonitor); @@ -132,6 +127,10 @@ public: bool canInputRec() override; bool allocBuffers() override; + int getTrackerPreview() const; + int getShift() const; + void setShift(int s); + void reset(int frame); /* fade methods @@ -143,10 +142,9 @@ public: void setXFade(int frame); /* pushWave - Adds a new wave to an existing channel. It also generates a unique name - on request. */ + Adds a new wave to an existing channel. */ - void pushWave(Wave* w, bool generateName=true); + void pushWave(Wave* w); /* getPosition * returns the position of an active sample. If EMPTY o MISSING @@ -168,12 +166,6 @@ public: void setEnd(int f); int getEnd(); void setTrackerPreview(int f); - int getTrackerPreview() const; - - /* save - * save sample to file. */ - - int save(const char* path); /* hardStop * stop the channel immediately, no further checks. */ @@ -191,8 +183,6 @@ public: void setOnEndPreviewCb(std::function f); - /* ------------------------------------------------------------------------ */ - Wave* wave; int tracker; // chan position int mode; // mode: see const.h diff --git a/src/core/storager.cpp b/src/core/storager.cpp index 191d485..6c72e6b 100644 --- a/src/core/storager.cpp +++ b/src/core/storager.cpp @@ -37,116 +37,121 @@ namespace giada { namespace m { namespace storager { -bool setString(json_t *jRoot, const char *key, string &output) +bool setString(json_t* jRoot, const char* key, string& output) { - json_t *jObject = json_object_get(jRoot, key); - if (!json_is_string(jObject)) { - gu_log("[storager::setString] key '%s' is not a string!\n", key); - json_decref(jRoot); - return false; - } - output = json_string_value(jObject); - return true; + json_t* jObject = json_object_get(jRoot, key); + if (!jObject) { + gu_log("[storager::setString] key '%s' not found, using default value\n", key); + output = ""; + return true; + } + if (!json_is_string(jObject)) { + gu_log("[storager::setString] key '%s' is not a string!\n", key); + json_decref(jRoot); + return false; + } + output = json_string_value(jObject); + return true; } /* -------------------------------------------------------------------------- */ -bool setFloat(json_t *jRoot, const char *key, float &output) +bool setFloat(json_t* jRoot, const char* key, float& output) { - json_t *jObject = json_object_get(jRoot, key); - if (!jObject) { - gu_log("[storager::setFloat] key '%s' not found, using default value\n", key); - output = 0.0f; - return true; - } - if (!json_is_real(jObject)) { - gu_log("[storager::setFloat] key '%s' is not a float!\n", key); - json_decref(jRoot); - return false; - } - output = json_real_value(jObject); - return true; + json_t* jObject = json_object_get(jRoot, key); + if (!jObject) { + gu_log("[storager::setFloat] key '%s' not found, using default value\n", key); + output = 0.0f; + return true; + } + if (!json_is_real(jObject)) { + gu_log("[storager::setFloat] key '%s' is not a float!\n", key); + json_decref(jRoot); + return false; + } + output = json_real_value(jObject); + return true; } /* -------------------------------------------------------------------------- */ -bool setUint32(json_t *jRoot, const char *key, uint32_t &output) +bool setUint32(json_t* jRoot, const char* key, uint32_t &output) { - json_t *jObject = json_object_get(jRoot, key); - if (!jObject) { - gu_log("[storager::setUint32] key '%s' not found, using default value\n", key); - output = 0; - return true; - } - if (!json_is_integer(jObject)) { - gu_log("[storager::setUint32] key '%s' is not an integer!\n", key); - json_decref(jRoot); - return false; - } - output = json_integer_value(jObject); - return true; + json_t* jObject = json_object_get(jRoot, key); + if (!jObject) { + gu_log("[storager::setUint32] key '%s' not found, using default value\n", key); + output = 0; + return true; + } + if (!json_is_integer(jObject)) { + gu_log("[storager::setUint32] key '%s' is not an integer!\n", key); + json_decref(jRoot); + return false; + } + output = json_integer_value(jObject); + return true; } /* -------------------------------------------------------------------------- */ -bool setBool(json_t *jRoot, const char *key, bool &output) +bool setBool(json_t* jRoot, const char* key, bool& output) { - json_t *jObject = json_object_get(jRoot, key); - if (!jObject) { - gu_log("[storager::setBool] key '%s' not found, using default value\n", key); - output = false; - return true; - } - if (!json_is_boolean(jObject)) { - gu_log("[storager::setBool] key '%s' is not a boolean!\n", key); - json_decref(jRoot); - return false; - } - output = json_boolean_value(jObject); - return true; + json_t* jObject = json_object_get(jRoot, key); + if (!jObject) { + gu_log("[storager::setBool] key '%s' not found, using default value\n", key); + output = false; + return true; + } + if (!json_is_boolean(jObject)) { + gu_log("[storager::setBool] key '%s' is not a boolean!\n", key); + json_decref(jRoot); + return false; + } + output = json_boolean_value(jObject); + return true; } /* -------------------------------------------------------------------------- */ -bool setInt(json_t *jRoot, const char *key, int &output) +bool setInt(json_t* jRoot, const char* key, int& output) { - return setUint32(jRoot, key, (uint32_t&) output); + return setUint32(jRoot, key, (uint32_t&) output); } /* -------------------------------------------------------------------------- */ -bool checkObject(json_t *jRoot, const char *key) +bool checkObject(json_t* jRoot, const char* key) { - if (!json_is_object(jRoot)) { - gu_log("[DataStorageJson::checkObject] malformed json: %s is not an object!\n", key); - json_decref(jRoot); - return false; - } - return true; + if (!json_is_object(jRoot)) { + gu_log("[DataStorageJson::checkObject] malformed json: %s is not an object!\n", key); + json_decref(jRoot); + return false; + } + return true; } /* -------------------------------------------------------------------------- */ -bool checkArray(json_t *jRoot, const char *key) +bool checkArray(json_t* jRoot, const char* key) { - if (!json_is_array(jRoot)) { - gu_log("[DataStorageJson::checkObject] malformed json: %s is not an array!\n", key); - json_decref(jRoot); - return false; - } - return true; + if (!json_is_array(jRoot)) { + gu_log("[DataStorageJson::checkObject] malformed json: %s is not an array!\n", key); + json_decref(jRoot); + return false; + } + return true; } }}}; // giada::m::storager:: diff --git a/src/core/wave.cpp b/src/core/wave.cpp index 28edd18..f1a6c42 100644 --- a/src/core/wave.cpp +++ b/src/core/wave.cpp @@ -31,6 +31,7 @@ #include // memcpy #include "../utils/fs.h" #include "../utils/log.h" +#include "../utils/string.h" #include "const.h" #include "wave.h" @@ -57,8 +58,7 @@ Wave::Wave(float* data, int size, int channels, int rate, int bits, m_bits (bits), m_logical (false), m_edited (false), - m_path (path), - m_name (gu_stripExt(gu_basename(path))) + m_path (path) { } @@ -83,8 +83,7 @@ Wave::Wave(const Wave& other) m_bits (other.m_bits), m_logical (true), // a cloned wave does not exist on disk m_edited (false), - m_path (other.m_path), - m_name (other.m_name) + m_path (other.m_path) { m_data = new float[m_size]; memcpy(m_data, other.m_data, m_size * sizeof(float)); @@ -126,25 +125,9 @@ string Wave::getBasename(bool ext) const /* -------------------------------------------------------------------------- */ -void Wave::setName(const string& n) -{ - string ext = gu_getExt(m_path); - m_name = gu_stripExt(gu_basename(n)); - m_path = gu_dirname(m_path) + G_SLASH + m_name + "." + ext; - m_logical = true; - - /* A wave with updated m_name must become logical, since the underlying file - does not exist yet. */ -} - - -/* -------------------------------------------------------------------------- */ - - int Wave::getRate() const { return m_rate; } int Wave::getChannels() const { return m_channels; } std::string Wave::getPath() const { return m_path; } -std::string Wave::getName() const { return m_name; } float* Wave::getData() const { return m_data; } int Wave::getSize() const { return m_size / m_channels; } int Wave::getBits() const { return m_bits; } @@ -164,6 +147,15 @@ int Wave::getDuration() const /* -------------------------------------------------------------------------- */ +std::string Wave::getExtension() const +{ + return gu_getExt(m_path); +} + + +/* -------------------------------------------------------------------------- */ + + float* Wave::getFrame(int f) const { assert(f >= 0); @@ -179,7 +171,6 @@ float* Wave::getFrame(int f) const void Wave::setRate(int v) { m_rate = v; } void Wave::setChannels(int v) { m_channels = v; } -void Wave::setPath(const string& p) { m_path = p; } void Wave::setLogical(bool l) { m_logical = l; } void Wave::setEdited(bool e) { m_edited = e; } @@ -187,6 +178,20 @@ void Wave::setEdited(bool e) { m_edited = e; } /* -------------------------------------------------------------------------- */ +void Wave::setPath(const string& p, int id) +{ + if (id == -1) + m_path = p; + else + m_path = gu_stripExt(p) + "-" + gu_toString(id) + "." + gu_getExt(p); + + +} + + +/* -------------------------------------------------------------------------- */ + + void Wave::setData(float* d, int size) { m_data = d; diff --git a/src/core/wave.h b/src/core/wave.h index 7c9f56b..19f7ea8 100644 --- a/src/core/wave.h +++ b/src/core/wave.h @@ -49,7 +49,6 @@ private: bool m_edited; // edited via editor std::string m_path; // E.g. /path/to/my/sample.wav - std::string m_name; // Sample name (can be changed) public: @@ -60,17 +59,21 @@ public: void setRate(int v); void setChannels(int v); - void setPath(const std::string& p); - void setName(const std::string& p); void setData(float* data, int size); void setLogical(bool l); void setEdited(bool e); + /* setPath + Sets new path 'p'. If 'id' != -1 inserts a numeric id next to the file + extension, e.g. : /path/to/sample-[id].wav */ + + void setPath(const std::string& p, int id=-1); + std::string getBasename(bool ext=false) const; + std::string getExtension() const; int getRate() const; int getChannels() const; std::string getPath() const; - std::string getName() const; int getBits() const; float* getData() const; int getSize() const; // with channels count diff --git a/src/core/waveFx.cpp b/src/core/waveFx.cpp index a215a7c..7f278ad 100644 --- a/src/core/waveFx.cpp +++ b/src/core/waveFx.cpp @@ -302,12 +302,28 @@ void smooth(Wave* w, int a, int b) /* -------------------------------------------------------------------------- */ +void shift(Wave* w, int offset) +{ + if (offset < 0) + offset = (w->getSize() + w->getChannels()) + offset; + + float* begin = w->getData(); + float* end = w->getData() + (w->getSize() * w->getChannels()); + + std::rotate(begin, end - (offset * w->getChannels()), end); + w->setEdited(true); +} + + +/* -------------------------------------------------------------------------- */ + + void reverse(Wave* w, int a, int b) { /* https://stackoverflow.com/questions/33201528/reversing-an-array-of-structures-in-c */ - float* begin = (w->getData() + (a * w->getChannels())); - float* end = (w->getData() + (b * w->getChannels())); + float* begin = w->getData() + (a * w->getChannels()); + float* end = w->getData() + (b * w->getChannels()); std::reverse(begin, end); diff --git a/src/core/waveFx.h b/src/core/waveFx.h index 438c7b6..918795c 100644 --- a/src/core/waveFx.h +++ b/src/core/waveFx.h @@ -71,6 +71,8 @@ Flips Wave's data. */ void reverse(Wave* w, int a, int b); +void shift(Wave* w, int offset); + }}}; // giada::m::wfx:: #endif diff --git a/src/core/waveManager.cpp b/src/core/waveManager.cpp index 3e430e4..becf115 100644 --- a/src/core/waveManager.cpp +++ b/src/core/waveManager.cpp @@ -142,8 +142,6 @@ int createEmpty(int size, int samplerate, const string& name, Wave** out) Wave* wave = new Wave(data, size, G_DEFAULT_AUDIO_CHANS, samplerate, G_DEFAULT_BIT_DEPTH, ""); wave->setLogical(true); - wave->setName(name); - wave->setPath(gu_getCurrentPath() + G_SLASH + wave->getName()); *out = wave; @@ -171,7 +169,6 @@ int createFromWave(const Wave* src, int a, int b, Wave** out) Wave* wave = new Wave(data, size, numChans, src->getRate(), src->getBits(), src->getPath()); wave->setLogical(true); - wave->setName(src->getName() + " part"); *out = wave; diff --git a/src/deps/juce-config.h b/src/deps/juce-config.h index ea20a6c..4039c86 100644 --- a/src/deps/juce-config.h +++ b/src/deps/juce-config.h @@ -1,5 +1,5 @@ -#ifndef __JUCE_APPCONFIG_H__ -#define __JUCE_APPCONFIG_H__ +#ifndef JUCE_APPCONFIG_H +#define JUCE_APPCONFIG_H #ifdef _WIN32 diff --git a/src/glue/channel.cpp b/src/glue/channel.cpp index 12f7582..edebcf5 100644 --- a/src/glue/channel.cpp +++ b/src/glue/channel.cpp @@ -113,9 +113,9 @@ int glue_loadChannel(SampleChannel* ch, const string& fname) Channel* glue_addChannel(int column, int type, int size) { - Channel* ch = mh::addChannel(type); - geChannel* gch = G_MainWin->keyboard->addChannel(column, ch, size); - ch->guiChannel = gch; + Channel* ch = mh::addChannel(type); + geChannel* gch = G_MainWin->keyboard->addChannel(column, ch, size); + ch->guiChannel = gch; return ch; } @@ -143,7 +143,7 @@ void glue_deleteChannel(Channel* ch) /* -------------------------------------------------------------------------- */ -void glue_freeChannel(Channel *ch) +void glue_freeChannel(Channel* ch) { if (ch->status == STATUS_PLAY) { if (!gdConfirmWin("Warning", "This action will stop the channel: are you sure?")) @@ -170,20 +170,20 @@ void glue_freeChannel(Channel *ch) /* -------------------------------------------------------------------------- */ -void glue_toggleArm(Channel *ch, bool gui) +void glue_toggleArm(Channel* ch, bool gui) { - ch->armed = !ch->armed; + ch->setArmed(!ch->isArmed()); if (!gui) - ch->guiChannel->arm->value(ch->armed); + ch->guiChannel->arm->value(ch->isArmed()); } /* -------------------------------------------------------------------------- */ -void glue_toggleInputMonitor(Channel *ch) +void glue_toggleInputMonitor(Channel* ch) { - SampleChannel *sch = static_cast(ch); + SampleChannel* sch = static_cast(ch); sch->inputMonitor = !sch->inputMonitor; } @@ -191,10 +191,10 @@ void glue_toggleInputMonitor(Channel *ch) /* -------------------------------------------------------------------------- */ -int glue_cloneChannel(Channel *src) +int glue_cloneChannel(Channel* src) { - Channel *ch = mh::addChannel(src->type); - geChannel *gch = G_MainWin->keyboard->addChannel(src->guiChannel->getColumnIndex(), + Channel* ch = mh::addChannel(src->type); + geChannel* gch = G_MainWin->keyboard->addChannel(src->guiChannel->getColumnIndex(), ch, src->guiChannel->getSize()); ch->guiChannel = gch; @@ -208,7 +208,7 @@ int glue_cloneChannel(Channel *src) /* -------------------------------------------------------------------------- */ -void glue_setVolume(Channel *ch, float v, bool gui, bool editor) +void glue_setVolume(Channel* ch, float v, bool gui, bool editor) { ch->volume = v; @@ -264,7 +264,7 @@ void glue_setPanning(SampleChannel* ch, float val) /* -------------------------------------------------------------------------- */ -void glue_setMute(Channel *ch, bool gui) +void glue_toggleMute(Channel* ch, bool gui) { if (recorder::active && recorder::canRec(ch, clock::isRunning(), mixer::recording)) { if (!ch->mute) { @@ -290,7 +290,25 @@ void glue_setMute(Channel *ch, bool gui) /* -------------------------------------------------------------------------- */ -void glue_setSoloOn(Channel *ch, bool gui) +void glue_toggleSolo(Channel* ch, bool gui) +{ + ch->solo ? glue_setSoloOn(ch, gui) : glue_setSoloOff(ch, gui); +} + + +/* -------------------------------------------------------------------------- */ + + +void glue_kill(Channel* ch) +{ + ch->kill(0); // on frame 0: it's a user-generated event +} + + +/* -------------------------------------------------------------------------- */ + + +void glue_setSoloOn(Channel* ch, bool gui) { /* if there's no solo session, store mute configuration of all chans * and start the session */ @@ -336,7 +354,7 @@ void glue_setSoloOn(Channel *ch, bool gui) /* -------------------------------------------------------------------------- */ -void glue_setSoloOff(Channel *ch, bool gui) +void glue_setSoloOff(Channel* ch, bool gui) { /* if this is uniqueSolo, stop solo session and restore mute status, * else mute this */ @@ -381,7 +399,7 @@ void glue_setSoloOff(Channel *ch, bool gui) /* -------------------------------------------------------------------------- */ -void glue_setBoost(SampleChannel *ch, float val) +void glue_setBoost(SampleChannel* ch, float val) { ch->setBoost(val); gdSampleEditor *gdEditor = static_cast(gu_getSubwindow(G_MainWin, WID_SAMPLE_EDITOR)); @@ -396,7 +414,17 @@ void glue_setBoost(SampleChannel *ch, float val) /* -------------------------------------------------------------------------- */ -void glue_startStopReadingRecs(SampleChannel *ch, bool gui) +void glue_setName(Channel* ch, const string& name) +{ + ch->setName(name); + ch->guiChannel->update(); +} + + +/* -------------------------------------------------------------------------- */ + + +void glue_toggleReadingRecs(SampleChannel* ch, bool gui) { /* When you call glue_startReadingRecs with conf::treatRecsAsLoops, the member value ch->readActions actually is not set to true immediately, because @@ -416,7 +444,7 @@ void glue_startStopReadingRecs(SampleChannel *ch, bool gui) /* -------------------------------------------------------------------------- */ -void glue_startReadingRecs(SampleChannel *ch, bool gui) +void glue_startReadingRecs(SampleChannel* ch, bool gui) { if (conf::treatRecsAsLoops) ch->recStatus = REC_WAITING; @@ -433,7 +461,7 @@ void glue_startReadingRecs(SampleChannel *ch, bool gui) /* -------------------------------------------------------------------------- */ -void glue_stopReadingRecs(SampleChannel *ch, bool gui) +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 diff --git a/src/glue/channel.h b/src/glue/channel.h index 65cd77e..e3b9468 100644 --- a/src/glue/channel.h +++ b/src/glue/channel.h @@ -40,55 +40,51 @@ class gdSampleEditor; /* addChannel * add an empty new channel to the stack. Returns the new channel. */ -Channel *glue_addChannel(int column, int type, int size); +Channel* glue_addChannel(int column, int type, int size); /* loadChannel * fill an existing channel with a wave. */ -int glue_loadChannel(SampleChannel *ch, const std::string &fname); +int glue_loadChannel(SampleChannel* ch, const std::string& fname); /* deleteChannel * Remove a channel from Mixer. */ -void glue_deleteChannel(Channel *ch); +void glue_deleteChannel(Channel* ch); /* freeChannel * Unload the sample from a sample channel. */ -void glue_freeChannel(Channel *ch); +void glue_freeChannel(Channel* ch); /* cloneChannel * Make an exact copy of Channel *ch. */ -int glue_cloneChannel(Channel *ch); +int glue_cloneChannel(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(Channel *ch, bool gui=true); -void glue_toggleInputMonitor(Channel *ch); -void glue_setMute(Channel *ch, bool gui=true); -void glue_setSoloOn (Channel *ch, bool gui=true); -void glue_setSoloOff(Channel *ch, bool gui=true); -void glue_setVolume(Channel *ch, float v, bool gui=true, bool editor=false); - -/* TODO move to glue_sampleEditor */ -void glue_setPitch(SampleChannel *ch, float val); - -/* TODO move to glue_sampleEditor */ -void glue_setPanning(SampleChannel *ch, float val); - -/* TODO move to glue_sampleEditor */ -void glue_setBoost(SampleChannel *ch, float val); - -/* start/stopReadingRecs +Toggles 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(Channel* ch, bool gui=true); +void glue_toggleInputMonitor(Channel* ch); +void glue_kill(Channel* ch); +void glue_toggleMute(Channel* ch, bool gui=true); +void glue_setSoloOn(Channel* ch, bool gui=true); +void glue_setSoloOff(Channel* ch, bool gui=true); +void glue_toggleSolo(Channel* ch, bool gui=true); +void glue_setVolume(Channel* ch, float v, bool gui=true, bool editor=false); +void glue_setName(Channel* ch, const std::string& name); +void glue_setPitch(SampleChannel* ch, float val); +void glue_setPanning(SampleChannel* ch, float val); +void glue_setBoost(SampleChannel* ch, float val); + +/* toggleReadingRecs 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(SampleChannel *ch, bool gui=true); -void glue_startReadingRecs (SampleChannel *ch, bool gui=true); -void glue_stopReadingRecs (SampleChannel *ch, bool gui=true); +void glue_toggleReadingRecs(SampleChannel* ch, bool gui=true); +void glue_startReadingRecs(SampleChannel* ch, bool gui=true); +void glue_stopReadingRecs(SampleChannel* ch, bool gui=true); #endif diff --git a/src/glue/io.cpp b/src/glue/io.cpp index 31d6781..0171457 100644 --- a/src/glue/io.cpp +++ b/src/glue/io.cpp @@ -81,7 +81,7 @@ void glue_keyRelease(Channel *ch, bool ctrl, bool shift) void glue_keyPress(MidiChannel *ch, bool ctrl, bool shift) { if (ctrl) - glue_setMute(ch); + glue_toggleMute(ch); else if (shift) ch->kill(0); // on frame 0: user-generated event @@ -98,7 +98,7 @@ void glue_keyPress(SampleChannel *ch, bool ctrl, bool shift) /* case CTRL */ if (ctrl) - glue_setMute(ch); + glue_toggleMute(ch); /* case SHIFT * @@ -320,7 +320,7 @@ int glue_stopInputRec(bool gui) if (mixer::channels.at(i)->type == CHANNEL_MIDI) continue; SampleChannel *ch = (SampleChannel*) mixer::channels.at(i); - if (ch->mode & (LOOP_ANY) && ch->status == STATUS_OFF && ch->armed) + if (ch->mode & (LOOP_ANY) && ch->status == STATUS_OFF && ch->isArmed()) ch->start(clock::getCurrentFrame(), true, clock::getQuantize(), clock::isRunning(), true, true); } diff --git a/src/glue/plugin.cpp b/src/glue/plugin.cpp index d8320c6..c7928d6 100644 --- a/src/glue/plugin.cpp +++ b/src/glue/plugin.cpp @@ -32,25 +32,36 @@ #include "../core/mixer.h" #include "../core/plugin.h" #include "../core/channel.h" +#include "../core/const.h" +#include "../utils/gui.h" +#include "../gui/dialogs/gd_mainWindow.h" +#include "../gui/dialogs/pluginWindow.h" +#include "../gui/dialogs/pluginList.h" #include "plugin.h" +extern gdMainWindow* G_MainWin; + + using namespace giada::m; -Plugin *glue_addPlugin(Channel *ch, int index, int stackType) +namespace giada { +namespace c { +namespace plugin +{ +Plugin* addPlugin(Channel* ch, int index, int stackType) { if (index >= pluginHost::countAvailablePlugins()) return nullptr; - - return pluginHost::addPlugin(index, stackType, &mixer::mutex_plugins, ch); + return pluginHost::addPlugin(index, stackType, &mixer::mutex_plugins, ch); } /* -------------------------------------------------------------------------- */ -void glue_swapPlugins(Channel *ch, int index1, int index2, int stackType) +void swapPlugins(Channel* ch, int index1, int index2, int stackType) { pluginHost::swapPlugin(index1, index2, stackType, &mixer::mutex_plugins, ch); @@ -60,10 +71,36 @@ void glue_swapPlugins(Channel *ch, int index1, int index2, int stackType) /* -------------------------------------------------------------------------- */ -void glue_freePlugin(Channel *ch, int index, int stackType) +void freePlugin(Channel* ch, int index, int stackType) { pluginHost::freePlugin(index, stackType, &mixer::mutex_plugins, ch); } +/* -------------------------------------------------------------------------- */ + + +void setParameter(Plugin* p, int index, float value, bool gui) +{ + p->setParameter(index, value); + + /* No need to update plug-in editor if it has one: the plug-in's editor takes + care of it on its own. Conversely, update the specific parameter for UI-less + plug-ins. */ + + if (p->hasEditor()) + return; + + gdPluginList* parent = static_cast(gu_getSubwindow(G_MainWin, WID_FX_LIST)); + gdPluginWindow* child = static_cast(gu_getSubwindow(parent, p->getId() + 1)); + if (child != nullptr) { + Fl::lock(); + child->updateParameter(index, !gui); + Fl::unlock(); + } +} + +}}}; // giada::c::plugin:: + + #endif diff --git a/src/glue/plugin.h b/src/glue/plugin.h index 9f95d0c..72041ff 100644 --- a/src/glue/plugin.h +++ b/src/glue/plugin.h @@ -34,11 +34,21 @@ #ifdef WITH_VST -class Plugin *glue_addPlugin(class Channel *ch, int index, int stackType); -void glue_swapPlugins(class Channel *ch, int indexP1, int indexP2, int stackType); +class Plugin; +class Channel; + + +namespace giada { +namespace c { +namespace plugin +{ +Plugin* addPlugin(Channel* ch, int index, int stackType); +void swapPlugins(Channel* ch, int indexP1, int indexP2, int stackType); +void freePlugin(Channel* ch, int index, int stackType); +void setParameter(Plugin* p, int index, float value, bool gui=true); +}}}; // giada::c::plugin:: -void glue_freePlugin(class Channel *ch, int index, int stackType); #endif diff --git a/src/glue/sampleEditor.cpp b/src/glue/sampleEditor.cpp index 0c20347..f5b037e 100644 --- a/src/glue/sampleEditor.cpp +++ b/src/glue/sampleEditor.cpp @@ -37,6 +37,7 @@ #include "../gui/elems/sampleEditor/panTool.h" #include "../gui/elems/sampleEditor/pitchTool.h" #include "../gui/elems/sampleEditor/rangeTool.h" +#include "../gui/elems/sampleEditor/shiftTool.h" #include "../gui/elems/sampleEditor/waveform.h" #include "../gui/elems/mainWindow/keyboard/channel.h" #include "../core/sampleChannel.h" @@ -103,6 +104,7 @@ void setBeginEnd(SampleChannel* ch, int b, int e) void cut(SampleChannel* ch, int a, int b) { + copy(ch, a, b); if (!wfx::cut(ch->wave, a, b)) { gdAlert("Unable to cut the sample!"); return; @@ -136,8 +138,10 @@ void copy(SampleChannel* ch, int a, int b) void paste(SampleChannel* ch, int a) { - if (!isWaveBufferFull()) + if (!isWaveBufferFull()) { + gu_log("[sampleEditor::paste] Buffer is empty, nothing to paste\n"); return; + } wfx::paste(m_waveBuffer, ch->wave, a); @@ -278,8 +282,7 @@ void toNewChannel(SampleChannel* ch, int a, int b) return; } - newCh->pushWave(wave, true); - + newCh->pushWave(wave); newCh->guiChannel->update(); } @@ -292,4 +295,18 @@ bool isWaveBufferFull() return m_waveBuffer != nullptr; } + +/* -------------------------------------------------------------------------- */ + + +void shift(SampleChannel* ch, int offset) +{ + wfx::shift(ch->wave, offset - ch->getShift()); + ch->setShift(offset); + gdSampleEditor* gdEditor = getSampleEditorWindow(); + gdEditor->shiftTool->refresh(); + gdEditor->waveTools->waveform->refresh(); +} + + }}}; // giada::c::sampleEditor:: diff --git a/src/glue/sampleEditor.h b/src/glue/sampleEditor.h index 3846f9f..e6d1221 100644 --- a/src/glue/sampleEditor.h +++ b/src/glue/sampleEditor.h @@ -59,6 +59,7 @@ void normalizeHard(SampleChannel* ch, int a, int b); void silence(SampleChannel* ch, int a, int b); void fade(SampleChannel* ch, int a, int b, int type); void smoothEdges(SampleChannel* ch, int a, int b); +void shift(SampleChannel* ch, int offset); bool isWaveBufferFull(); diff --git a/src/glue/storage.cpp b/src/glue/storage.cpp index da1a80f..c9db5ee 100644 --- a/src/glue/storage.cpp +++ b/src/glue/storage.cpp @@ -34,10 +34,12 @@ #include "../core/patch.h" #include "../core/sampleChannel.h" #include "../core/midiChannel.h" +#include "../core/waveManager.h" #include "../core/clock.h" #include "../core/wave.h" #include "../utils/gui.h" #include "../utils/log.h" +#include "../utils/string.h" #include "../utils/fs.h" #include "../gui/elems/basics/progress.h" #include "../gui/elems/mainWindow/keyboard/column.h" @@ -61,7 +63,7 @@ using namespace giada::m; #ifdef WITH_VST -static void __glue_fillPatchGlobalsPlugins__(vector *host, vector *patch) +static void glue_fillPatchGlobalsPlugins__(vector *host, vector *patch) { for (unsigned i=0; isize(); i++) { Plugin *pl = host->at(i); @@ -81,7 +83,7 @@ static void __glue_fillPatchGlobalsPlugins__(vector *host, vectorkeyboard->getTotalColumns(); i++) { geColumn *gCol = G_MainWin->keyboard->getColumn(i); @@ -106,7 +108,7 @@ static void __glue_fillPatchColumns__() /* -------------------------------------------------------------------------- */ -static void __glue_fillPatchChannels__(bool isProject) +static void glue_fillPatchChannels__(bool isProject) { for (unsigned i=0; iwritePatch(i, isProject); @@ -117,7 +119,7 @@ static void __glue_fillPatchChannels__(bool isProject) /* -------------------------------------------------------------------------- */ -static void __glue_fillPatchGlobals__(const string &name) +static void glue_fillPatchGlobals__(const string &name) { patch::version = G_VERSION_STR; patch::versionMajor = G_VERSION_MAJOR; @@ -134,9 +136,9 @@ static void __glue_fillPatchGlobals__(const string &name) #ifdef WITH_VST - __glue_fillPatchGlobalsPlugins__(pluginHost::getStack(pluginHost::MASTER_IN), + glue_fillPatchGlobalsPlugins__(pluginHost::getStack(pluginHost::MASTER_IN), &patch::masterInPlugins); - __glue_fillPatchGlobalsPlugins__(pluginHost::getStack(pluginHost::MASTER_OUT), + glue_fillPatchGlobalsPlugins__(pluginHost::getStack(pluginHost::MASTER_OUT), &patch::masterOutPlugins); #endif @@ -146,14 +148,14 @@ static void __glue_fillPatchGlobals__(const string &name) /* -------------------------------------------------------------------------- */ -static bool __glue_savePatch__(const string &fullPath, const string &name, +static bool glue_savePatch__(const string &fullPath, const string &name, bool isProject) { patch::init(); - __glue_fillPatchGlobals__(name); - __glue_fillPatchChannels__(isProject); - __glue_fillPatchColumns__(); + glue_fillPatchGlobals__(name); + glue_fillPatchChannels__(isProject); + glue_fillPatchColumns__(); if (patch::write(fullPath)) { gu_updateMainWinLabel(name); @@ -167,11 +169,34 @@ static bool __glue_savePatch__(const string &fullPath, const string &name, /* -------------------------------------------------------------------------- */ -void glue_savePatch(void *data) +static string glue_makeSamplePath__(const string& base, const Wave* w, int k) { - gdBrowserSave *browser = (gdBrowserSave*) data; - string name = browser->getName(); - string fullPath = browser->getCurrentPath() + G_SLASH + gu_stripExt(name) + ".gptc"; + return base + G_SLASH + w->getBasename(false) + "-" + gu_toString(k) + "." + w->getExtension(); +} + + +static string glue_makeUniqueSamplePath__(const string& base, const SampleChannel* ch) +{ + string path = base + G_SLASH + ch->wave->getBasename(true); + if (mh::uniqueSamplePath(ch, path)) + return path; + + int k = 0; + path = glue_makeSamplePath__(base, ch->wave, k); + while (!mh::uniqueSamplePath(ch, path)) + path = glue_makeSamplePath__(base, ch->wave, k++); + return path; +} + + +/* -------------------------------------------------------------------------- */ + + +void glue_savePatch(void* data) +{ + gdBrowserSave* browser = (gdBrowserSave*) data; + string name = gu_stripExt(browser->getName()); + string fullPath = browser->getCurrentPath() + G_SLASH + name + ".gptc"; if (name == "") { gdAlert("Please choose a file name."); @@ -182,7 +207,7 @@ void glue_savePatch(void *data) if (!gdConfirmWin("Warning", "File exists: overwrite?")) return; - if (__glue_savePatch__(fullPath, name, false)) { // false == not a project + if (glue_savePatch__(fullPath, name, false)) { // false == not a project conf::patchPath = gu_dirname(fullPath); browser->do_callback(); } @@ -222,58 +247,57 @@ void glue_loadPatch(void* data) return; } - /* close all other windows. This prevents segfault if plugin - * windows GUIs are on. */ + /* Close all other windows. This prevents segfault if plugin windows GUIs are + open. */ gu_closeAllSubwindows(); - /* reset the system. False(1): don't update the gui right now. False(2): do - * not create empty columns. */ + /* Reset the system. False(1): don't update the gui right now. False(2): do + not create empty columns. */ glue_resetToInitState(false, false); browser->setStatusBar(0.1f); - //__glue_setProgressBar__(status, 0.1f); - /* Add common stuff, columns and channels. Also increment the progress bar - * by 0.8 / total_channels steps. */ + /* Add common stuff, columns and channels. Also increment the progress bar by + 0.8 / total_channels steps. */ float steps = 0.8 / patch::channels.size(); for (const patch::column_t& col : patch::columns) { G_MainWin->keyboard->addColumn(col.width); - for (unsigned k=0; kreadPatch(basePath, k, &mixer::mutex_plugins, conf::samplerate, conf::rsmpQuality); } browser->setStatusBar(steps); + k++; } } - /* fill Mixer */ + /* Fill Mixer. */ mh::readPatch(); - /* let recorder recompute the actions' positions if the current - * samplerate != patch samplerate */ + /* Let recorder recompute the actions' positions if the current + samplerate != patch samplerate. */ recorder::updateSamplerate(conf::samplerate, patch::samplerate); - /* save patchPath by taking the last dir of the broswer, in order to - * reuse it the next time */ + /* Save patchPath by taking the last dir of the broswer, in order to reuse it + the next time. */ conf::patchPath = gu_dirname(fullPath); - /* refresh GUI */ + /* Refresh GUI. */ gu_updateControls(); gu_updateMainWinLabel(patch::name); browser->setStatusBar(0.1f); - //__glue_setProgressBar__(status, 1.0f); gu_log("[glue] patch loaded successfully\n"); @@ -291,12 +315,12 @@ void glue_loadPatch(void* data) /* -------------------------------------------------------------------------- */ -void glue_saveProject(void *data) +void glue_saveProject(void* data) { - gdBrowserSave *browser = (gdBrowserSave*) data; - string name = browser->getName(); - string folderPath = browser->getCurrentPath(); //browser->getSelectedItem(); - string fullPath = folderPath + G_SLASH + gu_stripExt(name) + ".gprj"; + gdBrowserSave* browser = (gdBrowserSave*) data; + string name = gu_stripExt(browser->getName()); + string folderPath = browser->getCurrentPath(); + string fullPath = folderPath + G_SLASH + name + ".gprj"; if (name == "") { gdAlert("Please choose a project name."); @@ -307,38 +331,36 @@ void glue_saveProject(void *data) return; if (!gu_dirExists(fullPath) && !gu_mkdir(fullPath)) { - gu_log("[glue_saveProject] unable to make project directory!\n"); + gu_log("[glue_saveProject] Unable to make project directory!\n"); return; } - gu_log("[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() */ + /* Copy all samples inside the folder. Takes and logical ones are saved via + glue_saveSample(). Update the new sample path: everything now comes from the + project folder (folderPath). Also make sure the file path is unique inside the + project folder.*/ - for (unsigned i=0; itype == CHANNEL_MIDI) + if (ch->type == CHANNEL_MIDI) continue; - SampleChannel *ch = (SampleChannel*) mixer::channels.at(i); + const SampleChannel* sch = static_cast(ch); - if (ch->wave == nullptr) + if (sch->wave == nullptr) continue; - /* update the new samplePath: everything now comes from the project - * folder (folderPath). Also remove any existing file. */ + sch->wave->setPath(glue_makeUniqueSamplePath__(fullPath, sch)); - string samplePath = fullPath + G_SLASH + ch->wave->getBasename(true); + gu_log("[glue_saveProject] Save file to %s\n", sch->wave->getPath().c_str()); - if (gu_fileExists(samplePath)) - remove(samplePath.c_str()); - if (ch->save(samplePath.c_str())) - ch->wave->setPath(samplePath); + waveManager::save(sch->wave, sch->wave->getPath()); // TODO - error checking } - string gptcPath = fullPath + G_SLASH + gu_stripExt(name) + ".gptc"; - if (__glue_savePatch__(gptcPath, name, true)) // true == it's a project + string gptcPath = fullPath + G_SLASH + name + ".gptc"; + if (glue_savePatch__(gptcPath, name, true)) // true == it's a project browser->do_callback(); else gdAlert("Unable to save the project!"); @@ -374,7 +396,7 @@ void glue_loadSample(void* data) void glue_saveSample(void *data) { - gdBrowserSave *browser = (gdBrowserSave*) data; + gdBrowserSave* browser = (gdBrowserSave*) data; string name = browser->getName(); string folderPath = browser->getCurrentPath(); @@ -391,7 +413,9 @@ void glue_saveSample(void *data) if (!gdConfirmWin("Warning", "File exists: overwrite?")) return; - if (static_cast(browser->getChannel())->save(filePath.c_str())) { + SampleChannel* ch = static_cast(browser->getChannel()); + + if (waveManager::save(ch->wave, filePath)) { gu_log("[glue_saveSample] sample saved to %s\n", filePath.c_str()); conf::samplePath = gu_dirname(filePath); browser->do_callback(); diff --git a/src/gui/dialogs/gd_beatsInput.cpp b/src/gui/dialogs/beatsInput.cpp similarity index 76% rename from src/gui/dialogs/gd_beatsInput.cpp rename to src/gui/dialogs/beatsInput.cpp index a00c2d7..3d77b6f 100644 --- a/src/gui/dialogs/gd_beatsInput.cpp +++ b/src/gui/dialogs/beatsInput.cpp @@ -25,7 +25,9 @@ * -------------------------------------------------------------------------- */ +#include #include "../../utils/gui.h" +#include "../../utils/string.h" #include "../../core/mixer.h" #include "../../core/clock.h" #include "../../core/conf.h" @@ -34,40 +36,41 @@ #include "../elems/basics/input.h" #include "../elems/basics/button.h" #include "../elems/basics/check.h" -#include "gd_beatsInput.h" +#include "beatsInput.h" #include "gd_mainWindow.h" -extern gdMainWindow *mainWin; +extern gdMainWindow* mainWin; using namespace giada::m; gdBeatsInput::gdBeatsInput() - : gdWindow(164, 60, "Beats") + : gdWindow(180, 60, "Beats") { if (conf::beatsX) resize(conf::beatsX, conf::beatsY, w(), h()); set_modal(); - beats = new geInput(8, 8, 35, 20); - bars = new geInput(47, 8, 35, 20); - ok = new geButton(86, 8, 70, 20, "Ok"); + beats = new geInput(8, 8, 43, G_GUI_UNIT); + bars = new geInput(beats->x()+beats->w()+4, 8, 43, G_GUI_UNIT); + ok = new geButton(bars->x()+bars->w()+4, 8, 70, G_GUI_UNIT, "Ok"); resizeRec = new geCheck(8, 40, 12, 12, "resize recorded actions"); end(); - char buf_bars[3]; sprintf(buf_bars, "%d", clock::getBars()); - char buf_beats[3]; sprintf(buf_beats, "%d", clock::getBeats()); beats->maximum_size(2); - beats->value(buf_beats); + beats->value(gu_toString(clock::getBeats()).c_str()); beats->type(FL_INT_INPUT); + bars->maximum_size(2); - bars->value(buf_bars); + bars->value(gu_toString(clock::getBars()).c_str()); bars->type(FL_INT_INPUT); + ok->shortcut(FL_Enter); - ok->callback(cb_update_batt, (void*)this); + ok->callback(cb_update, (void*)this); + resizeRec->value(conf::resizeRecordings); gu_setFavicon(this); @@ -76,27 +79,27 @@ gdBeatsInput::gdBeatsInput() } -/* ------------------------------------------------------------------ */ +/* -------------------------------------------------------------------------- */ gdBeatsInput::~gdBeatsInput() { conf::beatsX = x(); conf::beatsY = y(); - conf::resizeRecordings = resizeRec->value(); + conf::resizeRecordings = resizeRec->value(); } -/* ------------------------------------------------------------------ */ +/* -------------------------------------------------------------------------- */ -void gdBeatsInput::cb_update_batt(Fl_Widget *w, void *p) { ((gdBeatsInput*)p)->__cb_update_batt(); } +void gdBeatsInput::cb_update(Fl_Widget* w, void* p) { ((gdBeatsInput*)p)->cb_update(); } -/* ------------------------------------------------------------------ */ +/* -------------------------------------------------------------------------- */ -void gdBeatsInput::__cb_update_batt() +void gdBeatsInput::cb_update() { if (!strcmp(beats->value(), "") || !strcmp(bars->value(), "")) return; diff --git a/src/gui/dialogs/gd_beatsInput.h b/src/gui/dialogs/beatsInput.h similarity index 84% rename from src/gui/dialogs/gd_beatsInput.h rename to src/gui/dialogs/beatsInput.h index 90150ea..b1d0bb8 100644 --- a/src/gui/dialogs/gd_beatsInput.h +++ b/src/gui/dialogs/beatsInput.h @@ -1,8 +1,8 @@ -/* --------------------------------------------------------------------- +/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * - * --------------------------------------------------------------------- + * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual * @@ -22,13 +22,13 @@ * along with Giada - Your Hardcore Loopmachine. If not, see * . * - * ------------------------------------------------------------------ */ + * -------------------------------------------------------------------------- */ + #ifndef GD_BEATSINPUT_H #define GD_BEATSINPUT_H -#include -#include + #include "window.h" @@ -41,13 +41,13 @@ class gdBeatsInput : public gdWindow { private: - static void cb_update_batt(Fl_Widget *w, void *p); - inline void __cb_update_batt(); + static void cb_update(Fl_Widget* w, void* p); + void cb_update(); - geInput *beats; - geInput *bars; - geButton *ok; - geCheck *resizeRec; + geInput* beats; + geInput* bars; + geButton* ok; + geCheck* resizeRec; public: diff --git a/src/gui/dialogs/gd_bpmInput.cpp b/src/gui/dialogs/bpmInput.cpp similarity index 77% rename from src/gui/dialogs/gd_bpmInput.cpp rename to src/gui/dialogs/bpmInput.cpp index c3fbe0a..f76c629 100644 --- a/src/gui/dialogs/gd_bpmInput.cpp +++ b/src/gui/dialogs/bpmInput.cpp @@ -32,19 +32,22 @@ #include "../../core/clock.h" #include "../../glue/main.h" #include "../../utils/gui.h" +#include "../../utils/string.h" #include "../elems/basics/button.h" #include "../elems/basics/input.h" -#include "gd_bpmInput.h" +#include "bpmInput.h" #include "gd_mainWindow.h" extern gdMainWindow *mainWin; +using std::vector; +using std::string; using namespace giada::m; -gdBpmInput::gdBpmInput(const char *label) +gdBpmInput::gdBpmInput(const char* label) : gdWindow(144, 36, "Bpm") { if (conf::bpmX) @@ -52,29 +55,25 @@ gdBpmInput::gdBpmInput(const char *label) set_modal(); - input_a = new geInput(8, 8, 30, 20); - input_b = new geInput(42, 8, 20, 20); - ok = new geButton(66, 8, 70, 20, "Ok"); + input_a = new geInput(8, 8, 30, G_GUI_UNIT); + input_b = new geInput(42, 8, 20, G_GUI_UNIT); + ok = new geButton(66, 8, 70, G_GUI_UNIT, "Ok"); end(); - char a[4]; - snprintf(a, 4, "%d", (int) clock::getBpm()); - char b[2]; - for (unsigned i=0; imaximum_size(3); input_a->type(FL_INT_INPUT); - input_a->value(a); + input_a->value(gu_toString(clock::getBpm()).c_str()); + + /* Use the decimal value from the string label. */ + + vector tokens; + gu_split(label, ".", &tokens); input_b->maximum_size(1); input_b->type(FL_INT_INPUT); - input_b->value(b); + input_b->value(tokens[1].c_str()); ok->shortcut(FL_Enter); - ok->callback(cb_update_bpm, (void*)this); + ok->callback(cb_update, (void*)this); gu_setFavicon(this); setId(WID_BPM); @@ -95,13 +94,13 @@ gdBpmInput::~gdBpmInput() /* -------------------------------------------------------------------------- */ -void gdBpmInput::cb_update_bpm(Fl_Widget *w, void *p) { ((gdBpmInput*)p)->__cb_update_bpm(); } +void gdBpmInput::cb_update(Fl_Widget* w, void* p) { ((gdBpmInput*)p)->cb_update(); } /* -------------------------------------------------------------------------- */ -void gdBpmInput::__cb_update_bpm() +void gdBpmInput::cb_update() { if (strcmp(input_a->value(), "") == 0) return; diff --git a/src/gui/dialogs/gd_bpmInput.h b/src/gui/dialogs/bpmInput.h similarity index 86% rename from src/gui/dialogs/gd_bpmInput.h rename to src/gui/dialogs/bpmInput.h index 0f7a166..3a994ef 100644 --- a/src/gui/dialogs/gd_bpmInput.h +++ b/src/gui/dialogs/bpmInput.h @@ -40,16 +40,16 @@ class gdBpmInput : public gdWindow { private: - static void cb_update_bpm(Fl_Widget *w, void *p); - inline void __cb_update_bpm(); + static void cb_update(Fl_Widget* w, void* p); + void cb_update(); - geInput *input_a; - geInput *input_b; - geButton *ok; + geInput* input_a; + geInput* input_b; + geButton* ok; public: - gdBpmInput(const char *label); // pointer to mainWin->timing->bpm->label() + gdBpmInput(const char* label); // pointer to mainWin->timing->bpm->label() ~gdBpmInput(); }; diff --git a/src/gui/dialogs/browser/browserBase.cpp b/src/gui/dialogs/browser/browserBase.cpp index a1435d4..5903962 100644 --- a/src/gui/dialogs/browser/browserBase.cpp +++ b/src/gui/dialogs/browser/browserBase.cpp @@ -42,15 +42,15 @@ using std::string; using namespace giada::m; -gdBrowserBase::gdBrowserBase(int x, int y, int w, int h, const string &title, - const string &path, void (*callback)(void*)) +gdBrowserBase::gdBrowserBase(int x, int y, int w, int h, const string& title, + const string& path, void (*callback)(void*)) : gdWindow(x, y, w, h, title.c_str()), callback(callback) { set_non_modal(); groupTop = new Fl_Group(8, 8, w-16, 40); hiddenFiles = new geCheck(groupTop->x(), groupTop->y(), 400, 20, "Show hidden files"); - where = new geInput(groupTop->x(), hiddenFiles->y()+hiddenFiles->h(), 152, 20); + where = new geInput(groupTop->x(), hiddenFiles->y()+hiddenFiles->h(), 20, 20); updir = new geButton(groupTop->x()+groupTop->w()-20, where->y(), 20, 20, "", updirOff_xpm, updirOn_xpm); groupTop->end(); groupTop->resizable(where); @@ -108,18 +108,31 @@ gdBrowserBase::~gdBrowserBase() /* -------------------------------------------------------------------------- */ -void gdBrowserBase::cb_up (Fl_Widget *v, void *p) { ((gdBrowserBase*)p)->__cb_up(); } -void gdBrowserBase::cb_close(Fl_Widget *v, void *p) { ((gdBrowserBase*)p)->__cb_close(); } -void gdBrowserBase::cb_toggleHiddenFiles(Fl_Widget *v, void *p) { ((gdBrowserBase*)p)->__cb_toggleHiddenFiles(); } +void gdBrowserBase::cb_up (Fl_Widget* v, void* p) { ((gdBrowserBase*)p)->cb_up(); } +void gdBrowserBase::cb_close(Fl_Widget* v, void* p) { ((gdBrowserBase*)p)->cb_close(); } +void gdBrowserBase::cb_toggleHiddenFiles(Fl_Widget *v, void *p) { ((gdBrowserBase*)p)->cb_toggleHiddenFiles(); } /* -------------------------------------------------------------------------- */ -void gdBrowserBase::__cb_up() +void gdBrowserBase::cb_up() { string dir = browser->getCurrentDir(); - dir = dir.substr(0, dir.find_last_of(G_SLASH_STR)); // remove up to the next slash + + /* Take 'dir' path and remove all chars up to the next slash, e.g.: + /path/to/my/dir -> /path/to/my + Make sure not to remove the leading '/' (OS X/Linux only). */ + + dir = dir.substr(0, dir.find_last_of(G_SLASH_STR)); + +#if defined(G_OS_MAC) || defined(G_OS_LINUX) + + if (dir.empty()) + dir = G_SLASH_STR; + +#endif + browser->loadDir(dir); where->value(browser->getCurrentDir().c_str()); } @@ -128,7 +141,7 @@ void gdBrowserBase::__cb_up() /* -------------------------------------------------------------------------- */ -void gdBrowserBase::__cb_close() +void gdBrowserBase::cb_close() { do_callback(); } @@ -137,7 +150,7 @@ void gdBrowserBase::__cb_close() /* -------------------------------------------------------------------------- */ -void gdBrowserBase::__cb_toggleHiddenFiles() +void gdBrowserBase::cb_toggleHiddenFiles() { browser->toggleHiddenFiles(); } diff --git a/src/gui/dialogs/browser/browserBase.h b/src/gui/dialogs/browser/browserBase.h index f96dab1..9ca486a 100644 --- a/src/gui/dialogs/browser/browserBase.h +++ b/src/gui/dialogs/browser/browserBase.h @@ -45,32 +45,31 @@ class gdBrowserBase : public gdWindow { protected: - Channel *channel; - - Fl_Group *groupTop; - geCheck *hiddenFiles; - geBrowser *browser; - geButton *ok; - geButton *cancel; - geInput *where; - geButton *updir; - geProgress *status; - - static void cb_up (Fl_Widget *v, void *p); - static void cb_close (Fl_Widget *w, void *p); - static void cb_toggleHiddenFiles(Fl_Widget *w, void *p); - - inline void __cb_up (); - inline void __cb_close (); - inline void __cb_toggleHiddenFiles(); + Channel* channel; + + Fl_Group* groupTop; + geCheck* hiddenFiles; + geBrowser* browser; + geButton* ok; + geButton* cancel; + geInput* where; + geButton* updir; + geProgress* status; + + static void cb_up(Fl_Widget* v, void* p); + static void cb_close(Fl_Widget* w, void* p); + static void cb_toggleHiddenFiles(Fl_Widget* w, void* p); + void cb_up(); + void cb_close(); + void cb_toggleHiddenFiles(); /* Callback * Fired when the save/load button is pressed. */ void (*callback)(void*); - gdBrowserBase(int x, int y, int w, int h, const std::string &title, - const std::string &path, void (*callback)(void*)); + gdBrowserBase(int x, int y, int w, int h, const std::string& title, + const std::string& path, void (*callback)(void*)); public: @@ -88,9 +87,9 @@ public: void showStatusBar(); void hideStatusBar(); - std::string getCurrentPath(); + std::string getCurrentPath(); - Channel *getChannel() { return channel; } + Channel* getChannel() { return channel; } void fireCallback() { callback((void*) this); } }; diff --git a/src/gui/dialogs/browser/browserLoad.cpp b/src/gui/dialogs/browser/browserLoad.cpp index 4717fdd..d8e9dd1 100644 --- a/src/gui/dialogs/browser/browserLoad.cpp +++ b/src/gui/dialogs/browser/browserLoad.cpp @@ -35,8 +35,8 @@ using std::string; -gdBrowserLoad::gdBrowserLoad(int x, int y, int w, int h, const string &title, - const string &path, void (*cb)(void*), Channel *ch) +gdBrowserLoad::gdBrowserLoad(int x, int y, int w, int h, const string& title, + const string& path, void (*cb)(void*), Channel* ch) : gdBrowserBase(x, y, w, h, title, path, cb) { channel = ch; @@ -48,20 +48,25 @@ gdBrowserLoad::gdBrowserLoad(int x, int y, int w, int h, const string &title, ok->label("Load"); ok->callback(cb_load, (void*) this); ok->shortcut(FL_ENTER); + + /* On OS X the 'where' input doesn't get resized properly on startup. Let's + force it. */ + + where->redraw(); } /* -------------------------------------------------------------------------- */ -void gdBrowserLoad::cb_load(Fl_Widget *v, void *p) { ((gdBrowserLoad*)p)->__cb_load(); } -void gdBrowserLoad::cb_down(Fl_Widget *v, void *p) { ((gdBrowserLoad*)p)->__cb_down(); } +void gdBrowserLoad::cb_load(Fl_Widget* v, void* p) { ((gdBrowserLoad*)p)->cb_load(); } +void gdBrowserLoad::cb_down(Fl_Widget* v, void* p) { ((gdBrowserLoad*)p)->cb_down(); } /* -------------------------------------------------------------------------- */ -void gdBrowserLoad::__cb_load() +void gdBrowserLoad::cb_load() { callback((void*) this); } @@ -70,7 +75,7 @@ void gdBrowserLoad::__cb_load() /* -------------------------------------------------------------------------- */ -void gdBrowserLoad::__cb_down() +void gdBrowserLoad::cb_down() { string path = browser->getSelectedItem(); diff --git a/src/gui/dialogs/browser/browserLoad.h b/src/gui/dialogs/browser/browserLoad.h index b1ad762..e361484 100644 --- a/src/gui/dialogs/browser/browserLoad.h +++ b/src/gui/dialogs/browser/browserLoad.h @@ -46,16 +46,15 @@ class gdBrowserLoad : public gdBrowserBase { private: - static void cb_load(Fl_Widget *w, void *p); - static void cb_down(Fl_Widget *v, void *p); - - inline void __cb_load(); - inline void __cb_down(); + static void cb_load(Fl_Widget* w, void* p); + static void cb_down(Fl_Widget* v, void* p); + void cb_load(); + void cb_down(); public: - gdBrowserLoad(int x, int y, int w, int h, const std::string &title, - const std::string &path, void (*callback)(void*), Channel *ch); + gdBrowserLoad(int x, int y, int w, int h, const std::string& title, + const std::string& path, void (*callback)(void*), Channel* ch); }; diff --git a/src/gui/dialogs/browser/browserSave.cpp b/src/gui/dialogs/browser/browserSave.cpp index 7011bc1..ad765b9 100644 --- a/src/gui/dialogs/browser/browserSave.cpp +++ b/src/gui/dialogs/browser/browserSave.cpp @@ -35,8 +35,8 @@ using std::string; -gdBrowserSave::gdBrowserSave(int x, int y, int w, int h, const string &title, - const string &path, const string &_name, void (*cb)(void*), Channel *ch) +gdBrowserSave::gdBrowserSave(int x, int y, int w, int h, const string& title, + const string& path, const string& _name, void (*cb)(void*), Channel* ch) : gdBrowserBase(x, y, w, h, title, path, cb) { channel = ch; @@ -52,20 +52,26 @@ gdBrowserSave::gdBrowserSave(int x, int y, int w, int h, const string &title, ok->label("Save"); ok->callback(cb_save, (void*) this); ok->shortcut(FL_ENTER); + + /* On OS X the 'where' and 'name' inputs don't get resized properly on startup. + Let's force them. */ + + where->redraw(); + name->redraw(); } /* -------------------------------------------------------------------------- */ -void gdBrowserSave::cb_save(Fl_Widget *v, void *p) { ((gdBrowserSave*)p)->__cb_save(); } -void gdBrowserSave::cb_down(Fl_Widget *v, void *p) { ((gdBrowserSave*)p)->__cb_down(); } +void gdBrowserSave::cb_save(Fl_Widget* v, void* p) { ((gdBrowserSave*)p)->cb_save(); } +void gdBrowserSave::cb_down(Fl_Widget* v, void* p) { ((gdBrowserSave*)p)->cb_down(); } /* -------------------------------------------------------------------------- */ -void gdBrowserSave::__cb_down() +void gdBrowserSave::cb_down() { string path = browser->getSelectedItem(); @@ -96,7 +102,7 @@ string gdBrowserSave::getName() /* -------------------------------------------------------------------------- */ -void gdBrowserSave::__cb_save() +void gdBrowserSave::cb_save() { callback((void*) this); } diff --git a/src/gui/dialogs/browser/browserSave.h b/src/gui/dialogs/browser/browserSave.h index 3b7f818..53cb61b 100644 --- a/src/gui/dialogs/browser/browserSave.h +++ b/src/gui/dialogs/browser/browserSave.h @@ -46,19 +46,18 @@ class gdBrowserSave : public gdBrowserBase { private: - geInput *name; + geInput* name; - static void cb_down(Fl_Widget *v, void *p); - static void cb_save(Fl_Widget *w, void *p); - - inline void __cb_down(); - inline void __cb_save(); + static void cb_down(Fl_Widget* v, void* p); + static void cb_save(Fl_Widget* w, void* p); + void cb_down(); + void cb_save(); public: - gdBrowserSave(int x, int y, int w, int h, const std::string &title, - const std::string &path, const std::string &name, void (*callback)(void*), - Channel *ch); + gdBrowserSave(int x, int y, int w, int h, const std::string& title, + const std::string& path, const std::string& name, void (*callback)(void*), + Channel* ch); std::string getName(); }; diff --git a/src/gui/dialogs/channelNameInput.cpp b/src/gui/dialogs/channelNameInput.cpp new file mode 100644 index 0000000..a0bd3bc --- /dev/null +++ b/src/gui/dialogs/channelNameInput.cpp @@ -0,0 +1,101 @@ +/* ----------------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * ----------------------------------------------------------------------------- + * + * Copyright (C) 2010-2017 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 "../../glue/channel.h" +#include "../../utils/gui.h" +#include "../../core/const.h" +#include "../../core/conf.h" +#include "../../core/channel.h" +#include "../elems/basics/button.h" +#include "../elems/basics/input.h" +#include "channelNameInput.h" + + +using namespace giada::m; + + +gdChannelNameInput::gdChannelNameInput(Channel* ch) +: gdWindow(400, 64, "New channel name"), + m_ch (ch) +{ + if (conf::nameX) + resize(conf::nameX, conf::nameY, w(), h()); + + set_modal(); + + m_name = new geInput(G_GUI_OUTER_MARGIN, G_GUI_OUTER_MARGIN, w() - (G_GUI_OUTER_MARGIN * 2), G_GUI_UNIT); + m_ok = new geButton(w() - 70 - G_GUI_OUTER_MARGIN, m_name->y()+m_name->h() + G_GUI_OUTER_MARGIN, 70, G_GUI_UNIT, "Ok"); + m_cancel = new geButton(m_ok->x() - 70 - G_GUI_OUTER_MARGIN, m_ok->y(), 70, G_GUI_UNIT, "Cancel"); + end(); + + m_name->value(m_ch->getName().c_str()); + + m_ok->shortcut(FL_Enter); + m_ok->callback(cb_update, (void*)this); + + m_cancel->callback(cb_cancel, (void*)this); + + gu_setFavicon(this); + setId(WID_SAMPLE_NAME); + show(); +} + + +/* -------------------------------------------------------------------------- */ + + +gdChannelNameInput::~gdChannelNameInput() +{ + conf::nameX = x(); + conf::nameY = y(); +} + + +/* -------------------------------------------------------------------------- */ + + +void gdChannelNameInput::cb_update(Fl_Widget* w, void* p) { ((gdChannelNameInput*)p)->cb_update(); } +void gdChannelNameInput::cb_cancel(Fl_Widget* w, void* p) { ((gdChannelNameInput*)p)->cb_cancel(); } + + +/* -------------------------------------------------------------------------- */ + + +void gdChannelNameInput::cb_cancel() +{ + do_callback(); +} + + +/* -------------------------------------------------------------------------- */ + + +void gdChannelNameInput::cb_update() +{ + glue_setName(m_ch, m_name->value()); + do_callback(); +} diff --git a/src/gui/dialogs/channelNameInput.h b/src/gui/dialogs/channelNameInput.h new file mode 100644 index 0000000..86f03d5 --- /dev/null +++ b/src/gui/dialogs/channelNameInput.h @@ -0,0 +1,61 @@ +/* ----------------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * ----------------------------------------------------------------------------- + * + * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual + * + * This file is part of Giada - Your Hardcore Loopmachine. + * + * Giada - Your Hardcore Loopmachine is free software: you can + * redistribute it and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Giada - Your Hardcore Loopmachine is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Giada - Your Hardcore Loopmachine. If not, see + * . + * + * -------------------------------------------------------------------------- */ + + +#ifndef GD_CHANNEL_NAME_INPUT_H +#define GD_CHANNEL_NAME_INPUT_H + + +#include "window.h" + + +class Channel; +class geInput; +class geButton; + + +class gdChannelNameInput : public gdWindow +{ +private: + + static void cb_update(Fl_Widget* w, void* p); + static void cb_cancel(Fl_Widget* w, void* p); + void cb_update(); + void cb_cancel(); + + Channel* m_ch; + + geInput* m_name; + geButton* m_ok; + geButton* m_cancel; + +public: + + gdChannelNameInput(Channel* ch); + ~gdChannelNameInput(); +}; + +#endif diff --git a/src/gui/dialogs/gd_pluginWindow.cpp b/src/gui/dialogs/gd_pluginWindow.cpp deleted file mode 100644 index 23316df..0000000 --- a/src/gui/dialogs/gd_pluginWindow.cpp +++ /dev/null @@ -1,127 +0,0 @@ -/* ----------------------------------------------------------------------------- - * - * Giada - Your Hardcore Loopmachine - * - * ----------------------------------------------------------------------------- - * - * Copyright (C) 2010-2017 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 - * . - * - * -------------------------------------------------------------------------- */ - - -#ifdef WITH_VST - - -#include -#include "../../utils/gui.h" -#include "../../core/plugin.h" -#include "../elems/basics/boxtypes.h" -#include "../elems/basics/box.h" -#include "../elems/basics/liquidScroll.h" -#include "../elems/basics/slider.h" -#include "gd_pluginWindow.h" - - -using std::string; - - -Parameter::Parameter(int paramIndex, Plugin *p, int X, int Y, int W) - : Fl_Group(X, Y, W-24, 20), paramIndex(paramIndex), pPlugin(p) -{ - begin(); - - label = new geBox(x(), y(), 60, 20); - label->copy_label(pPlugin->getParameterName(paramIndex).c_str()); - label->align(FL_ALIGN_LEFT | FL_ALIGN_INSIDE); - - slider = new geSlider(label->x()+label->w()+8, y(), W-200, 20); - slider->value(pPlugin->getParameter(paramIndex)); - slider->callback(cb_setValue, (void *)this); - - value = new geBox(slider->x()+slider->w()+8, y(), 100, 20); - value->align(FL_ALIGN_LEFT | FL_ALIGN_INSIDE); - value->box(G_CUSTOM_BORDER_BOX); - updateValue(); - - resizable(slider); - - end(); -} - - -/* -------------------------------------------------------------------------- */ - - -void Parameter::cb_setValue(Fl_Widget *v, void *p) { ((Parameter*)p)->__cb_setValue(); } - - -/* -------------------------------------------------------------------------- */ - - -void Parameter::__cb_setValue() -{ - pPlugin->setParameter(paramIndex, slider->value()); - updateValue(); - value->redraw(); -} - - -/* -------------------------------------------------------------------------- */ - - -void Parameter::updateValue() -{ - string v = pPlugin->getParameterText(paramIndex) + " " + - pPlugin->getParameterLabel(paramIndex); - value->copy_label(v.c_str()); -} - - -/* -------------------------------------------------------------------------- */ -/* -------------------------------------------------------------------------- */ -/* -------------------------------------------------------------------------- */ - - -gdPluginWindow::gdPluginWindow(Plugin *p) - : gdWindow(400, 156), pPlugin(p) // 350 -{ - set_non_modal(); - - geLiquidScroll *list = new geLiquidScroll(8, 8, w()-16, h()-16); - list->type(Fl_Scroll::VERTICAL_ALWAYS); - list->begin(); - - int numParams = pPlugin->getNumParameters(); - for (int i=0; ix(), list->y()+(i*24), list->w()); - list->end(); - - end(); - - label(pPlugin->getName().c_str()); - - size_range(400, (24*1)+12); - resizable(list); - - gu_setFavicon(this); - show(); -} - - -#endif // #ifdef WITH_VST diff --git a/src/gui/dialogs/gd_pluginChooser.cpp b/src/gui/dialogs/pluginChooser.cpp similarity index 96% rename from src/gui/dialogs/gd_pluginChooser.cpp rename to src/gui/dialogs/pluginChooser.cpp index 8c62705..b7adc89 100644 --- a/src/gui/dialogs/gd_pluginChooser.cpp +++ b/src/gui/dialogs/pluginChooser.cpp @@ -33,14 +33,15 @@ #include "../../core/channel.h" #include "../../core/conf.h" #include "../../core/pluginHost.h" -#include "../elems/pluginBrowser.h" +#include "../elems/plugin/pluginBrowser.h" #include "../elems/basics/button.h" #include "../elems/basics/choice.h" #include "../elems/basics/box.h" -#include "gd_pluginChooser.h" +#include "pluginChooser.h" using namespace giada::m; +using namespace giada::c; gdPluginChooser::gdPluginChooser(int X, int Y, int W, int H, int stackType, Channel *ch) @@ -130,7 +131,7 @@ void gdPluginChooser::__cb_add() int index = browser->value() - 3; // subtract header lines if (index < 0) return; - glue_addPlugin(ch, index, stackType); + plugin::addPlugin(ch, index, stackType); do_callback(); } diff --git a/src/gui/dialogs/gd_pluginChooser.h b/src/gui/dialogs/pluginChooser.h similarity index 100% rename from src/gui/dialogs/gd_pluginChooser.h rename to src/gui/dialogs/pluginChooser.h diff --git a/src/gui/dialogs/gd_pluginList.cpp b/src/gui/dialogs/pluginList.cpp similarity index 72% rename from src/gui/dialogs/gd_pluginList.cpp rename to src/gui/dialogs/pluginList.cpp index edb8de7..8b4cfb9 100644 --- a/src/gui/dialogs/gd_pluginList.cpp +++ b/src/gui/dialogs/pluginList.cpp @@ -46,22 +46,23 @@ #include "../elems/basics/choice.h" #include "../elems/mainWindow/mainIO.h" #include "../elems/mainWindow/keyboard/channel.h" -#include "gd_pluginList.h" -#include "gd_pluginChooser.h" -#include "gd_pluginWindow.h" -#include "gd_pluginWindowGUI.h" +#include "pluginChooser.h" +#include "pluginWindow.h" +#include "pluginWindowGUI.h" #include "gd_mainWindow.h" +#include "pluginList.h" -extern gdMainWindow *G_MainWin; +extern gdMainWindow* G_MainWin; using std::string; using namespace giada::m; +using namespace giada::c; -gdPluginList::gdPluginList(int stackType, Channel *ch) - : gdWindow(468, 204), ch(ch), stackType(stackType) +gdPluginList::gdPluginList(int stackType, Channel* ch) + : gdWindow(468, 204), ch(ch), stackType(stackType) { if (conf::pluginListX) resize(conf::pluginListX, conf::pluginListY, w(), h()); @@ -80,8 +81,8 @@ gdPluginList::gdPluginList(int stackType, Channel *ch) end(); set_non_modal(); - /* TODO - awful stuff... we should subclass into gdPluginListChannel and - gdPluginListMaster */ + /* TODO - awful stuff... we should subclass into gdPluginListChannel and + gdPluginListMaster */ if (stackType == pluginHost::MASTER_OUT) label("Master Out Plugins"); @@ -89,8 +90,8 @@ gdPluginList::gdPluginList(int stackType, Channel *ch) if (stackType == pluginHost::MASTER_IN) label("Master In Plugins"); else { - string l = "Channel " + gu_toString(ch->index+1) + " Plugins"; - copy_label(l.c_str()); + string l = "Channel " + gu_toString(ch->index+1) + " Plugins"; + copy_label(l.c_str()); } gu_setFavicon(this); @@ -111,13 +112,13 @@ gdPluginList::~gdPluginList() /* -------------------------------------------------------------------------- */ -void gdPluginList::cb_addPlugin(Fl_Widget *v, void *p) { ((gdPluginList*)p)->__cb_addPlugin(); } +void gdPluginList::cb_addPlugin(Fl_Widget* v, void* p) { ((gdPluginList*)p)->__cb_addPlugin(); } /* -------------------------------------------------------------------------- */ -void gdPluginList::cb_refreshList(Fl_Widget *v, void *p) +void gdPluginList::cb_refreshList(Fl_Widget* v, void* p) { /* note: this callback is fired by gdBrowser. Close its window first, * by calling the parent (pluginList) and telling it to delete its @@ -146,11 +147,11 @@ void gdPluginList::__cb_addPlugin() * must be redrawn. We have a special callback, cb_refreshList, which * we add to gdPluginChooser. It does exactly what we need. */ - gdPluginChooser *pc = new gdPluginChooser(conf::pluginChooserX, - conf::pluginChooserY, conf::pluginChooserW, conf::pluginChooserH, - stackType, ch); - addSubWindow(pc); - pc->callback(cb_refreshList, (void*)this); // 'this' refers to gdPluginList + gdPluginChooser* pc = new gdPluginChooser(conf::pluginChooserX, + conf::pluginChooserY, conf::pluginChooserW, conf::pluginChooserH, + stackType, ch); + addSubWindow(pc); + pc->callback(cb_refreshList, (void*)this); // 'this' refers to gdPluginList } @@ -193,24 +194,24 @@ void gdPluginList::refreshList() redraw(); - /* set 'full' flag to FX button */ + /* set 'full' flag to FX button */ - /* TODO - awful stuff... we should subclass into gdPluginListChannel and - gdPluginListMaster */ + /* TODO - awful stuff... we should subclass into gdPluginListChannel and + gdPluginListMaster */ if (stackType == pluginHost::MASTER_OUT) { - G_MainWin->mainIO->setMasterFxOutFull( + G_MainWin->mainIO->setMasterFxOutFull( pluginHost::countPlugins(stackType, ch) > 0); - } + } else if (stackType == pluginHost::MASTER_IN) { - G_MainWin->mainIO->setMasterFxInFull( + G_MainWin->mainIO->setMasterFxInFull( pluginHost::countPlugins(stackType, ch) > 0); - } + } else { - ch->guiChannel->fx->status = pluginHost::countPlugins(stackType, ch) > 0; - ch->guiChannel->fx->redraw(); - } + ch->guiChannel->fx->status = pluginHost::countPlugins(stackType, ch) > 0; + ch->guiChannel->fx->redraw(); + } } @@ -219,7 +220,7 @@ void gdPluginList::refreshList() /* -------------------------------------------------------------------------- */ -gdPlugin::gdPlugin(gdPluginList *gdp, Plugin *p, int X, int Y, int W) +gdPlugin::gdPlugin(gdPluginList* gdp, Plugin* p, int X, int Y, int W) : Fl_Group(X, Y, W, 20), pParent(gdp), pPlugin (p) { begin(); @@ -236,15 +237,15 @@ gdPlugin::gdPlugin(gdPluginList *gdp, Plugin *p, int X, int Y, int W) program->callback(cb_setProgram, (void*)this); - for (int i=0; igetNumPrograms(); i++) - program->add(gu_removeFltkChars(pPlugin->getProgramName(i)).c_str()); + for (int i=0; igetNumPrograms(); i++) + program->add(gu_removeFltkChars(pPlugin->getProgramName(i)).c_str()); if (program->size() == 0) { program->add("-- no programs --\0"); program->deactivate(); } - else - program->value(pPlugin->getCurrentProgram()); + else + program->value(pPlugin->getCurrentProgram()); bypass->callback(cb_setBypass, (void*)this); bypass->type(FL_TOGGLE_BUTTON); @@ -259,12 +260,12 @@ gdPlugin::gdPlugin(gdPluginList *gdp, Plugin *p, int X, int Y, int W) /* -------------------------------------------------------------------------- */ -void gdPlugin::cb_removePlugin (Fl_Widget *v, void *p) { ((gdPlugin*)p)->__cb_removePlugin(); } -void gdPlugin::cb_openPluginWindow(Fl_Widget *v, void *p) { ((gdPlugin*)p)->__cb_openPluginWindow(); } -void gdPlugin::cb_setBypass (Fl_Widget *v, void *p) { ((gdPlugin*)p)->__cb_setBypass(); } -void gdPlugin::cb_shiftUp (Fl_Widget *v, void *p) { ((gdPlugin*)p)->__cb_shiftUp(); } -void gdPlugin::cb_shiftDown (Fl_Widget *v, void *p) { ((gdPlugin*)p)->__cb_shiftDown(); } -void gdPlugin::cb_setProgram (Fl_Widget *v, void *p) { ((gdPlugin*)p)->__cb_setProgram(); } +void gdPlugin::cb_removePlugin (Fl_Widget* v, void* p) { ((gdPlugin*)p)->__cb_removePlugin(); } +void gdPlugin::cb_openPluginWindow(Fl_Widget* v, void* p) { ((gdPlugin*)p)->__cb_openPluginWindow(); } +void gdPlugin::cb_setBypass (Fl_Widget* v, void* p) { ((gdPlugin*)p)->__cb_setBypass(); } +void gdPlugin::cb_shiftUp (Fl_Widget* v, void* p) { ((gdPlugin*)p)->__cb_shiftUp(); } +void gdPlugin::cb_shiftDown (Fl_Widget* v, void* p) { ((gdPlugin*)p)->__cb_shiftDown(); } +void gdPlugin::cb_setProgram (Fl_Widget* v, void* p) { ((gdPlugin*)p)->__cb_setProgram(); } /* -------------------------------------------------------------------------- */ @@ -278,12 +279,12 @@ void gdPlugin::__cb_shiftUp() return; int pluginIndex = pluginHost::getPluginIndex(pPlugin->getId(), - pParent->stackType, pParent->ch); + pParent->stackType, pParent->ch); if (pluginIndex == 0) // first of the stack, do nothing return; - glue_swapPlugins(pParent->ch, pluginIndex, pluginIndex-1, pParent->stackType); + plugin::swapPlugins(pParent->ch, pluginIndex, pluginIndex-1, pParent->stackType); pParent->refreshList(); } @@ -304,7 +305,7 @@ void gdPlugin::__cb_shiftDown() if (pluginIndex == stackSize-1) // last one in the stack, do nothing return; - glue_swapPlugins(pParent->ch, pluginIndex, pluginIndex+1, pParent->stackType); + plugin::swapPlugins(pParent->ch, pluginIndex, pluginIndex+1, pParent->stackType); pParent->refreshList(); } @@ -317,8 +318,8 @@ void gdPlugin::__cb_removePlugin() /* any subwindow linked to the plugin must be destroyed first */ pParent->delSubWindow(pPlugin->getId()); - glue_freePlugin(pParent->ch, pPlugin->getId(), pParent->stackType); - pParent->refreshList(); + plugin::freePlugin(pParent->ch, pPlugin->getId(), pParent->stackType); + pParent->refreshList(); } @@ -327,29 +328,30 @@ void gdPlugin::__cb_removePlugin() void gdPlugin::__cb_openPluginWindow() { - /* the new pluginWindow has id = id_plugin + 1, because id=0 is reserved - * for the parent window 'add plugin'. */ - - gdWindow *w; - if (pPlugin->hasEditor()) { - if (pPlugin->isEditorOpen()) { - gu_log("[gdPlugin::__cb_openPluginWindow] plugin has editor but it's already visible\n"); - return; - } - - int pwid = pPlugin->getId()+1; - - gu_log("[gdPlugin::__cb_openPluginWindow] plugin has editor, open window id=%d\n", pwid); - - if (pParent->hasWindow(pwid)) - pParent->delSubWindow(pwid); - w = new gdPluginWindowGUI(pPlugin); - w->setId(pwid); - pParent->addSubWindow(w); - } - else { - w = new gdPluginWindow(pPlugin); - } + /* the new pluginWindow has id = id_plugin + 1, because id=0 is reserved + * for the parent window 'add plugin'. */ + + int pwid = pPlugin->getId() + 1; + + gdWindow* w; + if (pPlugin->hasEditor()) { + if (pPlugin->isEditorOpen()) { + gu_log("[gdPlugin::__cb_openPluginWindow] Plug-in has editor but it's already visible\n"); + pParent->getChild(pwid)->show(); // Raise it to top + return; + } + gu_log("[gdPlugin::__cb_openPluginWindow] Plug-in has editor, window id=%d\n", pwid); + w = new gdPluginWindowGUI(pPlugin); + } + else + w = new gdPluginWindow(pPlugin); + + gu_log("[gdPlugin::__cb_openPluginWindow] Plug-in has no editor, window id=%d\n", pwid); + + if (pParent->hasWindow(pwid)) + pParent->delSubWindow(pwid); + w->setId(pwid); + pParent->addSubWindow(w); } diff --git a/src/gui/dialogs/gd_pluginList.h b/src/gui/dialogs/pluginList.h similarity index 100% rename from src/gui/dialogs/gd_pluginList.h rename to src/gui/dialogs/pluginList.h diff --git a/src/gui/dialogs/pluginWindow.cpp b/src/gui/dialogs/pluginWindow.cpp new file mode 100644 index 0000000..ede116e --- /dev/null +++ b/src/gui/dialogs/pluginWindow.cpp @@ -0,0 +1,97 @@ +/* ----------------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * ----------------------------------------------------------------------------- + * + * Copyright (C) 2010-2017 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 + * . + * + * -------------------------------------------------------------------------- */ + + +#ifdef WITH_VST + + +#include +#include "../../utils/gui.h" +#include "../../core/plugin.h" +#include "../../core/const.h" +#include "../elems/basics/liquidScroll.h" +#include "../elems/plugin/pluginParameter.h" +#include "pluginWindow.h" + + +gdPluginWindow::gdPluginWindow(Plugin* p) + : gdWindow(450, 156), m_plugin(p) +{ + set_non_modal(); + + m_list = new geLiquidScroll(G_GUI_OUTER_MARGIN, G_GUI_OUTER_MARGIN, + w()-(G_GUI_OUTER_MARGIN*2), h()-(G_GUI_OUTER_MARGIN*2)); + + m_list->type(Fl_Scroll::VERTICAL_ALWAYS); + m_list->begin(); + int labelWidth = getLabelWidth(); + int numParams = m_plugin->getNumParameters(); + for (int i=0; iy() + (i * (G_GUI_UNIT + G_GUI_INNER_MARGIN)); + int pw = m_list->w() - m_list->scrollbar_size() - (G_GUI_OUTER_MARGIN*3); + new gePluginParameter(i, m_plugin, m_list->x(), py, pw, labelWidth); + } + m_list->end(); + + end(); + + label(m_plugin->getName().c_str()); + + size_range(450, (G_GUI_UNIT + (G_GUI_OUTER_MARGIN*2))); + resizable(m_list); + + gu_setFavicon(this); + show(); +} + + +/* -------------------------------------------------------------------------- */ + + +void gdPluginWindow::updateParameter(int index, bool changeSlider) +{ + static_cast(m_list->child(index))->update(changeSlider); +} + + +/* -------------------------------------------------------------------------- */ + + +int gdPluginWindow::getLabelWidth() const +{ + int width = 0; + int numParams = m_plugin->getNumParameters(); + for (int i=0; igetParameterName(i).c_str(), wl, hl); + if (wl > width) + width = wl; + } + return width; +} + + +#endif // #ifdef WITH_VST diff --git a/src/gui/dialogs/gd_pluginWindow.h b/src/gui/dialogs/pluginWindow.h similarity index 73% rename from src/gui/dialogs/gd_pluginWindow.h rename to src/gui/dialogs/pluginWindow.h index ff399ab..1e0f922 100644 --- a/src/gui/dialogs/gd_pluginWindow.h +++ b/src/gui/dialogs/pluginWindow.h @@ -24,58 +24,37 @@ * * -------------------------------------------------------------------------- */ + #ifdef WITH_VST #ifndef GD_PLUGIN_WINDOW_H #define GD_PLUGIN_WINDOW_H -#include -#include #include "window.h" class Plugin; class geBox; class geSlider; +class geLiquidScroll; class gdPluginWindow : public gdWindow { private: - Plugin *pPlugin; - -public: - - int id; - - gdPluginWindow(Plugin *pPlugin); -}; - - -/* -------------------------------------------------------------------------- */ - - -class Parameter : public Fl_Group -{ -private: - - int paramIndex; - Plugin *pPlugin; + Plugin* m_plugin; - static void cb_setValue(Fl_Widget *v, void *p); - inline void __cb_setValue(); + geLiquidScroll* m_list; - void updateValue(); + int getLabelWidth() const; public: - geBox *label; - geSlider *slider; - geBox *value; + gdPluginWindow(Plugin* p); - Parameter(int paramIndex, Plugin *p, int x, int y, int w); + void updateParameter(int index, bool changeSlider=false); }; diff --git a/src/gui/dialogs/gd_pluginWindowGUI.cpp b/src/gui/dialogs/pluginWindowGUI.cpp similarity index 98% rename from src/gui/dialogs/gd_pluginWindowGUI.cpp rename to src/gui/dialogs/pluginWindowGUI.cpp index afd5cb8..ed11987 100644 --- a/src/gui/dialogs/gd_pluginWindowGUI.cpp +++ b/src/gui/dialogs/pluginWindowGUI.cpp @@ -34,7 +34,7 @@ #include "../../core/pluginHost.h" #include "../../core/plugin.h" #include "../../core/const.h" -#include "gd_pluginWindowGUI.h" +#include "pluginWindowGUI.h" #ifdef __APPLE__ #import "../../utils/cocoa.h" // objective-c diff --git a/src/gui/dialogs/gd_pluginWindowGUI.h b/src/gui/dialogs/pluginWindowGUI.h similarity index 97% rename from src/gui/dialogs/gd_pluginWindowGUI.h rename to src/gui/dialogs/pluginWindowGUI.h index f19f083..54731a3 100644 --- a/src/gui/dialogs/gd_pluginWindowGUI.h +++ b/src/gui/dialogs/pluginWindowGUI.h @@ -1,11 +1,11 @@ -/* --------------------------------------------------------------------- +/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * gd_pluginWindowGUI * - * --------------------------------------------------------------------- + * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual * @@ -25,7 +25,7 @@ * along with Giada - Your Hardcore Loopmachine. If not, see * . * - * ------------------------------------------------------------------ */ + * -------------------------------------------------------------------------- */ #ifdef WITH_VST diff --git a/src/gui/dialogs/sampleEditor.cpp b/src/gui/dialogs/sampleEditor.cpp index 6c250f6..ea80d9f 100644 --- a/src/gui/dialogs/sampleEditor.cpp +++ b/src/gui/dialogs/sampleEditor.cpp @@ -53,6 +53,7 @@ #include "../elems/sampleEditor/panTool.h" #include "../elems/sampleEditor/pitchTool.h" #include "../elems/sampleEditor/rangeTool.h" +#include "../elems/sampleEditor/shiftTool.h" #include "../elems/mainWindow/keyboard/channel.h" #include "gd_warnings.h" #include "sampleEditor.h" @@ -69,10 +70,10 @@ gdSampleEditor::gdSampleEditor(SampleChannel* ch) { Fl_Group* upperBar = createUpperBar(); - waveTools = new geWaveTools(8, upperBar->y()+upperBar->h()+8, w()-16, h()-128, - ch); + waveTools = new geWaveTools(G_GUI_OUTER_MARGIN, upperBar->y()+upperBar->h()+G_GUI_OUTER_MARGIN, + w()-16, h()-128, ch); - Fl_Group* bottomBar = createBottomBar(8, waveTools->y()+waveTools->h()+8, + Fl_Group* bottomBar = createBottomBar(G_GUI_OUTER_MARGIN, waveTools->y()+waveTools->h()+G_GUI_OUTER_MARGIN, h()-waveTools->h()-upperBar->h()-32); add(upperBar); @@ -83,7 +84,7 @@ gdSampleEditor::gdSampleEditor(SampleChannel* ch) gu_setFavicon(this); set_non_modal(); - copy_label(ch->wave->getName().c_str()); + copy_label(ch->getName().c_str()); size_range(720, 480); if (conf::sampleEditorX) @@ -115,13 +116,13 @@ gdSampleEditor::~gdSampleEditor() Fl_Group* gdSampleEditor::createUpperBar() { - Fl_Group* g = new Fl_Group(8, 8, w()-16, 20); + Fl_Group* g = new Fl_Group(G_GUI_OUTER_MARGIN, G_GUI_OUTER_MARGIN, w()-16, G_GUI_UNIT); g->begin(); - grid = new geChoice(g->x(), g->y(), 50, 20); + grid = new geChoice(g->x(), g->y(), 50, G_GUI_UNIT); snap = new geCheck(grid->x()+grid->w()+4, g->y()+3, 12, 12, "Snap"); - sep1 = new geBox(snap->x()+snap->w()+4, g->y(), 506, 20); - zoomOut = new geButton(sep1->x()+sep1->w()+4, g->y(), 20, 20, "", zoomOutOff_xpm, zoomOutOn_xpm); - zoomIn = new geButton(zoomOut->x()+zoomOut->w()+4, g->y(), 20, 20, "", zoomInOff_xpm, zoomInOn_xpm); + sep1 = new geBox(snap->x()+snap->w()+4, g->y(), 506, G_GUI_UNIT); + zoomOut = new geButton(sep1->x()+sep1->w()+4, g->y(), G_GUI_UNIT, G_GUI_UNIT, "", zoomOutOff_xpm, zoomOutOn_xpm); + zoomIn = new geButton(zoomOut->x()+zoomOut->w()+4, g->y(), G_GUI_UNIT, G_GUI_UNIT, "", zoomInOff_xpm, zoomInOn_xpm); g->end(); g->resizable(sep1); @@ -167,7 +168,8 @@ Fl_Group* gdSampleEditor::createOpTools(int x, int y, int h) pitchTool = new gePitchTool(g->x(), panTool->y()+panTool->h()+8, ch); rangeTool = new geRangeTool(g->x(), pitchTool->y()+pitchTool->h()+8, ch); - reload = new geButton(g->x()+g->w()-70, rangeTool->y(), 70, 20, "Reload"); + shiftTool = new geShiftTool(rangeTool->x()+rangeTool->w()+4, pitchTool->y()+pitchTool->h()+8, ch); + reload = new geButton(g->x()+g->w()-70, shiftTool->y(), 70, 20, "Reload"); g->end(); if (ch->wave->isLogical()) // Logical samples (aka takes) cannot be reloaded. @@ -249,19 +251,19 @@ Fl_Group* gdSampleEditor::createBottomBar(int x, int y, int h) /* -------------------------------------------------------------------------- */ -void gdSampleEditor::cb_reload (Fl_Widget* w, void* p) { ((gdSampleEditor*)p)->__cb_reload(); } -void gdSampleEditor::cb_zoomIn (Fl_Widget* w, void* p) { ((gdSampleEditor*)p)->__cb_zoomIn(); } -void gdSampleEditor::cb_zoomOut (Fl_Widget* w, void* p) { ((gdSampleEditor*)p)->__cb_zoomOut(); } -void gdSampleEditor::cb_changeGrid (Fl_Widget* w, void* p) { ((gdSampleEditor*)p)->__cb_changeGrid(); } -void gdSampleEditor::cb_enableSnap (Fl_Widget* w, void* p) { ((gdSampleEditor*)p)->__cb_enableSnap(); } -void gdSampleEditor::cb_togglePreview(Fl_Widget* w, void* p) { ((gdSampleEditor*)p)->__cb_togglePreview(); } -void gdSampleEditor::cb_rewindPreview(Fl_Widget* w, void* p) { ((gdSampleEditor*)p)->__cb_rewindPreview(); } +void gdSampleEditor::cb_reload (Fl_Widget* w, void* p) { ((gdSampleEditor*)p)->cb_reload(); } +void gdSampleEditor::cb_zoomIn (Fl_Widget* w, void* p) { ((gdSampleEditor*)p)->cb_zoomIn(); } +void gdSampleEditor::cb_zoomOut (Fl_Widget* w, void* p) { ((gdSampleEditor*)p)->cb_zoomOut(); } +void gdSampleEditor::cb_changeGrid (Fl_Widget* w, void* p) { ((gdSampleEditor*)p)->cb_changeGrid(); } +void gdSampleEditor::cb_enableSnap (Fl_Widget* w, void* p) { ((gdSampleEditor*)p)->cb_enableSnap(); } +void gdSampleEditor::cb_togglePreview(Fl_Widget* w, void* p) { ((gdSampleEditor*)p)->cb_togglePreview(); } +void gdSampleEditor::cb_rewindPreview(Fl_Widget* w, void* p) { ((gdSampleEditor*)p)->cb_rewindPreview(); } /* -------------------------------------------------------------------------- */ -void gdSampleEditor::__cb_enableSnap() +void gdSampleEditor::cb_enableSnap() { waveTools->waveform->setSnap(!waveTools->waveform->getSnap()); } @@ -270,7 +272,7 @@ void gdSampleEditor::__cb_enableSnap() /* -------------------------------------------------------------------------- */ -void gdSampleEditor::__cb_togglePreview() +void gdSampleEditor::cb_togglePreview() { if (play->value()) sampleEditor::setPreview(ch, G_PREVIEW_NONE); @@ -279,7 +281,7 @@ void gdSampleEditor::__cb_togglePreview() } -void gdSampleEditor::__cb_rewindPreview() +void gdSampleEditor::cb_rewindPreview() { sampleEditor::rewindPreview(ch); } @@ -288,7 +290,7 @@ void gdSampleEditor::__cb_rewindPreview() /* -------------------------------------------------------------------------- */ -void gdSampleEditor::__cb_reload() +void gdSampleEditor::cb_reload() { /* TODO - move to glue::sampleEditor */ if (!gdConfirmWin("Warning", "Reload sample: are you sure?")) @@ -316,7 +318,7 @@ void gdSampleEditor::__cb_reload() /* -------------------------------------------------------------------------- */ -void gdSampleEditor::__cb_zoomIn() +void gdSampleEditor::cb_zoomIn() { waveTools->waveform->setZoom(-1); waveTools->redraw(); @@ -326,7 +328,7 @@ void gdSampleEditor::__cb_zoomIn() /* -------------------------------------------------------------------------- */ -void gdSampleEditor::__cb_zoomOut() +void gdSampleEditor::cb_zoomOut() { waveTools->waveform->setZoom(0); waveTools->redraw(); @@ -336,7 +338,7 @@ void gdSampleEditor::__cb_zoomOut() /* -------------------------------------------------------------------------- */ -void gdSampleEditor::__cb_changeGrid() +void gdSampleEditor::cb_changeGrid() { waveTools->waveform->setGridLevel(atoi(grid->text())); } diff --git a/src/gui/dialogs/sampleEditor.h b/src/gui/dialogs/sampleEditor.h index dc09694..43ac556 100644 --- a/src/gui/dialogs/sampleEditor.h +++ b/src/gui/dialogs/sampleEditor.h @@ -40,6 +40,8 @@ class geBoostTool; class gePanTool; class gePitchTool; class geRangeTool; +class geSampleTool; +class geShiftTool; class geChoice; class geCheck; class geBox; @@ -66,13 +68,13 @@ private: static void cb_enableSnap(Fl_Widget* w, void* p); static void cb_togglePreview(Fl_Widget* w, void* p); static void cb_rewindPreview(Fl_Widget* w, void* p); - void __cb_reload(); - void __cb_zoomIn(); - void __cb_zoomOut(); - void __cb_changeGrid(); - void __cb_enableSnap(); - void __cb_togglePreview(); - void __cb_rewindPreview(); + void cb_reload(); + void cb_zoomIn(); + void cb_zoomOut(); + void cb_changeGrid(); + void cb_enableSnap(); + void cb_togglePreview(); + void cb_rewindPreview(); public: @@ -96,6 +98,7 @@ public: gePitchTool* pitchTool; geRangeTool* rangeTool; + geShiftTool* shiftTool; geButton* reload; geButton* play; diff --git a/src/gui/dialogs/window.h b/src/gui/dialogs/window.h index ec17e5b..698f512 100644 --- a/src/gui/dialogs/window.h +++ b/src/gui/dialogs/window.h @@ -36,36 +36,36 @@ /* cb_window_closer * callback for when closing windows. Deletes the widget (delete). */ -void __cb_window_closer(Fl_Widget *v, void *p); +void __cb_window_closer(Fl_Widget* v, void* p); class gdWindow : public Fl_Double_Window { protected: - std::vector subWindows; + std::vector subWindows; int id; - gdWindow *parent; + gdWindow* parent; public: - gdWindow(int x, int y, int w, int h, const char *title=0, int id=0); - gdWindow(int w, int h, const char *title=0, int id=0); + gdWindow(int x, int y, int w, int h, const char* title=0, int id=0); + gdWindow(int w, int h, const char* title=0, int id=0); ~gdWindow(); - static void cb_closeChild(Fl_Widget *v, void *p); + static void cb_closeChild(Fl_Widget* v, void* p); - void addSubWindow(gdWindow *w); - void delSubWindow(gdWindow *w); + void addSubWindow(gdWindow* w); + void delSubWindow(gdWindow* w); void delSubWindow(int id); int getId(); void setId(int id); void debug(); - void setParent(gdWindow *); - gdWindow *getParent(); - gdWindow *getChild(int id); + void setParent(gdWindow* w); + gdWindow* getParent(); + gdWindow* getChild(int id); /* hasWindow * true if the window with id 'id' exists in the stack. */ diff --git a/src/gui/elems/basics/liquidScroll.cpp b/src/gui/elems/basics/liquidScroll.cpp index 7645ab6..6640377 100644 --- a/src/gui/elems/basics/liquidScroll.cpp +++ b/src/gui/elems/basics/liquidScroll.cpp @@ -35,14 +35,14 @@ #include "liquidScroll.h" -geLiquidScroll::geLiquidScroll(int x, int y, int w, int h, const char *l) - : Fl_Scroll(x, y, w, h, l) +geLiquidScroll::geLiquidScroll(int x, int y, int w, int h, const char* l) + : Fl_Scroll(x, y, w, h, l) { - type(Fl_Scroll::VERTICAL); - scrollbar.color(G_COLOR_GREY_2); - scrollbar.selection_color(G_COLOR_GREY_4); - scrollbar.labelcolor(G_COLOR_LIGHT_1); - scrollbar.slider(G_CUSTOM_BORDER_BOX); + type(Fl_Scroll::VERTICAL); + scrollbar.color(G_COLOR_GREY_2); + scrollbar.selection_color(G_COLOR_GREY_4); + scrollbar.labelcolor(G_COLOR_LIGHT_1); + scrollbar.slider(G_CUSTOM_BORDER_BOX); } @@ -51,11 +51,11 @@ geLiquidScroll::geLiquidScroll(int x, int y, int w, int h, const char *l) void geLiquidScroll::resize(int X, int Y, int W, int H) { - int nc = children()-2; // skip hscrollbar and vscrollbar - for ( int t=0; tresize(c->x(), c->y(), W-24, c->h()); // W-24: leave room for scrollbar - } - init_sizes(); // tell scroll children changed in size - Fl_Scroll::resize(X,Y,W,H); + int nc = children()-2; // skip hscrollbar and vscrollbar + for ( int t=0; tresize(c->x(), c->y(), W-24, c->h()); // W-24: leave room for scrollbar + } + init_sizes(); // tell scroll children changed in size + Fl_Scroll::resize(X,Y,W,H); } diff --git a/src/gui/elems/config/tabAudio.cpp b/src/gui/elems/config/tabAudio.cpp index ae37e9f..59963c2 100644 --- a/src/gui/elems/config/tabAudio.cpp +++ b/src/gui/elems/config/tabAudio.cpp @@ -48,18 +48,18 @@ geTabAudio::geTabAudio(int X, int Y, int W, int H) : Fl_Group(X, Y, W, H, "Sound System") { begin(); - soundsys = new geChoice(x()+92, y()+9, 253, 20, "System"); - buffersize = new geChoice(x()+92, y()+37, 55, 20, "Buffer size"); - samplerate = new geChoice(x()+290, y()+37, 55, 20, "Sample rate"); - sounddevOut = new geChoice(x()+92, y()+65, 225, 20, "Output device"); - devOutInfo = new geButton (x()+325, y()+65, 20, 20, "?"); - channelsOut = new geChoice(x()+92, y()+93, 55, 20, "Output channels"); - limitOutput = new geCheck (x()+155, y()+97, 55, 20, "Limit output"); - sounddevIn = new geChoice(x()+92, y()+121, 225, 20, "Input device"); - devInInfo = new geButton (x()+325, y()+121, 20, 20, "?"); - channelsIn = new geChoice(x()+92, y()+149, 55, 20, "Input channels"); - delayComp = new geInput (x()+290, y()+149, 55, 20, "Rec delay comp."); - rsmpQuality = new geChoice(x()+92, y()+177, 253, 20, "Resampling"); + soundsys = new geChoice(x()+114, y()+9, 250, 20, "System"); + buffersize = new geChoice(x()+114, y()+37, 55, 20, "Buffer size"); + samplerate = new geChoice(x()+309, y()+37, 55, 20, "Sample rate"); + sounddevOut = new geChoice(x()+114, y()+65, 222, 20, "Output device"); + devOutInfo = new geButton(x()+344, y()+65, 20, 20, "?"); + channelsOut = new geChoice(x()+114, y()+93, 55, 20, "Output channels"); + limitOutput = new geCheck (x()+177, y()+97, 55, 20, "Limit output"); + sounddevIn = new geChoice(x()+114, y()+121, 222, 20, "Input device"); + devInInfo = new geButton(x()+344, y()+121, 20, 20, "?"); + channelsIn = new geChoice(x()+114, y()+149, 55, 20, "Input channels"); + delayComp = new geInput (x()+309, y()+149, 55, 20, "Rec delay comp."); + rsmpQuality = new geChoice(x()+114, y()+177, 250, 20, "Resampling"); new geBox(x(), rsmpQuality->y()+rsmpQuality->h()+8, w(), 92, "Restart Giada for the changes to take effect."); end(); diff --git a/src/gui/elems/config/tabMidi.cpp b/src/gui/elems/config/tabMidi.cpp index 39f2d26..3107899 100644 --- a/src/gui/elems/config/tabMidi.cpp +++ b/src/gui/elems/config/tabMidi.cpp @@ -46,13 +46,13 @@ geTabMidi::geTabMidi(int X, int Y, int W, int H) : Fl_Group(X, Y, W, H, "MIDI") { begin(); - system = new geChoice(x()+92, y()+9, 253, 20, "System"); - portOut = new geChoice(x()+92, system->y()+system->h()+8, 253, 20, "Output port"); - portIn = new geChoice(x()+92, portOut->y()+portOut->h()+8, 253, 20, "Input port"); - noNoteOff = new geCheck (x()+92, portIn->y()+portIn->h()+8, 253, 20, "Device does not send NoteOff"); - midiMap = new geChoice(x()+92, noNoteOff->y()+noNoteOff->h(), 253, 20, "Output Midi Map"); - sync = new geChoice(x()+92, midiMap->y()+midiMap->h()+8, 253, 20, "Sync"); - new geBox(x(), sync->y()+sync->h()+8, w(), h()-125, "Restart Giada for the changes to take effect."); + system = new geChoice(x()+w()-250, y()+9, 250, 20, "System"); + portOut = new geChoice(x()+w()-250, system->y()+system->h()+8, 250, 20, "Output port"); + portIn = new geChoice(x()+w()-250, portOut->y()+portOut->h()+8, 250, 20, "Input port"); + noNoteOff = new geCheck (x()+w()-250, portIn->y()+portIn->h()+8, 230, 20, "Device does not send NoteOff"); + midiMap = new geChoice(x()+w()-250, noNoteOff->y()+noNoteOff->h(), 250, 20, "Output Midi Map"); + sync = new geChoice(x()+w()-250, midiMap->y()+midiMap->h()+8, 250, 20, "Sync"); + new geBox(x(), sync->y()+sync->h()+8, w(), h()-150, "Restart Giada for the changes to take effect."); end(); labelsize(G_GUI_FONT_SIZE_BASE); diff --git a/src/gui/elems/config/tabMisc.cpp b/src/gui/elems/config/tabMisc.cpp index 57a5087..875e54a 100644 --- a/src/gui/elems/config/tabMisc.cpp +++ b/src/gui/elems/config/tabMisc.cpp @@ -38,7 +38,7 @@ geTabMisc::geTabMisc(int X, int Y, int W, int H) : Fl_Group(X, Y, W, H, "Misc") { begin(); - debugMsg = new geChoice(x()+92, y()+9, 253, 20, "Debug messages"); + debugMsg = new geChoice(x()+w()-230, y()+9, 230, 20, "Debug messages"); end(); debugMsg->add("(disabled)"); diff --git a/src/gui/elems/mainWindow/keyboard/channel.cpp b/src/gui/elems/mainWindow/keyboard/channel.cpp index d226c1f..a74eda4 100644 --- a/src/gui/elems/mainWindow/keyboard/channel.cpp +++ b/src/gui/elems/mainWindow/keyboard/channel.cpp @@ -33,7 +33,7 @@ #include "../../../../utils/gui.h" #include "../../../../glue/channel.h" #include "../../../dialogs/gd_mainWindow.h" -#include "../../../dialogs/gd_pluginList.h" +#include "../../../dialogs/pluginList.h" #include "../../basics/idButton.h" #include "../../basics/dial.h" #include "../../basics/statusButton.h" @@ -43,13 +43,13 @@ #include "channel.h" -extern gdMainWindow *G_MainWin; +extern gdMainWindow* G_MainWin; using namespace giada::m; -geChannel::geChannel(int X, int Y, int W, int H, int type, Channel *ch) +geChannel::geChannel(int X, int Y, int W, int H, int type, Channel* ch) : Fl_Group(X, Y, W, H, nullptr), ch (ch), type (type) @@ -60,19 +60,19 @@ geChannel::geChannel(int X, int Y, int W, int H, int type, Channel *ch) /* -------------------------------------------------------------------------- */ -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(); } +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(); } +void geChannel::cb_openFxWindow(Fl_Widget* v, void* p) { ((geChannel*)p)->cb_openFxWindow(); } #endif /* -------------------------------------------------------------------------- */ -void geChannel::__cb_arm() +void geChannel::cb_arm() { glue_toggleArm(ch, true); } @@ -81,16 +81,16 @@ void geChannel::__cb_arm() /* -------------------------------------------------------------------------- */ -void geChannel::__cb_mute() +void geChannel::cb_mute() { - glue_setMute(ch); + glue_toggleMute(ch); } /* -------------------------------------------------------------------------- */ -void geChannel::__cb_solo() +void geChannel::cb_solo() { solo->value() ? glue_setSoloOn(ch) : glue_setSoloOff(ch); } @@ -99,7 +99,7 @@ void geChannel::__cb_solo() /* -------------------------------------------------------------------------- */ -void geChannel::__cb_changeVol() +void geChannel::cb_changeVol() { glue_setVolume(ch, vol->value()); } @@ -109,7 +109,7 @@ void geChannel::__cb_changeVol() #ifdef WITH_VST -void geChannel::__cb_openFxWindow() +void geChannel::cb_openFxWindow() { gu_openSubWindow(G_MainWin, new gdPluginList(pluginHost::CHANNEL, ch), WID_FX_LIST); } @@ -131,7 +131,7 @@ int geChannel::keyPress(int e) int geChannel::getColumnIndex() { - return ((geColumn*)parent())->getIndex(); + return static_cast(parent())->getIndex(); } diff --git a/src/gui/elems/mainWindow/keyboard/channel.h b/src/gui/elems/mainWindow/keyboard/channel.h index 04cbfdd..7314a36 100644 --- a/src/gui/elems/mainWindow/keyboard/channel.h +++ b/src/gui/elems/mainWindow/keyboard/channel.h @@ -63,20 +63,19 @@ protected: static const int MIN_ELEM_W = 20; - 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); + 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(); + void cb_mute(); + void cb_arm(); + void cb_solo(); + void cb_changeVol(); #ifdef WITH_VST - inline void __cb_openFxWindow(); + void cb_openFxWindow(); #endif /* blink diff --git a/src/gui/elems/mainWindow/keyboard/channelButton.cpp b/src/gui/elems/mainWindow/keyboard/channelButton.cpp index 6eb8d62..c70371e 100644 --- a/src/gui/elems/mainWindow/keyboard/channelButton.cpp +++ b/src/gui/elems/mainWindow/keyboard/channelButton.cpp @@ -33,16 +33,19 @@ using std::string; -geChannelButton::geChannelButton(int x, int y, int w, int h, const char *l) - : geButton(x, y, w, h, l), key("") {} +geChannelButton::geChannelButton(int x, int y, int w, int h, const char* l) + : geButton(x, y, w, h, l), + m_key ("") +{ +} /* -------------------------------------------------------------------------- */ -void geChannelButton::setKey(const string &k) +void geChannelButton::setKey(const string& k) { - key = k; + m_key = k; } @@ -51,13 +54,13 @@ void geChannelButton::setKey(const string &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; - } + if (k == 0) + m_key = ""; + else { + // FIXME - this crap won't work with unicode/utf-8 + char c = (char) k; + m_key = c; + } } @@ -66,20 +69,20 @@ void geChannelButton::setKey(int k) void geChannelButton::draw() { - geButton::draw(); + geButton::draw(); - if (key == "") - return; + if (m_key == "") + return; - /* draw background */ + /* draw background */ - fl_rectf(x()+1, y()+1, 18, h()-2, bgColor0); + fl_rectf(x()+1, y()+1, 18, h()-2, bgColor0); - /* draw key */ + /* draw m_key */ - fl_color(G_COLOR_LIGHT_2); - fl_font(FL_HELVETICA, 11); - fl_draw(key.c_str(), x(), y(), 18, h(), FL_ALIGN_CENTER); + fl_color(G_COLOR_LIGHT_2); + fl_font(FL_HELVETICA, 11); + fl_draw(m_key.c_str(), x(), y(), 18, h(), FL_ALIGN_CENTER); } @@ -88,7 +91,7 @@ void geChannelButton::draw() void geChannelButton::setInputRecordMode() { - bgColor0 = G_COLOR_RED; + bgColor0 = G_COLOR_RED; } @@ -97,21 +100,21 @@ void geChannelButton::setInputRecordMode() void geChannelButton::setActionRecordMode() { - bgColor0 = G_COLOR_BLUE; - txtColor = G_COLOR_LIGHT_2; + bgColor0 = G_COLOR_BLUE; + txtColor = G_COLOR_LIGHT_2; } /* -------------------------------------------------------------------------- */ -void geChannelButton::setDefaultMode(const char *l) +void geChannelButton::setDefaultMode(const char* l) { - bgColor0 = G_COLOR_GREY_2; + bgColor0 = G_COLOR_GREY_2; bdColor = G_COLOR_GREY_4; txtColor = G_COLOR_LIGHT_2; - if (l) - label(l); + if (l) + label(l); } @@ -120,9 +123,9 @@ void geChannelButton::setDefaultMode(const char *l) void geChannelButton::setPlayMode() { - bgColor0 = G_COLOR_LIGHT_1; - bdColor = G_COLOR_LIGHT_1; - txtColor = G_COLOR_GREY_1; + bgColor0 = G_COLOR_LIGHT_1; + bdColor = G_COLOR_LIGHT_1; + txtColor = G_COLOR_GREY_1; } @@ -131,5 +134,5 @@ void geChannelButton::setPlayMode() void geChannelButton::setEndingMode() { - bgColor0 = G_COLOR_GREY_4; + bgColor0 = G_COLOR_GREY_4; } diff --git a/src/gui/elems/mainWindow/keyboard/channelButton.h b/src/gui/elems/mainWindow/keyboard/channelButton.h index 78c6f68..b2cef9e 100644 --- a/src/gui/elems/mainWindow/keyboard/channelButton.h +++ b/src/gui/elems/mainWindow/keyboard/channelButton.h @@ -36,21 +36,21 @@ class geChannelButton : public geButton { private: - std::string key; + std::string m_key; public: - geChannelButton(int x, int y, int w, int h, const char *l=0); + geChannelButton(int x, int y, int w, int h, const char* l=0); virtual int handle(int e) = 0; void draw() override; - void setKey(const std::string &k); + void setKey(const std::string& k); void setKey(int k); void setPlayMode(); void setEndingMode(); - void setDefaultMode(const char *l=0); + void setDefaultMode(const char* l=0); void setInputRecordMode(); void setActionRecordMode(); }; diff --git a/src/gui/elems/mainWindow/keyboard/channelStatus.cpp b/src/gui/elems/mainWindow/keyboard/channelStatus.cpp index ff94d68..49accc3 100644 --- a/src/gui/elems/mainWindow/keyboard/channelStatus.cpp +++ b/src/gui/elems/mainWindow/keyboard/channelStatus.cpp @@ -63,7 +63,7 @@ void geChannelStatus::draw() fl_rectf(x()+1, y()+1, w()-2, h()-2, G_COLOR_GREY_2); // status empty - if (mixer::recording && ch->armed) + if (mixer::recording && ch->isArmed()) fl_rectf(x()+1, y()+1, w()-2, h()-2, G_COLOR_RED); // take in progress else if (recorder::active && recorder::canRec(ch, clock::isRunning(), mixer::recording)) diff --git a/src/gui/elems/mainWindow/keyboard/midiChannel.cpp b/src/gui/elems/mainWindow/keyboard/midiChannel.cpp index 64e9b9d..7c4a113 100644 --- a/src/gui/elems/mainWindow/keyboard/midiChannel.cpp +++ b/src/gui/elems/mainWindow/keyboard/midiChannel.cpp @@ -30,15 +30,16 @@ #include "../../../../core/graphics.h" #include "../../../../core/midiChannel.h" #include "../../../../utils/gui.h" +#include "../../../../utils/string.h" #include "../../../../glue/channel.h" #include "../../../../glue/io.h" #include "../../../../glue/recorder.h" #include "../../../dialogs/gd_mainWindow.h" -#include "../../../dialogs/sampleEditor.h" +#include "../../../dialogs/channelNameInput.h" #include "../../../dialogs/gd_actionEditor.h" #include "../../../dialogs/gd_warnings.h" #include "../../../dialogs/gd_keyGrabber.h" -#include "../../../dialogs/gd_pluginList.h" +#include "../../../dialogs/pluginList.h" #include "../../../dialogs/midiIO/midiInputChannel.h" #include "../../../dialogs/midiIO/midiOutputMidiCh.h" #include "../../basics/boxtypes.h" @@ -46,12 +47,14 @@ #include "../../basics/statusButton.h" #include "../../basics/dial.h" #include "column.h" +#include "midiChannelButton.h" #include "midiChannel.h" -extern gdMainWindow *G_MainWin; +extern gdMainWindow* G_MainWin; +using std::string; using namespace giada::m; @@ -72,6 +75,7 @@ enum class Menu RESIZE_H3, RESIZE_H4, __END_RESIZE_SUBMENU__, + RENAME_CHANNEL, CLONE_CHANNEL, DELETE_CHANNEL }; @@ -80,9 +84,9 @@ enum class Menu /* -------------------------------------------------------------------------- */ -void menuCallback(Fl_Widget *w, void *v) +void menuCallback(Fl_Widget* w, void* v) { - geMidiChannel *gch = static_cast(w); + geMidiChannel* gch = static_cast(w); Menu selectedItem = (Menu) (intptr_t) v; switch (selectedItem) @@ -126,6 +130,9 @@ void menuCallback(Fl_Widget *w, void *v) break; case Menu::CLONE_CHANNEL: glue_cloneChannel(gch->ch); + break; + case Menu::RENAME_CHANNEL: + gu_openSubWindow(G_MainWin, new gdChannelNameInput(gch->ch), WID_SAMPLE_NAME); break; case Menu::DELETE_CHANNEL: glue_deleteChannel(gch->ch); @@ -139,7 +146,7 @@ void menuCallback(Fl_Widget *w, void *v) /* -------------------------------------------------------------------------- */ -geMidiChannel::geMidiChannel(int X, int Y, int W, int H, MidiChannel *ch) +geMidiChannel::geMidiChannel(int X, int Y, int W, int H, MidiChannel* ch) : geChannel(X, Y, W, H, CHANNEL_MIDI, ch) { begin(); @@ -197,14 +204,14 @@ geMidiChannel::geMidiChannel(int X, int Y, int W, int H, MidiChannel *ch) /* -------------------------------------------------------------------------- */ -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 (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() +void geMidiChannel::cb_button() { if (button->value()) glue_keyPress(ch, Fl::event_ctrl(), Fl::event_shift()); @@ -214,7 +221,7 @@ void geMidiChannel::__cb_button() /* -------------------------------------------------------------------------- */ -void geMidiChannel::__cb_openMenu() +void geMidiChannel::cb_openMenu() { Fl_Menu_Item rclick_menu[] = { {"Edit actions...", 0, menuCallback, (void*) Menu::EDIT_ACTIONS}, @@ -230,6 +237,7 @@ void geMidiChannel::__cb_openMenu() {"Large", 0, menuCallback, (void*) Menu::RESIZE_H3}, {"X-Large", 0, menuCallback, (void*) Menu::RESIZE_H4}, {0}, + {"Rename channel", 0, menuCallback, (void*) Menu::RENAME_CHANNEL}, {"Clone channel", 0, menuCallback, (void*) Menu::CLONE_CHANNEL}, {"Delete channel", 0, menuCallback, (void*) Menu::DELETE_CHANNEL}, {0} @@ -240,13 +248,13 @@ void geMidiChannel::__cb_openMenu() if (!ch->hasActions) rclick_menu[(int)Menu::CLEAR_ACTIONS].deactivate(); - Fl_Menu_Button *b = new Fl_Menu_Button(0, 0, 100, 50); + Fl_Menu_Button* b = new Fl_Menu_Button(0, 0, 100, 50); b->box(G_CUSTOM_BORDER_BOX); b->textsize(G_GUI_FONT_SIZE_BASE); b->textcolor(G_COLOR_LIGHT_2); b->color(G_COLOR_GREY_2); - const Fl_Menu_Item *m = rclick_menu->popup(Fl::event_x(), Fl::event_y(), 0, 0, b); + const Fl_Menu_Item* m = rclick_menu->popup(Fl::event_x(), Fl::event_y(), 0, 0, b); if (m) m->do_callback(this, m->user_data()); return; @@ -278,22 +286,29 @@ void geMidiChannel::reset() void geMidiChannel::update() { - if (((MidiChannel*) ch)->midiOut) { - char tmp[32]; - sprintf(tmp, "-- MIDI (channel %d) --", ((MidiChannel*) ch)->midiOutChan+1); - mainButton->copy_label(tmp); - } + const MidiChannel* mch = static_cast(ch); + + string label; + if (mch->getName().empty()) + label = "-- MIDI --"; else - mainButton->label("-- MIDI --"); + label = mch->getName().c_str(); + + if (mch->midiOut) + label += " (ch " + gu_toString(mch->midiOutChan + 1) + " out)"; + + mainButton->label(label.c_str()); + + vol->value(mch->volume); + mute->value(mch->mute); + solo->value(mch->solo); - vol->value(ch->volume); - mute->value(ch->mute); - solo->value(ch->solo); + mainButton->setKey(mch->key); - mainButton->setKey(ch->key); + arm->value(mch->isArmed()); #ifdef WITH_VST - fx->status = ch->plugins.size() > 0; + fx->status = mch->plugins.size() > 0; fx->redraw(); #endif } @@ -319,23 +334,4 @@ void geMidiChannel::resize(int X, int Y, int W, int H) #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 geButton::handle(e); -} +} \ No newline at end of file diff --git a/src/gui/elems/mainWindow/keyboard/midiChannel.h b/src/gui/elems/mainWindow/keyboard/midiChannel.h index 8ed63f0..881c610 100644 --- a/src/gui/elems/mainWindow/keyboard/midiChannel.h +++ b/src/gui/elems/mainWindow/keyboard/midiChannel.h @@ -40,16 +40,14 @@ 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 (); + static void cb_button(Fl_Widget* v, void* p); + static void cb_openMenu(Fl_Widget* v, void* p); + void cb_button(); + void cb_openMenu(); public: - geMidiChannel(int x, int y, int w, int h, MidiChannel *ch); + geMidiChannel(int x, int y, int w, int h, MidiChannel* ch); void resize(int x, int y, int w, int h) override; @@ -61,15 +59,4 @@ public: }; -/* -------------------------------------------------------------------------- */ - - -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/mainWindow/keyboard/midiChannelButton.cpp b/src/gui/elems/mainWindow/keyboard/midiChannelButton.cpp new file mode 100644 index 0000000..b1ed9b4 --- /dev/null +++ b/src/gui/elems/mainWindow/keyboard/midiChannelButton.cpp @@ -0,0 +1,44 @@ +/* ----------------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * ----------------------------------------------------------------------------- + * + * Copyright (C) 2010-2017 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 "midiChannelButton.h" + + +geMidiChannelButton::geMidiChannelButton(int x, int y, int w, int h, const char* l) + : geChannelButton(x, y, w, h, l) +{ +} + + +/* -------------------------------------------------------------------------- */ + + +int geMidiChannelButton::handle(int e) +{ + // Currently MIDI drag-n-drop does nothing. + return geButton::handle(e); +} diff --git a/src/gui/elems/mainWindow/keyboard/midiChannelButton.h b/src/gui/elems/mainWindow/keyboard/midiChannelButton.h new file mode 100644 index 0000000..f1bf9dd --- /dev/null +++ b/src/gui/elems/mainWindow/keyboard/midiChannelButton.h @@ -0,0 +1,43 @@ +/* ----------------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * ----------------------------------------------------------------------------- + * + * Copyright (C) 2010-2017 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_BUTTON_H +#define GE_MIDI_CHANNEL_BUTTON_H + + +#include "channelButton.h" + + +class geMidiChannelButton : public geChannelButton +{ +public: + geMidiChannelButton(int x, int y, int w, int h, const char* l=0); + int handle(int e); +}; + + +#endif \ No newline at end of file diff --git a/src/gui/elems/mainWindow/keyboard/sampleChannel.cpp b/src/gui/elems/mainWindow/keyboard/sampleChannel.cpp index 1595097..26a839c 100644 --- a/src/gui/elems/mainWindow/keyboard/sampleChannel.cpp +++ b/src/gui/elems/mainWindow/keyboard/sampleChannel.cpp @@ -39,6 +39,7 @@ #include "../../../dialogs/gd_mainWindow.h" #include "../../../dialogs/gd_keyGrabber.h" #include "../../../dialogs/sampleEditor.h" +#include "../../../dialogs/channelNameInput.h" #include "../../../dialogs/gd_actionEditor.h" #include "../../../dialogs/gd_warnings.h" #include "../../../dialogs/browser/browserSave.h" @@ -87,6 +88,7 @@ enum class Menu RESIZE_H3, RESIZE_H4, __END_RESIZE_SUBMENU__, + RENAME_CHANNEL, CLONE_CHANNEL, FREE_CHANNEL, DELETE_CHANNEL @@ -185,6 +187,10 @@ void menuCallback(Fl_Widget* w, void* v) glue_cloneChannel(gch->ch); break; } + case Menu::RENAME_CHANNEL: { + gu_openSubWindow(G_MainWin, new gdChannelNameInput(gch->ch), WID_SAMPLE_NAME); + break; + } case Menu::FREE_CHANNEL: { glue_freeChannel(gch->ch); break; @@ -311,6 +317,7 @@ void geSampleChannel::__cb_openMenu() {"Large", 0, menuCallback, (void*) Menu::RESIZE_H3}, {"X-Large", 0, menuCallback, (void*) Menu::RESIZE_H4}, {0}, + {"Rename channel", 0, menuCallback, (void*) Menu::RENAME_CHANNEL}, {"Clone channel", 0, menuCallback, (void*) Menu::CLONE_CHANNEL}, {"Free channel", 0, menuCallback, (void*) Menu::FREE_CHANNEL}, {"Delete channel", 0, menuCallback, (void*) Menu::DELETE_CHANNEL}, @@ -321,11 +328,13 @@ void geSampleChannel::__cb_openMenu() rclick_menu[(int) Menu::EXPORT_SAMPLE].deactivate(); rclick_menu[(int) Menu::EDIT_SAMPLE].deactivate(); rclick_menu[(int) Menu::FREE_CHANNEL].deactivate(); + rclick_menu[(int) Menu::RENAME_CHANNEL].deactivate(); } if (!ch->hasActions) rclick_menu[(int) Menu::CLEAR_ACTIONS].deactivate(); + /* No 'clear start/stop actions' for those channels in loop mode: they cannot have start/stop actions. */ @@ -350,7 +359,7 @@ void geSampleChannel::__cb_openMenu() void geSampleChannel::__cb_readActions() { - glue_startStopReadingRecs(static_cast(ch)); + glue_toggleReadingRecs(static_cast(ch)); } @@ -365,7 +374,7 @@ void geSampleChannel::refresh() setColorsByStatus(ch->status, ch->recStatus); if (static_cast(ch)->wave != nullptr) { - if (mixer::recording && ch->armed) + if (mixer::recording && ch->isArmed()) mainButton->setInputRecordMode(); if (recorder::active) { if (recorder::canRec(ch, clock::isRunning(), mixer::recording)) @@ -394,9 +403,9 @@ void geSampleChannel::reset() void geSampleChannel::update() { - /* update sample button's label */ + const SampleChannel* sch = static_cast(ch); - switch (ch->status) { + switch (sch->status) { case STATUS_EMPTY: mainButton->label("-- no sample --"); break; @@ -405,34 +414,35 @@ void geSampleChannel::update() mainButton->label("* file not found! *"); break; default: - mainButton->label(static_cast(ch)->wave->getName().c_str()); + if (sch->getName().empty()) + mainButton->label(sch->wave->getBasename(false).c_str()); + else + mainButton->label(sch->getName().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. */ + /* 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) + if (sch->hasActions) showActionButton(); else hideActionButton(); - /* updates modebox */ - - modeBox->value(static_cast(ch)->mode); + modeBox->value(sch->mode); modeBox->redraw(); - /* update volumes+mute+solo */ + vol->value(sch->volume); + mute->value(sch->mute); + solo->value(sch->solo); - vol->value(ch->volume); - mute->value(ch->mute); - solo->value(ch->solo); + mainButton->setKey(sch->key); - mainButton->setKey(ch->key); + arm->value(sch->isArmed()); #ifdef WITH_VST - fx->status = ch->plugins.size() > 0; + fx->status = sch->plugins.size() > 0; fx->redraw(); #endif } diff --git a/src/gui/elems/mainWindow/keyboard/sampleChannelButton.cpp b/src/gui/elems/mainWindow/keyboard/sampleChannelButton.cpp index a2a9c3f..9e504df 100644 --- a/src/gui/elems/mainWindow/keyboard/sampleChannelButton.cpp +++ b/src/gui/elems/mainWindow/keyboard/sampleChannelButton.cpp @@ -37,11 +37,11 @@ #include "sampleChannelButton.h" -extern gdMainWindow *G_MainWin; +extern gdMainWindow* G_MainWin; geSampleChannelButton::geSampleChannelButton(int x, int y, int w, int h, - const char *l) + const char* l) : geChannelButton(x, y, w, h, l) { } @@ -61,9 +61,9 @@ int geSampleChannelButton::handle(int e) break; } case FL_PASTE: { - geSampleChannel *gch = static_cast(parent()); - SampleChannel *ch = static_cast(gch->ch); - int result = glue_loadChannel(ch, gu_trim(gu_stripFileUrl(Fl::event_text())).c_str()); + geSampleChannel* gch = static_cast(parent()); + SampleChannel* ch = static_cast(gch->ch); + int result = glue_loadChannel(ch, gu_trim(gu_stripFileUrl(Fl::event_text()))); if (result != G_RES_OK) G_MainWin->keyboard->printChannelMessage(result); ret = 1; diff --git a/src/gui/elems/mainWindow/keyboard/sampleChannelButton.h b/src/gui/elems/mainWindow/keyboard/sampleChannelButton.h index 4857c44..eb47071 100644 --- a/src/gui/elems/mainWindow/keyboard/sampleChannelButton.h +++ b/src/gui/elems/mainWindow/keyboard/sampleChannelButton.h @@ -36,7 +36,7 @@ class geSampleChannelButton : public geChannelButton { public: - geSampleChannelButton(int x, int y, int w, int h, const char *l=0); + geSampleChannelButton(int x, int y, int w, int h, const char* l=0); int handle(int e); }; diff --git a/src/gui/elems/mainWindow/mainIO.cpp b/src/gui/elems/mainWindow/mainIO.cpp index 098785a..67bc074 100644 --- a/src/gui/elems/mainWindow/mainIO.cpp +++ b/src/gui/elems/mainWindow/mainIO.cpp @@ -35,7 +35,7 @@ #include "../../elems/basics/statusButton.h" #include "../../elems/basics/dial.h" #include "../../dialogs/gd_mainWindow.h" -#include "../../dialogs/gd_pluginList.h" +#include "../../dialogs/pluginList.h" #include "mainIO.h" diff --git a/src/gui/elems/mainWindow/mainMenu.cpp b/src/gui/elems/mainWindow/mainMenu.cpp index a61f8ed..2d7269b 100644 --- a/src/gui/elems/mainWindow/mainMenu.cpp +++ b/src/gui/elems/mainWindow/mainMenu.cpp @@ -99,7 +99,7 @@ void geMainMenu::__cb_about() void geMainMenu::__cb_config() { - gu_openSubWindow(G_MainWin, new gdConfig(380, 370), WID_CONFIG); + gu_openSubWindow(G_MainWin, new gdConfig(400, 370), WID_CONFIG); } diff --git a/src/gui/elems/mainWindow/mainTimer.cpp b/src/gui/elems/mainWindow/mainTimer.cpp index 334d6c8..ac19b4f 100644 --- a/src/gui/elems/mainWindow/mainTimer.cpp +++ b/src/gui/elems/mainWindow/mainTimer.cpp @@ -34,8 +34,8 @@ #include "../../elems/basics/button.h" #include "../../elems/basics/choice.h" #include "../../dialogs/gd_mainWindow.h" -#include "../../dialogs/gd_bpmInput.h" -#include "../../dialogs/gd_beatsInput.h" +#include "../../dialogs/bpmInput.h" +#include "../../dialogs/beatsInput.h" #include "mainTimer.h" diff --git a/src/gui/elems/pluginBrowser.cpp b/src/gui/elems/plugin/pluginBrowser.cpp similarity index 96% rename from src/gui/elems/pluginBrowser.cpp rename to src/gui/elems/plugin/pluginBrowser.cpp index 46a8bc5..11d0882 100644 --- a/src/gui/elems/pluginBrowser.cpp +++ b/src/gui/elems/plugin/pluginBrowser.cpp @@ -29,10 +29,10 @@ #include -#include "../../core/plugin.h" -#include "../../core/const.h" -#include "../../core/pluginHost.h" -#include "basics/boxtypes.h" +#include "../../../core/plugin.h" +#include "../../../core/const.h" +#include "../../../core/pluginHost.h" +#include "../basics/boxtypes.h" #include "pluginBrowser.h" diff --git a/src/gui/elems/pluginBrowser.h b/src/gui/elems/plugin/pluginBrowser.h similarity index 100% rename from src/gui/elems/pluginBrowser.h rename to src/gui/elems/plugin/pluginBrowser.h diff --git a/src/gui/elems/plugin/pluginParameter.cpp b/src/gui/elems/plugin/pluginParameter.cpp new file mode 100644 index 0000000..5a6916b --- /dev/null +++ b/src/gui/elems/plugin/pluginParameter.cpp @@ -0,0 +1,99 @@ +/* ----------------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * ----------------------------------------------------------------------------- + * + * Copyright (C) 2010-2017 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 + * . + * + * -------------------------------------------------------------------------- */ + + +#ifdef WITH_VST + + +#include "../../../core/plugin.h" +#include "../../../core/const.h" +#include "../../../glue/plugin.h" +#include "../basics/boxtypes.h" +#include "../basics/box.h" +#include "../basics/slider.h" +#include "pluginParameter.h" + + +using std::string; +using namespace giada::c; + + +gePluginParameter::gePluginParameter(int paramIndex, Plugin* p, int X, int Y, + int W, int labelWidth) + : Fl_Group (X, Y, W, G_GUI_UNIT), + m_paramIndex(paramIndex), + m_plugin (p) +{ + begin(); + + m_label = new geBox(x(), y(), labelWidth, G_GUI_UNIT); + m_label->copy_label(m_plugin->getParameterName(m_paramIndex).c_str()); + m_label->align(FL_ALIGN_LEFT | FL_ALIGN_INSIDE); + + m_slider = new geSlider(m_label->x()+m_label->w()+G_GUI_OUTER_MARGIN, y(), + w()-(m_label->x()+m_label->w()+G_GUI_OUTER_MARGIN)-VALUE_WIDTH, G_GUI_UNIT); + m_slider->value(m_plugin->getParameter(m_paramIndex)); + m_slider->callback(cb_setValue, (void*)this); + + m_value = new geBox(m_slider->x()+m_slider->w()+G_GUI_OUTER_MARGIN, y(), VALUE_WIDTH, G_GUI_UNIT); + m_value->align(FL_ALIGN_LEFT | FL_ALIGN_INSIDE); + m_value->box(G_CUSTOM_BORDER_BOX); + + end(); + resizable(m_slider); + update(false); +} + + +/* -------------------------------------------------------------------------- */ + + +void gePluginParameter::cb_setValue(Fl_Widget* v, void* p) { ((gePluginParameter*)p)->cb_setValue(); } + + +/* -------------------------------------------------------------------------- */ + + +void gePluginParameter::cb_setValue() +{ + plugin::setParameter(m_plugin, m_paramIndex, m_slider->value()); +} + + +/* -------------------------------------------------------------------------- */ + + +void gePluginParameter::update(bool changeSlider) +{ + string v = m_plugin->getParameterText(m_paramIndex) + " " + + m_plugin->getParameterLabel(m_paramIndex); + m_value->copy_label(v.c_str()); + if (changeSlider) + m_slider->value(m_plugin->getParameter(m_paramIndex)); +} + + +#endif // #ifdef WITH_VST diff --git a/src/gui/elems/plugin/pluginParameter.h b/src/gui/elems/plugin/pluginParameter.h new file mode 100644 index 0000000..268b7e6 --- /dev/null +++ b/src/gui/elems/plugin/pluginParameter.h @@ -0,0 +1,69 @@ +/* ----------------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * ----------------------------------------------------------------------------- + * + * Copyright (C) 2010-2017 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 + * . + * + * -------------------------------------------------------------------------- */ + + +#ifdef WITH_VST + + +#ifndef GE_PLUGIN_PARAMETER_H +#define GE_PLUGIN_PARAMETER_H + + +#include + + +class Plugin; +class geBox; +class geSlider; + + +class gePluginParameter : public Fl_Group +{ +private: + + static const int VALUE_WIDTH = 100; + + int m_paramIndex; + Plugin* m_plugin; + + geBox* m_label; + geSlider* m_slider; + geBox* m_value; + + static void cb_setValue(Fl_Widget* v, void* p); + void cb_setValue(); + +public: + + gePluginParameter(int paramIndex, Plugin* p, int x, int y, int w, int labelWidth); + + void update(bool changeSlider); +}; + + +#endif + +#endif // #ifdef WITH_VST diff --git a/src/gui/elems/sampleEditor/rangeTool.cpp b/src/gui/elems/sampleEditor/rangeTool.cpp index 9bdd59a..2125294 100644 --- a/src/gui/elems/sampleEditor/rangeTool.cpp +++ b/src/gui/elems/sampleEditor/rangeTool.cpp @@ -44,27 +44,27 @@ using namespace giada::c; geRangeTool::geRangeTool(int x, int y, SampleChannel* ch) - : Fl_Group(x, y, 300, 20), - m_ch (ch) + : Fl_Group(x, y, 280, G_GUI_UNIT), + m_ch (ch) { - begin(); - m_label = new geBox (x, y, gu_getStringWidth("Range"), 20, "Range", FL_ALIGN_RIGHT); - m_begin = new geInput(m_label->x()+m_label->w()+4, y, 70, 20); - m_end = new geInput(m_begin->x()+m_begin->w()+4, y, 70, 20); - m_reset = new geButton(m_end->x()+m_end->w()+4, y, 70, 20, "Reset"); - end(); - - m_begin->type(FL_INT_INPUT); - m_begin->when(FL_WHEN_RELEASE | FL_WHEN_ENTER_KEY); // on focus lost or enter key - m_begin->callback(cb_setChanPos, this); - - m_end->type(FL_INT_INPUT); - m_end->when(FL_WHEN_RELEASE | FL_WHEN_ENTER_KEY); // on focus lost or enter key - m_end->callback(cb_setChanPos, this); - - m_reset->callback(cb_resetStartEnd, this); - - refresh(); + begin(); + m_label = new geBox(x, y, gu_getStringWidth("Range"), G_GUI_UNIT, "Range", FL_ALIGN_RIGHT); + m_begin = new geInput(m_label->x()+m_label->w()+G_GUI_INNER_MARGIN, y, 70, G_GUI_UNIT); + m_end = new geInput(m_begin->x()+m_begin->w()+G_GUI_INNER_MARGIN, y, 70, G_GUI_UNIT); + m_reset = new geButton(m_end->x()+m_end->w()+G_GUI_INNER_MARGIN, y, 70, G_GUI_UNIT, "Reset"); + end(); + + m_begin->type(FL_INT_INPUT); + m_begin->when(FL_WHEN_RELEASE | FL_WHEN_ENTER_KEY); // on focus lost or enter key + m_begin->callback(cb_setChanPos, this); + + m_end->type(FL_INT_INPUT); + m_end->when(FL_WHEN_RELEASE | FL_WHEN_ENTER_KEY); // on focus lost or enter key + m_end->callback(cb_setChanPos, this); + + m_reset->callback(cb_resetStartEnd, this); + + refresh(); } @@ -73,8 +73,8 @@ geRangeTool::geRangeTool(int x, int y, SampleChannel* ch) void geRangeTool::refresh() { - m_begin->value(gu_toString(m_ch->getBegin()).c_str()); - m_end->value(gu_toString(m_ch->getEnd()).c_str()); + m_begin->value(gu_toString(m_ch->getBegin()).c_str()); + m_end->value(gu_toString(m_ch->getEnd()).c_str()); } @@ -90,8 +90,8 @@ void geRangeTool::cb_resetStartEnd(Fl_Widget* w, void* p) { ((geRangeTool*)p)->_ void geRangeTool::__cb_setChanPos() { - sampleEditor::setBeginEnd(m_ch, atoi(m_begin->value()), atoi(m_end->value())); - static_cast(window())->waveTools->updateWaveform(); + sampleEditor::setBeginEnd(m_ch, atoi(m_begin->value()), atoi(m_end->value())); + static_cast(window())->waveTools->updateWaveform(); // TODO - glue's business! } @@ -100,6 +100,6 @@ void geRangeTool::__cb_setChanPos() void geRangeTool::__cb_resetStartEnd() { - sampleEditor::setBeginEnd(m_ch, 0, m_ch->wave->getSize() - 1); - static_cast(window())->waveTools->updateWaveform(); + sampleEditor::setBeginEnd(m_ch, 0, m_ch->wave->getSize() - 1); + static_cast(window())->waveTools->updateWaveform(); // TODO - glue's business! } diff --git a/src/gui/elems/sampleEditor/shiftTool.cpp b/src/gui/elems/sampleEditor/shiftTool.cpp new file mode 100644 index 0000000..83e9afe --- /dev/null +++ b/src/gui/elems/sampleEditor/shiftTool.cpp @@ -0,0 +1,107 @@ +/* ----------------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * ----------------------------------------------------------------------------- + * + * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual + * + * This file is part of Giada - Your Hardcore Loopmachine. + * + * Giada - Your Hardcore Loopmachine is free software: you can + * redistribute it and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Giada - Your Hardcore Loopmachine is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Giada - Your Hardcore Loopmachine. If not, see + * . + * + * -------------------------------------------------------------------------- */ + + +#include "../../../core/const.h" +#include "../../../core/sampleChannel.h" +#include "../../../utils/gui.h" +#include "../../../utils/string.h" +#include "../../../glue/sampleEditor.h" +#include "../../dialogs/gd_warnings.h" +#include "../basics/input.h" +#include "../basics/box.h" +#include "../basics/button.h" +#include "shiftTool.h" + + +using namespace giada::c; + + +geShiftTool::geShiftTool(int x, int y, SampleChannel* ch) + : Fl_Group(x, y, 300, G_GUI_UNIT), + m_ch (ch) +{ + begin(); + m_label = new geBox(x, y, gu_getStringWidth("Shift"), G_GUI_UNIT, "Shift", FL_ALIGN_RIGHT); + m_shift = new geInput(m_label->x()+m_label->w()+G_GUI_INNER_MARGIN, y, 70, G_GUI_UNIT); + m_reset = new geButton(m_shift->x()+m_shift->w()+G_GUI_INNER_MARGIN, y, 70, G_GUI_UNIT, "Reset"); + end(); + + m_shift->type(FL_INT_INPUT); + m_shift->when(FL_WHEN_RELEASE | FL_WHEN_ENTER_KEY); // on focus lost or enter key + m_shift->value(gu_toString(ch->getShift()).c_str()); + m_shift->callback(cb_setShift, (void*)this); + + m_reset->callback(cb_reset, (void*)this); + + refresh(); +} + + +/* -------------------------------------------------------------------------- */ + + +void geShiftTool::cb_setShift(Fl_Widget* w, void* p) { ((geShiftTool*)p)->cb_setShift(); } +void geShiftTool::cb_reset(Fl_Widget* w, void* p) { ((geShiftTool*)p)->cb_reset(); } + + +/* -------------------------------------------------------------------------- */ + + +void geShiftTool::cb_setShift() +{ + shift(atoi(m_shift->value())); +} + + +/* -------------------------------------------------------------------------- */ + + +void geShiftTool::cb_reset() +{ + shift(0); +} + + +/* -------------------------------------------------------------------------- */ + + +void geShiftTool::refresh() +{ + m_shift->value(gu_toString(m_ch->getShift()).c_str()); +} + + +/* -------------------------------------------------------------------------- */ + + +void geShiftTool::shift(int f) +{ + if (m_ch->isPlaying()) + gdAlert("Can't shift sample while playing."); + else + sampleEditor::shift(m_ch, f); +} diff --git a/src/gui/elems/sampleEditor/shiftTool.h b/src/gui/elems/sampleEditor/shiftTool.h new file mode 100644 index 0000000..17aee46 --- /dev/null +++ b/src/gui/elems/sampleEditor/shiftTool.h @@ -0,0 +1,66 @@ +/* ----------------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * ----------------------------------------------------------------------------- + * + * Copyright (C) 2010-2017 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_SHIFT_TOOL_H +#define GE_SHIFT_TOOL_H + + +#include + + +class SampleChannel; +class geInput; +class geButton; +class geBox; + + +class geShiftTool : public Fl_Group +{ +private: + + SampleChannel* m_ch; + + geBox* m_label; + geInput* m_shift; + geButton* m_reset; + + static void cb_setShift(Fl_Widget* w, void* p); + static void cb_reset(Fl_Widget* w, void* p); + void cb_setShift(); + void cb_reset(); + + void shift(int f); + +public: + + geShiftTool(int x, int y, SampleChannel* ch); + + void refresh(); +}; + + +#endif diff --git a/src/gui/elems/sampleEditor/waveform.cpp b/src/gui/elems/sampleEditor/waveform.cpp index d13b369..dc4c980 100644 --- a/src/gui/elems/sampleEditor/waveform.cpp +++ b/src/gui/elems/sampleEditor/waveform.cpp @@ -349,7 +349,7 @@ int geWaveform::handle(int e) case FL_KEYDOWN: { if (Fl::event_key() == ' ') - static_cast(window())->__cb_togglePreview(); + static_cast(window())->cb_togglePreview(); else if (Fl::event_key() == FL_BackSpace) sampleEditor::rewindPreview(m_ch); diff --git a/src/utils/fs.h b/src/utils/fs.h index cee9c81..3e84fff 100644 --- a/src/utils/fs.h +++ b/src/utils/fs.h @@ -34,18 +34,35 @@ #include -bool gu_fileExists(const std::string &path); -bool gu_dirExists(const std::string &path); -bool gu_isDir(const std::string &path); -bool gu_isProject(const std::string &path); -bool gu_mkdir(const std::string &path); +bool gu_fileExists(const std::string& path); +bool gu_dirExists(const std::string& path); +bool gu_isDir(const std::string& path); +bool gu_isProject(const std::string& path); +bool gu_mkdir(const std::string& path); std::string gu_getCurrentPath(); std::string gu_getHomePath(); -std::string gu_basename(const std::string &s); -std::string gu_dirname(const std::string &s); -std::string gu_getExt(const std::string &s); -std::string gu_stripExt(const std::string &s); -std::string gu_stripFileUrl(const std::string &s); + +/* gu_basename +/path/to/file.txt -> file.txt */ + +std::string gu_basename(const std::string& s); + +/* gu_dirname +/path/to/file.txt -> /path/to */ + +std::string gu_dirname(const std::string& s); + +/* gu_getExt +/path/to/file.txt -> txt */ + +std::string gu_getExt(const std::string& s); + +/* gu_stripExt +/path/to/file.txt -> /path/to/file */ + +std::string gu_stripExt(const std::string& s); + +std::string gu_stripFileUrl(const std::string& s); #endif diff --git a/src/utils/string.cpp b/src/utils/string.cpp index 92df941..e19c235 100644 --- a/src/utils/string.cpp +++ b/src/utils/string.cpp @@ -37,7 +37,7 @@ using std::string; using std::vector; -string gu_getRealPath(const string &path) +string gu_getRealPath(const string& path) { string out = ""; @@ -85,7 +85,7 @@ string gu_toString(int i) /* -------------------------------------------------------------------------- */ -string gu_trim(const string &s) +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"); @@ -96,7 +96,7 @@ string gu_trim(const string &s) /* -------------------------------------------------------------------------- */ -string gu_replace(string in, const string &search, const string &replace) +string gu_replace(string in, const string& search, const string& replace) { size_t pos = 0; while ((pos = in.find(search, pos)) != string::npos) { @@ -110,7 +110,7 @@ string gu_replace(string in, const string &search, const string &replace) /* -------------------------------------------------------------------------- */ -void gu_split(string in, string sep, vector *v) +void gu_split(string in, string sep, vector* v) { string full = in; string token = ""; diff --git a/src/utils/string.h b/src/utils/string.h index 401b773..17b446c 100644 --- a/src/utils/string.h +++ b/src/utils/string.h @@ -35,16 +35,16 @@ #include -std::string gu_getRealPath(const std::string &path); +std::string gu_getRealPath(const std::string& path); -std::string gu_replace(std::string in, const std::string &search, - const std::string &replace); +std::string gu_replace(std::string in, const std::string& search, + const std::string& replace); -std::string gu_trim(const std::string &s); +std::string gu_trim(const std::string& s); std::string gu_toString(int i); -void gu_split(std::string in, std::string sep, std::vector *v); +void gu_split(std::string in, std::string sep, std::vector* v); #endif diff --git a/src/utils/time.cpp b/src/utils/time.cpp index e28a706..3318258 100644 --- a/src/utils/time.cpp +++ b/src/utils/time.cpp @@ -27,8 +27,13 @@ * -------------------------------------------------------------------------- */ -#include -#include +#include "../core/const.h" +#ifndef G_OS_MAC + #include + #include +#else + #include +#endif #include "time.h" @@ -38,6 +43,10 @@ namespace time { void sleep(int millisecs) { +#ifndef G_OS_MAC std::this_thread::sleep_for(std::chrono::milliseconds(millisecs)); +#else + usleep(millisecs * 1000); +#endif } }}}; // giada::u::time:: diff --git a/tests/wave.cpp b/tests/wave.cpp index 4f2e18a..7729024 100644 --- a/tests/wave.cpp +++ b/tests/wave.cpp @@ -13,42 +13,55 @@ TEST_CASE("Test Wave class") static const int CHANNELS = 2; static const int BIT_DEPTH = 32; - /* Each SECTION the TEST_CASE is executed from the start. Any code between - this comment and the first SECTION macro is exectuted before each SECTION. */ - - std::unique_ptr wave; - - SECTION("test basename") - { - wave = std::unique_ptr(new Wave(nullptr, BUFFER_SIZE, CHANNELS, - SAMPLE_RATE, BIT_DEPTH, "path/to/sample.wav")); - - REQUIRE(wave->getPath() == "path/to/sample.wav"); - REQUIRE(wave->getBasename() == "sample"); - REQUIRE(wave->getBasename(true) == "sample.wav"); - } - - SECTION("test change name") - { - wave = std::unique_ptr(new Wave(nullptr, BUFFER_SIZE, CHANNELS, - SAMPLE_RATE, BIT_DEPTH, "path/to/sample.wav")); - wave->setName("waveform"); - - REQUIRE(wave->getPath() == "path/to/waveform.wav"); - REQUIRE(wave->getBasename() == "waveform"); - REQUIRE(wave->getBasename(true) == "waveform.wav"); - } - - SECTION("test memory cleanup") - { - float* data = new float[BUFFER_SIZE]; - - wave = std::unique_ptr(new Wave(data, BUFFER_SIZE, CHANNELS, - SAMPLE_RATE, BIT_DEPTH, "path/to/sample.wav")); - wave->clear(); - - REQUIRE(wave->getData() == nullptr); - REQUIRE(wave->getPath() == ""); - REQUIRE(wave->getSize() == 0); - } + /* Each SECTION the TEST_CASE is executed from the start. Any code between + this comment and the first SECTION macro is exectuted before each SECTION. */ + + std::unique_ptr wave; + + SECTION("test basename") + { + wave = std::unique_ptr(new Wave(nullptr, BUFFER_SIZE, CHANNELS, + SAMPLE_RATE, BIT_DEPTH, "path/to/sample.wav")); + + REQUIRE(wave->getPath() == "path/to/sample.wav"); + REQUIRE(wave->getBasename() == "sample"); + REQUIRE(wave->getBasename(true) == "sample.wav"); + } + + SECTION("test path") + { + wave = std::unique_ptr(new Wave(nullptr, BUFFER_SIZE, CHANNELS, + SAMPLE_RATE, BIT_DEPTH, "path/to/sample.wav")); + + wave->setPath("path/is/now/different.mp3"); + + REQUIRE(wave->getPath() == "path/is/now/different.mp3"); + + wave->setPath("path/is/now/different.mp3", 5); + + REQUIRE(wave->getPath() == "path/is/now/different-5.mp3"); + } + + SECTION("test change name") + { + wave = std::unique_ptr(new Wave(nullptr, BUFFER_SIZE, CHANNELS, + SAMPLE_RATE, BIT_DEPTH, "path/to/sample.wav")); + + REQUIRE(wave->getPath() == "path/to/sample.wav"); + REQUIRE(wave->getBasename() == "sample"); + REQUIRE(wave->getBasename(true) == "sample.wav"); + } + + SECTION("test memory cleanup") + { + float* data = new float[BUFFER_SIZE]; + + wave = std::unique_ptr(new Wave(data, BUFFER_SIZE, CHANNELS, + SAMPLE_RATE, BIT_DEPTH, "path/to/sample.wav")); + wave->clear(); + + REQUIRE(wave->getData() == nullptr); + REQUIRE(wave->getPath() == ""); + REQUIRE(wave->getSize() == 0); + } } -- 2.30.2