--------------------------------------------------------------------------------
+0.14.0 --- 2017 . 05 . 29
+- Sample Editor reorganized and refactored
+- Removed support for old ini-based patch files
+- Improved and simplified pan algorithm
+- Ability to toggle input monitoring while recording audio
+- Lots of code refactoring
+- Convert all .h headers to C++ headers
+- Update Libsndfile to version 1.0.28
+- Fix crash when recording audio
+- Fix wrong file path when exporting samples
+- Fix a bug that prevented begin/end handles to work in Sample Editor
+- Fix Sample Editor's grid value not being stored properly on close
+
+
+0.13.4 --- 2017 . 04 . 23
+- Removed support for old ini-based MIDImap files
+- Initial support for channel-based MIDI filtering
+- New Orphaned MIDI events in Piano Roll editor
+- Improve action filtering in Piano Roll editor
+- Lots of code refactoring
+- New test suite for Action Recorder
+- Fix obscure bug when overdubbing actions and a null loop occurs
+- Fix "clear all actions" menu refresh when removing items on Piano Roll
+
+
+0.13.3 --- 2017 . 03 . 25
+- Strip VST folder from Git repository
+- Fix 'Close' button's position inside MIDI input window
+- Update RtMidi to version 2.1.1
+- Improve 'free channel' function (GitHub #105)
+- New 'Clock' structure for timing operations
+- New Jack implementation with BPM sync and Rewind (GitHub #89)
+- Fix missing tracker reset on 'free channel' function (GitHub #99)
+
+
0.13.2 --- 2017 . 01 . 14
- MIDI learn for plugins parameters
- Toggle hidden files in File Browser
src/core/kernelMidi.cpp \
src/core/graphics.h \
src/core/graphics.cpp \
-src/core/patch_DEPR_.h \
-src/core/patch_DEPR_.cpp \
src/core/patch.h \
src/core/patch.cpp \
src/core/recorder.h \
src/core/recorder.cpp \
src/core/mixer.h \
src/core/mixer.cpp \
-src/core/dataStorageIni.h \
-src/core/dataStorageIni.cpp \
-src/core/dataStorageJson.h \
-src/core/dataStorageJson.cpp \
+src/core/storager.h \
+src/core/storager.cpp \
+src/core/clock.h \
+src/core/clock.cpp \
src/glue/main.h \
src/glue/main.cpp \
src/glue/io.h \
src/glue/channel.cpp \
src/glue/plugin.h \
src/glue/plugin.cpp \
+src/glue/transport.h \
+src/glue/transport.cpp \
+src/glue/recorder.h \
+src/glue/recorder.cpp \
+src/gui/dialogs/window.h \
+src/gui/dialogs/window.cpp \
src/gui/dialogs/gd_keyGrabber.h \
src/gui/dialogs/gd_keyGrabber.cpp \
src/gui/dialogs/gd_about.h \
src/gui/dialogs/gd_warnings.cpp \
src/gui/dialogs/gd_bpmInput.h \
src/gui/dialogs/gd_bpmInput.cpp \
-src/gui/dialogs/gd_browser.h \
-src/gui/dialogs/gd_browser.cpp \
src/gui/dialogs/gd_config.h \
src/gui/dialogs/gd_config.cpp \
src/gui/dialogs/gd_devInfo.h \
src/gui/dialogs/gd_pluginList.cpp \
src/gui/dialogs/gd_pluginWindow.h \
src/gui/dialogs/gd_pluginWindow.cpp \
-src/gui/dialogs/gd_editor.h \
-src/gui/dialogs/gd_editor.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/gd_actionEditor.h \
src/gui/dialogs/gd_actionEditor.cpp \
src/gui/dialogs/gd_pluginChooser.h \
src/gui/dialogs/gd_pluginChooser.cpp \
-src/gui/dialogs/midiIO/midiOutputBase.h \
-src/gui/dialogs/midiIO/midiOutputBase.cpp \
-src/gui/dialogs/midiIO/midiOutputSampleCh.h \
+src/gui/dialogs/browser/browserBase.h \
+src/gui/dialogs/browser/browserBase.cpp \
+src/gui/dialogs/browser/browserLoad.h \
+src/gui/dialogs/browser/browserLoad.cpp \
+src/gui/dialogs/browser/browserSave.h \
+src/gui/dialogs/browser/browserSave.cpp \
+src/gui/dialogs/midiIO/midiOutputBase.h \
+src/gui/dialogs/midiIO/midiOutputBase.cpp \
+src/gui/dialogs/midiIO/midiOutputSampleCh.h \
src/gui/dialogs/midiIO/midiOutputSampleCh.cpp \
-src/gui/dialogs/midiIO/midiOutputMidiCh.h \
-src/gui/dialogs/midiIO/midiOutputMidiCh.cpp \
-src/gui/dialogs/midiIO/midiInputBase.h \
-src/gui/dialogs/midiIO/midiInputBase.cpp \
-src/gui/dialogs/midiIO/midiInputChannel.h \
-src/gui/dialogs/midiIO/midiInputChannel.cpp \
-src/gui/dialogs/midiIO/midiInputMaster.h \
-src/gui/dialogs/midiIO/midiInputMaster.cpp \
-src/gui/elems/midiLearner.h \
-src/gui/elems/midiLearner.cpp \
-src/gui/elems/ge_mixed.h \
-src/gui/elems/ge_mixed.cpp \
-src/gui/elems/ge_waveform.h \
-src/gui/elems/ge_waveform.cpp \
-src/gui/elems/browser.h \
-src/gui/elems/browser.cpp \
-src/gui/elems/baseActionEditor.h \
-src/gui/elems/baseActionEditor.cpp \
-src/gui/elems/envelopeEditor.h \
-src/gui/elems/envelopeEditor.cpp \
-src/gui/elems/pianoRoll.h \
-src/gui/elems/pianoRoll.cpp \
-src/gui/elems/noteEditor.h \
-src/gui/elems/noteEditor.cpp \
-src/gui/elems/pianoItem.h \
-src/gui/elems/pianoItem.cpp \
-src/gui/elems/muteEditor.h \
-src/gui/elems/muteEditor.cpp \
-src/gui/elems/actionEditor.h \
-src/gui/elems/actionEditor.cpp \
-src/gui/elems/ge_window.h \
-src/gui/elems/ge_window.cpp \
-src/gui/elems/ge_waveTools.h \
-src/gui/elems/ge_waveTools.cpp \
-src/gui/elems/ge_pluginBrowser.h \
-src/gui/elems/ge_pluginBrowser.cpp \
-src/gui/elems/mainWindow/mainIO.h \
-src/gui/elems/mainWindow/mainIO.cpp \
-src/gui/elems/mainWindow/mainMenu.h \
-src/gui/elems/mainWindow/mainMenu.cpp \
-src/gui/elems/mainWindow/mainTimer.h \
-src/gui/elems/mainWindow/mainTimer.cpp \
+src/gui/dialogs/midiIO/midiOutputMidiCh.h \
+src/gui/dialogs/midiIO/midiOutputMidiCh.cpp \
+src/gui/dialogs/midiIO/midiInputBase.h \
+src/gui/dialogs/midiIO/midiInputBase.cpp \
+src/gui/dialogs/midiIO/midiInputChannel.h \
+src/gui/dialogs/midiIO/midiInputChannel.cpp \
+src/gui/dialogs/midiIO/midiInputMaster.h \
+src/gui/dialogs/midiIO/midiInputMaster.cpp \
+src/gui/elems/midiLearner.h \
+src/gui/elems/midiLearner.cpp \
+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/sampleEditor/waveform.h \
+src/gui/elems/sampleEditor/waveform.cpp \
+src/gui/elems/sampleEditor/waveTools.h \
+src/gui/elems/sampleEditor/waveTools.cpp \
+src/gui/elems/sampleEditor/volumeTool.h \
+src/gui/elems/sampleEditor/volumeTool.cpp \
+src/gui/elems/sampleEditor/boostTool.h \
+src/gui/elems/sampleEditor/boostTool.cpp \
+src/gui/elems/sampleEditor/panTool.h \
+src/gui/elems/sampleEditor/panTool.cpp \
+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/actionEditor/baseActionEditor.h \
+src/gui/elems/actionEditor/baseActionEditor.cpp \
+src/gui/elems/actionEditor/envelopeEditor.h \
+src/gui/elems/actionEditor/envelopeEditor.cpp \
+src/gui/elems/actionEditor/pianoRoll.h \
+src/gui/elems/actionEditor/pianoRoll.cpp \
+src/gui/elems/actionEditor/noteEditor.h \
+src/gui/elems/actionEditor/noteEditor.cpp \
+src/gui/elems/actionEditor/basePianoItem.h \
+src/gui/elems/actionEditor/basePianoItem.cpp \
+src/gui/elems/actionEditor/pianoItem.h \
+src/gui/elems/actionEditor/pianoItem.cpp \
+src/gui/elems/actionEditor/pianoItemOrphaned.h \
+src/gui/elems/actionEditor/pianoItemOrphaned.cpp \
+src/gui/elems/actionEditor/muteEditor.h \
+src/gui/elems/actionEditor/muteEditor.cpp \
+src/gui/elems/actionEditor/actionEditor.h \
+src/gui/elems/actionEditor/actionEditor.cpp \
+src/gui/elems/actionEditor/action.h \
+src/gui/elems/actionEditor/action.cpp \
+src/gui/elems/actionEditor/gridTool.h \
+src/gui/elems/actionEditor/gridTool.cpp \
+src/gui/elems/mainWindow/mainIO.h \
+src/gui/elems/mainWindow/mainIO.cpp \
+src/gui/elems/mainWindow/mainMenu.h \
+src/gui/elems/mainWindow/mainMenu.cpp \
+src/gui/elems/mainWindow/mainTimer.h \
+src/gui/elems/mainWindow/mainTimer.cpp \
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/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/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/config/tabMisc.h \
+src/gui/elems/config/tabMisc.cpp \
+src/gui/elems/config/tabMidi.h \
+src/gui/elems/config/tabMidi.cpp \
+src/gui/elems/config/tabAudio.h \
+src/gui/elems/config/tabAudio.cpp \
+src/gui/elems/config/tabBehaviors.h \
+src/gui/elems/config/tabBehaviors.cpp \
+src/gui/elems/config/tabPlugins.h \
+src/gui/elems/config/tabPlugins.cpp \
src/gui/elems/basics/scroll.h \
src/gui/elems/basics/scroll.cpp \
src/gui/elems/basics/boxtypes.h \
src/gui/elems/basics/boxtypes.cpp \
+src/gui/elems/basics/baseButton.h \
+src/gui/elems/basics/baseButton.cpp \
+src/gui/elems/basics/statusButton.h \
+src/gui/elems/basics/statusButton.cpp \
+src/gui/elems/basics/button.h \
+src/gui/elems/basics/button.cpp \
+src/gui/elems/basics/idButton.h \
+src/gui/elems/basics/idButton.cpp \
+src/gui/elems/basics/resizerBar.h \
+src/gui/elems/basics/resizerBar.cpp \
+src/gui/elems/basics/input.h \
+src/gui/elems/basics/input.cpp \
+src/gui/elems/basics/liquidScroll.h \
+src/gui/elems/basics/liquidScroll.cpp \
+src/gui/elems/basics/choice.h \
+src/gui/elems/basics/choice.cpp \
+src/gui/elems/basics/dial.h \
+src/gui/elems/basics/dial.cpp \
+src/gui/elems/basics/box.h \
+src/gui/elems/basics/box.cpp \
+src/gui/elems/basics/slider.h \
+src/gui/elems/basics/slider.cpp \
+src/gui/elems/basics/progress.h \
+src/gui/elems/basics/progress.cpp \
+src/gui/elems/basics/check.h \
+src/gui/elems/basics/check.cpp \
+src/gui/elems/basics/radio.h \
+src/gui/elems/basics/radio.cpp \
src/utils/log.h \
src/utils/log.cpp \
+src/utils/math.h \
+src/utils/math.cpp \
src/utils/gui.h \
src/utils/gui.cpp \
src/utils/gvector.h \
src/utils/fs.h \
src/utils/fs.cpp \
+src/utils/deps.h \
+src/utils/deps.cpp \
src/utils/string.h \
src/utils/string.cpp
# via AM_CONDITIONAL inside configure.ac.
# Note: CPPFLAGS = C preprocessor flags, CXXFLAGS = C++ compiler flags.
+# TODO add -DNDEBUG for production code
giada_CXXFLAGS = -std=c++11 -Wall -Werror
giada_CPPFLAGS =
-D__WINDOWS_DS__
giada_LDADD = -ldsound -lwsock32 -lm -lfltk -lwininet -lgdi32 \
-lshell32 -lvfw32 -lrpcrt4 -luuid -lcomctl32 -lole32 -lws2_32 -lsndfile \
- -lsamplerate -lrtmidi -lwinmm -lsetupapi -lksuser -lpthreadGC2 -ljansson \
+ -lsamplerate -lrtmidi -lwinmm -lsetupapi -lksuser -ljansson \
-limm32 -lglu32 -lshell32 -lversion -lopengl32 -loleaut32 -lshlwapi -lcomdlg32
giada_LDFLAGS = -mwindows -static
giada_SOURCES += resource.rc
# -ObjC++: Juce requires to build some Objective C code
# -Wno-unknown-pragmas: shut up Juce even more
giada_SOURCES += src/utils/cocoa.mm src/utils/cocoa.h
-giada_CXXFLAGS += -ObjC++ -Wno-unknown-pragmas
+giada_CXXFLAGS += -ObjC++ -Wno-unknown-pragmas -Wno-auto-var-id
giada_LDADD = -lsndfile -lm -lpthread -lfltk -lrtmidi -lrtaudio \
-lsamplerate -ljansson
giada_LDFLAGS = -framework CoreAudio -framework Cocoa -framework Carbon \
tests/midiMapConf.cpp \
tests/pluginHost.cpp \
tests/utils.cpp \
+tests/recorder.cpp \
src/core/conf.cpp \
src/core/wave.cpp \
src/core/midiMapConf.cpp \
src/core/patch.cpp \
src/core/plugin.cpp \
-src/core/dataStorageIni.cpp \
-src/core/dataStorageJson.cpp \
+src/core/storager.cpp \
+src/core/recorder.cpp \
src/utils/fs.cpp \
src/utils/string.cpp \
src/utils/log.cpp
## Copyright
-Giada is Copyright (C) 2010-2016 by Giovanni A. Zuliani | Monocasual
+Giada is Copyright (C) 2010-2017 by Giovanni A. Zuliani | Monocasual
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.
[AM_CONDITIONAL(WITH_VST, false)]
)
+
+# ------------------------------------------------------------------------------
+
+# --debug. Enable debug compilation
+
+AC_ARG_ENABLE(
+ [debug],
+ AS_HELP_STRING([--enable-debug], [enable debug mode (asserts, ...)]),
+ [],
+ [AC_DEFINE(NDEBUG)]
+)
+
# ------------------------------------------------------------------------------
# test if files needed for Travis CI are present. If so, define a new macro
AC_LANG_PUSH([C++])
AC_CHECK_HEADER(
- [RtMidi.h],
+ [rtmidi/RtMidi.h],
[],
[AC_MSG_ERROR([library 'rtMidi' not found!])]
)
*
* Giada - Your Hardcore Loopmachine
*
- * channel
- *
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
* -------------------------------------------------------------------------- */
+#include <cassert>
+#include <cstring>
#include "../utils/log.h"
#include "../gui/elems/mainWindow/keyboard/channel.h"
+#include "const.h"
#include "channel.h"
#include "pluginHost.h"
#include "plugin.h"
#include "kernelMidi.h"
-#include "patch_DEPR_.h"
#include "patch.h"
+#include "clock.h"
#include "wave.h"
#include "mixer.h"
#include "mixerHandler.h"
#include "midiMapConf.h"
-extern Recorder G_Recorder;
-extern KernelMidi G_KernelMidi;
+using std::string;
+using namespace giada::m;
-Channel::Channel(int type, int status, int bufferSize, MidiMapConf *midiMapConf)
-#if defined(WITH_VST)
-: pluginHost(nullptr),
-#else
-:
-#endif
- midiMapConf (midiMapConf),
- bufferSize (bufferSize),
+Channel::Channel(int type, int status, int bufferSize)
+: bufferSize (bufferSize),
+ midiFilter (-1),
+ pan (0.5f),
type (type),
- status (status),
- key (0),
- volume (DEFAULT_VOL),
+ status (status),
+ key (0),
+ volume (G_DEFAULT_VOL),
volume_i (1.0f),
volume_d (0.0f),
- panLeft (1.0f),
- panRight (1.0f),
mute_i (false),
mute_s (false),
mute (false),
vChan = (float *) malloc(bufferSize * sizeof(float));
if (!vChan)
gu_log("[Channel::allocVchan] unable to alloc memory for vChan\n");
- memset(vChan, 0, bufferSize * sizeof(float));
+ std::memset(vChan, 0, bufferSize * sizeof(float));
}
volume = src->volume;
volume_i = src->volume_i;
volume_d = src->volume_d;
- panLeft = src->panLeft;
- panRight = src->panRight;
+ pan = src->pan;
mute_i = src->mute_i;
mute_s = src->mute_s;
mute = src->mute;
#ifdef WITH_VST
for (unsigned i=0; i<src->plugins.size(); i++)
- pluginHost->clonePlugin(src->plugins.at(i), PluginHost::CHANNEL,
+ pluginHost::clonePlugin(src->plugins.at(i), pluginHost::CHANNEL,
pluginMutex, this);
#endif
/* clone actions */
- for (unsigned i=0; i<G_Recorder.global.size(); i++) {
- for (unsigned k=0; k<G_Recorder.global.at(i).size(); k++) {
- Recorder::action *a = G_Recorder.global.at(i).at(k);
+ for (unsigned i=0; i<recorder::global.size(); i++) {
+ for (unsigned k=0; k<recorder::global.at(i).size(); k++) {
+ recorder::action *a = recorder::global.at(i).at(k);
if (a->chan == src->index) {
- G_Recorder.rec(index, a->type, a->frame, a->iValue, a->fValue);
+ recorder::rec(index, a->type, a->frame, a->iValue, a->fValue);
hasActions = true;
}
}
/* -------------------------------------------------------------------------- */
-void Channel::sendMidiLmessage(uint32_t learn, const MidiMapConf::message_t &msg)
+void Channel::sendMidiLmessage(uint32_t learn, const midimap::message_t &msg)
{
gu_log("[channel::sendMidiLmessage] learn=%#X, chan=%d, msg=%#X, offset=%d\n",
learn, msg.channel, msg.value, msg.offset);
* send it. */
out |= msg.value | (msg.channel << 24);
- G_KernelMidi.send(out);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void Channel::readPatchMidiIn_DEPR_(int i, Patch_DEPR_ &patch)
-{
- midiIn = patch.getMidiValue(i, "In");
- midiInKeyPress = patch.getMidiValue(i, "InKeyPress");
- midiInKeyRel = patch.getMidiValue(i, "InKeyRel");
- midiInKill = patch.getMidiValue(i, "InKill");
- midiInVolume = patch.getMidiValue(i, "InVolume");
- midiInMute = patch.getMidiValue(i, "InMute");
- midiInSolo = patch.getMidiValue(i, "InSolo");
-}
-
-void Channel::readPatchMidiOut_DEPR_(int i, Patch_DEPR_ &patch)
-{
- midiOutL = patch.getMidiValue(i, "OutL");
- midiOutLplaying = patch.getMidiValue(i, "OutLplaying");
- midiOutLmute = patch.getMidiValue(i, "OutLmute");
- midiOutLsolo = patch.getMidiValue(i, "OutLsolo");
+ kernelMidi::send(out);
}
/* -------------------------------------------------------------------------- */
-int Channel::writePatch(int i, bool isProject, Patch *patch)
+int Channel::writePatch(int i, bool isProject)
{
- Patch::channel_t pch;
+ patch::channel_t pch;
pch.type = type;
pch.key = key;
pch.index = index;
pch.mute_s = mute_s;
pch.solo = solo;
pch.volume = volume;
- pch.panLeft = panLeft;
- pch.panRight = panRight;
+ pch.pan = pan;
pch.midiIn = midiIn;
pch.midiInKeyPress = midiInKeyPress;
pch.midiInKeyRel = midiInKeyRel;
pch.midiOutLmute = midiOutLmute;
pch.midiOutLsolo = midiOutLsolo;
- for (unsigned i=0; i<G_Recorder.global.size(); i++) {
- for (unsigned k=0; k<G_Recorder.global.at(i).size(); k++) {
- Recorder::action *action = G_Recorder.global.at(i).at(k);
+ for (unsigned i=0; i<recorder::global.size(); i++) {
+ for (unsigned k=0; k<recorder::global.at(i).size(); k++) {
+ recorder::action *action = recorder::global.at(i).at(k);
if (action->chan == index) {
- Patch::action_t pac;
+ patch::action_t pac;
pac.type = action->type;
pac.frame = action->frame;
pac.fValue = action->fValue;
#ifdef WITH_VST
- unsigned numPlugs = pluginHost->countPlugins(PluginHost::CHANNEL, this);
+ unsigned numPlugs = pluginHost::countPlugins(pluginHost::CHANNEL, this);
for (unsigned i=0; i<numPlugs; i++) {
- Plugin *pPlugin = pluginHost->getPluginByIndex(i, PluginHost::CHANNEL, this);
- Patch::plugin_t pp;
+ Plugin *pPlugin = pluginHost::getPluginByIndex(i, pluginHost::CHANNEL, this);
+ patch::plugin_t pp;
pp.path = pPlugin->getUniqueId();
pp.bypass = pPlugin->isBypassed();
for (int k=0; k<pPlugin->getNumParameters(); k++)
#endif
- patch->channels.push_back(pch);
+ patch::channels.push_back(pch);
- return patch->channels.size() - 1;
+ return patch::channels.size() - 1;
}
/* -------------------------------------------------------------------------- */
-int Channel::readPatch(const string &path, int i, Patch *patch,
- pthread_mutex_t *pluginMutex, int samplerate, int rsmpQuality)
+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;
type = pch->type;
index = pch->index;
mute_s = pch->mute_s;
solo = pch->solo;
volume = pch->volume;
- panLeft = pch->panLeft;
- panRight = pch->panRight;
+ pan = pch->pan;
midiIn = pch->midiIn;
midiInKeyPress = pch->midiInKeyPress;
midiInKeyRel = pch->midiInKeyRel;
midiOutLsolo = pch->midiOutLsolo;
for (unsigned k=0; k<pch->actions.size(); k++) {
- Patch::action_t *ac = &pch->actions.at(k);
- G_Recorder.rec(index, ac->type, ac->frame, ac->iValue, ac->fValue);
+ patch::action_t *ac = &pch->actions.at(k);
+ recorder::rec(index, ac->type, ac->frame, ac->iValue, ac->fValue);
hasActions = true;
}
#ifdef WITH_VST
for (unsigned k=0; k<pch->plugins.size(); k++) {
- Patch::plugin_t *ppl = &pch->plugins.at(k);
- Plugin *plugin = pluginHost->addPlugin(ppl->path, PluginHost::CHANNEL,
+ patch::plugin_t *ppl = &pch->plugins.at(k);
+ Plugin *plugin = pluginHost::addPlugin(ppl->path, pluginHost::CHANNEL,
pluginMutex, this);
if (plugin == nullptr) {
ret &= 0;
if (!midiOutL || midiOutLmute == 0x0)
return;
if (mute)
- sendMidiLmessage(midiOutLsolo, midiMapConf->muteOn);
+ sendMidiLmessage(midiOutLsolo, midimap::muteOn);
else
- sendMidiLmessage(midiOutLsolo, midiMapConf->muteOff);
+ sendMidiLmessage(midiOutLsolo, midimap::muteOff);
}
if (!midiOutL || midiOutLsolo == 0x0)
return;
if (solo)
- sendMidiLmessage(midiOutLsolo, midiMapConf->soloOn);
+ sendMidiLmessage(midiOutLsolo, midimap::soloOn);
else
- sendMidiLmessage(midiOutLsolo, midiMapConf->soloOff);
+ sendMidiLmessage(midiOutLsolo, midimap::soloOff);
}
return;
switch (status) {
case STATUS_OFF:
- sendMidiLmessage(midiOutLplaying, midiMapConf->stopped);
+ sendMidiLmessage(midiOutLplaying, midimap::stopped);
break;
case STATUS_PLAY:
- sendMidiLmessage(midiOutLplaying, midiMapConf->playing);
+ sendMidiLmessage(midiOutLplaying, midimap::playing);
break;
case STATUS_WAIT:
- sendMidiLmessage(midiOutLplaying, midiMapConf->waiting);
+ sendMidiLmessage(midiOutLplaying, midimap::waiting);
break;
case STATUS_ENDING:
- sendMidiLmessage(midiOutLplaying, midiMapConf->stopping);
+ sendMidiLmessage(midiOutLplaying, midimap::stopping);
}
}
/* -------------------------------------------------------------------------- */
-#ifdef WITH_VST
+void Channel::setPan(float v)
+{
+ if (v > 1.0f)
+ pan = 1.0f;
+ else
+ if (v < 0.0f)
+ pan = 0.0f;
+ else
+ pan = v;
+}
-juce::MidiBuffer &Channel::getPluginMidiEvents()
+
+float Channel::getPan()
{
- return midiBuffer;
+ return pan;
}
/* -------------------------------------------------------------------------- */
-void Channel::clearMidiBuffer()
+float Channel::calcPanning(int ch)
{
- midiBuffer.clear();
+ if (pan == 0.5f) // center: nothing to do
+ return 1.0;
+ if (ch == 0)
+ return 1.0 - pan;
+ else // channel 1
+ return pan;
}
/* -------------------------------------------------------------------------- */
-void Channel::setPluginHost(PluginHost *pluginHost)
+#ifdef WITH_VST
+
+juce::MidiBuffer &Channel::getPluginMidiEvents()
{
- this->pluginHost = pluginHost;
+ return midiBuffer;
}
+
+/* -------------------------------------------------------------------------- */
+
+
+void Channel::clearMidiBuffer()
+{
+ midiBuffer.clear();
+}
+
+
#endif
*
* Giada - Your Hardcore Loopmachine
*
- * channel
- *
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
* -------------------------------------------------------------------------- */
-#ifndef CHANNEL_H
-#define CHANNEL_H
+#ifndef G_CHANNEL_H
+#define G_CHANNEL_H
#include <vector>
+#include <string>
#include <pthread.h>
#include "midiMapConf.h"
#include "recorder.h"
#endif
-using std::vector;
-using std::string;
+class Plugin;
+class MidiMapConf;
+class geChannel;
class Channel
#ifdef WITH_VST
- /* pluginHost
- * Pointer to PluginHost class, which manages and processes plugins. */
-
- class PluginHost *pluginHost;
-
/* 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. */
#endif
- /* MidiMapConf
- * Pointer to MidiMapConf. It deals with Midi lightning operations. */
-
- class MidiMapConf *midiMapConf;
-
/* bufferSize
* size of every buffer in this channel (vChan, pChan) */
int bufferSize;
+ /* midiFilter
+ Which MIDI channel should be filtered out when receiving MIDI messages. -1
+ means 'all'. */
+
+ int midiFilter;
+
+ float pan;
+
/* sendMidiLMessage
- * compose a MIDI message by merging bytes from MidiMap conf class, and send
- * it to KernelMidi. */
+ Composes a MIDI message by merging bytes from MidiMap conf class, and sends it
+ to KernelMidi. */
- void sendMidiLmessage(uint32_t learn, const MidiMapConf::message_t &msg);
+ 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);
public:
- Channel(int type, int status, int bufferSize, class MidiMapConf *midiMapConf);
+ Channel(int type, int status, int bufferSize);
virtual ~Channel();
/* readPatch
* Fill channel with data from patch. */
- virtual int readPatch_DEPR_(const char *file, int i, class Patch_DEPR_ *patch,
- int samplerate, int rsmpQuality) = 0;
- virtual int readPatch(const string &basePath, int i, class Patch *patch,
- pthread_mutex_t *pluginMutex, int samplerate, int rsmpQuality);
+ virtual int readPatch(const std::string &basePath, int i,
+ pthread_mutex_t *pluginMutex, int samplerate, int rsmpQuality);
/* process
Merges vChannels into buffer, plus plugin processing (if any). Warning:
* mixer::channels, used by recorder. LocalFrame = frame within the current
* buffer. */
- virtual void quantize(int index, int localFrame, class Mixer *m) = 0;
+ virtual void quantize(int index, int localFrame) = 0;
/* onZero
* action to do when frame goes to zero, i.e. sequencer restart. */
// TODO - quantize is useless!
- virtual void parseAction(Recorder::action *a, int localFrame, int globalFrame,
- int quantize, bool mixerIsRunning) = 0;
+ virtual void parseAction(giada::m::recorder::action *a, int localFrame,
+ int globalFrame, int quantize, bool mixerIsRunning) = 0;
/* rewind
* rewind channel when rewind button is pressed. */
* Fill a patch with channel values. Returns the index of the last
* Patch::channel_t added. */
- virtual int writePatch(int i, bool isProject, class Patch *patch);
+ virtual int writePatch(int i, bool isProject);
/* receiveMidi
* Receives and processes midi messages from external devices. */
float volume; // global volume
float volume_i; // internal volume
float volume_d; // delta volume (for envelope)
- float panLeft;
- float panRight;
bool mute_i; // internal mute
bool mute_s; // previous mute status after being solo'd
bool mute; // global mute
bool armed; // armed for recording
int recStatus; // status of recordings (waiting, ending, ...)
float *vChan; // virtual channel
- class geChannel *guiChannel; // pointer to a gChannel object, part of the GUI
+ geChannel *guiChannel; // pointer to a gChannel object, part of the GUI
// TODO - midi structs, please
uint32_t midiOutLsolo;
#ifdef WITH_VST
- vector <class Plugin *> plugins;
+ std::vector <Plugin *> plugins;
#endif
bool isPlaying();
- /* readPatchMidiIn
- * read from patch all midi-related parameters such as keypress, mute
- * and so on. */
-
- void readPatchMidiIn_DEPR_(int i, class Patch_DEPR_ &patch);
- void readPatchMidiOut_DEPR_(int i, class Patch_DEPR_ &patch);
-
/* sendMidiL*
* send MIDI lightning events to a physical device. */
void sendMidiLsolo();
void sendMidiLplay();
-#ifdef WITH_VST
-
- /* SetPluginHost
- * A neat trick to avoid duplicated constructors (with and without pointer
- * to PluginHost). */
+ void setPan(float v);
+ float getPan();
- void setPluginHost(class PluginHost *pluginHost);
+#ifdef WITH_VST
/* getPluginMidiEvents
* Return a reference to midiBuffer stack. This is available for any kind of
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * 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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#include <cassert>
+#include "../glue/transport.h"
+#include "../glue/main.h"
+#include "conf.h"
+#include "const.h"
+#include "kernelAudio.h"
+#include "kernelMidi.h"
+#include "clock.h"
+
+
+namespace giada {
+namespace m {
+namespace clock
+{
+namespace
+{
+bool running = false;
+float bpm = G_DEFAULT_BPM;
+int bars = G_DEFAULT_BARS;
+int beats = G_DEFAULT_BEATS;
+int quantize = G_DEFAULT_QUANTIZE;
+int quanto = 1; // quantizer step
+int framesPerBar = 0; // frames in one bar
+int framesPerBeat = 0; // frames in one beat
+int framesInSequencer = 0; // frames in the whole sequencer
+int totalFrames = 0; // frames in the selected range (e.g. 4/4)
+int currentFrame = 0;
+int currentBeat = 0;
+
+int midiTCrate = 0; // send MTC data every midiTCrate frames
+int midiTCframes = 0;
+int midiTCseconds = 0;
+int midiTCminutes = 0;
+int midiTChours = 0;
+
+#ifdef __linux__
+kernelAudio::JackState jackStatePrev;
+#endif
+
+
+void updateQuanto()
+{
+ if (quantize != 0)
+ quanto = framesPerBeat / quantize;
+ if (quanto % 2 != 0)
+ quanto++;
+}
+
+}; // {anonymous}
+
+
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+
+
+void init(int sampleRate, float midiTCfps)
+{
+ midiTCrate = (sampleRate / midiTCfps) * 2; // stereo values
+ updateFrameBars();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+bool isRunning()
+{
+ return running;
+}
+
+
+bool quantoHasPassed()
+{
+ return currentFrame % (quanto) == 0;
+}
+
+
+bool isOnBar()
+{
+ /* A bar cannot occur at frame 0. That's the first beat. */
+ return currentFrame % framesPerBar == 0 && currentFrame != 0;
+}
+
+
+bool isOnBeat()
+{
+ /* Skip frame 0: it is intended as 'first beat'. */
+ /* TODO - this is wrong! */
+ return currentFrame % framesPerBeat == 0 && currentFrame > 0;
+}
+
+
+bool isOnFirstBeat()
+{
+ return currentFrame == 0;
+}
+
+
+void start()
+{
+ running = true;
+ if (conf::midiSync == MIDI_SYNC_CLOCK_M) {
+ kernelMidi::send(MIDI_START, -1, -1);
+ kernelMidi::send(MIDI_POSITION_PTR, 0, 0);
+ }
+}
+
+
+void stop()
+{
+ running = false;
+ if (conf::midiSync == MIDI_SYNC_CLOCK_M)
+ kernelMidi::send(MIDI_STOP, -1, -1);
+}
+
+
+void setBpm(float b)
+{
+ if (b < G_MIN_BPM)
+ b = G_MIN_BPM;
+ bpm = b;
+ updateFrameBars();
+}
+
+
+void setBars(int newBars)
+{
+ /* Bars cannot be greater than beats and must be a sub multiple of beats. If
+ not, approximate to the nearest (and greater) value available. */
+
+ if (newBars > beats)
+ bars = beats;
+ else if (newBars <= 0)
+ bars = 1;
+ else if (beats % newBars != 0) {
+ bars = newBars + (beats % newBars);
+ if (beats % bars != 0) // it could be an odd value, let's check it (and avoid it)
+ bars = bars - (beats % bars);
+ }
+ else
+ bars = newBars;
+}
+
+
+void setBeats(int b)
+{
+ if (b > G_MAX_BEATS)
+ beats = G_MAX_BEATS;
+ else if (b < 1)
+ beats = 1;
+ else
+ beats = b;
+}
+
+
+void setQuantize(int q)
+{
+ quantize = q;
+ updateQuanto();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void incrCurrentFrame()
+{
+ currentFrame += 2;
+ if (currentFrame > totalFrames) {
+ currentFrame = 0;
+ currentBeat = 0;
+ }
+ else
+ if (isOnBeat())
+ currentBeat++;
+}
+
+
+void rewind()
+{
+ currentFrame = 0;
+ currentBeat = 0;
+ sendMIDIrewind();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void updateFrameBars()
+{
+ /* seconds ....... total time of play (in seconds) of the whole
+ * sequencer. 60 / bpm == how many seconds lasts one bpm
+ * totalFrames ... loop length in frames, x2 because it's stereo
+ * framesPerBar .. n. of frames within a bar
+ * framesPerBeat . n. of frames within a beat
+ * framesInSeq ... number of frames in the whole sequencer */
+
+ float seconds = (60.0f / bpm) * beats;
+ totalFrames = conf::samplerate * seconds * 2;
+ framesPerBar = totalFrames / bars;
+ framesPerBeat = totalFrames / beats;
+ framesInSequencer = framesPerBeat * G_MAX_BEATS;
+
+ /* big troubles if frames are odd. */
+
+ if (totalFrames % 2 != 0)
+ totalFrames--;
+ if (framesPerBar % 2 != 0)
+ framesPerBar--;
+ if (framesPerBeat % 2 != 0)
+ framesPerBeat--;
+
+ updateQuanto();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void sendMIDIsync()
+{
+ /* TODO - only Master (_M) is implemented so far. */
+
+ if (conf::midiSync == MIDI_SYNC_CLOCK_M) {
+ if (currentFrame % (framesPerBeat/24) == 0)
+ kernelMidi::send(MIDI_CLOCK, -1, -1);
+ return;
+ }
+
+ if (conf::midiSync == MIDI_SYNC_MTC_M) {
+
+ /* check if a new timecode frame has passed. If so, send MIDI TC
+ * quarter frames. 8 quarter frames, divided in two branches:
+ * 1-4 and 5-8. We check timecode frame's parity: if even, send
+ * range 1-4, if odd send 5-8. */
+
+ if (currentFrame % midiTCrate != 0) // no timecode frame passed
+ return;
+
+ /* frame low nibble
+ * frame high nibble
+ * seconds low nibble
+ * seconds high nibble */
+
+ if (midiTCframes % 2 == 0) {
+ kernelMidi::send(MIDI_MTC_QUARTER, (midiTCframes & 0x0F) | 0x00, -1);
+ kernelMidi::send(MIDI_MTC_QUARTER, (midiTCframes >> 4) | 0x10, -1);
+ kernelMidi::send(MIDI_MTC_QUARTER, (midiTCseconds & 0x0F) | 0x20, -1);
+ kernelMidi::send(MIDI_MTC_QUARTER, (midiTCseconds >> 4) | 0x30, -1);
+ }
+
+ /* minutes low nibble
+ * minutes high nibble
+ * hours low nibble
+ * hours high nibble SMPTE frame rate */
+
+ else {
+ kernelMidi::send(MIDI_MTC_QUARTER, (midiTCminutes & 0x0F) | 0x40, -1);
+ kernelMidi::send(MIDI_MTC_QUARTER, (midiTCminutes >> 4) | 0x50, -1);
+ kernelMidi::send(MIDI_MTC_QUARTER, (midiTChours & 0x0F) | 0x60, -1);
+ kernelMidi::send(MIDI_MTC_QUARTER, (midiTChours >> 4) | 0x70, -1);
+ }
+
+ midiTCframes++;
+
+ /* check if total timecode frames are greater than timecode fps:
+ * if so, a second has passed */
+
+ if (midiTCframes > conf::midiTCfps) {
+ midiTCframes = 0;
+ midiTCseconds++;
+ if (midiTCseconds >= 60) {
+ midiTCminutes++;
+ midiTCseconds = 0;
+ if (midiTCminutes >= 60) {
+ midiTChours++;
+ midiTCminutes = 0;
+ }
+ }
+ //gu_log("%d:%d:%d:%d\n", midiTChours, midiTCminutes, midiTCseconds, midiTCframes);
+ }
+ }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void sendMIDIrewind()
+{
+ midiTCframes = 0;
+ midiTCseconds = 0;
+ midiTCminutes = 0;
+ midiTChours = 0;
+
+ /* For cueing the slave to a particular start point, Quarter Frame
+ * messages are not used. Instead, an MTC Full Frame message should
+ * be sent. The Full Frame is a SysEx message that encodes the entire
+ * SMPTE time in one message */
+
+ if (conf::midiSync == MIDI_SYNC_MTC_M) {
+ kernelMidi::send(MIDI_SYSEX, 0x7F, 0x00); // send msg on channel 0
+ kernelMidi::send(0x01, 0x01, 0x00); // hours 0
+ kernelMidi::send(0x00, 0x00, 0x00); // mins, secs, frames 0
+ kernelMidi::send(MIDI_EOX, -1, -1); // end of sysex
+ }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+#ifdef __linux__
+
+void recvJackSync()
+{
+ kernelAudio::JackState jackState = kernelAudio::jackTransportQuery();
+
+ if (jackState.running != jackStatePrev.running) {
+ if (jackState.running) {
+ if (!isRunning())
+ glue_startSeq(false); // not from UI
+ }
+ else {
+ if (isRunning())
+ glue_stopSeq(false); // not from UI
+ }
+ }
+ if (jackState.bpm != jackStatePrev.bpm)
+ if (jackState.bpm > 1.0f) // 0 bpm if Jack does not send that info
+ glue_setBpm(jackState.bpm);
+
+ if (jackState.frame == 0 && jackState.frame != jackStatePrev.frame)
+ glue_rewindSeq(false, false); // not from UI, don't notify jack (avoid loop)
+
+ jackStatePrev = jackState;
+}
+
+#endif
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int getCurrentFrame()
+{
+ return currentFrame;
+}
+
+
+int getTotalFrames()
+{
+ return totalFrames;
+}
+
+
+int getCurrentBeat()
+{
+ return currentBeat;
+}
+
+
+int getQuantize()
+{
+ return quantize;
+}
+
+
+float getBpm()
+{
+ return bpm;
+}
+
+
+int getBeats()
+{
+ return beats;
+}
+
+
+int getBars()
+{
+ return bars;
+}
+
+
+int getQuanto()
+{
+ return quanto;
+}
+
+
+int getFramesPerBar()
+{
+ return framesPerBar;
+}
+
+
+int getFramesPerBeat()
+{
+ return framesPerBeat;
+}
+
+
+int getFramesInSequencer()
+{
+ return framesInSequencer;
+}
+
+
+}}}; // giada::m::clock::
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * 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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#ifndef G_CLOCK_H
+#define G_CLOCK_H
+
+
+class Conf;
+class KernelMidi;
+class KernelAudio;
+
+
+namespace giada {
+namespace m {
+namespace clock
+{
+void init(int sampleRate, float midiTCfps);
+
+/* sendMIDIsync
+Generates MIDI sync output data. */
+
+void sendMIDIsync();
+
+/* sendMIDIrewind
+Rewinds timecode to beat 0 and also send a MTC full frame to cue the slave. */
+
+void sendMIDIrewind();
+
+#ifdef __linux__
+void recvJackSync();
+#endif
+
+float getBpm();
+int getBeats();
+int getBars();
+int getCurrentFrame();
+int getCurrentBeat();
+int getFramesPerBar();
+int getFramesPerBeat();
+int getTotalFrames();
+int getFramesInSequencer();
+int getQuantize();
+int getQuanto();
+
+/* incrCurrentFrame
+Increases current frame of a stereo step (+2). */
+
+void incrCurrentFrame();
+
+/* quantoHasPassed
+Tells whether a quanto unit has passed yet. */
+
+bool quantoHasPassed();
+
+/* updateFrameBars
+Updates bpm, frames, beats and so on. */
+
+void updateFrameBars();
+
+void setBpm(float b);
+void setBars(int b);
+void setBeats(int b);
+void setQuantize(int q);
+
+bool isRunning();
+bool isOnBeat();
+bool isOnBar();
+bool isOnFirstBeat();
+
+void rewind();
+void start();
+void stop();
+}}}; // giada::m::clock::
+
+
+#endif
*
* Giada - Your Hardcore Loopmachine
*
- * conf
- *
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
#include <string>
-#include "conf.h"
-#include "const.h"
#include "../utils/fs.h"
#include "../utils/log.h"
+#include "storager.h"
+#include "const.h"
+#include "conf.h"
using std::string;
-Conf::Conf()
+namespace giada {
+namespace m {
+namespace conf
{
- /* Initialize confFilePath, i.e. the configuration file. In windows it is in
- * the same dir of the .exe, while in Linux and OS X in ~/.giada */
-
-#if defined(__linux__) || defined(__APPLE__)
+namespace
+{
+string confFilePath = "";
+string confDirPath = "";
- confFilePath = gu_getHomePath() + G_SLASH + CONF_FILENAME;
- confDirPath = gu_getHomePath() + G_SLASH;
-#elif defined(_WIN32)
+/* -------------------------------------------------------------------------- */
- confFilePath = CONF_FILENAME;
- confDirPath = "";
+/* sanitize
+Avoids funky values from config file. */
+void sanitize()
+{
+ if (!(soundSystem & G_SYS_API_ANY)) soundSystem = G_DEFAULT_SOUNDSYS;
+ if (soundDeviceOut < 0) soundDeviceOut = G_DEFAULT_SOUNDDEV_OUT;
+ if (soundDeviceIn < -1) soundDeviceIn = G_DEFAULT_SOUNDDEV_IN;
+ if (channelsOut < 0) channelsOut = 0;
+ if (channelsIn < 0) channelsIn = 0;
+ if (buffersize < 8 || buffersize > 4096) buffersize = G_DEFAULT_BUFSIZE;
+ if (delayComp < 0) delayComp = G_DEFAULT_DELAYCOMP;
+ if (midiPortOut < -1) midiPortOut = G_DEFAULT_MIDI_SYSTEM;
+ if (midiPortOut < -1) midiPortOut = G_DEFAULT_MIDI_PORT_OUT;
+ if (midiPortIn < -1) midiPortIn = G_DEFAULT_MIDI_PORT_IN;
+ if (browserX < 0) browserX = 0;
+ if (browserY < 0) browserY = 0;
+ if (browserW < 396) browserW = 396;
+ if (browserH < 302) browserH = 302;
+ if (actionEditorX < 0) actionEditorX = 0;
+ if (actionEditorY < 0) actionEditorY = 0;
+ if (actionEditorW < 640) actionEditorW = 640;
+ if (actionEditorH < 176) actionEditorH = 176;
+ if (actionEditorZoom < 100) actionEditorZoom = 100;
+ if (actionEditorGridVal < 0) actionEditorGridVal = 0;
+ if (actionEditorGridOn < 0) actionEditorGridOn = 0;
+ if (pianoRollH <= 0) pianoRollH = 422;
+ if (sampleEditorX < 0) sampleEditorX = 0;
+ if (sampleEditorY < 0) sampleEditorY = 0;
+ if (sampleEditorW < 500) sampleEditorW = 500;
+ if (sampleEditorH < 292) sampleEditorH = 292;
+ if (sampleEditorGridVal < 0) sampleEditorGridVal = 0;
+ if (sampleEditorGridOn < 0) sampleEditorGridOn = 0;
+ if (midiInputX < 0) midiInputX = 0;
+ if (midiInputY < 0) midiInputY = 0;
+ if (midiInputW < G_DEFAULT_MIDI_INPUT_UI_W) midiInputW = G_DEFAULT_MIDI_INPUT_UI_W;
+ if (midiInputH < G_DEFAULT_MIDI_INPUT_UI_H) midiInputH = G_DEFAULT_MIDI_INPUT_UI_H;
+ if (configX < 0) configX = 0;
+ if (configY < 0) configY = 0;
+ if (pluginListX < 0) pluginListX = 0;
+ if (pluginListY < 0) pluginListY = 0;
+#ifdef WITH_VST
+ if (pluginChooserW < 640) pluginChooserW = 640;
+ if (pluginChooserH < 480) pluginChooserW = 480;
#endif
+ if (bpmX < 0) bpmX = 0;
+ if (bpmY < 0) bpmY = 0;
+ if (beatsX < 0) beatsX = 0;
+ if (beatsY < 0) beatsY = 0;
+ if (aboutX < 0) aboutX = 0;
+ if (aboutY < 0) aboutY = 0;
+ if (samplerate < 8000) samplerate = G_DEFAULT_SAMPLERATE;
+ if (rsmpQuality < 0 || rsmpQuality > 4) rsmpQuality = 0;
}
/* -------------------------------------------------------------------------- */
-int Conf::createConfigFolder()
+/* createConfigFolder
+Creates local folder where to put the configuration file. Path differs from OS
+to OS. */
+
+int createConfigFolder()
{
#if defined(__linux__) || defined(__APPLE__)
if (gu_dirExists(confDirPath))
return 1;
- gu_log("[Conf::createConfigFolder] .giada folder not present. Updating...\n");
+ gu_log("[conf::createConfigFolder] .giada folder not present. Updating...\n");
if (gu_mkdir(confDirPath)) {
- gu_log("[Conf::createConfigFolder] status: ok\n");
+ gu_log("[conf::createConfigFolder] status: ok\n");
return 1;
}
else {
- gu_log("[Conf::createConfigFolder] status: error!\n");
+ gu_log("[conf::createConfigFolder] status: error!\n");
return 0;
}
#endif
}
+}; // {anonymous}
+
/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+
+
+string header = "GIADACFG";
+
+int logMode = LOG_MODE_MUTE;
+int soundSystem = G_DEFAULT_SOUNDSYS;
+int soundDeviceOut = G_DEFAULT_SOUNDDEV_OUT;
+int soundDeviceIn = G_DEFAULT_SOUNDDEV_IN;
+int channelsOut = 0;
+int channelsIn = 0;
+int samplerate = G_DEFAULT_SAMPLERATE;
+int buffersize = G_DEFAULT_BUFSIZE;
+int delayComp = G_DEFAULT_DELAYCOMP;
+bool limitOutput = false;
+int rsmpQuality = 0;
+
+int midiSystem = 0;
+int midiPortOut = G_DEFAULT_MIDI_PORT_OUT;
+int midiPortIn = G_DEFAULT_MIDI_PORT_IN;
+bool noNoteOff = false;
+string midiMapPath = "";
+string lastFileMap = "";
+int midiSync = MIDI_SYNC_NONE;
+float midiTCfps = 25.0f;
+
+uint32_t midiInRewind = 0x0;
+uint32_t midiInStartStop = 0x0;
+uint32_t midiInActionRec = 0x0;
+uint32_t midiInInputRec = 0x0;
+uint32_t midiInVolumeIn = 0x0;
+uint32_t midiInVolumeOut = 0x0;
+uint32_t midiInBeatDouble = 0x0;
+uint32_t midiInBeatHalf = 0x0;
+uint32_t midiInMetronome = 0x0;
+
+bool recsStopOnChanHalt = false;
+bool chansStopOnSeqHalt = false;
+bool treatRecsAsLoops = false;
+bool resizeRecordings = true;
+bool inputMonitorDefaultOn = false;
+
+string pluginPath = "";
+string patchPath = "";
+string samplePath = "";
+
+int mainWindowX = 0;
+int mainWindowY = 0;
+int mainWindowW = GUI_WIDTH;
+int mainWindowH = GUI_HEIGHT;
+
+int browserX = 0;
+int browserY = 0;
+int browserW = 640;
+int browserH = 480;
+int browserPosition = 0;
+int browserLastValue = 0;
+string browserLastPath = "";
+
+int actionEditorX = 0;
+int actionEditorY = 0;
+int actionEditorW = 640;
+int actionEditorH = 480;
+int actionEditorZoom = 100;
+int actionEditorGridVal = 1;
+int actionEditorGridOn = false;
+
+int sampleEditorX = 0;
+int sampleEditorY = 0;
+int sampleEditorW = 640;
+int sampleEditorH = 480;
+int sampleEditorGridVal = 1;
+int sampleEditorGridOn = false;
+
+int midiInputX = 0;
+int midiInputY = 0;
+int midiInputW = G_DEFAULT_MIDI_INPUT_UI_W;
+int midiInputH = G_DEFAULT_MIDI_INPUT_UI_H;
+
+int pianoRollY = -1;
+int pianoRollH = 422;
+
+int pluginListX = 0;
+int pluginListY = 0;
+
+int configX = 0;
+int configY = 0;
+
+int bpmX = 0;
+int bpmY = 0;
+
+int beatsX = 0;
+int beatsY = 0;
+
+int aboutX = 0;
+int aboutY = 0;
+#ifdef WITH_VST
+
+int pluginChooserX = 0;
+int pluginChooserY = 0;
+int pluginChooserW = 640;
+int pluginChooserH = 480;
+int pluginSortMethod = 0;
+
+#endif
-void Conf::init()
+
+/* -------------------------------------------------------------------------- */
+
+
+void init()
{
- header = "GIADACFG";
- logMode = LOG_MODE_MUTE;
-
- soundSystem = DEFAULT_SOUNDSYS;
- soundDeviceOut = DEFAULT_SOUNDDEV_OUT;
- soundDeviceIn = DEFAULT_SOUNDDEV_IN;
- samplerate = DEFAULT_SAMPLERATE;
- buffersize = DEFAULT_BUFSIZE;
- delayComp = DEFAULT_DELAYCOMP;
- limitOutput = false;
- rsmpQuality = 0;
-
- midiPortIn = DEFAULT_MIDI_PORT_IN;
- noNoteOff = false;
- midiMapPath = "";
- midiPortOut = DEFAULT_MIDI_PORT_OUT;
- midiSync = MIDI_SYNC_NONE;
- midiTCfps = 25.0f;
-
- midiInRewind = 0x0;
- midiInStartStop = 0x0;
- midiInActionRec = 0x0;
- midiInInputRec = 0x0;
- midiInVolumeIn = 0x0;
- midiInVolumeOut = 0x0;
- midiInBeatDouble = 0x0;
- midiInBeatHalf = 0x0;
- midiInMetronome = 0x0;
-
- pluginPath = "";
- patchPath = "";
- samplePath = "";
-
- recsStopOnChanHalt = false;
- chansStopOnSeqHalt = false;
- treatRecsAsLoops = false;
-
- resizeRecordings = true;
-
- mainWindowX = 0;
- mainWindowY = 0;
- mainWindowW = GUI_WIDTH;
- mainWindowH = GUI_HEIGHT;
-
- browserX = 0;
- browserY = 0;
- browserW = 640;
- browserH = 480;
- browserPosition = 0;
- browserLastValue = 0;
-
- actionEditorX = 0;
- actionEditorY = 0;
- actionEditorW = 640;
- actionEditorH = 480;
- actionEditorZoom = 100;
- actionEditorGridOn = false;
- actionEditorGridVal = 1;
-
- sampleEditorX = 0;
- sampleEditorY = 0;
- sampleEditorW = 640;
- sampleEditorH = 480;
-
- midiInputX = 0;
- midiInputY = 0;
- midiInputW = G_DEFAULT_MIDI_INPUT_UI_W;
- midiInputH = G_DEFAULT_MIDI_INPUT_UI_H;
-
- pianoRollY = -1;
- pianoRollH = 422;
-
- #ifdef WITH_VST
-
- pluginChooserX = 0;
- pluginChooserY = 0;
- pluginChooserW = 640;
- pluginChooserH = 480;
- pluginSortMethod = 0;
-
- #endif
+ /* Initialize confFilePath, i.e. the configuration file. In windows it is in
+ * the same dir of the .exe, while in Linux and OS X in ~/.giada */
+
+#if defined(__linux__) || defined(__APPLE__)
+
+ confFilePath = gu_getHomePath() + G_SLASH + CONF_FILENAME;
+ confDirPath = gu_getHomePath() + G_SLASH;
+
+#elif defined(_WIN32)
+
+ confFilePath = CONF_FILENAME;
+ confDirPath = "";
+
+#endif
}
/* -------------------------------------------------------------------------- */
-int Conf::read()
+int read()
{
init();
- jRoot = json_load_file(confFilePath.c_str(), 0, &jError);
+ json_error_t jError;
+ json_t *jRoot = json_load_file(confFilePath.c_str(), 0, &jError);
if (!jRoot) {
- gu_log("[Conf::read] unable to read configuration file! Error on line %d: %s\n", jError.line, jError.text);
+ gu_log("[conf::read] unable to read configuration file! Error on line %d: %s\n",
+ jError.line, jError.text);
return 0;
}
- if (!checkObject(jRoot, "root element")) {
+ if (!storager::checkObject(jRoot, "root element")) {
json_decref(jRoot);
return 0;
}
- if (!setString(jRoot, CONF_KEY_HEADER, header)) return 0;
- if (!setInt(jRoot, CONF_KEY_LOG_MODE, logMode)) return 0;
- if (!setInt(jRoot, CONF_KEY_SOUND_SYSTEM, soundSystem)) return 0;
- if (!setInt(jRoot, CONF_KEY_SOUND_DEVICE_OUT, soundDeviceOut)) return 0;
- if (!setInt(jRoot, CONF_KEY_SOUND_DEVICE_IN, soundDeviceIn)) return 0;
- if (!setInt(jRoot, CONF_KEY_CHANNELS_OUT, channelsOut)) return 0;
- if (!setInt(jRoot, CONF_KEY_CHANNELS_IN, channelsIn)) return 0;
- if (!setInt(jRoot, CONF_KEY_SAMPLERATE, samplerate)) return 0;
- if (!setInt(jRoot, CONF_KEY_BUFFER_SIZE, buffersize)) return 0;
- if (!setInt(jRoot, CONF_KEY_DELAY_COMPENSATION, delayComp)) return 0;
- if (!setBool(jRoot, CONF_KEY_LIMIT_OUTPUT, limitOutput)) return 0;
- if (!setInt(jRoot, CONF_KEY_RESAMPLE_QUALITY, rsmpQuality)) return 0;
- if (!setInt(jRoot, CONF_KEY_MIDI_SYSTEM, midiSystem)) return 0;
- if (!setInt(jRoot, CONF_KEY_MIDI_PORT_OUT, midiPortOut)) return 0;
- if (!setInt(jRoot, CONF_KEY_MIDI_PORT_IN, midiPortIn)) return 0;
- if (!setBool(jRoot, CONF_KEY_NO_NOTE_OFF, noNoteOff)) return 0;
- if (!setString(jRoot, CONF_KEY_MIDIMAP_PATH, midiMapPath)) return 0;
- if (!setString(jRoot, CONF_KEY_LAST_MIDIMAP, lastFileMap)) return 0;
- if (!setInt(jRoot, CONF_KEY_MIDI_SYNC, midiSync)) return 0;
- if (!setFloat(jRoot, CONF_KEY_MIDI_TC_FPS, midiTCfps)) return 0;
- if (!setUint32(jRoot, CONF_KEY_MIDI_IN_REWIND, midiInRewind)) return 0;
- if (!setUint32(jRoot, CONF_KEY_MIDI_IN_START_STOP, midiInStartStop)) return 0;
- if (!setUint32(jRoot, CONF_KEY_MIDI_IN_ACTION_REC, midiInActionRec)) return 0;
- if (!setUint32(jRoot, CONF_KEY_MIDI_IN_INPUT_REC, midiInInputRec)) return 0;
- if (!setUint32(jRoot, CONF_KEY_MIDI_IN_METRONOME, midiInMetronome)) return 0;
- if (!setUint32(jRoot, CONF_KEY_MIDI_IN_VOLUME_IN, midiInVolumeIn)) return 0;
- if (!setUint32(jRoot, CONF_KEY_MIDI_IN_VOLUME_OUT, midiInVolumeOut)) return 0;
- if (!setUint32(jRoot, CONF_KEY_MIDI_IN_BEAT_DOUBLE, midiInBeatDouble)) return 0;
- if (!setUint32(jRoot, CONF_KEY_MIDI_IN_BEAT_HALF, midiInBeatHalf)) return 0;
- if (!setBool(jRoot, CONF_KEY_RECS_STOP_ON_CHAN_HALT, recsStopOnChanHalt)) return 0;
- if (!setBool(jRoot, CONF_KEY_CHANS_STOP_ON_SEQ_HALT, chansStopOnSeqHalt)) return 0;
- if (!setBool(jRoot, CONF_KEY_TREAT_RECS_AS_LOOPS, treatRecsAsLoops)) return 0;
- if (!setBool(jRoot, CONF_KEY_RESIZE_RECORDINGS, resizeRecordings)) return 0;
- if (!setString(jRoot, CONF_KEY_PLUGINS_PATH, pluginPath)) return 0;
- if (!setString(jRoot, CONF_KEY_PATCHES_PATH, patchPath)) return 0;
- if (!setString(jRoot, CONF_KEY_SAMPLES_PATH, samplePath)) return 0;
-
- if (!setInt(jRoot, CONF_KEY_MAIN_WINDOW_X, mainWindowX)) return 0;
- if (!setInt(jRoot, CONF_KEY_MAIN_WINDOW_Y, mainWindowY)) return 0;
- if (!setInt(jRoot, CONF_KEY_MAIN_WINDOW_W, mainWindowW)) return 0;
- if (!setInt(jRoot, CONF_KEY_MAIN_WINDOW_H, mainWindowH)) return 0;
- if (!setInt(jRoot, CONF_KEY_BROWSER_X, browserX)) return 0;
- if (!setInt(jRoot, CONF_KEY_BROWSER_Y, browserY)) return 0;
- if (!setInt(jRoot, CONF_KEY_BROWSER_W, browserW)) return 0;
- if (!setInt(jRoot, CONF_KEY_BROWSER_H, browserH)) return 0;
- if (!setInt(jRoot, CONF_KEY_BROWSER_POSITION, browserPosition)) return 0;
- if (!setString(jRoot, CONF_KEY_BROWSER_LAST_PATH, browserLastPath)) return 0;
- if (!setInt(jRoot, CONF_KEY_BROWSER_LAST_VALUE, browserLastValue)) return 0;
- if (!setInt(jRoot, CONF_KEY_ACTION_EDITOR_X, actionEditorX)) return 0;
- if (!setInt(jRoot, CONF_KEY_ACTION_EDITOR_Y, actionEditorY)) return 0;
- if (!setInt(jRoot, CONF_KEY_ACTION_EDITOR_W, actionEditorW)) return 0;
- if (!setInt(jRoot, CONF_KEY_ACTION_EDITOR_H, actionEditorH)) return 0;
- if (!setInt(jRoot, CONF_KEY_ACTION_EDITOR_ZOOM, actionEditorZoom)) return 0;
- if (!setInt(jRoot, CONF_KEY_ACTION_EDITOR_GRID_VAL, actionEditorGridVal)) return 0;
- if (!setInt(jRoot, CONF_KEY_ACTION_EDITOR_GRID_ON, actionEditorGridOn)) return 0;
- if (!setInt(jRoot, CONF_KEY_SAMPLE_EDITOR_X, sampleEditorX)) return 0;
- if (!setInt(jRoot, CONF_KEY_SAMPLE_EDITOR_Y, sampleEditorY)) return 0;
- if (!setInt(jRoot, CONF_KEY_SAMPLE_EDITOR_W, sampleEditorW)) return 0;
- if (!setInt(jRoot, CONF_KEY_SAMPLE_EDITOR_H, sampleEditorH)) return 0;
- if (!setInt(jRoot, CONF_KEY_SAMPLE_EDITOR_GRID_VAL, sampleEditorGridVal)) return 0;
- if (!setInt(jRoot, CONF_KEY_SAMPLE_EDITOR_GRID_ON, sampleEditorGridOn)) return 0;
- if (!setInt(jRoot, CONF_KEY_PIANO_ROLL_Y, pianoRollY)) return 0;
- if (!setInt(jRoot, CONF_KEY_PIANO_ROLL_H, pianoRollH)) return 0;
- if (!setInt(jRoot, CONF_KEY_PLUGIN_LIST_X, pluginListX)) return 0;
- if (!setInt(jRoot, CONF_KEY_PLUGIN_LIST_Y, pluginListY)) return 0;
- if (!setInt(jRoot, CONF_KEY_CONFIG_X, configX)) return 0;
- if (!setInt(jRoot, CONF_KEY_CONFIG_Y, configY)) return 0;
- if (!setInt(jRoot, CONF_KEY_BPM_X, bpmX)) return 0;
- if (!setInt(jRoot, CONF_KEY_BPM_Y, bpmY)) return 0;
- if (!setInt(jRoot, CONF_KEY_BEATS_X, beatsX)) return 0;
- if (!setInt(jRoot, CONF_KEY_BEATS_Y, beatsY)) return 0;
- if (!setInt(jRoot, CONF_KEY_ABOUT_X, aboutX)) return 0;
- if (!setInt(jRoot, CONF_KEY_ABOUT_Y, aboutY)) return 0;
- if (!setInt(jRoot, CONF_KEY_MIDI_INPUT_X, midiInputX)) return 0;
- if (!setInt(jRoot, CONF_KEY_MIDI_INPUT_Y, midiInputY)) return 0;
- if (!setInt(jRoot, CONF_KEY_MIDI_INPUT_W, midiInputW)) return 0;
- if (!setInt(jRoot, CONF_KEY_MIDI_INPUT_H, midiInputH)) return 0;
+ if (!storager::setString(jRoot, CONF_KEY_HEADER, header)) return 0;
+ if (!storager::setInt(jRoot, CONF_KEY_LOG_MODE, logMode)) return 0;
+ if (!storager::setInt(jRoot, CONF_KEY_SOUND_SYSTEM, soundSystem)) return 0;
+ if (!storager::setInt(jRoot, CONF_KEY_SOUND_DEVICE_OUT, soundDeviceOut)) return 0;
+ if (!storager::setInt(jRoot, CONF_KEY_SOUND_DEVICE_IN, soundDeviceIn)) return 0;
+ if (!storager::setInt(jRoot, CONF_KEY_CHANNELS_OUT, channelsOut)) return 0;
+ if (!storager::setInt(jRoot, CONF_KEY_CHANNELS_IN, channelsIn)) return 0;
+ if (!storager::setInt(jRoot, CONF_KEY_SAMPLERATE, samplerate)) return 0;
+ if (!storager::setInt(jRoot, CONF_KEY_BUFFER_SIZE, buffersize)) return 0;
+ if (!storager::setInt(jRoot, CONF_KEY_DELAY_COMPENSATION, delayComp)) return 0;
+ if (!storager::setBool(jRoot, CONF_KEY_LIMIT_OUTPUT, limitOutput)) return 0;
+ if (!storager::setInt(jRoot, CONF_KEY_RESAMPLE_QUALITY, rsmpQuality)) return 0;
+ if (!storager::setInt(jRoot, CONF_KEY_MIDI_SYSTEM, midiSystem)) return 0;
+ if (!storager::setInt(jRoot, CONF_KEY_MIDI_PORT_OUT, midiPortOut)) return 0;
+ if (!storager::setInt(jRoot, CONF_KEY_MIDI_PORT_IN, midiPortIn)) return 0;
+ if (!storager::setBool(jRoot, CONF_KEY_NO_NOTE_OFF, noNoteOff)) return 0;
+ if (!storager::setString(jRoot, CONF_KEY_MIDIMAP_PATH, midiMapPath)) return 0;
+ if (!storager::setString(jRoot, CONF_KEY_LAST_MIDIMAP, lastFileMap)) return 0;
+ if (!storager::setInt(jRoot, CONF_KEY_MIDI_SYNC, midiSync)) return 0;
+ if (!storager::setFloat(jRoot, CONF_KEY_MIDI_TC_FPS, midiTCfps)) return 0;
+ if (!storager::setUint32(jRoot, CONF_KEY_MIDI_IN_REWIND, midiInRewind)) return 0;
+ if (!storager::setUint32(jRoot, CONF_KEY_MIDI_IN_START_STOP, midiInStartStop)) return 0;
+ if (!storager::setUint32(jRoot, CONF_KEY_MIDI_IN_ACTION_REC, midiInActionRec)) return 0;
+ if (!storager::setUint32(jRoot, CONF_KEY_MIDI_IN_INPUT_REC, midiInInputRec)) return 0;
+ if (!storager::setUint32(jRoot, CONF_KEY_MIDI_IN_METRONOME, midiInMetronome)) return 0;
+ if (!storager::setUint32(jRoot, CONF_KEY_MIDI_IN_VOLUME_IN, midiInVolumeIn)) return 0;
+ if (!storager::setUint32(jRoot, CONF_KEY_MIDI_IN_VOLUME_OUT, midiInVolumeOut)) return 0;
+ if (!storager::setUint32(jRoot, CONF_KEY_MIDI_IN_BEAT_DOUBLE, midiInBeatDouble)) return 0;
+ if (!storager::setUint32(jRoot, CONF_KEY_MIDI_IN_BEAT_HALF, midiInBeatHalf)) return 0;
+ if (!storager::setBool(jRoot, CONF_KEY_RECS_STOP_ON_CHAN_HALT, recsStopOnChanHalt)) return 0;
+ if (!storager::setBool(jRoot, CONF_KEY_CHANS_STOP_ON_SEQ_HALT, chansStopOnSeqHalt)) return 0;
+ if (!storager::setBool(jRoot, CONF_KEY_TREAT_RECS_AS_LOOPS, treatRecsAsLoops)) return 0;
+ if (!storager::setBool(jRoot, CONF_KEY_RESIZE_RECORDINGS, resizeRecordings)) return 0;
+ if (!storager::setBool(jRoot, CONF_KEY_INPUT_MONITOR_DEFAULT_ON, inputMonitorDefaultOn)) return 0;
+ if (!storager::setString(jRoot, CONF_KEY_PLUGINS_PATH, pluginPath)) return 0;
+ if (!storager::setString(jRoot, CONF_KEY_PATCHES_PATH, patchPath)) return 0;
+ if (!storager::setString(jRoot, CONF_KEY_SAMPLES_PATH, samplePath)) return 0;
+
+ if (!storager::setInt(jRoot, CONF_KEY_MAIN_WINDOW_X, mainWindowX)) return 0;
+ if (!storager::setInt(jRoot, CONF_KEY_MAIN_WINDOW_Y, mainWindowY)) return 0;
+ if (!storager::setInt(jRoot, CONF_KEY_MAIN_WINDOW_W, mainWindowW)) return 0;
+ if (!storager::setInt(jRoot, CONF_KEY_MAIN_WINDOW_H, mainWindowH)) return 0;
+ if (!storager::setInt(jRoot, CONF_KEY_BROWSER_X, browserX)) return 0;
+ if (!storager::setInt(jRoot, CONF_KEY_BROWSER_Y, browserY)) return 0;
+ if (!storager::setInt(jRoot, CONF_KEY_BROWSER_W, browserW)) return 0;
+ if (!storager::setInt(jRoot, CONF_KEY_BROWSER_H, browserH)) return 0;
+ if (!storager::setInt(jRoot, CONF_KEY_BROWSER_POSITION, browserPosition)) return 0;
+ if (!storager::setString(jRoot, CONF_KEY_BROWSER_LAST_PATH, browserLastPath)) return 0;
+ if (!storager::setInt(jRoot, CONF_KEY_BROWSER_LAST_VALUE, browserLastValue)) return 0;
+ if (!storager::setInt(jRoot, CONF_KEY_ACTION_EDITOR_X, actionEditorX)) return 0;
+ if (!storager::setInt(jRoot, CONF_KEY_ACTION_EDITOR_Y, actionEditorY)) return 0;
+ if (!storager::setInt(jRoot, CONF_KEY_ACTION_EDITOR_W, actionEditorW)) return 0;
+ if (!storager::setInt(jRoot, CONF_KEY_ACTION_EDITOR_H, actionEditorH)) return 0;
+ if (!storager::setInt(jRoot, CONF_KEY_ACTION_EDITOR_ZOOM, actionEditorZoom)) return 0;
+ if (!storager::setInt(jRoot, CONF_KEY_ACTION_EDITOR_GRID_VAL, actionEditorGridVal)) return 0;
+ if (!storager::setInt(jRoot, CONF_KEY_ACTION_EDITOR_GRID_ON, actionEditorGridOn)) return 0;
+ if (!storager::setInt(jRoot, CONF_KEY_SAMPLE_EDITOR_X, sampleEditorX)) return 0;
+ if (!storager::setInt(jRoot, CONF_KEY_SAMPLE_EDITOR_Y, sampleEditorY)) return 0;
+ if (!storager::setInt(jRoot, CONF_KEY_SAMPLE_EDITOR_W, sampleEditorW)) return 0;
+ if (!storager::setInt(jRoot, CONF_KEY_SAMPLE_EDITOR_H, sampleEditorH)) return 0;
+ if (!storager::setInt(jRoot, CONF_KEY_SAMPLE_EDITOR_GRID_VAL, sampleEditorGridVal)) return 0;
+ if (!storager::setInt(jRoot, CONF_KEY_SAMPLE_EDITOR_GRID_ON, sampleEditorGridOn)) return 0;
+ if (!storager::setInt(jRoot, CONF_KEY_PIANO_ROLL_Y, pianoRollY)) return 0;
+ if (!storager::setInt(jRoot, CONF_KEY_PIANO_ROLL_H, pianoRollH)) return 0;
+ if (!storager::setInt(jRoot, CONF_KEY_PLUGIN_LIST_X, pluginListX)) return 0;
+ if (!storager::setInt(jRoot, CONF_KEY_PLUGIN_LIST_Y, pluginListY)) return 0;
+ if (!storager::setInt(jRoot, CONF_KEY_CONFIG_X, configX)) return 0;
+ if (!storager::setInt(jRoot, CONF_KEY_CONFIG_Y, configY)) return 0;
+ if (!storager::setInt(jRoot, CONF_KEY_BPM_X, bpmX)) return 0;
+ if (!storager::setInt(jRoot, CONF_KEY_BPM_Y, bpmY)) return 0;
+ if (!storager::setInt(jRoot, CONF_KEY_BEATS_X, beatsX)) return 0;
+ 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_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;
+ if (!storager::setInt(jRoot, CONF_KEY_MIDI_INPUT_H, midiInputH)) return 0;
#ifdef WITH_VST
- if (!setInt(jRoot, CONF_KEY_PLUGIN_CHOOSER_X, pluginChooserX)) return 0;
- if (!setInt(jRoot, CONF_KEY_PLUGIN_CHOOSER_Y, pluginChooserY)) return 0;
- if (!setInt(jRoot, CONF_KEY_PLUGIN_CHOOSER_W, pluginChooserW)) return 0;
- if (!setInt(jRoot, CONF_KEY_PLUGIN_CHOOSER_H, pluginChooserH)) return 0;
- if (!setInt(jRoot, CONF_KEY_PLUGIN_SORT_METHOD, pluginSortMethod)) return 0;
+ if (!storager::setInt(jRoot, CONF_KEY_PLUGIN_CHOOSER_X, pluginChooserX)) return 0;
+ if (!storager::setInt(jRoot, CONF_KEY_PLUGIN_CHOOSER_Y, pluginChooserY)) return 0;
+ if (!storager::setInt(jRoot, CONF_KEY_PLUGIN_CHOOSER_W, pluginChooserW)) return 0;
+ if (!storager::setInt(jRoot, CONF_KEY_PLUGIN_CHOOSER_H, pluginChooserH)) return 0;
+ if (!storager::setInt(jRoot, CONF_KEY_PLUGIN_SORT_METHOD, pluginSortMethod)) return 0;
#endif
/* -------------------------------------------------------------------------- */
-int Conf::write()
+int write()
{
if (!createConfigFolder())
return 0;
- jRoot = json_object();
+ json_t *jRoot = json_object();
json_object_set_new(jRoot, CONF_KEY_HEADER, json_string(header.c_str()));
json_object_set_new(jRoot, CONF_KEY_LOG_MODE, json_integer(logMode));
json_object_set_new(jRoot, CONF_KEY_CHANS_STOP_ON_SEQ_HALT, json_boolean(chansStopOnSeqHalt));
json_object_set_new(jRoot, CONF_KEY_TREAT_RECS_AS_LOOPS, json_boolean(treatRecsAsLoops));
json_object_set_new(jRoot, CONF_KEY_RESIZE_RECORDINGS, json_boolean(resizeRecordings));
+ json_object_set_new(jRoot, CONF_KEY_INPUT_MONITOR_DEFAULT_ON, json_boolean(inputMonitorDefaultOn));
json_object_set_new(jRoot, CONF_KEY_PLUGINS_PATH, json_string(pluginPath.c_str()));
json_object_set_new(jRoot, CONF_KEY_PATCHES_PATH, json_string(patchPath.c_str()));
json_object_set_new(jRoot, CONF_KEY_SAMPLES_PATH, json_string(samplePath.c_str()));
#endif
if (json_dump_file(jRoot, confFilePath.c_str(), JSON_INDENT(2)) != 0) {
- gu_log("[Conf::write] unable to write configuration file!\n");
+ gu_log("[conf::write] unable to write configuration file!\n");
return 0;
}
return 1;
}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void Conf::sanitize()
-{
- if (!(soundSystem & SYS_API_ANY)) soundSystem = DEFAULT_SOUNDSYS;
- if (soundDeviceOut < 0) soundDeviceOut = DEFAULT_SOUNDDEV_OUT;
- if (soundDeviceIn < -1) soundDeviceIn = DEFAULT_SOUNDDEV_IN;
- if (channelsOut < 0) channelsOut = 0;
- if (channelsIn < 0) channelsIn = 0;
- if (buffersize < 8 || buffersize > 4096) buffersize = DEFAULT_BUFSIZE;
- if (delayComp < 0) delayComp = DEFAULT_DELAYCOMP;
- if (midiPortOut < -1) midiPortOut = DEFAULT_MIDI_SYSTEM;
- if (midiPortOut < -1) midiPortOut = DEFAULT_MIDI_PORT_OUT;
- if (midiPortIn < -1) midiPortIn = DEFAULT_MIDI_PORT_IN;
- if (browserX < 0) browserX = 0;
- if (browserY < 0) browserY = 0;
- if (browserW < 396) browserW = 396;
- if (browserH < 302) browserH = 302;
- if (actionEditorX < 0) actionEditorX = 0;
- if (actionEditorY < 0) actionEditorY = 0;
- if (actionEditorW < 640) actionEditorW = 640;
- if (actionEditorH < 176) actionEditorH = 176;
- if (actionEditorZoom < 100) actionEditorZoom = 100;
- if (actionEditorGridVal < 0) actionEditorGridVal = 0;
- if (actionEditorGridOn < 0) actionEditorGridOn = 0;
- if (pianoRollH <= 0) pianoRollH = 422;
- if (sampleEditorX < 0) sampleEditorX = 0;
- if (sampleEditorY < 0) sampleEditorY = 0;
- if (sampleEditorW < 500) sampleEditorW = 500;
- if (sampleEditorH < 292) sampleEditorH = 292;
- if (sampleEditorGridVal < 0) sampleEditorGridVal = 0;
- if (sampleEditorGridOn < 0) sampleEditorGridOn = 0;
- if (midiInputX < 0) midiInputX = 0;
- if (midiInputY < 0) midiInputY = 0;
- if (midiInputW < G_DEFAULT_MIDI_INPUT_UI_W) midiInputW = G_DEFAULT_MIDI_INPUT_UI_W;
- if (midiInputH < G_DEFAULT_MIDI_INPUT_UI_H) midiInputH = G_DEFAULT_MIDI_INPUT_UI_H;
- if (configX < 0) configX = 0;
- if (configY < 0) configY = 0;
- if (pluginListX < 0) pluginListX = 0;
- if (pluginListY < 0) pluginListY = 0;
-#ifdef WITH_VST
- if (pluginChooserW < 640) pluginChooserW = 640;
- if (pluginChooserH < 480) pluginChooserW = 480;
-#endif
- if (bpmX < 0) bpmX = 0;
- if (bpmY < 0) bpmY = 0;
- if (beatsX < 0) beatsX = 0;
- if (beatsY < 0) beatsY = 0;
- if (aboutX < 0) aboutX = 0;
- if (aboutY < 0) aboutY = 0;
- if (samplerate < 8000) samplerate = DEFAULT_SAMPLERATE;
- if (rsmpQuality < 0 || rsmpQuality > 4) rsmpQuality = 0;
-}
+}}}; // giada::m::conf::
*
* Giada - Your Hardcore Loopmachine
*
- * conf
- *
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
* -------------------------------------------------------------------------- */
-#ifndef __CONF_H__
-#define __CONF_H__
+#ifndef G_CONF_H
+#define G_CONF_H
#include <string>
-#include <cstdio>
-#include "dataStorageJson.h"
-
-using std::string;
-
-class Conf : public DataStorageJson
+namespace giada {
+namespace m {
+namespace conf
{
-private:
-
- string confFilePath;
- string confDirPath;
-
- /* init
- * Init Conf with default values. */
-
- void init();
-
- /* sanitize
- * Avoid funky values from config file. */
-
- void sanitize();
-
- /* createConfigFolder
- * Create local folder where to put the configuration file. Path differs from
- * OS to OS. */
-
- int createConfigFolder();
-
-public:
-
- Conf();
-
- string header;
-
- int logMode;
- int soundSystem;
- int soundDeviceOut;
- int soundDeviceIn;
- int channelsOut;
- int channelsIn;
- int samplerate;
- int buffersize;
- int delayComp;
- bool limitOutput;
- int rsmpQuality;
-
- int midiSystem;
- int midiPortOut;
- int midiPortIn;
- bool noNoteOff;
- string midiMapPath;
- string lastFileMap;
- int midiSync; // see const.h
- float midiTCfps;
-
- uint32_t midiInRewind;
- uint32_t midiInStartStop;
- uint32_t midiInActionRec;
- uint32_t midiInInputRec;
- uint32_t midiInMetronome;
- uint32_t midiInVolumeIn;
- uint32_t midiInVolumeOut;
- uint32_t midiInBeatDouble;
- uint32_t midiInBeatHalf;
-
- bool recsStopOnChanHalt;
- bool chansStopOnSeqHalt;
- bool treatRecsAsLoops;
- bool resizeRecordings;
-
- string pluginPath;
- string patchPath;
- string samplePath;
-
- int mainWindowX, mainWindowY, mainWindowW, mainWindowH;
-
- int browserX, browserY, browserW, browserH, browserPosition, browserLastValue;
- string browserLastPath;
-
- int actionEditorX, actionEditorY, actionEditorW, actionEditorH, actionEditorZoom;
- int actionEditorGridVal;
- int actionEditorGridOn;
-
- int sampleEditorX, sampleEditorY, sampleEditorW, sampleEditorH;
- int sampleEditorGridVal;
- int sampleEditorGridOn;
-
- int midiInputX, midiInputY, midiInputW, midiInputH;
-
- int pianoRollY, pianoRollH;
- int pluginListX, pluginListY;
- int configX, configY;
- int bpmX, bpmY;
- int beatsX, beatsY;
- int aboutX, aboutY;
+void init();
+int read();
+int write();
+
+extern std::string header;
+
+extern int logMode;
+extern int soundSystem;
+extern int soundDeviceOut;
+extern int soundDeviceIn;
+extern int channelsOut;
+extern int channelsIn;
+extern int samplerate;
+extern int buffersize;
+extern int delayComp;
+extern bool limitOutput;
+extern int rsmpQuality;
+
+extern int midiSystem;
+extern int midiPortOut;
+extern int midiPortIn;
+extern bool noNoteOff;
+extern std::string midiMapPath;
+extern std::string lastFileMap;
+extern int midiSync; // see const.h
+extern float midiTCfps;
+
+extern uint32_t midiInRewind;
+extern uint32_t midiInStartStop;
+extern uint32_t midiInActionRec;
+extern uint32_t midiInInputRec;
+extern uint32_t midiInMetronome;
+extern uint32_t midiInVolumeIn;
+extern uint32_t midiInVolumeOut;
+extern uint32_t midiInBeatDouble;
+extern uint32_t midiInBeatHalf;
+
+extern bool recsStopOnChanHalt;
+extern bool chansStopOnSeqHalt;
+extern bool treatRecsAsLoops;
+extern bool resizeRecordings;
+extern bool inputMonitorDefaultOn;
+
+extern std::string pluginPath;
+extern std::string patchPath;
+extern std::string samplePath;
+
+extern int mainWindowX, mainWindowY, mainWindowW, mainWindowH;
+
+extern int browserX, browserY, browserW, browserH, browserPosition, browserLastValue;
+extern std::string browserLastPath;
+
+extern int actionEditorX, actionEditorY, actionEditorW, actionEditorH, actionEditorZoom;
+extern int actionEditorGridVal;
+extern int actionEditorGridOn;
+
+extern int sampleEditorX, sampleEditorY, sampleEditorW, sampleEditorH;
+extern int sampleEditorGridVal;
+extern int sampleEditorGridOn;
+
+extern int midiInputX, midiInputY, midiInputW, midiInputH;
+
+extern int pianoRollY, pianoRollH;
+extern int pluginListX, pluginListY;
+extern int configX, configY;
+extern int bpmX, bpmY;
+extern int beatsX, beatsY;
+extern int aboutX, aboutY;
#ifdef WITH_VST
- int pluginChooserX, pluginChooserY, pluginChooserW, pluginChooserH;
- int pluginSortMethod;
+extern int pluginChooserX, pluginChooserY, pluginChooserW, pluginChooserH;
+extern int pluginSortMethod;
#endif
-
- int read();
- int write();
-};
+}}}; // giada::m::conf::
#endif
*
* Giada - Your Hardcore Loopmachine
*
- * const.h
- *
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
* -------------------------------------------------------------------------- */
-#ifndef CONST_H
-#define CONST_H
+#ifndef G_CONST_H
+#define G_CONST_H
/* -- version --------------------------------------------------------------- */
-#define G_VERSION_STR "0.13.2"
#define G_APP_NAME "Giada"
+#define G_VERSION_STR "0.14.0"
#define G_VERSION_MAJOR 0
-#define G_VERSION_MINOR 13
-#define G_VERSION_PATCH 2
+#define G_VERSION_MINOR 14
+#define G_VERSION_PATCH 0
-#define CONF_FILENAME "giada.conf"
+#define CONF_FILENAME "giada.conf"
#ifndef BUILD_DATE
#define BUILD_DATE __DATE__
/* -- MIN/MAX values -------------------------------------------------------- */
-#define MAX_BEATS 32
-#define MAX_BARS 32
-#define MAX_PATCHNAME_LEN 32
-#define DB_MIN_SCALE 60.0f
-#define MIN_COLUMN_WIDTH 140
+#define G_MIN_BPM 20.0f
+#define G_MAX_BPM 999.0f
+#define G_MAX_BEATS 32
+#define G_MAX_BARS 32
+#define G_MAX_QUANTIZE 8
+#define G_MAX_PATCHNAME_LEN 32
+#define G_DB_MIN_SCALE 60.0f
+#define G_MIN_COLUMN_WIDTH 140
+#define G_MAX_BOOST_DB 20.0f
+#define G_MAX_PITCH 4.0f
/* -- kernel audio ---------------------------------------------------------- */
-#define SYS_API_NONE 0x00 // 0000 0000
-#define SYS_API_JACK 0x01 // 0000 0001
-#define SYS_API_ALSA 0x02 // 0000 0010
-#define SYS_API_DS 0x04 // 0000 0100
-#define SYS_API_ASIO 0x08 // 0000 1000
-#define SYS_API_CORE 0x10 // 0001 0000
-#define SYS_API_PULSE 0x20 // 0010 0000
-#define SYS_API_WASAPI 0x40 // 0100 0000
-#define SYS_API_ANY 0x7F // 0111 1111
-
-#define KERNEL_OK 0
-#define KERNEL_UNDERRUN -1
-#define KERNEL_CRITICAL -2
+#define G_SYS_API_NONE 0x00 // 0000 0000
+#define G_SYS_API_JACK 0x01 // 0000 0001
+#define G_SYS_API_ALSA 0x02 // 0000 0010
+#define G_SYS_API_DS 0x04 // 0000 0100
+#define G_SYS_API_ASIO 0x08 // 0000 1000
+#define G_SYS_API_CORE 0x10 // 0001 0000
+#define G_SYS_API_PULSE 0x20 // 0010 0000
+#define G_SYS_API_WASAPI 0x40 // 0100 0000
+#define G_SYS_API_ANY 0x7F // 0111 1111
/* -- kernel midi ----------------------------------------------------------- */
-#define MIDI_API_JACK 0x01 // 0000 0001
-#define MIDI_API_ALSA 0x02 // 0000 0010
+#define G_MIDI_API_JACK 0x01 // 0000 0001
+#define G_MIDI_API_ALSA 0x02 // 0000 0010
/* -- default system -------------------------------------------------------- */
#if defined(__linux__)
- #define DEFAULT_SOUNDSYS SYS_API_NONE
+ #define G_DEFAULT_SOUNDSYS G_SYS_API_NONE
#elif defined(_WIN32)
- #define DEFAULT_SOUNDSYS SYS_API_DS
+ #define G_DEFAULT_SOUNDSYS G_SYS_API_DS
#elif defined(__APPLE__)
- #define DEFAULT_SOUNDSYS SYS_API_CORE
+ #define G_DEFAULT_SOUNDSYS G_SYS_API_CORE
#endif
-#define DEFAULT_SOUNDDEV_OUT 0 /// FIXME - please override with rtAudio::getDefaultDevice (or similar)
-#define DEFAULT_SOUNDDEV_IN -1 // no recording by default: input disabled
-#define DEFAULT_MIDI_SYSTEM 0
-#define DEFAULT_MIDI_PORT_IN -1
-#define DEFAULT_MIDI_PORT_OUT -1
-#define DEFAULT_SAMPLERATE 44100
-#define DEFAULT_BUFSIZE 1024
-#define DEFAULT_DELAYCOMP 0
-#define DEFAULT_VOL 1.0f
-#define G_DEFAULT_PITCH 1.0f
-#define DEFAULT_BOOST 0.0f
-#define DEFAULT_OUT_VOL 1.0f
-#define DEFAULT_IN_VOL 1.0f
-#define DEFAULT_CHANMODE SINGLE_BASIC
-#define DEFAULT_BPM 120.0f
-#define DEFAULT_BEATS 4
-#define DEFAULT_BARS 1
-#define DEFAULT_QUANTIZE 0 // quantizer off
-#define DEFAULT_FADEOUT_STEP 0.01f // micro-fadeout speed
-#define DEFAULT_COLUMN_WIDTH 380
-#define G_DEFAULT_PATCH_NAME "(default patch)"
+#define G_DEFAULT_SOUNDDEV_OUT 0 // FIXME - please override with rtAudio::getDefaultDevice (or similar)
+#define G_DEFAULT_SOUNDDEV_IN -1 // no recording by default: input disabled
+#define G_DEFAULT_MIDI_SYSTEM 0
+#define G_DEFAULT_MIDI_PORT_IN -1
+#define G_DEFAULT_MIDI_PORT_OUT -1
+#define G_DEFAULT_SAMPLERATE 44100
+#define G_DEFAULT_BUFSIZE 1024
+#define G_DEFAULT_DELAYCOMP 0
+#define G_DEFAULT_VOL 1.0f
+#define G_DEFAULT_PITCH 1.0f
+#define G_DEFAULT_BOOST 1.0f
+#define G_DEFAULT_OUT_VOL 1.0f
+#define G_DEFAULT_IN_VOL 1.0f
+#define G_DEFAULT_CHANMODE SINGLE_BASIC
+#define G_DEFAULT_BPM 120.0f
+#define G_DEFAULT_BEATS 4
+#define G_DEFAULT_BARS 1
+#define G_DEFAULT_QUANTIZE 0 // quantizer off
+#define G_DEFAULT_FADEOUT_STEP 0.01f // micro-fadeout speed
+#define G_DEFAULT_COLUMN_WIDTH 380
+#define G_DEFAULT_PATCH_NAME "(default patch)"
#define G_DEFAULT_MIDI_INPUT_UI_W 300
#define G_DEFAULT_MIDI_INPUT_UI_H 350
/* -- actions --------------------------------------------------------------- */
-#define ACTION_KEYPRESS 0x01 // 0000 0001
-#define ACTION_KEYREL 0x02 // 0000 0010
-#define ACTION_KILLCHAN 0x04 // 0000 0100
-#define ACTION_MUTEON 0x08 // 0000 1000
-#define ACTION_MUTEOFF 0x10 // 0001 0000
-#define ACTION_VOLUME 0x20 // 0010 0000
-#define ACTION_MIDI 0x40 // 0100 0000
+#define G_ACTION_KEYPRESS 0x01 // 0000 0001
+#define G_ACTION_KEYREL 0x02 // 0000 0010
+#define G_ACTION_KILL 0x04 // 0000 0100
+#define G_ACTION_MUTEON 0x08 // 0000 1000
+#define G_ACTION_MUTEOFF 0x10 // 0001 0000
+#define G_ACTION_VOLUME 0x20 // 0010 0000
+#define G_ACTION_MIDI 0x40 // 0100 0000
-#define ACTION_KEYS 0x03 // 0000 0011 any key
-#define ACTION_MUTES 0x24 // 0001 1000 any mute
+#define G_ACTION_KEYS 0x03 // 0000 0011 any key
+#define G_ACTION_MUTES 0x24 // 0001 1000 any mute
-#define RANGE_CHAR 0x01 // range for MIDI (0-127)
-#define RANGE_FLOAT 0x02 // range for volumes and VST params (0.0-1.0)
+#define G_RANGE_CHAR 0x01 // range for MIDI (0-127)
+#define G_RANGE_FLOAT 0x02 // range for volumes and VST params (0.0-1.0)
/* -- MIDI signals -------------------------------------------------------------
- * all signals are set to channel 0 (where channels are considered).
- * It's up to the caller to bitmask them with the proper channel number. */
-
-/* channel voices messages - controller (0xB0) is a special subset of
- * this family: it drives knobs, volume, faders and such. */
+All signals are set to channel 0 (where channels are considered). It's up to the
+caller to bitmask them with the proper channel number.
+Channel voices messages - controller (0xB0) is a special subset of this family:
+it drives knobs, volume, faders and such. */
#define MIDI_CONTROLLER 0xB0 << 24
#define MIDI_NOTE_ON 0x90 << 24
/* midi sync constants */
#define MIDI_SYNC_NONE 0x00
-#define MIDI_SYNC_CLOCK_M 0x01
-#define MIDI_SYNC_CLOCK_S 0x02
-#define MIDI_SYNC_MTC_M 0x04
-#define MIDI_SYNC_MTC_S 0x08
+#define MIDI_SYNC_CLOCK_M 0x01 // master
+#define MIDI_SYNC_CLOCK_S 0x02 // slave
+#define MIDI_SYNC_MTC_M 0x04 // master
+#define MIDI_SYNC_MTC_S 0x08 // slave
/* JSON patch keys */
#define PATCH_KEY_CHANNEL_MUTE_S "mute_s"
#define PATCH_KEY_CHANNEL_SOLO "solo"
#define PATCH_KEY_CHANNEL_VOLUME "volume"
-#define PATCH_KEY_CHANNEL_PAN_LEFT "pan_left"
-#define PATCH_KEY_CHANNEL_PAN_RIGHT "pan_right"
+#define PATCH_KEY_CHANNEL_PAN "pan"
#define PATCH_KEY_CHANNEL_MIDI_IN "midi_in"
#define PATCH_KEY_CHANNEL_MIDI_IN_KEYPRESS "midi_in_keypress"
#define PATCH_KEY_CHANNEL_MIDI_IN_KEYREL "midi_in_keyrel"
#define PATCH_KEY_CHANNEL_BOOST "boost"
#define PATCH_KEY_CHANNEL_REC_ACTIVE "rec_active"
#define PATCH_KEY_CHANNEL_PITCH "pitch"
+#define PATCH_KEY_CHANNEL_INPUT_MONITOR "input_monitor"
#define PATCH_KEY_CHANNEL_MIDI_IN_READ_ACTIONS "midi_in_read_actions"
#define PATCH_KEY_CHANNEL_MIDI_IN_PITCH "midi_in_pitch"
#define PATCH_KEY_CHANNEL_MIDI_OUT "midi_out"
#define CONF_KEY_CHANS_STOP_ON_SEQ_HALT "chans_stop_on_seq_halt"
#define CONF_KEY_TREAT_RECS_AS_LOOPS "treat_recs_as_loops"
#define CONF_KEY_RESIZE_RECORDINGS "resize_recordings"
+#define CONF_KEY_INPUT_MONITOR_DEFAULT_ON "input_monitor_default_on"
#define CONF_KEY_PLUGINS_PATH "plugins_path"
#define CONF_KEY_PATCHES_PATH "patches_path"
#define CONF_KEY_SAMPLES_PATH "samples_path"
+++ /dev/null
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * dataStorageIni
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
- *
- * This file is part of Giada - Your Hardcore Loopmachine.
- *
- * Giada - Your Hardcore Loopmachine is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General
- * Public License as published by the Free Software Foundation, either
- * version 3 of the License, or (at your option) any later version.
- *
- * Giada - Your Hardcore Loopmachine is distributed in the hope that it
- * will be useful, but WITHOUT ANY WARRANTY; without even the implied
- * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Giada - Your Hardcore Loopmachine. If not, see
- * <http://www.gnu.org/licenses/>.
- *
- * -------------------------------------------------------------------------- */
-
-
-#include <stdlib.h>
-#include <limits.h>
-#include "../utils/log.h"
-#include "dataStorageIni.h"
-#include "const.h"
-
-
-std::string DataStorageIni::getValue(const char *in)
-{
- /* on each call reset the pointe to the beginning of the file. Not so
- * good but necessary if you want to pick up random values from the
- * file. */
-
- fseek(fp, 0L, SEEK_SET);
- std::string out = "";
-
- while (!feof(fp)) {
-
- char buffer[MAX_LINE_LEN];
- if (fgets(buffer, MAX_LINE_LEN, fp) == NULL) {
- gu_log("[DataStorageIni::getValue] key '%s' not found\n", in);
- return "";
- }
-
- if (buffer[0] == '#')
- continue;
-
- unsigned len = strlen(in);
- if (strncmp(buffer, in, len) == 0) {
-
- for (unsigned i=len+1; i<MAX_LINE_LEN; i++) {
- if (buffer[i] == '\0' || buffer[i] == '\n' || buffer[i] == '\r')
- break;
- out += buffer[i];
- }
-
- break; // string found
- }
- }
- return out;
-}
+++ /dev/null
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * dataStorageIni
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
- *
- * This file is part of Giada - Your Hardcore Loopmachine.
- *
- * Giada - Your Hardcore Loopmachine is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General
- * Public License as published by the Free Software Foundation, either
- * version 3 of the License, or (at your option) any later version.
- *
- * Giada - Your Hardcore Loopmachine is distributed in the hope that it
- * will be useful, but WITHOUT ANY WARRANTY; without even the implied
- * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Giada - Your Hardcore Loopmachine. If not, see
- * <http://www.gnu.org/licenses/>.
- *
- * -------------------------------------------------------------------------- */
-
-
-#ifndef __DATA_STORAGE_INI_H__
-#define __DATA_STORAGE_INI_H__
-
-#include <stdio.h>
-#include <string>
-#include <string.h>
-
-#define MAX_LINE_LEN 1024
-
-
-class DataStorageIni
-{
-protected:
-
- FILE *fp;
- std::string getValue(const char *in);
-};
-
-#endif
+++ /dev/null
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * dataStorageIni
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
- *
- * This file is part of Giada - Your Hardcore Loopmachine.
- *
- * Giada - Your Hardcore Loopmachine is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General
- * Public License as published by the Free Software Foundation, either
- * version 3 of the License, or (at your option) any later version.
- *
- * Giada - Your Hardcore Loopmachine is distributed in the hope that it
- * will be useful, but WITHOUT ANY WARRANTY; without even the implied
- * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Giada - Your Hardcore Loopmachine. If not, see
- * <http://www.gnu.org/licenses/>.
- *
- * -------------------------------------------------------------------------- */
-
-
-#include <string>
-#include "../utils/log.h"
-#include "dataStorageJson.h"
-
-
-using std::string;
-
-
-bool DataStorageJson::setString(json_t *jRoot, const char *key, string &output)
-{
- json_t *jObject = json_object_get(jRoot, key);
- if (!json_is_string(jObject)) {
- gu_log("[dataStorageJson::setString] key '%s' is not a string!\n", key);
- json_decref(jRoot);
- return false;
- }
- output = json_string_value(jObject);
- return true;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-bool DataStorageJson::setFloat(json_t *jRoot, const char *key, float &output)
-{
- json_t *jObject = json_object_get(jRoot, key);
- if (!jObject) {
- gu_log("[dataStorageJson::setFloat] key '%s' not found, using default value\n", key);
- output = 0.0f;
- return true;
- }
- if (!json_is_real(jObject)) {
- gu_log("[dataStorageJson::setFloat] key '%s' is not a float!\n", key);
- json_decref(jRoot);
- return false;
- }
- output = json_real_value(jObject);
- return true;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-bool DataStorageJson::setUint32(json_t *jRoot, const char *key, uint32_t &output)
-{
- json_t *jObject = json_object_get(jRoot, key);
- if (!jObject) {
- gu_log("[dataStorageJson::setUint32] key '%s' not found, using default value\n", key);
- output = 0;
- return true;
- }
- if (!json_is_integer(jObject)) {
- gu_log("[dataStorageJson::setUint32] key '%s' is not an integer!\n", key);
- json_decref(jRoot);
- return false;
- }
- output = json_integer_value(jObject);
- return true;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-bool DataStorageJson::setBool(json_t *jRoot, const char *key, bool &output)
-{
- json_t *jObject = json_object_get(jRoot, key);
- if (!jObject) {
- gu_log("[dataStorageJson::setBool] key '%s' not found, using default value\n", key);
- output = false;
- return true;
- }
- if (!json_is_boolean(jObject)) {
- gu_log("[dataStorageJson::setBool] key '%s' is not a boolean!\n", key);
- json_decref(jRoot);
- return false;
- }
- output = json_boolean_value(jObject);
- return true;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-bool DataStorageJson::setInt(json_t *jRoot, const char *key, int &output)
-{
- return setUint32(jRoot, key, (uint32_t&) output);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-bool DataStorageJson::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;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-bool DataStorageJson::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;
-}
+++ /dev/null
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * dataStorageIni
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
- *
- * This file is part of Giada - Your Hardcore Loopmachine.
- *
- * Giada - Your Hardcore Loopmachine is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General
- * Public License as published by the Free Software Foundation, either
- * version 3 of the License, or (at your option) any later version.
- *
- * Giada - Your Hardcore Loopmachine is distributed in the hope that it
- * will be useful, but WITHOUT ANY WARRANTY; without even the implied
- * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Giada - Your Hardcore Loopmachine. If not, see
- * <http://www.gnu.org/licenses/>.
- *
- * -------------------------------------------------------------------------- */
-
-
-#ifndef __DATA_STORAGE_JSON_H__
-#define __DATA_STORAGE_JSON_H__
-
-
-#include <stdint.h>
-#include <jansson.h>
-
-
-using std::string;
-
-
-class DataStorageJson
-{
-protected:
-
- json_t *jRoot;
- json_error_t jError;
-
- bool setString(json_t *jRoot, const char *key, string &output);
- bool setFloat(json_t *jRoot, const char *key, float &output);
- bool setUint32(json_t *jRoot, const char *key, uint32_t &output);
- bool setInt(json_t *jRoot, const char *key, int &output);
- bool setBool(json_t *jRoot, const char *key, bool &output);
-
- /* checkObject
- check whether the jRoot object is a valid json object {} */
-
- bool checkObject(json_t *jRoot, const char *key);
-
- /* checkArray
- check whether the jRoot object is a valid json array [] */
-
- bool checkArray(json_t *jRoot, const char *key);
-};
-
-#endif
*
* ---------------------------------------------------------------------
*
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* ---------------------------------------------------------------------
*
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* ------------------------------------------------------------------ */
-#ifndef GRAPHICS_H
-#define GRAPHICS_H
+#ifndef G_GRAPHICS_H
+#define G_GRAPHICS_H
extern const char *giada_logo_xpm[];
*
* Giada - Your Hardcore Loopmachine
*
- * init
- *
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
#include <ctime>
+#ifdef __APPLE__
+ #include <pwd.h>
+#endif
#include "../utils/log.h"
#include "../utils/fs.h"
#include "../utils/gui.h"
#include "../gui/dialogs/gd_mainWindow.h"
#include "../gui/dialogs/gd_warnings.h"
+#include "../glue/main.h"
#include "init.h"
#include "mixer.h"
#include "wave.h"
#include "const.h"
+#include "clock.h"
#include "channel.h"
#include "mixerHandler.h"
-#include "patch_DEPR_.h"
#include "patch.h"
#include "conf.h"
#include "pluginHost.h"
#include "recorder.h"
#include "midiMapConf.h"
#include "kernelMidi.h"
+#include "kernelAudio.h"
-extern KernelAudio G_KernelAudio;
-extern Mixer G_Mixer;
-extern Recorder G_Recorder;
-extern KernelMidi G_KernelMidi;
-extern bool G_audio_status;
extern bool G_quit;
-extern Patch_DEPR_ G_Patch_DEPR_;
-extern Patch G_Patch;
-extern Conf G_Conf;
-extern MidiMapConf G_MidiMap;
extern gdMainWindow *G_MainWin;
-#ifdef WITH_VST
-extern PluginHost G_PluginHost;
-#endif
+
+using namespace giada::m;
void init_prepareParser()
time (&t);
gu_log("[init] Giada " G_VERSION_STR " - %s", ctime(&t));
- G_Conf.read();
- G_Patch_DEPR_.setDefault();
- G_Patch.init();
+ conf::init();
+ conf::read();
+ patch::init();
- if (!gu_logInit(G_Conf.logMode))
+ if (!gu_logInit(conf::logMode))
gu_log("[init] log init failed! Using default stdout\n");
gu_log("[init] configuration file ready\n");
void init_prepareKernelAudio()
{
- G_KernelAudio.openDevice(G_Conf.soundSystem, G_Conf.soundDeviceOut,
- G_Conf.soundDeviceIn, G_Conf.channelsOut, G_Conf.channelsIn,
- G_Conf.samplerate, G_Conf.buffersize);
- G_Mixer.init();
- G_Recorder.init();
+ kernelAudio::openDevice();
+ clock::init(conf::samplerate, conf::midiTCfps);
+ mixer::init(clock::getTotalFrames(), kernelAudio::getRealBufSize());
+ recorder::init();
#ifdef WITH_VST
/* If with Jack don't use buffer size stored in Conf. Use real buffersize
- from the soundcard (G_KernelAudio.realBufsize). */
+ from the soundcard (kernelAudio::realBufsize). */
- if (G_Conf.soundSystem == SYS_API_JACK)
- G_PluginHost.init(G_KernelAudio.realBufsize, G_Conf.samplerate);
+ if (conf::soundSystem == G_SYS_API_JACK)
+ pluginHost::init(kernelAudio::getRealBufSize(), conf::samplerate);
else
- G_PluginHost.init(G_Conf.buffersize, G_Conf.samplerate);
+ pluginHost::init(conf::buffersize, conf::samplerate);
- G_PluginHost.sortPlugins(G_Conf.pluginSortMethod);
+ pluginHost::sortPlugins(conf::pluginSortMethod);
#endif
+
}
void init_prepareKernelMIDI()
{
- G_KernelMidi.setApi(G_Conf.midiSystem);
- G_KernelMidi.openOutDevice(G_Conf.midiPortOut);
- G_KernelMidi.openInDevice(G_Conf.midiPortIn);
+ kernelMidi::setApi(conf::midiSystem);
+ kernelMidi::openOutDevice(conf::midiPortOut);
+ kernelMidi::openInDevice(conf::midiPortIn);
}
void init_prepareMidiMap()
{
- G_MidiMap.init();
- G_MidiMap.setDefault_DEPR_();
- G_MidiMap.setDefault();
-
- /* read with deprecated method first. If it fails, try with the new one. */
- // TODO - do the opposite: if json fails, go with deprecated one
-
- if (G_MidiMap.read(G_Conf.midiMapPath) != MIDIMAP_READ_OK) {
- gu_log("[init_prepareMidiMap] JSON-based midimap read failed, trying with the deprecated one...\n");
- if (G_MidiMap.readMap_DEPR_(G_Conf.midiMapPath) == MIDIMAP_INVALID)
- gu_log("[init_prepareMidiMap] unable to read deprecated midimap. Nothing to do\n");
- }
+ midimap::init();
+ midimap::setDefault();
+
+ if (midimap::read(conf::midiMapPath) != MIDIMAP_READ_OK)
+ gu_log("[init_prepareMidiMap] MIDI map read failed!\n");
}
void init_startGUI(int argc, char **argv)
{
G_MainWin = new gdMainWindow(GUI_WIDTH, GUI_HEIGHT, "", argc, argv);
- G_MainWin->resize(G_Conf.mainWindowX, G_Conf.mainWindowY, G_Conf.mainWindowW,
- G_Conf.mainWindowH);
+ G_MainWin->resize(conf::mainWindowX, conf::mainWindowY, conf::mainWindowW,
+ conf::mainWindowH);
- gu_updateMainWinLabel(G_Patch.name == "" ? G_DEFAULT_PATCH_NAME : G_Patch.name);
+ gu_updateMainWinLabel(patch::name == "" ? G_DEFAULT_PATCH_NAME : patch::name);
- /* never update the GUI elements if G_audio_status is bad, segfaults
+ /* never update the GUI elements if kernelAudio::getStatus() is bad, segfaults
* are around the corner */
- if (G_audio_status)
+ if (kernelAudio::getStatus())
gu_updateControls();
else
gdAlert("Your soundcard isn't configured correctly.\n"
void init_startKernelAudio()
{
- if (G_audio_status)
- G_KernelAudio.startStream();
+ if (kernelAudio::getStatus())
+ kernelAudio::startStream();
}
/* store position and size of the main window for the next startup */
- G_Conf.mainWindowX = G_MainWin->x();
- G_Conf.mainWindowY = G_MainWin->y();
- G_Conf.mainWindowW = G_MainWin->w();
- G_Conf.mainWindowH = G_MainWin->h();
+ conf::mainWindowX = G_MainWin->x();
+ conf::mainWindowY = G_MainWin->y();
+ conf::mainWindowW = G_MainWin->w();
+ conf::mainWindowH = G_MainWin->h();
- /* close any open subwindow, especially before cleaning PluginHost_DEPR_ to
+ /* close any open subwindow, especially before cleaning PluginHost to
* avoid mess */
gu_closeAllSubwindows();
/* write configuration file */
- if (!G_Conf.write())
+ if (!conf::write())
gu_log("[init] error while saving configuration file!\n");
else
gu_log("[init] configuration saved\n");
- /* if G_audio_status we close the kernelAudio FIRST, THEN the mixer.
+ /* if kernelAudio::getStatus() we close the kernelAudio FIRST, THEN the mixer.
* The opposite could cause random segfaults (even now with RtAudio?). */
- if (G_audio_status) {
- G_KernelAudio.closeDevice();
- G_Mixer.close();
+ if (kernelAudio::getStatus()) {
+ kernelAudio::closeDevice();
+ mixer::close();
gu_log("[init] Mixer closed\n");
}
- G_Recorder.clearAll();
- for (unsigned i=0; i<G_Mixer.channels.size(); i++) {
- G_Mixer.channels.at(i)->hasActions = false;
- G_Mixer.channels.at(i)->readActions = false;
- //if (G_Mixer.channels.at(i)->type == CHANNEL_SAMPLE)
- // ((SampleChannel*)G_Mixer.channels.at(i))->readActions = false;
+ recorder::clearAll();
+ for (unsigned i=0; i<mixer::channels.size(); i++) {
+ mixer::channels.at(i)->hasActions = false;
+ mixer::channels.at(i)->readActions = false;
+ //if (mixer::channels.at(i)->type == CHANNEL_SAMPLE)
+ // ((SampleChannel*)mixer::channels.at(i))->readActions = false;
}
gu_log("[init] Recorder cleaned up\n");
#ifdef WITH_VST
- G_PluginHost.freeAllStacks(&G_Mixer.channels, &G_Mixer.mutex_plugins);
+
+ pluginHost::freeAllStacks(&mixer::channels, &mixer::mutex_plugins);
+ pluginHost::close();
gu_log("[init] PluginHost cleaned up\n");
+
#endif
gu_log("[init] Giada " G_VERSION_STR " closed\n\n");
*
* Giada - Your Hardcore Loopmachine
*
- * init
- *
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
* -------------------------------------------------------------------------- */
-#ifndef INIT_H
-#define INIT_H
-
-
-#include <cstdio>
-#include <stdint.h>
-#ifdef __APPLE__
- #include <pwd.h>
-#endif
+#ifndef G_INIT_H
+#define G_INIT_H
void init_prepareParser();
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
* -------------------------------------------------------------------------- */
+#include "../deps/rtaudio-mod/RtAudio.h"
#include "../utils/log.h"
#include "../glue/main.h"
#include "conf.h"
#include "kernelAudio.h"
-extern KernelAudio G_KernelAudio;
-extern Mixer G_Mixer;
-extern Conf G_Conf;
-extern bool G_audio_status;
-
-
using std::string;
using std::vector;
-KernelAudio::KernelAudio()
+namespace giada {
+namespace m {
+namespace kernelAudio
+{
+namespace
+{
+RtAudio *rtSystem;
+bool status;
+unsigned numDevs;
+bool inputEnabled;
+unsigned realBufsize; // reale bufsize from the soundcard
+int api;
+
+#ifdef __linux__
+
+JackState jackState;
+
+jack_client_t *jackGetHandle()
+{
+ return static_cast<jack_client_t*>(rtSystem->rtapi_->__HACK__getJackClient());
+}
+
+#endif
+}; // {anonymous}
+
+
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+
+
+void init()
{
- system = nullptr;
- numDevs = 0;
- inputEnabled = 0;
- realBufsize = 0;
- api = 0;
+ rtSystem = nullptr;
+ numDevs = 0;
+ inputEnabled = 0;
+ realBufsize = 0;
+ api = 0;
}
/* -------------------------------------------------------------------------- */
+bool getStatus()
+{
+ return status;
+}
-int KernelAudio::openDevice(int _api, int outDev, int inDev, int outChan,
- int inChan, int samplerate, int buffersize)
+
+/* -------------------------------------------------------------------------- */
+
+
+int openDevice()
{
- api = _api;
- gu_log("[KA] using system 0x%x\n", api);
+ api = conf::soundSystem;
+ gu_log("[KA] using rtSystem 0x%x\n", api);
#if defined(__linux__)
- if (api == SYS_API_JACK && hasAPI(RtAudio::UNIX_JACK))
- system = new RtAudio(RtAudio::UNIX_JACK);
+ if (api == G_SYS_API_JACK && hasAPI(RtAudio::UNIX_JACK))
+ rtSystem = new RtAudio(RtAudio::UNIX_JACK);
else
- if (api == SYS_API_ALSA && hasAPI(RtAudio::LINUX_ALSA))
- system = new RtAudio(RtAudio::LINUX_ALSA);
+ if (api == G_SYS_API_ALSA && hasAPI(RtAudio::LINUX_ALSA))
+ rtSystem = new RtAudio(RtAudio::LINUX_ALSA);
else
- if (api == SYS_API_PULSE && hasAPI(RtAudio::LINUX_PULSE))
- system = new RtAudio(RtAudio::LINUX_PULSE);
+ if (api == G_SYS_API_PULSE && hasAPI(RtAudio::LINUX_PULSE))
+ rtSystem = new RtAudio(RtAudio::LINUX_PULSE);
#elif defined(_WIN32)
- if (api == SYS_API_DS && hasAPI(RtAudio::WINDOWS_DS))
- system = new RtAudio(RtAudio::WINDOWS_DS);
+ if (api == G_SYS_API_DS && hasAPI(RtAudio::WINDOWS_DS))
+ rtSystem = new RtAudio(RtAudio::WINDOWS_DS);
else
- if (api == SYS_API_ASIO && hasAPI(RtAudio::WINDOWS_ASIO))
- system = new RtAudio(RtAudio::WINDOWS_ASIO);
+ if (api == G_SYS_API_ASIO && hasAPI(RtAudio::WINDOWS_ASIO))
+ rtSystem = new RtAudio(RtAudio::WINDOWS_ASIO);
else
- if (api == SYS_API_WASAPI && hasAPI(RtAudio::WINDOWS_WASAPI))
- system = new RtAudio(RtAudio::WINDOWS_WASAPI);
+ if (api == G_SYS_API_WASAPI && hasAPI(RtAudio::WINDOWS_WASAPI))
+ rtSystem = new RtAudio(RtAudio::WINDOWS_WASAPI);
#elif defined(__APPLE__)
- if (api == SYS_API_CORE && hasAPI(RtAudio::MACOSX_CORE))
- system = new RtAudio(RtAudio::MACOSX_CORE);
+ if (api == G_SYS_API_CORE && hasAPI(RtAudio::MACOSX_CORE))
+ rtSystem = new RtAudio(RtAudio::MACOSX_CORE);
#endif
- else {
- G_audio_status = false;
+ else
return 0;
- }
- gu_log("[KA] Opening devices %d (out), %d (in), f=%d...\n", outDev, inDev, samplerate);
+ gu_log("[KA] Opening devices %d (out), %d (in), f=%d...\n",
+ conf::soundDeviceOut, conf::soundDeviceIn, conf::samplerate);
- numDevs = system->getDeviceCount();
+ numDevs = rtSystem->getDeviceCount();
if (numDevs < 1) {
gu_log("[KA] no devices found with this API\n");
closeDevice();
- G_audio_status = false;
return 0;
}
else {
RtAudio::StreamParameters outParams;
RtAudio::StreamParameters inParams;
- if (outDev == DEFAULT_SOUNDDEV_OUT)
+ if (conf::soundDeviceOut == G_DEFAULT_SOUNDDEV_OUT)
outParams.deviceId = getDefaultOut();
else
- outParams.deviceId = outDev;
+ outParams.deviceId = conf::soundDeviceOut;
outParams.nChannels = 2;
- outParams.firstChannel = outChan*2; // chan 0=0, 1=2, 2=4, ...
+ outParams.firstChannel = conf::channelsOut * 2; // chan 0=0, 1=2, 2=4, ...
/* inDevice can be disabled */
- if (inDev != -1) {
- inParams.deviceId = inDev;
+ if (conf::soundDeviceIn != -1) {
+ inParams.deviceId = conf::soundDeviceIn;
inParams.nChannels = 2;
- inParams.firstChannel = inChan*2; // chan 0=0, 1=2, 2=4, ...
+ inParams.firstChannel = conf::channelsIn * 2; // chan 0=0, 1=2, 2=4, ...
inputEnabled = true;
}
else
inputEnabled = false;
RtAudio::StreamOptions options;
- options.streamName = "Giada";
+ options.streamName = G_APP_NAME;
options.numberOfBuffers = 4;
- realBufsize = buffersize;
+ realBufsize = conf::buffersize;
#if defined(__linux__) || defined(__APPLE__)
- if (api == SYS_API_JACK) {
- samplerate = getFreq(outDev, 0);
- gu_log("[KA] JACK in use, freq = %d\n", samplerate);
- G_Conf.samplerate = samplerate;
+
+ if (api == G_SYS_API_JACK) {
+ conf::samplerate = getFreq(conf::soundDeviceOut, 0);
+ gu_log("[KA] JACK in use, freq = %d\n", conf::samplerate);
}
+
#endif
try {
- system->openStream(
+ rtSystem->openStream(
&outParams, // output params
- inDev != -1 ? &inParams : nullptr, // input params if inDevice is selected
+ conf::soundDeviceIn != -1 ? &inParams : nullptr, // input params if inDevice is selected
RTAUDIO_FLOAT32, // audio format
- samplerate, // sample rate
+ conf::samplerate, // sample rate
&realBufsize, // buffer size in byte
- &G_Mixer.masterPlay, // audio callback
+ &mixer::masterPlay, // audio callback
nullptr, // user data (unused)
&options);
- G_audio_status = true;
-
-#if defined(__linux__)
- if (api == SYS_API_JACK)
- jackSetSyncCb();
-#endif
-
+ status = true;
return 1;
}
catch (RtAudioError &e) {
- gu_log("[KA] system init error: %s\n", e.getMessage().c_str());
+ gu_log("[KA] rtSystem init error: %s\n", e.getMessage().c_str());
closeDevice();
- G_audio_status = false;
return 0;
}
}
/* -------------------------------------------------------------------------- */
-int KernelAudio::startStream()
+int startStream()
{
try {
- system->startStream();
- gu_log("[KA] latency = %lu\n", system->getStreamLatency());
+ rtSystem->startStream();
+ gu_log("[KA] latency = %lu\n", rtSystem->getStreamLatency());
return 1;
}
catch (RtAudioError &e) {
/* -------------------------------------------------------------------------- */
-int KernelAudio::stopStream()
+int stopStream()
{
try {
- system->stopStream();
+ rtSystem->stopStream();
return 1;
}
catch (RtAudioError &e) {
/* -------------------------------------------------------------------------- */
-string KernelAudio::getDeviceName(unsigned dev)
+string getDeviceName(unsigned dev)
{
try {
- return ((RtAudio::DeviceInfo) system->getDeviceInfo(dev)).name;
+ return static_cast<RtAudio::DeviceInfo>(rtSystem->getDeviceInfo(dev)).name;
}
catch (RtAudioError &e) {
gu_log("[KA] invalid device ID = %d\n", dev);
/* -------------------------------------------------------------------------- */
-int KernelAudio::closeDevice()
+int closeDevice()
{
- if (system->isStreamOpen()) {
+ if (rtSystem->isStreamOpen()) {
#if defined(__linux__) || defined(__APPLE__)
- system->abortStream(); // stopStream seems to lock the thread
+ rtSystem->abortStream(); // stopStream seems to lock the thread
#elif defined(_WIN32)
- system->stopStream(); // on Windows it's the opposite
+ rtSystem->stopStream(); // on Windows it's the opposite
#endif
- system->closeStream();
- delete system;
- system = nullptr;
+ rtSystem->closeStream();
+ delete rtSystem;
+ rtSystem = nullptr;
}
return 1;
}
/* -------------------------------------------------------------------------- */
-unsigned KernelAudio::getMaxInChans(int dev)
+unsigned getMaxInChans(int dev)
{
if (dev == -1) return 0;
try {
- return ((RtAudio::DeviceInfo) system->getDeviceInfo(dev)).inputChannels;
+ return static_cast<RtAudio::DeviceInfo>(rtSystem->getDeviceInfo(dev)).inputChannels;
}
catch (RtAudioError &e) {
gu_log("[KA] Unable to get input channels\n");
/* -------------------------------------------------------------------------- */
-unsigned KernelAudio::getMaxOutChans(unsigned dev)
+unsigned getMaxOutChans(unsigned dev)
{
try {
- return ((RtAudio::DeviceInfo) system->getDeviceInfo(dev)).outputChannels;
+ return static_cast<RtAudio::DeviceInfo>(rtSystem->getDeviceInfo(dev)).outputChannels;
}
catch (RtAudioError &e) {
gu_log("[KA] Unable to get output channels\n");
/* -------------------------------------------------------------------------- */
-bool KernelAudio::isProbed(unsigned dev)
+bool isProbed(unsigned dev)
{
try {
- return ((RtAudio::DeviceInfo) system->getDeviceInfo(dev)).probed;
+ return static_cast<RtAudio::DeviceInfo>(rtSystem->getDeviceInfo(dev)).probed;
}
catch (RtAudioError &e) {
return 0;
/* -------------------------------------------------------------------------- */
-unsigned KernelAudio::getDuplexChans(unsigned dev)
+unsigned getDuplexChans(unsigned dev)
{
try {
- return ((RtAudio::DeviceInfo) system->getDeviceInfo(dev)).duplexChannels;
+ return static_cast<RtAudio::DeviceInfo>(rtSystem->getDeviceInfo(dev)).duplexChannels;
}
catch (RtAudioError &e) {
return 0;
/* -------------------------------------------------------------------------- */
-bool KernelAudio::isDefaultIn(unsigned dev)
+bool isDefaultIn(unsigned dev)
{
try {
- return ((RtAudio::DeviceInfo) system->getDeviceInfo(dev)).isDefaultInput;
+ return static_cast<RtAudio::DeviceInfo>(rtSystem->getDeviceInfo(dev)).isDefaultInput;
}
catch (RtAudioError &e) {
return 0;
/* -------------------------------------------------------------------------- */
-bool KernelAudio::isDefaultOut(unsigned dev)
+bool isDefaultOut(unsigned dev)
{
try {
- return ((RtAudio::DeviceInfo) system->getDeviceInfo(dev)).isDefaultOutput;
+ return static_cast<RtAudio::DeviceInfo>(rtSystem->getDeviceInfo(dev)).isDefaultOutput;
}
catch (RtAudioError &e) {
return 0;
/* -------------------------------------------------------------------------- */
-int KernelAudio::getTotalFreqs(unsigned dev)
+int getTotalFreqs(unsigned dev)
{
try {
- return ((RtAudio::DeviceInfo) system->getDeviceInfo(dev)).sampleRates.size();
+ return static_cast<RtAudio::DeviceInfo>(rtSystem->getDeviceInfo(dev)).sampleRates.size();
}
catch (RtAudioError &e) {
return 0;
/* -------------------------------------------------------------------------- */
-int KernelAudio::getFreq(unsigned dev, int i)
+int getFreq(unsigned dev, int i)
{
try {
- return ((RtAudio::DeviceInfo) system->getDeviceInfo(dev)).sampleRates.at(i);
+ return static_cast<RtAudio::DeviceInfo>(rtSystem->getDeviceInfo(dev)).sampleRates.at(i);
}
catch (RtAudioError &e) {
return 0;
/* -------------------------------------------------------------------------- */
-int KernelAudio::getDefaultIn()
+unsigned getRealBufSize()
{
- return system->getDefaultInputDevice();
+ return realBufsize;
}
-int KernelAudio::getDefaultOut()
+
+/* -------------------------------------------------------------------------- */
+
+
+bool isInputEnabled()
{
- return system->getDefaultOutputDevice();
+ return inputEnabled;
}
/* -------------------------------------------------------------------------- */
-int KernelAudio::getDeviceByName(const char *name)
+unsigned countDevices()
{
- for (unsigned i=0; i<numDevs; i++)
- if (name == getDeviceName(i))
- return i;
- return -1;
+ return numDevs;
}
/* -------------------------------------------------------------------------- */
-bool KernelAudio::hasAPI(int API)
+int getDefaultIn()
{
- vector<RtAudio::Api> APIs;
- RtAudio::getCompiledApi(APIs);
- for (unsigned i=0; i<APIs.size(); i++)
- if (APIs.at(i) == API)
- return true;
- return false;
+ return rtSystem->getDefaultInputDevice();
+}
+
+int getDefaultOut()
+{
+ return rtSystem->getDefaultOutputDevice();
}
/* -------------------------------------------------------------------------- */
-string KernelAudio::getRtAudioVersion()
+int getDeviceByName(const char *name)
{
- return RtAudio::getVersion();
+ for (unsigned i=0; i<numDevs; i++)
+ if (name == getDeviceName(i))
+ return i;
+ return -1;
}
/* -------------------------------------------------------------------------- */
-#ifdef __linux__
-
-int KernelAudio::jackSyncCb(jack_transport_state_t state, jack_position_t *pos,
- void *arg)
+bool hasAPI(int API)
{
- return G_KernelAudio.__jackSyncCb(state, pos, arg);
+ vector<RtAudio::Api> APIs;
+ RtAudio::getCompiledApi(APIs);
+ for (unsigned i=0; i<APIs.size(); i++)
+ if (APIs.at(i) == API)
+ return true;
+ return false;
}
/* -------------------------------------------------------------------------- */
-jack_client_t *KernelAudio::jackGetHandle()
+#ifdef __linux__
+
+
+const JackState &jackTransportQuery()
{
- return (jack_client_t*) system->rtapi_->__HACK__getJackClient();
+ if (api != G_SYS_API_JACK)
+ return jackState;
+ jack_position_t position;
+ jack_transport_state_t ts = jack_transport_query(jackGetHandle(), &position);
+ jackState.running = ts != JackTransportStopped;
+ jackState.bpm = position.beats_per_minute;
+ jackState.frame = position.frame;
+ return jackState;
}
/* -------------------------------------------------------------------------- */
-void KernelAudio::jackStart()
+void jackStart()
{
- if (api == SYS_API_JACK) {
- jack_client_t *client = jackGetHandle();
- jack_transport_start(client);
- }
+ if (api == G_SYS_API_JACK)
+ jack_transport_start(jackGetHandle());
}
/* -------------------------------------------------------------------------- */
-void KernelAudio::jackStop()
+void jackSetPosition(uint32_t frame)
{
- if (api == SYS_API_JACK) {
- jack_client_t *client = jackGetHandle();
- jack_transport_stop(client);
- }
+ if (api != G_SYS_API_JACK)
+ return;
+ jack_position_t position;
+ jack_transport_query(jackGetHandle(), &position);
+ position.frame = frame;
+ jack_transport_reposition(jackGetHandle(), &position);
}
/* -------------------------------------------------------------------------- */
-void KernelAudio::jackSetSyncCb()
+void jackSetBpm(double bpm)
{
- jack_client_t *client = jackGetHandle();
- jack_set_sync_callback(client, jackSyncCb, nullptr);
- //jack_set_sync_timeout(client, 8);
+ if (api != G_SYS_API_JACK)
+ return;
+ jack_position_t position;
+ jack_transport_query(jackGetHandle(), &position);
+ position.valid = jack_position_bits_t::JackPositionBBT;
+ position.bar = 0; // no such info from Giada
+ position.beat = 0; // no such info from Giada
+ position.tick = 0; // no such info from Giada
+ position.beats_per_minute = bpm;
+ jack_transport_reposition(jackGetHandle(), &position);
}
/* -------------------------------------------------------------------------- */
-int KernelAudio::__jackSyncCb(jack_transport_state_t state, jack_position_t *pos,
- void *arg)
+void jackStop()
{
- switch (state) {
- case JackTransportStopped:
- gu_log("[KA] Jack transport stopped, frame=%d\n", pos->frame);
- glue_stopSeq(false); // false = not from GUI
- if (pos->frame == 0)
- glue_rewindSeq();
- break;
-
- case JackTransportRolling:
- gu_log("[KA] Jack transport rolling\n");
- break;
-
- case JackTransportStarting:
- gu_log("[KA] Jack transport starting, frame=%d\n", pos->frame);
- glue_startSeq(false); // false = not from GUI
- if (pos->frame == 0)
- glue_rewindSeq();
- break;
-
- default:
- gu_log("[KA] Jack transport [unknown]\n");
- }
- return 1;
+ if (api == G_SYS_API_JACK)
+ jack_transport_stop(jackGetHandle());
}
-#endif
+#endif // #ifdef __linux__
+
+}}}; // giada::m::kernelAudio
*
* Giada - Your Hardcore Loopmachine
*
- * KernelAudio
- *
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
* -------------------------------------------------------------------------- */
-#ifndef KERNELAUDIO_H
-#define KERNELAUDIO_H
+#ifndef G_KERNELAUDIO_H
+#define G_KERNELAUDIO_H
-#include "../deps/rtaudio-mod/RtAudio.h"
+#include <string>
#ifdef __linux__
#include <jack/jack.h>
#include <jack/intclient.h>
#endif
-class KernelAudio
-{
-public:
+class RtAudio;
+class Mixer;
- KernelAudio();
- int openDevice(int api, int outDev, int inDev, int outChan, int inChan,
- int samplerate, int buffersize);
+namespace giada {
+namespace m {
+namespace kernelAudio
+{
+#ifdef __linux__
- int closeDevice();
+struct JackState
+{
+ bool running;
+ double bpm;
+ uint32_t frame;
+};
- int startStream();
- int stopStream();
+#endif
- bool isProbed (unsigned dev);
- bool isDefaultIn (unsigned dev);
- bool isDefaultOut (unsigned dev);
- std::string getDeviceName (unsigned dev);
- unsigned getMaxInChans (int dev);
- unsigned getMaxOutChans (unsigned dev);
- unsigned getDuplexChans (unsigned dev);
- int getTotalFreqs (unsigned dev);
- int getFreq (unsigned dev, int i);
- int getDeviceByName (const char *name);
- int getDefaultOut ();
- int getDefaultIn ();
- bool hasAPI (int API);
- std::string getRtAudioVersion();
+void init();
+
+int openDevice();
+int closeDevice();
+int startStream();
+int stopStream();
+
+bool getStatus();
+bool isProbed(unsigned dev);
+bool isDefaultIn(unsigned dev);
+bool isDefaultOut(unsigned dev);
+bool isInputEnabled();
+std::string getDeviceName(unsigned dev);
+unsigned getMaxInChans(int dev);
+unsigned getMaxOutChans(unsigned dev);
+unsigned getDuplexChans(unsigned dev);
+unsigned getRealBufSize();
+unsigned countDevices();
+int getTotalFreqs(unsigned dev);
+int getFreq(unsigned dev, int i);
+int getDeviceByName(const char *name);
+int getDefaultOut();
+int getDefaultIn();
+bool hasAPI(int API);
#ifdef __linux__
- jack_client_t *jackGetHandle();
- void jackStart();
- void jackStop();
- void jackSetSyncCb();
- static int jackSyncCb(jack_transport_state_t state, jack_position_t *pos, void *arg);
- int __jackSyncCb(jack_transport_state_t state, jack_position_t *pos, void *arg);
-
-#endif
-
- unsigned numDevs;
- bool inputEnabled;
- unsigned realBufsize; // reale bufsize from the soundcard
- int api;
+void jackStart();
+void jackStop();
+void jackSetPosition(uint32_t frame);
+void jackSetBpm(double bpm);
+const JackState &jackTransportQuery();
-private:
-
- RtAudio *system;
-};
+#endif
+}}}; // giada::m::kernelAudio::
#endif
*
* Giada - Your Hardcore Loopmachine
*
- * KernelMidi
- *
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
* -------------------------------------------------------------------------- */
-#include <RtMidi.h>
+#include <rtmidi/RtMidi.h>
#include "../utils/log.h"
#include "../glue/channel.h"
#include "../glue/main.h"
+#include "../glue/transport.h"
#include "../glue/io.h"
#include "mixer.h"
#include "const.h"
#include "kernelMidi.h"
-extern bool G_midiStatus;
-extern Conf G_Conf;
-extern Mixer G_Mixer;
-extern KernelMidi G_KernelMidi;
-extern MidiMapConf G_MidiMap;
+using std::string;
+using std::vector;
+
+
+namespace giada {
+namespace m {
+namespace kernelMidi
+{
+namespace
+{
+bool status = false;
+int api = 0;
+RtMidiOut *midiOut = nullptr;
+RtMidiIn *midiIn = nullptr;
+unsigned numOutPorts = 0;
+unsigned numInPorts = 0;
+
+/* cb_learn
+ * callback prepared by the gdMidiGrabber window and called by
+ * kernelMidi. It contains things to do once the midi message has been
+ * stored. */
+
+cb_midiLearn *cb_learn = nullptr;
+void *cb_data = nullptr;
+
+
+/* -------------------------------------------------------------------------- */
+
+
#ifdef WITH_VST
-extern PluginHost G_PluginHost;
+
+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 <Plugin *> *plugins. */
+
+ vector <Plugin *> *plugins = pluginHost::getStack(pluginHost::CHANNEL, ch);
+
+ for (unsigned i=0; i<plugins->size(); i++)
+ {
+ Plugin *plugin = plugins->at(i);
+ for (unsigned k=0; k<plugin->midiInParams.size(); k++) {
+ uint32_t midiInParam = plugin->midiInParams.at(k);
+ if (pure != midiInParam)
+ continue;
+ float vf = (value >> 8)/127.0f;
+ plugin->setParameter(k, vf);
+ gu_log(" >>> [plugin %d parameter %d] ch=%d (pure=0x%X, value=%d, float=%f)\n",
+ i, k, ch->index, pure, value >> 8, vf);
+ }
+ }
+}
+
#endif
-using std::string;
-using std::vector;
+/* -------------------------------------------------------------------------- */
+
+
+void processChannels(uint32_t pure, uint32_t value)
+{
+ for (unsigned i=0; i<mixer::channels.size(); i++) {
+
+ Channel *ch = static_cast<SampleChannel*>(mixer::channels.at(i));
+
+ if (!ch->midiIn)
+ continue;
+
+ if (pure == ch->midiInKeyPress) {
+ gu_log(" >>> keyPress, ch=%d (pure=0x%X)\n", ch->index, pure);
+ glue_keyPress(ch, false, false);
+ }
+ else if (pure == ch->midiInKeyRel) {
+ gu_log(" >>> keyRel ch=%d (pure=0x%X)\n", ch->index, pure);
+ glue_keyRelease(ch, false, false);
+ }
+ else if (pure == ch->midiInMute) {
+ gu_log(" >>> mute ch=%d (pure=0x%X)\n", ch->index, pure);
+ glue_setMute(ch, false);
+ }
+ else if (pure == ch->midiInSolo) {
+ gu_log(" >>> solo ch=%d (pure=0x%X)\n", ch->index, pure);
+ ch->solo ? glue_setSoloOn(ch, false) : glue_setSoloOff(ch, false);
+ }
+ else if (pure == ch->midiInVolume) {
+ float vf = (value >> 8)/127.0f;
+ gu_log(" >>> volume ch=%d (pure=0x%X, value=%d, float=%f)\n",
+ 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<SampleChannel*>(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<SampleChannel*>(ch), false);
+ }
+
+#ifdef WITH_VST
+ processPlugins(ch, pure, value); // Process plugins' parameters
+#endif
-KernelMidi::KernelMidi()
- : numOutPorts(0),
- numInPorts (0),
- api (0), // one api for both in & out
- midiOut (nullptr),
- midiIn (nullptr),
- cb_learn (nullptr),
- cb_data (nullptr)
+ /* Redirect full midi message (pure + value) to plugins */
+ ch->receiveMidi(pure | value);
+ }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void processMaster(uint32_t pure, uint32_t value)
{
+ if (pure == conf::midiInRewind) {
+ gu_log(" >>> rewind (master) (pure=0x%X)\n", pure);
+ glue_rewindSeq(false);
+ }
+ else if (pure == conf::midiInStartStop) {
+ gu_log(" >>> startStop (master) (pure=0x%X)\n", pure);
+ glue_startStopSeq(false);
+ }
+ else if (pure == conf::midiInActionRec) {
+ gu_log(" >>> actionRec (master) (pure=0x%X)\n", pure);
+ glue_startStopActionRec(false);
+ }
+ else if (pure == conf::midiInInputRec) {
+ gu_log(" >>> inputRec (master) (pure=0x%X)\n", pure);
+ glue_startStopInputRec(false);
+ }
+ else if (pure == conf::midiInMetronome) {
+ gu_log(" >>> metronome (master) (pure=0x%X)\n", pure);
+ glue_startStopMetronome(false);
+ }
+ else if (pure == conf::midiInVolumeIn) {
+ float vf = (value >> 8)/127.0f;
+ gu_log(" >>> input volume (master) (pure=0x%X, value=%d, float=%f)\n",
+ pure, value >> 8, vf);
+ glue_setInVol(vf, false);
+ }
+ else if (pure == conf::midiInVolumeOut) {
+ float vf = (value >> 8)/127.0f;
+ gu_log(" >>> output volume (master) (pure=0x%X, value=%d, float=%f)\n",
+ pure, value >> 8, vf);
+ glue_setOutVol(vf, false);
+ }
+ else if (pure == conf::midiInBeatDouble) {
+ gu_log(" >>> sequencer x2 (master) (pure=0x%X)\n", pure);
+ glue_beatsMultiply();
+ }
+ else if (pure == conf::midiInBeatHalf) {
+ gu_log(" >>> sequencer /2 (master) (pure=0x%X)\n", pure);
+ glue_beatsDivide();
+ }
}
/* -------------------------------------------------------------------------- */
-void KernelMidi::callback(double t, vector<unsigned char> *msg, void *data)
+static void callback(double t, std::vector<unsigned char> *msg, void *data)
{
- G_KernelMidi.__callback(t, msg, data);
+ /* 0.8.0 - for now we handle other midi signals (common and real-time
+ * messages) as unknown, for debugging purposes */
+
+ if (msg->size() < 3) {
+ gu_log("[KM] MIDI received - unknown signal - size=%d, value=0x", (int) msg->size());
+ for (unsigned i=0; i<msg->size(); 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 */
+
+ 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
+ else
+ pure = input & 0xFFFFFF00; // input with 'value' 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. */
+
+ if (cb_learn)
+ cb_learn(pure, cb_data);
+ else {
+ processMaster(pure, value);
+ processChannels(pure, value);
+ }
}
/* -------------------------------------------------------------------------- */
-void KernelMidi::sendMidiLightningInitMsgs()
+void sendMidiLightningInitMsgs()
{
- for(unsigned i=0; i<G_MidiMap.initCommands.size(); i++) {
- MidiMapConf::message_t msg = G_MidiMap.initCommands.at(i);
+ for(unsigned i=0; i<midimap::initCommands.size(); i++) {
+ midimap::message_t msg = midimap::initCommands.at(i);
if (msg.value != 0x0 && msg.channel != -1) {
gu_log("[KM] MIDI send (init) - Channel %x - Event 0x%X\n", msg.channel, msg.value);
send(msg.value | MIDI_CHANS[msg.channel]);
}
}
+}; // {anonymous}
+
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
-void KernelMidi::startMidiLearn(cb_midiLearn *cb, void *data)
+void startMidiLearn(cb_midiLearn *cb, void *data)
{
cb_learn = cb;
cb_data = data;
/* -------------------------------------------------------------------------- */
-void KernelMidi::stopMidiLearn()
+void stopMidiLearn()
{
cb_learn = nullptr;
cb_data = nullptr;
/* -------------------------------------------------------------------------- */
-void KernelMidi::setApi(int _api)
+void setApi(int _api)
{
api = _api;
gu_log("[KM] using system 0x%x\n", api);
/* -------------------------------------------------------------------------- */
-int KernelMidi::openOutDevice(int port)
+int openOutDevice(int port)
{
try {
midiOut = new RtMidiOut((RtMidi::Api) api, "Giada MIDI Output");
- G_midiStatus = true;
+ status = true;
}
catch (RtMidiError &error) {
gu_log("[KM] MIDI out device error: %s\n", error.getMessage().c_str());
- G_midiStatus = false;
+ status = false;
return 0;
}
gu_log("[KM] MIDI out port %d open\n", port);
/* TODO - it shold send midiLightning message only if there is a map loaded
- and available in G_MidiMap. */
+ and available in midimap:: */
sendMidiLightningInitMsgs();
return 1;
}
catch (RtMidiError &error) {
gu_log("[KM] unable to open MIDI out port %d: %s\n", port, error.getMessage().c_str());
- G_midiStatus = false;
+ status = false;
return 0;
}
}
/* -------------------------------------------------------------------------- */
-int KernelMidi::openInDevice(int port)
+int openInDevice(int port)
{
try {
midiIn = new RtMidiIn((RtMidi::Api) api, "Giada MIDI input");
- G_midiStatus = true;
+ status = true;
}
catch (RtMidiError &error) {
gu_log("[KM] MIDI in device error: %s\n", error.getMessage().c_str());
- G_midiStatus = false;
+ status = false;
return 0;
}
}
catch (RtMidiError &error) {
gu_log("[KM] unable to open MIDI in port %d: %s\n", port, error.getMessage().c_str());
- G_midiStatus = false;
+ status = false;
return 0;
}
}
/* -------------------------------------------------------------------------- */
-bool KernelMidi::hasAPI(int API)
+bool hasAPI(int API)
{
vector<RtMidi::Api> APIs;
RtMidi::getCompiledApi(APIs);
/* -------------------------------------------------------------------------- */
-string KernelMidi::getOutPortName(unsigned p)
+string getOutPortName(unsigned p)
{
try { return midiOut->getPortName(p); }
catch (RtMidiError &error) { return ""; }
}
-string KernelMidi::getInPortName(unsigned p)
+string getInPortName(unsigned p)
{
try { return midiIn->getPortName(p); }
catch (RtMidiError &error) { return ""; }
/* -------------------------------------------------------------------------- */
-void KernelMidi::send(uint32_t data)
+void send(uint32_t data)
{
- if (!G_midiStatus)
+ if (!status)
return;
vector<unsigned char> msg(1, getB1(data));
/* -------------------------------------------------------------------------- */
-void KernelMidi::send(int b1, int b2, int b3)
+void send(int b1, int b2, int b3)
{
- if (!G_midiStatus)
+ if (!status)
return;
vector<unsigned char> msg(1, b1);
/* -------------------------------------------------------------------------- */
-void KernelMidi::__callback(double t, vector<unsigned char> *msg, void *data)
+unsigned countInPorts()
{
- /* 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; i<msg->size(); 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 */
-
- 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 (!G_Conf.noNoteOff)
- pure = input & 0xFFFF0000; // input without 'value' byte
- else
- pure = input & 0xFFFFFF00; // input with 'value' 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. */
-
- if (cb_learn)
- cb_learn(pure, cb_data);
- else {
- processMaster(pure, value);
- processChannels(pure, value);
- }
+ return numInPorts;
}
-/* -------------------------------------------------------------------------- */
-
-
-void KernelMidi::processMaster(uint32_t pure, uint32_t value)
+unsigned countOutPorts()
{
- if (pure == G_Conf.midiInRewind) {
- gu_log(" >>> rewind (master) (pure=0x%X)\n", pure);
- glue_rewindSeq();
- }
- else if (pure == G_Conf.midiInStartStop) {
- gu_log(" >>> startStop (master) (pure=0x%X)\n", pure);
- glue_startStopSeq(false);
- }
- else if (pure == G_Conf.midiInActionRec) {
- gu_log(" >>> actionRec (master) (pure=0x%X)\n", pure);
- glue_startStopActionRec(false);
- }
- else if (pure == G_Conf.midiInInputRec) {
- gu_log(" >>> inputRec (master) (pure=0x%X)\n", pure);
- glue_startStopInputRec(false);
- }
- else if (pure == G_Conf.midiInMetronome) {
- gu_log(" >>> metronome (master) (pure=0x%X)\n", pure);
- glue_startStopMetronome(false);
- }
- else if (pure == G_Conf.midiInVolumeIn) {
- float vf = (value >> 8)/127.0f;
- gu_log(" >>> input volume (master) (pure=0x%X, value=%d, float=%f)\n",
- pure, value >> 8, vf);
- glue_setInVol(vf, false);
- }
- else if (pure == G_Conf.midiInVolumeOut) {
- float vf = (value >> 8)/127.0f;
- gu_log(" >>> output volume (master) (pure=0x%X, value=%d, float=%f)\n",
- pure, value >> 8, vf);
- glue_setOutVol(vf, false);
- }
- else if (pure == G_Conf.midiInBeatDouble) {
- gu_log(" >>> sequencer x2 (master) (pure=0x%X)\n", pure);
- glue_beatsMultiply();
- }
- else if (pure == G_Conf.midiInBeatHalf) {
- gu_log(" >>> sequencer /2 (master) (pure=0x%X)\n", pure);
- glue_beatsDivide();
- }
+ return numOutPorts;
}
/* -------------------------------------------------------------------------- */
-void KernelMidi::processChannels(uint32_t pure, uint32_t value)
-{
- for (unsigned i=0; i<G_Mixer.channels.size(); i++) {
+int getB1(uint32_t iValue) { return (iValue >> 24) & 0xFF; }
+int getB2(uint32_t iValue) { return (iValue >> 16) & 0xFF; }
+int getB3(uint32_t iValue) { return (iValue >> 8) & 0xFF; }
- Channel *ch = (Channel*) G_Mixer.channels.at(i);
- if (!ch->midiIn)
- continue;
-
- if (pure == ch->midiInKeyPress) {
- gu_log(" >>> keyPress, ch=%d (pure=0x%X)\n", ch->index, pure);
- glue_keyPress(ch, false, false);
- }
- else if (pure == ch->midiInKeyRel) {
- gu_log(" >>> keyRel ch=%d (pure=0x%X)\n", ch->index, pure);
- glue_keyRelease(ch, false, false);
- }
- else if (pure == ch->midiInMute) {
- gu_log(" >>> mute ch=%d (pure=0x%X)\n", ch->index, pure);
- glue_setMute(ch, false);
- }
- else if (pure == ch->midiInSolo) {
- gu_log(" >>> solo ch=%d (pure=0x%X)\n", ch->index, pure);
- ch->solo ? glue_setSoloOn(ch, false) : glue_setSoloOff(ch, false);
- }
- else if (pure == ch->midiInVolume) {
- float vf = (value >> 8)/127.0f;
- gu_log(" >>> volume ch=%d (pure=0x%X, value=%d, float=%f)\n",
- ch->index, pure, value >> 8, vf);
- glue_setChanVol(ch, vf, false);
- }
- else if (pure == ((SampleChannel*)ch)->midiInPitch) {
- float vf = (value >> 8)/(127/4.0f); // [0-127] ~> [0.0 4.0]
- gu_log(" >>> pitch ch=%d (pure=0x%X, value=%d, float=%f)\n",
- ch->index, pure, value >> 8, vf);
- glue_setPitch(nullptr, (SampleChannel*)ch, vf, false);
- }
- else if (pure == ((SampleChannel*)ch)->midiInReadActions) {
- gu_log(" >>> start/stop read actions ch=%d (pure=0x%X)\n", ch->index, pure);
- glue_startStopReadingRecs((SampleChannel*)ch, false);
- }
-
-#ifdef WITH_VST
-
- /* Process plugins' parameters */
-
- processPlugins(ch, pure, value);
-
-#endif
-
- /* Redirect full midi message (pure + value) to plugins */
-
- ch->receiveMidi(pure | value);
- }
+uint32_t getIValue(int b1, int b2, int b3)
+{
+ return (b1 << 24) | (b2 << 16) | (b3 << 8) | (0x00);
}
/* -------------------------------------------------------------------------- */
-#ifdef WITH_VST
-
-void KernelMidi::processPlugins(Channel *ch, uint32_t pure, uint32_t value)
+uint32_t setChannel(uint32_t iValue, int channel)
{
- /* 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 <Plugin *> *plugins. */
-
- vector <Plugin *> *plugins = G_PluginHost.getStack(PluginHost::CHANNEL, ch);
-
- for (unsigned i=0; i<plugins->size(); i++)
- {
- Plugin *plugin = plugins->at(i);
- for (unsigned k=0; k<plugin->midiInParams.size(); k++) {
- uint32_t midiInParam = plugin->midiInParams.at(k);
- if (pure != midiInParam)
- continue;
- float vf = (value >> 8)/127.0f;
- plugin->setParameter(k, vf);
- gu_log(" >>> [plugin %d parameter %d] ch=%d (pure=0x%X, value=%d, float=%f)\n",
- i, k, ch->index, pure, value >> 8, vf);
- }
- }
+ uint32_t chanMask = 0xF << 24;
+ return (iValue & (~chanMask)) | (channel << 24);
}
-#endif
-
/* -------------------------------------------------------------------------- */
-string KernelMidi::getRtMidiVersion()
+bool getStatus()
{
- return midiOut->getVersion();
+ return status;
}
+
+}}}; // giada::m::kernelMidi::
*
* Giada - Your Hardcore Loopmachine
*
- * KernelMidi
- *
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
* -------------------------------------------------------------------------- */
-#ifndef KERNELMIDI_H
-#define KERNELMIDI_H
+#ifndef G_KERNELMIDI_H
+#define G_KERNELMIDI_H
-#ifdef __APPLE__ // our compiler still doesn't know about cstdint (c++11 stuff)
+#ifdef __APPLE__ // our Clang still doesn't know about cstdint (c++11 stuff)
#include <stdint.h>
#else
#include <cstdint>
#include <vector>
-class KernelMidi
+namespace giada {
+namespace m {
+namespace kernelMidi
{
-public:
-
- unsigned numOutPorts;
- unsigned numInPorts;
-
- KernelMidi();
-
- typedef void (cb_midiLearn) (uint32_t, void *);
+typedef void (cb_midiLearn) (uint32_t, void *);
- void startMidiLearn(cb_midiLearn *cb, void *data);
- void stopMidiLearn();
+void startMidiLearn(cb_midiLearn *cb, void *data);
+void stopMidiLearn();
- int getB1(uint32_t iValue) { return (iValue >> 24) & 0xFF; }
- int getB2(uint32_t iValue) { return (iValue >> 16) & 0xFF; }
- int getB3(uint32_t iValue) { return (iValue >> 8) & 0xFF; }
+int getB1(uint32_t iValue);
+int getB2(uint32_t iValue);
+int getB3(uint32_t iValue);
- uint32_t getIValue(int b1, int b2, int b3) {
- return (b1 << 24) | (b2 << 16) | (b3 << 8) | (0x00);
- }
+uint32_t getIValue(int b1, int b2, int b3);
- /* send
- * send a MIDI message 's' (uint32_t). */
+/* setChannel
+Changes MIDI channel number inside iValue. Returns new message with updated
+channel. */
- void send(uint32_t s);
+uint32_t setChannel(uint32_t iValue, int channel);
- /* send (2)
- * send separate bytes of MIDI message. */
+/* send
+ * send a MIDI message 's' (uint32_t). */
- void send(int b1, int b2=-1, int b3=-1);
+void send(uint32_t s);
- /* setApi
- * set the Api in use for both in & out messages. */
+/* send (2)
+ * send separate bytes of MIDI message. */
- void setApi(int api);
+void send(int b1, int b2=-1, int b3=-1);
- /* open/close/in/outDevice */
+/* setApi
+ * set the Api in use for both in & out messages. */
- int openOutDevice(int port);
- int openInDevice(int port);
- int closeInDevice();
- int closeOutDevice();
+void setApi(int api);
- /* getIn/OutPortName
- * return the name of the port 'p'. */
+/* getStatus
+Returns current engine status. */
- std::string getInPortName(unsigned p);
- std::string getOutPortName(unsigned p);
+bool getStatus();
- bool hasAPI(int API);
+/* open/close/in/outDevice */
- std::string getRtMidiVersion();
+int openOutDevice(int port);
+int openInDevice(int port);
+int closeInDevice();
+int closeOutDevice();
-private:
+/* getIn/OutPortName
+ * return the name of the port 'p'. */
- int api;
- class RtMidiOut *midiOut;
- class RtMidiIn *midiIn;
+std::string getInPortName(unsigned p);
+std::string getOutPortName(unsigned p);
- /* cb_learn
- * callback prepared by the gdMidiGrabber window and called by
- * kernelMidi. It contains things to do once the midi message has been
- * stored. */
+unsigned countInPorts();
+unsigned countOutPorts();
- cb_midiLearn *cb_learn;
- void *cb_data;
+bool hasAPI(int API);
- /* callback
- * master callback for input events. */
+}}}; // giada::m::kernelMidi::
- static void callback(double t, std::vector<unsigned char> *msg, void *data);
- void __callback(double t, std::vector<unsigned char> *msg, void *data);
-
- void sendMidiLightningInitMsgs();
-
-
- void processMaster(uint32_t pure, uint32_t value);
- void processChannels(uint32_t pure, uint32_t value);
-#ifdef WITH_VST
- void processPlugins(class Channel *ch, uint32_t pure, uint32_t value);
-#endif
-};
#endif
*
* Giada - Your Hardcore Loopmachine
*
- * channel
- *
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
#include "../utils/log.h"
#include "midiChannel.h"
#include "channel.h"
-#include "patch_DEPR_.h"
#include "patch.h"
+#include "const.h"
+#include "clock.h"
#include "conf.h"
#include "mixer.h"
#ifdef WITH_VST
#include "kernelMidi.h"
-extern Recorder G_Recorder;
-extern KernelMidi G_KernelMidi;
-extern Mixer G_Mixer;
-#ifdef WITH_VST
-extern PluginHost G_PluginHost;
-#endif
+using std::string;
+using namespace giada::m;
-MidiChannel::MidiChannel(int bufferSize, MidiMapConf *midiMapConf)
- : Channel (CHANNEL_MIDI, STATUS_OFF, bufferSize, midiMapConf),
+
+MidiChannel::MidiChannel(int bufferSize)
+ : Channel (CHANNEL_MIDI, STATUS_OFF, bufferSize),
midiOut (false),
midiOutChan(MIDI_CHANS[0])
{
void MidiChannel::addVstMidiEvent(uint32_t msg, int localFrame)
{
juce::MidiMessage message = juce::MidiMessage(
- G_KernelMidi.getB1(msg),
- G_KernelMidi.getB2(msg),
- G_KernelMidi.getB3(msg));
+ kernelMidi::getB1(msg),
+ kernelMidi::getB2(msg),
+ kernelMidi::getB3(msg));
midiBuffer.addEvent(message, localFrame);
}
/* -------------------------------------------------------------------------- */
-void MidiChannel::quantize(int index, int localFrame, Mixer *m) {}
+void MidiChannel::quantize(int index, int localFrame) {}
/* -------------------------------------------------------------------------- */
-void MidiChannel::parseAction(Recorder::action *a, int localFrame,
+void MidiChannel::parseAction(recorder::action *a, int localFrame,
int globalFrame, int quantize, bool mixerIsRunning)
{
- if (a->type == ACTION_MIDI)
+ if (a->type == G_ACTION_MIDI)
sendMidi(a, localFrame/2);
}
{
mute = true; // internal mute does not exist for midi (for now)
if (midiOut)
- G_KernelMidi.send(MIDI_ALL_NOTES_OFF);
+ kernelMidi::send(MIDI_ALL_NOTES_OFF);
#ifdef WITH_VST
addVstMidiEvent(MIDI_ALL_NOTES_OFF, 0);
#endif
void MidiChannel::process(float *outBuffer, float *inBuffer)
{
#ifdef WITH_VST
- pluginHost->processStack(vChan, PluginHost::CHANNEL, this);
+ pluginHost::processStack(vChan, pluginHost::CHANNEL, this);
#endif
/* TODO - isn't this useful only if WITH_VST ? */
{
if (status & (STATUS_PLAY | STATUS_ENDING)) {
if (midiOut)
- G_KernelMidi.send(MIDI_ALL_NOTES_OFF);
+ kernelMidi::send(MIDI_ALL_NOTES_OFF);
#ifdef WITH_VST
addVstMidiEvent(MIDI_ALL_NOTES_OFF, 0);
#endif
/* -------------------------------------------------------------------------- */
-int MidiChannel::readPatch_DEPR_(const char *f, int i, Patch_DEPR_ *patch,
- int samplerate, int rsmpQuality)
-{
- volume = patch->getVol(i);
- index = patch->getIndex(i);
- mute = patch->getMute(i);
- mute_s = patch->getMute_s(i);
- solo = patch->getSolo(i);
- panLeft = patch->getPanLeft(i);
- panRight = patch->getPanRight(i);
-
- midiOut = patch->getMidiValue(i, "Out");
- midiOutChan = patch->getMidiValue(i, "OutChan");
-
- readPatchMidiIn_DEPR_(i, *patch);
- readPatchMidiOut_DEPR_(i, *patch);
-
- return SAMPLE_LOADED_OK; /// TODO - change name, it's meaningless here
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-int MidiChannel::readPatch(const string &basePath, int i, Patch *patch,
+int MidiChannel::readPatch(const string &basePath, int i,
pthread_mutex_t *pluginMutex, int samplerate, int rsmpQuality)
{
- Channel::readPatch("", i, patch, pluginMutex, samplerate, rsmpQuality);
+ Channel::readPatch("", i, pluginMutex, samplerate, rsmpQuality);
- Patch::channel_t *pch = &patch->channels.at(i);
+ patch::channel_t *pch = &patch::channels.at(i);
midiOut = pch->midiOut;
midiOutChan = pch->midiOutChan;
/* -------------------------------------------------------------------------- */
-void MidiChannel::sendMidi(Recorder::action *a, int localFrame)
+void MidiChannel::sendMidi(recorder::action *a, int localFrame)
{
if (status & (STATUS_PLAY | STATUS_ENDING) && !mute) {
if (midiOut)
- G_KernelMidi.send(a->iValue | MIDI_CHANS[midiOutChan]);
+ kernelMidi::send(a->iValue | MIDI_CHANS[midiOutChan]);
#ifdef WITH_VST
addVstMidiEvent(a->iValue, localFrame);
{
if (status & (STATUS_PLAY | STATUS_ENDING) && !mute) {
if (midiOut)
- G_KernelMidi.send(data | MIDI_CHANS[midiOutChan]);
+ kernelMidi::send(data | MIDI_CHANS[midiOutChan]);
#ifdef WITH_VST
addVstMidiEvent(data, 0);
#endif
void MidiChannel::rewind()
{
if (midiOut)
- G_KernelMidi.send(MIDI_ALL_NOTES_OFF);
+ kernelMidi::send(MIDI_ALL_NOTES_OFF);
#ifdef WITH_VST
addVstMidiEvent(MIDI_ALL_NOTES_OFF, 0);
#endif
/* -------------------------------------------------------------------------- */
-int MidiChannel::writePatch(int i, bool isProject, Patch *patch)
+int MidiChannel::writePatch(int i, bool isProject)
{
- int pchIndex = Channel::writePatch(i, isProject, patch);
- Patch::channel_t *pch = &patch->channels.at(pchIndex);
+ int pchIndex = Channel::writePatch(i, isProject);
+ patch::channel_t *pch = &patch::channels.at(pchIndex);
pch->midiOut = midiOut;
pch->midiOutChan = midiOutChan;
if (!armed)
return;
+ /* Filter time, based on MIDI channel. If midiFilter == -1, all messages
+ are taken into account. */
+ /* TODO - actual MIDI filtering not yet implemented. */
+
+ if (midiFilter != -1)
+ {
+ gu_log("[Channel::processMidi] MIDI channel filtering not yet implemented\n");
+ return;
+ }
+
+ /* Now all messages are turned into Channel-0 messages. Giada doesn't care about
+ holding MIDI channel information. Moreover, having all internal messages on
+ channel 0 is way easier. */
+
+ msg = kernelMidi::setChannel(msg, 0);
+
#ifdef WITH_VST
while (true) {
- if (pthread_mutex_trylock(&G_PluginHost.mutex_midi) != 0)
+ if (pthread_mutex_trylock(&pluginHost::mutex_midi) != 0)
continue;
gu_log("[Channel::processMidi] msg=%X\n", msg);
addVstMidiEvent(msg, 0);
- pthread_mutex_unlock(&G_PluginHost.mutex_midi);
+ pthread_mutex_unlock(&pluginHost::mutex_midi);
break;
}
#endif
- if (G_Recorder.canRec(this, &G_Mixer)) {
- G_Recorder.rec(index, ACTION_MIDI, G_Mixer.currentFrame, msg);
+ if (recorder::canRec(this, clock::isRunning(), mixer::recording)) {
+ recorder::rec(index, G_ACTION_MIDI, clock::getCurrentFrame(), msg);
hasActions = true;
}
}
*
* Giada - Your Hardcore Loopmachine
*
- * channel
- *
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
* -------------------------------------------------------------------------- */
-#ifndef MIDI_CHANNEL_H
-#define MIDI_CHANNEL_H
+#ifndef G_MIDI_CHANNEL_H
+#define G_MIDI_CHANNEL_H
#ifdef WITH_VST
#include "../deps/juce-config.h"
#endif
-
#include "channel.h"
+class MidiMapConf;
+class Patch;
+
+
class MidiChannel : public Channel
{
public:
- MidiChannel(int bufferSize, class MidiMapConf *midiMapConf);
+ MidiChannel(int bufferSize);
~MidiChannel();
bool midiOut; // enable midi output
void rewind() override;
void setMute(bool internal) override;
void unsetMute(bool internal) override;
- int readPatch_DEPR_(const char *file, int i, class Patch_DEPR_ *patch,
- int samplerate, int rsmpQuality) override;
- int readPatch(const string &basePath, int i, class Patch *patch,
- pthread_mutex_t *pluginMutex, int samplerate, int rsmpQuality) override;
- int writePatch(int i, bool isProject, class Patch *patch) override;
- void quantize(int index, int localFrame, class Mixer *m) override;
+ 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(Recorder::action *a, int localFrame, int globalFrame,
+ 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(Recorder::action *a, int localFrame);
+ void sendMidi(giada::m::recorder::action *a, int localFrame);
void sendMidi(uint32_t data);
#ifdef WITH_VST
*
* Giada - Your Hardcore Loopmachine
*
- * midiMapConf
- *
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
* -------------------------------------------------------------------------- */
-#include <stdlib.h>
#include <vector>
-#include <iostream>
#include <string>
-#include <sstream>
+#include <cstring>
#include <dirent.h>
-#include "midiMapConf.h"
-#include "const.h"
#include "../utils/string.h"
#include "../utils/log.h"
+#include "../utils/fs.h"
+#include "const.h"
+#include "storager.h"
+#include "midiMapConf.h"
using std::string;
using std::vector;
+namespace giada {
+namespace m {
+namespace midimap
+{
+namespace
+{
+bool readInitCommands(json_t *jContainer)
+{
+ json_t *jInitCommands = json_object_get(jContainer, MIDIMAP_KEY_INIT_COMMANDS);
+ if (!storager::checkArray(jInitCommands, MIDIMAP_KEY_INIT_COMMANDS))
+ return 0;
+
+ size_t commandIndex;
+ json_t *jInitCommand;
+ json_array_foreach(jInitCommands, commandIndex, jInitCommand) {
+
+ string indexStr = "init command " + gu_itoa(commandIndex);
+ if (!storager::checkObject(jInitCommand, indexStr.c_str()))
+ return 0;
+
+ message_t message;
+ if (!storager::setInt(jInitCommand, MIDIMAP_KEY_CHANNEL, message.channel)) return 0;
+ if (!storager::setString(jInitCommand, MIDIMAP_KEY_MESSAGE, message.valueStr)) return 0;
+ message.value = strtoul(message.valueStr.c_str(), nullptr, 16);
+
+ initCommands.push_back(message);
+ }
+
+ return 1;
+}
+
+
+/* -------------------------------------------------------------------------- */
-void MidiMapConf::init()
+
+bool readCommand(json_t *jContainer, message_t *msg, const string &key)
+{
+ json_t *jCommand = json_object_get(jContainer, key.c_str());
+ if (!storager::checkObject(jCommand, key.c_str()))
+ return 0;
+
+ if (!storager::setInt(jCommand, MIDIMAP_KEY_CHANNEL, msg->channel)) return 0;
+ if (!storager::setString(jCommand, MIDIMAP_KEY_MESSAGE, msg->valueStr)) return 0;
+
+ return 1;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void parse(message_t *message)
+{
+ /* Remove '0x' part from the original string. */
+
+ string input = message->valueStr.replace(0, 2, "");
+
+ /* Then transform string value into the actual uint32_t value, by parsing
+ * each char (i.e. nibble) in the original string. Substitute 'n' with
+ * zeros. */
+
+ string output;
+ for (unsigned i=0, p=24; i<input.length(); i++, p-=4) {
+ if (input[i] == 'n') {
+ output += '0';
+ if (message->offset == -1) // do it once
+ message->offset = p;
+ }
+ else
+ output += input[i];
+ }
+
+ /* from string to uint32_t */
+
+ message->value = strtoul(output.c_str(), nullptr, 16);
+
+ gu_log("[parse] parsed chan=%d valueStr=%s value=%#x, offset=%d\n",
+ message->channel, message->valueStr.c_str(), message->value, message->offset);
+}
+
+}; // {anonymous}
+
+
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+
+
+
+string brand;
+string device;
+vector<message_t> initCommands;
+message_t muteOn;
+message_t muteOff;
+message_t soloOn;
+message_t soloOff;
+message_t waiting;
+message_t playing;
+message_t stopping;
+message_t stopped;
+
+string midimapsPath;
+vector<string> maps;
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void init()
{
midimapsPath = gu_getHomePath() + G_SLASH + "midimaps" + G_SLASH;
/* scan dir of midi maps and load the filenames into <>maps. */
- gu_log("[MidiMapConf::init] scanning midimaps directory...\n");
+ gu_log("[init] scanning midimaps directory...\n");
DIR *dp;
dirent *ep;
dp = opendir(midimapsPath.c_str());
if (!dp) {
- gu_log("[MidiMapConf::init] unable to scan midimaps directory!\n");
+ gu_log("[init] unable to scan midimaps directory!\n");
return;
}
// TODO - check if is a valid midimap file (verify headers)
- gu_log("[MidiMapConf::init] found midimap '%s'\n", ep->d_name);
+ gu_log("[init] found midimap '%s'\n", ep->d_name);
maps.push_back(ep->d_name);
}
- gu_log("[MidiMapConf::init] total midimaps found: %d\n", maps.size());
+ gu_log("[init] total midimaps found: %d\n", maps.size());
closedir(dp);
}
/* -------------------------------------------------------------------------- */
-void MidiMapConf::setDefault()
+void setDefault()
{
brand = "";
device = "";
/* -------------------------------------------------------------------------- */
-int MidiMapConf::read(const string &file)
+int read(const string &file)
{
if (file.empty()) {
- gu_log("[MidiMapConf::read] midimap not specified, nothing to do\n");
+ gu_log("[read] midimap not specified, nothing to do\n");
return MIDIMAP_NOT_SPECIFIED;
}
- gu_log("[MidiMapConf::read] reading midimap file '%s'\n", file.c_str());
+ gu_log("[read] reading midimap file '%s'\n", file.c_str());
+ json_error_t jError;
string path = midimapsPath + file;
- jRoot = json_load_file(path.c_str(), 0, &jError);
+ json_t *jRoot = json_load_file(path.c_str(), 0, &jError);
if (!jRoot) {
- gu_log("[MidiMapConf::read] unreadable midimap file. Error on line %d: %s\n", jError.line, jError.text);
+ gu_log("[read] unreadable midimap file. Error on line %d: %s\n", jError.line, jError.text);
return MIDIMAP_UNREADABLE;
}
- if (!setString(jRoot, MIDIMAP_KEY_BRAND, brand)) return MIDIMAP_UNREADABLE;
- if (!setString(jRoot, MIDIMAP_KEY_DEVICE, device)) return MIDIMAP_UNREADABLE;
+ if (!storager::setString(jRoot, MIDIMAP_KEY_BRAND, brand)) return MIDIMAP_UNREADABLE;
+ if (!storager::setString(jRoot, MIDIMAP_KEY_DEVICE, device)) return MIDIMAP_UNREADABLE;
if (!readInitCommands(jRoot)) return MIDIMAP_UNREADABLE;
if (!readCommand(jRoot, &muteOn, MIDIMAP_KEY_MUTE_ON)) return MIDIMAP_UNREADABLE;
if (!readCommand(jRoot, &muteOff, MIDIMAP_KEY_MUTE_OFF)) return MIDIMAP_UNREADABLE;
return MIDIMAP_READ_OK;
}
-
-/* -------------------------------------------------------------------------- */
-
-
-bool MidiMapConf::readInitCommands(json_t *jContainer)
-{
- json_t *jInitCommands = json_object_get(jContainer, MIDIMAP_KEY_INIT_COMMANDS);
- if (!checkArray(jInitCommands, MIDIMAP_KEY_INIT_COMMANDS))
- return 0;
-
- size_t commandIndex;
- json_t *jInitCommand;
- json_array_foreach(jInitCommands, commandIndex, jInitCommand) {
-
- string indexStr = "init command " + gu_itoa(commandIndex);
- if (!checkObject(jInitCommand, indexStr.c_str()))
- return 0;
-
- message_t message;
- if (!setInt(jInitCommand, MIDIMAP_KEY_CHANNEL, message.channel)) return 0;
- if (!setString(jInitCommand, MIDIMAP_KEY_MESSAGE, message.valueStr)) return 0;
- message.value = strtoul(message.valueStr.c_str(), NULL, 16);
-
- initCommands.push_back(message);
- }
-
- return 1;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-bool MidiMapConf::readCommand(json_t *jContainer, message_t *msg, const string &key)
-{
- json_t *jCommand = json_object_get(jContainer, key.c_str());
- if (!checkObject(jCommand, key.c_str()))
- return 0;
-
- if (!setInt(jCommand, MIDIMAP_KEY_CHANNEL, msg->channel)) return 0;
- if (!setString(jCommand, MIDIMAP_KEY_MESSAGE, msg->valueStr)) return 0;
-
- return 1;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void MidiMapConf::parse(message_t *message)
-{
- /* Remove '0x' part from the original string. */
-
- string input = message->valueStr.replace(0, 2, "");
-
- /* Then transform string value into the actual uint32_t value, by parsing
- * each char (i.e. nibble) in the original string. Substitute 'n' with
- * zeros. */
-
- string output;
- for (unsigned i=0, p=24; i<input.length(); i++, p-=4) {
- if (input[i] == 'n') {
- output += '0';
- if (message->offset == -1) // do it once
- message->offset = p;
- }
- else
- output += input[i];
- }
-
- /* from string to uint32_t */
-
- message->value = strtoul(output.c_str(), NULL, 16);
-
- gu_log("[MidiMapConf::parse] parsed chan=%d valueStr=%s value=%#x, offset=%d\n",
- message->channel, message->valueStr.c_str(), message->value, message->offset);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void MidiMapConf::setDefault_DEPR_()
-{
- brand = "";
- device = "";
-
- for (int i=0; i<MAX_INIT_COMMANDS; i++) {
- init_channels[i] = -1;
- init_messages[i] = 0x00;
- }
-
- muteOnChan = 0;
- muteOnOffset = 0;
- muteOnMsg = 0;
-
- muteOffChan = 0;
- muteOffOffset = 0;
- muteOffMsg = 0;
-
- soloOnChan = 0;
- soloOnOffset = 0;
- soloOnMsg = 0;
-
- soloOffChan = 0;
- soloOffOffset = 0;
- soloOffMsg = 0;
-
- waitingChan = 0;
- waitingOffset = 0;
- waitingMsg = 0;
-
- playingChan = 0;
- playingOffset = 0;
- playingMsg = 0;
-
- stoppingChan = 0;
- stoppingOffset = 0;
- stoppingMsg = 0;
-
- stoppedChan = 0;
- stoppedOffset = 0;
- stoppedMsg = 0;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-int MidiMapConf::readMap_DEPR_(string file)
-{
- if (file.empty()) {
- gu_log("[MidiMapConf::readMap_DEPR_] midimap not specified, nothing to do\n");
- return MIDIMAP_NOT_SPECIFIED;
- }
-
- gu_log("[MidiMapConf::readMap_DEPR_] reading midimap file '%s'\n", file.c_str());
-
- string path = midimapsPath + file;
- fp = fopen(path.c_str(), "r");
- if (!fp) {
- gu_log("[MidiMapConf::readMap_DEPR_] unreadable midimap file\n");
- return MIDIMAP_UNREADABLE;
- }
-
- brand = getValue("brand");
- device = getValue("device");
-
- if (brand.empty() || device.empty()) {
- gu_log("[MidiMapConf::readMap_DEPR_] invalid midimap file\n");
- return MIDIMAP_INVALID;
- }
-
- gu_log("[MidiMapConf::readMap_DEPR_] reading midimap for %s %s\n",
- brand.c_str(), device.c_str());
-
- /* parse init commands */
-
- vector<string> ic;
- gu_split(getValue("init_commands"), ";", &ic);
- for (unsigned i=0; i<(unsigned)MAX_INIT_COMMANDS && i<ic.size(); i++) {
- sscanf(ic.at(i).c_str(), "%d:%x", &init_channels[i], &init_messages[i]);
- gu_log("[MidiMapConf::readMap_DEPR_] init command %d - channel %d - message 0x%X\n",
- i, init_channels[i], init_messages[i]);
-
- /* forward compatibility */
- message_t message;
- message.channel = init_channels[i];
- message.value = init_messages[i];
- initCommands.push_back(message);
- }
-
- /* parse messages */
-
- parse_DEPR_("mute_on", &muteOnChan, &muteOnMsg, &muteOnOffset);
- parse_DEPR_("mute_off", &muteOffChan, &muteOffMsg, &muteOffOffset);
- parse_DEPR_("solo_on", &soloOnChan, &soloOnMsg, &soloOnOffset);
- parse_DEPR_("solo_off", &soloOffChan, &soloOffMsg, &soloOffOffset);
- parse_DEPR_("waiting", &waitingChan, &waitingMsg, &waitingOffset);
- parse_DEPR_("playing", &playingChan, &playingMsg, &playingOffset);
- parse_DEPR_("stopping", &stoppingChan, &stoppingMsg, &stoppingOffset);
- parse_DEPR_("stopped", &stoppedChan, &stoppedMsg, &stoppedOffset);
-
- /* forward compatibility with new JSON-based midimaps. This stuff will be
- wiped out soon. */
-
- muteOn.channel = muteOnChan;
- muteOn.offset = muteOnOffset;
- muteOn.value = muteOnMsg;
- muteOff.channel = muteOffChan;
- muteOff.offset = muteOffOffset;
- muteOff.value = muteOffMsg;
- soloOn.channel = soloOnChan;
- soloOn.offset = soloOnOffset;
- soloOn.value = soloOnMsg;
- soloOff.channel = soloOffChan;
- soloOff.offset = soloOffOffset;
- soloOff.value = soloOffMsg;
- waiting.channel = waitingChan;
- waiting.offset = waitingOffset;
- waiting.value = waitingMsg;
- playing.channel = playingChan;
- playing.offset = playingOffset;
- playing.value = playingMsg;
- stopping.channel = stoppingChan;
- stopping.offset = stoppingOffset;
- stopping.value = stoppingMsg;
- stopped.channel = stoppedChan;
- stopped.offset = stoppedOffset;
- stopped.value = stoppedMsg;
-
- close_DEPR_();
- return MIDIMAP_READ_OK;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void MidiMapConf::close_DEPR_()
-{
- if (fp != NULL)
- fclose(fp);
- fp = NULL;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void MidiMapConf::parse_DEPR_(string key, int *chan, uint32_t *msg, int *offset)
-{
- gu_log("[MidiMapConf::parse_DEPR_] command %s - ", key.c_str());
- string value = getValue(key.c_str());
-
- /* grab channel part, i.e. [channel]:*/
-
- *chan = atoi(value.substr(0, value.find(':')).c_str());
-
- /* grab MIDI part :[midi-message] and search for 'nn' note placeholder within.
- * Note: when using 'string::npos' as the value for a len (or sublen)
- * parameter in string's member functions, means "until the end of the
- * string". */
-
- string midiParts = value.substr(value.find(':')+3, string::npos);
-
- char strmsg[MAX_MIDI_NIBBLES];
- *offset = 0;
-
- /* build the message as a string, for each char (i.e. nibble) in the
- * original string. Substitute 'n' with zeros. */
-
- for (unsigned i=0, p=24; i<(unsigned)MAX_MIDI_NIBBLES; i++, p-=4) {
- if (midiParts[i] == 'n') {
- strmsg[i] = '0';
- if (*offset == 0)
- *offset = p;
- }
- else
- strmsg[i] = midiParts[i];
- }
-
- *msg = strtoul(strmsg, NULL, 16); // from string to uint32_t
-
- gu_log("chan=%d value=%s msg=%#x, offset=%d\n", *chan, midiParts.c_str(), *msg, *offset);
-}
+}}}; // giada::m::midimap::
*
* Giada - Your Hardcore Loopmachine
*
- * midiMapConf
- *
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
* -------------------------------------------------------------------------- */
-#ifndef __MIDIMAPCONF_H__
-#define __MIDIMAPCONF_H__
+#ifndef G_MIDIMAPCONF_H
+#define G_MIDIMAPCONF_H
-#include <limits.h>
-#include <stdint.h>
#include <vector>
-#include "dataStorageIni.h"
-#include "dataStorageJson.h"
-#include "../utils/fs.h"
-#if defined(__APPLE__)
-#include <pwd.h>
-#endif
-
-
-using std::string;
-using std::vector;
+#include <string>
-class MidiMapConf : public DataStorageIni, public DataStorageJson
+namespace giada {
+namespace m {
+namespace midimap
{
-public:
-
- struct message_t
- {
- int channel;
- string valueStr;
- int offset;
- uint32_t value;
- };
-
- string brand;
- string device;
- vector<message_t> initCommands;
- message_t muteOn;
- message_t muteOff;
- message_t soloOn;
- message_t soloOff;
- message_t waiting;
- message_t playing;
- message_t stopping;
- message_t stopped;
-
- /* midimapsPath
- * path of midimap files, different between OSes. */
-
- string midimapsPath;
-
- /* maps
- * Maps are the available .giadamap files. Each element of the vector
- * represents a .giadamap filename. */
-
- vector<string> maps;
-
- /* init
- Parse the midi maps folders and find the available maps. */
-
- void init();
-
- /* setDefault
- Set default values in case no maps are available/choosen. */
-
- void setDefault();
-
- /* read
- Read a midi map from file 'file'. */
-
- int read(const string &file);
-
- /* --- DEPRECATED STUFF --------------------------------------------------- */
- /* --- DEPRECATED STUFF --------------------------------------------------- */
- /* --- DEPRECATED STUFF --------------------------------------------------- */
-
- static const int MAX_INIT_COMMANDS = 32;
- static const int MAX_MIDI_BYTES = 4;
- static const int MAX_MIDI_NIBBLES = 8;
-
- /* init_*
- * init_commands. These messages are sent to the physical device as a wake up
- * signal. */
-
- int init_channels[MAX_INIT_COMMANDS];
- uint32_t init_messages[MAX_INIT_COMMANDS];
-
- /* events
- * [event]Channel: the MIDI output channel to send the event to
- * [event]notePos: the byte where the note is stored ('nn' placeholder)
- * [event]offset: the note offset (i.e. of 'nn' placeholder) */
-
- int muteOnChan;
- int muteOnOffset;
- uint32_t muteOnMsg;
-
- int muteOffChan;
- int muteOffOffset;
- uint32_t muteOffMsg;
-
- int soloOnChan;
- int soloOnOffset;
- uint32_t soloOnMsg;
-
- int soloOffChan;
- int soloOffOffset;
- uint32_t soloOffMsg;
-
- int waitingChan;
- int waitingOffset;
- uint32_t waitingMsg;
-
- int playingChan;
- int playingOffset;
- uint32_t playingMsg;
+struct message_t
+{
+ int channel;
+ std::string valueStr;
+ int offset;
+ uint32_t value;
+};
- int stoppingChan;
- int stoppingOffset;
- uint32_t stoppingMsg;
+extern std::string brand;
+extern std::string device;
+extern std::vector<message_t> initCommands;
+extern message_t muteOn;
+extern message_t muteOff;
+extern message_t soloOn;
+extern message_t soloOff;
+extern message_t waiting;
+extern message_t playing;
+extern message_t stopping;
+extern message_t stopped;
- int stoppedChan;
- int stoppedOffset;
- uint32_t stoppedMsg;
+/* midimapsPath
+ * path of midimap files, different between OSes. */
- /* setDefault
- Set default values in case no maps are available/choosen. */
+extern std::string midimapsPath;
- void setDefault_DEPR_();
+/* maps
+ * Maps are the available .giadamap files. Each element of the std::vector
+ * represents a .giadamap filename. */
- /* readMap
- Read a midi map from file 'file'. */
+extern std::vector<std::string> maps;
- int readMap_DEPR_(string file);
+/* init
+Parse the midi maps folders and find the available maps. */
-private:
+void init();
- bool readInitCommands(json_t *jContainer);
+/* setDefault
+Set default values in case no maps are available/choosen. */
- bool readCommand(json_t *jContainer, message_t *msg, const string &key);
+void setDefault();
- void parse(message_t *message);
+/* read
+Read a midi map from file 'file'. */
- /* --- DEPRECATED STUFF --------------------------------------------------- */
- /* --- DEPRECATED STUFF --------------------------------------------------- */
- /* --- DEPRECATED STUFF --------------------------------------------------- */
+int read(const std::string &file);
- void close_DEPR_();
- void parse_DEPR_(string key, int *chan, uint32_t *msg, int *offset);
-};
+}}}; // giada::m::midimap::
#endif
*
* Giada - Your Hardcore Loopmachine
*
- * mixer
- *
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
* -------------------------------------------------------------------------- */
+#include <cassert>
+#include <cstring>
+#include "../deps/rtaudio-mod/RtAudio.h"
#include "../utils/log.h"
#include "wave.h"
+#include "kernelAudio.h"
#include "recorder.h"
#include "pluginHost.h"
-#include "patch_DEPR_.h"
#include "conf.h"
#include "mixerHandler.h"
+#include "clock.h"
+#include "const.h"
#include "channel.h"
#include "sampleChannel.h"
#include "midiChannel.h"
-#include "kernelMidi.h"
#include "mixer.h"
-extern KernelAudio G_KernelAudio;
-extern Mixer G_Mixer;
-extern Recorder G_Recorder;
-extern KernelMidi G_KernelMidi;
-extern MidiMapConf G_MidiMap;
-extern Patch_DEPR_ G_Patch_DEPR_;
-extern Conf G_Conf;
-#ifdef WITH_VST
-extern PluginHost G_PluginHost;
-#endif
-
-
-Mixer::Mixer()
- : vChanInput(NULL),
- vChanInToOut(NULL)
-{}
-
-
-/* -------------------------------------------------------------------------- */
-
-
+namespace giada {
+namespace m {
+namespace mixer
+{
+namespace
+{
#define TICKSIZE 38
-float Mixer::tock[TICKSIZE] = {
- 0.059033, 0.117240, 0.173807, 0.227943, 0.278890, 0.325936,
- 0.368423, 0.405755, 0.437413, 0.462951, 0.482013, 0.494333,
- 0.499738, 0.498153, 0.489598, 0.474195, 0.452159, 0.423798,
- 0.389509, 0.349771, 0.289883, 0.230617, 0.173194, 0.118739,
- 0.068260, 0.022631, -0.017423, -0.051339, -0.078721, -0.099345,
- -0.113163, -0.120295, -0.121028, -0.115804, -0.105209, -0.089954,
- -0.070862, -0.048844
+float tock[TICKSIZE] = {
+ 0.059033, 0.117240, 0.173807, 0.227943, 0.278890, 0.325936,
+ 0.368423, 0.405755, 0.437413, 0.462951, 0.482013, 0.494333,
+ 0.499738, 0.498153, 0.489598, 0.474195, 0.452159, 0.423798,
+ 0.389509, 0.349771, 0.289883, 0.230617, 0.173194, 0.118739,
+ 0.068260, 0.022631, -0.017423, -0.051339, -0.078721, -0.099345,
+ -0.113163, -0.120295, -0.121028, -0.115804, -0.105209, -0.089954,
+ -0.070862, -0.048844
};
-float Mixer::tick[TICKSIZE] = {
- 0.175860, 0.341914, 0.488904, 0.608633, 0.694426, 0.741500,
- 0.747229, 0.711293, 0.635697, 0.524656, 0.384362, 0.222636,
- 0.048496, -0.128348, -0.298035, -0.451105, -0.579021, -0.674653,
- -0.732667, -0.749830, -0.688924, -0.594091, -0.474481, -0.340160,
- -0.201360, -0.067752, 0.052194, 0.151746, 0.226280, 0.273493,
- 0.293425, 0.288307, 0.262252, 0.220811, 0.170435, 0.117887,
- 0.069639, 0.031320
+float tick[TICKSIZE] = {
+ 0.175860, 0.341914, 0.488904, 0.608633, 0.694426, 0.741500,
+ 0.747229, 0.711293, 0.635697, 0.524656, 0.384362, 0.222636,
+ 0.048496, -0.128348, -0.298035, -0.451105, -0.579021, -0.674653,
+ -0.732667, -0.749830, -0.688924, -0.594091, -0.474481, -0.340160,
+ -0.201360, -0.067752, 0.052194, 0.151746, 0.226280, 0.273493,
+ 0.293425, 0.288307, 0.262252, 0.220811, 0.170435, 0.117887,
+ 0.069639, 0.031320
};
/* -------------------------------------------------------------------------- */
-void Mixer::init()
-{
- quanto = 1;
- docross = false;
- rewindWait = false;
- running = false;
- recording = false;
- ready = true;
- waitRec = 0;
- currentFrame = 0;
- bpm = DEFAULT_BPM;
- bars = DEFAULT_BARS;
- beats = DEFAULT_BEATS;
- quantize = DEFAULT_QUANTIZE;
- metronome = false;
-
- tickTracker = 0;
- tockTracker = 0;
- tickPlay = false;
- tockPlay = false;
-
- outVol = DEFAULT_OUT_VOL;
- inVol = DEFAULT_IN_VOL;
- peakOut = 0.0f;
- peakIn = 0.0f;
- inputTracker = 0;
-
- actualBeat = 0;
-
- midiTCstep = 0;
- midiTCrate = (G_Conf.samplerate / G_Conf.midiTCfps) * 2; // dealing with stereo vals
- midiTCframes = 0;
- midiTCseconds = 0;
- midiTCminutes = 0;
- midiTChours = 0;
-
- /* alloc virtual input channels. vChanInput malloc is done in
- * updateFrameBars, because of its variable size */
- /** TODO - set kernelAudio::realBufsize * 2 as private member */
-
- vChanInput = NULL;
- vChanInToOut = (float *) malloc(G_KernelAudio.realBufsize * 2 * sizeof(float));
-
- pthread_mutex_init(&mutex_recs, NULL);
- pthread_mutex_init(&mutex_chans, NULL);
- pthread_mutex_init(&mutex_plugins, NULL);
-
- updateFrameBars();
- rewind();
-}
-
+/* lineInRec
+Records from line in. */
-/* -------------------------------------------------------------------------- */
-
-
-Channel *Mixer::addChannel(int type)
+void lineInRec(float *inBuf, unsigned frame)
{
- Channel *ch;
- int bufferSize = G_KernelAudio.realBufsize * 2;
-
- if (type == CHANNEL_SAMPLE)
- ch = new SampleChannel(bufferSize, &G_MidiMap);
- else
- ch = new MidiChannel(bufferSize, &G_MidiMap);
+ if (!mh::hasArmedSampleChannels() || !kernelAudio::isInputEnabled() || !recording)
+ return;
-#ifdef WITH_VST
- ch->setPluginHost(&G_PluginHost);
-#endif
+ /* Delay comp: wait until waitRec reaches delayComp. WaitRec
+ * returns to 0 in mixerHandler, as soon as the recording ends */
- while (true) {
- int lockStatus = pthread_mutex_trylock(&mutex_chans);
- if (lockStatus == 0) {
- channels.push_back(ch);
- pthread_mutex_unlock(&mutex_chans);
- break;
- }
+ if (waitRec < conf::delayComp) {
+ waitRec += 2;
+ return;
}
- ch->index = getNewIndex();
- gu_log("[mixer] channel index=%d added, type=%d, total=%d\n", ch->index, ch->type, channels.size());
- return ch;
+ vChanInput[inputTracker] += inBuf[frame] * inVol;
+ vChanInput[inputTracker+1] += inBuf[frame+1] * inVol;
+ inputTracker += 2;
+ if (inputTracker >= clock::getTotalFrames())
+ inputTracker = 0;
}
/* -------------------------------------------------------------------------- */
+/* ProcessLineIn
+Computes line in peaks, plus handles "hear what you're playin'" thing. */
-int Mixer::getNewIndex()
+void processLineIn(float *inBuf, unsigned frame)
{
- /* always skip last channel: it's the last one just added */
-
- if (channels.size() == 1)
- return 0;
-
- int index = 0;
- for (unsigned i=0; i<channels.size()-1; i++) {
- if (channels.at(i)->index > index)
- index = channels.at(i)->index;
- }
- index += 1;
- return index;
-}
-
-
-/* -------------------------------------------------------------------------- */
+ if (!kernelAudio::isInputEnabled())
+ return;
+ /* input peak calculation (left chan only so far). */
-int Mixer::deleteChannel(Channel *ch)
-{
- int index = -1;
- for (unsigned i=0; i<channels.size(); i++) {
- if (channels.at(i) == ch) {
- index = i;
- break;
- }
- }
+ if (inBuf[frame] * inVol > peakIn)
+ peakIn = inBuf[frame] * inVol;
- if (index == -1) {
- gu_log("[Mixer::deleteChannel] unable to find Channel %d for deletion!\n", ch->index);
- return 0;
- }
+ /* "hear what you're playing" - process, copy and paste the input buffer
+ * onto the output buffer */
- int lockStatus;
- while (true) {
- lockStatus = pthread_mutex_trylock(&mutex_chans);
- if (lockStatus == 0) {
- channels.erase(channels.begin() + index);
- delete ch;
- pthread_mutex_unlock(&mutex_chans);
- return 1;
- }
- //else
- // gu_log("[mixer::deleteChannel] waiting for mutex...\n");
+ if (inToOut) {
+ vChanInToOut[frame] = inBuf[frame] * inVol;
+ vChanInToOut[frame+1] = inBuf[frame+1] * inVol;
}
}
/* -------------------------------------------------------------------------- */
+/* clearAllBuffers
+Cleans up every buffer, both in Mixer and in channels. */
-Channel *Mixer::getChannelByIndex(int index)
+void clearAllBuffers(float *outBuf, unsigned bufferSize)
{
+ memset(outBuf, 0, sizeof(float) * bufferSize); // out
+ memset(vChanInToOut, 0, sizeof(float) * bufferSize); // inToOut vChan
+
+ pthread_mutex_lock(&mutex_chans);
for (unsigned i=0; i<channels.size(); i++)
- if (channels.at(i)->index == index)
- return channels.at(i);
- gu_log("[mixer::getChannelByIndex] channel at index %d not found!\n", index);
- return NULL;
+ channels.at(i)->clear();
+ pthread_mutex_unlock(&mutex_chans);
}
/* -------------------------------------------------------------------------- */
+/* readActions
+Reads all recorded actions. */
-void Mixer::sendMIDIsync()
+void readActions(unsigned frame)
{
- if (G_Conf.midiSync == MIDI_SYNC_CLOCK_M) {
- if (currentFrame % (framesPerBeat/24) == 0)
- G_KernelMidi.send(MIDI_CLOCK, -1, -1);
- }
- else
- if (G_Conf.midiSync == MIDI_SYNC_MTC_M) {
-
- /* check if a new timecode frame has passed. If so, send MIDI TC
- * quarter frames. 8 quarter frames, divided in two branches:
- * 1-4 and 5-8. We check timecode frame's parity: if even, send
- * range 1-4, if odd send 5-8. */
-
- if (currentFrame % midiTCrate == 0) {
-
- /* frame low nibble
- * frame high nibble
- * seconds low nibble
- * seconds high nibble */
-
- if (midiTCframes % 2 == 0) {
- G_KernelMidi.send(MIDI_MTC_QUARTER, (midiTCframes & 0x0F) | 0x00, -1);
- G_KernelMidi.send(MIDI_MTC_QUARTER, (midiTCframes >> 4) | 0x10, -1);
- G_KernelMidi.send(MIDI_MTC_QUARTER, (midiTCseconds & 0x0F) | 0x20, -1);
- G_KernelMidi.send(MIDI_MTC_QUARTER, (midiTCseconds >> 4) | 0x30, -1);
- }
-
- /* minutes low nibble
- * minutes high nibble
- * hours low nibble
- * hours high nibble SMPTE frame rate */
-
- else {
- G_KernelMidi.send(MIDI_MTC_QUARTER, (midiTCminutes & 0x0F) | 0x40, -1);
- G_KernelMidi.send(MIDI_MTC_QUARTER, (midiTCminutes >> 4) | 0x50, -1);
- G_KernelMidi.send(MIDI_MTC_QUARTER, (midiTChours & 0x0F) | 0x60, -1);
- G_KernelMidi.send(MIDI_MTC_QUARTER, (midiTChours >> 4) | 0x70, -1);
- }
-
- midiTCframes++;
-
- /* check if total timecode frames are greater than timecode fps:
- * if so, a second has passed */
-
- if (midiTCframes > G_Conf.midiTCfps) {
- midiTCframes = 0;
- midiTCseconds++;
- if (midiTCseconds >= 60) {
- midiTCminutes++;
- midiTCseconds = 0;
- if (midiTCminutes >= 60) {
- midiTChours++;
- midiTCminutes = 0;
- }
- }
- //gu_log("%d:%d:%d:%d\n", midiTChours, midiTCminutes, midiTCseconds, midiTCframes);
+ pthread_mutex_lock(&mutex_recs);
+ for (unsigned i=0; i<recorder::frames.size(); i++) {
+ if (recorder::frames.at(i) == clock::getCurrentFrame()) {
+ for (unsigned j=0; j<recorder::global.at(i).size(); j++) {
+ int index = recorder::global.at(i).at(j)->chan;
+ Channel *ch = mh::getChannelByIndex(index);
+ ch->parseAction(recorder::global.at(i).at(j), frame,
+ clock::getCurrentFrame(), clock::getQuantize(), clock::isRunning());
}
+ break;
}
}
+ pthread_mutex_unlock(&mutex_recs);
}
/* -------------------------------------------------------------------------- */
+/* doQuantize
+Computes quantization on 'rewind' button and all channels. */
-void Mixer::sendMIDIrewind()
+void doQuantize(unsigned frame)
{
- midiTCframes = 0;
- midiTCseconds = 0;
- midiTCminutes = 0;
- midiTChours = 0;
-
- /* For cueing the slave to a particular start point, Quarter Frame
- * messages are not used. Instead, an MTC Full Frame message should
- * be sent. The Full Frame is a SysEx message that encodes the entire
- * SMPTE time in one message */
-
- if (G_Conf.midiSync == MIDI_SYNC_MTC_M) {
- G_KernelMidi.send(MIDI_SYSEX, 0x7F, 0x00); // send msg on channel 0
- G_KernelMidi.send(0x01, 0x01, 0x00); // hours 0
- G_KernelMidi.send(0x00, 0x00, 0x00); // mins, secs, frames 0
- G_KernelMidi.send(MIDI_EOX, -1, -1); // end of sysex
+ /* Nothing to do if quantizer disabled or a quanto has not passed yet. */
+
+ if (clock::getQuantize() == 0 || !clock::quantoHasPassed())
+ return;
+ if (rewindWait) {
+ rewindWait = false;
+ rewind();
}
+ pthread_mutex_lock(&mutex_chans);
+ for (unsigned i=0; i<channels.size(); i++)
+ channels.at(i)->quantize(i, frame);
+ pthread_mutex_unlock(&mutex_chans);
}
+
/* -------------------------------------------------------------------------- */
+/* sumChannels
+Sums channels, i.e. lets them add sample frames to their virtual channels.
+This is required for CHANNEL_SAMPLE only */
-int Mixer::masterPlay(void *outBuf, void *inBuf, unsigned bufferSize,
- double streamTime, RtAudioStreamStatus status, void *userData)
+void sumChannels(unsigned frame)
{
- return G_Mixer.__masterPlay(outBuf, inBuf, bufferSize);
+ pthread_mutex_lock(&mutex_chans);
+ for (unsigned k=0; k<channels.size(); k++) {
+ if (channels.at(k)->type == CHANNEL_SAMPLE)
+ static_cast<SampleChannel*>(channels.at(k))->sum(frame, clock::isRunning());
+ }
+ pthread_mutex_unlock(&mutex_chans);
}
/* -------------------------------------------------------------------------- */
+/* renderMetronome
+Generates metronome when needed and pastes it to the output buffer. */
-int Mixer::__masterPlay(void *_outBuf, void *_inBuf, unsigned bufferSize)
+void renderMetronome(float *outBuf, unsigned frame)
{
- if (!ready)
- return 0;
-
- float *outBuf = (float *) _outBuf;
- float *inBuf = G_KernelAudio.inputEnabled ? (float *) _inBuf : nullptr;
- bufferSize *= 2; // stereo
- peakOut = 0.0f; // reset peak calculator
- peakIn = 0.0f; // reset peak calculator
-
- clearAllBuffers(outBuf, bufferSize);
-
- for (unsigned j=0; j<bufferSize; j+=2) {
- processLineIn(inBuf, j);
- if (running) {
- lineInRec(inBuf, j);
- doQuantize(j);
- testBar(j);
- testFirstBeat(j);
- readActions(j);
- currentFrame += 2;
- testLastBeat(); // this test must be the last one
- sendMIDIsync();
+ if (tockPlay) {
+ outBuf[frame] += tock[tockTracker];
+ outBuf[frame+1] += tock[tockTracker];
+ tockTracker++;
+ if (tockTracker >= TICKSIZE-1) {
+ tockPlay = false;
+ tockTracker = 0;
}
- sumChannels(j);
}
-
- renderIO(outBuf, inBuf);
-
- /* post processing */
-
- for (unsigned j=0; j<bufferSize; j+=2) {
- finalizeOutput(outBuf, j);
- if (G_Conf.limitOutput)
- limitOutput(outBuf, j);
- computePeak(outBuf, j);
- renderMetronome(outBuf, j);
+ if (tickPlay) {
+ outBuf[frame] += tick[tickTracker];
+ outBuf[frame+1] += tick[tickTracker];
+ tickTracker++;
+ if (tickTracker >= TICKSIZE-1) {
+ tickPlay = false;
+ tickTracker = 0;
+ }
}
-
- return 0;
}
/* -------------------------------------------------------------------------- */
+/* renderIO
+Final processing stage. Take each channel and process it (i.e. copy its
+content to the output buffer). Process plugins too, if any. */
-void Mixer::updateFrameBars()
+void renderIO(float *outBuf, float *inBuf)
{
- /* seconds ....... total time of play (in seconds) of the whole
- * sequencer. 60 / bpm == how many seconds lasts one bpm
- * totalFrames ... number of frames in the whole sequencer, x2 because
- * it's stereo
- * framesPerBar .. n. of frames within a bar
- * framesPerBeat . n. of frames within a beat */
-
- float seconds = (60.0f / bpm) * beats;
- totalFrames = G_Conf.samplerate * seconds * 2;
- framesPerBar = totalFrames / bars;
- framesPerBeat = totalFrames / beats;
- framesInSequencer = framesPerBeat * MAX_BEATS;
-
- /* big troubles if frames are odd. */
-
- if (totalFrames % 2 != 0)
- totalFrames--;
- if (framesPerBar % 2 != 0)
- framesPerBar--;
- if (framesPerBeat % 2 != 0)
- framesPerBeat--;
-
- updateQuanto();
-
- /* realloc input virtual channel, if not NULL. TotalFrames is changed! */
+ pthread_mutex_lock(&mutex_chans);
+ for (unsigned k=0; k<channels.size(); k++)
+ channels.at(k)->process(outBuf, inBuf);
+ pthread_mutex_unlock(&mutex_chans);
- if (vChanInput != NULL)
- free(vChanInput);
- vChanInput = (float*) malloc(totalFrames * sizeof(float));
- if (!vChanInput)
- gu_log("[Mixer] vChanInput realloc error!\n");
+#ifdef WITH_VST
+ pthread_mutex_lock(&mutex_plugins);
+ pluginHost::processStack(outBuf, pluginHost::MASTER_OUT);
+ pluginHost::processStack(vChanInToOut, pluginHost::MASTER_IN);
+ pthread_mutex_unlock(&mutex_plugins);
+#endif
}
/* -------------------------------------------------------------------------- */
+/* limitOutput
+Applies a very dumb hard limiter. */
-int Mixer::close()
+void limitOutput(float *outBuf, unsigned frame)
{
- running = false;
- while (channels.size() > 0)
- deleteChannel(channels.at(0));
+ if (outBuf[frame] > 1.0f)
+ outBuf[frame] = 1.0f;
+ else
+ if (outBuf[frame] < -1.0f)
+ outBuf[frame] = -1.0f;
- if (vChanInput) {
- free(vChanInput);
- vChanInput = NULL;
- }
- if (vChanInToOut) {
- free(vChanInToOut);
- vChanInToOut = NULL;
- }
- return 1;
+ if (outBuf[frame+1] > 1.0f)
+ outBuf[frame+1] = 1.0f;
+ else
+ if (outBuf[frame+1] < -1.0f)
+ outBuf[frame+1] = -1.0f;
}
/* -------------------------------------------------------------------------- */
+/* computePeak */
-bool Mixer::isSilent()
+void computePeak(float *outBuf, unsigned frame)
{
- for (unsigned i=0; i<channels.size(); i++)
- if (channels.at(i)->status == STATUS_PLAY)
- return false;
- return true;
+ /* TODO it takes into account only left channel so far! */
+ if (outBuf[frame] > peakOut)
+ peakOut = outBuf[frame];
}
/* -------------------------------------------------------------------------- */
+/* finalizeOutput
+Last touches after the output has been rendered: apply inToOut if any, apply
+output volume. */
-void Mixer::rewind()
+void finalizeOutput(float *outBuf, unsigned frame)
{
- currentFrame = 0;
- actualBeat = 0;
-
- if (running)
- for (unsigned i=0; i<channels.size(); i++)
- channels.at(i)->rewind();
+ /* merge vChanInToOut, if enabled */
- sendMIDIrewind();
+ if (inToOut) {
+ outBuf[frame] += vChanInToOut[frame];
+ outBuf[frame+1] += vChanInToOut[frame+1];
+ }
+ outBuf[frame] *= outVol;
+ outBuf[frame+1] *= outVol;
}
/* -------------------------------------------------------------------------- */
+/* test*
+Checks if the sequencer has reached a specific point (bar, first beat or
+last frame). */
-void Mixer::updateQuanto()
+void testBar(unsigned frame)
{
- /* big troubles if frames are odd. */
-
- if (quantize != 0)
- quanto = framesPerBeat / quantize;
- if (quanto % 2 != 0)
- quanto++;
-}
-
-
-/* -------------------------------------------------------------------------- */
+ if (!clock::isOnBar())
+ return;
+ if (metronome)
+ tickPlay = true;
-bool Mixer::hasLogicalSamples()
-{
- for (unsigned i=0; i<channels.size(); i++)
- if (channels.at(i)->type == CHANNEL_SAMPLE)
- if (((SampleChannel*)channels.at(i))->wave)
- if (((SampleChannel*)channels.at(i))->wave->isLogical)
- return true;
- return false;
+ pthread_mutex_lock(&mutex_chans);
+ for (unsigned k=0; k<channels.size(); k++)
+ channels.at(k)->onBar(frame);
+ pthread_mutex_unlock(&mutex_chans);
}
/* -------------------------------------------------------------------------- */
-bool Mixer::hasEditedSamples()
+void testFirstBeat(unsigned frame)
{
- for (unsigned i=0; i<channels.size(); i++)
- if (channels.at(i)->type == CHANNEL_SAMPLE)
- if (((SampleChannel*)channels.at(i))->wave)
- if (((SampleChannel*)channels.at(i))->wave->isEdited)
- return true;
- return false;
+ if (!clock::isOnFirstBeat())
+ return;
+ pthread_mutex_lock(&mutex_chans);
+ for (unsigned k=0; k<channels.size(); k++)
+ channels.at(k)->onZero(frame, conf::recsStopOnChanHalt);
+ pthread_mutex_unlock(&mutex_chans);
}
/* -------------------------------------------------------------------------- */
-void Mixer::mergeVirtualInput()
+void testLastBeat()
{
- for (unsigned i=0; i<channels.size(); i++) {
- if (channels.at(i)->type == CHANNEL_MIDI)
- continue;
- SampleChannel *ch = (SampleChannel*) channels.at(i);
- if (ch->armed)
- memcpy(ch->wave->data, vChanInput, totalFrames * sizeof(float));
- }
- memset(vChanInput, 0, totalFrames * sizeof(float)); // clear vchan
+ if (clock::isOnBeat())
+ if (metronome && !tickPlay)
+ tockPlay = true;
}
-
-/* -------------------------------------------------------------------------- */
-
-
-void Mixer::lineInRec(float *inBuf, unsigned frame)
-{
- if (!mh_hasArmedSampleChannels() || !G_KernelAudio.inputEnabled || !recording)
- return;
-
- /* Delay comp: wait until waitRec reaches delayComp. WaitRec
- * returns to 0 in mixerHandler, as soon as the recording ends */
-
- if (waitRec < G_Conf.delayComp) {
- waitRec += 2;
- return;
- }
-
- vChanInput[inputTracker] += inBuf[frame] * inVol;
- vChanInput[inputTracker+1] += inBuf[frame+1] * inVol;
- inputTracker += 2;
- if (inputTracker >= totalFrames)
- inputTracker = 0;
-}
+}; // {anonymous}
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
-void Mixer::processLineIn(float *inBuf, unsigned frame)
-{
- if (!G_KernelAudio.inputEnabled)
- return;
+std::vector<Channel*> channels;
- /* input peak calculation (left chan only so far). */
+bool recording = false; // is recording something?
+bool ready = true;
+float *vChanInput = nullptr; // virtual channel for recording
+float *vChanInToOut = nullptr; // virtual channel in->out bridge (hear what you're playin)
+float outVol = G_DEFAULT_OUT_VOL;
+float inVol = G_DEFAULT_IN_VOL;
+float peakOut = 0.0f;
+float peakIn = 0.0f;
+bool metronome = false;
+int waitRec = 0; // delayComp guard
+bool docross = false; // crossfade guard
+bool rewindWait = false; // rewind guard, if quantized
- if (inBuf[frame] * inVol > peakIn)
- peakIn = inBuf[frame] * inVol;
+int tickTracker, tockTracker = 0;
+bool tickPlay, tockPlay = false; // 1 = play, 0 = stop
- /* "hear what you're playing" - process, copy and paste the input buffer
- * onto the output buffer */
-
- if (inToOut) {
- vChanInToOut[frame] = inBuf[frame] * inVol;
- vChanInToOut[frame+1] = inBuf[frame+1] * inVol;
- }
-}
+/* inputTracker
+ * position of the sample in the input side (recording) */
+int inputTracker = 0;
-/* -------------------------------------------------------------------------- */
+/* inToOut
+ * copy, process and paste the input into the output, in order to
+ * obtain a "hear what you're playing" feature. */
+bool inToOut = false;
-void Mixer::readActions(unsigned frame)
-{
- pthread_mutex_lock(&mutex_recs);
- for (unsigned i=0; i<G_Recorder.frames.size(); i++) {
- if (G_Recorder.frames.at(i) == currentFrame) {
- for (unsigned j=0; j<G_Recorder.global.at(i).size(); j++) {
- int index = G_Recorder.global.at(i).at(j)->chan;
- Channel *ch = getChannelByIndex(index);
- ch->parseAction(G_Recorder.global.at(i).at(j), frame, currentFrame, quantize, running);
- }
- break;
- }
- }
- pthread_mutex_unlock(&mutex_recs);
-}
+pthread_mutex_t mutex_recs;
+pthread_mutex_t mutex_chans;
+pthread_mutex_t mutex_plugins;
/* -------------------------------------------------------------------------- */
-void Mixer::doQuantize(unsigned frame)
+void init(int framesInSeq, int audioBufferSize)
{
- if (quantize < 0 || quanto <= 0) // if quantizer disabled
- return;
- if (currentFrame % (quanto) != 0) // if a quanto has not passed yet
- return;
-
- if (rewindWait) {
- rewindWait = false;
- rewind();
- }
- pthread_mutex_lock(&mutex_chans);
- for (unsigned i=0; i<channels.size(); i++)
- channels.at(i)->quantize(i, frame, this); // j == localFrame
- pthread_mutex_unlock(&mutex_chans);
-}
-
-
-/* -------------------------------------------------------------------------- */
+ /* Allocate virtual input channels. vChanInput relies on clock::totalFrames:
+ it has variable size. */
+ if (vChanInput != nullptr)
+ free(vChanInput);
+ vChanInput = (float*) malloc(framesInSeq * sizeof(float));
+ if (vChanInToOut != nullptr)
+ free(vChanInToOut);
+ vChanInToOut = (float*) malloc(audioBufferSize * 2 * sizeof(float));
-void Mixer::testBar(unsigned frame)
-{
- if (currentFrame % framesPerBar != 0 || currentFrame == 0)
- return;
+ pthread_mutex_init(&mutex_recs, nullptr);
+ pthread_mutex_init(&mutex_chans, nullptr);
+ pthread_mutex_init(&mutex_plugins, nullptr);
- if (metronome)
- tickPlay = true;
-
- pthread_mutex_lock(&mutex_chans);
- for (unsigned k=0; k<channels.size(); k++)
- channels.at(k)->onBar(frame);
- pthread_mutex_unlock(&mutex_chans);
+ rewind();
}
/* -------------------------------------------------------------------------- */
-void Mixer::testFirstBeat(unsigned frame)
+int masterPlay(void *_outBuf, void *_inBuf, unsigned bufferSize,
+ double streamTime, RtAudioStreamStatus status, void *userData)
{
- if (currentFrame != 0)
- return;
- pthread_mutex_lock(&mutex_chans);
- for (unsigned k=0; k<channels.size(); k++)
- channels.at(k)->onZero(frame, G_Conf.recsStopOnChanHalt);
- pthread_mutex_unlock(&mutex_chans);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
+ if (!ready)
+ return 0;
-void Mixer::testLastBeat()
-{
- /* if currentFrame > totalFrames the sequencer returns to frame 0,
- * beat 0. This must be the last operation. */
+#ifdef __linux__
+ clock::recvJackSync();
+#endif
- if (currentFrame > totalFrames) {
- currentFrame = 0;
- actualBeat = 0;
- }
- else
- if (currentFrame % framesPerBeat == 0 && currentFrame > 0) {
- actualBeat++;
+ float *outBuf = (float*) _outBuf;
+ float *inBuf = kernelAudio::isInputEnabled() ? (float*) _inBuf : nullptr;
+ bufferSize *= 2; // stereo
+ peakOut = 0.0f; // reset peak calculator
+ peakIn = 0.0f; // reset peak calculator
- /* avoid tick and tock to overlap when a new bar has passed (which
- * is also a beat) */
+ clearAllBuffers(outBuf, bufferSize);
- if (metronome && !tickPlay)
- tockPlay = true;
+ for (unsigned j=0; j<bufferSize; j+=2) {
+ processLineIn(inBuf, j);
+ if (clock::isRunning()) {
+ lineInRec(inBuf, j);
+ doQuantize(j);
+ testBar(j);
+ testFirstBeat(j);
+ readActions(j);
+ clock::incrCurrentFrame();
+ testLastBeat(); // this test must be the last one
+ clock::sendMIDIsync();
+ }
+ sumChannels(j);
}
-}
-
-/* -------------------------------------------------------------------------- */
+ renderIO(outBuf, inBuf);
+ /* post processing */
-void Mixer::sumChannels(unsigned frame)
-{
- pthread_mutex_lock(&mutex_chans);
- for (unsigned k=0; k<channels.size(); k++) {
- if (channels.at(k)->type == CHANNEL_SAMPLE)
- ((SampleChannel*)channels.at(k))->sum(frame, running);
+ for (unsigned j=0; j<bufferSize; j+=2) {
+ finalizeOutput(outBuf, j);
+ if (conf::limitOutput)
+ limitOutput(outBuf, j);
+ computePeak(outBuf, j);
+ renderMetronome(outBuf, j);
}
- pthread_mutex_unlock(&mutex_chans);
-}
-
-/* -------------------------------------------------------------------------- */
-
-
-void Mixer::renderMetronome(float *outBuf, unsigned frame)
-{
- if (tockPlay) {
- outBuf[frame] += tock[tockTracker];
- outBuf[frame+1] += tock[tockTracker];
- tockTracker++;
- if (tockTracker >= TICKSIZE-1) {
- tockPlay = false;
- tockTracker = 0;
- }
- }
- if (tickPlay) {
- outBuf[frame] += tick[tickTracker];
- outBuf[frame+1] += tick[tickTracker];
- tickTracker++;
- if (tickTracker >= TICKSIZE-1) {
- tickPlay = false;
- tickTracker = 0;
- }
- }
+ return 0;
}
/* -------------------------------------------------------------------------- */
-void Mixer::renderIO(float *outBuf, float *inBuf)
+int close()
{
- pthread_mutex_lock(&mutex_chans);
- for (unsigned k=0; k<channels.size(); k++)
- channels.at(k)->process(outBuf, inBuf);
- pthread_mutex_unlock(&mutex_chans);
+ clock::stop();
+ while (channels.size() > 0)
+ mh::deleteChannel(channels.at(0));
-#ifdef WITH_VST
- pthread_mutex_lock(&mutex_plugins);
- G_PluginHost.processStack(outBuf, PluginHost::MASTER_OUT);
- G_PluginHost.processStack(vChanInToOut, PluginHost::MASTER_IN);
- pthread_mutex_unlock(&mutex_plugins);
-#endif
+ if (vChanInput) {
+ free(vChanInput);
+ vChanInput = nullptr;
+ }
+ if (vChanInToOut) {
+ free(vChanInToOut);
+ vChanInToOut = nullptr;
+ }
+ return 1;
}
/* -------------------------------------------------------------------------- */
-void Mixer::computePeak(float *outBuf, unsigned frame)
+bool isSilent()
{
- /* TODO it takes into account only left channel so far! */
- if (outBuf[frame] > peakOut)
- peakOut = outBuf[frame];
+ for (unsigned i=0; i<channels.size(); i++)
+ if (channels.at(i)->status == STATUS_PLAY)
+ return false;
+ return true;
}
/* -------------------------------------------------------------------------- */
-void Mixer::limitOutput(float *outBuf, unsigned frame)
+void rewind()
{
- if (outBuf[frame] > 1.0f)
- outBuf[frame] = 1.0f;
- else
- if (outBuf[frame] < -1.0f)
- outBuf[frame] = -1.0f;
-
- if (outBuf[frame+1] > 1.0f)
- outBuf[frame+1] = 1.0f;
- else
- if (outBuf[frame+1] < -1.0f)
- outBuf[frame+1] = -1.0f;
+ clock::rewind();
+ if (clock::isRunning())
+ for (unsigned i=0; i<channels.size(); i++)
+ channels.at(i)->rewind();
}
/* -------------------------------------------------------------------------- */
-void Mixer::finalizeOutput(float *outBuf, unsigned frame)
+void mergeVirtualInput()
{
- /* merge vChanInToOut, if enabled */
-
- if (inToOut) {
- outBuf[frame] += vChanInToOut[frame];
- outBuf[frame+1] += vChanInToOut[frame+1];
+ for (unsigned i=0; i<channels.size(); i++) {
+ if (channels.at(i)->type == CHANNEL_MIDI)
+ continue;
+ SampleChannel *ch = static_cast<SampleChannel*>(channels.at(i));
+ if (ch->armed)
+ memcpy(ch->wave->data, vChanInput, clock::getTotalFrames() * sizeof(float));
}
- outBuf[frame] *= outVol;
- outBuf[frame+1] *= outVol;
+ memset(vChanInput, 0, clock::getTotalFrames() * sizeof(float)); // clear vchan
}
-/* -------------------------------------------------------------------------- */
-
-
-void Mixer::clearAllBuffers(float *outBuf, unsigned bufferSize)
-{
- memset(outBuf, 0, sizeof(float) * bufferSize); // out
- memset(vChanInToOut, 0, sizeof(float) * bufferSize); // inToOut vChan
-
- pthread_mutex_lock(&mutex_chans);
- for (unsigned i=0; i<channels.size(); i++)
- channels.at(i)->clear();
- pthread_mutex_unlock(&mutex_chans);
-}
+}}}; // giada::m::mixer::
*
* Giada - Your Hardcore Loopmachine
*
- * mixer
- *
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
* -------------------------------------------------------------------------- */
-#ifndef MIXER_H
-#define MIXER_H
+#ifndef G_MIXER_H
+#define G_MIXER_H
#include <pthread.h>
#include <vector>
-#include "kernelAudio.h"
-
-
-class Mixer
-{
-public:
-
- Mixer();
-
- void init();
- int close();
-
- /* addChannel
- * add a new channel without any wave inside of it. */
-
- class Channel *addChannel(int type);
-
- /* deleteChannel
- * completely remove a channel from the stack. */
-
- int deleteChannel(class Channel *ch);
-
- /* masterPlay
- * core method (callback) */
-
- static int masterPlay(void *outBuf, void *inBuf, unsigned bufferSize,
- double streamTime, RtAudioStreamStatus status, void *userData);
- int __masterPlay(void *outBuf, void *inBuf, unsigned bufferSize);
-
- /* updateFrameBars
- * updates bpm, frames, beats and so on. */
-
- void updateFrameBars();
-
- /* isSilent
- * is mixer silent? */
-
- bool isSilent();
-
- /* rewind
- * rewind sequencer to sample 0. */
-
- void rewind();
-
- /* updateQuanto
- * recomputes the quanto between two quantizations */
-
- void updateQuanto();
-
- /* hasLogicalSamples
- * true if 1 or more samples are logical (memory only, such as takes) */
-
- bool hasLogicalSamples();
-
- /* hasEditedSamples
- * true if 1 or more samples was edited via gEditor */
-
- bool hasEditedSamples();
-
- /* mergeVirtualInput
- * memcpy the virtual channel input in the channel designed for input
- * recording. Called by mixerHandler on stopInputRec() */
-
- void mergeVirtualInput();
-
- /* getChannelByIndex
- * return channel with given index 'i'. */
-
- Channel *getChannelByIndex(int i);
-
- /* getLastChannel
- * Return last channel in the stack. */
+#include "../deps/rtaudio-mod/RtAudio.h"
- inline Channel* getLastChannel() { return channels.back(); }
+class Channel;
- /* ---------------------------------------------------------------- */
+namespace giada {
+namespace m {
+namespace mixer
+{
+void init(int framesInSeq, int audioBufferSize);
+int close();
- enum { // const - what to do when a fadeout ends
- DO_STOP = 0x01,
- DO_MUTE = 0x02,
- DO_MUTE_I = 0x04
- };
-
- enum { // const - fade types
- FADEOUT = 0x01,
- XFADE = 0x02
- };
-
- std::vector<class Channel*> channels;
-
- bool running;
- bool recording; // is recording something?
- bool ready;
- float *vChanInput; // virtual channel for recording
- float *vChanInToOut; // virtual channel in->out bridge (hear what you're playin)
- int frameSize;
- float outVol;
- float inVol;
- float peakOut;
- float peakIn;
- int quanto;
- char quantize;
- bool metronome;
- float bpm;
- int bars;
- int beats;
- int waitRec; // delayComp guard
-
- bool docross; // crossfade guard
- bool rewindWait; // rewind guard, if quantized
-
- int framesPerBar; // frames in one bar
- int framesPerBeat; // frames in one beat
- int framesInSequencer; // frames in the whole sequencer
- int totalFrames; // frames in the selected range (e.g. 4/4)
- int currentFrame;
- int actualBeat;
-
-#define TICKSIZE 38
- static float tock[TICKSIZE];
- static float tick[TICKSIZE];
- int tickTracker, tockTracker;
- bool tickPlay, tockPlay; // 1 = play, 0 = stop
-
- /* inputTracker
- * position of the sample in the input side (recording) */
-
- int inputTracker;
-
- /* inToOut
- * copy, process and paste the input into the output, in order to
- * obtain a "hear what you're playing" feature. */
-
- bool inToOut;
-
- pthread_mutex_t mutex_recs;
- pthread_mutex_t mutex_chans;
- pthread_mutex_t mutex_plugins;
-
-private:
-
- int midiTCstep; // part of MTC to send (0 to 7)
- int midiTCrate; // send MTC data every midiTCrate frames
- int midiTCframes;
- int midiTCseconds;
- int midiTCminutes;
- int midiTChours;
-
- /* getNewIndex
- * compute new index value for new channels */
-
- int getNewIndex();
-
- /* sendMIDIsync
- * generate MIDI sync output data */
-
- void sendMIDIsync();
-
- /* sendMIDIrewind
- * rewind timecode to beat 0 and also send a MTC full frame to cue
- * the slave */
-
- void sendMIDIrewind();
-
- /* lineInRec
- Records from line in. */
-
- void lineInRec(float *inBuf, unsigned frame);
-
- /* ProcessLineIn
- Computes line in peaks, plus handles "hear what you're playin'" thing. */
-
- void processLineIn(float *inBuf, unsigned frame);
-
- /* clearAllBuffers
- Cleans up every buffer, both in Mixer and in channels. */
+/* masterPlay
+ * core method (callback) */
- void clearAllBuffers(float *outBuf, unsigned bufferSize);
+int masterPlay(void *outBuf, void *inBuf, unsigned bufferSize, double streamTime,
+ RtAudioStreamStatus status, void *userData);
- /* readActions
- Reads all recorded actions. */
+/* isSilent
+ * is mixer silent? */
- void readActions(unsigned frame);
+bool isSilent();
- /* doQuantize
- Computes quantization on 'rewind' button and all channels. */
+/* rewind
+ * rewind sequencer to sample 0. */
- void doQuantize(unsigned frame);
+void rewind();
- /* sumChannels
- Sums channels, i.e. lets them add sample frames to their virtual channels.
- This is required for CHANNEL_SAMPLE only */
+/* mergeVirtualInput
+ * memcpy the virtual channel input in the channel designed for input
+ * recording. Called by mixerHandler on stopInputRec() */
- void sumChannels(unsigned frame);
+void mergeVirtualInput();
- /* renderMetronome
- Generates metronome when needed and pastes it to the output buffer. */
+enum { // const - what to do when a fadeout ends
+ DO_STOP = 0x01,
+ DO_MUTE = 0x02,
+ DO_MUTE_I = 0x04
+};
- void renderMetronome(float *outBuf, unsigned frame);
+enum { // const - fade types
+ FADEOUT = 0x01,
+ XFADE = 0x02
+};
- /* renderIO
- Final processing stage. Take each channel and process it (i.e. copy its
- content to the output buffer). Process plugins too, if any. */
+extern std::vector<Channel*> channels;
- void renderIO(float *outBuf, float *inBuf);
+extern bool recording; // is recording something?
+extern bool ready;
+extern float *vChanInput; // virtual channel for recording
+extern float *vChanInToOut; // virtual channel in->out bridge (hear what you're playin)
+extern int frameSize;
+extern float outVol;
+extern float inVol;
+extern float peakOut;
+extern float peakIn;
+extern bool metronome;
+extern int waitRec; // delayComp guard
+extern bool docross; // crossfade guard
+extern bool rewindWait; // rewind guard, if quantized
- /* limitOutput
- Applies a very dumb hard limiter. */
+extern int tickTracker, tockTracker;
+extern bool tickPlay, tockPlay; // 1 = play, 0 = stop
- void limitOutput(float *outBuf, unsigned frame);
+/* inputTracker
+ * position of the sample in the input side (recording) */
- /* computePeak */
+extern int inputTracker;
- void computePeak(float *outBuf, unsigned frame);
+/* inToOut
+ * copy, process and paste the input into the output, in order to
+ * obtain a "hear what you're playing" feature. */
- /* finalizeOutput
- Last touches after the output has been rendered: apply inToOut if any, apply
- output volume. */
+extern bool inToOut;
- void finalizeOutput(float *outBuf, unsigned frame);
+extern pthread_mutex_t mutex_recs;
+extern pthread_mutex_t mutex_chans;
+extern pthread_mutex_t mutex_plugins;
- /* test*
- Checks if the sequencer has reached a specific point (bar, first beat or
- last frame). */
+}}} // giada::m::mixer::;
- void testBar(unsigned frame);
- void testFirstBeat(unsigned frame);
- void testLastBeat();
-};
#endif
*
* Giada - Your Hardcore Loopmachine
*
- * mixerHandler
- *
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
* -------------------------------------------------------------------------- */
-#if defined(__linux__)
- #include <jack/jack.h>
- #include <jack/intclient.h>
- #include <jack/transport.h>
-#endif
-
#include <vector>
#include "../utils/fs.h"
#include "../utils/string.h"
#include "plugin.h"
#include "waveFx.h"
#include "conf.h"
-#include "patch_DEPR_.h"
#include "patch.h"
#include "recorder.h"
+#include "clock.h"
#include "channel.h"
+#include "kernelAudio.h"
+#include "midiMapConf.h"
#include "sampleChannel.h"
+#include "midiChannel.h"
#include "wave.h"
-extern Mixer G_Mixer;
-extern Patch_DEPR_ G_Patch_DEPR_;
-extern Patch G_Patch;
-extern Conf G_Conf;
-
-#ifdef WITH_VST
-extern PluginHost G_PluginHost;
-#endif
-
-
using std::vector;
+using std::string;
+namespace giada {
+namespace m {
+namespace mh
+{
+namespace
+{
#ifdef WITH_VST
-static int __mh_readPatchPlugins__(vector<Patch::plugin_t> *list, int type)
+int readPatchPlugins(vector<patch::plugin_t> *list, int type)
{
int ret = 1;
for (unsigned i=0; i<list->size(); i++) {
- Patch::plugin_t *ppl = &list->at(i);
+ patch::plugin_t *ppl = &list->at(i);
// TODO use glue_addPlugin()
- Plugin *plugin = G_PluginHost.addPlugin(ppl->path.c_str(), type,
- &G_Mixer.mutex_plugins, NULL);
- if (plugin != NULL) {
+ Plugin *plugin = pluginHost::addPlugin(ppl->path.c_str(), type,
+ &mixer::mutex_plugins, nullptr);
+ if (plugin != nullptr) {
plugin->setBypass(ppl->bypass);
for (unsigned j=0; j<ppl->params.size(); j++)
plugin->setParameter(j, ppl->params.at(j));
#endif
-/* -------------------------------------------------------------------------- */
+/* ------------------------------------------------------------------------ */
-void mh_stopSequencer()
+int getNewChanIndex()
{
- G_Mixer.running = false;
- for (unsigned i=0; i<G_Mixer.channels.size(); i++)
- G_Mixer.channels.at(i)->stopBySeq(G_Conf.chansStopOnSeqHalt);
+ /* always skip last channel: it's the last one just added */
+
+ if (mixer::channels.size() == 1)
+ return 0;
+
+ int index = 0;
+ for (unsigned i=0; i<mixer::channels.size()-1; i++) {
+ if (mixer::channels.at(i)->index > index)
+ index = mixer::channels.at(i)->index;
+ }
+ index += 1;
+ return index;
}
+}; // {anonymous}
+
+
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
-bool mh_uniqueSolo(Channel *ch)
+bool uniqueSampleName(SampleChannel *ch, const string &name)
{
- int solos = 0;
- for (unsigned i=0; i<G_Mixer.channels.size(); i++) {
- Channel *ch = G_Mixer.channels.at(i);
- if (ch->solo) solos++;
- if (solos > 1) return false;
+ for (unsigned i=0; i<mixer::channels.size(); i++) {
+ if (ch == mixer::channels.at(i)) // skip itself
+ continue;
+ if (mixer::channels.at(i)->type != CHANNEL_SAMPLE)
+ continue;
+ SampleChannel *other = (SampleChannel*) mixer::channels.at(i);
+ if (other->wave != nullptr && name == other->wave->name)
+ return false;
}
return true;
}
/* -------------------------------------------------------------------------- */
-/** TODO - revision needed: mh should not call glue_addChannel */
+Channel *addChannel(int type)
+{
+ Channel *ch;
+ int bufferSize = kernelAudio::getRealBufSize() * 2;
+
+ if (type == CHANNEL_SAMPLE)
+ ch = new SampleChannel(bufferSize, conf::inputMonitorDefaultOn);
+ else
+ ch = new MidiChannel(bufferSize);
+
+ while (true) {
+ if (pthread_mutex_trylock(&mixer::mutex_chans) != 0)
+ continue;
+ mixer::channels.push_back(ch);
+ pthread_mutex_unlock(&mixer::mutex_chans);
+ break;
+ }
+
+ ch->index = getNewChanIndex();
+ gu_log("[addChannel] channel index=%d added, type=%d, total=%d\n",
+ ch->index, ch->type, mixer::channels.size());
+ return ch;
+}
-void mh_loadPatch_DEPR_(bool isProject, const char *projPath)
+
+/* -------------------------------------------------------------------------- */
+
+
+int deleteChannel(Channel *ch)
{
- G_Mixer.init();
- G_Mixer.ready = false; // put it in wait mode
-
- int numChans = G_Patch_DEPR_.getNumChans();
- for (int i=0; i<numChans; i++) {
- Channel *ch = glue_addChannel(G_Patch_DEPR_.getColumn(i), G_Patch_DEPR_.getType(i));
- string projectPath = projPath; // safe
- string samplePath = isProject ? projectPath + G_SLASH + G_Patch_DEPR_.getSamplePath(i) : "";
- ch->readPatch_DEPR_(samplePath.c_str(), i, &G_Patch_DEPR_, G_Conf.samplerate,
- G_Conf.rsmpQuality);
+ int index = -1;
+ for (unsigned i=0; i<mixer::channels.size(); i++) {
+ if (mixer::channels.at(i) == ch) {
+ index = i;
+ break;
+ }
+ }
+ if (index == -1) {
+ gu_log("[deleteChannel] unable to find channel %d for deletion!\n", ch->index);
+ return 0;
}
- G_Mixer.outVol = G_Patch_DEPR_.getOutVol();
- G_Mixer.inVol = G_Patch_DEPR_.getInVol();
- G_Mixer.bpm = G_Patch_DEPR_.getBpm();
- G_Mixer.bars = G_Patch_DEPR_.getBars();
- G_Mixer.beats = G_Patch_DEPR_.getBeats();
- G_Mixer.quantize = G_Patch_DEPR_.getQuantize();
- G_Mixer.metronome = G_Patch_DEPR_.getMetronome();
- G_Patch_DEPR_.lastTakeId = G_Patch_DEPR_.getLastTakeId();
- G_Patch_DEPR_.samplerate = G_Patch_DEPR_.getSamplerate();
-
- /* rewind and update frames in Mixer (it's vital) */
-
- G_Mixer.rewind();
- G_Mixer.updateFrameBars();
- G_Mixer.ready = true;
+ while (true) {
+ if (pthread_mutex_trylock(&mixer::mutex_chans) != 0)
+ continue;
+ mixer::channels.erase(mixer::channels.begin() + index);
+ delete ch;
+ pthread_mutex_unlock(&mixer::mutex_chans);
+ return 1;
+ }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+Channel *getChannelByIndex(int index)
+{
+ for (unsigned i=0; i<mixer::channels.size(); i++)
+ if (mixer::channels.at(i)->index == index)
+ return mixer::channels.at(i);
+ gu_log("[getChannelByIndex] channel at index %d not found!\n", index);
+ return nullptr;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+bool hasLogicalSamples()
+{
+ for (unsigned i=0; i<mixer::channels.size(); i++) {
+ if (mixer::channels.at(i)->type != CHANNEL_SAMPLE)
+ continue;
+ SampleChannel *ch = static_cast<SampleChannel*>(mixer::channels.at(i));
+ if (ch->wave && ch->wave->isLogical)
+ return true;
+ }
+ return false;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+bool hasEditedSamples()
+{
+ for (unsigned i=0; i<mixer::channels.size(); i++)
+ {
+ if (mixer::channels.at(i)->type != CHANNEL_SAMPLE)
+ continue;
+ SampleChannel *ch = static_cast<SampleChannel*>(mixer::channels.at(i));
+ if (ch->wave && ch->wave->isEdited)
+ return true;
+ }
+ return false;
}
/* -------------------------------------------------------------------------- */
-void mh_readPatch()
+void stopSequencer()
{
- G_Mixer.ready = false;
+ clock::stop();
+ for (unsigned i=0; i<mixer::channels.size(); i++)
+ mixer::channels.at(i)->stopBySeq(conf::chansStopOnSeqHalt);
+}
- G_Mixer.outVol = G_Patch.masterVolOut;
- G_Mixer.inVol = G_Patch.masterVolIn;
- G_Mixer.bpm = G_Patch.bpm;
- G_Mixer.bars = G_Patch.bars;
- G_Mixer.beats = G_Patch.beats;
- G_Mixer.quantize = G_Patch.quantize;
- G_Mixer.metronome = G_Patch.metronome;
+
+/* -------------------------------------------------------------------------- */
+
+
+bool uniqueSolo(Channel *ch)
+{
+ int solos = 0;
+ for (unsigned i=0; i<mixer::channels.size(); i++) {
+ Channel *ch = mixer::channels.at(i);
+ if (ch->solo) solos++;
+ if (solos > 1) return false;
+ }
+ return true;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void readPatch()
+{
+ mixer::ready = false;
+
+ mixer::outVol = patch::masterVolOut;
+ mixer::inVol = patch::masterVolIn;
+ clock::setBpm(patch::bpm);
+ clock::setBars(patch::bars);
+ clock::setBeats(patch::beats);
+ clock::setQuantize(patch::quantize);
+ mixer::metronome = patch::metronome;
#ifdef WITH_VST
- __mh_readPatchPlugins__(&G_Patch.masterInPlugins, PluginHost::MASTER_IN);
- __mh_readPatchPlugins__(&G_Patch.masterOutPlugins, PluginHost::MASTER_OUT);
+ readPatchPlugins(&patch::masterInPlugins, pluginHost::MASTER_IN);
+ readPatchPlugins(&patch::masterOutPlugins, pluginHost::MASTER_OUT);
#endif
/* rewind and update frames in Mixer (it's essential) */
- G_Mixer.rewind();
- G_Mixer.updateFrameBars();
-
- G_Mixer.ready = true;
+ mixer::rewind();
+ clock::updateFrameBars();
+ mixer::ready = true;
}
/* -------------------------------------------------------------------------- */
-void mh_rewindSequencer()
+void rewindSequencer()
{
- if (G_Mixer.quantize > 0 && G_Mixer.running) // quantize rewind
- G_Mixer.rewindWait = true;
+ if (clock::getQuantize() > 0 && clock::isRunning()) // quantize rewind
+ mixer::rewindWait = true;
else
- G_Mixer.rewind();
+ mixer::rewind();
}
/* -------------------------------------------------------------------------- */
-bool mh_startInputRec()
+bool startInputRec()
{
int channelsReady = 0;
- for (unsigned i=0; i<G_Mixer.channels.size(); i++) {
+ for (unsigned i=0; i<mixer::channels.size(); i++) {
- if (!G_Mixer.channels.at(i)->canInputRec())
+ if (!mixer::channels.at(i)->canInputRec())
continue;
- SampleChannel *ch = (SampleChannel*) G_Mixer.channels.at(i);
+ SampleChannel *ch = (SampleChannel*) mixer::channels.at(i);
/* Allocate empty sample for the current channel. */
- if (!ch->allocEmpty(G_Mixer.totalFrames, G_Conf.samplerate, G_Patch.lastTakeId))
+ if (!ch->allocEmpty(clock::getTotalFrames(), conf::samplerate, patch::lastTakeId))
{
- gu_log("[mh_startInputRec] unable to allocate new Wave in chan %d!\n",
+ gu_log("[startInputRec] unable to allocate new Wave in chan %d!\n",
ch->index);
continue;
}
/* Increase lastTakeId until the sample name TAKE-[n] is unique */
- while (!mh_uniqueSampleName(ch, ch->wave->name)) {
- G_Patch_DEPR_.lastTakeId++;
- G_Patch.lastTakeId++;
- ch->wave->name = "TAKE-" + gu_itoa(G_Patch.lastTakeId);
+ while (!uniqueSampleName(ch, ch->wave->name)) {
+ patch::lastTakeId++;
+ ch->wave->name = "TAKE-" + gu_itoa(patch::lastTakeId);
}
- gu_log("[mh_startInputRec] start input recs using chan %d with size %d "
- "frame=%d\n", ch->index, G_Mixer.totalFrames, G_Mixer.inputTracker);
+ 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) {
- G_Mixer.recording = true;
+ mixer::recording = true;
/* start to write from the currentFrame, not the beginning */
/** FIXME: this should be done before wave allocation */
- G_Mixer.inputTracker = G_Mixer.currentFrame;
+ mixer::inputTracker = clock::getCurrentFrame();
return true;
}
return false;
/* -------------------------------------------------------------------------- */
-void mh_stopInputRec()
+void stopInputRec()
{
- G_Mixer.mergeVirtualInput();
- G_Mixer.recording = false;
- G_Mixer.waitRec = 0; // in case delay compensation is in use
+ mixer::mergeVirtualInput();
+ mixer::recording = false;
+ mixer::waitRec = 0; // in case delay compensation is in use
gu_log("[mh] stop input recs\n");
}
/* -------------------------------------------------------------------------- */
-bool mh_hasArmedSampleChannels()
+bool hasArmedSampleChannels()
{
- for (unsigned i=0; i<G_Mixer.channels.size(); i++) {
- Channel *ch = G_Mixer.channels.at(i);
+ for (unsigned i=0; i<mixer::channels.size(); i++) {
+ Channel *ch = mixer::channels.at(i);
if (ch->type == CHANNEL_SAMPLE && ch->armed)
return true;
}
}
-/* -------------------------------------------------------------------------- */
-
-
-bool mh_uniqueSampleName(SampleChannel *ch, const string &name)
-{
- for (unsigned i=0; i<G_Mixer.channels.size(); i++) {
- if (ch == G_Mixer.channels.at(i)) // skip itself
- continue;
- if (G_Mixer.channels.at(i)->type != CHANNEL_SAMPLE)
- continue;
- SampleChannel *other = (SampleChannel*) G_Mixer.channels.at(i);
- if (other->wave != NULL && name == other->wave->name)
- return false;
- }
- return true;
-}
+}}}; // giada::m::mh::
-/* ---------------------------------------------------------------------
+/* -----------------------------------------------------------------------------
*
* Giada - Your Hardcore Loopmachine
*
- * mixerHandler
+ * -----------------------------------------------------------------------------
*
- * ---------------------------------------------------------------------
- *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
* along with Giada - Your Hardcore Loopmachine. If not, see
* <http://www.gnu.org/licenses/>.
*
- * ------------------------------------------------------------------ */
+ * -------------------------------------------------------------------------- */
-#ifndef MIXERHANDLER_H
-#define MIXERHANDLER_H
+#ifndef G_MIXER_HANDLER_H
+#define G_MIXER_HANDLER_H
#include <string>
+class Channel;
+class SampleChannel;
+
+
+namespace giada {
+namespace m {
+namespace mh
+{
+/* addChannel
+Adds a new channel of type 'type' into mixer's stack. */
+
+Channel *addChannel(int type);
+
+/* deleteChannel
+Completely removes a channel from the stack. */
+
+int deleteChannel(Channel *ch);
+
+/* getChannelByIndex
+Returns channel with given index 'i'. */
+
+Channel *getChannelByIndex(int i);
+
+/* hasLogicalSamples
+True if 1 or more samples are logical (memory only, such as takes) */
+
+bool hasLogicalSamples();
+
+/* hasEditedSamples
+True if 1 or more samples was edited via gEditor */
+
+bool hasEditedSamples();
+
/* stopSequencer
* stop the sequencer, with special case if samplesStopOnSeqHalt is
* true. */
-void mh_stopSequencer();
+void stopSequencer();
-void mh_rewindSequencer();
+void rewindSequencer();
/* uniqueSolo
* true if ch is the only solo'd channel in mixer. */
-bool mh_uniqueSolo(class 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. */
-void mh_loadPatch_DEPR_(bool isProject, const char *projPath=0);
-void mh_readPatch();
+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. */
-bool mh_startInputRec();
+bool startInputRec();
-void mh_stopInputRec();
+void stopInputRec();
/* uniqueSamplename
* return true if samplename 'n' is unique. Requires SampleChannel *ch
* in order to skip check against itself. */
-bool mh_uniqueSampleName(class SampleChannel *ch, const std::string &s);
+bool uniqueSampleName(SampleChannel *ch, const std::string &s);
/* hasArmedSampleChannels
Tells whether Mixer has one or more sample channels armed for input
recording. */
-bool mh_hasArmedSampleChannels();
+bool hasArmedSampleChannels();
+}}} // giada::m::mh::
+
#endif
*
* Giada - Your Hardcore Loopmachine
*
- * patch
- *
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
#include "../utils/log.h"
#include "../utils/string.h"
#include "const.h"
+#include "storager.h"
#include "conf.h"
#include "mixer.h"
#include "patch.h"
-extern Mixer G_Mixer;
-extern Conf G_Conf;
+using std::string;
+using std::vector;
-void Patch::init()
+namespace giada {
+namespace m {
+namespace patch
{
- columns.clear();
- channels.clear();
-#ifdef WITH_VST
- masterInPlugins.clear();
- masterOutPlugins.clear();
-#endif
- header = "GIADAPTC";
- lastTakeId = 0;
- samplerate = DEFAULT_SAMPLERATE;
+namespace
+{
+/* sanitize
+Internal sanity check. */
+
+void sanitize()
+{
+ bpm = bpm < G_MIN_BPM || bpm > G_MAX_BPM ? G_DEFAULT_BPM : bpm;
+ bars = bars <= 0 || bars > G_MAX_BARS ? G_DEFAULT_BARS : bars;
+ beats = beats <= 0 || beats > G_MAX_BEATS ? G_DEFAULT_BEATS : beats;
+ quantize = quantize < 0 || quantize > G_MAX_QUANTIZE ? G_DEFAULT_QUANTIZE : quantize;
+ masterVolIn = masterVolIn < 0.0f || masterVolIn > 1.0f ? G_DEFAULT_VOL : masterVolIn;
+ masterVolOut = masterVolOut < 0.0f || masterVolOut > 1.0f ? G_DEFAULT_VOL : masterVolOut;
+ samplerate = samplerate <= 0 ? G_DEFAULT_SAMPLERATE : samplerate;
+
+ for (unsigned i=0; i<columns.size(); i++) {
+ column_t *col = &columns.at(i);
+ col->index = col->index < 0 ? 0 : col->index;
+ col->width = col->width < G_MIN_COLUMN_WIDTH ? G_MIN_COLUMN_WIDTH : col->width;
+ }
+
+ for (unsigned i=0; i<channels.size(); i++) {
+ channel_t *ch = &channels.at(i);
+ ch->volume = ch->volume < 0.0f || ch->volume > 1.0f ? G_DEFAULT_VOL : ch->volume;
+ ch->pan = ch->pan < 0.0f || ch->pan > 1.0f ? 1.0f : ch->pan;
+ ch->boost = ch->boost < 1.0f ? G_DEFAULT_BOOST : ch->boost;
+ ch->pitch = ch->pitch < 0.1f || ch->pitch > 4.0f ? G_DEFAULT_PITCH : ch->pitch;
+ }
}
/* -------------------------------------------------------------------------- */
+/* setInvalid
+Helper function used to return invalid status while reading. */
-int Patch::write(const string &file)
+int setInvalid(json_t *jRoot)
{
- jRoot = json_object();
+ json_decref(jRoot);
+ return PATCH_INVALID;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+bool readCommons(json_t *jContainer)
+{
+ if (!storager::setString(jContainer, PATCH_KEY_HEADER, header)) return 0;
+ if (!storager::setString(jContainer, PATCH_KEY_VERSION, version)) return 0;
+ if (!storager::setInt (jContainer, PATCH_KEY_VERSION_MAJOR, versionMajor)) return 0;
+ if (!storager::setInt (jContainer, PATCH_KEY_VERSION_MINOR, versionMinor)) return 0;
+ if (!storager::setInt (jContainer, PATCH_KEY_VERSION_PATCH, versionPatch)) return 0;
+ if (!storager::setString(jContainer, PATCH_KEY_NAME, name)) return 0;
+ if (!storager::setFloat (jContainer, PATCH_KEY_BPM, bpm)) return 0;
+ if (!storager::setInt (jContainer, PATCH_KEY_BARS, bars)) return 0;
+ if (!storager::setInt (jContainer, PATCH_KEY_BEATS, beats)) return 0;
+ if (!storager::setInt (jContainer, PATCH_KEY_QUANTIZE, quantize)) return 0;
+ if (!storager::setFloat (jContainer, PATCH_KEY_MASTER_VOL_IN, masterVolIn)) return 0;
+ if (!storager::setFloat (jContainer, PATCH_KEY_MASTER_VOL_OUT, masterVolOut)) return 0;
+ if (!storager::setInt (jContainer, PATCH_KEY_METRONOME, metronome)) return 0;
+ if (!storager::setInt (jContainer, PATCH_KEY_LAST_TAKE_ID, lastTakeId)) return 0;
+ if (!storager::setInt (jContainer, PATCH_KEY_SAMPLERATE, samplerate)) return 0;
+ return 1;
+}
+
+
+
+/* -------------------------------------------------------------------------- */
- writeCommons(jRoot);
- writeColumns(jRoot, &columns);
- writeChannels(jRoot, &channels);
#ifdef WITH_VST
- writePlugins(jRoot, &masterInPlugins, PATCH_KEY_MASTER_IN_PLUGINS);
- writePlugins(jRoot, &masterOutPlugins, PATCH_KEY_MASTER_OUT_PLUGINS);
-#endif
- if (json_dump_file(jRoot, file.c_str(), JSON_COMPACT) != 0) {
- gu_log("[Patch::write] unable to write patch file!\n");
+bool readPlugins(json_t *jContainer, vector<plugin_t> *container, const char *key)
+{
+ json_t *jPlugins = json_object_get(jContainer, key);
+ if (!storager::checkArray(jPlugins, key))
return 0;
+
+ size_t pluginIndex;
+ json_t *jPlugin;
+ json_array_foreach(jPlugins, pluginIndex, jPlugin) {
+
+ if (!storager::checkObject(jPlugin, "")) // TODO pass pluginIndex as string
+ return 0;
+
+ plugin_t plugin;
+ if (!storager::setString(jPlugin, PATCH_KEY_PLUGIN_PATH, plugin.path)) return 0;
+ if (!storager::setBool (jPlugin, PATCH_KEY_PLUGIN_BYPASS, plugin.bypass)) return 0;
+
+ /* read plugin params */
+
+ json_t *jParams = json_object_get(jPlugin, PATCH_KEY_PLUGIN_PARAMS);
+ if (!storager::checkArray(jParams, PATCH_KEY_PLUGIN_PARAMS)) return 0;
+
+ size_t paramIndex;
+ json_t *jParam;
+ json_array_foreach(jParams, paramIndex, jParam)
+ plugin.params.push_back(json_real_value(jParam));
+
+ /* read midiIn params (midi learning on plugins' parameters) */
+
+ json_t *jMidiInParams = json_object_get(jPlugin, PATCH_KEY_PLUGIN_MIDI_IN_PARAMS);
+ if (!storager::checkArray(jMidiInParams, PATCH_KEY_PLUGIN_MIDI_IN_PARAMS)) return 0;
+
+ size_t midiInParamIndex;
+ json_t *jMidiInParam;
+ json_array_foreach(jMidiInParams, midiInParamIndex, jMidiInParam)
+ plugin.midiInParams.push_back(json_integer_value(jMidiInParam));
+
+ container->push_back(plugin);
}
return 1;
}
+#endif
/* -------------------------------------------------------------------------- */
-int Patch::read(const string &file)
+bool readActions(json_t *jContainer, channel_t *channel)
{
- jRoot = json_load_file(file.c_str(), 0, &jError);
- if (!jRoot) {
- gu_log("[Patch::read] unable to read patch file! Error on line %d: %s\n", jError.line, jError.text);
- return PATCH_UNREADABLE;
+ json_t *jActions = json_object_get(jContainer, PATCH_KEY_CHANNEL_ACTIONS);
+ if (!storager::checkArray(jActions, PATCH_KEY_CHANNEL_ACTIONS))
+ return 0;
+
+ size_t actionIndex;
+ json_t *jAction;
+ json_array_foreach(jActions, actionIndex, jAction) {
+
+ if (!storager::checkObject(jAction, "")) // TODO pass actionIndex as string
+ return 0;
+
+ action_t action;
+ if (!storager::setInt (jAction, PATCH_KEY_ACTION_TYPE, action.type)) return 0;
+ if (!storager::setInt (jAction, PATCH_KEY_ACTION_FRAME, action.frame)) return 0;
+ if (!storager::setFloat (jAction, PATCH_KEY_ACTION_F_VALUE, action.fValue)) return 0;
+ if (!storager::setUint32(jAction, PATCH_KEY_ACTION_I_VALUE, action.iValue)) return 0;
+ channel->actions.push_back(action);
}
+ return 1;
+}
- if (!checkObject(jRoot, "root element"))
- return PATCH_INVALID;
- init();
+/* -------------------------------------------------------------------------- */
- /* TODO json_decref also when PATCH_INVALID */
- if (!readCommons(jRoot)) return setInvalid();
- if (!readColumns(jRoot)) return setInvalid();
- if (!readChannels(jRoot)) return setInvalid();
+bool readChannels(json_t *jContainer)
+{
+ json_t *jChannels = json_object_get(jContainer, PATCH_KEY_CHANNELS);
+ if (!storager::checkArray(jChannels, PATCH_KEY_CHANNELS))
+ return 0;
+
+ size_t channelIndex;
+ json_t *jChannel;
+ json_array_foreach(jChannels, channelIndex, jChannel) {
+
+ string channelIndexStr = "channel " + gu_itoa(channelIndex);
+ if (!storager::checkObject(jChannel, channelIndexStr.c_str()))
+ return 0;
+
+ channel_t channel;
+
+ 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_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;
+ if (!storager::setInt (jChannel, PATCH_KEY_CHANNEL_SOLO, channel.solo)) return 0;
+ if (!storager::setFloat (jChannel, PATCH_KEY_CHANNEL_VOLUME, channel.volume)) return 0;
+ if (!storager::setFloat (jChannel, PATCH_KEY_CHANNEL_PAN, channel.pan)) return 0;
+ if (!storager::setBool (jChannel, PATCH_KEY_CHANNEL_MIDI_IN, channel.midiIn)) return 0;
+ if (!storager::setUint32(jChannel, PATCH_KEY_CHANNEL_MIDI_IN_KEYPRESS, channel.midiInKeyPress)) return 0;
+ if (!storager::setUint32(jChannel, PATCH_KEY_CHANNEL_MIDI_IN_KEYREL, channel.midiInKeyRel)) return 0;
+ if (!storager::setUint32(jChannel, PATCH_KEY_CHANNEL_MIDI_IN_KILL, channel.midiInKill)) return 0;
+ if (!storager::setUint32(jChannel, PATCH_KEY_CHANNEL_MIDI_IN_ARM, channel.midiInArm)) return 0;
+ if (!storager::setUint32(jChannel, PATCH_KEY_CHANNEL_MIDI_IN_VOLUME, channel.midiInVolume)) return 0;
+ if (!storager::setUint32(jChannel, PATCH_KEY_CHANNEL_MIDI_IN_MUTE, channel.midiInMute)) return 0;
+ if (!storager::setUint32(jChannel, PATCH_KEY_CHANNEL_MIDI_IN_SOLO, channel.midiInSolo)) return 0;
+ if (!storager::setBool (jChannel, PATCH_KEY_CHANNEL_MIDI_OUT_L, channel.midiOutL)) return 0;
+ if (!storager::setUint32(jChannel, PATCH_KEY_CHANNEL_MIDI_OUT_L_PLAYING, channel.midiOutLplaying)) return 0;
+ if (!storager::setUint32(jChannel, PATCH_KEY_CHANNEL_MIDI_OUT_L_MUTE, channel.midiOutLmute)) return 0;
+ if (!storager::setUint32(jChannel, PATCH_KEY_CHANNEL_MIDI_OUT_L_SOLO, channel.midiOutLsolo)) return 0;
+ if (!storager::setString(jChannel, PATCH_KEY_CHANNEL_SAMPLE_PATH, channel.samplePath)) return 0;
+ if (!storager::setInt (jChannel, PATCH_KEY_CHANNEL_KEY, channel.key)) return 0;
+ if (!storager::setInt (jChannel, PATCH_KEY_CHANNEL_MODE, channel.mode)) return 0;
+ if (!storager::setInt (jChannel, PATCH_KEY_CHANNEL_BEGIN, channel.begin)) return 0;
+ if (!storager::setInt (jChannel, PATCH_KEY_CHANNEL_END, channel.end)) return 0;
+ if (!storager::setFloat (jChannel, PATCH_KEY_CHANNEL_BOOST, channel.boost)) return 0;
+ if (!storager::setInt (jChannel, PATCH_KEY_CHANNEL_REC_ACTIVE, channel.recActive)) return 0;
+ if (!storager::setFloat (jChannel, PATCH_KEY_CHANNEL_PITCH, channel.pitch)) return 0;
+ if (!storager::setBool (jChannel, PATCH_KEY_CHANNEL_INPUT_MONITOR, channel.inputMonitor)) return 0;
+ if (!storager::setUint32(jChannel, PATCH_KEY_CHANNEL_MIDI_IN_READ_ACTIONS, channel.midiInReadActions)) return 0;
+ 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;
+
+ readActions(jChannel, &channel);
+
#ifdef WITH_VST
- if (!readPlugins(jRoot, &masterInPlugins, PATCH_KEY_MASTER_IN_PLUGINS)) return setInvalid();
- if (!readPlugins(jRoot, &masterOutPlugins, PATCH_KEY_MASTER_OUT_PLUGINS)) return setInvalid();
+ readPlugins(jChannel, &channel.plugins, PATCH_KEY_CHANNEL_PLUGINS);
#endif
+ channels.push_back(channel);
+ }
+ return 1;
+}
- json_decref(jRoot);
- sanitize();
+/* -------------------------------------------------------------------------- */
- return PATCH_READ_OK;
+
+bool readColumns(json_t *jContainer)
+{
+ json_t *jColumns = json_object_get(jContainer, PATCH_KEY_COLUMNS);
+ if (!storager::checkArray(jColumns, PATCH_KEY_COLUMNS))
+ return 0;
+
+ size_t columnIndex;
+ json_t *jColumn;
+ json_array_foreach(jColumns, columnIndex, jColumn) {
+
+ string columnIndexStr = "column " + gu_itoa(columnIndex);
+ if (!storager::checkObject(jColumn, columnIndexStr.c_str()))
+ return 0;
+
+ column_t column;
+ if (!storager::setInt(jColumn, PATCH_KEY_COLUMN_INDEX, column.index)) return 0;
+ if (!storager::setInt(jColumn, PATCH_KEY_COLUMN_WIDTH, column.width)) return 0;
+
+ columns.push_back(column);
+ }
+ return 1;
}
+
/* -------------------------------------------------------------------------- */
#ifdef WITH_VST
-void Patch::writePlugins(json_t *jContainer, vector<plugin_t> *plugins, const char *key)
+void writePlugins(json_t *jContainer, vector<plugin_t> *plugins, const char *key)
{
json_t *jPlugins = json_array();
for (unsigned j=0; j<plugins->size(); j++) {
/* -------------------------------------------------------------------------- */
-void Patch::writeColumns(json_t *jContainer, vector<column_t> *columns)
+void writeColumns(json_t *jContainer, vector<column_t> *columns)
{
json_t *jColumns = json_array();
for (unsigned i=0; i<columns->size(); i++) {
/* -------------------------------------------------------------------------- */
-void Patch::writeActions(json_t *jContainer, vector<action_t> *actions)
+void writeActions(json_t *jContainer, vector<action_t> *actions)
{
json_t *jActions = json_array();
for (unsigned k=0; k<actions->size(); k++) {
/* -------------------------------------------------------------------------- */
-void Patch::writeCommons(json_t *jContainer)
+void writeCommons(json_t *jContainer)
{
json_object_set_new(jContainer, PATCH_KEY_HEADER, json_string(header.c_str()));
json_object_set_new(jContainer, PATCH_KEY_VERSION, json_string(version.c_str()));
/* -------------------------------------------------------------------------- */
-void Patch::writeChannels(json_t *jContainer, vector<channel_t> *channels)
+void writeChannels(json_t *jContainer, vector<channel_t> *channels)
{
json_t *jChannels = json_array();
for (unsigned i=0; i<channels->size(); i++) {
json_object_set_new(jChannel, PATCH_KEY_CHANNEL_MUTE_S, json_integer(channel.mute_s));
json_object_set_new(jChannel, PATCH_KEY_CHANNEL_SOLO, json_integer(channel.solo));
json_object_set_new(jChannel, PATCH_KEY_CHANNEL_VOLUME, json_real(channel.volume));
- json_object_set_new(jChannel, PATCH_KEY_CHANNEL_PAN_LEFT, json_real(channel.panLeft));
- json_object_set_new(jChannel, PATCH_KEY_CHANNEL_PAN_RIGHT, json_real(channel.panRight));
+ json_object_set_new(jChannel, PATCH_KEY_CHANNEL_PAN, json_real(channel.pan));
json_object_set_new(jChannel, PATCH_KEY_CHANNEL_MIDI_IN, json_boolean(channel.midiIn));
json_object_set_new(jChannel, PATCH_KEY_CHANNEL_MIDI_IN_KEYPRESS, json_integer(channel.midiInKeyPress));
json_object_set_new(jChannel, PATCH_KEY_CHANNEL_MIDI_IN_KEYREL, json_integer(channel.midiInKeyRel));
json_object_set_new(jChannel, PATCH_KEY_CHANNEL_BOOST, json_real(channel.boost));
json_object_set_new(jChannel, PATCH_KEY_CHANNEL_REC_ACTIVE, json_integer(channel.recActive));
json_object_set_new(jChannel, PATCH_KEY_CHANNEL_PITCH, json_real(channel.pitch));
+ json_object_set_new(jChannel, PATCH_KEY_CHANNEL_INPUT_MONITOR, json_boolean(channel.inputMonitor));
json_object_set_new(jChannel, PATCH_KEY_CHANNEL_MIDI_IN_READ_ACTIONS, json_integer(channel.midiInReadActions));
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(jContainer, PATCH_KEY_CHANNELS, jChannels);
}
-
-/* -------------------------------------------------------------------------- */
-
-
-bool Patch::readCommons(json_t *jContainer)
-{
- if (!setString(jContainer, PATCH_KEY_HEADER, header)) return 0;
- if (!setString(jContainer, PATCH_KEY_VERSION, version)) return 0;
- if (!setInt (jContainer, PATCH_KEY_VERSION_MAJOR, versionMajor)) return 0;
- if (!setInt (jContainer, PATCH_KEY_VERSION_MINOR, versionMinor)) return 0;
- if (!setInt (jContainer, PATCH_KEY_VERSION_PATCH, versionPatch)) return 0;
- if (!setString(jContainer, PATCH_KEY_NAME, name)) return 0;
- if (!setFloat (jContainer, PATCH_KEY_BPM, bpm)) return 0;
- if (!setInt (jContainer, PATCH_KEY_BARS, bars)) return 0;
- if (!setInt (jContainer, PATCH_KEY_BEATS, beats)) return 0;
- if (!setInt (jContainer, PATCH_KEY_QUANTIZE, quantize)) return 0;
- if (!setFloat (jContainer, PATCH_KEY_MASTER_VOL_IN, masterVolIn)) return 0;
- if (!setFloat (jContainer, PATCH_KEY_MASTER_VOL_OUT, masterVolOut)) return 0;
- if (!setInt (jContainer, PATCH_KEY_METRONOME, metronome)) return 0;
- if (!setInt (jContainer, PATCH_KEY_LAST_TAKE_ID, lastTakeId)) return 0;
- if (!setInt (jContainer, PATCH_KEY_SAMPLERATE, samplerate)) return 0;
- return 1;
-}
+}; // {anonymous}
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
-bool Patch::readColumns(json_t *jContainer)
-{
- json_t *jColumns = json_object_get(jContainer, PATCH_KEY_COLUMNS);
- if (!checkArray(jColumns, PATCH_KEY_COLUMNS))
- return 0;
-
- size_t columnIndex;
- json_t *jColumn;
- json_array_foreach(jColumns, columnIndex, jColumn) {
-
- string columnIndexStr = "column " + gu_itoa(columnIndex);
- if (!checkObject(jColumn, columnIndexStr.c_str()))
- return 0;
-
- column_t column;
- if (!setInt(jColumn, PATCH_KEY_COLUMN_INDEX, column.index)) return 0;
- if (!setInt(jColumn, PATCH_KEY_COLUMN_WIDTH, column.width)) return 0;
+std::string header;
+std::string version;
+int versionMajor;
+int versionMinor;
+int versionPatch;
+std::string name;
+float bpm;
+int bars;
+int beats;
+int quantize;
+float masterVolIn;
+float masterVolOut;
+int metronome;
+int lastTakeId;
+int samplerate; // original samplerate when the patch was saved
+
+std::vector<column_t> columns;
+std::vector<channel_t> channels;
- columns.push_back(column);
- }
- return 1;
-}
+#ifdef WITH_VST
+std::vector<plugin_t> masterInPlugins;
+std::vector<plugin_t> masterOutPlugins;
+#endif
/* -------------------------------------------------------------------------- */
-bool Patch::readChannels(json_t *jContainer)
+void init()
{
- json_t *jChannels = json_object_get(jContainer, PATCH_KEY_CHANNELS);
- if (!checkArray(jChannels, PATCH_KEY_CHANNELS))
- return 0;
-
- size_t channelIndex;
- json_t *jChannel;
- json_array_foreach(jChannels, channelIndex, jChannel) {
-
- string channelIndexStr = "channel " + gu_itoa(channelIndex);
- if (!checkObject(jChannel, channelIndexStr.c_str()))
- return 0;
-
- channel_t channel;
-
- if (!setInt (jChannel, PATCH_KEY_CHANNEL_TYPE, channel.type)) return 0;
- if (!setInt (jChannel, PATCH_KEY_CHANNEL_INDEX, channel.index)) return 0;
- if (!setInt (jChannel, PATCH_KEY_CHANNEL_COLUMN, channel.column)) return 0;
- if (!setInt (jChannel, PATCH_KEY_CHANNEL_MUTE, channel.mute)) return 0;
- if (!setInt (jChannel, PATCH_KEY_CHANNEL_MUTE_S, channel.mute_s)) return 0;
- if (!setInt (jChannel, PATCH_KEY_CHANNEL_SOLO, channel.solo)) return 0;
- if (!setFloat (jChannel, PATCH_KEY_CHANNEL_VOLUME, channel.volume)) return 0;
- if (!setFloat (jChannel, PATCH_KEY_CHANNEL_PAN_LEFT, channel.panRight)) return 0;
- if (!setFloat (jChannel, PATCH_KEY_CHANNEL_PAN_RIGHT, channel.panLeft)) return 0;
- if (!setBool (jChannel, PATCH_KEY_CHANNEL_MIDI_IN, channel.midiIn)) return 0;
- if (!setUint32(jChannel, PATCH_KEY_CHANNEL_MIDI_IN_KEYPRESS, channel.midiInKeyPress)) return 0;
- if (!setUint32(jChannel, PATCH_KEY_CHANNEL_MIDI_IN_KEYREL, channel.midiInKeyRel)) return 0;
- if (!setUint32(jChannel, PATCH_KEY_CHANNEL_MIDI_IN_KILL, channel.midiInKill)) return 0;
- if (!setUint32(jChannel, PATCH_KEY_CHANNEL_MIDI_IN_ARM, channel.midiInArm)) return 0;
- if (!setUint32(jChannel, PATCH_KEY_CHANNEL_MIDI_IN_VOLUME, channel.midiInVolume)) return 0;
- if (!setUint32(jChannel, PATCH_KEY_CHANNEL_MIDI_IN_MUTE, channel.midiInMute)) return 0;
- if (!setUint32(jChannel, PATCH_KEY_CHANNEL_MIDI_IN_SOLO, channel.midiInSolo)) return 0;
- if (!setBool (jChannel, PATCH_KEY_CHANNEL_MIDI_OUT_L, channel.midiOutL)) return 0;
- if (!setUint32(jChannel, PATCH_KEY_CHANNEL_MIDI_OUT_L_PLAYING, channel.midiOutLplaying)) return 0;
- if (!setUint32(jChannel, PATCH_KEY_CHANNEL_MIDI_OUT_L_MUTE, channel.midiOutLmute)) return 0;
- if (!setUint32(jChannel, PATCH_KEY_CHANNEL_MIDI_OUT_L_SOLO, channel.midiOutLsolo)) return 0;
- if (!setString(jChannel, PATCH_KEY_CHANNEL_SAMPLE_PATH, channel.samplePath)) return 0;
- if (!setInt (jChannel, PATCH_KEY_CHANNEL_KEY, channel.key)) return 0;
- if (!setInt (jChannel, PATCH_KEY_CHANNEL_MODE, channel.mode)) return 0;
- if (!setInt (jChannel, PATCH_KEY_CHANNEL_BEGIN, channel.begin)) return 0;
- if (!setInt (jChannel, PATCH_KEY_CHANNEL_END, channel.end)) return 0;
- if (!setFloat (jChannel, PATCH_KEY_CHANNEL_BOOST, channel.boost)) return 0;
- if (!setInt (jChannel, PATCH_KEY_CHANNEL_REC_ACTIVE, channel.recActive)) return 0;
- if (!setFloat (jChannel, PATCH_KEY_CHANNEL_PITCH, channel.pitch)) return 0;
- if (!setUint32(jChannel, PATCH_KEY_CHANNEL_MIDI_IN_READ_ACTIONS, channel.midiInReadActions)) return 0;
- if (!setUint32(jChannel, PATCH_KEY_CHANNEL_MIDI_IN_PITCH, channel.midiInPitch)) return 0;
- if (!setUint32(jChannel, PATCH_KEY_CHANNEL_MIDI_OUT, channel.midiOut)) return 0;
- if (!setUint32(jChannel, PATCH_KEY_CHANNEL_MIDI_OUT_CHAN, channel.midiOutChan)) return 0;
-
- readActions(jChannel, &channel);
-
+ columns.clear();
+ channels.clear();
#ifdef WITH_VST
- readPlugins(jChannel, &channel.plugins, PATCH_KEY_CHANNEL_PLUGINS);
+ masterInPlugins.clear();
+ masterOutPlugins.clear();
#endif
- channels.push_back(channel);
- }
- return 1;
+ header = "GIADAPTC";
+ lastTakeId = 0;
+ samplerate = G_DEFAULT_SAMPLERATE;
}
/* -------------------------------------------------------------------------- */
-bool Patch::readActions(json_t *jContainer, channel_t *channel)
+int write(const string &file)
{
- json_t *jActions = json_object_get(jContainer, PATCH_KEY_CHANNEL_ACTIONS);
- if (!checkArray(jActions, PATCH_KEY_CHANNEL_ACTIONS))
- return 0;
-
- size_t actionIndex;
- json_t *jAction;
- json_array_foreach(jActions, actionIndex, jAction) {
+ json_t *jRoot = json_object();
- if (!checkObject(jAction, "")) // TODO pass actionIndex as string
- return 0;
+ writeCommons(jRoot);
+ writeColumns(jRoot, &columns);
+ writeChannels(jRoot, &channels);
+#ifdef WITH_VST
+ writePlugins(jRoot, &masterInPlugins, PATCH_KEY_MASTER_IN_PLUGINS);
+ writePlugins(jRoot, &masterOutPlugins, PATCH_KEY_MASTER_OUT_PLUGINS);
+#endif
- action_t action;
- if (!setInt (jAction, PATCH_KEY_ACTION_TYPE, action.type)) return 0;
- if (!setInt (jAction, PATCH_KEY_ACTION_FRAME, action.frame)) return 0;
- if (!setFloat (jAction, PATCH_KEY_ACTION_F_VALUE, action.fValue)) return 0;
- if (!setUint32(jAction, PATCH_KEY_ACTION_I_VALUE, action.iValue)) return 0;
- channel->actions.push_back(action);
+ if (json_dump_file(jRoot, file.c_str(), JSON_COMPACT) != 0) {
+ gu_log("[write] unable to write patch file!\n");
+ return 0;
}
return 1;
}
/* -------------------------------------------------------------------------- */
-#ifdef WITH_VST
-
-bool Patch::readPlugins(json_t *jContainer, vector<plugin_t> *container, const char *key)
+int read(const string &file)
{
- json_t *jPlugins = json_object_get(jContainer, key);
- if (!checkArray(jPlugins, key))
- return 0;
-
- size_t pluginIndex;
- json_t *jPlugin;
- json_array_foreach(jPlugins, pluginIndex, jPlugin) {
-
- if (!checkObject(jPlugin, "")) // TODO pass pluginIndex as string
- return 0;
-
- plugin_t plugin;
- if (!setString(jPlugin, PATCH_KEY_PLUGIN_PATH, plugin.path)) return 0;
- if (!setBool (jPlugin, PATCH_KEY_PLUGIN_BYPASS, plugin.bypass)) return 0;
-
- /* read plugin params */
-
- json_t *jParams = json_object_get(jPlugin, PATCH_KEY_PLUGIN_PARAMS);
- if (!checkArray(jParams, PATCH_KEY_PLUGIN_PARAMS)) return 0;
-
- size_t paramIndex;
- json_t *jParam;
- json_array_foreach(jParams, paramIndex, jParam)
- plugin.params.push_back(json_real_value(jParam));
-
- /* read midiIn params (midi learning on plugins' parameters) */
-
- json_t *jMidiInParams = json_object_get(jPlugin, PATCH_KEY_PLUGIN_MIDI_IN_PARAMS);
- if (!checkArray(jMidiInParams, PATCH_KEY_PLUGIN_MIDI_IN_PARAMS)) return 0;
-
- size_t midiInParamIndex;
- json_t *jMidiInParam;
- json_array_foreach(jMidiInParams, midiInParamIndex, jMidiInParam)
- plugin.midiInParams.push_back(json_integer_value(jMidiInParam));
-
- container->push_back(plugin);
+ json_error_t jError;
+ json_t *jRoot = json_load_file(file.c_str(), 0, &jError);
+ if (!jRoot) {
+ gu_log("[read] unable to read patch file! Error on line %d: %s\n", jError.line, jError.text);
+ return PATCH_UNREADABLE;
}
- return 1;
-}
-#endif
+ if (!storager::checkObject(jRoot, "root element"))
+ return PATCH_INVALID;
+ init();
-/* -------------------------------------------------------------------------- */
+ /* TODO json_decref also when PATCH_INVALID */
+ if (!readCommons(jRoot)) return setInvalid(jRoot);
+ if (!readColumns(jRoot)) return setInvalid(jRoot);
+ if (!readChannels(jRoot)) return setInvalid(jRoot);
+#ifdef WITH_VST
+ if (!readPlugins(jRoot, &masterInPlugins, PATCH_KEY_MASTER_IN_PLUGINS)) return setInvalid(jRoot);
+ if (!readPlugins(jRoot, &masterOutPlugins, PATCH_KEY_MASTER_OUT_PLUGINS)) return setInvalid(jRoot);
+#endif
-void Patch::sanitize()
-{
- bpm = bpm < 20.0f || bpm > 999.0f ? DEFAULT_BPM : bpm;
- bars = bars <= 0 || bars > 32 ? DEFAULT_BARS : bars;
- beats = beats <= 0 || beats > 32 ? DEFAULT_BEATS : beats;
- quantize = quantize < 0 || quantize > 8 ? DEFAULT_QUANTIZE : quantize;
- masterVolIn = masterVolIn < 0.0f || masterVolIn > 1.0f ? DEFAULT_VOL : masterVolIn;
- masterVolOut = masterVolOut < 0.0f || masterVolOut > 1.0f ? DEFAULT_VOL : masterVolOut;
- samplerate = samplerate <= 0 ? DEFAULT_SAMPLERATE : samplerate;
+ json_decref(jRoot);
- for (unsigned i=0; i<columns.size(); i++) {
- column_t *col = &columns.at(i);
- col->index = col->index < 0 ? 0 : col->index;
- col->width = col->width < MIN_COLUMN_WIDTH ? MIN_COLUMN_WIDTH : col->width;
- }
+ sanitize();
- for (unsigned i=0; i<channels.size(); i++) {
- channel_t *ch = &channels.at(i);
- ch->volume = ch->volume < 0.0f || ch->volume > 1.0f ? DEFAULT_VOL : ch->volume;
- ch->panLeft = ch->panLeft < 0.0f || ch->panLeft > 1.0f ? 1.0f : ch->panLeft;
- ch->panRight = ch->panRight < 0.0f || ch->panRight > 1.0f ? 1.0f : ch->panRight;
- ch->boost = ch->boost < 1.0f ? DEFAULT_BOOST : ch->boost;
- ch->pitch = ch->pitch < 0.1f || ch->pitch > 4.0f ? G_DEFAULT_PITCH : ch->pitch;
- }
+ return PATCH_READ_OK;
}
-/* -------------------------------------------------------------------------- */
-
-
-int Patch::setInvalid()
-{
- json_decref(jRoot);
- return PATCH_INVALID;
-}
+}}}; // giada::m::patch::
*
* Giada - Your Hardcore Loopmachine
*
- * patch
- *
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
* -------------------------------------------------------------------------- */
-#ifndef __PATCH_H__
-#define __PATCH_H__
+#ifndef G_PATCH_H
+#define G_PATCH_H
#include <string>
#include <vector>
-#include <stdint.h>
-#include "dataStorageJson.h"
-
-
-using std::string;
-using std::vector;
+#ifdef __APPLE__ // our Clang still doesn't know about cstdint (c++11 stuff)
+ #include <stdint.h>
+#else
+ #include <cstdint>
+#endif
-class Patch : public DataStorageJson
+namespace giada {
+namespace m {
+namespace patch
{
-public:
-
- struct action_t
- {
- int type;
- int frame;
- float fValue;
- uint32_t iValue;
- };
+struct action_t
+{
+ int type;
+ int frame;
+ float fValue;
+ uint32_t iValue;
+};
#ifdef WITH_VST
- struct plugin_t
- {
- string path;
- bool bypass;
- vector<float> params;
- vector<uint32_t> midiInParams;
- };
+struct plugin_t
+{
+ std::string path;
+ bool bypass;
+ std::vector<float> params;
+ std::vector<uint32_t> midiInParams;
+};
#endif
- struct channel_t
- {
- int type;
- int index;
- int column;
- int mute;
- int mute_s;
- int solo;
- float volume;
- float panLeft;
- float panRight;
- bool midiIn;
- uint32_t midiInKeyPress;
- uint32_t midiInKeyRel;
- uint32_t midiInKill;
- uint32_t midiInArm;
- uint32_t midiInVolume;
- uint32_t midiInMute;
- uint32_t midiInSolo;
- bool midiOutL;
- uint32_t midiOutLplaying;
- uint32_t midiOutLmute;
- uint32_t midiOutLsolo;
- // sample channel
- string samplePath;
- int key;
- int mode;
- int begin;
- int end;
- float boost;
- int recActive;
- float pitch;
- uint32_t midiInReadActions;
- uint32_t midiInPitch;
- // midi channel
- uint32_t midiOut;
- uint32_t midiOutChan;
-
- vector<action_t> actions;
-
-#ifdef WITH_VST
- vector<plugin_t> plugins;
-#endif
- };
-
- struct column_t
- {
- int index;
- int width;
- vector<int> channels;
- };
-
- string header;
- string version;
- int versionMajor;
- int versionMinor;
- int versionPatch;
- string name;
- float bpm;
- int bars;
- int beats;
- int quantize;
- float masterVolIn;
- float masterVolOut;
- int metronome;
- int lastTakeId;
- int samplerate; // original samplerate when the patch was saved
-
- vector<column_t> columns;
- vector<channel_t> channels;
+struct channel_t
+{
+ int type;
+ int index;
+ int column;
+ int mute;
+ int mute_s;
+ int solo;
+ float volume;
+ float pan;
+ bool midiIn;
+ uint32_t midiInKeyPress;
+ uint32_t midiInKeyRel;
+ uint32_t midiInKill;
+ uint32_t midiInArm;
+ uint32_t midiInVolume;
+ uint32_t midiInMute;
+ uint32_t midiInSolo;
+ bool midiOutL;
+ uint32_t midiOutLplaying;
+ uint32_t midiOutLmute;
+ uint32_t midiOutLsolo;
+ // sample channel
+ std::string samplePath;
+ int key;
+ int mode;
+ int begin;
+ int end;
+ float boost;
+ int recActive;
+ float pitch;
+ bool inputMonitor;
+ uint32_t midiInReadActions;
+ uint32_t midiInPitch;
+ // midi channel
+ uint32_t midiOut;
+ uint32_t midiOutChan;
+
+ std::vector<action_t> actions;
#ifdef WITH_VST
- vector<plugin_t> masterInPlugins;
- vector<plugin_t> masterOutPlugins;
+ std::vector<plugin_t> plugins;
#endif
+};
- /* init
- * Init Patch with default values. */
-
- void init();
-
- /* read/write
- * Read/write patch to/from file. */
-
- int write(const string &file);
- int read (const string &file);
-
-private:
-
- /* sanitize
- * Internal sanity check. */
-
- void sanitize();
-
- /* setInvalid
- * Helper function used to return invalid status while reading. */
-
- int setInvalid();
+struct column_t
+{
+ int index;
+ int width;
+ std::vector<int> channels;
+};
- /* readers */
+extern std::string header;
+extern std::string version;
+extern int versionMajor;
+extern int versionMinor;
+extern int versionPatch;
+extern std::string name;
+extern float bpm;
+extern int bars;
+extern int beats;
+extern int quantize;
+extern float masterVolIn;
+extern float masterVolOut;
+extern int metronome;
+extern int lastTakeId;
+extern int samplerate; // original samplerate when the patch was saved
+
+extern std::vector<column_t> columns;
+extern std::vector<channel_t> channels;
- bool readCommons (json_t *jContainer);
- bool readChannels(json_t *jContainer);
#ifdef WITH_VST
- bool readPlugins (json_t *jContainer, vector<plugin_t> *container, const char* key);
+extern std::vector<plugin_t> masterInPlugins;
+extern std::vector<plugin_t> masterOutPlugins;
#endif
- bool readActions (json_t *jContainer, channel_t *channel);
- bool readColumns (json_t *jContainer);
- /* writers */
+/* init
+ * Init Patch with default values. */
- void writeCommons (json_t *jContainer);
- void writeChannels(json_t *jContainer, vector<channel_t> *channels);
-#ifdef WITH_VST
- void writePlugins (json_t *jContainer, vector<plugin_t> *plugins, const char* key);
-#endif
- void writeActions (json_t *jContainer, vector<action_t> *actions);
- void writeColumns (json_t *jContainer, vector<column_t> *columns);
-};
+void init();
+
+/* read/write
+ * Read/write patch to/from file. */
+
+int write(const std::string &file);
+int read (const std::string &file);
+}}}; // giada::m::patch::
#endif
+++ /dev/null
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * patch
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
- *
- * This file is part of Giada - Your Hardcore Loopmachine.
- *
- * Giada - Your Hardcore Loopmachine is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General
- * Public License as published by the Free Software Foundation, either
- * version 3 of the License, or (at your option) any later version.
- *
- * Giada - Your Hardcore Loopmachine is distributed in the hope that it
- * will be useful, but WITHOUT ANY WARRANTY; without even the implied
- * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Giada - Your Hardcore Loopmachine. If not, see
- * <http://www.gnu.org/licenses/>.
- *
- * -------------------------------------------------------------------------- */
-
-
-#include <stdint.h>
-#include "../utils/log.h"
-#include "../utils/fs.h"
-#include "../gui/dialogs/gd_mainWindow.h"
-#include "../gui/elems/mainWindow/keyboard/keyboard.h"
-#include "patch_DEPR_.h"
-#include "init.h"
-#include "recorder.h"
-#include "conf.h"
-#include "pluginHost.h"
-#include "plugin.h"
-#include "wave.h"
-#include "mixer.h"
-#include "channel.h"
-
-
-extern Mixer G_Mixer;
-extern Conf G_Conf;
-extern Recorder G_Recorder;
-#ifdef WITH_VST
-extern PluginHost G_PluginHost;
-#endif
-extern gdMainWindow *mainWin;
-
-
-int Patch_DEPR_::open(const char *file)
-{
- fp = fopen(file, "r");
- if (fp == NULL)
- return PATCH_UNREADABLE;
-
- if (getValue("header") != "GIADAPTC")
- return PATCH_INVALID;
-
- version = atof(getValue("versionf").c_str());
- gu_log("[patch_DEPR_] open patch version %f\n", version);
-
- return PATCH_READ_OK;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void Patch_DEPR_::setDefault()
-{
- name[0] = '\0';
- lastTakeId = 0;
- samplerate = DEFAULT_SAMPLERATE;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-int Patch_DEPR_::close()
-{
- return fclose(fp);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void Patch_DEPR_::getName()
-{
- std::string out = getValue("patchname");
- strncpy(name, out.c_str(), MAX_PATCHNAME_LEN);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-std::string Patch_DEPR_::getSamplePath(int c)
-{
- char tmp[16];
- sprintf(tmp, "samplepath%d", c);
- return getValue(tmp);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-float Patch_DEPR_::getPitch(int c)
-{
- char tmp[16];
- sprintf(tmp, "chanPitch%d", c);
- float out = atof(getValue(tmp).c_str());
- if (out > 2.0f || out < 0.1f)
- return 1.0f;
- return out;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-int Patch_DEPR_::getNumChans()
-{
- if (version == 0.0) // backward compatibility with version < 0.6.1
- return 32;
- return atoi(getValue("channels").c_str());
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-int Patch_DEPR_::getNumColumns()
-{
- return atoi(getValue("columns").c_str());
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-int Patch_DEPR_::getColumn(int c)
-{
- if (version == 0.0) // backward compatibility with version < 0.6.1
- return 0;
- char tmp[16];
- sprintf(tmp, "chanColumn%d", c);
- return atoi(getValue(tmp).c_str());
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-int Patch_DEPR_::getIndex(int c)
-{
- if (version == 0.0) // backward compatibility with version < 0.6.1
- return c;
-
- char tmp[16];
- sprintf(tmp, "chanIndex%d", c);
- return atoi(getValue(tmp).c_str());
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-float Patch_DEPR_::getVol(int c)
-{
- char tmp[16];
- sprintf(tmp, "chanvol%d", c);
- float out = atof(getValue(tmp).c_str());
- if (out > 1.0f || out < 0.0f)
- return DEFAULT_VOL;
- return out;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-int Patch_DEPR_::getMode(int c)
-{
- char tmp[16];
- sprintf(tmp, "chanmode%d", c);
- int out = atoi(getValue(tmp).c_str());
- if (out & (LOOP_ANY | SINGLE_ANY))
- return out;
- return DEFAULT_CHANMODE;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-int Patch_DEPR_::getMute(int c)
-{
- char tmp[16];
- sprintf(tmp, "chanMute%d", c);
- return atoi(getValue(tmp).c_str());
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-int Patch_DEPR_::getMute_s(int c)
-{
- char tmp[16];
- sprintf(tmp, "chanMute_s%d", c);
- return atoi(getValue(tmp).c_str());
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-int Patch_DEPR_::getSolo(int c)
-{
- char tmp[16];
- sprintf(tmp, "chanSolo%d", c);
- return atoi(getValue(tmp).c_str());
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-int Patch_DEPR_::getType(int c)
-{
- char tmp[16];
- sprintf(tmp, "chanType%d", c);
- int out = atoi(getValue(tmp).c_str());
- if (out == 0)
- return CHANNEL_SAMPLE;
- return out;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-int Patch_DEPR_::getBegin(int c)
-{
- char tmp[16];
- if (version < 0.73f)
- sprintf(tmp, "chanstart%d", c);
- else
- sprintf(tmp, "chanBegin%d", c);
- int out = atoi(getValue(tmp).c_str());
- if (out < 0)
- return 0;
- return out;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-int Patch_DEPR_::getEnd(int c, unsigned size)
-{
- char tmp[16];
- sprintf(tmp, "chanend%d", c);
-
- /* if chanEnd doesn't exist, it returns an atoi(empty string) == 0.
- * good in theory, a disaster in practice. */
-
- std::string val = getValue(tmp);
- if (val == "")
- return size;
-
- unsigned out = atoi(val.c_str());
- if (out <= 0 || out > size)
- return size;
- return out;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-float Patch_DEPR_::getBoost(int c)
-{
- char tmp[16];
- sprintf(tmp, "chanBoost%d", c);
- float out = atof(getValue(tmp).c_str());
- if (out < 1.0f)
- return DEFAULT_BOOST;
- return out;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-float Patch_DEPR_::getPanLeft(int c)
-{
- char tmp[16];
- sprintf(tmp, "chanPanLeft%d", c);
- std::string val = getValue(tmp);
- if (val == "")
- return 1.0f;
-
- float out = atof(val.c_str());
- if (out < 0.0f || out > 1.0f)
- return 1.0f;
- return out;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-int Patch_DEPR_::getKey(int c)
-{
- if (version == 0.0) // backward compatibility with version < 0.6.1
- return 0;
- char tmp[16];
- sprintf(tmp, "chanKey%d", c);
- return atoi(getValue(tmp).c_str());
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-float Patch_DEPR_::getPanRight(int c)
-{
- char tmp[16];
- sprintf(tmp, "chanPanRight%d", c);
- std::string val = getValue(tmp);
- if (val == "")
- return 1.0f;
-
- float out = atof(val.c_str());
- if (out < 0.0f || out > 1.0f)
- return 1.0f;
- return out;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-bool Patch_DEPR_::getRecActive(int c)
-{
- char tmp[16];
- sprintf(tmp, "chanRecActive%d", c);
- return atoi(getValue(tmp).c_str());
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-float Patch_DEPR_::getOutVol()
-{
- return atof(getValue("outVol").c_str());
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-float Patch_DEPR_::getInVol()
-{
- return atof(getValue("inVol").c_str());
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-float Patch_DEPR_::getBpm()
-{
- float out = atof(getValue("bpm").c_str());
- if (out < 20.0f || out > 999.0f)
- return DEFAULT_BPM;
- return out;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-int Patch_DEPR_::getBars()
-{
- int out = atoi(getValue("bars").c_str());
- if (out <= 0 || out > 32)
- return DEFAULT_BARS;
- return out;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-int Patch_DEPR_::getBeats()
-{
- int out = atoi(getValue("beats").c_str());
- if (out <= 0 || out > 32)
- return DEFAULT_BEATS;
- return out;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-int Patch_DEPR_::getQuantize()
-{
- int out = atoi(getValue("quantize").c_str());
- if (out < 0 || out > 8)
- return DEFAULT_QUANTIZE;
- return out;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-bool Patch_DEPR_::getMetronome()
-{
- return atoi(getValue("metronome").c_str());
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-int Patch_DEPR_::getLastTakeId()
-{
- return atoi(getValue("lastTakeId").c_str());
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-int Patch_DEPR_::getSamplerate()
-{
- int out = atoi(getValue("samplerate").c_str());
- if (out <= 0)
- return DEFAULT_SAMPLERATE;
- return out;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-uint32_t Patch_DEPR_::getMidiValue(int i, const char *c)
-{
- char tmp[32];
- sprintf(tmp, "chanMidi%s%d", c, i);
- return strtoul(getValue(tmp).c_str(), NULL, 10);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-int Patch_DEPR_::readRecs()
-{
- gu_log("[patch_DEPR_] Reading recs...\n");
-
- unsigned numrecs = atoi(getValue("numrecs").c_str());
-
- for (unsigned i=0; i<numrecs; i++) {
- int frame, recPerFrame;
-
- /* parsing 'dddddd d': [framenumber] [num. recs for that frame] */
-
- char tmpbuf[16];
- sprintf(tmpbuf, "recframe%d", i);
- sscanf(getValue(tmpbuf).c_str(), "%d %d", &frame, &recPerFrame);
-
-//gu_log("processing frame=%d, recPerFrame=%d\n", frame, recPerFrame);
-
- for (int k=0; k<recPerFrame; k++) {
- int chan = 0;
- int type = 0;
- float fValue = 0.0f;
- int iValue_fix = 0;
- uint32_t iValue = 0;
-
- /* reading info for each frame: %d|%d */
-
- char tmpbuf[16];
- sprintf(tmpbuf, "f%da%d", i, k);
-
- if (version < 0.61f) // no float and int values
- sscanf(getValue(tmpbuf).c_str(), "%d|%d", &chan, &type);
- else
- if (version < 0.83f) // iValues were stored as signed int (wrong)
- sscanf(getValue(tmpbuf).c_str(), "%d|%d|%f|%d", &chan, &type, &fValue, &iValue_fix);
- else
- sscanf(getValue(tmpbuf).c_str(), "%d|%d|%f|%u", &chan, &type, &fValue, &iValue);
-
-//gu_log(" loading chan=%d, type=%d, fValue=%f, iValue=%u\n", chan, type, fValue, iValue);
-
- Channel *ch = G_Mixer.getChannelByIndex(chan);
- if (ch)
- if (ch->status & ~(STATUS_WRONG | STATUS_MISSING | STATUS_EMPTY)) {
- if (version < 0.83f)
- G_Recorder.rec(ch->index, type, frame, iValue_fix, fValue);
- else
- G_Recorder.rec(ch->index, type, frame, iValue, fValue);
- ch->hasActions = true;
- }
- }
- }
- return 1;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-#ifdef WITH_VST
-int Patch_DEPR_::readPlugins()
-{
- gu_log("[patch_DEPR_] Reading plugins...\n");
-
- int globalOut = 1;
-
- /* master plugins */
-
- globalOut &= readMasterPlugins(PluginHost::MASTER_IN);
- globalOut &= readMasterPlugins(PluginHost::MASTER_OUT);
-
- /* channel plugins */
-
- for (unsigned i=0; i<G_Mixer.channels.size(); i++) {
- Channel *ch = G_Mixer.channels.at(i);
-
- char tmp[MAX_LINE_LEN];
- sprintf(tmp, "chan%dPlugins", ch->index);
- int np = atoi(getValue(tmp).c_str());
-
- for (int j=0; j<np; j++) {
- sprintf(tmp, "chan%d_p%dpathfile", ch->index, j);
- Plugin *plugin = G_PluginHost.addPlugin(getValue(tmp).c_str(), PluginHost::CHANNEL, &G_Mixer.mutex_plugins, ch);
- if (plugin != NULL) {
- sprintf(tmp, "chan%d_p%dnumParams", ch->index, j);
- int nparam = atoi(getValue(tmp).c_str());
- Plugin *pPlugin = G_PluginHost.getPluginByIndex(j, PluginHost::CHANNEL, ch);
- sprintf(tmp, "chan%d_p%dbypass", ch->index, j);
- if (pPlugin) {
- pPlugin->setBypass(atoi(getValue(tmp).c_str()));
- for (int k=0; k<nparam; k++) {
- sprintf(tmp, "chan%d_p%dparam%dvalue", ch->index, j, k);
- float pval = atof(getValue(tmp).c_str());
- pPlugin->setParameter(k, pval);
- }
- }
- globalOut &= 1;
- }
- else
- globalOut &= 0;
- }
- }
- return globalOut;
-}
-#endif
-
-
-/* -------------------------------------------------------------------------- */
-
-#ifdef WITH_VST
-
-int Patch_DEPR_::readMasterPlugins(int type)
-{
- int nmp;
- char chr;
- int res = 1;
-
- if (type == PluginHost::MASTER_IN) {
- chr = 'I';
- nmp = atoi(getValue("masterIPlugins").c_str());
- }
- else {
- chr = 'O';
- nmp = atoi(getValue("masterOPlugins").c_str());
- }
-
- for (int i=0; i<nmp; i++) {
- char tmp[MAX_LINE_LEN];
- sprintf(tmp, "master%c_p%dpathfile", chr, i);
- Plugin *p = G_PluginHost.addPlugin(getValue(tmp).c_str(), type, &G_Mixer.mutex_plugins);
- if (p != NULL) {
- Plugin *pPlugin = G_PluginHost.getPluginByIndex(i, type);
- sprintf(tmp, "master%c_p%dbypass", chr, i);
- pPlugin->setBypass(atoi(getValue(tmp).c_str()));
- sprintf(tmp, "master%c_p%dnumParams", chr, i);
- int nparam = atoi(getValue(tmp).c_str());
- for (int j=0; j<nparam; j++) {
- sprintf(tmp, "master%c_p%dparam%dvalue", chr, i, j);
- float pval = atof(getValue(tmp).c_str());
- pPlugin->setParameter(j, pval);
- }
- res &= 1;
- }
- else
- res &= 0;
- }
-
- return res;
-}
-
-#endif
+++ /dev/null
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * patch
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
- *
- * This file is part of Giada - Your Hardcore Loopmachine.
- *
- * Giada - Your Hardcore Loopmachine is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General
- * Public License as published by the Free Software Foundation, either
- * version 3 of the License, or (at your option) any later version.
- *
- * Giada - Your Hardcore Loopmachine is distributed in the hope that it
- * will be useful, but WITHOUT ANY WARRANTY; without even the implied
- * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Giada - Your Hardcore Loopmachine. If not, see
- * <http://www.gnu.org/licenses/>.
- *
- * -------------------------------------------------------------------------- */
-
-
-#ifndef __PATCH_DEPR_H__
-#define __PATCH_DEPR_H__
-
-#include <stdio.h>
-#include <string>
-#include <stdint.h>
-#include "dataStorageIni.h"
-#include "const.h"
-
-
-class Patch_DEPR_ : public DataStorageIni
-{
-private:
-
- int readMasterPlugins(int type);
-
-public:
-
- char name[MAX_PATCHNAME_LEN];
- float version;
- int lastTakeId;
- int samplerate;
-
- int open(const char *file);
- void setDefault();
- int close();
-
- void getName ();
- int getNumChans ();
- int getNumColumns ();
- std::string getSamplePath (int i);
- float getVol (int i);
- int getMode (int i);
- int getMute (int i);
- int getMute_s (int i);
- int getSolo (int i);
- int getBegin (int i);
- int getEnd (int i, unsigned sampleSize);
- float getBoost (int i);
- float getPanLeft (int i);
- float getPanRight (int i);
- float getPitch (int i);
- bool getRecActive (int i);
- int getColumn (int i);
- int getIndex (int i);
- int getType (int i);
- int getKey (int i);
- uint32_t getMidiValue (int i, const char *c);
- float getOutVol ();
- float getInVol ();
- float getBpm ();
- int getBars ();
- int getBeats ();
- int getQuantize ();
- bool getMetronome ();
- int getLastTakeId ();
- int getSamplerate ();
-
- int readRecs();
-#ifdef WITH_VST
- int readPlugins();
-#endif
-};
-
-#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
/* Try to enable editor (i.e. plugin's UI) */
- if (plugin->getActiveEditor() != NULL) {
+ if (plugin->getActiveEditor() != nullptr) {
gu_log("[Plugin] plugin has an already active editor!\n");
return;
}
ui = plugin->createEditorIfNeeded();
- if (ui == NULL) {
+ if (ui == nullptr) {
gu_log("[Plugin] unable to create editor, the plugin might be GUI-less!\n");
return;
}
void Plugin::showEditor(void *parent)
{
- if (ui == NULL) {
+ if (ui == nullptr) {
gu_log("[Plugin::showEditor] can't show editor!\n");
return;
}
void Plugin::closeEditor()
{
- if (ui == NULL)
+ if (ui == nullptr)
return;
if (ui->isOnDesktop())
ui->removeFromDesktop();
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
#ifdef WITH_VST
-#ifndef __PLUGIN_H__
-#define __PLUGIN_H__
+#ifndef G_PLUGIN_H
+#define G_PLUGIN_H
#include "../deps/juce-config.h"
*
* Giada - Your Hardcore Loopmachine
*
- * pluginHost
- *
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
#include "../utils/log.h"
+#include "../utils/fs.h"
#include "const.h"
#include "channel.h"
#include "plugin.h"
#include "pluginHost.h"
+using std::vector;
using std::string;
-PluginHost::~PluginHost()
+namespace giada {
+namespace m {
+namespace pluginHost
+{
+namespace
+{
+juce::MessageManager *messageManager;
+
+/* pluginFormat
+ * Plugin format manager. */
+
+juce::VSTPluginFormat pluginFormat;
+
+/* knownPuginList
+ * List of known (i.e. scanned) plugins. */
+
+juce::KnownPluginList knownPluginList;
+
+/* unknownPluginList
+ * List of unrecognized plugins found in a patch. */
+
+std::vector<std::string> unknownPluginList;
+
+std::vector<Plugin*> masterOut;
+std::vector<Plugin*> masterIn;
+
+/* Audio|MidiBuffer
+ * Dynamic buffers. */
+
+juce::AudioBuffer<float> audioBuffer;
+
+int samplerate;
+int buffersize;
+
+/* missingPlugins
+ * If some plugins from any stack are missing. */
+
+bool missingPlugins;
+
+}; // {anonymous}
+
+
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+
+
+pthread_mutex_t mutex_midi;
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void close()
{
messageManager->deleteInstance();
}
/* -------------------------------------------------------------------------- */
-void PluginHost::init(int _buffersize, int _samplerate)
+void init(int _buffersize, int _samplerate)
{
- gu_log("[PluginHost::init] initialize with buffersize=%d, samplerate=%d\n",
+ gu_log("[pluginHost::init] initialize with buffersize=%d, samplerate=%d\n",
_buffersize, _samplerate);
messageManager = juce::MessageManager::getInstance();
//unknownPluginList.empty();
loadList(gu_getHomePath() + G_SLASH + "plugins.xml");
- pthread_mutex_init(&mutex_midi, NULL);
+ pthread_mutex_init(&mutex_midi, nullptr);
}
/* -------------------------------------------------------------------------- */
-int PluginHost::scanDir(const string &dirpath, void (*callback)(float progress, 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());
+ 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
bool cont = true;
juce::String name;
while (cont) {
- gu_log("[PluginHost::scanDir] scanning '%s'\n", name.toRawUTF8());
+ gu_log("[pluginHost::scanDir] scanning '%s'\n", name.toRawUTF8());
cont = scanner.scanNextFile(false, name);
if (callback)
callback(scanner.getProgress(), p);
}
- gu_log("[PluginHost::scanDir] %d plugin(s) found\n", knownPluginList.getNumTypes());
+ gu_log("[pluginHost::scanDir] %d plugin(s) found\n", knownPluginList.getNumTypes());
return knownPluginList.getNumTypes();
}
/* -------------------------------------------------------------------------- */
-int PluginHost::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());
+ gu_log("[pluginHost::saveList] unable to save plugin list to %s\n", filepath.c_str());
return out;
}
/* -------------------------------------------------------------------------- */
-int PluginHost::loadList(const string &filepath)
+int loadList(const string &filepath)
{
juce::XmlElement *elem = juce::XmlDocument::parse(juce::File(filepath));
if (elem) {
/* -------------------------------------------------------------------------- */
-Plugin *PluginHost::addPlugin(const string &fid, int stackType,
- pthread_mutex_t *mutex, class Channel *ch)
+Plugin *addPlugin(const string &fid, int stackType,
+ pthread_mutex_t *mutex, Channel *ch)
{
/* Get the proper stack to add the plugin to */
juce::PluginDescription *pd = knownPluginList.getTypeForFile(fid);
if (!pd) {
- gu_log("[PluginHost::addPlugin] no plugin found with fid=%s!\n", fid.c_str());
+ gu_log("[pluginHost::addPlugin] no plugin found with fid=%s!\n", fid.c_str());
missingPlugins = true;
unknownPluginList.push_back(fid);
- return NULL;
+ 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());
+ gu_log("[pluginHost::addPlugin] unable to create instance with fid=%s!\n", fid.c_str());
missingPlugins = true;
- return NULL;
+ return nullptr;
}
- gu_log("[PluginHost::addPlugin] plugin instance with fid=%s created\n", fid.c_str());
+ gu_log("[pluginHost::addPlugin] plugin instance with fid=%s created\n", fid.c_str());
Plugin *p = new Plugin(pi, samplerate, buffersize);
break;
}
- gu_log("[PluginHost::addPlugin] plugin id=%s loaded (%s), stack type=%d, stack size=%d\n",
+ gu_log("[pluginHost::addPlugin] plugin id=%s loaded (%s), stack type=%d, stack size=%d\n",
fid.c_str(), p->getName().c_str(), stackType, pStack->size());
return p;
/* -------------------------------------------------------------------------- */
-Plugin *PluginHost::addPlugin(int index, int stackType, pthread_mutex_t *mutex,
- class 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",
+ 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 NULL;
+ gu_log("[pluginHost::addPlugin] no plugins found at index=%d!\n", index);
+ return nullptr;
}
}
/* -------------------------------------------------------------------------- */
-vector <Plugin *> *PluginHost::getStack(int stackType, Channel *ch)
+vector <Plugin *> *getStack(int stackType, Channel *ch)
{
switch(stackType) {
case MASTER_OUT:
case CHANNEL:
return &ch->plugins;
default:
- return NULL;
+ return nullptr;
}
}
/* -------------------------------------------------------------------------- */
-unsigned PluginHost::countPlugins(int stackType, Channel *ch)
+unsigned countPlugins(int stackType, Channel *ch)
{
vector <Plugin *> *pStack = getStack(stackType, ch);
return pStack->size();
/* -------------------------------------------------------------------------- */
-int PluginHost::countAvailablePlugins()
+int countAvailablePlugins()
{
return knownPluginList.getNumTypes();
}
/* -------------------------------------------------------------------------- */
-unsigned PluginHost::countUnknownPlugins()
+unsigned countUnknownPlugins()
{
return unknownPluginList.size();
}
/* -------------------------------------------------------------------------- */
-PluginHost::PluginInfo PluginHost::getAvailablePluginInfo(int i)
+pluginHost::PluginInfo getAvailablePluginInfo(int i)
{
juce::PluginDescription *pd = knownPluginList.getType(i);
PluginInfo pi;
pi.isInstrument = pd->isInstrument;
/*
if (!p) {
- gu_log("[PluginHost::getAvailablePlugin] unable to create plugin instance!\n");
- return NULL;
+ gu_log("[pluginHost::getAvailablePlugin] unable to create plugin instance!\n");
+ return nullptr;
}
*/
return pi;
/* -------------------------------------------------------------------------- */
-string PluginHost::getUnknownPluginInfo(int i)
+bool hasMissingPlugins()
+{
+ return missingPlugins;
+};
+
+
+/* -------------------------------------------------------------------------- */
+
+
+string getUnknownPluginInfo(int i)
{
return unknownPluginList.at(i);
}
/* -------------------------------------------------------------------------- */
-void PluginHost::freeStack(int stackType, pthread_mutex_t *mutex, Channel *ch)
+void freeStack(int stackType, pthread_mutex_t *mutex, Channel *ch)
{
vector <Plugin *> *pStack;
pStack = getStack(stackType, ch);
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 PluginHost::processStack(float *buffer, int stackType, Channel *ch)
+void processStack(float *buffer, int stackType, Channel *ch)
{
vector <Plugin *> *pStack = getStack(stackType, ch);
/* empty stack, stack not found or mixer not ready: do nothing */
- if (pStack == NULL || pStack->size() == 0)
+ if (pStack == nullptr || pStack->size() == 0)
return;
/* converting buffer from Giada to Juce */
/* -------------------------------------------------------------------------- */
-Plugin *PluginHost::getPluginByIndex(int index, int stackType, Channel *ch)
+Plugin *getPluginByIndex(int index, int stackType, Channel *ch)
{
vector <Plugin *> *pStack = getStack(stackType, ch);
if (pStack->size() == 0)
- return NULL;
+ return nullptr;
if ((unsigned) index >= pStack->size())
- return NULL;
+ return nullptr;
return pStack->at(index);
}
/* -------------------------------------------------------------------------- */
-int PluginHost::getPluginIndex(int id, int stackType, Channel *ch)
+int getPluginIndex(int id, int stackType, Channel *ch)
{
vector <Plugin *> *pStack = getStack(stackType, ch);
for (unsigned i=0; i<pStack->size(); i++)
/* -------------------------------------------------------------------------- */
-void PluginHost::swapPlugin(unsigned indexA, unsigned indexB, int stackType,
+void swapPlugin(unsigned indexA, unsigned indexB, int stackType,
pthread_mutex_t *mutex, Channel *ch)
{
vector <Plugin *> *pStack = getStack(stackType, ch);
/* -------------------------------------------------------------------------- */
-int PluginHost::freePlugin(int id, int stackType, pthread_mutex_t *mutex,
+int freePlugin(int id, int stackType, pthread_mutex_t *mutex,
Channel *ch)
{
vector <Plugin *> *pStack = getStack(stackType, ch);
/* -------------------------------------------------------------------------- */
-void PluginHost::runDispatchLoop()
+void runDispatchLoop()
{
messageManager->runDispatchLoopUntil(10);
- //gu_log("[PluginHost::runDispatchLoop] %d, hasStopMessageBeenSent=%d\n", r, messageManager->hasStopMessageBeenSent());
+ //gu_log("[pluginHost::runDispatchLoop] %d, hasStopMessageBeenSent=%d\n", r, messageManager->hasStopMessageBeenSent());
}
/* -------------------------------------------------------------------------- */
-void PluginHost::freeAllStacks(vector <Channel*> *channels, pthread_mutex_t *mutex)
+void freeAllStacks(vector <Channel*> *channels, pthread_mutex_t *mutex)
{
- freeStack(PluginHost::MASTER_OUT, mutex);
- freeStack(PluginHost::MASTER_IN, mutex);
+ freeStack(pluginHost::MASTER_OUT, mutex);
+ freeStack(pluginHost::MASTER_IN, mutex);
for (unsigned i=0; i<channels->size(); i++)
- freeStack(PluginHost::CHANNEL, mutex, channels->at(i));
+ freeStack(pluginHost::CHANNEL, mutex, channels->at(i));
missingPlugins = false;
unknownPluginList.clear();
}
/* -------------------------------------------------------------------------- */
-int PluginHost::clonePlugin(Plugin *src, int stackType, pthread_mutex_t *mutex,
+int clonePlugin(Plugin *src, int stackType, pthread_mutex_t *mutex,
Channel *ch)
{
Plugin *p = addPlugin(src->getUniqueId(), stackType, mutex, ch);
if (!p) {
- gu_log("[PluginHost::clonePlugin] unable to add new plugin to stack!\n");
+ gu_log("[pluginHost::clonePlugin] unable to add new plugin to stack!\n");
return 0;
}
/* -------------------------------------------------------------------------- */
-bool PluginHost::doesPluginExist(const string &fid)
+bool doesPluginExist(const string &fid)
{
return pluginFormat.doesPluginStillExist(*knownPluginList.getTypeForFile(fid));
}
/* -------------------------------------------------------------------------- */
-void PluginHost::sortPlugins(int method)
+void sortPlugins(int method)
{
switch (method) {
case sortMethod::NAME:
}
}
+}}}; // giada::m::pluginHost::
+
#endif // #ifdef WITH_VST
*
* Giada - Your Hardcore Loopmachine
*
- * pluginHost
- *
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
+
#ifdef WITH_VST
-#ifndef __PLUGIN_HOST_H__
-#define __PLUGIN_HOST_H__
+#ifndef G_PLUGIN_HOST_H
+#define G_PLUGIN_HOST_H
#include <pthread.h>
#include "../deps/juce-config.h"
-using std::string;
-using std::vector;
+class Plugin;
+class Channel;
-class PluginHost
+namespace giada {
+namespace m {
+namespace pluginHost
{
-private:
-
- juce::MessageManager *messageManager;
-
- /* pluginFormat
- * Plugin format manager. */
-
- juce::VSTPluginFormat pluginFormat;
-
- /* knownPuginList
- * List of known (i.e. scanned) plugins. */
-
- juce::KnownPluginList knownPluginList;
-
- /* unknownPluginList
- * List of unrecognized plugins found in a patch. */
-
- vector<string> unknownPluginList;
-
- vector<class Plugin*> masterOut;
- vector<class Plugin*> masterIn;
-
- /* Audio|MidiBuffer
- * Dynamic buffers. */
-
- juce::AudioBuffer<float> audioBuffer;
-
- int samplerate;
- int buffersize;
-
- /* missingPlugins
- * If some plugins from any stack are missing. */
-
- bool missingPlugins;
+enum stackType
+{
+ MASTER_OUT,
+ MASTER_IN,
+ CHANNEL
+};
-public:
+enum sortMethod
+{
+ NAME,
+ CATEGORY,
+ MANUFACTURER,
+ FORMAT
+};
- enum stackType {
- MASTER_OUT,
- MASTER_IN,
- CHANNEL
- };
+struct PluginInfo
+{
+ std::string uid;
+ std::string name;
+ std::string category;
+ std::string manufacturerName;
+ std::string format;
+ bool isInstrument;
+};
- enum sortMethod {
- NAME,
- CATEGORY,
- MANUFACTURER,
- FORMAT
- };
+void init(int bufSize, int frequency);
+void close();
- struct PluginInfo {
- string uid;
- string name;
- string category;
- string manufacturerName;
- string format;
- bool isInstrument;
- };
+/* scanDir
+ * Parse plugin directory and store list in knownPluginList. The callback is
+ * called on each plugin found. Used to update the main window from the GUI
+ * thread. */
- ~PluginHost();
+int scanDir(const std::string &path, void (*callback)(float progress, void *p)=nullptr,
+ void *p=nullptr);
- void init(int bufSize, int frequency);
+/* (save|load)List
+ * (Save|Load) knownPluginList (in|from) an XML file. */
- /* scanDir
- * Parse plugin directory and store list in knownPluginList. The callback is
- * called on each plugin found. Used to update the main window from the GUI
- * thread. */
+int saveList(const std::string &path);
+int loadList(const std::string &path);
- int scanDir(const string &path, void (*callback)(float progress, void *p)=NULL,
- void *p=NULL);
+/* addPlugin
+ * Add a new plugin to 'stackType' by unique id or by index in knownPluginList
+ * std::vector. Requires:
+ * fid - plugin unique file id (i.e. path to dynamic library)
+ * stackType - which stack to add plugin to
+ * mutex - Mixer.mutex_plugin
+ * freq - current audio frequency
+ * bufSize - buffer size
+ * ch - if stackType == CHANNEL. */
- /* (save|load)List
- * (Save|Load) knownPluginList (in|from) an XML file. */
+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);
- int saveList(const string &path);
- int loadList(const string &path);
+/* countPlugins
+ * Return size of 'stackType'. */
- /* addPlugin
- * Add a new plugin to 'stackType' by unique id or by index in knownPluginList
- * vector. Requires:
- * fid - plugin unique file id (i.e. path to dynamic library)
- * stackType - which stack to add plugin to
- * mutex - Mixer.mutex_plugin
- * freq - current audio frequency
- * bufSize - buffer size
- * ch - if stackType == CHANNEL. */
+unsigned countPlugins(int stackType, Channel *ch=nullptr);
- Plugin *addPlugin(const string &fid, int stackType, pthread_mutex_t *mutex,
- class Channel *ch=NULL);
- Plugin *addPlugin(int index, int stackType, pthread_mutex_t *mutex,
- class Channel *ch=NULL);
+/* countAvailablePlugins
+ * Return size of knownPluginList. */
- /* countPlugins
- * Return size of 'stackType'. */
+int countAvailablePlugins();
- unsigned countPlugins(int stackType, class Channel *ch=NULL);
+/* countUnknownPlugins
+ * Return size of unknownPluginList. */
- /* countAvailablePlugins
- * Return size of knownPluginList. */
+unsigned countUnknownPlugins();
- int countAvailablePlugins();
+/* getAvailablePluginInfo
+ * Return the available plugin information (name, type, ...) from
+ * knownPluginList at index 'index'. */
- /* countUnknownPlugins
- * Return size of unknownPluginList. */
+PluginInfo getAvailablePluginInfo(int index);
- unsigned countUnknownPlugins();
+std::string getUnknownPluginInfo(int index);
- /* getAvailablePluginInfo
- * Return the available plugin information (name, type, ...) from
- * knownPluginList at index 'index'. */
+/* freeStack
+ * free plugin stack of type 'stackType'. */
- PluginInfo getAvailablePluginInfo(int index);
+void freeStack(int stackType, pthread_mutex_t *mutex, Channel *ch=nullptr);
- string getUnknownPluginInfo(int index);
+/* processStack
+ * apply the fx list to the buffer. */
- /* freeStack
- * free plugin stack of type 'stackType'. */
+void processStack(float *buffer, int stackType, Channel *ch=nullptr);
- void freeStack(int stackType, pthread_mutex_t *mutex, class Channel *ch=NULL);
+/* getStack
+* Return a std::vector <Plugin *> given the stackType. If stackType == CHANNEL
+* a pointer to Channel is also required. */
- /* processStack
- * apply the fx list to the buffer. */
+std::vector <Plugin *> *getStack(int stackType, Channel *ch=nullptr);
- void processStack(float *buffer, int stackType, class Channel *ch=NULL);
+/* getPluginByIndex */
- /* getStack
- * Return a vector <Plugin *> given the stackType. If stackType == CHANNEL
- * a pointer to Channel is also required. */
+Plugin *getPluginByIndex(int index, int stackType, Channel *ch=nullptr);
- vector <Plugin *> *getStack(int stackType, class Channel *ch=NULL);
+/* getPluginIndex */
- /* getPluginByIndex */
+int getPluginIndex(int id, int stackType, Channel *ch=nullptr);
- Plugin *getPluginByIndex(int index, int stackType, class Channel *ch=NULL);
+/* swapPlugin */
- /* getPluginIndex */
+void swapPlugin(unsigned indexA, unsigned indexB, int stackType,
+ pthread_mutex_t *mutex, Channel *ch=nullptr);
- int getPluginIndex(int id, int stackType, class Channel *ch=NULL);
+/* freePlugin.
+Returns the internal stack index of the deleted plugin. */
- /* swapPlugin */
+int freePlugin(int id, int stackType, pthread_mutex_t *mutex,
+ Channel *ch=nullptr);
- void swapPlugin(unsigned indexA, unsigned indexB, int stackType,
- pthread_mutex_t *mutex, class Channel *ch=NULL);
+/* runDispatchLoop
+ * Wakes up plugins' GUI manager for N milliseconds. */
- /* freePlugin.
- Returns the internal stack index of the deleted plugin. */
+void runDispatchLoop();
- int freePlugin(int id, int stackType, pthread_mutex_t *mutex,
- class Channel *ch=NULL);
+/* freeAllStacks
+ * Frees everything. */
- /* runDispatchLoop
- * Wakes up plugins' GUI manager for N milliseconds. */
+void freeAllStacks(std::vector <Channel*> *channels, pthread_mutex_t *mutex);
- void runDispatchLoop();
+/* clonePlugin */
- /* freeAllStacks
- * Frees everything. */
+int clonePlugin(Plugin *src, int stackType, pthread_mutex_t *mutex,
+ Channel *ch);
- void freeAllStacks(vector <Channel*> *channels, pthread_mutex_t *mutex);
+/* doesPluginExist */
- /* clonePlugin */
+bool doesPluginExist(const std::string &fid);
- int clonePlugin(Plugin *src, int stackType, pthread_mutex_t *mutex,
- class Channel *ch);
+bool hasMissingPlugins();
- /* doesPluginExist */
+void sortPlugins(int sortMethod);
- bool doesPluginExist(const string &fid);
+extern pthread_mutex_t mutex_midi;
- bool hasMissingPlugins() { return missingPlugins; };
+}}}; // giada::m::pluginHost::
- void sortPlugins(int sortMethod);
- pthread_mutex_t mutex_midi;
-};
#endif
#endif // #ifdef WITH_VST
*
* Giada - Your Hardcore Loopmachine
*
- * recorder
- *
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
* -------------------------------------------------------------------------- */
-#include <math.h>
+#include <cassert>
+#include <cmath>
#include "../utils/log.h"
-#include "mixer.h"
-#include "patch_DEPR_.h"
+#include "const.h"
#include "sampleChannel.h"
#include "recorder.h"
-Recorder::Recorder()
- : active (false),
- sortedActions(false)
+using std::vector;
+
+
+namespace giada {
+namespace m {
+namespace recorder
+{
+namespace
+{
+/* composite
+A group of two actions (keypress+keyrel, muteon+muteoff) used during the overdub
+process. */
+
+struct composite
+{
+ action a1;
+ action a2;
+} cmp;
+
+
+/* -------------------------------------------------------------------------- */
+
+
+/* fixOverdubTruncation
+Fixes underlying action truncation when overdubbing over a longer action. I.e.:
+ Original: |#############|
+ Overdub: ---|#######|---
+ fix: |#||#######|--- */
+
+void fixOverdubTruncation(const composite &comp, pthread_mutex_t *mixerMutex)
{
+ action *next = nullptr;
+ int res = getNextAction(comp.a2.chan, comp.a1.type | comp.a2.type, comp.a2.frame,
+ &next);
+ if (res != 1 || next->type != comp.a2.type)
+ return;
+ gu_log("[recorder::fixOverdubTruncation] add truncation at frame %d, type=%d\n",
+ next->frame, next->type);
+ deleteAction(next->chan, next->frame, next->type, false, mixerMutex);
}
+}; // {anonymous}
+
+
/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+
+
+vector<int> frames;
+vector<vector<action*>> global;
+vector<action*> actions; // used internally
+
+bool active = false;
+bool sortedActions = false;
-void Recorder::init()
+/* -------------------------------------------------------------------------- */
+
+
+void init()
{
- sortedActions = false;
active = false;
+ sortedActions = false;
clearAll();
}
/* -------------------------------------------------------------------------- */
-bool Recorder::canRec(Channel *ch, Mixer *mixer)
+bool canRec(Channel *ch, bool clockRunning, bool mixerRecording)
{
/* NO recording if:
* recorder is inactive
* mixer is recording a take somewhere
* channel is empty */
- if (!active ||
- !mixer->running ||
- mixer->recording ||
- (ch->type == CHANNEL_SAMPLE && ((SampleChannel*)ch)->wave == NULL)
+ if (!active ||
+ !clockRunning ||
+ mixerRecording ||
+ (ch->type == CHANNEL_SAMPLE && ((SampleChannel*)ch)->wave == nullptr)
)
return false;
return true;
/* -------------------------------------------------------------------------- */
-void Recorder::rec(int index, int type, int frame, uint32_t iValue, float fValue)
+void rec(int index, int type, int frame, uint32_t iValue, float fValue)
{
/* make sure frame is even */
sortedActions = false;
- gu_log("[REC] action recorded, type=%d frame=%d chan=%d iValue=%d (0x%X) fValue=%f\n",
+ gu_log("[recorder::rec] action recorded, type=%d frame=%d chan=%d iValue=%d (0x%X) fValue=%f\n",
a->type, a->frame, a->chan, a->iValue, a->iValue, a->fValue);
//print();
}
/* -------------------------------------------------------------------------- */
-void Recorder::clearChan(int index)
+void clearChan(int index)
{
- gu_log("[REC] clearing chan %d...\n", index);
+ gu_log("[recorder::clearChan] clearing chan %d...\n", index);
for (unsigned i=0; i<global.size(); i++) { // for each frame i
unsigned j=0;
/* -------------------------------------------------------------------------- */
-void Recorder::clearAction(int index, char act)
+void clearAction(int index, char act)
{
- gu_log("[REC] clearing action %d from chan %d...\n", act, index);
+ gu_log("[recorder::clearAction] clearing action %d from chan %d...\n", act, index);
for (unsigned i=0; i<global.size(); i++) { // for each frame i
unsigned j=0;
while (true) { // for each action j of frame i
/* -------------------------------------------------------------------------- */
-void Recorder::deleteAction(int chan, int frame, char type, bool checkValues,
+void deleteAction(int chan, int frame, char type, bool checkValues,
pthread_mutex_t *mixerMutex, uint32_t iValue, float fValue)
{
/* make sure frame is even */
break;
}
else
- gu_log("[REC] delete action: waiting for mutex...\n");
+ gu_log("[recorder::deleteAction] waiting for mutex...\n");
}
}
}
if (found) {
optimize();
- gu_log("[REC] action deleted, type=%d frame=%d chan=%d iValue=%d (%X) fValue=%f\n",
+ gu_log("[recorder::deleteAction] action deleted, type=%d frame=%d chan=%d iValue=%d (%X) fValue=%f\n",
type, frame, chan, iValue, iValue, fValue);
}
else
- gu_log("[REC] unable to delete action, not found! type=%d frame=%d chan=%d iValue=%d (%X) fValue=%f\n",
+ gu_log("[recorder::deleteAction] unable to delete action, not found! type=%d frame=%d chan=%d iValue=%d (%X) fValue=%f\n",
type, frame, chan, iValue, iValue, fValue);
}
/* -------------------------------------------------------------------------- */
-void Recorder::deleteActions(int chan, int frame_a, int frame_b, char type,
+void deleteActions(int chan, int frame_a, int frame_b, char type,
pthread_mutex_t *mixerMutex)
{
sortActions();
/* -------------------------------------------------------------------------- */
-void Recorder::clearAll()
+void clearAll()
{
while (global.size() > 0) {
for (unsigned i=0; i<global.size(); i++) {
/* -------------------------------------------------------------------------- */
-void Recorder::optimize()
+void optimize()
{
/* do something until the i frame is empty. */
/* -------------------------------------------------------------------------- */
-void Recorder::sortActions()
+void sortActions()
{
if (sortedActions)
return;
/* -------------------------------------------------------------------------- */
-void Recorder::updateBpm(float oldval, float newval, int oldquanto)
+void updateBpm(float oldval, float newval, int oldquanto)
{
for (unsigned i=0; i<frames.size(); i++) {
* and the quantizer set to 44100. That would mean two recs completely
* useless. So we compute a reject value ('scarto'): if it's lower
* than 6 frames the new frame is collapsed with a quantized frame. */
- /** CHECKME - maybe 6 frames are too low */
+ /** XXX - maybe 6 frames are too low */
if (frames.at(i) != 0) {
int scarto = oldquanto % frames.at(i);
/* -------------------------------------------------------------------------- */
-void Recorder::updateSamplerate(int systemRate, int patchRate)
+void updateSamplerate(int systemRate, int patchRate)
{
/* diff ratio: systemRate / patchRate
* e.g. 44100 / 96000 = 0.4... */
if (systemRate == patchRate)
return;
- gu_log("[REC] systemRate (%d) != patchRate (%d), converting...\n", systemRate, patchRate);
+ gu_log("[recorder::updateSamplerate] systemRate (%d) != patchRate (%d), converting...\n", systemRate, patchRate);
float ratio = systemRate / (float) patchRate;
for (unsigned i=0; i<frames.size(); i++) {
- gu_log("[REC] oldFrame = %d", frames.at(i));
+ gu_log("[recorder::updateSamplerate] oldFrame = %d", frames.at(i));
float newFrame = frames.at(i);
newFrame = floorf(newFrame * ratio);
/* -------------------------------------------------------------------------- */
-void Recorder::expand(int old_fpb, int new_fpb)
+void expand(int old_fpb, int new_fpb)
{
/* this algorithm requires multiple passages if we expand from e.g. 2
* to 16 beats, precisely 16 / 2 - 1 = 7 times (-1 is the first group,
}
}
}
- gu_log("[REC] expanded recs\n");
+ gu_log("[recorder::expand] expanded recs\n");
//print();
}
/* -------------------------------------------------------------------------- */
-void Recorder::shrink(int new_fpb)
+void shrink(int new_fpb)
{
/* easier than expand(): here we delete eveything beyond old_framesPerBars. */
i++;
}
optimize();
- gu_log("[REC] shrinked recs\n");
+ gu_log("[recorder::shrink] shrinked recs\n");
//print();
}
/* -------------------------------------------------------------------------- */
-bool Recorder::hasActions(int chanIndex)
+bool hasActions(int chanIndex)
{
if (global.size() == 0)
return false;
/* -------------------------------------------------------------------------- */
-int Recorder::getNextAction(int chan, char type, int frame, action **out,
+int getNextAction(int chan, char type, int frame, action **out,
uint32_t iValue)
{
sortActions(); // mandatory
/* -------------------------------------------------------------------------- */
-int Recorder::getAction(int chan, char action, int frame, struct action **out)
+int getAction(int chan, char action, int frame, struct action **out)
{
for (unsigned i=0; i<global.size(); i++)
for (unsigned j=0; j<global.at(i).size(); j++)
/* -------------------------------------------------------------------------- */
-void Recorder::startOverdub(int index, char actionMask, int frame,
- unsigned bufferSize)
+void startOverdub(int index, char actionMask, int frame, unsigned bufferSize)
{
/* prepare the composite struct */
- if (actionMask == ACTION_KEYS) {
- cmp.a1.type = ACTION_KEYPRESS;
- cmp.a2.type = ACTION_KEYREL;
+ if (actionMask == G_ACTION_KEYS) {
+ cmp.a1.type = G_ACTION_KEYPRESS;
+ cmp.a2.type = G_ACTION_KEYREL;
}
else {
- cmp.a1.type = ACTION_MUTEON;
- cmp.a2.type = ACTION_MUTEOFF;
+ cmp.a1.type = G_ACTION_MUTEON;
+ cmp.a2.type = G_ACTION_MUTEOFF;
}
cmp.a1.chan = index;
cmp.a2.chan = index;
rec(index, cmp.a1.type, frame);
- action *act = NULL;
+ action *act = nullptr;
int res = getNextAction(index, cmp.a1.type | cmp.a2.type, cmp.a1.frame, &act);
if (res == 1) {
if (act->type == cmp.a2.type) {
int truncFrame = cmp.a1.frame - bufferSize;
if (truncFrame < 0)
truncFrame = 0;
- gu_log("[REC] add truncation at frame %d, type=%d\n", truncFrame, cmp.a2.type);
+ gu_log("[recorder::startOverdub] add truncation at frame %d, type=%d\n", truncFrame, cmp.a2.type);
rec(index, cmp.a2.type, truncFrame);
}
}
/* -------------------------------------------------------------------------- */
-void Recorder::stopOverdub(Mixer *mixer)
+void stopOverdub(int currentFrame, int totalFrames, pthread_mutex_t *mixerMutex)
{
- cmp.a2.frame = mixer->currentFrame;
+ cmp.a2.frame = currentFrame;
bool ringLoop = false;
bool nullLoop = false;
- /* ring loop verification, i.e. a composite action with key_press at
- * frame N and key_release at frame M, with M <= N */
-
- if (cmp.a2.frame < cmp.a1.frame) {
+ /* Check for ring loops or null loops. Ring loop: a composite action with
+ key_press at frame N and key_release at frame M, with M <= N.
+ Null loop: a composite action that begins and ends on the very same frame,
+ i.e. with 0 size. Very unlikely.
+ If ring loop: record the last action at the end of the sequencer (that
+ is 'totalFrames').
+ If null loop: remove previous action and do nothing. Also make sure to avoid
+ underlying action truncation, if the null loop occurs inside a composite
+ action. */
+
+ if (cmp.a2.frame < cmp.a1.frame) { // ring loop
ringLoop = true;
- gu_log("[REC] ring loop! frame1=%d < frame2=%d\n", cmp.a1.frame, cmp.a2.frame);
- rec(cmp.a2.chan, cmp.a2.type, mixer->totalFrames); // record at the end of the sequencer
+ gu_log("[recorder::stopOverdub] ring loop! frame1=%d < frame2=%d\n", cmp.a1.frame, cmp.a2.frame);
+ rec(cmp.a2.chan, cmp.a2.type, totalFrames);
}
else
- if (cmp.a2.frame == cmp.a1.frame) {
+ if (cmp.a2.frame == cmp.a1.frame) { // null loop
nullLoop = true;
- gu_log("[REC] null loop! frame1=%d == frame2=%d\n", cmp.a1.frame, cmp.a2.frame);
- deleteAction(cmp.a1.chan, cmp.a1.frame, cmp.a1.type, false, &mixer->mutex_recs); // false == don't check values
- }
+ gu_log("[recorder::stopOverdub] null loop! frame1=%d == frame2=%d\n", cmp.a1.frame, cmp.a2.frame);
+ deleteAction(cmp.a1.chan, cmp.a1.frame, cmp.a1.type, false, mixerMutex); // false == don't check values
+ fixOverdubTruncation(cmp, mixerMutex);
+ }
- /* remove any nested action between keypress----keyrel, then record */
+ if (nullLoop)
+ return;
- if (!nullLoop) {
- deleteActions(cmp.a2.chan, cmp.a1.frame, cmp.a2.frame, cmp.a1.type, &mixer->mutex_recs);
- deleteActions(cmp.a2.chan, cmp.a1.frame, cmp.a2.frame, cmp.a2.type, &mixer->mutex_recs);
- }
+ /* Remove any nested action between keypress----keyrel. */
- if (!ringLoop && !nullLoop) {
- rec(cmp.a2.chan, cmp.a2.type, cmp.a2.frame);
+ deleteActions(cmp.a2.chan, cmp.a1.frame, cmp.a2.frame, cmp.a1.type, mixerMutex);
+ deleteActions(cmp.a2.chan, cmp.a1.frame, cmp.a2.frame, cmp.a2.type, mixerMutex);
- /* avoid underlying action truncation, if keyrel happens inside a
- * composite action */
+ if (ringLoop)
+ return;
- action *act = NULL;
- int res = getNextAction(cmp.a2.chan, cmp.a1.type | cmp.a2.type, cmp.a2.frame, &act);
- if (res == 1 && act->type == cmp.a2.type) {
- gu_log("[REC] add truncation at frame %d, type=%d\n", act->frame, act->type);
- deleteAction(act->chan, act->frame, act->type, false, &mixer->mutex_recs); // false == don't check values
- }
- }
-}
+ /* Record second part of the composite action. Also make sure to avoid
+ underlying action truncation, if keyrel happens inside a composite action. */
-
-/* -------------------------------------------------------------------------- */
+ rec(cmp.a2.chan, cmp.a2.type, cmp.a2.frame);
+ fixOverdubTruncation(cmp, mixerMutex);
+}
-void Recorder::print()
-{
- gu_log("[REC] ** print debug **\n");
- for (unsigned i=0; i<global.size(); i++) {
- gu_log(" frame %d\n", frames.at(i));
- for (unsigned j=0; j<global.at(i).size(); j++) {
- gu_log(" action %d | chan %d | frame %d\n", global.at(i).at(j)->type,
- global.at(i).at(j)->chan, global.at(i).at(j)->frame);
- }
- }
-}
+}}}; // giada::m::recorder::
*
* Giada - Your Hardcore Loopmachine
*
- * recorder
- *
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
* -------------------------------------------------------------------------- */
-#ifndef RECORDER_H
-#define RECORDER_H
+#ifndef G_RECORDER_H
+#define G_RECORDER_H
-#ifdef __APPLE__ // our compiler still doesn't know about cstdint (c++11 stuff)
+#ifdef __APPLE__ // our Clang still doesn't know about cstdint (c++11 stuff)
#include <stdint.h>
#else
#include <cstdint>
#include <pthread.h>
-using std::vector;
+class Channel;
-class Recorder
+namespace giada {
+namespace m {
+namespace recorder
{
-public:
-
- Recorder();
-
- /* action
- * struct containing fields to describe an atomic action. Note from
- * VST sdk: parameter values, like all VST parameters, are declared as
- * floats with an inclusive range of 0.0 to 1.0 (fValue). */
-
- struct action
- {
- int chan; // channel index, i.e. Channel->index
- int type;
- int frame; // redundant info, used by helper functions
- float fValue; // used only for envelopes (volumes, vst params).
- uint32_t iValue; // used only for MIDI events
- };
-
- /* [global0]-->[vector<_action*>0]-->[a0][a1][a2] 0[frames1]
- * [global1]-->[vector<_action*>1]-->[a0][a1][a2] 1[frames2]
- * [global2]-->[vector<_action*>2]-->[a0][a1][a2] 2[frames3]
- * [global3]-->[vector<_action*>3]-->[a0][a1][a2] 3[frames4] */
+/* action
+ * struct containing fields to describe an atomic action. Note from
+ * VST sdk: parameter values, like all VST parameters, are declared as
+ * floats with an inclusive range of 0.0 to 1.0 (fValue). */
- vector<int> frames; // frame counter (sentinel) frames.size == global.size
- vector<vector<action*>> global; // container of containers of actions
- vector<action*> actions; // container of actions
-
- bool active;
- bool sortedActions; // are actions sorted via sortActions()?
+struct action
+{
+ int chan; // channel index, i.e. Channel->index
+ int type;
+ int frame; // redundant info, used by helper functions
+ float fValue; // used only for envelopes (volumes, vst params).
+ uint32_t iValue; // used only for MIDI events
+};
- /* init
- * everything starts from here. */
+/* frames
+Frame counter sentinel. It tells which frames contain actions. E.g.:
+ frames[0] = 155 // some actions on frame 155
+ frames[1] = 2048 // some actions on frame 2048
+It always matches 'global''s size: frames.size() == global.size() */
- void init();
+extern std::vector<int> frames;
- /* hasActions
- Checks if the channel has at least one action recorded. Used after an
- action deletion. */
+/* global
+Contains the actual actions. E.g.:
+ global[0] = <actions>
+ global[1] = <actions> */
- bool hasActions(int chanIndex);
+extern std::vector<std::vector<action*>> global;
- /* canRec
- * can a channel rec an action? Call this one BEFORE rec(). */
+extern bool active;
+extern bool sortedActions; // are actions sorted via sortActions()?
- bool canRec(class Channel *ch, class Mixer *m);
+/* init
+ * everything starts from here. */
- /* rec
- * record an action. */
+void init();
- void rec(int chan, int action, int frame, uint32_t iValue=0,
- float fValue=0.0f);
+/* hasActions
+Checks if the channel has at least one action recorded. Used after an
+action deletion. */
- /* clearChan
- * clear all actions from a channel. */
+bool hasActions(int chanIndex);
- void clearChan(int chan);
+/* canRec
+ * can a channel rec an action? Call this one BEFORE rec(). */
- /* clearAction
- * clear the 'action' action type from a channel. */
+bool canRec(Channel *ch, bool clockRunning, bool mixerRecording);
- void clearAction(int chan, char action);
+/* rec
+ * record an action. */
- /* deleteAction
- * delete ONE action. Useful in the action editor. 'type' can be a mask. */
+void rec(int chan, int action, int frame, uint32_t iValue=0, float fValue=0.0f);
- void deleteAction(int chan, int frame, char type, bool checkValues,
- pthread_mutex_t *mixerMutex, uint32_t iValue=0, float fValue=0.0);
+/* clearChan
+ * clear all actions from a channel. */
- /* deleteActions
- * delete A RANGE of actions from frame_a to frame_b in channel 'chan'.
- * 'type' can be a bitmask. Exclusive range (frame_a, frame_b). */
+void clearChan(int chan);
- void deleteActions(int chan, int frame_a, int frame_b, char type,
- pthread_mutex_t *mixerMutex);
+/* clearAction
+ * clear the 'action' action type from a channel. */
- /* clearAll
- * delete everything. */
+void clearAction(int chan, char action);
- void clearAll();
+/* deleteAction
+ * delete ONE action. Useful in the action editor. 'type' can be a mask. */
- /* optimize
- * clear frames without actions. */
+void deleteAction(int chan, int frame, char type, bool checkValues,
+ pthread_mutex_t *mixerMutex, uint32_t iValue=0, float fValue=0.0);
- void optimize();
+/* deleteActions
+Deletes A RANGE of actions from frame_a to frame_b in channel 'chan' of type
+'type' (can be a bitmask). Exclusive range (frame_a, frame_b). */
- /* sortActions
- * sorts actions by frame, asc mode. */
+void deleteActions(int chan, int frame_a, int frame_b, char type,
+ pthread_mutex_t *mixerMutex);
- void sortActions();
+/* clearAll
+ * delete everything. */
- /* updateBpm
- * reassign frames by calculating the new bpm value. */
+void clearAll();
- void updateBpm(float oldval, float newval, int oldquanto);
+/* optimize
+ * clear frames without actions. */
- /* updateSamplerate
- * reassign frames taking in account the samplerate. If f_system ==
- * f_patch nothing changes, otherwise the conversion is mandatory. */
+void optimize();
- void updateSamplerate(int systemRate, int patchRate);
+/* sortActions
+ * sorts actions by frame, asc mode. */
- void expand(int old_fpb, int new_fpb);
- void shrink(int new_fpb);
+void sortActions();
- /* getNextAction
- * Return the nearest action in chan 'chan' of type 'action' starting
- * from 'frame'. Action can be a bitmask. If iValue != 0 search for
- * next action with iValue == iValue: useful for MIDI key_release. iValue
- * can be a bitmask. */
+/* updateBpm
+ * reassign frames by calculating the new bpm value. */
- int getNextAction(int chan, char action, int frame, struct action **out,
- uint32_t iValue=0);
+void updateBpm(float oldval, float newval, int oldquanto);
- /* getAction
- * return a pointer to action in chan 'chan' of type 'action' at frame
- * 'frame'. */
+/* updateSamplerate
+ * reassign frames taking in account the samplerate. If f_system ==
+ * f_patch nothing changes, otherwise the conversion is mandatory. */
- int getAction(int chan, char action, int frame, struct action **out);
+void updateSamplerate(int systemRate, int patchRate);
- /* start/endOverdub */
+void expand(int old_fpb, int new_fpb);
+void shrink(int new_fpb);
- void startOverdub(int chan, char action, int frame, unsigned bufferSize);
- void stopOverdub(class Mixer *m);
+/* getNextAction
+ * Return the nearest action in chan 'chan' of type 'action' starting
+ * from 'frame'. Action can be a bitmask. If iValue != 0 search for
+ * next action with iValue == iValue: useful for MIDI key_release. iValue
+ * can be a bitmask. */
-private:
+int getNextAction(int chan, char action, int frame, struct action **out,
+ uint32_t iValue=0);
- /* composite
- * a group of two actions (keypress+keyrel, muteon+muteoff) used during
- * the overdub process */
+/* getAction
+ * return a pointer to action in chan 'chan' of type 'action' at frame
+ * 'frame'. */
- struct composite
- {
- action a1;
- action a2;
- } cmp;
+int getAction(int chan, char action, int frame, struct action **out);
- /* print
- * debug of the frame stack. */
+/* start/stopOverdub
+These functions are used when you overwrite existing actions. For example:
+pressing Mute button on a channel with some existing mute actions. */
- void print();
+void startOverdub(int chan, char action, int frame, unsigned bufferSize);
+void stopOverdub(int currentFrame, int totalFrames, pthread_mutex_t *mixerMutex);
-};
+}}}; // giada::m::recorder::
#endif
*
* Giada - Your Hardcore Loopmachine
*
- * channel
- *
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
* -------------------------------------------------------------------------- */
-#include <math.h>
+#include <cmath>
+#include <cstring>
#include "../utils/log.h"
+#include "../utils/fs.h"
#include "../utils/string.h"
#include "sampleChannel.h"
-#include "patch_DEPR_.h"
#include "patch.h"
+#include "const.h"
#include "conf.h"
+#include "clock.h"
#include "mixer.h"
#include "wave.h"
#include "pluginHost.h"
using std::string;
+using namespace giada::m;
-extern Recorder G_Recorder;
-extern KernelAudio G_KernelAudio;
-
-
-SampleChannel::SampleChannel(int bufferSize, MidiMapConf *midiMapConf)
- : Channel (CHANNEL_SAMPLE, STATUS_EMPTY, bufferSize, midiMapConf),
+SampleChannel::SampleChannel(int bufferSize, bool inputMonitor)
+ : Channel (CHANNEL_SAMPLE, STATUS_EMPTY, bufferSize),
frameRewind (-1),
- wave (NULL),
+ boost (G_DEFAULT_BOOST),
+ pitch (G_DEFAULT_PITCH),
+ wave (nullptr),
tracker (0),
begin (0),
end (0),
- pitch (G_DEFAULT_PITCH),
- boost (1.0f),
- mode (DEFAULT_CHANMODE),
+ mode (G_DEFAULT_CHANMODE),
qWait (false),
fadeinOn (false),
fadeinVol (1.0f),
fadeoutOn (false),
fadeoutVol (1.0f),
fadeoutTracker (0),
- fadeoutStep (DEFAULT_FADEOUT_STEP),
+ fadeoutStep (G_DEFAULT_FADEOUT_STEP),
+ inputMonitor (inputMonitor),
midiInReadActions(0x0),
midiInPitch (0x0)
{
- rsmp_state = src_new(SRC_LINEAR, 2, NULL);
- pChan = (float *) malloc(G_KernelAudio.realBufsize * 2 * sizeof(float));
+ rsmp_state = src_new(SRC_LINEAR, 2, nullptr);
+ pChan = (float *) malloc(kernelAudio::getRealBufSize() * 2 * sizeof(float));
}
{
string oldName = wave->name;
int k = 1; // Start from k = 1, zero is too nerdy
- while (!mh_uniqueSampleName(this, wave->name)) {
+ while (!mh::uniqueSampleName(this, wave->name)) {
wave->updateName((oldName + "-" + gu_itoa(k)).c_str());
k++;
}
/** TODO - these memsets can be done only if status PLAY (if below),
* but it would require extra clearPChan calls when samples stop */
- memset(vChan, 0, sizeof(float) * bufferSize);
- memset(pChan, 0, sizeof(float) * bufferSize);
+ std::memset(vChan, 0, sizeof(float) * bufferSize);
+ std::memset(pChan, 0, sizeof(float) * bufferSize);
if (status & (STATUS_PLAY | STATUS_ENDING)) {
tracker = fillChan(vChan, tracker, 0);
{
/* method: check this frame && next frame, then calculate delta */
- Recorder::action *a0 = NULL;
- Recorder::action *a1 = NULL;
+ recorder::action *a0 = nullptr;
+ recorder::action *a1 = nullptr;
int res;
/* get this action on frame 'frame'. It's unlikely that the action
* is not found. */
- res = G_Recorder.getAction(index, ACTION_VOLUME, frame, &a0);
+ res = recorder::getAction(index, G_ACTION_VOLUME, frame, &a0);
if (res == 0)
return;
/* get the action next to this one.
* res == -1: a1 not found, this is the last one. Rewind the search
* and use action at frame number 0 (actions[0]).
- * res == -2 ACTION_VOLUME not found. This should never happen */
+ * res == -2 G_ACTION_VOLUME not found. This should never happen */
- res = G_Recorder.getNextAction(index, ACTION_VOLUME, frame, &a1);
+ res = recorder::getNextAction(index, G_ACTION_VOLUME, frame, &a1);
if (res == -1)
- res = G_Recorder.getAction(index, ACTION_VOLUME, 0, &a1);
+ res = recorder::getAction(index, G_ACTION_VOLUME, 0, &a1);
volume_i = a0->fValue;
volume_d = ((a1->fValue - a0->fValue) / ((a1->frame - a0->frame) / 2)) * 1.003f;
/* -------------------------------------------------------------------------- */
-void SampleChannel::setBegin(unsigned v)
+void SampleChannel::setBegin(int v)
{
- begin = v;
+ if (v < 0)
+ begin = 0;
+ else
+ if (v > wave->size)
+ begin = wave->size - 2;
+ else
+ if (v >= end)
+ begin = end - 2;
+ else
+ begin = v;
tracker = begin;
}
/* -------------------------------------------------------------------------- */
-void SampleChannel::setEnd(unsigned v)
+void SampleChannel::setEnd(int v)
{
- end = v;
+ if (v < 0)
+ end = begin + 2;
+ else
+ if (v > wave->size)
+ end = wave->size;
+ else
+ if (v <= begin)
+ end = begin + 2;
+ else
+ end = v;
}
void SampleChannel::setPitch(float v)
{
- pitch = v;
+ if (v > G_MAX_PITCH)
+ pitch = G_MAX_PITCH;
+ else
+ if (v < 0.1f)
+ pitch = 0.1000f;
+ else
+ pitch = v;
+
rsmp_data.src_ratio = 1/pitch;
/* if status is off don't slide between frequencies */
}
+float SampleChannel::getPitch()
+{
+ return pitch;
+}
+
+
/* -------------------------------------------------------------------------- */
{
/* rewind LOOP_ANY or SINGLE_ANY only if it's in read-record-mode */
- if (wave != NULL) {
+ if (wave != nullptr) {
if ((mode & LOOP_ANY) || (recStatus == REC_READING && (mode & SINGLE_ANY)))
reset(0); // rewind is user-generated events, always on frame 0
}
/* -------------------------------------------------------------------------- */
-void SampleChannel::parseAction(Recorder::action *a, int localFrame,
+void SampleChannel::parseAction(recorder::action *a, int localFrame,
int globalFrame, int quantize, bool mixerIsRunning)
{
if (readActions == false)
return;
switch (a->type) {
- case ACTION_KEYPRESS:
+ case G_ACTION_KEYPRESS:
if (mode & SINGLE_ANY)
start(localFrame, false, quantize, mixerIsRunning, false, false);
break;
- case ACTION_KEYREL:
+ case G_ACTION_KEYREL:
if (mode & SINGLE_ANY)
stop();
break;
- case ACTION_KILLCHAN:
+ case G_ACTION_KILL:
if (mode & SINGLE_ANY)
kill(localFrame);
break;
- case ACTION_MUTEON:
+ case G_ACTION_MUTEON:
setMute(true); // internal mute
break;
- case ACTION_MUTEOFF:
+ case G_ACTION_MUTEOFF:
unsetMute(true); // internal mute
break;
- case ACTION_VOLUME:
+ case G_ACTION_VOLUME:
calcVolumeEnv(globalFrame);
break;
}
void SampleChannel::sum(int frame, bool running)
{
- if (wave == NULL || status & ~(STATUS_PLAY | STATUS_ENDING))
+ if (wave == nullptr || status & ~(STATUS_PLAY | STATUS_ENDING))
return;
if (frame != frameRewind) {
void SampleChannel::onZero(int frame, bool recsStopOnChanHalt)
{
- if (wave == NULL)
+ if (wave == nullptr)
return;
if (mode & LOOP_ANY) {
/* -------------------------------------------------------------------------- */
-void SampleChannel::quantize(int index, int localFrame, Mixer *mixer)
+void SampleChannel::quantize(int index, int localFrame)
{
/* skip if LOOP_ANY or not in quantizer-wait mode */
/* this is the moment in which we record the keypress, if the
* quantizer is on. SINGLE_PRESS needs overdub */
- if (G_Recorder.canRec(this, mixer)) {
+ if (recorder::canRec(this, clock::isRunning(), mixer::recording)) {
if (mode == SINGLE_PRESS) {
- G_Recorder.startOverdub(index, ACTION_KEYS, mixer->currentFrame,
- G_KernelAudio.realBufsize);
+ recorder::startOverdub(index, G_ACTION_KEYS, clock::getCurrentFrame(),
+ kernelAudio::getRealBufSize());
readActions = false; // don't read actions while overdubbing
}
else
- G_Recorder.rec(index, ACTION_KEYPRESS, mixer->currentFrame);
+ recorder::rec(index, G_ACTION_KEYPRESS, clock::getCurrentFrame());
hasActions = true;
}
}
/* -------------------------------------------------------------------------- */
+void SampleChannel::setBoost(float v)
+{
+ if (v > G_MAX_BOOST_DB)
+ boost = G_MAX_BOOST_DB;
+ else
+ if (v < 0.0f)
+ boost = 0.0f;
+ else
+ boost = v;
+}
+
+
+float SampleChannel::getBoost()
+{
+ return boost;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
void SampleChannel::calcFadeoutStep()
{
- if (end - tracker < (1 / DEFAULT_FADEOUT_STEP) * 2)
+ if (end - tracker < (1 / G_DEFAULT_FADEOUT_STEP) * 2)
fadeoutStep = ceil((end - tracker) / volume) * 2; /// or volume_i ???
else
- fadeoutStep = DEFAULT_FADEOUT_STEP;
+ fadeoutStep = G_DEFAULT_FADEOUT_STEP;
}
status = STATUS_OFF;
if (wave) {
delete wave;
- wave = NULL;
+ wave = nullptr;
}
- status = STATUS_EMPTY;
+ begin = 0;
+ end = 0;
+ tracker = 0;
+ status = STATUS_EMPTY;
+ volume = G_DEFAULT_VOL;
+ boost = G_DEFAULT_BOOST;
sendMidiLplay();
}
void SampleChannel::process(float *outBuffer, float *inBuffer)
{
- /* If armed and inbuffer is not null (i.e. input device available), copy input
- buffer to vChan: this enables the live recording mode. The vChan will be
- overwritten later by PluginHost::processStack, so that you would record "clean"
- audio (i.e. not plugin-processed). */
+ /* If armed and inbuffer is not nullptr (i.e. input device available) and
+ input monitor is on, copy input buffer to vChan: this enables the input
+ monitoring. The vChan will be overwritten later by pluginHost::processStack,
+ so that you would record "clean" audio (i.e. not plugin-processed). */
- if (armed && inBuffer)
+ if (armed && inBuffer && inputMonitor)
for (int i=0; i<bufferSize; i++)
vChan[i] += inBuffer[i]; // add, don't overwrite (no raw memcpy)
#ifdef WITH_VST
- pluginHost->processStack(vChan, PluginHost::CHANNEL, this);
+ pluginHost::processStack(vChan, pluginHost::CHANNEL, this);
#endif
- for (int j=0; j<bufferSize; j+=2) {
- outBuffer[j] += vChan[j] * volume * panLeft * boost;
- outBuffer[j+1] += vChan[j+1] * volume * panRight * boost;
+ for (int j=0; j<bufferSize; j+=2) {
+ outBuffer[j] += vChan[j] * volume * calcPanning(0) * boost;
+ outBuffer[j+1] += vChan[j+1] * volume * calcPanning(1) * boost;
}
}
void SampleChannel::kill(int frame)
{
- if (wave != NULL && status != STATUS_OFF) {
+ if (wave != nullptr && status != STATUS_OFF) {
if (mute || mute_i || (status == STATUS_WAIT && mode & LOOP_ANY))
hardStop(frame);
else
/* -------------------------------------------------------------------------- */
-int SampleChannel::readPatch_DEPR_(const char *f, int i, Patch_DEPR_ *patch,
- int samplerate, int rsmpQuality)
-{
- int res = load(f, samplerate, rsmpQuality);
-
- volume = patch->getVol(i);
- key = patch->getKey(i);
- index = patch->getIndex(i);
- mode = patch->getMode(i);
- mute = patch->getMute(i);
- mute_s = patch->getMute_s(i);
- solo = patch->getSolo(i);
- boost = patch->getBoost(i);
- panLeft = patch->getPanLeft(i);
- panRight = patch->getPanRight(i);
- readActions = patch->getRecActive(i);
- recStatus = readActions ? REC_READING : REC_STOPPED;
-
- readPatchMidiIn_DEPR_(i, *patch);
- midiInReadActions = patch->getMidiValue(i, "InReadActions");
- midiInPitch = patch->getMidiValue(i, "InPitch");
- readPatchMidiOut_DEPR_(i, *patch);
-
- if (res == SAMPLE_LOADED_OK) {
- setBegin(patch->getBegin(i));
- setEnd (patch->getEnd(i, wave->size));
- setPitch(patch->getPitch(i));
- }
- else {
- // volume = DEFAULT_VOL;
- // mode = DEFAULT_CHANMODE;
- // status = STATUS_WRONG;
- // key = 0;
-
- if (res == SAMPLE_LEFT_EMPTY)
- status = STATUS_EMPTY;
- else
- if (res == SAMPLE_READ_ERROR)
- status = STATUS_MISSING;
- sendMidiLplay();
- }
-
- return res;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-int SampleChannel::readPatch(const string &basePath, int i, Patch *patch,
+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, patch, pluginMutex, samplerate, rsmpQuality);
+ Channel::readPatch("", i, pluginMutex, samplerate, rsmpQuality);
- Patch::channel_t *pch = &patch->channels.at(i);
+ patch::channel_t *pch = &patch::channels.at(i);
mode = pch->mode;
boost = pch->boost;
recStatus = readActions ? REC_READING : REC_STOPPED;
midiInReadActions = pch->midiInReadActions;
midiInPitch = pch->midiInPitch;
+ inputMonitor = pch->inputMonitor;
int res = load((basePath + pch->samplePath).c_str(), samplerate, rsmpQuality);
if (res == SAMPLE_LOADED_OK) {
bool SampleChannel::canInputRec()
{
- return wave == NULL && armed;
+ return wave == nullptr && armed;
}
/* -------------------------------------------------------------------------- */
-int SampleChannel::writePatch(int i, bool isProject, Patch *patch)
+int SampleChannel::writePatch(int i, bool isProject)
{
// TODO - this code belongs to an upper level (glue)
- int pchIndex = Channel::writePatch(i, isProject, patch);
- Patch::channel_t *pch = &patch->channels.at(pchIndex);
+ int pchIndex = Channel::writePatch(i, isProject);
+ patch::channel_t *pch = &patch::channels.at(pchIndex);
- if (wave != NULL) {
+ if (wave != nullptr) {
pch->samplePath = wave->pathfile;
if (isProject)
pch->samplePath = gu_basename(wave->pathfile); // make it portable
pch->boost = boost;
pch->recActive = readActions;
pch->pitch = pitch;
+ pch->inputMonitor = inputMonitor;
pch->midiInReadActions = midiInReadActions;
pch->midiInPitch = midiInPitch;
*
* Giada - Your Hardcore Loopmachine
*
- * sampleChannel
- *
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
* -------------------------------------------------------------------------- */
-#ifndef SAMPLE_CHANNEL_H
-#define SAMPLE_CHANNEL_H
+#ifndef G_SAMPLE_CHANNEL_H
+#define G_SAMPLE_CHANNEL_H
#include <samplerate.h>
#include "channel.h"
+class Patch;
+class Wave;
+
+
class SampleChannel : public Channel
{
private:
SRC_DATA rsmp_data;
/* pChan
- * extra virtual channel for processing resampled data. */
+ Extra virtual channel for processing resampled data. */
float *pChan;
/* frameRewind
- * exact frame in which a rewind occurs */
+ Exact frame in which a rewind occurs. */
int frameRewind;
+ float boost;
+ float pitch;
+
/* fillChan
* copy from wave to *dest and resample data from wave, if necessary.
* Start to fill pChan from byte 'offset'. If rewind=false don't
public:
- SampleChannel(int bufferSize, class MidiMapConf *midiMapConf);
+ SampleChannel(int bufferSize, bool inputMonitor);
~SampleChannel();
void copy(const Channel *src, pthread_mutex_t *pluginMutex) override;
void rewind() override;
void setMute(bool internal) override;
void unsetMute(bool internal) override;
- int readPatch_DEPR_(const char *file, int i, class Patch_DEPR_ *patch,
- int samplerate, int rsmpQuality) override;
- int readPatch(const string &basePath, int i, class Patch *patch,
- pthread_mutex_t *pluginMutex, int samplerate, int rsmpQuality) override;
- int writePatch(int i, bool isProject, class Patch *patch) override;
- void quantize(int index, int localFrame, class Mixer *m) override;
+ 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(Recorder::action *a, int localFrame, int globalFrame,
+ void parseAction(giada::m::recorder::action *a, int localFrame, int globalFrame,
int quantize, bool mixerIsRunning) override;
bool canInputRec() override;
/* pushWave
* add a new wave to an existing channel. */
- void pushWave(class Wave *w);
+ void pushWave(Wave *w);
/* getPosition
* returns the position of an active sample. If EMPTY o MISSING
* updates the pitch value and chanStart+chanEnd accordingly. */
void setPitch(float v);
+ float getPitch();
/* setStart/end
* change begin/end read points in sample. */
- void setBegin(unsigned v);
- void setEnd (unsigned v);
+ void setBegin(int v);
+ void setEnd (int v);
/* save
* save sample to file. */
void setReadActions(bool v, bool recsStopOnChanHalt);
+ void setBoost(float v);
+ float getBoost();
+
/* ------------------------------------------------------------------------ */
- class Wave *wave;
+ Wave *wave;
int tracker; // chan position
int begin;
int end;
- float pitch;
- float boost;
int mode; // mode: see const.h
bool qWait; // quantizer wait
bool fadeinOn;
float fadeoutStep; // fadeout decrease
int fadeoutType; // xfade or fadeout
int fadeoutEnd; // what to do when fadeout ends
+ bool inputMonitor;
/* midi stuff */
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * 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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#include <string>
+#include "../utils/log.h"
+#include "storager.h"
+
+
+using std::string;
+
+
+namespace giada {
+namespace m {
+namespace storager
+{
+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;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+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;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+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;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+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;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+bool setInt(json_t *jRoot, const char *key, int &output)
+{
+ return setUint32(jRoot, key, (uint32_t&) output);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+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;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+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;
+}
+
+}}}; // giada::m::storager::
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * 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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#ifndef G_STORAGER_H
+#define G_STORAGER_H
+
+
+#include <jansson.h>
+
+
+namespace giada {
+namespace m {
+namespace storager
+{
+bool setString(json_t *jRoot, const char *key, std::string &output);
+bool setFloat(json_t *jRoot, const char *key, float &output);
+bool setUint32(json_t *jRoot, const char *key, uint32_t &output);
+bool setInt(json_t *jRoot, const char *key, int &output);
+bool setBool(json_t *jRoot, const char *key, bool &output);
+
+/* checkObject
+check whether the jRoot object is a valid json object {} */
+
+bool checkObject(json_t *jRoot, const char *key);
+
+/* checkArray
+check whether the jRoot object is a valid json array [] */
+
+bool checkArray(json_t *jRoot, const char *key);
+
+}}}; // giada::m::storager::
+
+
+#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
* -------------------------------------------------------------------------- */
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h> // memcpy
-#include <math.h>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring> // memcpy
+#include <cmath>
+#include <samplerate.h>
#include "../utils/fs.h"
#include "../utils/log.h"
#include "init.h"
Wave::Wave()
-: data (NULL),
+: data (nullptr),
size (0),
isLogical(0),
isEdited (0) {}
Wave::Wave(const Wave &other)
-: data (NULL),
+: data (nullptr),
size (0),
isLogical(false),
isEdited (false)
name = gu_stripExt(gu_basename(f));
fileIn = sf_open(f, SFM_READ, &inHeader);
- if (fileIn == NULL) {
+ if (fileIn == nullptr) {
gu_log("[wave] unable to read %s. %s\n", f, sf_strerror(fileIn));
pathfile = "";
name = "";
{
size = inHeader.frames * inHeader.channels;
data = (float *) malloc(size * sizeof(float));
- if (data == NULL) {
+ if (data == nullptr) {
gu_log("[wave] unable to allocate memory\n");
return 0;
}
outHeader.format = inHeader.format;
fileOut = sf_open(f, SFM_WRITE, &outHeader);
- if (fileOut == NULL) {
+ if (fileOut == nullptr) {
gu_log("[wave] unable to open %s for exporting\n", f);
return 0;
}
void Wave::clear()
{
- if (data != NULL) {
+ if (data != nullptr) {
free(data);
- data = NULL;
+ data = nullptr;
pathfile = "";
size = 0;
}
/// FIXME - this way if malloc fails size becomes wrong
size = __size;
data = (float *) malloc(size * sizeof(float));
- if (data == NULL) {
+ if (data == nullptr) {
gu_log("[wave] unable to allocate memory\n");
return 0;
}
/* a wave with updated name must become logical, since the underlying
* file does not exist yet. */
}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int Wave::rate () { return inHeader.samplerate; }
+int Wave::channels() { return inHeader.channels; }
+int Wave::frames () { return inHeader.frames; }
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void Wave::rate (int v) { inHeader.samplerate = v; }
+void Wave::channels(int v) { inHeader.channels = v; }
+void Wave::frames (int v) { inHeader.frames = v; }
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
* -------------------------------------------------------------------------- */
-#ifndef WAVE_H
-#define WAVE_H
+#ifndef G_WAVE_H
+#define G_WAVE_H
-#include <samplerate.h>
#include <sndfile.h>
#include <string>
-using std::string;
-
-
class Wave
{
private:
- SNDFILE *fileIn;
- SNDFILE *fileOut;
- SF_INFO inHeader;
- SF_INFO outHeader;
-
+ SNDFILE *fileIn;
+ SNDFILE *fileOut;
+ SF_INFO inHeader;
+ SF_INFO outHeader;
public:
~Wave();
Wave(const Wave &other);
- string pathfile; // full path + sample name
- string name; // sample name (changeable)
+ std::string pathfile; // full path + sample name
+ std::string name; // sample name (changeable)
- float *data;
- int size; // wave size (size in stereo: size / 2)
- bool isLogical; // memory only (a take)
- bool isEdited; // edited via editor
+ float *data;
+ int size; // wave size (size in stereo: size / 2)
+ bool isLogical; // memory only (a take)
+ bool isEdited; // edited via editor
- inline int rate () { return inHeader.samplerate; }
- inline int channels() { return inHeader.channels; }
- inline int frames () { return inHeader.frames; }
- inline void rate (int v) { inHeader.samplerate = v; }
- inline void channels(int v) { inHeader.channels = v; }
- inline void frames (int v) { inHeader.frames = v; }
+ int rate ();
+ int channels();
+ int frames ();
+ void rate (int v);
+ void channels(int v);
+ void frames (int v);
- string basename (bool ext=false) const;
- string extension() const;
+ std::string basename(bool ext=false) const;
+ std::string extension() const;
void updateName(const char *n);
int open (const char *f);
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
{
unsigned newSize = w->size * 2;
float *dataNew = (float *) malloc(newSize * sizeof(float));
- if (dataNew == NULL) {
+ if (dataNew == nullptr) {
gu_log("[wfx] unable to allocate memory for mono>stereo conversion\n");
return 0;
}
unsigned newSize = w->size-(b-a);
float *temp = (float *) malloc(newSize * sizeof(float));
- if (temp == NULL) {
+ if (temp == nullptr) {
gu_log("[wfx] unable to allocate memory for cutting\n");
return 0;
}
int newSize = b - a;
float *temp = (float *) malloc(newSize * sizeof(float));
- if (temp == NULL) {
+ if (temp == nullptr) {
gu_log("[wfx] unable to allocate memory for trimming\n");
return 0;
}
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
* -------------------------------------------------------------------------- */
-#ifndef WAVEFX_H
-#define WAVEFX_H
+#ifndef G_WAVE_FX_H
+#define G_WAVE_FX_H
+
+
+class Wave;
/* normalizeSoft
* normalize the wave by returning the dB value for the boost volume. It
* doesn't deal with data in memory. */
-float wfx_normalizeSoft(class Wave *w);
+float wfx_normalizeSoft(Wave *w);
-bool wfx_monoToStereo(class Wave *w);
+bool wfx_monoToStereo(Wave *w);
-void wfx_silence(class Wave *w, int a, int b);
+void wfx_silence(Wave *w, int a, int b);
-int wfx_cut(class Wave *w, int a, int b);
+int wfx_cut(Wave *w, int a, int b);
-int wfx_trim(class Wave *w, int a, int b);
+int wfx_trim(Wave *w, int a, int b);
/* fade
* fade in or fade out selection. Fade In = type 0, Fade Out = type 1 */
-void wfx_fade(class Wave *w, int a, int b, int type);
+void wfx_fade(Wave *w, int a, int b, int type);
/* smooth
* smooth edges of selection. */
-void wfx_smooth(class Wave *w, int a, int b);
+void wfx_smooth(Wave *w, int a, int b);
#endif
*
* Giada - Your Hardcore Loopmachine
*
- * glue
- * Intermediate layer GUI <-> CORE.
- *
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
* -------------------------------------------------------------------------- */
+#include <cmath>
+#include <FL/Fl.H>
#include "../gui/dialogs/gd_mainWindow.h"
-#include "../gui/dialogs/gd_editor.h"
-#include "../gui/elems/ge_waveTools.h"
-#include "../gui/elems/ge_waveform.h"
-#include "../gui/elems/ge_mixed.h"
+#include "../gui/dialogs/sampleEditor.h"
+#include "../gui/dialogs/gd_warnings.h"
+#include "../gui/elems/basics/input.h"
+#include "../gui/elems/basics/dial.h"
+#include "../gui/elems/sampleEditor/waveTools.h"
+#include "../gui/elems/sampleEditor/volumeTool.h"
+#include "../gui/elems/sampleEditor/boostTool.h"
+#include "../gui/elems/sampleEditor/panTool.h"
+#include "../gui/elems/sampleEditor/pitchTool.h"
+#include "../gui/elems/sampleEditor/rangeTool.h"
+#include "../gui/elems/sampleEditor/waveform.h"
#include "../gui/elems/mainWindow/keyboard/keyboard.h"
#include "../gui/elems/mainWindow/keyboard/channel.h"
+#include "../gui/elems/mainWindow/keyboard/sampleChannel.h"
+#include "../gui/elems/mainWindow/keyboard/channelButton.h"
#include "../utils/gui.h"
+#include "../utils/fs.h"
+#include "../core/kernelAudio.h"
#include "../core/mixerHandler.h"
#include "../core/mixer.h"
+#include "../core/clock.h"
#include "../core/pluginHost.h"
#include "../core/conf.h"
#include "../core/wave.h"
extern gdMainWindow *G_MainWin;
-extern Conf G_Conf;
-extern KernelAudio G_KernelAudio;
-extern Recorder G_Recorder;
-extern Mixer G_Mixer;
-#ifdef WITH_VST
-extern PluginHost G_PluginHost;
-#endif
using std::string;
+using namespace giada::m;
static bool __soloSession__ = false;
/* save the patch and take the last browser's dir in order to re-use it
* the next time */
- G_Conf.samplePath = gu_dirname(fname);
+ conf::samplePath = gu_dirname(fname);
- int result = ch->load(fname.c_str(), G_Conf.samplerate, G_Conf.rsmpQuality);
+ int result = ch->load(fname.c_str(), conf::samplerate, conf::rsmpQuality);
if (result == SAMPLE_LOADED_OK)
G_MainWin->keyboard->updateChannel(ch->guiChannel);
Channel *glue_addChannel(int column, int type)
{
- Channel *ch = G_Mixer.addChannel(type);
+ Channel *ch = mh::addChannel(type);
geChannel *gch = G_MainWin->keyboard->addChannel(column, ch);
ch->guiChannel = gch;
return ch;
void glue_deleteChannel(Channel *ch)
{
- G_Recorder.clearChan(ch->index);
+ if (!gdConfirmWin("Warning", "Delete channel: are you sure?"))
+ return;
+ recorder::clearChan(ch->index);
ch->hasActions = false;
#ifdef WITH_VST
- G_PluginHost.freeStack(PluginHost::CHANNEL, &G_Mixer.mutex_plugins, ch);
+ pluginHost::freeStack(pluginHost::CHANNEL, &mixer::mutex_plugins, ch);
#endif
Fl::lock();
G_MainWin->keyboard->deleteChannel(ch->guiChannel);
Fl::unlock();
- G_Mixer.deleteChannel(ch);
+ mh::deleteChannel(ch);
gu_closeAllSubwindows();
}
void glue_freeChannel(Channel *ch)
{
-#ifdef WITH_VST
+ if (ch->status == STATUS_PLAY) {
+ if (!gdConfirmWin("Warning", "This action will stop the channel: are you sure?"))
+ return;
+ }
+ else
+ if (!gdConfirmWin("Warning", "Free channel: are you sure?"))
+ return;
- G_PluginHost.freeStack(PluginHost::CHANNEL, &G_Mixer.mutex_plugins, ch);
- ch->guiChannel->fx->full = false;
-
-#endif
G_MainWin->keyboard->freeChannel(ch->guiChannel);
- G_Recorder.clearChan(ch->index);
+ recorder::clearChan(ch->index);
ch->hasActions = false;
ch->empty();
+
+ /* delete any related subwindow */
+ /** TODO - use gu_closeAllSubwindows() */
+ G_MainWin->delSubWindow(WID_FILE_BROWSER);
+ G_MainWin->delSubWindow(WID_ACTION_EDITOR);
+ G_MainWin->delSubWindow(WID_SAMPLE_EDITOR);
+ G_MainWin->delSubWindow(WID_FX_LIST);
}
/* -------------------------------------------------------------------------- */
+void glue_toggleInputMonitor(Channel *ch)
+{
+ SampleChannel *sch = static_cast<SampleChannel*>(ch);
+ sch->inputMonitor = !sch->inputMonitor;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
int glue_cloneChannel(Channel *src)
{
- Channel *ch = G_Mixer.addChannel(src->type);
+ Channel *ch = mh::addChannel(src->type);
geChannel *gch = G_MainWin->keyboard->addChannel(src->guiChannel->getColumnIndex(), ch);
ch->guiChannel = gch;
- ch->copy(src, &G_Mixer.mutex_plugins);
+ ch->copy(src, &mixer::mutex_plugins);
G_MainWin->keyboard->updateChannel(ch->guiChannel);
return true;
/* -------------------------------------------------------------------------- */
-void glue_setChanVol(Channel *ch, float v, bool gui)
+void glue_setVolume(Channel *ch, float v, bool gui, bool editor)
{
ch->volume = v;
- /* also update wave editor if it's shown */
+ /* Changing channel volume? Update wave editor (if it's shown). */
- gdEditor *editor = (gdEditor*) gu_getSubwindow(G_MainWin, WID_SAMPLE_EDITOR);
- if (editor) {
- glue_setVolEditor(editor, (SampleChannel*) ch, v, false);
- Fl::lock();
- editor->volume->value(v);
- Fl::unlock();
- }
+ if (!editor) {
+ gdSampleEditor *gdEditor = (gdSampleEditor*) gu_getSubwindow(G_MainWin, WID_SAMPLE_EDITOR);
+ if (gdEditor) {
+ Fl::lock();
+ gdEditor->volumeTool->refresh();
+ Fl::unlock();
+ }
+ }
if (!gui) {
Fl::lock();
/* -------------------------------------------------------------------------- */
-void glue_setPitch(gdEditor *win, SampleChannel *ch, float val, bool numeric)
+void glue_setPitch(SampleChannel *ch, float val)
{
- if (numeric) {
- if (val <= 0.0f)
- val = 0.1000f;
- if (val > 4.0f)
- val = 4.0000f;
- if (win)
- win->pitch->value(val);
- }
-
ch->setPitch(val);
-
- if (win) {
- char buf[16];
- sprintf(buf, "%.4f", val);
+ gdSampleEditor *gdEditor = static_cast<gdSampleEditor*>(gu_getSubwindow(G_MainWin, WID_SAMPLE_EDITOR));
+ if (gdEditor) {
Fl::lock();
- win->pitchNum->value(buf);
- win->pitchNum->redraw();
+ gdEditor->pitchTool->refresh();
Fl::unlock();
}
}
/* -------------------------------------------------------------------------- */
-void glue_setPanning(gdEditor *win, SampleChannel *ch, float val)
+void glue_setPanning(SampleChannel *ch, float val)
{
- if (val < 1.0f) {
- ch->panLeft = 1.0f;
- ch->panRight= 0.0f + val;
-
- char buf[8];
- sprintf(buf, "%d L", (int) std::abs((ch->panRight * 100.0f) - 100));
- win->panNum->value(buf);
- }
- else if (val == 1.0f) {
- ch->panLeft = 1.0f;
- ch->panRight= 1.0f;
- win->panNum->value("C");
- }
- else {
- ch->panLeft = 2.0f - val;
- ch->panRight= 1.0f;
-
- char buf[8];
- sprintf(buf, "%d R", (int) std::abs((ch->panLeft * 100.0f) - 100));
- win->panNum->value(buf);
+ ch->setPan(val);
+ gdSampleEditor *gdEditor = static_cast<gdSampleEditor*>(gu_getSubwindow(G_MainWin, WID_SAMPLE_EDITOR));
+ if (gdEditor) {
+ Fl::lock();
+ gdEditor->panTool->refresh();
+ Fl::unlock();
}
- win->panNum->redraw();
}
void glue_setMute(Channel *ch, bool gui)
{
- if (G_Recorder.active && G_Recorder.canRec(ch, &G_Mixer)) {
+ if (recorder::active && recorder::canRec(ch, clock::isRunning(), mixer::recording)) {
if (!ch->mute) {
- G_Recorder.startOverdub(ch->index, ACTION_MUTES, G_Mixer.currentFrame,
- G_KernelAudio.realBufsize);
+ recorder::startOverdub(ch->index, G_ACTION_MUTES, clock::getCurrentFrame(),
+ kernelAudio::getRealBufSize());
ch->readActions = false; // don't read actions while overdubbing
}
else
- G_Recorder.stopOverdub(&G_Mixer);
+ recorder::stopOverdub(clock::getCurrentFrame(), clock::getTotalFrames(),
+ &mixer::mutex_recs);
}
ch->mute ? ch->unsetMute(false) : ch->setMute(false);
* and start the session */
if (!__soloSession__) {
- for (unsigned i=0; i<G_Mixer.channels.size(); i++) {
- Channel *och = G_Mixer.channels.at(i);
+ for (unsigned i=0; i<mixer::channels.size(); i++) {
+ Channel *och = mixer::channels.at(i);
och->mute_s = och->mute;
}
__soloSession__ = true;
/* mute all other channels and unmute this (if muted) */
- for (unsigned i=0; i<G_Mixer.channels.size(); i++) {
- Channel *och = G_Mixer.channels.at(i);
+ for (unsigned i=0; i<mixer::channels.size(); i++) {
+ Channel *och = mixer::channels.at(i);
if (!och->solo && !och->mute) {
och->setMute(false);
Fl::lock();
/* if this is uniqueSolo, stop solo session and restore mute status,
* else mute this */
- if (mh_uniqueSolo(ch)) {
+ if (mh::uniqueSolo(ch)) {
__soloSession__ = false;
- for (unsigned i=0; i<G_Mixer.channels.size(); i++) {
- Channel *och = G_Mixer.channels.at(i);
+ for (unsigned i=0; i<mixer::channels.size(); i++) {
+ Channel *och = mixer::channels.at(i);
if (och->mute_s) {
och->setMute(false);
Fl::lock();
/* -------------------------------------------------------------------------- */
-void glue_setBeginEndChannel(gdEditor *win, SampleChannel *ch, int b, int e,
- bool recalc, bool check)
+void glue_setBeginEndChannel(SampleChannel *ch, int b, int e)
{
- if (check) {
- if (e > ch->wave->size)
- e = ch->wave->size;
- if (b < 0)
- b = 0;
- if (b > ch->wave->size)
- b = ch->wave->size-2;
- if (b >= ch->end)
- b = ch->begin;
- if (e <= ch->begin)
- e = ch->end;
- }
-
- /* continue only if new values != old values */
-
- if (b == ch->begin && e == ch->end)
- return;
-
- /* print mono values */
-
- char tmp[16];
- sprintf(tmp, "%d", b/2);
- win->chanStart->value(tmp);
-
- tmp[0] = '\0';
- sprintf(tmp, "%d", e/2);
- win->chanEnd->value(tmp);
-
ch->setBegin(b);
ch->setEnd(e);
-
- /* Recalc is not needed when the user drags the bars directly over the
- waveform */
-
- if (recalc) {
- win->waveTools->waveform->recalcPoints();
- win->waveTools->waveform->redraw();
- }
+ gdSampleEditor *gdEditor = static_cast<gdSampleEditor*>(gu_getSubwindow(G_MainWin, WID_SAMPLE_EDITOR));
+ if (gdEditor) {
+ Fl::lock();
+ gdEditor->rangeTool->refresh();
+ Fl::unlock();
+ }
}
/* -------------------------------------------------------------------------- */
-void glue_setBoost(gdEditor *win, SampleChannel *ch, float val, bool numeric)
+void glue_setBoost(SampleChannel *ch, float val)
{
- if (numeric) {
- if (val > 20.0f)
- val = 20.0f;
- else if (val < 0.0f)
- val = 0.0f;
+ ch->setBoost(val);
+ gdSampleEditor *gdEditor = static_cast<gdSampleEditor*>(gu_getSubwindow(G_MainWin, WID_SAMPLE_EDITOR));
+ if (gdEditor) {
+ Fl::lock();
+ gdEditor->boostTool->refresh();
+ Fl::unlock();
+ }
+}
- float linear = pow(10, (val / 20)); // linear = 10^(dB/20)
- ch->boost = linear;
+/* -------------------------------------------------------------------------- */
- char buf[10];
- sprintf(buf, "%.2f", val);
- win->boostNum->value(buf);
- win->boostNum->redraw();
- win->boost->value(linear);
- win->boost->redraw(); /// inutile
- }
- else {
- ch->boost = val;
- char buf[10];
- sprintf(buf, "%.2f", 20*log10(val));
- win->boostNum->value(buf);
- win->boostNum->redraw();
- }
+void glue_startStopReadingRecs(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
+ the channel is in wait mode (REC_WAITING). ch->readActions will become true on
+ the next first beat. So a 'stop rec' command should occur also when
+ ch->readActions is false but the channel is in wait mode; this check will
+ handle the case of when you press 'R', the channel goes into REC_WAITING and
+ then you press 'R' again to undo the status. */
+
+ if (ch->readActions || (!ch->readActions && ch->recStatus == REC_WAITING))
+ glue_stopReadingRecs(ch, gui);
+ else
+ glue_startReadingRecs(ch, gui);
}
/* -------------------------------------------------------------------------- */
-void glue_setVolEditor(gdEditor *win, SampleChannel *ch, float val, bool numeric)
+void glue_startReadingRecs(SampleChannel *ch, bool gui)
{
- if (numeric) {
- if (val > 0.0f)
- val = 0.0f;
- else if (val < -60.0f)
- val = -INFINITY;
+ if (conf::treatRecsAsLoops)
+ ch->recStatus = REC_WAITING;
+ else
+ ch->setReadActions(true, conf::recsStopOnChanHalt);
+ if (!gui) {
+ Fl::lock();
+ ((geSampleChannel*)ch->guiChannel)->readActions->value(1);
+ Fl::unlock();
+ }
+}
- float linear = pow(10, (val / 20)); // linear = 10^(dB/20)
- ch->volume = linear;
+/* -------------------------------------------------------------------------- */
- win->volume->value(linear);
- win->volume->redraw();
- char buf[10];
- if (val > -INFINITY)
- sprintf(buf, "%.2f", val);
- else
- sprintf(buf, "-inf");
- win->volumeNum->value(buf);
- win->volumeNum->redraw();
+void glue_stopReadingRecs(SampleChannel *ch, bool gui)
+{
+ /* First of all, if the mixer is not running just stop and disable everything.
+ Then if "treatRecsAsLoop" wait until the sequencer reaches beat 0, so put the
+ channel in REC_ENDING status. */
- ch->guiChannel->vol->value(linear);
- ch->guiChannel->vol->redraw();
+ if (!clock::isRunning()) {
+ ch->recStatus = REC_STOPPED;
+ ch->readActions = false;
}
- else {
- ch->volume = val;
-
- float dbVal = 20 * log10(val);
- char buf[10];
- if (dbVal > -INFINITY)
- sprintf(buf, "%.2f", dbVal);
- else
- sprintf(buf, "-inf");
-
- win->volumeNum->value(buf);
- win->volumeNum->redraw();
+ else
+ if (conf::treatRecsAsLoops)
+ ch->recStatus = REC_ENDING;
+ else
+ ch->setReadActions(false, conf::recsStopOnChanHalt);
- ch->guiChannel->vol->value(val);
- ch->guiChannel->vol->redraw();
+ if (!gui) {
+ Fl::lock();
+ ((geSampleChannel*)ch->guiChannel)->readActions->value(0);
+ Fl::unlock();
}
}
*
* Giada - Your Hardcore Loopmachine
*
- * glue
- * Intermediate layer GUI <-> CORE.
- *
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
* -------------------------------------------------------------------------- */
-#ifndef GLUE_CHANNEL_H
-#define GLUE_CHANNEL_H
+#ifndef G_GLUE_CHANNEL_H
+#define G_GLUE_CHANNEL_H
#include <string>
+class Channel;
+class SampleChannel;
+class gdSampleEditor;
+
+
/* addChannel
* add an empty new channel to the stack. Returns the new channel. */
-class Channel *glue_addChannel(int column, int type);
+Channel *glue_addChannel(int column, int type);
/* loadChannel
* fill an existing channel with a wave. */
-int glue_loadChannel(class SampleChannel *ch, const std::string &fname);
+int glue_loadChannel(SampleChannel *ch, const std::string &fname);
/* deleteChannel
* Remove a channel from Mixer. */
-void glue_deleteChannel(class Channel *ch);
+void glue_deleteChannel(Channel *ch);
/* freeChannel
* Unload the sample from a sample channel. */
-void glue_freeChannel(class Channel *ch);
+void glue_freeChannel(Channel *ch);
/* cloneChannel
* Make an exact copy of Channel *ch. */
-int glue_cloneChannel(class 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(class Channel *ch, bool gui=true);
-void glue_setChanVol(class Channel *ch, float v, bool gui=true);
-void glue_setMute(class Channel *ch, bool gui=true);
-void glue_setSoloOn (class Channel *ch, bool gui=true);
-void glue_setSoloOff(class Channel *ch, bool gui=true);
+void glue_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);
-void glue_setPitch(class gdEditor *win, class SampleChannel *ch, float val,
- bool numeric);
+/* TODO move to glue_sampleEditor */
+void glue_setPitch(SampleChannel *ch, float val);
-void glue_setPanning(class gdEditor *win, class SampleChannel *ch, float val);
+/* TODO move to glue_sampleEditor */
+void glue_setPanning(SampleChannel *ch, float val);
/* setBeginEndChannel
* sets start/end points in the sample editor. Recalc=false: don't recalc
* internal position. check=true: check the points' consistency */
-void glue_setBeginEndChannel(class gdEditor *win, class SampleChannel *ch,
- int b, int e, bool recalc=false, bool check=true);
-
-void glue_setBoost(class gdEditor *win, class SampleChannel *ch, float val,
- bool numeric);
+/* TODO move to glue_sampleEditor */
+void glue_setBeginEndChannel(SampleChannel *ch, int b, int e);
-/* setVolEditor
- * handles the volume inside the SAMPLE EDITOR (not the main gui). The
- * numeric flag tells if we want to handle the dial or the numeric input
- * field. */
+/* TODO move to glue_sampleEditor */
+void glue_setBoost(SampleChannel *ch, float val);
-void glue_setVolEditor(class gdEditor *win, class SampleChannel *ch, float val,
- bool numeric);
+/* start/stopReadingRecs
+Handles the 'R' button. If gui == true the signal comes from an user interaction
+on the GUI, otherwise it's a MIDI/Jack/external signal. */
+void glue_startStopReadingRecs(SampleChannel *ch, bool gui=true);
+void glue_startReadingRecs (SampleChannel *ch, bool gui=true);
+void glue_stopReadingRecs (SampleChannel *ch, bool gui=true);
#endif
*
* Giada - Your Hardcore Loopmachine
*
- * glue
- * Intermediate layer GUI <-> CORE.
- *
- * How to know if you need another glue_ function? Ask yourself if the
- * new action will ever be called via MIDI or keyboard/mouse. If yes,
- * put it here.
- *
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
#include "../utils/gui.h"
#include "../utils/log.h"
#include "../core/recorder.h"
+#include "../core/kernelAudio.h"
#include "../core/mixer.h"
#include "../core/mixerHandler.h"
#include "../core/wave.h"
#include "../core/channel.h"
+#include "../core/clock.h"
#include "../core/sampleChannel.h"
#include "../core/midiChannel.h"
#include "main.h"
#include "channel.h"
+#include "transport.h"
#include "io.h"
-extern Recorder G_Recorder;
-extern KernelAudio G_KernelAudio;
-extern bool G_audio_status;
-extern Mixer G_Mixer;
extern gdMainWindow *G_MainWin;
+using namespace giada::m;
+
+
void glue_keyPress(Channel *ch, bool ctrl, bool shift)
{
if (ch->type == CHANNEL_SAMPLE)
if (shift)
ch->kill(0); // on frame 0: user-generated event
else
- ch->start(0, true, G_Mixer.quantize, G_Mixer.running, false, true); // on frame 0: user-generated event
+ ch->start(0, true, clock::getQuantize(), clock::isRunning(), false, true); // on frame 0: user-generated event
}
else
if (shift) {
- if (G_Recorder.active) {
- if (G_Mixer.running) {
+ if (recorder::active) {
+ if (clock::isRunning()) {
ch->kill(0); // on frame 0: user-generated event
- if (G_Recorder.canRec(ch, &G_Mixer) && !(ch->mode & LOOP_ANY)) { // don't record killChan actions for LOOP channels
- G_Recorder.rec(ch->index, ACTION_KILLCHAN, G_Mixer.currentFrame);
+ if (recorder::canRec(ch, clock::isRunning(), mixer::recording) &&
+ !(ch->mode & LOOP_ANY))
+ { // don't record killChan actions for LOOP channels
+ recorder::rec(ch->index, G_ACTION_KILL, clock::getCurrentFrame());
ch->hasActions = true;
}
}
}
else {
if (ch->hasActions) {
- if (G_Mixer.running || ch->status == STATUS_OFF)
+ if (clock::isRunning() || ch->status == STATUS_OFF)
ch->readActions ? glue_stopReadingRecs(ch) : glue_startReadingRecs(ch);
else
ch->kill(0); // on frame 0: user-generated event
* when a quantoWait has passed. Moreover, KEYPRESS and KEYREL are
* meaningless for loop modes */
- if (G_Mixer.quantize == 0 &&
- G_Recorder.canRec(ch, &G_Mixer) &&
+ if (clock::getQuantize() == 0 &&
+ recorder::canRec(ch, clock::isRunning(), mixer::recording) &&
!(ch->mode & LOOP_ANY))
{
if (ch->mode == SINGLE_PRESS) {
- G_Recorder.startOverdub(ch->index, ACTION_KEYS, G_Mixer.currentFrame,
- G_KernelAudio.realBufsize);
+ recorder::startOverdub(ch->index, G_ACTION_KEYS, clock::getCurrentFrame(),
+ kernelAudio::getRealBufSize());
ch->readActions = false; // don't read actions while overdubbing
}
else {
- G_Recorder.rec(ch->index, ACTION_KEYPRESS, G_Mixer.currentFrame);
+ recorder::rec(ch->index, G_ACTION_KEYPRESS, clock::getCurrentFrame());
ch->hasActions = true;
/* Why return here? You record an action (as done on line 148) and then
/* This is a user-generated event, so it's on frame 0 */
- ch->start(0, true, G_Mixer.quantize, G_Mixer.running, false, true);
+ ch->start(0, true, clock::getQuantize(), clock::isRunning(), false, true);
}
/* the GUI update is done by gui_refresh() */
/* record a key release only if channel is single_press. For any
* other mode the KEY REL is meaningless. */
- if (ch->mode == SINGLE_PRESS && G_Recorder.canRec(ch, &G_Mixer))
- G_Recorder.stopOverdub(&G_Mixer);
+ if (ch->mode == SINGLE_PRESS && recorder::canRec(ch, clock::isRunning(), mixer::recording))
+ recorder::stopOverdub(clock::getCurrentFrame(), clock::getTotalFrames(),
+ &mixer::mutex_recs);
/* the GUI update is done by gui_refresh() */
void glue_startStopActionRec(bool gui)
{
- G_Recorder.active ? glue_stopActionRec(gui) : glue_startActionRec(gui);
+ recorder::active ? glue_stopActionRec(gui) : glue_startActionRec(gui);
}
void glue_startActionRec(bool gui)
{
- if (G_audio_status == false)
+ if (kernelAudio::getStatus() == false)
return;
- G_Recorder.active = true;
+ recorder::active = true;
- if (!G_Mixer.running)
+ if (!clock::isRunning())
glue_startSeq(false); // update gui ayway
if (!gui) {
{
/* stop the recorder and sort new actions */
- G_Recorder.active = false;
- G_Recorder.sortActions();
+ recorder::active = false;
+ recorder::sortActions();
- for (unsigned i=0; i<G_Mixer.channels.size(); i++)
+ for (unsigned i=0; i<mixer::channels.size(); i++)
{
- if (G_Mixer.channels.at(i)->type == CHANNEL_MIDI)
+ if (mixer::channels.at(i)->type == CHANNEL_MIDI)
continue;
- SampleChannel *ch = (SampleChannel*) G_Mixer.channels.at(i);
+ SampleChannel *ch = (SampleChannel*) mixer::channels.at(i);
G_MainWin->keyboard->setChannelWithActions((geSampleChannel*)ch->guiChannel);
if (!ch->readActions && ch->hasActions)
glue_startReadingRecs(ch, false);
void glue_startStopInputRec(bool gui)
{
- if (G_Mixer.recording)
+ if (mixer::recording)
glue_stopInputRec(gui);
else
if (!glue_startInputRec(gui))
int glue_startInputRec(bool gui)
{
- if (G_audio_status == false)
+ if (kernelAudio::getStatus() == false)
return false;
- if (!mh_startInputRec()) {
+ if (!mh::startInputRec()) {
Fl::lock();
G_MainWin->mainTransport->updateRecInput(0); // set it off, anyway
Fl::unlock();
return false;
}
- if (!G_Mixer.running)
+ if (!clock::isRunning())
glue_startSeq(false); // update gui anyway
Fl::lock();
/* Update sample name inside sample channels' main button. This is useless for
midi channel, but let's do it anyway. */
- for (unsigned i=0; i<G_Mixer.channels.size(); i++)
- G_Mixer.channels.at(i)->guiChannel->update();
+ for (unsigned i=0; i<mixer::channels.size(); i++)
+ mixer::channels.at(i)->guiChannel->update();
return true;
}
int glue_stopInputRec(bool gui)
{
- mh_stopInputRec();
+ mh::stopInputRec();
/* Start all sample channels in loop mode that were armed, i.e. that were
recording stuff and not yet in play. They are also started in force mode, i.e.
they must start playing right away at the current frame, not at the next first
beat. */
- for (unsigned i=0; i<G_Mixer.channels.size(); i++) {
- if (G_Mixer.channels.at(i)->type == CHANNEL_MIDI)
+ for (unsigned i=0; i<mixer::channels.size(); i++) {
+ if (mixer::channels.at(i)->type == CHANNEL_MIDI)
continue;
- SampleChannel *ch = (SampleChannel*) G_Mixer.channels.at(i);
+ SampleChannel *ch = (SampleChannel*) mixer::channels.at(i);
if (ch->mode & (LOOP_ANY) && ch->status == STATUS_OFF && ch->armed)
- ch->start(G_Mixer.currentFrame, true, G_Mixer.quantize, G_Mixer.running, true, true);
+ ch->start(clock::getCurrentFrame(), true, clock::getQuantize(),
+ clock::isRunning(), true, true);
}
Fl::lock();
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
* -------------------------------------------------------------------------- */
-#ifndef GLUE_IO_H
-#define GLUE_IO_H
+#ifndef G_GLUE_IO_H
+#define G_GLUE_IO_H
/* keyPress / keyRelease
*
* Giada - Your Hardcore Loopmachine
*
- * glue
- * Intermediate layer GUI <-> CORE.
- *
- * How to know if you need another glue_ function? Ask yourself if the
- * new action will ever be called via MIDI or keyboard/mouse. If yes,
- * put it here.
- *
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
#include <cmath>
-#include "../gui/elems/ge_waveform.h"
-#include "../gui/elems/ge_mixed.h"
-#include "../gui/elems/ge_waveTools.h"
-#include "../gui/elems/mainWindow/mainTransport.h"
+#include <FL/Fl.H>
#include "../gui/elems/mainWindow/mainIO.h"
#include "../gui/elems/mainWindow/mainTimer.h"
-#include "../gui/elems/mainWindow/keyboard/channel.h"
#include "../gui/elems/mainWindow/keyboard/sampleChannel.h"
#include "../gui/elems/mainWindow/keyboard/keyboard.h"
#include "../gui/dialogs/gd_mainWindow.h"
-#include "../gui/dialogs/gd_editor.h"
-#include "../gui/dialogs/gd_warnings.h"
#include "../utils/gui.h"
-#include "../utils/fs.h"
+#include "../utils/string.h"
#include "../utils/log.h"
#include "../core/mixerHandler.h"
#include "../core/mixer.h"
-#include "../core/recorder.h"
-#include "../core/wave.h"
-#include "../core/pluginHost.h"
-#include "../core/channel.h"
-#include "../core/sampleChannel.h"
#include "../core/midiChannel.h"
+#include "../core/clock.h"
#include "../core/kernelMidi.h"
-#include "../core/patch_DEPR_.h"
+#include "../core/kernelAudio.h"
#include "../core/conf.h"
+#ifdef WITH_VST
+#include "../core/pluginHost.h"
+#endif
#include "main.h"
extern gdMainWindow *G_MainWin;
-extern Mixer G_Mixer;
-extern Recorder G_Recorder;
-extern KernelAudio G_KernelAudio;
-extern KernelMidi G_KernelMidi;
-extern Patch_DEPR_ G_Patch_DEPR_;
-extern Conf G_Conf;
-extern bool G_audio_status;
-#ifdef WITH_VST
-extern PluginHost G_PluginHost;
-#endif
+
+
+using namespace giada::m;
void glue_setBpm(const char *v1, const char *v2)
{
/* Never change this stuff while recording audio */
- if (G_Mixer.recording)
+ if (mixer::recording)
return;
- char buf[6];
- float value = atof(v1) + (atof(v2)/10);
- if (value < 20.0f) {
- value = 20.0f;
- sprintf(buf, "20.0");
+ char bpmS[6];
+ float bpmF = atof(v1) + (atof(v2)/10);
+ if (bpmF < 20.0f) {
+ bpmF = 20.0f;
+ sprintf(bpmS, "20.0");
}
else
- sprintf(buf, "%s.%s", v1, !strcmp(v2, "") ? "0" : v2);
+ sprintf(bpmS, "%s.%s", v1, !strcmp(v2, "") ? "0" : v2);
/* a value such as atof("120.1") will never be 120.1 but 120.0999999,
* because of the rounding error. So we pass the real "wrong" value to
* G_Mixer and we show the nice looking (but fake) one to the GUI. */
- float old_bpm = G_Mixer.bpm;
- G_Mixer.bpm = value;
- G_Mixer.updateFrameBars();
+ float oldBpmF = clock::getBpm();
+ clock::setBpm(bpmF);
+ recorder::updateBpm(oldBpmF, bpmF, clock::getQuanto());
- /* inform recorder and actionEditor of the change */
+#ifdef __linux__
+ kernelAudio::jackSetBpm(clock::getBpm());
+#endif
- G_Recorder.updateBpm(old_bpm, value, G_Mixer.quanto);
- gu_refreshActionEditor();
+ gu_refreshActionEditor();
+ G_MainWin->mainTimer->setBpm(bpmS);
- G_MainWin->mainTimer->setBpm(buf);
- gu_log("[glue] Bpm changed to %s (real=%f)\n", buf, G_Mixer.bpm);
+ gu_log("[glue] Bpm changed to %s (real=%f)\n", bpmS, clock::getBpm());
}
/* -------------------------------------------------------------------------- */
-void glue_setBeats(int beats, int bars, bool expand)
+void glue_setBpm(float v)
{
- /* Never change this stuff while recording audio */
-
- if (G_Mixer.recording)
- return;
-
- /* Temp vars to store old data (they are necessary) */
-
- int oldvalue = G_Mixer.beats;
- unsigned oldfpb = G_Mixer.totalFrames;
-
- if (beats > MAX_BEATS)
- G_Mixer.beats = MAX_BEATS;
- else if (beats < 1)
- G_Mixer.beats = 1;
- else
- G_Mixer.beats = beats;
-
- /* update bars - bars cannot be greate than beats and must be a sub
- * multiple of beats. If not, approximation to the nearest (and greater)
- * value available. */
-
- if (bars > G_Mixer.beats)
- G_Mixer.bars = G_Mixer.beats;
- else if (bars <= 0)
- G_Mixer.bars = 1;
- else if (beats % bars != 0) {
- G_Mixer.bars = bars + (beats % bars);
- if (beats % G_Mixer.bars != 0) // it could be an odd value, let's check it (and avoid it)
- G_Mixer.bars = G_Mixer.bars - (beats % G_Mixer.bars);
- }
- else
- G_Mixer.bars = bars;
-
- G_Mixer.updateFrameBars();
-
- /* update recorded actions */
-
- if (expand) {
- if (G_Mixer.beats > oldvalue)
- G_Recorder.expand(oldfpb, G_Mixer.totalFrames);
- //else if (G_Mixer.beats < oldvalue)
- // G_Recorder.shrink(G_Mixer.totalFrames);
- }
-
- G_MainWin->mainTimer->setMeter(G_Mixer.beats, G_Mixer.bars);
- gu_refreshActionEditor(); // in case the action editor is open
+ if (v < G_MIN_BPM || v > G_MAX_BPM)
+ v = G_DEFAULT_BPM;
+ double fIpart;
+ double fPpart = modf(v, &fIpart);
+ int iIpart = fIpart;
+ int iPpart = ceilf(fPpart);
+ glue_setBpm(gu_itoa(iIpart).c_str(), gu_itoa(iPpart).c_str());
}
/* -------------------------------------------------------------------------- */
-void glue_startStopSeq(bool gui)
+void glue_setBeats(int beats, int bars, bool expand)
{
- G_Mixer.running ? glue_stopSeq(gui) : glue_startSeq(gui);
-}
+ /* Never change this stuff while recording audio */
+ if (mixer::recording)
+ return;
-/* -------------------------------------------------------------------------- */
+ /* Temp vars to store old data (they are necessary) */
+ int oldBeats = clock::getBeats();
+ unsigned oldTotalFrames = clock::getTotalFrames();
-void glue_startSeq(bool gui)
-{
- G_Mixer.running = true;
+ clock::setBeats(beats);
+ clock::setBars(bars);
+ clock::updateFrameBars();
- if (gui) {
-#ifdef __linux__
- G_KernelAudio.jackStart();
-#endif
- }
+ /* Update recorded actions, if 'expand' required and an expansion is taking
+ place. */
- if (G_Conf.midiSync == MIDI_SYNC_CLOCK_M) {
- G_KernelMidi.send(MIDI_START, -1, -1);
- G_KernelMidi.send(MIDI_POSITION_PTR, 0, 0);
- }
+ if (expand && clock::getBeats() > oldBeats)
+ recorder::expand(oldTotalFrames, clock::getTotalFrames());
- if (!gui) {
- Fl::lock();
- G_MainWin->mainTransport->updatePlay(1);
- Fl::unlock();
- }
+ G_MainWin->mainTimer->setMeter(clock::getBeats(), clock::getBars());
+ gu_refreshActionEditor(); // in case the action editor is open
}
/* -------------------------------------------------------------------------- */
-void glue_stopSeq(bool gui)
+void glue_rewindSeq(bool gui, bool notifyJack)
{
- mh_stopSequencer();
+ mh::rewindSequencer();
- if (G_Conf.midiSync == MIDI_SYNC_CLOCK_M)
- G_KernelMidi.send(MIDI_STOP, -1, -1);
+ /* FIXME - potential desync when Quantizer is enabled from this point on.
+ Mixer would wait, while the following calls would be made regardless of its
+ state. */
#ifdef __linux__
- if (gui)
- G_KernelAudio.jackStop();
+ if (notifyJack)
+ kernelAudio::jackSetPosition(0);
#endif
- /* what to do if we stop the sequencer and some action recs are active?
- * Deactivate the button and delete any 'rec on' status */
-
- if (G_Recorder.active) {
- G_Recorder.active = false;
- Fl::lock();
- G_MainWin->mainTransport->updateRecAction(0);
- Fl::unlock();
- }
-
- /* if input recs are active (who knows why) we must deactivate them.
- * One might stop the sequencer while an input rec is running. */
-
- if (G_Mixer.recording) {
- mh_stopInputRec();
- Fl::lock();
- G_MainWin->mainTransport->updateRecInput(0);
- Fl::unlock();
- }
-
- if (!gui) {
- Fl::lock();
- G_MainWin->mainTransport->updatePlay(0);
- Fl::unlock();
- }
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void glue_rewindSeq()
-{
- mh_rewindSequencer();
- if (G_Conf.midiSync == MIDI_SYNC_CLOCK_M)
- G_KernelMidi.send(MIDI_POSITION_PTR, 0, 0);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void glue_startStopReadingRecs(SampleChannel *ch, bool gui)
-{
- /* When you call glue_startReadingRecs with G_Conf.treatRecsAsLoops, the
- member value ch->readActions actually is not set to true immediately, because
- the channel is in wait mode (REC_WAITING). ch->readActions will become true on
- the next first beat. So a 'stop rec' command should occur also when
- ch->readActions is false but the channel is in wait mode; this check will
- handle the case of when you press 'R', the channel goes into REC_WAITING and
- then you press 'R' again to undo the status. */
-
- if (ch->readActions || (!ch->readActions && ch->recStatus == REC_WAITING))
- glue_stopReadingRecs(ch, gui);
- else
- glue_startReadingRecs(ch, gui);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void glue_startReadingRecs(SampleChannel *ch, bool gui)
-{
- if (G_Conf.treatRecsAsLoops)
- ch->recStatus = REC_WAITING;
- else
- ch->setReadActions(true, G_Conf.recsStopOnChanHalt);
- if (!gui) {
- Fl::lock();
- ((geSampleChannel*)ch->guiChannel)->readActions->value(1);
- Fl::unlock();
- }
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void glue_stopReadingRecs(SampleChannel *ch, bool gui)
-{
- /* First of all, if the mixer is not running just stop and disable everything.
- Then if "treatRecsAsLoop" wait until the sequencer reaches beat 0, so put the
- channel in REC_ENDING status. */
-
- if (!G_Mixer.running) {
- ch->recStatus = REC_STOPPED;
- ch->readActions = false;
- }
- else
- if (G_Conf.treatRecsAsLoops)
- ch->recStatus = REC_ENDING;
- else
- ch->setReadActions(false, G_Conf.recsStopOnChanHalt);
-
- if (!gui) {
- Fl::lock();
- ((geSampleChannel*)ch->guiChannel)->readActions->value(0);
- Fl::unlock();
- }
+ if (conf::midiSync == MIDI_SYNC_CLOCK_M)
+ kernelMidi::send(MIDI_POSITION_PTR, 0, 0);
}
void glue_quantize(int val)
{
- G_Mixer.quantize = val;
- G_Mixer.updateQuanto();
+ clock::setQuantize(val);
}
void glue_setOutVol(float v, bool gui)
{
- G_Mixer.outVol = v;
+ mixer::outVol = v;
if (!gui) {
Fl::lock();
G_MainWin->mainIO->setOutVol(v);
void glue_setInVol(float v, bool gui)
{
- G_Mixer.inVol = v;
+ mixer::inVol = v;
if (!gui) {
Fl::lock();
G_MainWin->mainIO->setInVol(v);
void glue_clearAllSamples()
{
- G_Mixer.running = false;
- for (unsigned i=0; i<G_Mixer.channels.size(); i++) {
- G_Mixer.channels.at(i)->empty();
- G_Mixer.channels.at(i)->guiChannel->reset();
+ clock::stop();
+ for (unsigned i=0; i<mixer::channels.size(); i++) {
+ mixer::channels.at(i)->empty();
+ mixer::channels.at(i)->guiChannel->reset();
}
- G_Recorder.init();
+ recorder::init();
return;
}
void glue_clearAllRecs()
{
- G_Recorder.init();
+ recorder::init();
gu_updateControls();
}
void glue_resetToInitState(bool resetGui, bool createColumns)
{
- G_Patch_DEPR_.setDefault();
- G_Mixer.close();
- G_Mixer.init();
- G_Recorder.init();
+ mixer::close();
+ mixer::init(clock::getTotalFrames(), kernelAudio::getRealBufSize());
+ recorder::init();
#ifdef WITH_VST
- G_PluginHost.freeAllStacks(&G_Mixer.channels, &G_Mixer.mutex_plugins);
+ pluginHost::freeAllStacks(&mixer::channels, &mixer::mutex_plugins);
#endif
G_MainWin->keyboard->clear();
/* -------------------------------------------------------------------------- */
-void glue_startStopMetronome(bool gui)
-{
- G_Mixer.metronome = !G_Mixer.metronome;
- if (!gui) {
- Fl::lock();
- G_MainWin->mainTransport->updateMetronome(G_Mixer.metronome);
- Fl::unlock();
- }
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
/* never expand or shrink recordings (last param of setBeats = false):
* this is live manipulation */
void glue_beatsMultiply()
{
- glue_setBeats(G_Mixer.beats*2, G_Mixer.bars, false);
+ glue_setBeats(clock::getBeats() * 2, clock::getBars(), false);
}
void glue_beatsDivide()
{
- glue_setBeats(G_Mixer.beats/2, G_Mixer.bars, false);
+ glue_setBeats(clock::getBeats() / 2, clock::getBars(), false);
}
-/* ---------------------------------------------------------------------
+/* -----------------------------------------------------------------------------
*
* Giada - Your Hardcore Loopmachine
*
* new action will ever be called via MIDI or keyboard/mouse. If yes,
* put it here.
*
- * ---------------------------------------------------------------------
+ * -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
* along with Giada - Your Hardcore Loopmachine. If not, see
* <http://www.gnu.org/licenses/>.
*
- * ------------------------------------------------------------------ */
+ * -------------------------------------------------------------------------- */
-#ifndef GLUE_H
-#define GLUE_H
+#ifndef G_GLUE_MAIN_H
+#define G_GLUE_MAIN_H
void glue_setBpm(const char *v1, const char *v2);
+void glue_setBpm(float v);
void glue_setBeats(int beats, int bars, bool expand);
-
-/* start, stop, rewind sequencer
-If gui == true the signal comes from an user interaction on the GUI,
-otherwise it's a MIDI/Jack/external signal. */
-
-void glue_startStopSeq(bool gui=true);
-void glue_startSeq (bool gui=true);
-void glue_stopSeq (bool gui=true);
-void glue_rewindSeq ();
-
-/* start/stopReadingRecs
-Handles the 'R' button. If gui == true the signal comes from an user interaction
-on the GUI, otherwise it's a MIDI/Jack/external signal. */
-
-void glue_startStopReadingRecs(class SampleChannel *ch, bool gui=true);
-void glue_startReadingRecs (class SampleChannel *ch, bool gui=true);
-void glue_stopReadingRecs (class SampleChannel *ch, bool gui=true);
-
void glue_quantize(int val);
-
-void glue_setOutVol (float v, bool gui=true);
-void glue_setInVol (float v, bool gui=true);
-
+void glue_setOutVol(float v, bool gui=true);
+void glue_setInVol(float v, bool gui=true);
void glue_clearAllSamples();
void glue_clearAllRecs();
void glue_resetToInitState(bool resetGui=true, bool createColumns=true);
-void glue_startStopMetronome(bool gui=true);
-
/* beatsDivide/Multiply
* shrinks or enlarges the number of beats by 2. */
*
* Giada - Your Hardcore Loopmachine
*
- * glue
- * Intermediate layer GUI <-> CORE.
- *
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
#include "plugin.h"
-extern Mixer G_Mixer;
-extern PluginHost G_PluginHost;
+using namespace giada::m;
Plugin *glue_addPlugin(Channel *ch, int index, int stackType)
{
- if (index >= G_PluginHost.countAvailablePlugins())
+ if (index >= pluginHost::countAvailablePlugins())
return nullptr;
- return G_PluginHost.addPlugin(index, stackType, &G_Mixer.mutex_plugins, ch);
+ return pluginHost::addPlugin(index, stackType, &mixer::mutex_plugins, ch);
}
void glue_swapPlugins(Channel *ch, int index1, int index2, int stackType)
{
- G_PluginHost.swapPlugin(index1, index2, stackType, &G_Mixer.mutex_plugins,
+ pluginHost::swapPlugin(index1, index2, stackType, &mixer::mutex_plugins,
ch);
}
void glue_freePlugin(Channel *ch, int index, int stackType)
{
- G_PluginHost.freePlugin(index, stackType, &G_Mixer.mutex_plugins, ch);
+ pluginHost::freePlugin(index, stackType, &mixer::mutex_plugins, ch);
}
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
* -------------------------------------------------------------------------- */
-#ifndef __GLUE_PLUGIN_H__
-#define __GLUE_PLUGIN_H__
+#ifndef G_GLUE_PLUGIN_H
+#define G_GLUE_PLUGIN_H
#ifdef WITH_VST
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * 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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#include "../gui/dialogs/gd_warnings.h"
+#include "../gui/elems/mainWindow/keyboard/channel.h"
+#include "../gui/elems/mainWindow/keyboard/sampleChannel.h"
+#include "../core/const.h"
+#include "../core/channel.h"
+#include "../core/recorder.h"
+#include "../utils/gui.h"
+#include "recorder.h"
+
+
+using namespace giada::m;
+
+
+namespace
+{
+void updateChannel(geChannel *gch)
+{
+ gch->ch->hasActions = recorder::hasActions(gch->ch->index);
+ if (gch->ch->type == CHANNEL_SAMPLE && !gch->ch->hasActions)
+ static_cast<geSampleChannel*>(gch)->hideActionButton();
+ /* TODO - set mute=false */
+ gu_refreshActionEditor(); // refresh a.editor window, it could be open
+}
+}; // {namespace}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void glue_clearAllActions(geChannel *gch)
+{
+ if (!gdConfirmWin("Warning", "Clear all actions: are you sure?"))
+ return;
+ recorder::clearChan(gch->ch->index);
+ updateChannel(gch);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void glue_clearVolumeActions(geChannel *gch)
+{
+ if (!gdConfirmWin("Warning", "Clear all volume actions: are you sure?"))
+ return;
+ recorder::clearAction(gch->ch->index, G_ACTION_VOLUME);
+ updateChannel(gch);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void glue_clearStartStopActions(geChannel *gch)
+{
+ if (!gdConfirmWin("Warning", "Clear all start/stop actions: are you sure?"))
+ return;
+ recorder::clearAction(gch->ch->index, G_ACTION_KEYPRESS | G_ACTION_KEYREL | G_ACTION_KILL);
+ updateChannel(gch);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void glue_clearMuteActions(geChannel *gch)
+{
+ if (!gdConfirmWin("Warning", "Clear all mute actions: are you sure?"))
+ return;
+ recorder::clearAction(gch->ch->index, G_ACTION_MUTEON | G_ACTION_MUTEOFF);
+ updateChannel(gch);
+}
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * 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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#ifndef G_GLUE_RECORDER_H
+#define G_GLUE_RECORDER_H
+
+
+class geChannel;
+
+
+void glue_clearAllActions(geChannel *gch);
+void glue_clearMuteActions(geChannel *gch);
+void glue_clearVolumeActions(geChannel *gch);
+void glue_clearStartStopActions(geChannel *gch);
+
+
+#endif
*
* Giada - Your Hardcore Loopmachine
*
- * glue
- * Intermediate layer GUI <-> CORE.
- *
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
#include "../core/plugin.h"
#include "../core/conf.h"
#include "../core/patch.h"
-#include "../core/patch_DEPR_.h" // TODO - remove, used only for DEPR calls
#include "../core/sampleChannel.h"
#include "../core/midiChannel.h"
+#include "../core/clock.h"
#include "../core/wave.h"
#include "../utils/gui.h"
#include "../utils/log.h"
+#include "../utils/fs.h"
+#include "../gui/elems/basics/progress.h"
#include "../gui/elems/mainWindow/keyboard/column.h"
#include "../gui/elems/mainWindow/keyboard/keyboard.h"
#include "../gui/dialogs/gd_mainWindow.h"
#include "../gui/dialogs/gd_warnings.h"
-#include "../gui/dialogs/gd_browser.h"
-#include "main.h" // TODO - remove, used only for DEPR calls
+#include "../gui/dialogs/browser/browserSave.h"
+#include "../gui/dialogs/browser/browserLoad.h"
+#include "main.h"
#include "channel.h"
#include "storage.h"
-using std::string;
-using std::vector;
+extern gdMainWindow *G_MainWin;
-extern gdMainWindow *G_MainWin;
-extern Mixer G_Mixer;
-extern Recorder G_Recorder;
-extern Patch G_Patch;
-extern Conf G_Conf;
-extern Patch_DEPR_ G_Patch_DEPR_; // TODO - remove, used only for DEPR calls
-#ifdef WITH_VST
-extern PluginHost G_PluginHost;
-#endif
+using std::string;
+using std::vector;
+using namespace giada::m;
#ifdef WITH_VST
-static void __glue_fillPatchGlobalsPlugins__(vector <Plugin *> *host, vector<Patch::plugin_t> *patch)
+static void __glue_fillPatchGlobalsPlugins__(vector <Plugin *> *host, vector<patch::plugin_t> *patch)
{
for (unsigned i=0; i<host->size(); i++) {
Plugin *pl = host->at(i);
- Patch::plugin_t ppl;
+ patch::plugin_t ppl;
ppl.path = pl->getUniqueId();
ppl.bypass = pl->isBypassed();
int numParams = pl->getNumParameters();
{
for (unsigned i=0; i<G_MainWin->keyboard->getTotalColumns(); i++) {
geColumn *gCol = G_MainWin->keyboard->getColumn(i);
- Patch::column_t pCol;
+ patch::column_t pCol;
pCol.index = gCol->getIndex();
pCol.width = gCol->w();
for (int k=0; k<gCol->countChannels(); k++) {
Channel *colChannel = gCol->getChannel(k);
- for (unsigned j=0; j<G_Mixer.channels.size(); j++) {
- Channel *mixerChannel = G_Mixer.channels.at(j);
+ for (unsigned j=0; j<mixer::channels.size(); j++) {
+ Channel *mixerChannel = mixer::channels.at(j);
if (colChannel == mixerChannel) {
pCol.channels.push_back(mixerChannel->index);
break;
}
}
}
- G_Patch.columns.push_back(pCol);
+ patch::columns.push_back(pCol);
}
}
static void __glue_fillPatchChannels__(bool isProject)
{
- for (unsigned i=0; i<G_Mixer.channels.size(); i++) {
- G_Mixer.channels.at(i)->writePatch(i, isProject, &G_Patch);
+ for (unsigned i=0; i<mixer::channels.size(); i++) {
+ mixer::channels.at(i)->writePatch(i, isProject);
}
}
static void __glue_fillPatchGlobals__(const string &name)
{
- G_Patch.version = G_VERSION_STR;
- G_Patch.versionMajor = G_VERSION_MAJOR;
- G_Patch.versionMinor = G_VERSION_MINOR;
- G_Patch.versionPatch = G_VERSION_PATCH;
- G_Patch.name = name;
- G_Patch.bpm = G_Mixer.bpm;
- G_Patch.bars = G_Mixer.bars;
- G_Patch.beats = G_Mixer.beats;
- G_Patch.quantize = G_Mixer.quantize;
- G_Patch.masterVolIn = G_Mixer.inVol;
- G_Patch.masterVolOut = G_Mixer.outVol;
- G_Patch.metronome = G_Mixer.metronome;
+ patch::version = G_VERSION_STR;
+ patch::versionMajor = G_VERSION_MAJOR;
+ patch::versionMinor = G_VERSION_MINOR;
+ patch::versionPatch = G_VERSION_PATCH;
+ patch::name = name;
+ patch::bpm = clock::getBpm();
+ patch::bars = clock::getBars();
+ patch::beats = clock::getBeats();
+ patch::quantize = clock::getQuantize();
+ patch::masterVolIn = mixer::inVol;
+ patch::masterVolOut = mixer::outVol;
+ patch::metronome = mixer::metronome;
#ifdef WITH_VST
- __glue_fillPatchGlobalsPlugins__(G_PluginHost.getStack(PluginHost::MASTER_IN),
- &G_Patch.masterInPlugins);
- __glue_fillPatchGlobalsPlugins__(G_PluginHost.getStack(PluginHost::MASTER_OUT),
- &G_Patch.masterOutPlugins);
+ __glue_fillPatchGlobalsPlugins__(pluginHost::getStack(pluginHost::MASTER_IN),
+ &patch::masterInPlugins);
+ __glue_fillPatchGlobalsPlugins__(pluginHost::getStack(pluginHost::MASTER_OUT),
+ &patch::masterOutPlugins);
#endif
}
static bool __glue_savePatch__(const string &fullPath, const string &name,
bool isProject)
{
- G_Patch.init();
+ patch::init();
__glue_fillPatchGlobals__(name);
__glue_fillPatchChannels__(isProject);
__glue_fillPatchColumns__();
- if (G_Patch.write(fullPath)) {
+ if (patch::write(fullPath)) {
gu_updateMainWinLabel(name);
gu_log("[glue_savePatch] patch saved as %s\n", fullPath.c_str());
return true;
void glue_savePatch(void *data)
{
- gdSaveBrowser *browser = (gdSaveBrowser*) data;
+ gdBrowserSave *browser = (gdBrowserSave*) data;
string name = browser->getName();
string fullPath = browser->getCurrentPath() + G_SLASH + gu_stripExt(name) + ".gptc";
return;
if (__glue_savePatch__(fullPath, name, false)) { // false == not a project
- G_Conf.patchPath = gu_dirname(fullPath);
+ conf::patchPath = gu_dirname(fullPath);
browser->do_callback();
}
else
void glue_loadPatch(void *data)
{
- gdLoadBrowser *browser = (gdLoadBrowser*) data;
+ gdBrowserLoad *browser = (gdBrowserLoad*) data;
string fullPath = browser->getSelectedItem();
bool isProject = gu_isProject(browser->getSelectedItem());
basePath = fullPath + G_SLASH;
}
- /* try to load the new JSON-based patch. If it fails, fall back to deprecated
- * one. */
-
- int res = G_Patch.read(fileToLoad);
- bool deprecated = false;
-
- if (res == PATCH_UNREADABLE) {
- gu_log("[glue] failed reading JSON-based patch. Trying with the deprecated method\n");
- deprecated = true;
- res = glue_loadPatch__DEPR__(gu_basename(fileToLoad).c_str(), fileToLoad.c_str(),
- browser->getStatusBar(), isProject);
- }
-
+ int res = patch::read(fileToLoad);
if (res != PATCH_READ_OK) {
if (res == PATCH_UNREADABLE)
isProject ? gdAlert("This project is unreadable.") : gdAlert("This patch is unreadable.");
else
if (res == PATCH_INVALID)
isProject ? gdAlert("This project is not valid.") : gdAlert("This patch is not valid.");
-
browser->hideStatusBar();
return;
}
- else
- if (deprecated) {
- browser->do_callback();
- return;
- }
/* close all other windows. This prevents segfault if plugin
* windows GUIs are on. */
/* Add common stuff, columns and channels. Also increment the progress bar
* by 0.8 / total_channels steps. */
- float steps = 0.8 / G_Patch.channels.size();
- for (unsigned i=0; i<G_Patch.columns.size(); i++) {
- Patch::column_t *col = &G_Patch.columns.at(i);
+ float steps = 0.8 / patch::channels.size();
+ for (unsigned i=0; i<patch::columns.size(); i++) {
+ patch::column_t *col = &patch::columns.at(i);
G_MainWin->keyboard->addColumn(col->width);
- for (unsigned k=0; k<G_Patch.channels.size(); k++) {
- if (G_Patch.channels.at(k).column == col->index) {
- Channel *ch = glue_addChannel(G_Patch.channels.at(k).column,
- G_Patch.channels.at(k).type);
- ch->readPatch(basePath, k, &G_Patch, &G_Mixer.mutex_plugins,
- G_Conf.samplerate, G_Conf.rsmpQuality);
+ for (unsigned k=0; k<patch::channels.size(); k++) {
+ if (patch::channels.at(k).column == col->index) {
+ Channel *ch = glue_addChannel(patch::channels.at(k).column,
+ patch::channels.at(k).type);
+ ch->readPatch(basePath, k, &mixer::mutex_plugins, conf::samplerate,
+ conf::rsmpQuality);
}
- //__glue_setProgressBar__(status, steps);
browser->setStatusBar(steps);
}
}
/* fill Mixer */
- mh_readPatch();
+ mh::readPatch();
/* let recorder recompute the actions' positions if the current
* samplerate != patch samplerate */
- G_Recorder.updateSamplerate(G_Conf.samplerate, G_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 */
- G_Conf.patchPath = gu_dirname(fullPath);
+ conf::patchPath = gu_dirname(fullPath);
/* refresh GUI */
gu_updateControls();
- gu_updateMainWinLabel(G_Patch.name);
+ gu_updateMainWinLabel(patch::name);
browser->setStatusBar(0.1f);
//__glue_setProgressBar__(status, 1.0f);
#ifdef WITH_VST
- if (G_PluginHost.hasMissingPlugins())
+ if (pluginHost::hasMissingPlugins())
gdAlert("Some plugins were not loaded successfully.\nCheck the plugin browser to know more.");
#endif
/* -------------------------------------------------------------------------- */
-int glue_loadPatch__DEPR__(const char *fname, const char *fpath, gProgress *status, bool isProject)
-{
- /* update browser's status bar with % 0.1 */
-
- status->show();
- status->value(0.1f);
- //Fl::check();
- Fl::wait(0);
-
- /* is it a valid patch? */
-
- int res = G_Patch_DEPR_.open(fpath);
- if (res != PATCH_READ_OK)
- return res;
-
- /* close all other windows. This prevents segfault if plugin windows
- * GUI are on. */
-
- if (res)
- gu_closeAllSubwindows();
-
- /* reset the system. False(1): don't update the gui right now. False(2): do
- * not create empty columns. */
-
- glue_resetToInitState(false, false);
-
- status->value(0.2f); // progress status: % 0.2
- //Fl::check();
- Fl::wait(0);
-
- /* mixerHandler will update the samples inside Mixer */
-
- mh_loadPatch_DEPR_(isProject, gu_dirname(fpath).c_str());
-
- /* take the patch name and update the main window's title */
-
- G_Patch_DEPR_.getName();
- gu_updateMainWinLabel(G_Patch_DEPR_.name);
-
- status->value(0.4f); // progress status: 0.4
- //Fl::check();
- Fl::wait(0);
-
- G_Patch_DEPR_.readRecs();
- status->value(0.6f); // progress status: 0.6
- //Fl::check();
- Fl::wait(0);
-
-#ifdef WITH_VST
- int resPlugins = G_Patch_DEPR_.readPlugins();
- status->value(0.8f); // progress status: 0.8
- //Fl::check();
- Fl::wait(0);
-#endif
-
- /* this one is vital: let recorder recompute the actions' positions if
- * the current samplerate != patch samplerate */
-
- G_Recorder.updateSamplerate(G_Conf.samplerate, G_Patch_DEPR_.samplerate);
-
- /* update gui */
-
- gu_updateControls();
-
- status->value(1.0f); // progress status: 1.0 (done)
- //Fl::check();
- Fl::wait(0);
-
- /* save patchPath by taking the last dir of the broswer, in order to
- * reuse it the next time */
-
- G_Conf.patchPath = gu_dirname(fpath).c_str();
-
- gu_log("[glue] patch %s loaded\n", fname);
-
-#ifdef WITH_VST
- if (resPlugins != 1)
- gdAlert("Some VST plugins were not loaded successfully.");
-#endif
-
- gdAlert("This patch is using a deprecated format.\nPlease save it again to store it properly.");
-
- return res;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
void glue_saveProject(void *data)
{
- gdSaveBrowser *browser = (gdSaveBrowser*) data;
+ gdBrowserSave *browser = (gdBrowserSave*) data;
string name = browser->getName();
string folderPath = browser->getCurrentPath(); //browser->getSelectedItem();
string fullPath = folderPath + G_SLASH + gu_stripExt(name) + ".gprj";
/* copy all samples inside the folder. Takes and logical ones are saved
* via glue_saveSample() */
- for (unsigned i=0; i<G_Mixer.channels.size(); i++) {
+ for (unsigned i=0; i<mixer::channels.size(); i++) {
- if (G_Mixer.channels.at(i)->type == CHANNEL_MIDI)
+ if (mixer::channels.at(i)->type == CHANNEL_MIDI)
continue;
- SampleChannel *ch = (SampleChannel*) G_Mixer.channels.at(i);
+ SampleChannel *ch = (SampleChannel*) mixer::channels.at(i);
- if (ch->wave == NULL)
+ if (ch->wave == nullptr)
continue;
/* update the new samplePath: everything now comes from the project
void glue_loadSample(void *data)
{
- gdLoadBrowser *browser = (gdLoadBrowser*) data;
+ gdBrowserLoad *browser = (gdBrowserLoad*) data;
string fullPath = browser->getSelectedItem();
if (fullPath.empty())
int res = glue_loadChannel((SampleChannel*) browser->getChannel(), fullPath.c_str());
if (res == SAMPLE_LOADED_OK) {
- G_Conf.samplePath = gu_dirname(fullPath);
+ conf::samplePath = gu_dirname(fullPath);
browser->do_callback();
G_MainWin->delSubWindow(WID_SAMPLE_EDITOR); // if editor is open
}
void glue_saveSample(void *data)
{
- gdSaveBrowser *browser = (gdSaveBrowser*) data;
+ gdBrowserSave *browser = (gdBrowserSave*) data;
string name = browser->getName();
- string folderPath = browser->getSelectedItem();
+ string folderPath = browser->getCurrentPath();
if (name == "") {
gdAlert("Please choose a file name.");
if (!gdConfirmWin("Warning", "File exists: overwrite?"))
return;
- if (((SampleChannel*)browser->getChannel())->save(filePath.c_str())) {
- G_Conf.samplePath = gu_dirname(folderPath);
+ if (static_cast<SampleChannel*>(browser->getChannel())->save(filePath.c_str())) {
+ gu_log("[glue_saveSample] sample saved to %s\n", filePath.c_str());
+ conf::samplePath = gu_dirname(filePath);
browser->do_callback();
}
else
*
* Giada - Your Hardcore Loopmachine
*
- * glue
- * Intermediate layer GUI <-> CORE.
- *
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
* -------------------------------------------------------------------------- */
-#ifndef GLUE_STORAGE_H
-#define GLUE_STORAGE_H
+#ifndef G_GLUE_STORAGE_H
+#define G_GLUE_STORAGE_H
void glue_loadPatch (void *data);
-int glue_loadPatch__DEPR__(const char *fname, const char *fpath, class gProgress *status, bool isProject);
void glue_savePatch (void *data);
void glue_saveProject(void *data);
void glue_saveSample (void *data);
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * 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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#include <FL/Fl.H>
+#include "../gui/elems/mainWindow/mainTransport.h"
+#include "../gui/dialogs/gd_mainWindow.h"
+#include "../core/clock.h"
+#include "../core/kernelAudio.h"
+#include "../core/mixerHandler.h"
+#include "../core/mixer.h"
+#include "../core/recorder.h"
+#include "transport.h"
+
+
+extern gdMainWindow *G_MainWin;
+
+
+using namespace giada::m;
+
+
+void glue_startStopSeq(bool gui)
+{
+ clock::isRunning() ? glue_stopSeq(gui) : glue_startSeq(gui);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void glue_startSeq(bool gui)
+{
+ clock::start();
+
+#ifdef __linux__
+ kernelAudio::jackStart();
+#endif
+
+ if (!gui) {
+ Fl::lock();
+ G_MainWin->mainTransport->updatePlay(1);
+ Fl::unlock();
+ }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void glue_stopSeq(bool gui)
+{
+ mh::stopSequencer();
+
+#ifdef __linux__
+ kernelAudio::jackStop();
+#endif
+
+ /* what to do if we stop the sequencer and some action recs are active?
+ * Deactivate the button and delete any 'rec on' status */
+
+ if (recorder::active) {
+ recorder::active = false;
+ Fl::lock();
+ G_MainWin->mainTransport->updateRecAction(0);
+ Fl::unlock();
+ }
+
+ /* if input recs are active (who knows why) we must deactivate them.
+ * One might stop the sequencer while an input rec is running. */
+
+ if (mixer::recording) {
+ mh::stopInputRec();
+ Fl::lock();
+ G_MainWin->mainTransport->updateRecInput(0);
+ Fl::unlock();
+ }
+
+ if (!gui) {
+ Fl::lock();
+ G_MainWin->mainTransport->updatePlay(0);
+ Fl::unlock();
+ }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void glue_startStopMetronome(bool gui)
+{
+ mixer::metronome = !mixer::metronome;
+ if (!gui) {
+ Fl::lock();
+ G_MainWin->mainTransport->updateMetronome(mixer::metronome);
+ Fl::unlock();
+ }
+}
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * 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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#ifndef G_GLUE_TRANSPORT_H
+#define G_GLUE_TRANSPORT_H
+
+
+/* start, stop, rewind sequencer
+If gui == true the signal comes from an user interaction on the GUI,
+otherwise it's a MIDI/Jack/external signal. */
+
+void glue_startStopSeq(bool gui=true);
+void glue_startSeq(bool gui=true);
+void glue_stopSeq(bool gui=true);
+void glue_rewindSeq(bool gui=true, bool notifyJack=true);
+void glue_startStopMetronome(bool gui=true);
+
+
+#endif
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * 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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#include "../../../core/graphics.h"
+#include "../../../core/conf.h"
+#include "../../../core/const.h"
+#include "../../../utils/gui.h"
+#include "../../../utils/fs.h"
+#include "../../elems/browser.h"
+#include "../../elems/basics/button.h"
+#include "../../elems/basics/input.h"
+#include "../../elems/basics/progress.h"
+#include "../../elems/basics/check.h"
+#include "browserBase.h"
+
+
+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*))
+ : 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);
+ updir = new geButton(groupTop->x()+groupTop->w()-20, where->y(), 20, 20, "", updirOff_xpm, updirOn_xpm);
+ groupTop->end();
+ groupTop->resizable(where);
+
+ hiddenFiles->callback(cb_toggleHiddenFiles, (void*) this);
+
+ where->readonly(true);
+ where->cursor_color(COLOR_BG_DARK);
+ where->value(path.c_str());
+
+ updir->callback(cb_up, (void*) this);
+
+ browser = new geBrowser(8, groupTop->y()+groupTop->h()+8, w-16, h-93);
+ browser->loadDir(path);
+ if (path == conf::browserLastPath)
+ browser->preselect(conf::browserPosition, conf::browserLastValue);
+
+ Fl_Group *groupButtons = new Fl_Group(8, browser->y()+browser->h()+8, w-16, 20);
+ ok = new geButton(w-88, groupButtons->y(), 80, 20);
+ cancel = new geButton(w-ok->w()-96, groupButtons->y(), 80, 20, "Cancel");
+ status = new geProgress(8, groupButtons->y(), cancel->x()-16, 20);
+ status->minimum(0);
+ status->maximum(1);
+ status->hide(); // show the bar only if necessary
+ groupButtons->resizable(status);
+ groupButtons->end();
+
+ end();
+
+ cancel->callback(cb_close, (void*) this);
+
+ resizable(browser);
+ size_range(320, 200);
+
+ gu_setFavicon(this);
+ show();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+gdBrowserBase::~gdBrowserBase()
+{
+ conf::browserX = x();
+ conf::browserY = y();
+ conf::browserW = w();
+ conf::browserH = h();
+ conf::browserPosition = browser->position();
+ conf::browserLastPath = gu_dirname(browser->getSelectedItem());
+ conf::browserLastValue = browser->value();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+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()
+{
+ string dir = browser->getCurrentDir();
+ dir = dir.substr(0, dir.find_last_of(G_SLASH_STR)); // remove up to the next slash
+ browser->loadDir(dir);
+ where->value(browser->getCurrentDir().c_str());
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gdBrowserBase::__cb_close()
+{
+ do_callback();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gdBrowserBase::__cb_toggleHiddenFiles()
+{
+ browser->toggleHiddenFiles();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gdBrowserBase::setStatusBar(float v)
+{
+ status->value(status->value() + v);
+ Fl::wait(0);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gdBrowserBase::showStatusBar()
+{
+ status->show();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gdBrowserBase::hideStatusBar()
+{
+ status->hide();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+string gdBrowserBase::getCurrentPath()
+{
+ return where->value();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+string gdBrowserBase::getSelectedItem()
+{
+ return browser->getSelectedItem();
+}
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * 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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#ifndef GD_BROWSER_BASE_H
+#define GD_BROWSER_BASE_H
+
+
+#include "../window.h"
+
+
+class Channel;
+class Fl_Group;
+class geCheck;
+class geBrowser;
+class geButton;
+class geInput;
+class geProgress;
+
+
+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();
+
+ /* 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*));
+
+public:
+
+ ~gdBrowserBase();
+
+ /* getSelectedItem
+ * Return the full path of the selected file. */
+
+ std::string getSelectedItem();
+
+ /* setStatusBar
+ * Increment status bar for progress tracking. */
+
+ void setStatusBar(float v);
+
+ void showStatusBar();
+ void hideStatusBar();
+ std::string getCurrentPath();
+
+ Channel *getChannel() { return channel; }
+ void fireCallback() { callback((void*) this); }
+};
+
+
+#endif
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * 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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#include "../../../utils/fs.h"
+#include "../../elems/browser.h"
+#include "../../elems/basics/button.h"
+#include "../../elems/basics/input.h"
+#include "browserLoad.h"
+
+
+using std::string;
+
+
+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;
+
+ where->size(groupTop->w()-updir->w()-8, 20);
+
+ browser->callback(cb_down, (void*) this);
+
+ ok->label("Load");
+ ok->callback(cb_load, (void*) this);
+ ok->shortcut(FL_ENTER);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+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()
+{
+ callback((void*) this);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gdBrowserLoad::__cb_down()
+{
+ string path = browser->getSelectedItem();
+
+ if (path.empty() || !gu_isDir(path)) // when click on an empty area or not a dir
+ return;
+
+ browser->loadDir(path);
+ where->value(browser->getCurrentDir().c_str());
+}
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * 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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#ifndef GD_BROWSER_LOAD_H
+#define GD_BROWSER_LOAD_H
+
+
+#include "../window.h"
+#include "browserBase.h"
+
+
+class Channel;
+class Fl_Group;
+class geCheck;
+class geBrowser;
+class geButton;
+class geInput;
+class geProgress;
+
+
+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();
+
+public:
+
+ gdBrowserLoad(int x, int y, int w, int h, const std::string &title,
+ const std::string &path, void (*callback)(void*), Channel *ch);
+};
+
+
+#endif
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * 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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#include "../../../utils/fs.h"
+#include "../../elems/browser.h"
+#include "../../elems/basics/button.h"
+#include "../../elems/basics/input.h"
+#include "browserSave.h"
+
+
+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)
+ : gdBrowserBase(x, y, w, h, title, path, cb)
+{
+ channel = ch;
+
+ where->size(groupTop->w()-236, 20);
+
+ name = new geInput(where->x()+where->w()+8, where->y(), 200, 20);
+ name->value(_name.c_str());
+ groupTop->add(name);
+
+ browser->callback(cb_down, (void*) this);
+
+ ok->label("Save");
+ ok->callback(cb_save, (void*) this);
+ ok->shortcut(FL_ENTER);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+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()
+{
+ string path = browser->getSelectedItem();
+
+ if (path.empty()) // when click on an empty area
+ return;
+
+ /* if the selected item is a directory just load its content. If it's a file
+ * use it as the file name (i.e. fill name->value()). */
+
+ if (gu_isDir(path)) {
+ browser->loadDir(path);
+ where->value(browser->getCurrentDir().c_str());
+ }
+ else
+ name->value(browser->getSelectedItem(false).c_str());
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+string gdBrowserSave::getName()
+{
+ return name->value();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gdBrowserSave::__cb_save()
+{
+ callback((void*) this);
+}
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * 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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#ifndef GD_BROWSER_SAVE_H
+#define GD_BROWSER_SAVE_H
+
+
+#include "../window.h"
+#include "browserBase.h"
+
+
+class Channel;
+class Fl_Group;
+class geCheck;
+class geBrowser;
+class geButton;
+class geInput;
+class geProgress;
+
+
+class gdBrowserSave : public gdBrowserBase
+{
+private:
+
+ 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();
+
+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);
+
+ std::string getName();
+};
+
+
+#endif
*
* Giada - Your Hardcore Loopmachine
*
- * gd_about
- *
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
* -------------------------------------------------------------------------- */
+#include <FL/Fl_Pixmap.H>
+#include <FL/fl_draw.H>
#include <jansson.h>
#include "../../core/conf.h"
#include "../../core/const.h"
-#include "../../core/kernelAudio.h"
-#include "../../core/kernelMidi.h"
#include "../../core/graphics.h"
#ifdef WITH_VST
#include "../../deps/juce-config.h"
#endif
#include "../../utils/gui.h"
-#include "../elems/ge_mixed.h"
+#include "../../utils/deps.h"
+#include "../elems/basics/button.h"
+#include "../elems/basics/box.h"
#include "gd_about.h"
-extern Conf G_Conf;
-extern KernelAudio G_KernelAudio;
-extern KernelMidi G_KernelMidi;
+using namespace giada::m;
+using namespace giada::u;
gdAbout::gdAbout()
#ifdef WITH_VST
-: gWindow(340, 435, "About Giada")
+: gdWindow(340, 435, "About Giada")
{
#else
-: gWindow(340, 350, "About Giada")
+: gdWindow(340, 350, "About Giada")
{
#endif
- if (G_Conf.aboutX)
- resize(G_Conf.aboutX, G_Conf.aboutY, w(), h());
+ if (conf::aboutX)
+ resize(conf::aboutX, conf::aboutY, w(), h());
set_modal();
- logo = new gBox(8, 10, 324, 86);
- text = new gBox(8, 120, 324, 145);
- close = new gClick(252, h()-28, 80, 20, "Close");
+ logo = new geBox(8, 10, 324, 86);
+ text = new geBox(8, 120, 324, 145);
+ close = new geButton(252, h()-28, 80, 20, "Close");
#ifdef WITH_VST
- vstLogo = new gBox(8, 265, 324, 50);
- vstText = new gBox(8, 315, 324, 46);
+ vstLogo = new geBox(8, 265, 324, 50);
+ vstText = new geBox(8, 315, 324, 46);
#endif
end();
"Developed by Monocasual\n"
"Based on FLTK (%d.%d.%d), RtAudio (%s),\n"
"RtMidi (%s), Libsamplerate, Jansson (%s),\n"
- "Libsndfile"
+ "Libsndfile (%s)"
#ifdef WITH_VST
", JUCE (%d.%d.%d)\n\n"
#else
"News, infos, contacts and documentation:\n"
"www.giadamusic.com",
FL_MAJOR_VERSION, FL_MINOR_VERSION, FL_PATCH_VERSION,
- G_KernelAudio.getRtAudioVersion().c_str(),
- G_KernelMidi.getRtMidiVersion().c_str(),
- JANSSON_VERSION
+ deps::getRtAudioVersion().c_str(),
+ deps::getRtMidiVersion().c_str(),
+ JANSSON_VERSION, deps::getLibsndfileVersion().c_str()
#ifdef WITH_VST
, JUCE_MAJOR_VERSION, JUCE_MINOR_VERSION, JUCE_BUILDNUMBER);
#else
gdAbout::~gdAbout()
{
- G_Conf.aboutX = x();
- G_Conf.aboutY = y();
+ conf::aboutX = x();
+ conf::aboutY = y();
}
*
* Giada - Your Hardcore Loopmachine
*
- * gd_about
- *
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
#define GD_ABOUT_H
-#include "../elems/ge_window.h"
+#include "window.h"
+
+
+class geBox;
+class geButton;
-class gdAbout : public gWindow
+class gdAbout : public gdWindow
{
private:
- class gBox *logo;
- class gBox *text;
- class gClick *close;
+ geBox *logo;
+ geBox *text;
+ geButton *close;
#ifdef WITH_VST
- class gBox *vstText;
- class gBox *vstLogo;
+ geBox *vstText;
+ geBox *vstLogo;
#endif
public:
*
* Giada - Your Hardcore Loopmachine
*
- * gd_actionEditor
- *
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
* -------------------------------------------------------------------------- */
-#include <math.h>
+#include <cmath>
#include "../../utils/gui.h"
#include "../../core/graphics.h"
-#include "../../core/mixer.h"
-#include "../../core/recorder.h"
#include "../../core/conf.h"
-#include "../../core/channel.h"
+#include "../../core/const.h"
+#include "../../core/clock.h"
#include "../../core/sampleChannel.h"
-#include "../elems/actionEditor.h"
-#include "../elems/envelopeEditor.h"
-#include "../elems/muteEditor.h"
-#include "../elems/noteEditor.h"
-#include "../elems/ge_mixed.h"
#include "../elems/basics/scroll.h"
+#include "../elems/basics/button.h"
+#include "../elems/basics/resizerBar.h"
+#include "../elems/basics/choice.h"
+#include "../elems/basics/box.h"
+#include "../elems/actionEditor/actionEditor.h"
+#include "../elems/actionEditor/envelopeEditor.h"
+#include "../elems/actionEditor/muteEditor.h"
+#include "../elems/actionEditor/noteEditor.h"
+#include "../elems/actionEditor/gridTool.h"
#include "gd_actionEditor.h"
-extern Mixer G_Mixer;
-extern Conf G_Conf;
+using namespace giada::m;
gdActionEditor::gdActionEditor(Channel *chan)
- : gWindow(640, 284),
+ : gdWindow(640, 284),
chan (chan),
zoom (100),
coverX (0)
{
- if (G_Conf.actionEditorW) {
- resize(G_Conf.actionEditorX, G_Conf.actionEditorY, G_Conf.actionEditorW, G_Conf.actionEditorH);
- zoom = G_Conf.actionEditorZoom;
+ if (conf::actionEditorW) {
+ resize(conf::actionEditorX, conf::actionEditorY, conf::actionEditorW, conf::actionEditorH);
+ zoom = conf::actionEditorZoom;
}
- totalWidth = (int) ceilf(G_Mixer.framesInSequencer / (float) zoom);
+ totalWidth = (int) std::ceil(clock::getFramesInSequencer() / (float) zoom);
/* container with zoom buttons and the action type selector. Scheme of
* the resizable boxes: |[--b1--][actionType][--b2--][+][-]| */
upperArea->begin();
if (chan->type == CHANNEL_SAMPLE) {
- actionType = new gChoice(8, 8, 80, 20);
- gridTool = new gGridTool(actionType->x()+actionType->w()+4, 8, this);
+ actionType = new geChoice(8, 8, 80, 20);
+ gridTool = new geGridTool(actionType->x()+actionType->w()+4, 8, this);
actionType->add("key press");
actionType->add("key release");
actionType->add("kill chan");
actionType->deactivate();
}
else {
- gridTool = new gGridTool(8, 8, this);
+ gridTool = new geGridTool(8, 8, this);
}
- gBox *b1 = new gBox(gridTool->x()+gridTool->w()+4, 8, 300, 20); // padding actionType - zoomButtons
- zoomIn = new gClick(w()-8-40-4, 8, 20, 20, "", zoomInOff_xpm, zoomInOn_xpm);
- zoomOut = new gClick(w()-8-20, 8, 20, 20, "", zoomOutOff_xpm, zoomOutOn_xpm);
+ geBox *b1 = new geBox(gridTool->x()+gridTool->w()+4, 8, 300, 20); // padding actionType - zoomButtons
+ zoomIn = new geButton(w()-8-40-4, 8, 20, 20, "", zoomInOff_xpm, zoomInOn_xpm);
+ zoomOut = new geButton(w()-8-20, 8, 20, 20, "", zoomOutOff_xpm, zoomOutOn_xpm);
upperArea->end();
upperArea->resizable(b1);
ac = new geActionEditor (scroller->x(), upperArea->y()+upperArea->h()+8, this, ch);
mc = new geMuteEditor (scroller->x(), ac->y()+ac->h()+8, this);
- vc = new geEnvelopeEditor(scroller->x(), mc->y()+mc->h()+8, this, ACTION_VOLUME, RANGE_FLOAT, "volume");
+ vc = new geEnvelopeEditor(scroller->x(), mc->y()+mc->h()+8, this, G_ACTION_VOLUME, G_RANGE_FLOAT, "volume");
scroller->add(ac);
- //scroller->add(new gResizerBar(ac->x(), ac->y()+ac->h(), scroller->w(), 8));
+ //scroller->add(new geResizerBar(ac->x(), ac->y()+ac->h(), scroller->w(), 8));
scroller->add(mc);
- //scroller->add(new gResizerBar(mc->x(), mc->y()+mc->h(), scroller->w(), 8));
+ //scroller->add(new geResizerBar(mc->x(), mc->y()+mc->h(), scroller->w(), 8));
scroller->add(vc);
- //scroller->add(new gResizerBar(vc->x(), vc->y()+vc->h(), scroller->w(), 8));
+ //scroller->add(new geResizerBar(vc->x(), vc->y()+vc->h(), scroller->w(), 8));
/* fill volume envelope with actions from recorder */
else {
pr = new geNoteEditor(scroller->x(), upperArea->y()+upperArea->h()+8, this);
scroller->add(pr);
- scroller->add(new gResizerBar(pr->x(), pr->y()+pr->h(), scroller->w(), 8));
+ scroller->add(new geResizerBar(pr->x(), pr->y()+pr->h(), scroller->w(), 8));
}
end();
/* -------------------------------------------------------------------------- */
-gdActionEditor::~gdActionEditor() {
- G_Conf.actionEditorX = x();
- G_Conf.actionEditorY = y();
- G_Conf.actionEditorW = w();
- G_Conf.actionEditorH = h();
- G_Conf.actionEditorZoom = zoom;
+gdActionEditor::~gdActionEditor()
+{
+ conf::actionEditorX = x();
+ conf::actionEditorY = y();
+ conf::actionEditorW = w();
+ conf::actionEditorH = h();
+ conf::actionEditorZoom = zoom;
/** CHECKME - missing clear() ? */
/* -------------------------------------------------------------------------- */
-void gdActionEditor::__cb_zoomIn() {
-
+void gdActionEditor::__cb_zoomIn()
+{
/* zoom 50: empirical value, to avoid a totalWidth > 16 bit signed
* (32767 max), unsupported by FLTK 1.3.x */
/* -------------------------------------------------------------------------- */
-void gdActionEditor::__cb_zoomOut() {
-
+void gdActionEditor::__cb_zoomOut()
+{
zoom *= 2;
update();
/* -------------------------------------------------------------------------- */
-void gdActionEditor::update() {
- totalWidth = (int) ceilf(G_Mixer.framesInSequencer / (float) zoom);
+void gdActionEditor::update()
+{
+ totalWidth = (int) ceilf(clock::getFramesInSequencer() / (float) zoom);
if (totalWidth < scroller->w()) {
totalWidth = scroller->w();
- zoom = (int) ceilf(G_Mixer.framesInSequencer / (float) totalWidth);
+ zoom = (int) ceilf(clock::getFramesInSequencer() / (float) totalWidth);
scroller->scroll_to(0, scroller->yposition());
}
}
/* -------------------------------------------------------------------------- */
-int gdActionEditor::handle(int e) {
+int gdActionEditor::handle(int e)
+{
int ret = Fl_Group::handle(e);
switch (e) {
case FL_MOUSEWHEEL: {
/* -------------------------------------------------------------------------- */
-int gdActionEditor::getActionType() {
+int gdActionEditor::getActionType()
+{
if (actionType->value() == 0)
- return ACTION_KEYPRESS;
+ return G_ACTION_KEYPRESS;
else
if (actionType->value() == 1)
- return ACTION_KEYREL;
+ return G_ACTION_KEYREL;
else
if (actionType->value() == 2)
- return ACTION_KILLCHAN;
+ return G_ACTION_KILL;
else
return -1;
}
-
-
-/* -------------------------------------------------------------------------- */
-/* -------------------------------------------------------------------------- */
-/* -------------------------------------------------------------------------- */
-
-
-gGridTool::gGridTool(int x, int y, gdActionEditor *parent)
- : Fl_Group(x, y, 80, 20), parent(parent)
-{
- gridType = new gChoice(x, y, 40, 20);
- gridType->add("1");
- gridType->add("2");
- gridType->add("3");
- gridType->add("4");
- gridType->add("6");
- gridType->add("8");
- gridType->add("16");
- gridType->add("32");
- gridType->value(0);
- gridType->callback(cb_changeType, (void*)this);
-
- active = new gCheck (x+44, y+4, 12, 12);
-
- gridType->value(G_Conf.actionEditorGridVal);
- active->value(G_Conf.actionEditorGridOn);
-
- end();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-gGridTool::~gGridTool() {
- G_Conf.actionEditorGridVal = gridType->value();
- G_Conf.actionEditorGridOn = active->value();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gGridTool::cb_changeType(Fl_Widget *w, void *p) { ((gGridTool*)p)->__cb_changeType(); }
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gGridTool::__cb_changeType() {
- calc();
- parent->redraw();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-bool gGridTool::isOn() {
- return active->value();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-int gGridTool::getValue() {
- switch (gridType->value()) {
- case 0: return 1;
- case 1: return 2;
- case 2: return 3;
- case 3: return 4;
- case 4: return 6;
- case 5: return 8;
- case 6: return 16;
- case 7: return 32;
- }
- return 0;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gGridTool::calc() {
-
- points.clear();
- frames.clear();
- bars.clear();
- beats.clear();
-
- /* find beats, bars and grid. The method is the same of the waveform in sample
- * editor. Take totalwidth (the width in pixel of the area to draw), knowing
- * that totalWidth = totalFrames / zoom. Then, for each pixel of totalwidth,
- * put a concentrate of each block (which is totalFrames / zoom) */
-
- int j = 0;
- int fpgc = floor(G_Mixer.framesPerBeat / getValue()); // frames per grid cell
-
- for (int i=1; i<parent->totalWidth; i++) { // if i=0, step=0 -> useless cycle
- int step = parent->zoom*i;
- while (j < step && j < G_Mixer.totalFrames) {
- if (j % fpgc == 0) {
- points.push_back(i);
- frames.push_back(j);
- }
- if (j % G_Mixer.framesPerBeat == 0)
- beats.push_back(i);
- if (j % G_Mixer.framesPerBar == 0 && i != 1)
- bars.push_back(i);
- if (j == G_Mixer.totalFrames-1)
- parent->coverX = i;
- j++;
- }
- j = step;
- }
-
- /* fix coverX if == 0, which means G_Mixer.beats == 32 */
-
- if (G_Mixer.beats == 32)
- parent->coverX = parent->totalWidth;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-int gGridTool::getSnapPoint(int v) {
-
- if (v == 0) return 0;
-
- for (int i=0; i<(int)points.size(); i++) {
-
- if (i == (int) points.size()-1)
- return points.at(i);
-
- int gp = points.at(i);
- int gpn = points.at(i+1);
-
- if (v >= gp && v < gpn)
- return gp;
- }
- return v; // default value
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-int gGridTool::getSnapFrame(int v) {
-
- v *= parent->zoom; // transformation pixel -> frame
-
- for (int i=0; i<(int)frames.size(); i++) {
-
- if (i == (int) frames.size()-1)
- return frames.at(i);
-
- int gf = frames.at(i); // grid frame
- int gfn = frames.at(i+1); // grid frame next
-
- if (v >= gf && v < gfn) {
-
- /* which one is the closest? gf < v < gfn */
-
- if ((gfn - v) < (v - gf))
- return gfn;
- else
- return gf;
- }
- }
- return v; // default value
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-int gGridTool::getCellSize() {
- return (parent->coverX - parent->ac->x()) / G_Mixer.beats / getValue();
-}
-/* ---------------------------------------------------------------------
+/* -----------------------------------------------------------------------------
*
* Giada - Your Hardcore Loopmachine
*
- * gd_actionEditor
+ * -----------------------------------------------------------------------------
*
- * ---------------------------------------------------------------------
- *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
* along with Giada - Your Hardcore Loopmachine. If not, see
* <http://www.gnu.org/licenses/>.
*
- * ------------------------------------------------------------------ */
+ * -------------------------------------------------------------------------- */
#ifndef GD_ACTIONEDITOR_H
#define GD_ACTIONEDITOR_H
+
#include <vector>
#include <FL/Fl.H>
#include <FL/Fl_Double_Window.H>
#include <FL/Fl_Scroll.H>
-#include "../elems/ge_window.h"
+#include "window.h"
-using std::vector;
+class Channel;
+class geChoice;
+class geGridTool;
+class geButton;
+class geButton;
+class geScroll;
+class geActionEditor;
+class geMuteEditor;
+class geEnvelopeEditor;
+class geNoteEditor;
/* gActionEditor
- * main window which contains the tools for dealing with actions.
- * This class calculates chan, zoom, frames per beat, and so on. Each
- * sub-widget contains a pointer to this window to query those data. */
+Main window which contains the tools for dealing with actions. This class
+calculates chan, zoom, frames per beat, and so on. Each sub-widget contains a
+pointer to this window to query those data. */
-class gdActionEditor : public gWindow
+class gdActionEditor : public gdWindow
{
private:
/* update
- * compute total width, in pixel. */
+ Computes total width, in pixel. */
void update();
public:
- gdActionEditor(class Channel *chan);
+ gdActionEditor(Channel *chan);
~gdActionEditor();
int handle(int e);
inline void __cb_zoomIn();
inline void __cb_zoomOut();
- class gChoice *actionType;
- class gGridTool *gridTool;
- class gClick *zoomIn;
- class gClick *zoomOut;
- class geScroll *scroller; // widget container
-
- class geActionEditor *ac;
- class geMuteEditor *mc;
- class geEnvelopeEditor *vc;
- class geNoteEditor *pr;
+ geChoice *actionType;
+ geGridTool *gridTool;
+ geButton *zoomIn;
+ geButton *zoomOut;
+ geScroll *scroller; // widget container
- vector <class gActionWidget*> widgets;
+ geActionEditor *ac;
+ geMuteEditor *mc;
+ geEnvelopeEditor *vc;
+ geNoteEditor *pr;
- class Channel *chan;
+ Channel *chan;
int zoom;
int totalWidth; // total width of the widget, in pixel (zoom affected)
};
-/* ------------------------------------------------------------------ */
-
-
-class gGridTool : public Fl_Group {
-
-private:
- class gChoice *gridType;
- class gCheck *active;
-
- class gdActionEditor *parent;
-
- static void cb_changeType(Fl_Widget *w, void *p);
- inline void __cb_changeType();
-
-public:
-
- gGridTool(int x, int y, gdActionEditor *parent);
- ~gGridTool();
-
- int getValue();
- bool isOn();
- void calc();
-
- /* getSnapPoint
- * given a cursor position in input, return the x coordinates of the
- * nearest snap point (in pixel, clean, ie. not x()-shifted) */
-
- int getSnapPoint(int v);
- int getSnapFrame(int v);
-
- /* getCellSize
- * return the size in pixel of a single cell of the grid. */
-
- int getCellSize();
-
- vector<int> points; // points of the grid
- vector<int> frames; // frames of the grid
-
- vector<int> bars;
- vector<int> beats;
-};
-
-
#endif
-/* ---------------------------------------------------------------------
+/* -----------------------------------------------------------------------------
*
* Giada - Your Hardcore Loopmachine
*
- * gd_beatsInput
+ * -----------------------------------------------------------------------------
*
- * ---------------------------------------------------------------------
- *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
* along with Giada - Your Hardcore Loopmachine. If not, see
* <http://www.gnu.org/licenses/>.
*
- * ------------------------------------------------------------------ */
+ * -------------------------------------------------------------------------- */
#include "../../utils/gui.h"
#include "../../core/mixer.h"
+#include "../../core/clock.h"
#include "../../core/conf.h"
#include "../../core/const.h"
#include "../../glue/main.h"
-#include "../elems/ge_mixed.h"
+#include "../elems/basics/input.h"
+#include "../elems/basics/button.h"
+#include "../elems/basics/check.h"
#include "gd_beatsInput.h"
#include "gd_mainWindow.h"
-extern Mixer G_Mixer;
-extern Conf G_Conf;
extern gdMainWindow *mainWin;
+using namespace giada::m;
+
+
gdBeatsInput::gdBeatsInput()
- : gWindow(164, 60, "Beats")
+ : gdWindow(164, 60, "Beats")
{
- if (G_Conf.beatsX)
- resize(G_Conf.beatsX, G_Conf.beatsY, w(), h());
+ if (conf::beatsX)
+ resize(conf::beatsX, conf::beatsY, w(), h());
set_modal();
- beats = new gInput(8, 8, 35, 20);
- bars = new gInput(47, 8, 35, 20);
- ok = new gClick(86, 8, 70, 20, "Ok");
- resizeRec = new gCheck(8, 40, 12, 12, "resize recorded actions");
+ beats = new geInput(8, 8, 35, 20);
+ bars = new geInput(47, 8, 35, 20);
+ ok = new geButton(86, 8, 70, 20, "Ok");
+ resizeRec = new geCheck(8, 40, 12, 12, "resize recorded actions");
end();
- char buf_bars[3]; sprintf(buf_bars, "%d", G_Mixer.bars);
- char buf_beats[3]; sprintf(buf_beats, "%d", G_Mixer.beats);
+ 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->type(FL_INT_INPUT);
bars->type(FL_INT_INPUT);
ok->shortcut(FL_Enter);
ok->callback(cb_update_batt, (void*)this);
- resizeRec->value(G_Conf.resizeRecordings);
+ resizeRec->value(conf::resizeRecordings);
gu_setFavicon(this);
setId(WID_BEATS);
gdBeatsInput::~gdBeatsInput()
{
- G_Conf.beatsX = x();
- G_Conf.beatsY = y();
- G_Conf.resizeRecordings = resizeRec->value();
+ conf::beatsX = x();
+ conf::beatsY = y();
+ conf::resizeRecordings = resizeRec->value();
}
*
* Giada - Your Hardcore Loopmachine
*
- * gd_beatsInput
- *
* ---------------------------------------------------------------------
*
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
#include <FL/Fl.H>
#include <FL/Fl_Window.H>
-#include "../elems/ge_window.h"
+#include "window.h"
+
+
+class geInput;
+class geButton;
+class geCheck;
-class gdBeatsInput : public gWindow
+class gdBeatsInput : public gdWindow
{
private:
static void cb_update_batt(Fl_Widget *w, void *p);
inline void __cb_update_batt();
- class gInput *beats;
- class gInput *bars;
- class gClick *ok;
- class gCheck *resizeRec;
+ geInput *beats;
+ geInput *bars;
+ geButton *ok;
+ geCheck *resizeRec;
public:
-/* ---------------------------------------------------------------------
+/* -----------------------------------------------------------------------------
*
* Giada - Your Hardcore Loopmachine
*
- * gd_bpmInput
+ * -----------------------------------------------------------------------------
*
- * ---------------------------------------------------------------------
- *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
* along with Giada - Your Hardcore Loopmachine. If not, see
* <http://www.gnu.org/licenses/>.
*
- * ------------------------------------------------------------------ */
+ * -------------------------------------------------------------------------- */
+#include <cstring>
#include "../../core/conf.h"
#include "../../core/const.h"
#include "../../core/mixer.h"
+#include "../../core/clock.h"
#include "../../glue/main.h"
#include "../../utils/gui.h"
-#include "../elems/ge_mixed.h"
+#include "../elems/basics/button.h"
+#include "../elems/basics/input.h"
#include "gd_bpmInput.h"
#include "gd_mainWindow.h"
-extern Mixer G_Mixer;
-extern Conf G_Conf;
extern gdMainWindow *mainWin;
-gdBpmInput::gdBpmInput(const char *label)
-: gWindow(144, 36, "Bpm") {
+using namespace giada::m;
- if (G_Conf.bpmX)
- resize(G_Conf.bpmX, G_Conf.bpmY, w(), h());
+
+gdBpmInput::gdBpmInput(const char *label)
+: gdWindow(144, 36, "Bpm")
+{
+ if (conf::bpmX)
+ resize(conf::bpmX, conf::bpmY, w(), h());
set_modal();
- input_a = new gInput(8, 8, 30, 20);
- input_b = new gInput(42, 8, 20, 20);
- ok = new gClick(66, 8, 70, 20, "Ok");
+ input_a = new geInput(8, 8, 30, 20);
+ input_b = new geInput(42, 8, 20, 20);
+ ok = new geButton(66, 8, 70, 20, "Ok");
end();
char a[4];
- snprintf(a, 4, "%d", (int) G_Mixer.bpm);
+ snprintf(a, 4, "%d", (int) clock::getBpm());
char b[2];
for (unsigned i=0; i<strlen(label); i++) // looking for the dot
if (label[i] == '.') {
}
-/* ------------------------------------------------------------------ */
+/* -------------------------------------------------------------------------- */
-gdBpmInput::~gdBpmInput() {
- G_Conf.bpmX = x();
- G_Conf.bpmY = y();
+gdBpmInput::~gdBpmInput()
+{
+ conf::bpmX = x();
+ conf::bpmY = y();
}
-/* ------------------------------------------------------------------ */
+/* -------------------------------------------------------------------------- */
void gdBpmInput::cb_update_bpm(Fl_Widget *w, void *p) { ((gdBpmInput*)p)->__cb_update_bpm(); }
-/* ------------------------------------------------------------------ */
+/* -------------------------------------------------------------------------- */
-void gdBpmInput::__cb_update_bpm() {
+void gdBpmInput::__cb_update_bpm()
+{
if (strcmp(input_a->value(), "") == 0)
return;
glue_setBpm(input_a->value(), input_b->value());
-/* ---------------------------------------------------------------------
+/* -----------------------------------------------------------------------------
*
* Giada - Your Hardcore Loopmachine
*
- * gd_bpmInput
+ * -----------------------------------------------------------------------------
*
- * ---------------------------------------------------------------------
- *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
* along with Giada - Your Hardcore Loopmachine. If not, see
* <http://www.gnu.org/licenses/>.
*
- * ------------------------------------------------------------------ */
+ * -------------------------------------------------------------------------- */
+
#ifndef GD_BPMINPUT_H
#define GD_BPMINPUT_H
-#include <FL/Fl.H>
-#include <FL/Fl_Window.H>
-#include "../elems/ge_window.h"
+#include "window.h"
+
+
+class geInput;
+class geButton;
-class gdBpmInput : public gWindow {
+
+class gdBpmInput : public gdWindow
+{
private:
+
static void cb_update_bpm(Fl_Widget *w, void *p);
inline void __cb_update_bpm();
- class gInput *input_a;
- class gInput *input_b;
- class gClick *ok;
+ geInput *input_a;
+ geInput *input_b;
+ geButton *ok;
public:
+
gdBpmInput(const char *label); // pointer to mainWin->timing->bpm->label()
~gdBpmInput();
};
+++ /dev/null
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * gd_browser
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
- *
- * This file is part of Giada - Your Hardcore Loopmachine.
- *
- * Giada - Your Hardcore Loopmachine is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General
- * Public License as published by the Free Software Foundation, either
- * version 3 of the License, or (at your option) any later version.
- *
- * Giada - Your Hardcore Loopmachine is distributed in the hope that it
- * will be useful, but WITHOUT ANY WARRANTY; without even the implied
- * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Giada - Your Hardcore Loopmachine. If not, see
- * <http://www.gnu.org/licenses/>.
- *
- * -------------------------------------------------------------------------- */
-
-
-#include "../../core/graphics.h"
-#include "../../core/conf.h"
-#include "../../core/const.h"
-#include "../../utils/gui.h"
-#include "../elems/ge_mixed.h"
-#include "../elems/browser.h"
-#include "gd_browser.h"
-
-
-using std::string;
-
-
-extern Conf G_Conf;
-
-
-gdBaseBrowser::gdBaseBrowser(int x, int y, int w, int h, const string &title,
- const string &path, void (*callback)(void*))
- : gWindow(x, y, w, h, title.c_str()), callback(callback)
-{
- set_non_modal();
-
- groupTop = new Fl_Group(8, 8, w-16, 40);
- hiddenFiles = new gCheck(groupTop->x(), groupTop->y(), 400, 20, "Show hidden files");
- where = new gInput(groupTop->x(), hiddenFiles->y()+hiddenFiles->h(), 152, 20);
- updir = new gClick(groupTop->x()+groupTop->w()-20, where->y(), 20, 20, "", updirOff_xpm, updirOn_xpm);
- groupTop->end();
- groupTop->resizable(where);
-
- hiddenFiles->callback(cb_toggleHiddenFiles, (void*) this);
-
- where->readonly(true);
- where->cursor_color(COLOR_BG_DARK);
- where->value(path.c_str());
-
- updir->callback(cb_up, (void*) this);
-
- browser = new geBrowser(8, groupTop->y()+groupTop->h()+8, w-16, h-93);
- browser->loadDir(path);
- if (path == G_Conf.browserLastPath)
- browser->preselect(G_Conf.browserPosition, G_Conf.browserLastValue);
-
- Fl_Group *groupButtons = new Fl_Group(8, browser->y()+browser->h()+8, w-16, 20);
- ok = new gClick(w-88, groupButtons->y(), 80, 20);
- cancel = new gClick(w-ok->w()-96, groupButtons->y(), 80, 20, "Cancel");
- status = new gProgress(8, groupButtons->y(), cancel->x()-16, 20);
- status->minimum(0);
- status->maximum(1);
- status->hide(); // show the bar only if necessary
- groupButtons->resizable(status);
- groupButtons->end();
-
- end();
-
- cancel->callback(cb_close, (void*) this);
-
- resizable(browser);
- size_range(320, 200);
-
- gu_setFavicon(this);
- show();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-gdBaseBrowser::~gdBaseBrowser()
-{
- G_Conf.browserX = x();
- G_Conf.browserY = y();
- G_Conf.browserW = w();
- G_Conf.browserH = h();
- G_Conf.browserPosition = browser->position();
- G_Conf.browserLastPath = gu_dirname(browser->getSelectedItem());
- G_Conf.browserLastValue = browser->value();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gdBaseBrowser::cb_up (Fl_Widget *v, void *p) { ((gdBaseBrowser*)p)->__cb_up(); }
-void gdBaseBrowser::cb_close(Fl_Widget *v, void *p) { ((gdBaseBrowser*)p)->__cb_close(); }
-void gdBaseBrowser::cb_toggleHiddenFiles(Fl_Widget *v, void *p) { ((gdBaseBrowser*)p)->__cb_toggleHiddenFiles(); }
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gdBaseBrowser::__cb_up()
-{
- string dir = browser->getCurrentDir();
- dir = dir.substr(0, dir.find_last_of(G_SLASH_STR)); // remove up to the next slash
- browser->loadDir(dir);
- where->value(browser->getCurrentDir().c_str());
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gdBaseBrowser::__cb_close()
-{
- do_callback();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gdBaseBrowser::__cb_toggleHiddenFiles()
-{
- browser->toggleHiddenFiles();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gdBaseBrowser::setStatusBar(float v)
-{
- status->value(status->value() + v);
- Fl::wait(0);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gdBaseBrowser::showStatusBar()
-{
- status->show();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gdBaseBrowser::hideStatusBar()
-{
- status->hide();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-string gdBaseBrowser::getCurrentPath()
-{
- return where->value();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-string gdBaseBrowser::getSelectedItem()
-{
- return browser->getSelectedItem();
-}
-
-
-/* -------------------------------------------------------------------------- */
-/* -------------------------------------------------------------------------- */
-/* -------------------------------------------------------------------------- */
-
-
-gdSaveBrowser::gdSaveBrowser(int x, int y, int w, int h, const string &title,
- const string &path, const string &_name, void (*cb)(void*), Channel *ch)
- : gdBaseBrowser(x, y, w, h, title, path, cb)
-{
- channel = ch;
-
- where->size(groupTop->w()-236, 20);
-
- name = new gInput(where->x()+where->w()+8, where->y(), 200, 20);
- name->value(_name.c_str());
- groupTop->add(name);
-
- browser->callback(cb_down, (void*) this);
-
- ok->label("Save");
- ok->callback(cb_save, (void*) this);
- ok->shortcut(FL_ENTER);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gdSaveBrowser::cb_save(Fl_Widget *v, void *p) { ((gdSaveBrowser*)p)->__cb_save(); }
-void gdSaveBrowser::cb_down(Fl_Widget *v, void *p) { ((gdSaveBrowser*)p)->__cb_down(); }
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gdSaveBrowser::__cb_down()
-{
- string path = browser->getSelectedItem();
-
- if (path.empty()) // when click on an empty area
- return;
-
- /* if the selected item is a directory just load its content. If it's a file
- * use it as the file name (i.e. fill name->value()). */
-
- if (gu_isDir(path)) {
- browser->loadDir(path);
- where->value(browser->getCurrentDir().c_str());
- }
- else
- name->value(browser->getSelectedItem(false).c_str());
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-string gdSaveBrowser::getName()
-{
- return name->value();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gdSaveBrowser::__cb_save()
-{
- callback((void*) this);
-}
-
-
-/* -------------------------------------------------------------------------- */
-/* -------------------------------------------------------------------------- */
-/* -------------------------------------------------------------------------- */
-
-
-gdLoadBrowser::gdLoadBrowser(int x, int y, int w, int h, const string &title,
- const string &path, void (*cb)(void*), Channel *ch)
- : gdBaseBrowser(x, y, w, h, title, path, cb)
-{
- channel = ch;
-
- where->size(groupTop->w()-updir->w()-8, 20);
-
- browser->callback(cb_down, (void*) this);
-
- ok->label("Load");
- ok->callback(cb_load, (void*) this);
- ok->shortcut(FL_ENTER);
-}
-
-
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gdLoadBrowser::cb_load(Fl_Widget *v, void *p) { ((gdLoadBrowser*)p)->__cb_load(); }
-void gdLoadBrowser::cb_down(Fl_Widget *v, void *p) { ((gdLoadBrowser*)p)->__cb_down(); }
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gdLoadBrowser::__cb_load()
-{
- callback((void*) this);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gdLoadBrowser::__cb_down()
-{
- string path = browser->getSelectedItem();
-
- if (path.empty() || !gu_isDir(path)) // when click on an empty area or not a dir
- return;
-
- browser->loadDir(path);
- where->value(browser->getCurrentDir().c_str());
-}
+++ /dev/null
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * gd_browser
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
- *
- * This file is part of Giada - Your Hardcore Loopmachine.
- *
- * Giada - Your Hardcore Loopmachine is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General
- * Public License as published by the Free Software Foundation, either
- * version 3 of the License, or (at your option) any later version.
- *
- * Giada - Your Hardcore Loopmachine is distributed in the hope that it
- * will be useful, but WITHOUT ANY WARRANTY; without even the implied
- * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Giada - Your Hardcore Loopmachine. If not, see
- * <http://www.gnu.org/licenses/>.
- *
- * -------------------------------------------------------------------------- */
-
-
-#ifndef GD_BROWSER_H
-#define GD_BROWSER_H
-
-
-#include "../elems/ge_window.h"
-
-
-class gdBaseBrowser : public gWindow
-{
-protected:
-
- class Fl_Group *groupTop;
- class gCheck *hiddenFiles;
- class geBrowser *browser;
- class gClick *ok;
- class gClick *cancel;
- class gInput *where;
- class gClick *updir;
- class gProgress *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();
-
- /* Callback
- * Fired when the save/load button is pressed. */
-
- void (*callback)(void*);
-
- class Channel *channel;
-
-public:
-
- gdBaseBrowser(int x, int y, int w, int h, const string &title,
- const string &path, void (*callback)(void*));
-
- ~gdBaseBrowser();
-
- /* getSelectedItem
- * Return the full path of the selected file. */
-
- string getSelectedItem();
-
- /* setStatusBar
- * Increment status bar for progress tracking. */
-
- void setStatusBar(float v);
-
- gProgress *getStatusBar() { return status; } // TODO - remove with Patch_DEPR_
- void showStatusBar();
- void hideStatusBar();
- string getCurrentPath();
-
- Channel *getChannel() { return channel; }
- void fireCallback() { callback((void*) this); }
-};
-
-
-/* -------------------------------------------------------------------------- */
-
-
-class gdSaveBrowser : public gdBaseBrowser
-{
-private:
-
- class gInput *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();
-
-public:
-
- gdSaveBrowser(int x, int y, int w, int h, const string &title,
- const string &path, const string &name, void (*callback)(void*),
- class Channel *ch);
-
- string getName();
-};
-
-
-/* -------------------------------------------------------------------------- */
-
-
-class gdLoadBrowser : public gdBaseBrowser
-{
-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();
-
-public:
-
- gdLoadBrowser(int x, int y, int w, int h, const string &title,
- const string &path, void (*callback)(void*), class Channel *ch);
-};
-
-#endif
*
* Giada - Your Hardcore Loopmachine
*
- * gd_config
- *
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
* -------------------------------------------------------------------------- */
-#include <RtMidi.h>
#include <FL/Fl_Tabs.H>
#include "../../core/conf.h"
-#include "../../core/midiMapConf.h"
-#include "../../core/patch_DEPR_.h"
-#include "../../core/kernelAudio.h"
-#include "../../core/kernelMidi.h"
-#include "../../core/pluginHost.h"
+#include "../../core/const.h"
#include "../../utils/gui.h"
-#include "../../utils/log.h"
-#include "../../utils/string.h"
-#include "../elems/ge_mixed.h"
#include "../elems/basics/boxtypes.h"
+#include "../elems/basics/button.h"
+#include "../elems/config/tabMisc.h"
+#include "../elems/config/tabMidi.h"
+#include "../elems/config/tabAudio.h"
+#include "../elems/config/tabBehaviors.h"
+#include "../elems/config/tabPlugins.h"
#include "gd_config.h"
-#include "gd_keyGrabber.h"
-#include "gd_devInfo.h"
-#include "gd_browser.h"
-
-
-extern Patch_DEPR_ G_Patch_DEPR_;
-extern Conf G_Conf;
-extern KernelAudio G_KernelAudio;
-extern KernelMidi G_KernelMidi;
-extern bool G_audio_status;
-extern MidiMapConf G_MidiMap;
-
-#ifdef WITH_VST
-extern PluginHost G_PluginHost;
-#endif
-
-
-using std::string;
-
-
-gTabMisc::gTabMisc(int X, int Y, int W, int H)
- : Fl_Group(X, Y, W, H, "Misc")
-{
- begin();
- debugMsg = new gChoice(x()+92, y()+9, 253, 20, "Debug messages");
- end();
-
- debugMsg->add("(disabled)");
- debugMsg->add("To standard output");
- debugMsg->add("To file");
-
- labelsize(GUI_FONT_SIZE_BASE);
-
- switch (G_Conf.logMode) {
- case LOG_MODE_MUTE:
- debugMsg->value(0);
- break;
- case LOG_MODE_STDOUT:
- debugMsg->value(1);
- break;
- case LOG_MODE_FILE:
- debugMsg->value(2);
- break;
- }
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gTabMisc::save()
-{
- switch(debugMsg->value()) {
- case 0:
- G_Conf.logMode = LOG_MODE_MUTE;
- break;
- case 1:
- G_Conf.logMode = LOG_MODE_STDOUT;
- break;
- case 2:
- G_Conf.logMode = LOG_MODE_FILE;
- break;
- }
-}
-
-
-/* -------------------------------------------------------------------------- */
-/* -------------------------------------------------------------------------- */
-/* -------------------------------------------------------------------------- */
-
-
-gTabAudio::gTabAudio(int X, int Y, int W, int H)
- : Fl_Group(X, Y, W, H, "Sound System")
-{
- begin();
- soundsys = new gChoice(x()+92, y()+9, 253, 20, "System");
- buffersize = new gChoice(x()+92, y()+37, 55, 20, "Buffer size");
- samplerate = new gChoice(x()+290, y()+37, 55, 20, "Sample rate");
- sounddevOut = new gChoice(x()+92, y()+65, 225, 20, "Output device");
- devOutInfo = new gClick (x()+325, y()+65, 20, 20, "?");
- channelsOut = new gChoice(x()+92, y()+93, 55, 20, "Output channels");
- limitOutput = new gCheck (x()+155, y()+97, 55, 20, "Limit output");
- sounddevIn = new gChoice(x()+92, y()+121, 225, 20, "Input device");
- devInInfo = new gClick (x()+325, y()+121, 20, 20, "?");
- channelsIn = new gChoice(x()+92, y()+149, 55, 20, "Input channels");
- delayComp = new gInput (x()+290, y()+149, 55, 20, "Rec delay comp.");
- rsmpQuality = new gChoice(x()+92, y()+177, 253, 20, "Resampling");
- new gBox(x(), rsmpQuality->y()+rsmpQuality->h()+8, w(), 92,
- "Restart Giada for the changes to take effect.");
- end();
- labelsize(GUI_FONT_SIZE_BASE);
-
- soundsys->add("(none)");
-
-#if defined(__linux__)
-
- if (G_KernelAudio.hasAPI(RtAudio::LINUX_ALSA))
- soundsys->add("ALSA");
- if (G_KernelAudio.hasAPI(RtAudio::UNIX_JACK))
- soundsys->add("Jack");
- if (G_KernelAudio.hasAPI(RtAudio::LINUX_PULSE))
- soundsys->add("PulseAudio");
-
- switch (G_Conf.soundSystem) {
- case SYS_API_NONE:
- soundsys->showItem("(none)");
- break;
- case SYS_API_ALSA:
- soundsys->showItem("ALSA");
- break;
- case SYS_API_JACK:
- soundsys->showItem("Jack");
- buffersize->deactivate();
- samplerate->deactivate();
- break;
- case SYS_API_PULSE:
- soundsys->showItem("PulseAudio");
- break;
- }
-
-#elif defined(_WIN32)
-
- if (G_KernelAudio.hasAPI(RtAudio::WINDOWS_DS))
- soundsys->add("DirectSound");
- if (G_KernelAudio.hasAPI(RtAudio::WINDOWS_ASIO))
- soundsys->add("ASIO");
- if (G_KernelAudio.hasAPI(RtAudio::WINDOWS_WASAPI))
- soundsys->add("WASAPI");
-
- switch (G_Conf.soundSystem) {
- case SYS_API_NONE:
- soundsys->showItem("(none)");
- break;
- case SYS_API_DS:
- soundsys->showItem("DirectSound");
- break;
- case SYS_API_ASIO:
- soundsys->showItem("ASIO");
- break;
- case SYS_API_WASAPI:
- soundsys->showItem("WASAPI");
- break;
- }
-
-#elif defined (__APPLE__)
-
- if (G_KernelAudio.hasAPI(RtAudio::MACOSX_CORE))
- soundsys->add("CoreAudio");
-
- switch (G_Conf.soundSystem) {
- case SYS_API_NONE:
- soundsys->showItem("(none)");
- break;
- case SYS_API_CORE:
- soundsys->showItem("CoreAudio");
- break;
- }
-
-#endif
-
- soundsysInitValue = soundsys->value();
-
- soundsys->callback(cb_deactivate_sounddev, (void*)this);
-
- sounddevIn->callback(cb_fetchInChans, this);
- sounddevOut->callback(cb_fetchOutChans, this);
-
- devOutInfo->callback(cb_showOutputInfo, this);
- devInInfo->callback(cb_showInputInfo, this);
-
- if (G_Conf.soundSystem != SYS_API_NONE) {
- fetchSoundDevs();
- fetchOutChans(sounddevOut->value());
- fetchInChans(sounddevIn->value());
-
- /* fill frequency dropdown menu */
- /* TODO - add fetchFrequencies() */
-
- int nfreq = G_KernelAudio.getTotalFreqs(sounddevOut->value());
- for (int i=0; i<nfreq; i++) {
- int freq = G_KernelAudio.getFreq(sounddevOut->value(), i);
- samplerate->add(gu_itoa(freq).c_str());
- if (freq == G_Conf.samplerate)
- samplerate->value(i);
- }
- }
- else {
- sounddevIn->deactivate();
- sounddevOut->deactivate();
- channelsIn->deactivate();
- channelsOut->deactivate();
- devOutInfo->deactivate();
- devInInfo->deactivate();
- samplerate->deactivate();
- }
-
- buffersize->add("8");
- buffersize->add("16");
- buffersize->add("32");
- buffersize->add("64");
- buffersize->add("128");
- buffersize->add("256");
- buffersize->add("512");
- buffersize->add("1024");
- buffersize->add("2048");
- buffersize->add("4096");
- buffersize->showItem(gu_itoa(G_Conf.buffersize).c_str());
-
- rsmpQuality->add("Sinc best quality (very slow)");
- rsmpQuality->add("Sinc medium quality (slow)");
- rsmpQuality->add("Sinc basic quality (medium)");
- rsmpQuality->add("Zero Order Hold (fast)");
- rsmpQuality->add("Linear (very fast)");
- rsmpQuality->value(G_Conf.rsmpQuality);
-
- delayComp->value(gu_itoa(G_Conf.delayComp).c_str());
- delayComp->type(FL_INT_INPUT);
- delayComp->maximum_size(5);
-
- limitOutput->value(G_Conf.limitOutput);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gTabAudio::cb_deactivate_sounddev(Fl_Widget *w, void *p) { ((gTabAudio*)p)->__cb_deactivate_sounddev(); }
-void gTabAudio::cb_fetchInChans(Fl_Widget *w, void *p) { ((gTabAudio*)p)->__cb_fetchInChans(); }
-void gTabAudio::cb_fetchOutChans(Fl_Widget *w, void *p) { ((gTabAudio*)p)->__cb_fetchOutChans(); }
-void gTabAudio::cb_showInputInfo(Fl_Widget *w, void *p) { ((gTabAudio*)p)->__cb_showInputInfo(); }
-void gTabAudio::cb_showOutputInfo(Fl_Widget *w, void *p) { ((gTabAudio*)p)->__cb_showOutputInfo(); }
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gTabAudio::__cb_fetchInChans()
-{
- fetchInChans(sounddevIn->value());
- channelsIn->value(0);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gTabAudio::__cb_fetchOutChans()
-{
- fetchOutChans(sounddevOut->value());
- channelsOut->value(0);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gTabAudio::__cb_showInputInfo()
-{
- unsigned dev = G_KernelAudio.getDeviceByName(sounddevIn->text(sounddevIn->value()));
- new gdDevInfo(dev);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gTabAudio::__cb_showOutputInfo()
-{
- unsigned dev = G_KernelAudio.getDeviceByName(sounddevOut->text(sounddevOut->value()));
- new gdDevInfo(dev);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gTabAudio::__cb_deactivate_sounddev()
-{
- /* if the user changes sound system (eg ALSA->JACK) device menu deactivates.
- * If it returns to the original sound system, we re-fill the list by
- * querying kernelAudio. Watch out if soundsysInitValue == 0: you don't want
- * to query kernelAudio for '(none)' soundsystem! */
-
- if (soundsysInitValue == soundsys->value() && soundsysInitValue != 0) {
- sounddevOut->clear();
- sounddevIn->clear();
-
- fetchSoundDevs();
-
- /* the '?' button is added by fetchSoundDevs */
-
- fetchOutChans(sounddevOut->value());
- sounddevOut->activate();
- channelsOut->activate();
-
- /* chan menus and '?' button are activated by fetchInChans(...) */
-
- fetchInChans(sounddevIn->value());
- sounddevIn->activate();
- samplerate->activate();
- }
- else {
- sounddevOut->deactivate();
- sounddevOut->clear();
- sounddevOut->add("-- restart to fetch device(s) --");
- sounddevOut->value(0);
- channelsOut->deactivate();
- devOutInfo->deactivate();
- samplerate->deactivate();
-
- sounddevIn->deactivate();
- sounddevIn->clear();
- sounddevIn->add("-- restart to fetch device(s) --");
- sounddevIn->value(0);
- channelsIn->deactivate();
- devInInfo->deactivate();
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-
-void gTabAudio::fetchInChans(int menuItem)
-{
- /* if menuItem==0 device in input is disabled. */
-
- if (menuItem == 0) {
- devInInfo ->deactivate();
- channelsIn->deactivate();
- delayComp ->deactivate();
- return;
- }
-
- devInInfo ->activate();
- channelsIn->activate();
- delayComp ->activate();
-
- channelsIn->clear();
-
- unsigned dev = G_KernelAudio.getDeviceByName(sounddevIn->text(sounddevIn->value()));
- unsigned chs = G_KernelAudio.getMaxInChans(dev);
-
- if (chs == 0) {
- channelsIn->add("none");
- channelsIn->value(0);
- return;
- }
- for (unsigned i=0; i<chs; i+=2) {
- char str[16];
- sprintf(str, "%d-%d", (i+1), (i+2));
- channelsIn->add(str);
- }
- channelsIn->value(G_Conf.channelsIn);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gTabAudio::fetchOutChans(int menuItem)
-{
- channelsOut->clear();
-
- unsigned dev = G_KernelAudio.getDeviceByName(sounddevOut->text(sounddevOut->value()));
- unsigned chs = G_KernelAudio.getMaxOutChans(dev);
-
- if (chs == 0) {
- channelsOut->add("none");
- channelsOut->value(0);
- return;
- }
- for (unsigned i=0; i<chs; i+=2) {
- char str[16];
- sprintf(str, "%d-%d", (i+1), (i+2));
- channelsOut->add(str);
- }
- channelsOut->value(G_Conf.channelsOut);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-int gTabAudio::findMenuDevice(gChoice *m, int device)
-{
- if (device == -1)
- return 0;
-
- if (G_audio_status == false)
- return 0;
-
- for (int i=0; i<m->size(); i++) {
- if (G_KernelAudio.getDeviceName(device) == "")
- continue;
- if (m->text(i) == NULL)
- continue;
- if (m->text(i) == G_KernelAudio.getDeviceName(device))
- return i;
- }
-
- return 0;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gTabAudio::fetchSoundDevs()
-{
- if (G_KernelAudio.numDevs == 0) {
- sounddevOut->add("-- no devices found --");
- sounddevOut->value(0);
- sounddevIn->add("-- no devices found --");
- sounddevIn->value(0);
- devInInfo ->deactivate();
- devOutInfo->deactivate();
- }
- else {
-
- devInInfo ->activate();
- devOutInfo->activate();
-
- /* input device may be disabled: now device number -1 is the disabled
- * one. KernelAudio knows how to handle it. */
-
- sounddevIn->add("(disabled)");
-
- for (unsigned i=0; i<G_KernelAudio.numDevs; i++) {
-
- /* escaping '/', very dangerous in FLTK (it creates a submenu) */
-
- string tmp = G_KernelAudio.getDeviceName(i);
- for (unsigned k=0; k<tmp.size(); k++)
- if (tmp[k] == '/' || tmp[k] == '|' || tmp[k] == '&' || tmp[k] == '_')
- tmp[k] = '-';
-
- /* add to list devices with at least 1 channel available. In this
- * way we can filter devices only for input or output, e.g. an input
- * devices has 0 output channels. */
-
- if (G_KernelAudio.getMaxOutChans(i) > 0)
- sounddevOut->add(tmp.c_str());
-
- if (G_KernelAudio.getMaxInChans(i) > 0)
- sounddevIn->add(tmp.c_str());
- }
-
- /* we show the device saved in the configuration file. */
-
- if (sounddevOut->size() == 0) {
- sounddevOut->add("-- no devices found --");
- sounddevOut->value(0);
- devOutInfo->deactivate();
- }
- else {
- int outMenuValue = findMenuDevice(sounddevOut, G_Conf.soundDeviceOut);
- sounddevOut->value(outMenuValue);
- }
-
- if (sounddevIn->size() == 0) {
- sounddevIn->add("-- no devices found --");
- sounddevIn->value(0);
- devInInfo->deactivate();
- }
- else {
- int inMenuValue = findMenuDevice(sounddevIn, G_Conf.soundDeviceIn);
- sounddevIn->value(inMenuValue);
- }
- }
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gTabAudio::save()
-{
- string text = soundsys->text(soundsys->value());
-
- if (text == "(none)") {
- G_Conf.soundSystem = SYS_API_NONE;
- return;
- }
-
-#if defined(__linux__)
-
- else if (text == "ALSA")
- G_Conf.soundSystem = SYS_API_ALSA;
- else if (text == "Jack")
- G_Conf.soundSystem = SYS_API_JACK;
- else if (text == "PulseAudio")
- G_Conf.soundSystem = SYS_API_PULSE;
-
-#elif defined(_WIN32)
-
- else if (text == "DirectSound")
- G_Conf.soundSystem = SYS_API_DS;
- else if (text == "ASIO")
- G_Conf.soundSystem = SYS_API_ASIO;
- else if (text == "WASAPI")
- G_Conf.soundSystem = SYS_API_WASAPI;
-
-#elif defined (__APPLE__)
-
- else if (text == "CoreAudio")
- G_Conf.soundSystem = SYS_API_CORE;
-
-#endif
-
- /* use the device name to search into the drop down menu's */
-
- G_Conf.soundDeviceOut = G_KernelAudio.getDeviceByName(sounddevOut->text(sounddevOut->value()));
- G_Conf.soundDeviceIn = G_KernelAudio.getDeviceByName(sounddevIn->text(sounddevIn->value()));
- G_Conf.channelsOut = channelsOut->value();
- G_Conf.channelsIn = channelsIn->value();
- G_Conf.limitOutput = limitOutput->value();
- G_Conf.rsmpQuality = rsmpQuality->value();
-
- /* if sounddevOut is disabled (because of system change e.g. alsa ->
- * jack) its value is equal to -1. Change it! */
-
- if (G_Conf.soundDeviceOut == -1)
- G_Conf.soundDeviceOut = 0;
-
- int bufsize = atoi(buffersize->text());
- if (bufsize % 2 != 0) bufsize++;
- if (bufsize < 8) bufsize = 8;
- if (bufsize > 8192) bufsize = 8192;
- G_Conf.buffersize = bufsize;
-
- const Fl_Menu_Item *i = NULL;
- i = samplerate->mvalue(); // mvalue() returns a pointer to the last menu item that was picked
- if (i)
- G_Conf.samplerate = atoi(i->label());
-
- G_Conf.delayComp = atoi(delayComp->value());
-}
-/* -------------------------------------------------------------------------- */
-/* -------------------------------------------------------------------------- */
-/* -------------------------------------------------------------------------- */
-
-
-gTabMidi::gTabMidi(int X, int Y, int W, int H)
- : Fl_Group(X, Y, W, H, "MIDI")
-{
- begin();
- system = new gChoice(x()+92, y()+9, 253, 20, "System");
- portOut = new gChoice(x()+92, system->y()+system->h()+8, 253, 20, "Output port");
- portIn = new gChoice(x()+92, portOut->y()+portOut->h()+8, 253, 20, "Input port");
- noNoteOff = new gCheck (x()+92, portIn->y()+portIn->h()+8, 253, 20, "Device does not send NoteOff");
- midiMap = new gChoice(x()+92, noNoteOff->y()+noNoteOff->h(), 253, 20, "Output Midi Map");
- sync = new gChoice(x()+92, midiMap->y()+midiMap->h()+8, 253, 20, "Sync");
- new gBox(x(), sync->y()+sync->h()+8, w(), h()-125, "Restart Giada for the changes to take effect.");
- end();
-
- labelsize(GUI_FONT_SIZE_BASE);
-
- system->callback(cb_changeSystem, (void*)this);
-
- fetchSystems();
- fetchOutPorts();
- fetchInPorts();
- fetchMidiMaps();
-
- noNoteOff->value(G_Conf.noNoteOff);
-
- sync->add("(disabled)");
- sync->add("MIDI Clock (master)");
- sync->add("MTC (master)");
- if (G_Conf.midiSync == MIDI_SYNC_NONE)
- sync->value(0);
- else if (G_Conf.midiSync == MIDI_SYNC_CLOCK_M)
- sync->value(1);
- else if (G_Conf.midiSync == MIDI_SYNC_MTC_M)
- sync->value(2);
-
- systemInitValue = system->value();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gTabMidi::fetchOutPorts() {
-
- if (G_KernelMidi.numOutPorts == 0) {
- portOut->add("-- no ports found --");
- portOut->value(0);
- portOut->deactivate();
- }
- else {
-
- portOut->add("(disabled)");
-
- for (unsigned i=0; i<G_KernelMidi.numOutPorts; i++)
- portOut->add(gu_removeFltkChars(G_KernelMidi.getOutPortName(i)).c_str());
-
- portOut->value(G_Conf.midiPortOut+1); // +1 because midiPortOut=-1 is '(disabled)'
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-
-void gTabMidi::fetchInPorts()
-{
- if (G_KernelMidi.numInPorts == 0) {
- portIn->add("-- no ports found --");
- portIn->value(0);
- portIn->deactivate();
- }
- else {
-
- portIn->add("(disabled)");
-
- for (unsigned i=0; i<G_KernelMidi.numInPorts; i++)
- portIn->add(gu_removeFltkChars(G_KernelMidi.getInPortName(i)).c_str());
-
- portIn->value(G_Conf.midiPortIn+1); // +1 because midiPortIn=-1 is '(disabled)'
- }
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gTabMidi::fetchMidiMaps()
-{
- if (G_MidiMap.maps.size() == 0) {
- midiMap->add("(no MIDI maps available)");
- midiMap->value(0);
- midiMap->deactivate();
- return;
- }
-
- for (unsigned i=0; i<G_MidiMap.maps.size(); i++) {
- const char *imap = G_MidiMap.maps.at(i).c_str();
- midiMap->add(imap);
- if (G_Conf.midiMapPath == imap)
- midiMap->value(i);
- }
-
- /* Preselect the 0 midimap if nothing is selected but midimaps exist. */
-
- if (midiMap->value() == -1 && G_MidiMap.maps.size() > 0)
- midiMap->value(0);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gTabMidi::save()
-{
- string text = system->text(system->value());
-
- if (text == "ALSA")
- G_Conf.midiSystem = RtMidi::LINUX_ALSA;
- else if (text == "Jack")
- G_Conf.midiSystem = RtMidi::UNIX_JACK;
- else if (text == "Multimedia MIDI")
- G_Conf.midiSystem = RtMidi::WINDOWS_MM;
- else if (text == "OSX Core MIDI")
- G_Conf.midiSystem = RtMidi::MACOSX_CORE;
-
- G_Conf.midiPortOut = portOut->value()-1; // -1 because midiPortOut=-1 is '(disabled)'
- G_Conf.midiPortIn = portIn->value()-1; // -1 because midiPortIn=-1 is '(disabled)'
-
- G_Conf.noNoteOff = noNoteOff->value();
- G_Conf.midiMapPath = G_MidiMap.maps.size() == 0 ? "" : midiMap->text(midiMap->value());
-
- if (sync->value() == 0)
- G_Conf.midiSync = MIDI_SYNC_NONE;
- else if (sync->value() == 1)
- G_Conf.midiSync = MIDI_SYNC_CLOCK_M;
- else if (sync->value() == 2)
- G_Conf.midiSync = MIDI_SYNC_MTC_M;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gTabMidi::fetchSystems()
-{
-#if defined(__linux__)
-
- if (G_KernelMidi.hasAPI(RtMidi::LINUX_ALSA))
- system->add("ALSA");
- if (G_KernelMidi.hasAPI(RtMidi::UNIX_JACK))
- system->add("Jack");
-
-#elif defined(_WIN32)
-
- if (G_KernelMidi.hasAPI(RtMidi::WINDOWS_MM))
- system->add("Multimedia MIDI");
-
-#elif defined (__APPLE__)
-
- system->add("OSX Core MIDI");
-
-#endif
-
- switch (G_Conf.midiSystem) {
- case RtMidi::LINUX_ALSA: system->showItem("ALSA"); break;
- case RtMidi::UNIX_JACK: system->showItem("Jack"); break;
- case RtMidi::WINDOWS_MM: system->showItem("Multimedia MIDI"); break;
- case RtMidi::MACOSX_CORE: system->showItem("OSX Core MIDI"); break;
- default: system->value(0); break;
- }
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gTabMidi::cb_changeSystem(Fl_Widget *w, void *p) { ((gTabMidi*)p)->__cb_changeSystem(); }
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gTabMidi::__cb_changeSystem()
-{
- /* if the user changes MIDI device (eg ALSA->JACK) device menu deactivates.
- * If it returns to the original system, we re-fill the list by
- * querying kernelMidi. */
-
- if (systemInitValue == system->value()) {
- portOut->clear();
- fetchOutPorts();
- portOut->activate();
- portIn->clear();
- fetchInPorts();
- portIn->activate();
- noNoteOff->activate();
- sync->activate();
- }
- else {
- portOut->deactivate();
- portOut->clear();
- portOut->add("-- restart to fetch device(s) --");
- portOut->value(0);
- portIn->deactivate();
- portIn->clear();
- portIn->add("-- restart to fetch device(s) --");
- portIn->value(0);
- noNoteOff->deactivate();
- sync->deactivate();
- }
-
-}
-
-
-/* -------------------------------------------------------------------------- */
-/* -------------------------------------------------------------------------- */
-/* -------------------------------------------------------------------------- */
-
-
-gTabBehaviors::gTabBehaviors(int X, int Y, int W, int H)
- : Fl_Group(X, Y, W, H, "Behaviors")
-{
- begin();
- Fl_Group *radioGrp_1 = new Fl_Group(x(), y()+10, w(), 70); // radio group for the mutex
- new gBox(x(), y()+10, 70, 25, "When a channel with recorded actions is halted:", FL_ALIGN_LEFT);
- recsStopOnChanHalt_1 = new gRadio(x()+25, y()+35, 280, 20, "stop it immediately");
- recsStopOnChanHalt_0 = new gRadio(x()+25, y()+55, 280, 20, "play it until finished");
- radioGrp_1->end();
-
- Fl_Group *radioGrp_2 = new Fl_Group(x(), y()+70, w(), 70); // radio group for the mutex
- new gBox(x(), y()+80, 70, 25, "When the sequencer is halted:", FL_ALIGN_LEFT);
- chansStopOnSeqHalt_1 = new gRadio(x()+25, y()+105, 280, 20, "stop immediately all dynamic channels");
- chansStopOnSeqHalt_0 = new gRadio(x()+25, y()+125, 280, 20, "play all dynamic channels until finished");
- radioGrp_2->end();
-
- treatRecsAsLoops = new gCheck(x(), y()+155, 280, 20, "Treat one shot channels with actions as loops");
-
- end();
- labelsize(GUI_FONT_SIZE_BASE);
-
- G_Conf.recsStopOnChanHalt == 1 ? recsStopOnChanHalt_1->value(1) : recsStopOnChanHalt_0->value(1);
- G_Conf.chansStopOnSeqHalt == 1 ? chansStopOnSeqHalt_1->value(1) : chansStopOnSeqHalt_0->value(1);
- G_Conf.treatRecsAsLoops == 1 ? treatRecsAsLoops->value(1) : treatRecsAsLoops->value(0);
-
- recsStopOnChanHalt_1->callback(cb_radio_mutex, (void*)this);
- recsStopOnChanHalt_0->callback(cb_radio_mutex, (void*)this);
- chansStopOnSeqHalt_1->callback(cb_radio_mutex, (void*)this);
- chansStopOnSeqHalt_0->callback(cb_radio_mutex, (void*)this);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gTabBehaviors::cb_radio_mutex(Fl_Widget *w, void *p) { ((gTabBehaviors*)p)->__cb_radio_mutex(w); }
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gTabBehaviors::__cb_radio_mutex(Fl_Widget *w)
-{
- ((Fl_Button *)w)->type(FL_RADIO_BUTTON);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gTabBehaviors::save()
-{
- G_Conf.recsStopOnChanHalt = recsStopOnChanHalt_1->value() == 1 ? 1 : 0;
- G_Conf.chansStopOnSeqHalt = chansStopOnSeqHalt_1->value() == 1 ? 1 : 0;
- G_Conf.treatRecsAsLoops = treatRecsAsLoops->value() == 1 ? 1 : 0;
-}
-
-
-/* -------------------------------------------------------------------------- */
-/* -------------------------------------------------------------------------- */
-/* -------------------------------------------------------------------------- */
-
-
-#ifdef WITH_VST
-
-gTabPlugins::gTabPlugins(int X, int Y, int W, int H)
- : Fl_Group(X, Y, W, H, "Plugins")
-{
- folderPath = new gInput(x()+w()-250, y()+8, 250, 20);
- scanButton = new gClick(x()+w()-120, folderPath->y()+folderPath->h()+8, 120, 20);
- info = new gBox(x(), scanButton->y()+scanButton->h()+8, w(), 242);
-
- end();
-
- labelsize(GUI_FONT_SIZE_BASE);
-
- info->label("Scan in progress. Please wait...");
- info->hide();
-
- folderPath->value(G_Conf.pluginPath.c_str());
- folderPath->label("Plugins folder");
-
- scanButton->callback(cb_scan, (void*) this);
-
- updateCount();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gTabPlugins::updateCount()
-{
- string scanLabel = "Scan (" + gu_itoa(G_PluginHost.countAvailablePlugins()) + " found)";
- scanButton->label(scanLabel.c_str());
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gTabPlugins::cb_scan(Fl_Widget *w, void *p) { ((gTabPlugins*)p)->__cb_scan(w); }
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gTabPlugins::cb_onScan(float progress, void *p)
-{
- string l = "Scan in progress (" + gu_itoa((int)(progress*100)) + "%). Please wait...";
- ((gTabPlugins *)p)->info->label(l.c_str());
- Fl::wait();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gTabPlugins::__cb_scan(Fl_Widget *w)
-{
- info->show();
- G_PluginHost.scanDir(folderPath->value(), cb_onScan, (void*) this);
- G_PluginHost.saveList(gu_getHomePath() + G_SLASH + "plugins.xml");
- info->hide();
- updateCount();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gTabPlugins::save()
-{
- G_Conf.pluginPath = folderPath->value();
-}
-
-
-#endif // if WITH_VST
-
-
-/* -------------------------------------------------------------------------- */
-/* -------------------------------------------------------------------------- */
-/* -------------------------------------------------------------------------- */
+using namespace giada::m;
-gdConfig::gdConfig(int w, int h) : gWindow(w, h, "Configuration")
+gdConfig::gdConfig(int w, int h) : gdWindow(w, h, "Configuration")
{
set_modal();
- if (G_Conf.configX)
- resize(G_Conf.configX, G_Conf.configY, this->w(), this->h());
+ if (conf::configX)
+ resize(conf::configX, conf::configY, this->w(), this->h());
Fl_Tabs *tabs = new Fl_Tabs(8, 8, w-16, h-44);
tabs->box(G_CUSTOM_BORDER_BOX);
tabs->labelcolor(COLOR_TEXT_0);
tabs->begin();
- tabAudio = new gTabAudio(tabs->x()+10, tabs->y()+20, tabs->w()-20, tabs->h()-40);
- tabMidi = new gTabMidi(tabs->x()+10, tabs->y()+20, tabs->w()-20, tabs->h()-40);
- tabBehaviors = new gTabBehaviors(tabs->x()+10, tabs->y()+20, tabs->w()-20, tabs->h()-40);
- tabMisc = new gTabMisc(tabs->x()+10, tabs->y()+20, tabs->w()-20, tabs->h()-40);
+ tabAudio = new geTabAudio(tabs->x()+10, tabs->y()+20, tabs->w()-20, tabs->h()-40);
+ tabMidi = new geTabMidi(tabs->x()+10, tabs->y()+20, tabs->w()-20, tabs->h()-40);
+ tabBehaviors = new geTabBehaviors(tabs->x()+10, tabs->y()+20, tabs->w()-20, tabs->h()-40);
+ tabMisc = new geTabMisc(tabs->x()+10, tabs->y()+20, tabs->w()-20, tabs->h()-40);
#ifdef WITH_VST
- tabPlugins = new gTabPlugins(tabs->x()+10, tabs->y()+20, tabs->w()-20, tabs->h()-40);
+ tabPlugins = new geTabPlugins(tabs->x()+10, tabs->y()+20, tabs->w()-20, tabs->h()-40);
#endif
tabs->end();
- save = new gClick (w-88, h-28, 80, 20, "Save");
- cancel = new gClick (w-176, h-28, 80, 20, "Cancel");
+ save = new geButton (w-88, h-28, 80, 20, "Save");
+ cancel = new geButton (w-176, h-28, 80, 20, "Cancel");
end();
gdConfig::~gdConfig()
{
- G_Conf.configX = x();
- G_Conf.configY = y();
+ conf::configX = x();
+ conf::configY = y();
}
*
* Giada - Your Hardcore Loopmachine
*
- * gd_config
- *
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
#define GD_CONFIG_H
-#include "../elems/ge_window.h"
+#include "window.h"
+
+
+class geTabAudio;
+class geTabBehaviors;
+class geTabMidi;
+class geTabMisc;
+#ifdef WITH_VST
+class geTabPlugins;
+#endif
+class geButton;
+class geChoice;
+class geCheck;
+class geInput;
+class geRadio;
+class geBox;
-class gdConfig : public gWindow
+class gdConfig : public gdWindow
{
private:
- static void cb_save_config (Fl_Widget *w, void *p);
- static void cb_cancel (Fl_Widget *w, void *p);
+ static void cb_save_config(Fl_Widget *w, void *p);
+ static void cb_cancel (Fl_Widget *w, void *p);
inline void __cb_save_config();
inline void __cb_cancel();
gdConfig(int w, int h);
~gdConfig();
- class gTabAudio *tabAudio;
- class gTabBehaviors *tabBehaviors;
- class gTabMidi *tabMidi;
- class gTabMisc *tabMisc;
+ geTabAudio *tabAudio;
+ geTabBehaviors *tabBehaviors;
+ geTabMidi *tabMidi;
+ geTabMisc *tabMisc;
#ifdef WITH_VST
- class gTabPlugins *tabPlugins;
+ geTabPlugins *tabPlugins;
#endif
- class gClick *save;
- class gClick *cancel;
-};
-
-
-/* -------------------------------------------------------------------------- */
-
-
-class gTabMidi : public Fl_Group
-{
-private:
-
- void fetchSystems();
- void fetchOutPorts();
- void fetchInPorts();
- void fetchMidiMaps();
-
- static void cb_changeSystem (Fl_Widget *w, void *p);
- inline void __cb_changeSystem();
-
- int systemInitValue;
-
-public:
-
- class gChoice *system;
- class gChoice *portOut;
- class gChoice *portIn;
- class gCheck *noNoteOff;
- class gChoice *midiMap;
- class gChoice *sync;
-
- gTabMidi(int x, int y, int w, int h);
-
- void save();
+ geButton *save;
+ geButton *cancel;
};
-/* -------------------------------------------------------------------------- */
-
-
-class gTabAudio : public Fl_Group
-{
-private:
-
- static void cb_deactivate_sounddev(Fl_Widget *w, void *p);
- static void cb_fetchInChans (Fl_Widget *w, void *p);
- static void cb_fetchOutChans (Fl_Widget *w, void *p);
- static void cb_showInputInfo (Fl_Widget *w, void *p);
- static void cb_showOutputInfo (Fl_Widget *w, void *p);
- inline void __cb_deactivate_sounddev();
- inline void __cb_fetchInChans();
- inline void __cb_fetchOutChans();
- inline void __cb_showInputInfo();
- inline void __cb_showOutputInfo();
-
- void fetchSoundDevs();
- void fetchInChans(int menuItem);
- void fetchOutChans(int menuItem);
- int findMenuDevice(class gChoice *m, int device);
-
- int soundsysInitValue;
-
-public:
-
- class gChoice *soundsys;
- class gChoice *samplerate;
- class gChoice *rsmpQuality;
- class gChoice *sounddevIn;
- class gClick *devInInfo;
- class gChoice *channelsIn;
- class gChoice *sounddevOut;
- class gClick *devOutInfo;
- class gChoice *channelsOut;
- class gCheck *limitOutput;
- class gChoice *buffersize;
- class gInput *delayComp;
-
- gTabAudio(int x, int y, int w, int h);
-
- void save();
-};
-
-
-/* -------------------------------------------------------------------------- */
-
-
-class gTabBehaviors : public Fl_Group
-{
-private:
-
- static void cb_radio_mutex (Fl_Widget *w, void *p);
- inline void __cb_radio_mutex(Fl_Widget *w);
-
-public:
-
- class gRadio *recsStopOnChanHalt_1;
- class gRadio *recsStopOnChanHalt_0;
- class gRadio *chansStopOnSeqHalt_1;
- class gRadio *chansStopOnSeqHalt_0;
- class gCheck *treatRecsAsLoops;
-
- gTabBehaviors(int x, int y, int w, int h);
-
- void save();
-};
-
-
-/* -------------------------------------------------------------------------- */
-
-
-class gTabMisc : public Fl_Group
-{
-public:
-
- class gChoice *debugMsg;
-
- gTabMisc(int x, int y, int w, int h);
-
- void save();
-};
-
-
-/* -------------------------------------------------------------------------- */
-
-
-#ifdef WITH_VST
-
-class gTabPlugins : public Fl_Group
-{
-private:
-
- static void cb_scan (Fl_Widget *w, void *p);
- static void cb_onScan(float progress, void *p);
- inline void __cb_scan(Fl_Widget *w);
-
- void updateCount();
-
-public:
-
- class gInput *folderPath;
- class gClick *scanButton;
- class gBox *info;
-
- gTabPlugins(int x, int y, int w, int h);
-
- void save();
-};
-
-#endif
-
#endif
-/* ---------------------------------------------------------------------
+/* -----------------------------------------------------------------------------
*
* Giada - Your Hardcore Loopmachine
*
- * gd_devInfo
+ * -----------------------------------------------------------------------------
*
- * ---------------------------------------------------------------------
- *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
* along with Giada - Your Hardcore Loopmachine. If not, see
* <http://www.gnu.org/licenses/>.
*
- * ------------------------------------------------------------------ */
+ * -------------------------------------------------------------------------- */
+#include <FL/fl_draw.H>
#include "../../core/kernelAudio.h"
#include "../../utils/gui.h"
#include "../../utils/string.h"
-#include "../elems/ge_mixed.h"
+#include "../elems/basics/button.h"
+#include "../elems/basics/box.h"
+#include "window.h"
#include "gd_devInfo.h"
-extern KernelAudio G_KernelAudio;
-
-
using std::string;
+using namespace giada::m;
gdDevInfo::gdDevInfo(unsigned dev)
{
set_modal();
- text = new gBox(8, 8, 320, 200, "", (Fl_Align) (FL_ALIGN_LEFT | FL_ALIGN_TOP));
- close = new gClick(252, h()-28, 80, 20, "Close");
+ text = new geBox(8, 8, 320, 200, "", (Fl_Align) (FL_ALIGN_LEFT | FL_ALIGN_TOP));
+ close = new geButton(252, h()-28, 80, 20, "Close");
end();
string body = "";
int lines = 7;
- body = "Device name: " + G_KernelAudio.getDeviceName(dev) + "\n";
- body += "Total output(s): " + gu_itoa(G_KernelAudio.getMaxOutChans(dev)) + "\n";
- body += "Total intput(s): " + gu_itoa(G_KernelAudio.getMaxInChans(dev)) + "\n";
- body += "Duplex channel(s): " + gu_itoa(G_KernelAudio.getDuplexChans(dev)) + "\n";
- body += "Default output: " + string(G_KernelAudio.isDefaultOut(dev) ? "yes" : "no") + "\n";
- body += "Default input: " + string(G_KernelAudio.isDefaultIn(dev) ? "yes" : "no") + "\n";
+ body = "Device name: " + kernelAudio::getDeviceName(dev) + "\n";
+ body += "Total output(s): " + gu_itoa(kernelAudio::getMaxOutChans(dev)) + "\n";
+ body += "Total intput(s): " + gu_itoa(kernelAudio::getMaxInChans(dev)) + "\n";
+ body += "Duplex channel(s): " + gu_itoa(kernelAudio::getDuplexChans(dev)) + "\n";
+ body += "Default output: " + string(kernelAudio::isDefaultOut(dev) ? "yes" : "no") + "\n";
+ body += "Default input: " + string(kernelAudio::isDefaultIn(dev) ? "yes" : "no") + "\n";
- int totalFreq = G_KernelAudio.getTotalFreqs(dev);
+ int totalFreq = kernelAudio::getTotalFreqs(dev);
body += "Supported frequencies: " + gu_itoa(totalFreq);
for (int i=0; i<totalFreq; i++) {
body += "\n "; // add new line each 6 printed freqs AND on the first line (i % 0 != 0)
lines++;
}
- body += gu_itoa( G_KernelAudio.getFreq(dev, i)) + " ";
+ body += gu_itoa( kernelAudio::getFreq(dev, i)) + " ";
}
text->copy_label(body.c_str());
-/* ---------------------------------------------------------------------
+/* -----------------------------------------------------------------------------
*
* Giada - Your Hardcore Loopmachine
*
- * gd_devInfo
+ * -----------------------------------------------------------------------------
*
- * ---------------------------------------------------------------------
- *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
* along with Giada - Your Hardcore Loopmachine. If not, see
* <http://www.gnu.org/licenses/>.
*
- * ------------------------------------------------------------------ */
+ * -------------------------------------------------------------------------- */
#ifndef GD_DEV_INFO_H
#define GD_DEV_INFO_H
-#include <FL/Fl.H>
+
#include <FL/Fl_Window.H>
-class gdDevInfo : public Fl_Window {
+class geBox;
+class geButton;
+
+
+class gdDevInfo : public Fl_Window
+{
private:
- class gBox *text;
- class gClick *close;
+
+ geBox *text;
+ geButton *close;
public:
+
gdDevInfo(unsigned dev);
~gdDevInfo();
};
+++ /dev/null
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * gd_editor
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
- *
- * This file is part of Giada - Your Hardcore Loopmachine.
- *
- * Giada - Your Hardcore Loopmachine is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General
- * Public License as published by the Free Software Foundation, either
- * version 3 of the License, or (at your option) any later version.
- *
- * Giada - Your Hardcore Loopmachine is distributed in the hope that it
- * will be useful, but WITHOUT ANY WARRANTY; without even the implied
- * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Giada - Your Hardcore Loopmachine. If not, see
- * <http://www.gnu.org/licenses/>.
- *
- * -------------------------------------------------------------------------- */
-
-
-#include "../../glue/channel.h"
-#include "../../core/waveFx.h"
-#include "../../core/conf.h"
-#include "../../core/const.h"
-#include "../../core/graphics.h"
-#include "../../core/sampleChannel.h"
-#include "../../core/mixer.h"
-#include "../../core/wave.h"
-#include "../elems/ge_waveform.h"
-#include "../elems/ge_mixed.h"
-#include "../elems/ge_waveTools.h"
-#include "../elems/mainWindow/keyboard/channel.h"
-#include "gd_warnings.h"
-#include "gd_editor.h"
-
-
-extern Mixer G_Mixer;
-extern Conf G_Conf;
-
-
-gdEditor::gdEditor(SampleChannel *ch)
- : gWindow(640, 480),
- ch(ch)
-{
- set_non_modal();
-
- if (G_Conf.sampleEditorX)
- resize(G_Conf.sampleEditorX, G_Conf.sampleEditorY, G_Conf.sampleEditorW, G_Conf.sampleEditorH);
-
- /* top bar: grid and zoom tools */
-
- Fl_Group *bar = new Fl_Group(8, 8, w()-16, 20);
- bar->begin();
- grid = new gChoice(bar->x(), bar->y(), 50, 20);
- snap = new gCheck(grid->x()+grid->w()+4, bar->y()+4, 12, 12);
- zoomOut = new gClick(bar->x()+bar->w()-20, bar->y(), 20, 20, "", zoomOutOff_xpm, zoomOutOn_xpm);
- zoomIn = new gClick(zoomOut->x()-24, bar->y(), 20, 20, "", zoomInOff_xpm, zoomInOn_xpm);
- bar->end();
- bar->resizable(new gBox(grid->x()+grid->w()+4, bar->y(), 80, bar->h()));
-
- /* waveform */
-
- waveTools = new gWaveTools(8, 36, w()-16, h()-120, ch);
- waveTools->end();
-
- /* other tools */
-
- Fl_Group *tools = new Fl_Group(8, waveTools->y()+waveTools->h()+8, w()-16, 130);
- tools->begin();
- volume = new gDial (tools->x()+50, tools->y(), 20, 20, "Volume");
- volumeNum = new gInput(volume->x()+volume->w()+4, tools->y(), 46, 20, "dB");
-
- boost = new gDial (volumeNum->x()+volumeNum->w()+108, tools->y(), 20, 20, "Boost");
- boostNum = new gInput(boost->x()+boost->w()+4, tools->y(), 44, 20, "dB");
-
- normalize = new gClick(boostNum->x()+boostNum->w()+54, tools->y(), 70, 20, "Normalize");
- pan = new gDial (normalize->x()+normalize->w()+40, tools->y(), 20, 20, "Pan");
- panNum = new gInput(pan->x()+pan->w()+4, tools->y(), 45, 20, "%");
-
- pitch = new gDial (tools->x()+50, volume->y()+volume->h()+4, 20, 20, "Pitch");
- pitchNum = new gInput(pitch->x()+pitch->w()+4, volume->y()+volume->h()+4, 46, 20);
- pitchToBar = new gClick(pitchNum->x()+pitchNum->w()+4, volume->y()+volume->h()+4, 60, 20, "To bar");
- pitchToSong = new gClick(pitchToBar->x()+pitchToBar->w()+4, volume->y()+volume->h()+4, 60, 20, "To song");
- pitchHalf = new gClick(pitchToSong->x()+pitchToSong->w()+4, volume->y()+volume->h()+4, 20, 20, "", divideOff_xpm, divideOn_xpm);
- pitchDouble = new gClick(pitchHalf->x()+pitchHalf->w()+4, volume->y()+volume->h()+4, 20, 20, "", multiplyOff_xpm, multiplyOn_xpm);
- pitchReset = new gClick(pitchDouble->x()+pitchDouble->w()+4, volume->y()+volume->h()+4, 46, 20, "Reset");
- reload = new gClick(pitchReset->x()+pitchReset->w()+4, volume->y()+volume->h()+4, 70, 20, "Reload");
-
- chanStart = new gInput(tools->x()+60, pitch->y()+pitch->h()+4, 60, 20, "Range");
- chanEnd = new gInput(chanStart->x()+chanStart->w()+4, pitch->y()+pitch->h()+4, 60, 20, "");
- resetStartEnd = new gClick(chanEnd->x()+chanEnd->w()+4, pitch->y()+pitch->h()+4, 60, 20, "Reset");
-
- tools->end();
- tools->resizable(new gBox(panNum->x()+panNum->w()+4, tools->y(), 80, tools->h()));
-
- /* grid tool setup */
-
- grid->add("(off)");
- grid->add("2");
- grid->add("3");
- grid->add("4");
- grid->add("6");
- grid->add("8");
- grid->add("16");
- grid->add("32");
- grid->add("64");
- grid->value(G_Conf.sampleEditorGridVal);
- grid->callback(cb_changeGrid, (void*)this);
-
- snap->value(G_Conf.sampleEditorGridOn);
- snap->callback(cb_enableSnap, (void*)this);
-
- /* TODO - redraw grid if != (off) */
-
- char buf[16];
- sprintf(buf, "%d", ch->begin / 2); // divided by 2 because stereo
- chanStart->value(buf);
- chanStart->type(FL_INT_INPUT);
- chanStart->callback(cb_setChanPos, this);
-
- /* inputs callback: fire when they lose focus or Enter is pressed. */
-
- chanStart->when(FL_WHEN_RELEASE | FL_WHEN_ENTER_KEY);
- chanEnd ->when(FL_WHEN_RELEASE | FL_WHEN_ENTER_KEY);
-
- sprintf(buf, "%d", ch->end / 2); // divided by 2 because stereo
- chanEnd->value(buf);
- chanEnd->type(FL_INT_INPUT);
- chanEnd->callback(cb_setChanPos, this);
-
- resetStartEnd->callback(cb_resetStartEnd, this);
-
- volume->callback(cb_setVolume, (void*)this);
- volume->value(ch->guiChannel->vol->value());
-
- float dB = 20*log10(ch->volume); // dB = 20*log_10(linear value)
- if (dB > -INFINITY) sprintf(buf, "%.2f", dB);
- else sprintf(buf, "-inf");
- volumeNum->value(buf);
- volumeNum->align(FL_ALIGN_RIGHT);
- volumeNum->callback(cb_setVolumeNum, (void*)this);
-
- boost->range(1.0f, 10.0f);
- boost->callback(cb_setBoost, (void*)this);
- if (ch->boost > 10.f)
- boost->value(10.0f);
- else
- boost->value(ch->boost);
- boost->when(FL_WHEN_CHANGED | FL_WHEN_RELEASE);
-
- float boost = 20*log10(ch->boost); // dB = 20*log_10(linear value)
- sprintf(buf, "%.2f", boost);
- boostNum->value(buf);
- boostNum->align(FL_ALIGN_RIGHT);
- boostNum->callback(cb_setBoostNum, (void*)this);
-
- normalize->callback(cb_normalize, (void*)this);
-
- pan->range(0.0f, 2.0f);
- pan->callback(cb_panning, (void*)this);
-
- pitch->range(0.01f, 4.0f);
- pitch->value(ch->pitch);
- pitch->callback(cb_setPitch, (void*)this);
- pitch->when(FL_WHEN_RELEASE);
-
- sprintf(buf, "%.4f", ch->pitch); // 4 digits
- pitchNum->value(buf);
- pitchNum->align(FL_ALIGN_RIGHT);
- pitchNum->callback(cb_setPitchNum, (void*)this);
- pitchNum->when(FL_WHEN_RELEASE | FL_WHEN_ENTER_KEY);
-
- pitchToBar->callback(cb_setPitchToBar, (void*)this);
- pitchToSong->callback(cb_setPitchToSong, (void*)this);
- pitchHalf->callback(cb_setPitchHalf, (void*)this);
- pitchDouble->callback(cb_setPitchDouble, (void*)this);
- pitchReset->callback(cb_resetPitch, (void*)this);
-
- reload->callback(cb_reload, (void*)this);
-
- zoomOut->callback(cb_zoomOut, (void*)this);
- zoomIn->callback(cb_zoomIn, (void*)this);
-
- /* logical samples (aka takes) cannot be reloaded. So far. */
-
- if (ch->wave->isLogical)
- reload->deactivate();
-
- if (ch->panRight < 1.0f) {
- char buf[8];
- sprintf(buf, "%d L", (int) std::abs((ch->panRight * 100.0f) - 100));
- pan->value(ch->panRight);
- panNum->value(buf);
- }
- else if (ch->panRight == 1.0f && ch->panLeft == 1.0f) {
- pan->value(1.0f);
- panNum->value("C");
- }
- else {
- char buf[8];
- sprintf(buf, "%d R", (int) std::abs((ch->panLeft * 100.0f) - 100));
- pan->value(2.0f - ch->panLeft);
- panNum->value(buf);
- }
-
- panNum->align(FL_ALIGN_RIGHT);
- panNum->readonly(1);
- panNum->cursor_color(FL_WHITE);
-
- gu_setFavicon(this);
- size_range(640, 480);
- resizable(waveTools);
-
- label(ch->wave->name.c_str());
-
- show();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-gdEditor::~gdEditor()
-{
- G_Conf.sampleEditorX = x();
- G_Conf.sampleEditorY = y();
- G_Conf.sampleEditorW = w();
- G_Conf.sampleEditorH = h();
- G_Conf.sampleEditorGridVal = grid->value();
- G_Conf.sampleEditorGridOn = snap->value();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gdEditor::cb_setChanPos (Fl_Widget *w, void *p) { ((gdEditor*)p)->__cb_setChanPos(); }
-void gdEditor::cb_resetStartEnd (Fl_Widget *w, void *p) { ((gdEditor*)p)->__cb_resetStartEnd(); }
-void gdEditor::cb_setVolume (Fl_Widget *w, void *p) { ((gdEditor*)p)->__cb_setVolume(); }
-void gdEditor::cb_setVolumeNum (Fl_Widget *w, void *p) { ((gdEditor*)p)->__cb_setVolumeNum(); }
-void gdEditor::cb_setBoost (Fl_Widget *w, void *p) { ((gdEditor*)p)->__cb_setBoost(); }
-void gdEditor::cb_setBoostNum (Fl_Widget *w, void *p) { ((gdEditor*)p)->__cb_setBoostNum(); }
-void gdEditor::cb_normalize (Fl_Widget *w, void *p) { ((gdEditor*)p)->__cb_normalize(); }
-void gdEditor::cb_panning (Fl_Widget *w, void *p) { ((gdEditor*)p)->__cb_panning(); }
-void gdEditor::cb_reload (Fl_Widget *w, void *p) { ((gdEditor*)p)->__cb_reload(); }
-void gdEditor::cb_setPitch (Fl_Widget *w, void *p) { ((gdEditor*)p)->__cb_setPitch(); }
-void gdEditor::cb_setPitchToBar (Fl_Widget *w, void *p) { ((gdEditor*)p)->__cb_setPitchToBar(); }
-void gdEditor::cb_setPitchToSong (Fl_Widget *w, void *p) { ((gdEditor*)p)->__cb_setPitchToSong(); }
-void gdEditor::cb_setPitchHalf (Fl_Widget *w, void *p) { ((gdEditor*)p)->__cb_setPitchHalf(); }
-void gdEditor::cb_setPitchDouble (Fl_Widget *w, void *p) { ((gdEditor*)p)->__cb_setPitchDouble(); }
-void gdEditor::cb_resetPitch (Fl_Widget *w, void *p) { ((gdEditor*)p)->__cb_resetPitch(); }
-void gdEditor::cb_setPitchNum (Fl_Widget *w, void *p) { ((gdEditor*)p)->__cb_setPitchNum(); }
-void gdEditor::cb_zoomIn (Fl_Widget *w, void *p) { ((gdEditor*)p)->__cb_zoomIn(); }
-void gdEditor::cb_zoomOut (Fl_Widget *w, void *p) { ((gdEditor*)p)->__cb_zoomOut(); }
-void gdEditor::cb_changeGrid (Fl_Widget *w, void *p) { ((gdEditor*)p)->__cb_changeGrid(); }
-void gdEditor::cb_enableSnap (Fl_Widget *w, void *p) { ((gdEditor*)p)->__cb_enableSnap(); }
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gdEditor::__cb_enableSnap()
-{
- waveTools->waveform->setSnap(!waveTools->waveform->getSnap());
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gdEditor::__cb_setPitchToBar()
-{
- glue_setPitch(this, ch, ch->end/(float)G_Mixer.framesPerBar, true);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gdEditor::__cb_setPitchToSong()
-{
- glue_setPitch(this, ch, ch->end/(float)G_Mixer.totalFrames, true);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gdEditor::__cb_resetPitch()
-{
- glue_setPitch(this, ch, 1.0f, true);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gdEditor::__cb_setChanPos()
-{
- glue_setBeginEndChannel(
- this,
- ch,
- atoi(chanStart->value())*2, // glue halves printed values
- atoi(chanEnd->value())*2,
- true
- );
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gdEditor::__cb_resetStartEnd()
-{
- glue_setBeginEndChannel(this, ch, 0, ch->wave->size, true);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gdEditor::__cb_setVolume()
-{
- glue_setVolEditor(this, ch, volume->value(), false);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gdEditor::__cb_setVolumeNum()
-{
- glue_setVolEditor(this, ch, atof(volumeNum->value()), true);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gdEditor::__cb_setBoost()
-{
- if (Fl::event() == FL_DRAG)
- glue_setBoost(this, ch, boost->value(), false);
- else if (Fl::event() == FL_RELEASE) {
- glue_setBoost(this, ch, boost->value(), false);
- waveTools->updateWaveform();
- }
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gdEditor::__cb_setBoostNum()
-{
- glue_setBoost(this, ch, atof(boostNum->value()), true);
- waveTools->updateWaveform();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gdEditor::__cb_normalize()
-{
- float val = wfx_normalizeSoft(ch->wave);
- glue_setBoost(this, ch, val, false); // we pretend that a fake user turns the dial (numeric=false)
- if (val < 0.0f)
- boost->value(0.0f);
- else
- if (val > 20.0f) // a dial > than it's max value goes crazy
- boost->value(10.0f);
- else
- boost->value(val);
- waveTools->updateWaveform();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gdEditor::__cb_panning()
-{
- glue_setPanning(this, ch, pan->value());
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gdEditor::__cb_reload()
-{
- if (!gdConfirmWin("Warning", "Reload sample: are you sure?"))
- return;
-
- /* no need for glue_loadChan, there's no gui to refresh */
-
- ch->load(ch->wave->pathfile.c_str(), G_Conf.samplerate, G_Conf.rsmpQuality);
-
- glue_setBoost(this, ch, DEFAULT_BOOST, true);
- glue_setPitch(this, ch, G_DEFAULT_PITCH, true);
- glue_setPanning(this, ch, 1.0f);
- pan->value(1.0f); // glue_setPanning doesn't do it
- pan->redraw(); // glue_setPanning doesn't do it
-
- waveTools->waveform->stretchToWindow();
- waveTools->updateWaveform();
-
- glue_setBeginEndChannel(this, ch, 0, ch->wave->size, true);
-
- redraw();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gdEditor::__cb_setPitch()
-{
- glue_setPitch(this, ch, pitch->value(), false);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gdEditor::__cb_setPitchNum()
-{
- glue_setPitch(this, ch, atof(pitchNum->value()), true);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gdEditor::__cb_setPitchHalf()
-{
- glue_setPitch(this, ch, pitch->value()/2, true);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gdEditor::__cb_setPitchDouble()
-{
- glue_setPitch(this, ch, pitch->value()*2, true);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gdEditor::__cb_zoomIn()
-{
- waveTools->waveform->setZoom(-1);
- waveTools->redraw();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gdEditor::__cb_zoomOut()
-{
- waveTools->waveform->setZoom(0);
- waveTools->redraw();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gdEditor::__cb_changeGrid()
-{
- waveTools->waveform->setGridLevel(atoi(grid->text()));
-}
+++ /dev/null
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * gd_editor
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
- *
- * This file is part of Giada - Your Hardcore Loopmachine.
- *
- * Giada - Your Hardcore Loopmachine is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General
- * Public License as published by the Free Software Foundation, either
- * version 3 of the License, or (at your option) any later version.
- *
- * Giada - Your Hardcore Loopmachine is distributed in the hope that it
- * will be useful, but WITHOUT ANY WARRANTY; without even the implied
- * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Giada - Your Hardcore Loopmachine. If not, see
- * <http://www.gnu.org/licenses/>.
- *
- * -------------------------------------------------------------------------- */
-
-
-#ifndef GD_EDITOR_H
-#define GD_EDITOR_H
-
-
-#include "../elems/ge_window.h"
-
-
-class gdEditor : public gWindow
-{
-private:
-
- static void cb_setChanPos (Fl_Widget *w, void *p);
- static void cb_resetStartEnd (Fl_Widget *w, void *p);
- static void cb_setVolume (Fl_Widget *w, void *p);
- static void cb_setVolumeNum (Fl_Widget *w, void *p);
- static void cb_setBoost (Fl_Widget *w, void *p);
- static void cb_setBoostNum (Fl_Widget *w, void *p);
- static void cb_normalize (Fl_Widget *w, void *p);
- static void cb_panning (Fl_Widget *w, void *p);
- static void cb_reload (Fl_Widget *w, void *p);
- static void cb_setPitch (Fl_Widget *w, void *p);
- static void cb_setPitchToBar (Fl_Widget *w, void *p);
- static void cb_setPitchToSong(Fl_Widget *w, void *p);
- static void cb_setPitchHalf (Fl_Widget *w, void *p);
- static void cb_setPitchDouble(Fl_Widget *w, void *p);
- static void cb_resetPitch (Fl_Widget *w, void *p);
- static void cb_setPitchNum (Fl_Widget *w, void *p);
- static void cb_zoomIn (Fl_Widget *w, void *p);
- static void cb_zoomOut (Fl_Widget *w, void *p);
- static void cb_changeGrid (Fl_Widget *w, void *p);
- static void cb_enableSnap (Fl_Widget *w, void *p);
- inline void __cb_setChanPos();
- inline void __cb_resetStartEnd();
- inline void __cb_setVolume();
- inline void __cb_setVolumeNum();
- inline void __cb_setBoost();
- inline void __cb_setBoostNum();
- inline void __cb_normalize();
- inline void __cb_panning();
- inline void __cb_reload();
- inline void __cb_setPitch();
- inline void __cb_setPitchToBar();
- inline void __cb_setPitchToSong();
- inline void __cb_setPitchHalf();
- inline void __cb_setPitchDouble();
- inline void __cb_resetPitch();
- inline void __cb_setPitchNum();
- inline void __cb_zoomIn();
- inline void __cb_zoomOut();
- inline void __cb_changeGrid();
- inline void __cb_enableSnap();
-
-public:
-
- gdEditor(class SampleChannel *ch);
- ~gdEditor();
-
- class gClick *zoomIn;
- class gClick *zoomOut;
- class gWaveTools *waveTools;
- class gInput *chanStart;
- class gInput *chanEnd;
- class gClick *resetStartEnd;
- class gDial *volume;
- class gInput *volumeNum;
- class gDial *boost;
- class gInput *boostNum;
- class gClick *normalize;
- class gDial *pan;
- class gInput *panNum;
- class gClick *reload;
- class gDial *pitch;
- class gInput *pitchNum;
- class gClick *pitchToBar;
- class gClick *pitchToSong;
- class gClick *pitchHalf;
- class gClick *pitchDouble;
- class gClick *pitchReset;
- class gClick *close;
- class gChoice *grid;
- class gCheck *snap;
-
- class SampleChannel *ch;
-};
-
-
-#endif
*
* Giada - Your Hardcore Loopmachine
*
- * gd_keyGrabber
- *
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
#include "../../core/sampleChannel.h"
#include "../../core/midiChannel.h"
#include "../../utils/log.h"
-#include "../elems/ge_mixed.h"
+#include "../elems/basics/box.h"
#include "../elems/mainWindow/keyboard/keyboard.h"
#include "../elems/mainWindow/keyboard/channel.h"
#include "../elems/mainWindow/keyboard/channelButton.h"
#include "gd_mainWindow.h"
-extern Conf G_Conf;
extern gdMainWindow *mainWin;
gdKeyGrabber::gdKeyGrabber(Channel *ch)
- : gWindow(300, 126, "Key configuration"), ch(ch)
+ : gdWindow(300, 126, "Key configuration"), ch(ch)
{
set_modal();
- text = new gBox(8, 8, 284, 80, "");
- clear = new gClick(w()-88, text->y()+text->h()+8, 80, 20, "Clear");
- cancel = new gClick(clear->x()-88, clear->y(), 80, 20, "Close");
+ text = new geBox(8, 8, 284, 80, "");
+ clear = new geButton(w()-88, text->y()+text->h()+8, 80, 20, "Clear");
+ cancel = new geButton(clear->x()-88, clear->y(), 80, 20, "Close");
end();
clear->callback(cb_clear, (void*)this);
*
* Giada - Your Hardcore Loopmachine
*
- * gd_keyGrabber
- *
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
#include <FL/Fl.H>
-#include "../elems/ge_window.h"
+#include "window.h"
+
+class Channel;
+class geBox;
+class geButton;
-class gdKeyGrabber : public gWindow
+
+class gdKeyGrabber : public gdWindow
{
private:
- class Channel *ch;
+ Channel *ch;
- class gBox *text;
- class gClick *clear;
- class gClick *cancel;
+ geBox *text;
+ geButton *clear;
+ geButton *cancel;
static void cb_clear (Fl_Widget *w, void *p);
static void cb_cancel(Fl_Widget *w, void *p);
inline void __cb_cancel();
void setButtonLabel(int key);
-
void updateText(int key);
public:
- gdKeyGrabber(class Channel *ch);
+ gdKeyGrabber(Channel *ch);
int handle(int e);
};
*
* Giada - Your Hardcore Loopmachine
*
- * gd_mainWindow
- *
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
#include "../../core/const.h"
#include "../../core/init.h"
#include "../../utils/gui.h"
-#include "../elems/ge_mixed.h"
#include "../elems/basics/boxtypes.h"
#include "../elems/mainWindow/mainIO.h"
#include "../elems/mainWindow/mainMenu.h"
gdMainWindow::gdMainWindow(int W, int H, const char *title, int argc, char **argv)
- : gWindow(W, H, title)
+ : gdWindow(W, H, title)
{
Fl::visible_focus(0);
/* -----------------------------------------------------------------------------
*
* Giada - Your Hardcore Loopmachine
- * gd_mainWindow
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
#define GD_MAINWINDOW_H
-#include "../elems/ge_window.h"
+#include "window.h"
-class gdMainWindow : public gWindow
+class Fl_Widget;
+class geKeyboard;
+class geBeatMeter;
+class geMainMenu;
+class geMainIO;
+class geMainTimer;
+class geMainTransport;
+
+
+class gdMainWindow : public gdWindow
{
private:
- static void cb_endprogram (class Fl_Widget *v, void *p);
+ static void cb_endprogram (Fl_Widget *v, void *p);
inline void __cb_endprogram();
public:
- class geKeyboard *keyboard;
- class geBeatMeter *beatMeter;
- class geMainMenu *mainMenu;
- class geMainIO *mainIO;
- class geMainTimer *mainTimer;
- class geMainTransport *mainTransport;
+ geKeyboard *keyboard;
+ geBeatMeter *beatMeter;
+ geMainMenu *mainMenu;
+ geMainIO *mainIO;
+ geMainTimer *mainTimer;
+ geMainTransport *mainTransport;
gdMainWindow(int w, int h, const char *title, int argc, char **argv);
};
*
* Giada - Your Hardcore Loopmachine
*
- * gd_pluginChooser
- *
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
#include "../../core/channel.h"
#include "../../core/conf.h"
#include "../../core/pluginHost.h"
-#include "../elems/ge_pluginBrowser.h"
-#include "../elems/ge_mixed.h"
+#include "../elems/pluginBrowser.h"
+#include "../elems/basics/button.h"
+#include "../elems/basics/choice.h"
+#include "../elems/basics/box.h"
#include "gd_pluginChooser.h"
-extern PluginHost G_PluginHost;
-extern Conf G_Conf;
+using namespace giada::m;
-gdPluginChooser::gdPluginChooser(int X, int Y, int W, int H, int stackType, class Channel *ch)
- : gWindow(X, Y, W, H, "Available plugins"), ch(ch), stackType(stackType)
+gdPluginChooser::gdPluginChooser(int X, int Y, int W, int H, int stackType, Channel *ch)
+ : gdWindow(X, Y, W, H, "Available plugins"), ch(ch), stackType(stackType)
{
/* top area */
Fl_Group *group_top = new Fl_Group(8, 8, w()-16, 20);
- sortMethod = new gChoice(group_top->x() + 45, group_top->y(), 100, 20, "Sort by");
- gBox *b1 = new gBox(sortMethod->x()+sortMethod->w(), group_top->y(), 100, 20); // spacer window border <-> menu
+ sortMethod = new geChoice(group_top->x() + 45, group_top->y(), 100, 20, "Sort by");
+ geBox *b1 = new geBox(sortMethod->x()+sortMethod->w(), group_top->y(), 100, 20); // spacer window border <-> menu
group_top->resizable(b1);
group_top->end();
/* ok/cancel buttons */
Fl_Group *group_btn = new Fl_Group(8, browser->y()+browser->h()+8, w()-16, h()-browser->h()-16);
- gBox *b2 = new gBox(8, browser->y()+browser->h(), 100, 20); // spacer window border <-> buttons
- addBtn = new gClick(w()-88, group_btn->y(), 80, 20, "Add");
- cancelBtn = new gClick(addBtn->x()-88, group_btn->y(), 80, 20, "Cancel");
+ geBox *b2 = new geBox(8, browser->y()+browser->h(), 100, 20); // spacer window border <-> buttons
+ addBtn = new geButton(w()-88, group_btn->y(), 80, 20, "Add");
+ cancelBtn = new geButton(addBtn->x()-88, group_btn->y(), 80, 20, "Cancel");
group_btn->resizable(b2);
group_btn->end();
sortMethod->add("Category");
sortMethod->add("Manufacturer");
sortMethod->callback(cb_sort, (void*) this);
- sortMethod->value(G_Conf.pluginSortMethod);
+ sortMethod->value(conf::pluginSortMethod);
addBtn->callback(cb_add, (void*) this);
addBtn->shortcut(FL_Enter);
gdPluginChooser::~gdPluginChooser()
{
- G_Conf.pluginChooserX = x();
- G_Conf.pluginChooserY = y();
- G_Conf.pluginChooserW = w();
- G_Conf.pluginChooserH = h();
- G_Conf.pluginSortMethod = sortMethod->value();
+ conf::pluginChooserX = x();
+ conf::pluginChooserY = y();
+ conf::pluginChooserW = w();
+ conf::pluginChooserH = h();
+ conf::pluginSortMethod = sortMethod->value();
}
void gdPluginChooser::__cb_sort()
{
- G_PluginHost.sortPlugins(sortMethod->value());
+ pluginHost::sortPlugins(sortMethod->value());
browser->refresh();
}
*
* Giada - Your Hardcore Loopmachine
*
- * gd_pluginChooser
- *
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
#ifdef WITH_VST
-#ifndef __GD_PLUGIN_CHOOSER_H__
-#define __GD_PLUGIN_CHOOSER_H__
+#ifndef GD_PLUGIN_CHOOSER_H
+#define GD_PLUGIN_CHOOSER_H
+
#include <FL/Fl.H>
#include <FL/Fl_Scroll.H>
-#include "../elems/ge_window.h"
+#include "window.h"
+
+class Channel;
+class geChoice;
+class geButton;
+class geButton;
+class gePluginBrowser;
-class gdPluginChooser : public gWindow {
+class gdPluginChooser : public gdWindow
+{
private:
- class Channel *ch; // ch == NULL ? masterOut
- int stackType;
+ Channel *ch; // ch == nullptr ? masterOut
+ int stackType;
- class gChoice *sortMethod;
- class gClick *addBtn;
- class gClick *cancelBtn;
- class gePluginBrowser *browser;
+ geChoice *sortMethod;
+ geButton *addBtn;
+ geButton *cancelBtn;
+ gePluginBrowser *browser;
static void cb_close(Fl_Widget *w, void *p);
static void cb_add (Fl_Widget *w, void *p);
public:
- gdPluginChooser(int x, int y, int w, int h, int stackType, class Channel *ch=NULL);
+ gdPluginChooser(int x, int y, int w, int h, int stackType, Channel *ch=nullptr);
~gdPluginChooser();
};
*
* Giada - Your Hardcore Loopmachine
*
- * gd_pluginList
- *
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
#include "../../glue/plugin.h"
#include "../../utils/log.h"
#include "../../utils/string.h"
-#include "../elems/ge_mixed.h"
#include "../elems/basics/boxtypes.h"
+#include "../elems/basics/idButton.h"
+#include "../elems/basics/statusButton.h"
+#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 "gd_browser.h"
#include "gd_mainWindow.h"
-extern Conf G_Conf;
-extern Mixer G_Mixer;
-extern PluginHost G_PluginHost;
extern gdMainWindow *G_MainWin;
using std::string;
+using namespace giada::m;
gdPluginList::gdPluginList(int stackType, Channel *ch)
- : gWindow(468, 204), ch(ch), stackType(stackType)
+ : gdWindow(468, 204), ch(ch), stackType(stackType)
{
- if (G_Conf.pluginListX)
- resize(G_Conf.pluginListX, G_Conf.pluginListY, w(), h());
+ if (conf::pluginListX)
+ resize(conf::pluginListX, conf::pluginListY, w(), h());
list = new Fl_Scroll(8, 8, 476, 188);
list->type(Fl_Scroll::VERTICAL);
/* TODO - awful stuff... we should subclass into gdPluginListChannel and
gdPluginListMaster */
- if (stackType == PluginHost::MASTER_OUT)
+ if (stackType == pluginHost::MASTER_OUT)
label("Master Out Plugins");
else
- if (stackType == PluginHost::MASTER_IN)
+ if (stackType == pluginHost::MASTER_IN)
label("Master In Plugins");
else {
string l = "Channel " + gu_itoa(ch->index+1) + " Plugins";
gdPluginList::~gdPluginList()
{
- G_Conf.pluginListX = x();
- G_Conf.pluginListY = y();
+ conf::pluginListX = x();
+ conf::pluginListY = y();
}
* by calling the parent (pluginList) and telling it to delete its
* subwindow (i.e. gdBrowser). */
- gWindow *child = (gWindow*) v;
- if (child->getParent() != NULL)
+ gdWindow *child = (gdWindow*) v;
+ if (child->getParent() != nullptr)
(child->getParent())->delSubWindow(child);
/* finally refresh plugin list: void *p is a pointer to gdPluginList.
void gdPluginList::__cb_addPlugin()
{
- /* the usual callback that gWindow adds to each subwindow in this case
+ /* the usual callback that gdWindow adds to each subwindow in this case
* is not enough, because when we close the browser the plugin list
* 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(G_Conf.pluginChooserX,
- G_Conf.pluginChooserY, G_Conf.pluginChooserW, G_Conf.pluginChooserH,
+ 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
list->scroll_to(0, 0);
/* add new buttons, as many as the plugin in pluginHost::stack + 1,
- * the 'add new' button. Warning: if ch == NULL we are working with
+ * the 'add new' button. Warning: if ch == nullptr we are working with
* master in/master out stacks. */
- int numPlugins = G_PluginHost.countPlugins(stackType, ch);
+ int numPlugins = pluginHost::countPlugins(stackType, ch);
int i = 0;
while (i<numPlugins) {
- Plugin *pPlugin = G_PluginHost.getPluginByIndex(i, stackType, ch);
+ Plugin *pPlugin = pluginHost::getPluginByIndex(i, stackType, ch);
gdPlugin *gdp = new gdPlugin(this, pPlugin, list->x(), list->y()-list->yposition()+(i*24), 800);
list->add(gdp);
i++;
}
int addPlugY = numPlugins == 0 ? 90 : list->y()-list->yposition()+(i*24);
- addPlugin = new gClick(8, addPlugY, 452, 20, "-- add new plugin --");
+ addPlugin = new geButton(8, addPlugY, 452, 20, "-- add new plugin --");
addPlugin->callback(cb_addPlugin, (void*)this);
list->add(addPlugin);
/* TODO - awful stuff... we should subclass into gdPluginListChannel and
gdPluginListMaster */
- if (stackType == PluginHost::MASTER_OUT) {
+ if (stackType == pluginHost::MASTER_OUT) {
G_MainWin->mainIO->setMasterFxOutFull(
- G_PluginHost.countPlugins(stackType, ch) > 0);
+ pluginHost::countPlugins(stackType, ch) > 0);
}
else
- if (stackType == PluginHost::MASTER_IN) {
+ if (stackType == pluginHost::MASTER_IN) {
G_MainWin->mainIO->setMasterFxInFull(
- G_PluginHost.countPlugins(stackType, ch) > 0);
+ pluginHost::countPlugins(stackType, ch) > 0);
}
else {
- ch->guiChannel->fx->full = G_PluginHost.countPlugins(stackType, ch) > 0;
+ ch->guiChannel->fx->status = pluginHost::countPlugins(stackType, ch) > 0;
ch->guiChannel->fx->redraw();
}
}
: Fl_Group(X, Y, W, 20), pParent(gdp), pPlugin (p)
{
begin();
- button = new gButton(8, y(), 220, 20);
- program = new gChoice(button->x()+button->w()+4, y(), 132, 20);
- bypass = new gButton(program->x()+program->w()+4, y(), 20, 20);
- shiftUp = new gButton(bypass->x()+bypass->w()+4, y(), 20, 20, "", fxShiftUpOff_xpm, fxShiftUpOn_xpm);
- shiftDown = new gButton(shiftUp->x()+shiftUp->w()+4, y(), 20, 20, "", fxShiftDownOff_xpm, fxShiftDownOn_xpm);
- remove = new gButton(shiftDown->x()+shiftDown->w()+4, y(), 20, 20, "", fxRemoveOff_xpm, fxRemoveOn_xpm);
+ button = new geIdButton(8, y(), 220, 20);
+ program = new geChoice(button->x()+button->w()+4, y(), 132, 20);
+ bypass = new geIdButton(program->x()+program->w()+4, y(), 20, 20);
+ shiftUp = new geIdButton(bypass->x()+bypass->w()+4, y(), 20, 20, "", fxShiftUpOff_xpm, fxShiftUpOn_xpm);
+ shiftDown = new geIdButton(shiftUp->x()+shiftUp->w()+4, y(), 20, 20, "", fxShiftDownOff_xpm, fxShiftDownOn_xpm);
+ remove = new geIdButton(shiftDown->x()+shiftDown->w()+4, y(), 20, 20, "", fxRemoveOff_xpm, fxRemoveOn_xpm);
end();
button->copy_label(pPlugin->getName().c_str());
{
/*nothing to do if there's only one plugin */
- if (G_PluginHost.countPlugins(pParent->stackType, pParent->ch) == 1)
+ if (pluginHost::countPlugins(pParent->stackType, pParent->ch) == 1)
return;
- int pluginIndex = G_PluginHost.getPluginIndex(pPlugin->getId(),
+ int pluginIndex = pluginHost::getPluginIndex(pPlugin->getId(),
pParent->stackType, pParent->ch);
if (pluginIndex == 0) // first of the stack, do nothing
{
/*nothing to do if there's only one plugin */
- if (G_PluginHost.countPlugins(pParent->stackType, pParent->ch) == 1)
+ if (pluginHost::countPlugins(pParent->stackType, pParent->ch) == 1)
return;
- unsigned pluginIndex = G_PluginHost.getPluginIndex(pPlugin->getId(), pParent->stackType, pParent->ch);
- unsigned stackSize = (G_PluginHost.getStack(pParent->stackType, pParent->ch))->size();
+ unsigned pluginIndex = pluginHost::getPluginIndex(pPlugin->getId(), pParent->stackType, pParent->ch);
+ unsigned stackSize = (pluginHost::getStack(pParent->stackType, pParent->ch))->size();
if (pluginIndex == stackSize-1) // last one in the stack, do nothing
return;
/* the new pluginWindow has id = id_plugin + 1, because id=0 is reserved
* for the parent window 'add plugin'. */
- gWindow *w;
+ gdWindow *w;
if (pPlugin->hasEditor()) {
if (pPlugin->isEditorOpen()) {
gu_log("[gdPlugin::__cb_openPluginWindow] plugin has editor but it's already visible\n");
*
* Giada - Your Hardcore Loopmachine
*
- * gd_pluginList
- *
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
#ifdef WITH_VST
-#ifndef __GD_PLUGINLIST_H__
-#define __GD_PLUGINLIST_H__
+#ifndef GD_PLUGINLIST_H
+#define GD_PLUGINLIST_H
#include <FL/Fl.H>
#include <FL/Fl_Scroll.H>
-#include "../elems/ge_window.h"
+#include "window.h"
+
+
+class Plugin;
+class Channel;
+class geButton;
+class gdPluginList;
+class geIdButton;
+class geChoice;
-class gdPluginList : public gWindow
+class gdPluginList : public gdWindow
{
private:
- class gClick *addPlugin;
- Fl_Scroll *list;
+ geButton *addPlugin;
+ Fl_Scroll *list;
static void cb_addPlugin (Fl_Widget *v, void *p);
inline void __cb_addPlugin();
public:
- class Channel *ch; // ch == NULL ? masterOut
- int stackType;
+ Channel *ch; // ch == nullptr ? masterOut
+ int stackType;
- gdPluginList(int stackType, class Channel *ch=NULL);
+ gdPluginList(int stackType, Channel *ch=nullptr);
~gdPluginList();
/* special callback, passed to browser. When closed (i.e. plugin
{
private:
- class gdPluginList *pParent;
- class Plugin *pPlugin;
+ gdPluginList *pParent;
+ Plugin *pPlugin;
static void cb_removePlugin (Fl_Widget *v, void *p);
static void cb_openPluginWindow (Fl_Widget *v, void *p);
public:
- class gButton *button;
- class gChoice *program;
- class gButton *bypass;
- class gButton *shiftUp;
- class gButton *shiftDown;
- class gButton *remove;
+ geIdButton *button;
+ geChoice *program;
+ geIdButton *bypass;
+ geIdButton *shiftUp;
+ geIdButton *shiftDown;
+ geIdButton *remove;
- gdPlugin(gdPluginList *gdp, class Plugin *p, int x, int y, int w);
+ gdPlugin(gdPluginList *gdp, Plugin *p, int x, int y, int w);
};
#endif
*
* Giada - Your Hardcore Loopmachine
*
- * gd_pluginWindow
- *
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
#include <FL/Fl_Scroll.H>
#include "../../utils/gui.h"
#include "../../core/plugin.h"
-#include "../elems/ge_mixed.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"
{
begin();
- label = new gBox(x(), y(), 60, 20);
+ 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 gSlider(label->x()+label->w()+8, y(), W-200, 20);
+ 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 gBox(slider->x()+slider->w()+8, y(), 100, 20);
+ 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();
gdPluginWindow::gdPluginWindow(Plugin *p)
- : gWindow(400, 156), pPlugin(p) // 350
+ : gdWindow(400, 156), pPlugin(p) // 350
{
set_non_modal();
- gLiquidScroll *list = new gLiquidScroll(8, 8, w()-16, h()-16);
+ geLiquidScroll *list = new geLiquidScroll(8, 8, w()-16, h()-16);
list->type(Fl_Scroll::VERTICAL_ALWAYS);
list->begin();
*
* Giada - Your Hardcore Loopmachine
*
- * gd_pluginWindow
- *
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
#ifdef WITH_VST
-#ifndef __GD_PLUGIN_WINDOW_H__
-#define __GD_PLUGIN_WINDOW_H__
+#ifndef GD_PLUGIN_WINDOW_H
+#define GD_PLUGIN_WINDOW_H
#include <FL/Fl.H>
#include <FL/Fl_Window.H>
-#include "../elems/ge_window.h"
+#include "window.h"
+
+class Plugin;
+class geBox;
+class geSlider;
-class gdPluginWindow : public gWindow
+
+class gdPluginWindow : public gdWindow
{
private:
- class Plugin *pPlugin;
+
+ Plugin *pPlugin;
public:
- int id;
+
+ int id;
gdPluginWindow(Plugin *pPlugin);
};
class Parameter : public Fl_Group
{
private:
- int paramIndex;
- class Plugin *pPlugin;
+
+ int paramIndex;
+ Plugin *pPlugin;
static void cb_setValue(Fl_Widget *v, void *p);
inline void __cb_setValue();
void updateValue();
public:
- class gBox *label;
- class gSlider *slider;
- class gBox *value;
- Parameter(int paramIndex, class Plugin *p, int x, int y, int w);
+ geBox *label;
+ geSlider *slider;
+ geBox *value;
+
+ Parameter(int paramIndex, Plugin *p, int x, int y, int w);
};
*
* Giada - Your Hardcore Loopmachine
*
- * gd_pluginWindowGUI
- *
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
#ifdef WITH_VST
+#include <FL/x.H>
#include "../../utils/log.h"
#include "../../utils/gui.h"
#include "../../core/pluginHost.h"
#include "../../core/plugin.h"
#include "../../core/const.h"
-#include "../elems/ge_mixed.h"
#include "gd_pluginWindowGUI.h"
#ifdef __APPLE__
#endif
-extern PluginHost G_PluginHost;
+using namespace giada::m;
gdPluginWindowGUI::gdPluginWindowGUI(Plugin *pPlugin)
- : gWindow(450, 300), pPlugin(pPlugin)
+ : gdWindow(450, 300), pPlugin(pPlugin)
{
show();
void gdPluginWindowGUI::__cb_refresh()
{
- //gu_log("[gdPluginWindowGUI::__cb_refresh] refresh!\n");
- G_PluginHost.runDispatchLoop();
+ pluginHost::runDispatchLoop();
Fl::repeat_timeout(GUI_PLUGIN_RATE, cb_refresh, (void*) this);
}
*
* ---------------------------------------------------------------------
*
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
#ifdef WITH_VST
-#ifndef __GD_PLUGIN_WINDOW_GUI_H__
-#define __GD_PLUGIN_WINDOW_GUI_H__
+#ifndef GD_PLUGIN_WINDOW_GUI_H
+#define GD_PLUGIN_WINDOW_GUI_H
#include <FL/Fl.H>
#include <FL/Fl_Window.H>
-#include "../elems/ge_window.h"
+#include "window.h"
#if defined(__APPLE__)
#include <Carbon/Carbon.h>
#endif
-class gdPluginWindowGUI : public gWindow
+class Plugin;
+
+
+class gdPluginWindowGUI : public gdWindow
{
private:
- class Plugin *pPlugin;
+ Plugin *pPlugin;
static void cb_close (Fl_Widget *v, void *p);
static void cb_refresh (void *data);
};
-/* -------------------------------------------------------------------------- */
-
-#if 0
-#if defined(__APPLE__)
-
-class gdPluginWindowGUImac : public gWindow
-{
-private:
-
- static pascal OSStatus windowHandler(EventHandlerCallRef ehc, EventRef e, void *data);
- inline pascal OSStatus __wh(EventHandlerCallRef ehc, EventRef e);
-
- class Plugin *pPlugin;
- WindowRef carbonWindow;
- bool open;
-
-public:
-
- gdPluginWindowGUImac(Plugin *pPlugin);
- ~gdPluginWindowGUImac();
-};
-
-#endif
-#endif
-
#endif // include guard
#endif // #ifdef WITH_VST
-/* ---------------------------------------------------------------------
+/* -----------------------------------------------------------------------------
*
* Giada - Your Hardcore Loopmachine
*
- * gd_warnings
+ * -----------------------------------------------------------------------------
*
- * ---------------------------------------------------------------------
- *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
* along with Giada - Your Hardcore Loopmachine. If not, see
* <http://www.gnu.org/licenses/>.
*
- * ------------------------------------------------------------------ */
+ * -------------------------------------------------------------------------- */
+#include <FL/Fl.H>
+#include <FL/Fl_Window.H>
+#include "../../utils/gui.h"
#include "../../core/const.h"
+#include "../elems/basics/button.h"
+#include "../elems/basics/box.h"
+#include "window.h"
#include "gd_warnings.h"
-void gdAlert(const char *c) {
+void gdAlert(const char *c)
+{
Fl_Window *modal = new Fl_Window(
(Fl::w() / 2) - 150,
(Fl::h() / 2) - 47,
300, 90, "Alert");
modal->set_modal();
modal->begin();
- gBox *box = new gBox(10, 10, 280, 40, c);
- gClick *b = new gClick(210, 60, 80, 20, "Close");
+ geBox *box = new geBox(10, 10, 280, 40, c);
+ geButton *b = new geButton(210, 60, 80, 20, "Close");
modal->end();
box->labelsize(GUI_FONT_SIZE_BASE);
b->callback(__cb_window_closer, (void *)modal);
}
-int gdConfirmWin(const char *title, const char *msg) {
+int gdConfirmWin(const char *title, const char *msg)
+{
Fl_Window *win = new Fl_Window(
(Fl::w() / 2) - 150,
(Fl::h() / 2) - 47,
300, 90, title);
win->set_modal();
win->begin();
- new gBox(10, 10, 280, 40, msg);
- gClick *ok = new gClick(212, 62, 80, 20, "Ok");
- gClick *ko = new gClick(124, 62, 80, 20, "Cancel");
+ new geBox(10, 10, 280, 40, msg);
+ geButton *ok = new geButton(212, 62, 80, 20, "Ok");
+ geButton *ko = new geButton(124, 62, 80, 20, "Cancel");
win->end();
ok->shortcut(FL_Enter);
gu_setFavicon(win);
-/* ---------------------------------------------------------------------
+/* -----------------------------------------------------------------------------
*
* Giada - Your Hardcore Loopmachine
*
- * gd_warnings
+ * -----------------------------------------------------------------------------
*
- * ---------------------------------------------------------------------
- *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
* along with Giada - Your Hardcore Loopmachine. If not, see
* <http://www.gnu.org/licenses/>.
*
- * ------------------------------------------------------------------ */
+ * -------------------------------------------------------------------------- */
+
#ifndef GD_WARNINGS_H
#define GD_WARNINGS_H
-#include <FL/Fl.H>
-#include <FL/Fl_Window.H>
-#include <FL/Fl_Box.H>
-#include "../elems/ge_mixed.h"
-#include "../../utils/gui.h"
-
void gdAlert(const char *c);
-
int gdConfirmWin(const char *title, const char *msg);
+
#endif
*
* Giada - Your Hardcore Loopmachine
*
- * midiInputBase
- *
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
#include "midiInputBase.h"
-extern KernelMidi G_KernelMidi;
-
-
using std::string;
+using namespace giada::m;
gdMidiInputBase::gdMidiInputBase(int x, int y, int w, int h, const char *title)
- : gWindow(x, y, w, h, title)
+ : gdWindow(x, y, w, h, title)
{
}
gdMidiInputBase::~gdMidiInputBase()
{
- G_KernelMidi.stopMidiLearn();
+ kernelMidi::stopMidiLearn();
}
void gdMidiInputBase::stopMidiLearn(geMidiLearner *learner)
{
- G_KernelMidi.stopMidiLearn();
+ kernelMidi::stopMidiLearn();
learner->updateValue();
}
*
* Giada - Your Hardcore Loopmachine
*
- * midiInputBase
- *
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
#define GD_MIDI_INPUT_BASE_H
-#include "../../elems/ge_window.h"
+#include "../window.h"
+
+
+class geButton;
+class geMidiLearner;
-class gdMidiInputBase : public gWindow
+class gdMidiInputBase : public gdWindow
{
protected:
- class gClick *ok;
+ geButton *ok;
- void stopMidiLearn(class geMidiLearner *l);
+ void stopMidiLearn(geMidiLearner *l);
/* cb_learn
* callback attached to kernelMidi to learn various actions. */
*
* Giada - Your Hardcore Loopmachine
*
- * midiInputChannel
- *
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
#include "../../../core/plugin.h"
#endif
#include "../../../utils/string.h"
-#include "../../elems/ge_mixed.h"
#include "../../elems/midiLearner.h"
#include "../../elems/basics/scroll.h"
+#include "../../elems/basics/box.h"
+#include "../../elems/basics/button.h"
+#include "../../elems/basics/check.h"
#include "midiInputChannel.h"
-#ifdef WITH_VST
-extern PluginHost G_PluginHost;
-#endif
-extern Conf G_Conf;
-
-
using std::string;
+using std::vector;
+using namespace giada::m;
gdMidiInputChannel::gdMidiInputChannel(Channel *ch)
- : gdMidiInputBase(G_Conf.midiInputX, G_Conf.midiInputY, G_Conf.midiInputW,
- G_Conf.midiInputH, "MIDI Input Setup"),
+ : gdMidiInputBase(conf::midiInputX, conf::midiInputY, conf::midiInputW,
+ conf::midiInputH, "MIDI Input Setup"),
ch(ch)
{
string title = "MIDI Input Setup (channel " + gu_itoa(ch->index+1) + ")";
label(title.c_str());
size_range(G_DEFAULT_MIDI_INPUT_UI_W, G_DEFAULT_MIDI_INPUT_UI_H);
- enable = new gCheck(8, 8, 120, 20, "enable MIDI input");
+ enable = new geCheck(8, 8, 120, 20, "enable MIDI input");
- container = new geScroll(8, enable->y()+enable->h()+4, w()-16, h()-70);
+ container = new geScroll(8, enable->y()+enable->h()+4, w()-16, h()-68);
container->begin();
addChannelLearners();
container->end();
- ok = new gButton(w()-88, container->y()+container->h()+8, 80, 20, "Close");
- ok->callback(cb_close, (void*)this);
+ Fl_Group *groupButtons = new Fl_Group(8, container->y()+container->h()+8, container->w(), 20);
+ groupButtons->begin();
+
+ geBox *spacer = new geBox(groupButtons->x(), groupButtons->y(), 100, 20); // spacer window border <-> buttons
+ ok = new geButton(w()-88, groupButtons->y(), 80, 20, "Close");
+
+ groupButtons->resizable(spacer);
+ groupButtons->end();
+
+ ok->callback(cb_close, (void*)this);
- enable->value(ch->midiIn);
+ enable->value(ch->midiIn);
enable->callback(cb_enable, (void*)this);
resizable(container);
gdMidiInputChannel::~gdMidiInputChannel()
{
- G_Conf.midiInputX = x();
- G_Conf.midiInputY = y();
- G_Conf.midiInputW = w();
- G_Conf.midiInputH = h();
+ conf::midiInputX = x();
+ conf::midiInputY = y();
+ conf::midiInputW = w();
+ conf::midiInputH = h();
}
pack->spacing(4);
pack->begin();
- gBox *header = new gBox(0, 0, LEARNER_WIDTH, 20, "channel");
+ geBox *header = new geBox(0, 0, LEARNER_WIDTH, 20, "channel");
header->box(FL_BORDER_BOX);
new geMidiLearner(0, 0, LEARNER_WIDTH, "key press", cb_learn, &ch->midiInKeyPress);
new geMidiLearner(0, 0, LEARNER_WIDTH, "key release", cb_learn, &ch->midiInKeyRel);
void gdMidiInputChannel::addPluginLearners()
{
- vector <Plugin *> *plugins = G_PluginHost.getStack(PluginHost::CHANNEL, ch);
+ vector <Plugin *> *plugins = pluginHost::getStack(pluginHost::CHANNEL, ch);
for (unsigned i=0; i<plugins->size(); i++) {
Fl_Pack *pack = new Fl_Pack(container->x() + ((i + 1) * (LEARNER_WIDTH + 8)),
Plugin *plugin = plugins->at(i);
- gBox *header = new gBox(0, 0, LEARNER_WIDTH, 20, plugin->getName().c_str());
+ geBox *header = new geBox(0, 0, LEARNER_WIDTH, 20, plugin->getName().c_str());
header->box(FL_BORDER_BOX);
for (int k=0; k<plugin->getNumParameters(); k++)
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
class Channel *ch;
class geScroll *container;
- class gCheck *enable;
+ class geCheck *enable;
//gVector <geMidiLearner *> items; // plugins parameters
*
* Giada - Your Hardcore Loopmachine
*
- * midiInputMaster
- *
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
#include "../../../utils/gui.h"
#include "../../../core/conf.h"
-#include "../../elems/ge_mixed.h"
#include "../../elems/midiLearner.h"
+#include "../../elems/basics/button.h"
#include "midiInputMaster.h"
-extern Conf G_Conf;
+using namespace giada::m;
gdMidiInputMaster::gdMidiInputMaster()
{
set_modal();
- new geMidiLearner(8, 8, w()-16, "rewind", &cb_learn, &G_Conf.midiInRewind);
- new geMidiLearner(8, 32, w()-16, "play/stop", &cb_learn, &G_Conf.midiInStartStop);
- new geMidiLearner(8, 56, w()-16, "action recording", &cb_learn, &G_Conf.midiInActionRec);
- new geMidiLearner(8, 80, w()-16, "input recording", &cb_learn, &G_Conf.midiInInputRec);
- new geMidiLearner(8, 104, w()-16, "metronome", &cb_learn, &G_Conf.midiInMetronome);
- new geMidiLearner(8, 128, w()-16, "input volume", &cb_learn, &G_Conf.midiInVolumeIn);
- new geMidiLearner(8, 152, w()-16, "output volume", &cb_learn, &G_Conf.midiInVolumeOut);
- new geMidiLearner(8, 176, w()-16, "sequencer ×2", &cb_learn, &G_Conf.midiInBeatDouble);
- new geMidiLearner(8, 200, w()-16, "sequencer ÷2", &cb_learn, &G_Conf.midiInBeatHalf);
- ok = new gButton(w()-88, 228, 80, 20, "Close");
+ new geMidiLearner(8, 8, w()-16, "rewind", &cb_learn, &conf::midiInRewind);
+ new geMidiLearner(8, 32, w()-16, "play/stop", &cb_learn, &conf::midiInStartStop);
+ new geMidiLearner(8, 56, w()-16, "action recording", &cb_learn, &conf::midiInActionRec);
+ new geMidiLearner(8, 80, w()-16, "input recording", &cb_learn, &conf::midiInInputRec);
+ new geMidiLearner(8, 104, w()-16, "metronome", &cb_learn, &conf::midiInMetronome);
+ new geMidiLearner(8, 128, w()-16, "input volume", &cb_learn, &conf::midiInVolumeIn);
+ new geMidiLearner(8, 152, w()-16, "output volume", &cb_learn, &conf::midiInVolumeOut);
+ new geMidiLearner(8, 176, w()-16, "sequencer ×2", &cb_learn, &conf::midiInBeatDouble);
+ new geMidiLearner(8, 200, w()-16, "sequencer ÷2", &cb_learn, &conf::midiInBeatHalf);
+ ok = new geButton(w()-88, 228, 80, 20, "Close");
ok->callback(cb_close, (void*)this);
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* Giada - Your Hardcore Loopmachine
*
- * gd_midiOutput
- *
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
#include "midiOutputBase.h"
-extern KernelMidi G_KernelMidi;
+using namespace giada::m;
gdMidiOutputBase::gdMidiOutputBase(int w, int h)
- : gWindow(w, h, "Midi Output Setup")
+ : gdWindow(w, h, "Midi Output Setup")
{
}
void gdMidiOutputBase::stopMidiLearn(geMidiLearner *learner)
{
- G_KernelMidi.stopMidiLearn();
+ kernelMidi::stopMidiLearn();
learner->updateValue();
}
*
* Giada - Your Hardcore Loopmachine
*
- * gd_midiOutput
- *
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
#include <FL/Fl.H>
-#include "../../elems/ge_window.h"
+#include "../window.h"
+
+
+class geButton;
+class geCheck;
+class geMidiLearner;
/* There's no such thing as a gdMidiOutputMaster vs gdMidiOutputChannel. MIDI
/* TODO - gdMidiOutput is almost the same thing of gdMidiInput. Create another
parent class gdMidiIO to inherit from */
-class gdMidiOutputBase : public gWindow
+class gdMidiOutputBase : public gdWindow
{
protected:
- class gClick *close;
- class gCheck *enableLightning;
+ geButton *close;
+ geCheck *enableLightning;
- void stopMidiLearn(class geMidiLearner *l);
+ void stopMidiLearn(geMidiLearner *l);
/* cb_learn
* callback attached to kernelMidi to learn various actions. */
static void cb_learn (uint32_t msg, void *data);
- inline void __cb_learn(uint32_t *param, uint32_t msg, class geMidiLearner *l);
+ inline void __cb_learn(uint32_t *param, uint32_t msg, geMidiLearner *l);
/* cb_close
close current window. */
*
* Giada - Your Hardcore Loopmachine
*
- * midiOutputMidiCh
- *
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
#include "../../../core/midiChannel.h"
#include "../../../utils/gui.h"
-#include "../../elems/ge_mixed.h"
#include "../../elems/midiLearner.h"
+#include "../../elems/basics/button.h"
+#include "../../elems/basics/check.h"
+#include "../../elems/basics/choice.h"
#include "../../elems/mainWindow/keyboard/channel.h"
#include "midiOutputMidiCh.h"
setTitle(ch->index+1);
begin();
- enableOut = new gCheck(x()+8, y()+8, 150, 20, "Enable MIDI output");
- chanListOut = new gChoice(w()-108, y()+8, 100, 20);
+ enableOut = new geCheck(x()+8, y()+8, 150, 20, "Enable MIDI output");
+ chanListOut = new geChoice(w()-108, y()+8, 100, 20);
- enableLightning = new gCheck(x()+8, chanListOut->y()+chanListOut->h()+8, 120, 20, "Enable MIDI lightning output");
+ enableLightning = new geCheck(x()+8, chanListOut->y()+chanListOut->h()+8, 120, 20, "Enable MIDI lightning output");
new geMidiLearner(x()+8, enableLightning->y()+enableLightning->h()+8, w()-16, "playing", cb_learn, &ch->midiOutLplaying);
new geMidiLearner(x()+8, enableLightning->y()+enableLightning->h()+32, w()-16, "mute", cb_learn, &ch->midiOutLmute);
new geMidiLearner(x()+8, enableLightning->y()+enableLightning->h()+56, w()-16, "solo", cb_learn, &ch->midiOutLsolo);
- close = new gButton(w()-88, enableLightning->y()+enableLightning->h()+84, 80, 20, "Close");
+ close = new geButton(w()-88, enableLightning->y()+enableLightning->h()+84, 80, 20, "Close");
end();
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
static void cb_close (Fl_Widget *w, void *p);
inline void __cb_close();
- class gCheck *enableOut;
- class gChoice *chanListOut;
+ class geCheck *enableOut;
+ class geChoice *chanListOut;
class MidiChannel *ch;
*
* Giada - Your Hardcore Loopmachine
*
- * midiOutputSampleCh
- *
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
#include "../../../core/sampleChannel.h"
#include "../../../utils/gui.h"
-#include "../../elems/ge_mixed.h"
#include "../../elems/midiLearner.h"
+#include "../../elems/basics/button.h"
+#include "../../elems/basics/check.h"
#include "midiOutputSampleCh.h"
{
setTitle(ch->index+1);
- enableLightning = new gCheck(8, 8, 120, 20, "Enable MIDI lightning output");
+ enableLightning = new geCheck(8, 8, 120, 20, "Enable MIDI lightning output");
new geMidiLearner(8, enableLightning->y()+enableLightning->h()+8, w()-16, "playing", cb_learn, &ch->midiOutLplaying);
new geMidiLearner(8, enableLightning->y()+enableLightning->h()+32, w()-16, "mute", cb_learn, &ch->midiOutLmute);
new geMidiLearner(8, enableLightning->y()+enableLightning->h()+56, w()-16, "solo", cb_learn, &ch->midiOutLsolo);
- close = new gButton(w()-88, enableLightning->y()+enableLightning->h()+84, 80, 20, "Close");
+ close = new geButton(w()-88, enableLightning->y()+enableLightning->h()+84, 80, 20, "Close");
close->callback(cb_close, (void*)this);
enableLightning->value(ch->midiOutL);
*
* Giada - Your Hardcore Loopmachine
*
- * midiOutputSampleCh
- *
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
#include "midiOutputBase.h"
+class SampleChannel;
+
+
class gdMidiOutputSampleCh : public gdMidiOutputBase
{
private:
- class SampleChannel *ch;
+ SampleChannel *ch;
/* __cb_close
override parent method, we need to do more stuff on close. */
public:
- gdMidiOutputSampleCh(class SampleChannel *ch);
+ gdMidiOutputSampleCh(SampleChannel *ch);
};
#endif
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * 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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#include <cmath>
+#include <FL/Fl.H>
+#include <FL/Fl_Group.H>
+#include <FL/fl_draw.H>
+#include "../../glue/channel.h"
+#include "../../core/waveFx.h"
+#include "../../core/conf.h"
+#include "../../core/const.h"
+#include "../../core/graphics.h"
+#include "../../core/sampleChannel.h"
+#include "../../core/mixer.h"
+#include "../../core/wave.h"
+#include "../../core/clock.h"
+#include "../../utils/gui.h"
+#include "../../utils/string.h"
+#include "../elems/basics/button.h"
+#include "../elems/basics/input.h"
+#include "../elems/basics/choice.h"
+#include "../elems/basics/dial.h"
+#include "../elems/basics/box.h"
+#include "../elems/basics/check.h"
+#include "../elems/sampleEditor/waveform.h"
+#include "../elems/sampleEditor/waveTools.h"
+#include "../elems/sampleEditor/volumeTool.h"
+#include "../elems/sampleEditor/boostTool.h"
+#include "../elems/sampleEditor/panTool.h"
+#include "../elems/sampleEditor/pitchTool.h"
+#include "../elems/sampleEditor/rangeTool.h"
+#include "../elems/mainWindow/keyboard/channel.h"
+#include "gd_warnings.h"
+#include "sampleEditor.h"
+
+
+using namespace giada::m;
+
+
+gdSampleEditor::gdSampleEditor(SampleChannel *ch)
+ : gdWindow(640, 480),
+ ch(ch)
+{
+ begin();
+
+ /* top bar: grid and zoom tools */
+
+ Fl_Group *bar = new Fl_Group(8, 8, w()-16, 20);
+ bar->begin();
+ grid = new geChoice(bar->x(), bar->y(), 50, 20);
+ snap = new geCheck(grid->x()+grid->w()+4, bar->y(), 12, 12);
+ sep1 = new geBox(snap->x()+snap->w()+4, bar->y(), 506, 20);
+ zoomOut = new geButton(sep1->x()+sep1->w()+4, bar->y(), 20, 20, "", zoomOutOff_xpm, zoomOutOn_xpm);
+ zoomIn = new geButton(zoomOut->x()+zoomOut->w()+4, bar->y(), 20, 20, "", zoomInOff_xpm, zoomInOn_xpm);
+ bar->end();
+ bar->resizable(sep1);
+
+ /* waveform */
+
+ waveTools = new geWaveTools(8, bar->y()+bar->h()+8, w()-16, h()-128, ch);
+ waveTools->end();
+
+ /* other tools */
+
+ Fl_Group *row1 = new Fl_Group(8, waveTools->y()+waveTools->h()+8, w()-16, 20);
+ row1->begin();
+ volumeTool = new geVolumeTool(row1->x(), row1->y(), ch);
+ boostTool = new geBoostTool(volumeTool->x()+volumeTool->w()+4, row1->y(), ch);
+ panTool = new gePanTool(boostTool->x()+boostTool->w()+4, row1->y(), ch);
+ row1->end();
+ row1->resizable(0);
+
+ Fl_Group *row2 = new Fl_Group(8, row1->y()+row1->h()+8, 800, 20);
+ row2->begin();
+ pitchTool = new gePitchTool(row2->x(), row2->y(), ch);
+ row2->end();
+ row2->resizable(0);
+
+ Fl_Group *row3 = new Fl_Group(8, row2->y()+row2->h()+8, w()-16, 20);
+ row3->begin();
+ rangeTool = new geRangeTool(row3->x(), row3->y(), ch);
+ sep2 = new geBox(rangeTool->x()+rangeTool->w()+4, row3->y(), 246, 20);
+ reload = new geButton(sep2->x()+sep2->w()+4, row3->y(), 70, 20, "Reload");
+ row3->end();
+ row3->resizable(sep2);
+
+ end();
+
+ /* grid tool setup */
+
+ grid->add("(off)");
+ grid->add("2");
+ grid->add("3");
+ grid->add("4");
+ grid->add("6");
+ grid->add("8");
+ grid->add("16");
+ grid->add("32");
+ grid->add("64");
+ grid->value(grid->find_item(gu_itoa(conf::sampleEditorGridVal).c_str()));
+ grid->callback(cb_changeGrid, (void*)this);
+
+ snap->value(conf::sampleEditorGridOn);
+ snap->callback(cb_enableSnap, (void*)this);
+
+ /* TODO - redraw grid if != (off) */
+
+ reload->callback(cb_reload, (void*)this);
+
+ zoomOut->callback(cb_zoomOut, (void*)this);
+ zoomIn->callback(cb_zoomIn, (void*)this);
+
+ /* logical samples (aka takes) cannot be reloaded. So far. */
+
+ if (ch->wave->isLogical)
+ reload->deactivate();
+
+ gu_setFavicon(this);
+ size_range(640, 480);
+ resizable(waveTools);
+
+ label(ch->wave->name.c_str());
+
+ set_non_modal();
+
+ if (conf::sampleEditorX)
+ resize(conf::sampleEditorX, conf::sampleEditorY, conf::sampleEditorW, conf::sampleEditorH);
+
+ show();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+gdSampleEditor::~gdSampleEditor()
+{
+ conf::sampleEditorX = x();
+ conf::sampleEditorY = y();
+ conf::sampleEditorW = w();
+ conf::sampleEditorH = h();
+ conf::sampleEditorGridVal = atoi(grid->text());
+ conf::sampleEditorGridOn = snap->value();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+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_enableSnap()
+{
+ waveTools->waveform->setSnap(!waveTools->waveform->getSnap());
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gdSampleEditor::__cb_reload()
+{
+ if (!gdConfirmWin("Warning", "Reload sample: are you sure?"))
+ return;
+
+ /* no need for glue_loadChan, there's no gui to refresh */
+
+ ch->load(ch->wave->pathfile.c_str(), conf::samplerate, conf::rsmpQuality);
+
+ glue_setBoost(ch, G_DEFAULT_BOOST);
+ glue_setPitch(ch, G_DEFAULT_PITCH);
+ glue_setPanning(ch, 1.0f);
+
+ panTool->refresh();
+ boostTool->refresh();
+
+ waveTools->waveform->stretchToWindow();
+ waveTools->updateWaveform();
+
+ glue_setBeginEndChannel(ch, 0, ch->wave->size);
+
+ redraw();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gdSampleEditor::__cb_zoomIn()
+{
+ waveTools->waveform->setZoom(-1);
+ waveTools->redraw();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gdSampleEditor::__cb_zoomOut()
+{
+ waveTools->waveform->setZoom(0);
+ waveTools->redraw();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gdSampleEditor::__cb_changeGrid()
+{
+ waveTools->waveform->setGridLevel(atoi(grid->text()));
+}
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * 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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#ifndef GD_EDITOR_H
+#define GD_EDITOR_H
+
+
+#include "window.h"
+
+
+class SampleChannel;
+class geButton;
+class geWaveTools;
+class geVolumeTool;
+class geBoostTool;
+class gePanTool;
+class gePitchTool;
+class geRangeTool;
+class geChoice;
+class geCheck;
+class geBox;
+class geButton;
+
+
+class gdSampleEditor : public gdWindow
+{
+private:
+
+ static void cb_reload (Fl_Widget *w, void *p);
+ static void cb_zoomIn (Fl_Widget *w, void *p);
+ static void cb_zoomOut (Fl_Widget *w, void *p);
+ static void cb_changeGrid (Fl_Widget *w, void *p);
+ static void cb_enableSnap (Fl_Widget *w, void *p);
+ inline void __cb_reload();
+ inline void __cb_zoomIn();
+ inline void __cb_zoomOut();
+ inline void __cb_changeGrid();
+ inline void __cb_enableSnap();
+
+public:
+
+ gdSampleEditor(SampleChannel *ch);
+ ~gdSampleEditor();
+
+ geChoice *grid;
+ geCheck *snap;
+ geBox *sep1;
+ geButton *zoomIn;
+ geButton *zoomOut;
+
+ geWaveTools *waveTools;
+
+ geVolumeTool *volumeTool;
+ geBoostTool *boostTool;
+ gePanTool *panTool;
+
+ gePitchTool *pitchTool;
+
+ geRangeTool *rangeTool;
+ geBox *sep2;
+ geButton *reload;
+
+ SampleChannel *ch;
+};
+
+
+#endif
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * 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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#include "../../utils/log.h"
+#include "window.h"
+
+
+void __cb_window_closer(Fl_Widget *v, void *p)
+{
+ delete (Fl_Window*) p;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+gdWindow::gdWindow(int x, int y, int w, int h, const char *title, int id)
+ : Fl_Double_Window(x, y, w, h, title), id(id), parent(nullptr)
+{
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+gdWindow::gdWindow(int w, int h, const char *title, int id)
+ : Fl_Double_Window(w, h, title), id(id), parent(nullptr)
+{
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+gdWindow::~gdWindow()
+{
+ /* delete all subwindows in order to empty the stack */
+
+ for (unsigned i=0; i<subWindows.size(); i++)
+ delete subWindows.at(i);
+ subWindows.clear();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+/* this is the default callback of each window, fired when the user closes
+ * the window with the 'x'. Watch out: is the parent that calls delSubWIndow */
+
+void gdWindow::cb_closeChild(Fl_Widget *v, void *p)
+{
+ gdWindow *child = (gdWindow*) v;
+ if (child->getParent() != nullptr)
+ (child->getParent())->delSubWindow(child);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gdWindow::addSubWindow(gdWindow *w)
+{
+ /** TODO - useless: delete ---------------------------------------- */
+ for (unsigned i=0; i<subWindows.size(); i++)
+ if (w->getId() == subWindows.at(i)->getId()) {
+ //gu_log("[gdWindow] window %p (id=%d) exists, not added (and deleted)\n", (void*)w, w->getId());
+ delete w;
+ return;
+ }
+ /** --------------------------------------------------------------- */
+
+ w->setParent(this);
+ w->callback(cb_closeChild); // you can pass params: w->callback(cb_closeChild, (void*)params)
+ subWindows.push_back(w);
+ //debug();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gdWindow::delSubWindow(gdWindow *w)
+{
+ for (unsigned i=0; i<subWindows.size(); i++)
+ if (w->getId() == subWindows.at(i)->getId()) {
+ delete subWindows.at(i);
+ subWindows.erase(subWindows.begin() + i);
+ //debug();
+ return;
+ }
+ //debug();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gdWindow::delSubWindow(int id)
+{
+ for (unsigned i=0; i<subWindows.size(); i++)
+ if (subWindows.at(i)->getId() == id) {
+ delete subWindows.at(i);
+ subWindows.erase(subWindows.begin() + i);
+ //debug();
+ return;
+ }
+ //debug();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int gdWindow::getId()
+{
+ return id;
+}
+
+
+void gdWindow::setId(int id)
+{
+ this->id = id;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gdWindow::debug()
+{
+ gu_log("---- window stack (id=%d): ----\n", getId());
+ for (unsigned i=0; i<subWindows.size(); i++)
+ gu_log("[gdWindow] %p (id=%d)\n", (void*)subWindows.at(i), subWindows.at(i)->getId());
+ gu_log("----\n");
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+gdWindow *gdWindow::getParent()
+{
+ return parent;
+}
+
+
+void gdWindow::setParent(gdWindow *w)
+{
+ parent = w;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+bool gdWindow::hasWindow(int id)
+{
+ for (unsigned i=0; i<subWindows.size(); i++)
+ if (id == subWindows.at(i)->getId())
+ return true;
+ return false;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+gdWindow *gdWindow::getChild(int id)
+{
+ for (unsigned i=0; i<subWindows.size(); i++)
+ if (id == subWindows.at(i)->getId())
+ return subWindows.at(i);
+ return nullptr;
+}
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * 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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#ifndef GD_WINDOW_H
+#define GD_WINDOW_H
+
+
+#include <vector>
+#include <FL/Fl_Double_Window.H>
+
+
+/* cb_window_closer
+ * callback for when closing windows. Deletes the widget (delete). */
+
+void __cb_window_closer(Fl_Widget *v, void *p);
+
+
+class gdWindow : public Fl_Double_Window
+{
+protected:
+
+ std::vector <gdWindow*> subWindows;
+ int id;
+ 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();
+
+ static void cb_closeChild(Fl_Widget *v, void *p);
+
+ 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);
+
+ /* hasWindow
+ * true if the window with id 'id' exists in the stack. */
+
+ bool hasWindow(int id);
+};
+
+
+#endif
+++ /dev/null
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * ge_actionChannel
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
- *
- * This file is part of Giada - Your Hardcore Loopmachine.
- *
- * Giada - Your Hardcore Loopmachine is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General
- * Public License as published by the Free Software Foundation, either
- * version 3 of the License, or (at your option) any later version.
- *
- * Giada - Your Hardcore Loopmachine is distributed in the hope that it
- * will be useful, but WITHOUT ANY WARRANTY; without even the implied
- * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Giada - Your Hardcore Loopmachine. If not, see
- * <http://www.gnu.org/licenses/>.
- *
- * -------------------------------------------------------------------------- */
-
-
-#include <FL/fl_draw.H>
-#include "../../core/conf.h"
-#include "../../core/channel.h"
-#include "../../core/sampleChannel.h"
-#include "../../glue/main.h"
-#include "../../utils/log.h"
-#include "../dialogs/gd_mainWindow.h"
-#include "../dialogs/gd_actionEditor.h"
-#include "mainWindow/keyboard/keyboard.h"
-#include "actionEditor.h"
-
-
-extern gdMainWindow *G_MainWin;
-extern Mixer G_Mixer;
-extern Conf G_Conf;
-extern Recorder G_Recorder;
-
-
-/* -------------------------------------------------------------------------- */
-
-
-geActionEditor::geActionEditor(int x, int y, gdActionEditor *pParent, SampleChannel *ch)
- : geBaseActionEditor(x, y, 200, 40, pParent),
- ch (ch),
- selected (NULL)
-{
- size(pParent->totalWidth, h());
-
- /* add actions when the window opens. Their position is zoom-based;
- * each frame is / 2 because we don't care about stereo infos. */
-
- for (unsigned i=0; i<G_Recorder.frames.size(); i++) {
- for (unsigned j=0; j<G_Recorder.global.at(i).size(); j++) {
-
- Recorder::action *action = G_Recorder.global.at(i).at(j);
-
- /* Don't show actions:
- - that don't belong to the displayed channel (!= pParent->chan->index);
- - that are covered by the grey area (> G_Mixer.totalFrames);
- - of type ACTION_KILLCHAN in a SINGLE_PRESS channel. They cannot be
- recorded in such mode, but they can exist if you change from another
- mode to singlepress;
- - of type ACTION_KEYREL in a SINGLE_PRESS channel. It's up to gAction to
- find the other piece (namely frame_b)
- - not of types ACTION_KEYPRESS | ACTION_KEYREL | ACTION_KILLCHAN */
-
- if ((action->chan != pParent->chan->index) ||
- (G_Recorder.frames.at(i) > G_Mixer.totalFrames) ||
- (action->type == ACTION_KILLCHAN && ch->mode == SINGLE_PRESS) ||
- (action->type == ACTION_KEYREL && ch->mode == SINGLE_PRESS) ||
- (action->type & ~(ACTION_KEYPRESS | ACTION_KEYREL | ACTION_KILLCHAN))
- )
- continue;
-
- int ax = x + (action->frame / pParent->zoom);
- gAction *a = new gAction(
- ax, // x
- y + 4, // y
- h() - 8, // h
- action->frame, // frame_a
- i, // n. of recordings
- pParent, // pointer to the pParent window
- ch, // pointer to SampleChannel
- false, // record = false: don't record it, we are just displaying it!
- action->type); // type of action
- add(a);
- }
- }
- end(); // mandatory when you add widgets to a fl_group, otherwise mega malfunctions
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-gAction *geActionEditor::getSelectedAction()
-{
- for (int i=0; i<children(); i++) {
- int action_x = ((gAction*)child(i))->x();
- int action_w = ((gAction*)child(i))->w();
- if (Fl::event_x() >= action_x && Fl::event_x() <= action_x + action_w)
- return (gAction*)child(i);
- }
- return NULL;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void geActionEditor::updateActions()
-{
- /* when zooming, don't delete and re-add actions, just MOVE them. This
- * function shifts the action by a zoom factor. Those singlepress are
- * stretched, as well */
-
- gAction *a;
- for (int i=0; i<children(); i++) {
-
- a = (gAction*)child(i);
- int newX = x() + (a->frame_a / pParent->zoom);
-
- if (ch->mode == SINGLE_PRESS) {
- int newW = ((a->frame_b - a->frame_a) / pParent->zoom);
- if (newW < gAction::MIN_WIDTH)
- newW = gAction::MIN_WIDTH;
- a->resize(newX, a->y(), newW, a->h());
- }
- else
- a->resize(newX, a->y(), gAction::MIN_WIDTH, a->h());
- }
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void geActionEditor::draw()
-{
- /* draw basic boundaries (+ beat bars) and hide the unused area. Then
- * draw the children (the actions) */
-
- baseDraw();
-
- /* print label */
-
- fl_color(COLOR_BG_1);
- fl_font(FL_HELVETICA, 12);
- if (active())
- fl_draw("start/stop", x()+4, y(), w(), h(), (Fl_Align) (FL_ALIGN_LEFT | FL_ALIGN_CENTER)); /// FIXME h() is too much!
- else
- fl_draw("start/stop (disabled)", x()+4, y(), w(), h(), (Fl_Align) (FL_ALIGN_LEFT | FL_ALIGN_CENTER)); /// FIXME h() is too much!
-
- draw_children();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-int geActionEditor::handle(int e)
-{
- int ret = Fl_Group::handle(e);
-
- /* do nothing if the widget is deactivated. It could happen for loopmode
- * channels */
-
- if (!active())
- return 1;
-
- switch (e) {
-
- case FL_DRAG: {
-
- if (selected == NULL) { // if you drag an empty area
- ret = 1;
- break;
- }
-
- /* if onLeftEdge o onRightEdge are true it means that you're resizing
- * an action. Otherwise move the widget. */
-
- if (selected->onLeftEdge || selected->onRightEdge) {
-
- /* some checks: a) cannot resize an action < N pixels, b) no beyond zero,
- * c) no beyond bar maxwidth. Checks for overlap are done in FL_RELEASE */
-
- if (selected->onRightEdge) {
-
- int aw = Fl::event_x()-selected->x();
- int ah = selected->h();
-
- if (Fl::event_x() < selected->x()+gAction::MIN_WIDTH)
- aw = gAction::MIN_WIDTH;
- else
- if (Fl::event_x() > pParent->coverX)
- aw = pParent->coverX-selected->x();
-
- selected->size(aw, ah);
- }
- else {
-
- int ax = Fl::event_x();
- int ay = selected->y();
- int aw = selected->x()-Fl::event_x()+selected->w();
- int ah = selected->h();
-
- if (Fl::event_x() < x()) {
- ax = x();
- aw = selected->w()+selected->x()-x();
- }
- else
- if (Fl::event_x() > selected->x()+selected->w()-gAction::MIN_WIDTH) {
- ax = selected->x()+selected->w()-gAction::MIN_WIDTH;
- aw = gAction::MIN_WIDTH;
- }
- selected->resize(ax, ay, aw, ah);
- }
- }
-
- /* move the widget around */
-
- else {
- int real_x = Fl::event_x() - actionPickPoint;
- if (real_x < x()) // don't go beyond the left border
- selected->position(x(), selected->y());
- else
- if (real_x+selected->w() > pParent->coverX+x()) // don't go beyond the right border
- selected->position(pParent->coverX+x()-selected->w(), selected->y());
- else {
- if (pParent->gridTool->isOn()) {
- int snpx = pParent->gridTool->getSnapPoint(real_x-x()) + x() -1;
- selected->position(snpx, selected->y());
- }
- else
- selected->position(real_x, selected->y());
- }
- }
- redraw();
- ret = 1;
- break;
- }
-
- case FL_PUSH: {
-
- if (Fl::event_button1()) {
-
- /* avoid at all costs two overlapping actions. We use 'selected' because
- * the selected action can be reused in FL_DRAG, in case you want to move
- * it. */
-
- selected = getSelectedAction();
-
- if (selected == NULL) {
-
- /* avoid click on grey area */
-
- if (Fl::event_x() >= pParent->coverX) {
- ret = 1;
- break;
- }
-
- /* snap function, if enabled */
-
- int ax = Fl::event_x();
- int fx = (ax - x()) * pParent->zoom;
- if (pParent->gridTool->isOn()) {
- ax = pParent->gridTool->getSnapPoint(ax-x()) + x() -1;
- fx = pParent->gridTool->getSnapFrame(ax-x());
-
- /* with snap=on an action can fall onto another */
-
- if (actionCollides(fx)) {
- ret = 1;
- break;
- }
- }
-
- gAction *a = new gAction(
- ax, // x
- y()+4, // y
- h()-8, // h
- fx, // frame_a
- G_Recorder.frames.size()-1, // n. of actions recorded
- pParent, // pParent window pointer
- ch, // pointer to SampleChannel
- true, // record = true: record it!
- pParent->getActionType()); // type of action
- add(a);
- G_MainWin->keyboard->setChannelWithActions((geSampleChannel*)ch->guiChannel); // mainWindow update
- redraw();
- ret = 1;
- }
- else {
- actionOriginalX = selected->x();
- actionOriginalW = selected->w();
- actionPickPoint = Fl::event_x() - selected->x();
- ret = 1; // for dragging
- }
- }
- else
- if (Fl::event_button3()) {
- gAction *a = getSelectedAction();
- if (a != NULL) {
- a->delAction();
- remove(a);
- delete a;
- G_MainWin->keyboard->setChannelWithActions((geSampleChannel*)pParent->chan->guiChannel);
- redraw();
- ret = 1;
- }
- }
- break;
- }
- case FL_RELEASE: {
-
- if (selected == NULL) {
- ret = 1;
- break;
- }
-
- /* noChanges = true when you click on an action without doing anything
- * (dragging or moving it). */
-
- bool noChanges = false;
- if (actionOriginalX == selected->x())
- noChanges = true;
- if (ch->mode == SINGLE_PRESS &&
- actionOriginalX+actionOriginalW != selected->x()+selected->w())
- noChanges = false;
-
- if (noChanges) {
- ret = 1;
- selected = NULL;
- break;
- }
-
- /* step 1: check if the action doesn't overlap with another one.
- * In case of overlap the moved action returns to the original X
- * value ("actionOriginalX"). */
-
- bool overlap = false;
- for (int i=0; i<children() && !overlap; i++) {
-
- /* never check against itself. */
-
- if ((gAction*)child(i) == selected)
- continue;
-
- int action_x = ((gAction*)child(i))->x();
- int action_w = ((gAction*)child(i))->w();
- if (ch->mode == SINGLE_PRESS) {
-
- /* when 2 segments overlap?
- * start = the highest value between the two starting points
- * end = the lowest value between the two ending points
- * if start < end then there's an overlap of end-start pixels. */
-
- int start = action_x >= selected->x() ? action_x : selected->x();
- int end = action_x+action_w < selected->x()+selected->w() ? action_x+action_w : selected->x()+selected->w();
- if (start < end) {
- selected->resize(actionOriginalX, selected->y(), actionOriginalW, selected->h());
- redraw();
- overlap = true; // one overlap: stop checking
- }
- }
- else {
- if (selected->x() == action_x) {
- selected->position(actionOriginalX, selected->y());
- redraw();
- overlap = true; // one overlap: stop checking
- }
- }
- }
-
- /* step 2: no overlap? then update the coordinates of the action, ie
- * delete the previous rec and create a new one.
- * Anyway the selected action becomes NULL, because when you release
- * the mouse button the dragging process ends. */
-
- if (!overlap) {
- if (pParent->gridTool->isOn()) {
- int f = pParent->gridTool->getSnapFrame(selected->absx());
- selected->moveAction(f);
- }
- else
- selected->moveAction();
- }
- selected = NULL;
- ret = 1;
- break;
- }
- }
-
- return ret;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-bool geActionEditor::actionCollides(int frame)
-{
- /* if SINGLE_PRESS we check that the tail (frame_b) of the action doesn't
- * overlap the head (frame) of the new one. First the general case, yet. */
-
- bool collision = false;
-
- for (int i=0; i<children() && !collision; i++)
- if (((gAction*) child(i))->frame_a == frame)
- collision = true;
-
- if (ch->mode == SINGLE_PRESS) {
- for (int i=0; i<children() && !collision; i++) {
- gAction *c = ((gAction*) child(i));
- if (frame <= c->frame_b && frame >= c->frame_a)
- collision = true;
- }
- }
-
- return collision;
-}
-
-
-/* -------------------------------------------------------------------------- */
-/* -------------------------------------------------------------------------- */
-/* -------------------------------------------------------------------------- */
-
-
-/** TODO - index is useless?
- * TODO - pass a record::action pointer and let gAction compute values */
-
-gAction::gAction(int X, int Y, int H, int frame_a, unsigned index, gdActionEditor *parent, SampleChannel *ch, bool record, char type)
-: Fl_Box (X, Y, MIN_WIDTH, H),
- selected (false),
- index (index),
- parent (parent),
- ch (ch),
- type (type),
- frame_a (frame_a),
- onRightEdge(false),
- onLeftEdge (false)
-{
- /* bool 'record' defines how to understand the action.
- * record = false: don't record it, just show it. It happens when you
- * open the editor with some actions to be shown.
- *
- * record = true: record it AND show it. It happens when you click on
- * an empty area in order to add a new actions. First you record it
- * (addAction()) then you show it (FLTK::Draw()) */
-
- if (record)
- addAction();
-
- /* in order to show a singlepress action we must compute the frame_b. We
- * do that after the possible recording, otherwise we don't know which
- * key_release is associated. */
-
- if (ch->mode == SINGLE_PRESS && type == ACTION_KEYPRESS) {
- Recorder::action *a2 = NULL;
- G_Recorder.getNextAction(ch->index, ACTION_KEYREL, frame_a, &a2);
- if (a2) {
- frame_b = a2->frame;
- w((frame_b - frame_a)/parent->zoom);
- }
- else
- gu_log("[geActionEditor] frame_b not found! [%d:???]\n", frame_a);
-
- /* a singlepress action narrower than 8 pixel is useless. So check it.
- * Warning: if an action is 8 px narrow, it has no body space to drag
- * it. It's up to the user to zoom in and drag it. */
-
- if (w() < MIN_WIDTH)
- size(MIN_WIDTH, h());
- }
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gAction::draw()
-{
- int color;
- if (selected) /// && geActionEditor !disabled
- color = COLOR_BD_1;
- else
- color = COLOR_BG_2;
-
- if (ch->mode == SINGLE_PRESS) {
- fl_rectf(x(), y(), w(), h(), (Fl_Color) color);
- }
- else {
- if (type == ACTION_KILLCHAN)
- fl_rect(x(), y(), MIN_WIDTH, h(), (Fl_Color) color);
- else {
- fl_rectf(x(), y(), MIN_WIDTH, h(), (Fl_Color) color);
- if (type == ACTION_KEYPRESS)
- fl_rectf(x()+3, y()+h()-11, 2, 8, COLOR_BD_0);
- else
- if (type == ACTION_KEYREL)
- fl_rectf(x()+3, y()+3, 2, 8, COLOR_BD_0);
- }
- }
-
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-int gAction::handle(int e)
-{
- /* ret = 0 sends the event to the parent window. */
-
- int ret = 0;
-
- switch (e) {
-
- case FL_ENTER: {
- selected = true;
- ret = 1;
- redraw();
- break;
- }
- case FL_LEAVE: {
- fl_cursor(FL_CURSOR_DEFAULT, FL_WHITE, FL_BLACK);
- selected = false;
- ret = 1;
- redraw();
- break;
- }
- case FL_MOVE: {
-
- /* handling of the two margins, left & right. 4 pixels are good enough */
-
- if (ch->mode == SINGLE_PRESS) {
- onLeftEdge = false;
- onRightEdge = false;
- if (Fl::event_x() >= x() && Fl::event_x() < x()+4) {
- onLeftEdge = true;
- fl_cursor(FL_CURSOR_WE, FL_WHITE, FL_BLACK);
- }
- else
- if (Fl::event_x() >= x()+w()-4 && Fl::event_x() <= x()+w()) {
- onRightEdge = true;
- fl_cursor(FL_CURSOR_WE, FL_WHITE, FL_BLACK);
- }
- else
- fl_cursor(FL_CURSOR_DEFAULT, FL_WHITE, FL_BLACK);
- }
- }
- }
-
- return ret;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gAction::addAction()
-{
- /* always check frame parity */
-
- if (frame_a % 2 != 0)
- frame_a++;
-
- /* anatomy of an action
- * ____[#######]_____ (a) is the left margin, ACTION_KEYPRESS. (b) is
- * a b the right margin, the ACTION_KEYREL. This is the
- * theory behind the singleshot.press actions; for any other kind the
- * (b) is just a graphical and meaningless point. */
-
- if (ch->mode == SINGLE_PRESS) {
- G_Recorder.rec(parent->chan->index, ACTION_KEYPRESS, frame_a);
- G_Recorder.rec(parent->chan->index, ACTION_KEYREL, frame_a+4096);
- //gu_log("action added, [%d, %d]\n", frame_a, frame_a+4096);
- }
- else {
- G_Recorder.rec(parent->chan->index, parent->getActionType(), frame_a);
- //gu_log("action added, [%d]\n", frame_a);
- }
-
- parent->chan->hasActions = true;
-
- G_Recorder.sortActions();
-
- index++; // important!
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gAction::delAction()
-{
- /* if SINGLE_PRESS you must delete both the keypress and the keyrelease
- * actions. */
-
- if (ch->mode == SINGLE_PRESS) {
- G_Recorder.deleteAction(parent->chan->index, frame_a, ACTION_KEYPRESS,
- false, &G_Mixer.mutex_recs);
- G_Recorder.deleteAction(parent->chan->index, frame_b, ACTION_KEYREL,
- false, &G_Mixer.mutex_recs);
- }
- else
- G_Recorder.deleteAction(parent->chan->index, frame_a, type, false,
- &G_Mixer.mutex_recs);
-
- parent->chan->hasActions = G_Recorder.hasActions(parent->chan->index);
-
-
- /* restore the initial cursor shape, in case you delete an action and
- * the double arrow (for resizing) is displayed */
-
- fl_cursor(FL_CURSOR_DEFAULT, FL_WHITE, FL_BLACK);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gAction::moveAction(int frame_a)
-{
- /* easy one: delete previous action and record the new ones. As usual,
- * SINGLE_PRESS requires two jobs. If frame_a is valid, use that frame
- * value. */
-
- delAction();
-
- if (frame_a != -1)
- this->frame_a = frame_a;
- else
- this->frame_a = xToFrame_a();
-
-
- /* always check frame parity */
-
- if (this->frame_a % 2 != 0)
- this->frame_a++;
-
- G_Recorder.rec(parent->chan->index, type, this->frame_a);
-
- if (ch->mode == SINGLE_PRESS) {
- frame_b = xToFrame_b();
- G_Recorder.rec(parent->chan->index, ACTION_KEYREL, frame_b);
- }
-
- parent->chan->hasActions = true;
-
- G_Recorder.sortActions();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-int gAction::absx()
-{
- return x() - parent->ac->x();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-int gAction::xToFrame_a()
-{
- return (absx()) * parent->zoom;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-int gAction::xToFrame_b()
-{
- return (absx() + w()) * parent->zoom;
-}
+++ /dev/null
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * ge_actionChannel
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
- *
- * This file is part of Giada - Your Hardcore Loopmachine.
- *
- * Giada - Your Hardcore Loopmachine is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General
- * Public License as published by the Free Software Foundation, either
- * version 3 of the License, or (at your option) any later version.
- *
- * Giada - Your Hardcore Loopmachine is distributed in the hope that it
- * will be useful, but WITHOUT ANY WARRANTY; without even the implied
- * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Giada - Your Hardcore Loopmachine. If not, see
- * <http://www.gnu.org/licenses/>.
- *
- * -------------------------------------------------------------------------- */
-
-
-#ifndef GE_ACTIONCHANNEL_H
-#define GE_ACTIONCHANNEL_H
-
-
-#include <FL/Fl.H>
-#include <FL/Fl_Box.H>
-#include "../../utils/gui.h"
-#include "../../core/mixer.h"
-#include "../../core/recorder.h"
-#include "baseActionEditor.h"
-
-
-class gAction : public Fl_Box
-{
-private:
-
- bool selected;
- unsigned index;
- class gdActionEditor *parent; // pointer to parent (gActionEditor)
- class SampleChannel *ch;
- char type; // type of action
-
-public:
-
- gAction(int x, int y, int h, int frame_a, unsigned index,
- gdActionEditor *parent, class SampleChannel *ch, bool record,
- char type);
- void draw();
- int handle(int e);
- void addAction();
- void delAction();
-
- /* moveAction
- * shift the action on the x-axis and update Recorder. If frame_a != -1
- * use the new frame in input (used while snapping) */
-
- void moveAction(int frame_a=-1);
-
- /* absx
- * x() is relative to scrolling position. absx() returns the absolute
- * x value of the action, from the leftmost edge. */
-
- int absx();
-
- /* xToFrame_a,b
- * return the real frames of x() position */
-
- int xToFrame_a();
- int xToFrame_b();
-
- int frame_a; // initial frame (KEYPRESS for singlemode.press)
- int frame_b; // terminal frame (KEYREL for singlemode.press, null for others)
-
- bool onRightEdge;
- bool onLeftEdge;
-
- static const int MIN_WIDTH = 8;
-};
-
-
-/* -------------------------------------------------------------------------- */
-
-
-class geActionEditor : public geBaseActionEditor
-{
-
-private:
-
- class SampleChannel *ch;
-
- /* getSelectedAction
- * get the action under the mouse. NULL if nothing found. */
-
- gAction *getSelectedAction();
-
- /* selected
- * pointer to the selected action. Useful when dragging around. */
-
- gAction *selected;
-
- /* actionOriginalX, actionOriginalW
- * x and w of the action, when moved. Useful for checking if the action
- * overlaps another one: in that case the moved action returns to
- * actionOriginalX (and to actionOriginalW if resized). */
-
- int actionOriginalX;
- int actionOriginalW;
-
- /* actionPickPoint
- * the precise x point in which the action has been picked with the mouse,
- * before a dragging action. */
-
- int actionPickPoint;
-
-
- /* actionCollides
- * true if an action collides with another. Used while adding new points
- * with snap active.*/
-
- bool actionCollides(int frame);
-
-public:
-
- geActionEditor(int x, int y, gdActionEditor *pParent, class SampleChannel *ch);
- void draw();
- int handle(int e);
- void updateActions();
-};
-
-
-#endif
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * 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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#include <FL/fl_draw.H>
+#include "../../../core/const.h"
+#include "../../../core/mixer.h"
+#include "../../../core/sampleChannel.h"
+#include "../../../utils/log.h"
+#include "../../dialogs/gd_actionEditor.h"
+#include "actionEditor.h"
+#include "action.h"
+
+
+using namespace giada::m;
+
+
+/** TODO - index is useless?
+ * TODO - pass a record::action pointer and let geAction compute values */
+
+geAction::geAction(int X, int Y, int H, int frame_a, unsigned index,
+ gdActionEditor *parent, SampleChannel *ch, bool record, char type)
+: Fl_Box (X, Y, MIN_WIDTH, H),
+ selected (false),
+ index (index),
+ parent (parent),
+ ch (ch),
+ type (type),
+ frame_a (frame_a),
+ onRightEdge(false),
+ onLeftEdge (false)
+{
+ /* bool 'record' defines how to understand the action.
+ * record = false: don't record it, just show it. It happens when you
+ * open the editor with some actions to be shown.
+ *
+ * record = true: record it AND show it. It happens when you click on
+ * an empty area in order to add a new actions. First you record it
+ * (addAction()) then you show it (FLTK::Draw()) */
+
+ if (record)
+ addAction();
+
+ /* in order to show a singlepress action we must compute the frame_b. We
+ * do that after the possible recording, otherwise we don't know which
+ * key_release is associated. */
+
+ if (ch->mode == SINGLE_PRESS && type == G_ACTION_KEYPRESS) {
+ recorder::action *a2 = nullptr;
+ recorder::getNextAction(ch->index, G_ACTION_KEYREL, frame_a, &a2);
+ if (a2) {
+ frame_b = a2->frame;
+ w((frame_b - frame_a)/parent->zoom);
+ }
+ else
+ gu_log("[geActionEditor] frame_b not found! [%d:???]\n", frame_a);
+
+ /* a singlepress action narrower than 8 pixel is useless. So check it.
+ * Warning: if an action is 8 px narrow, it has no body space to drag
+ * it. It's up to the user to zoom in and drag it. */
+
+ if (w() < MIN_WIDTH)
+ size(MIN_WIDTH, h());
+ }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geAction::draw()
+{
+ int color;
+ if (selected) /// && geActionEditor !disabled
+ color = COLOR_BD_1;
+ else
+ color = COLOR_BG_2;
+
+ if (ch->mode == SINGLE_PRESS) {
+ fl_rectf(x(), y(), w(), h(), (Fl_Color) color);
+ }
+ else {
+ if (type == G_ACTION_KILL)
+ fl_rect(x(), y(), MIN_WIDTH, h(), (Fl_Color) color);
+ else {
+ fl_rectf(x(), y(), MIN_WIDTH, h(), (Fl_Color) color);
+ if (type == G_ACTION_KEYPRESS)
+ fl_rectf(x()+3, y()+h()-11, 2, 8, COLOR_BD_0);
+ else
+ if (type == G_ACTION_KEYREL)
+ fl_rectf(x()+3, y()+3, 2, 8, COLOR_BD_0);
+ }
+ }
+
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int geAction::handle(int e)
+{
+ /* ret = 0 sends the event to the parent window. */
+
+ int ret = 0;
+
+ switch (e) {
+
+ case FL_ENTER: {
+ selected = true;
+ ret = 1;
+ redraw();
+ break;
+ }
+ case FL_LEAVE: {
+ fl_cursor(FL_CURSOR_DEFAULT, FL_WHITE, FL_BLACK);
+ selected = false;
+ ret = 1;
+ redraw();
+ break;
+ }
+ case FL_MOVE: {
+
+ /* handling of the two margins, left & right. 4 pixels are good enough */
+
+ if (ch->mode == SINGLE_PRESS) {
+ onLeftEdge = false;
+ onRightEdge = false;
+ if (Fl::event_x() >= x() && Fl::event_x() < x()+4) {
+ onLeftEdge = true;
+ fl_cursor(FL_CURSOR_WE, FL_WHITE, FL_BLACK);
+ }
+ else
+ if (Fl::event_x() >= x()+w()-4 && Fl::event_x() <= x()+w()) {
+ onRightEdge = true;
+ fl_cursor(FL_CURSOR_WE, FL_WHITE, FL_BLACK);
+ }
+ else
+ fl_cursor(FL_CURSOR_DEFAULT, FL_WHITE, FL_BLACK);
+ }
+ }
+ }
+
+ return ret;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geAction::addAction()
+{
+ /* always check frame parity */
+
+ if (frame_a % 2 != 0)
+ frame_a++;
+
+ /* anatomy of an action
+ * ____[#######]_____ (a) is the left margin, G_ACTION_KEYPRESS. (b) is
+ * a b the right margin, the G_ACTION_KEYREL. This is the
+ * theory behind the singleshot.press actions; for any other kind the
+ * (b) is just a graphical and meaningless point. */
+
+ if (ch->mode == SINGLE_PRESS) {
+ recorder::rec(parent->chan->index, G_ACTION_KEYPRESS, frame_a);
+ recorder::rec(parent->chan->index, G_ACTION_KEYREL, frame_a+4096);
+ //gu_log("action added, [%d, %d]\n", frame_a, frame_a+4096);
+ }
+ else {
+ recorder::rec(parent->chan->index, parent->getActionType(), frame_a);
+ //gu_log("action added, [%d]\n", frame_a);
+ }
+
+ parent->chan->hasActions = true;
+
+ recorder::sortActions();
+
+ index++; // important!
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geAction::delAction()
+{
+ /* if SINGLE_PRESS you must delete both the keypress and the keyrelease
+ * actions. */
+
+ if (ch->mode == SINGLE_PRESS) {
+ recorder::deleteAction(parent->chan->index, frame_a, G_ACTION_KEYPRESS,
+ false, &mixer::mutex_recs);
+ recorder::deleteAction(parent->chan->index, frame_b, G_ACTION_KEYREL,
+ false, &mixer::mutex_recs);
+ }
+ else
+ recorder::deleteAction(parent->chan->index, frame_a, type, false,
+ &mixer::mutex_recs);
+
+ parent->chan->hasActions = recorder::hasActions(parent->chan->index);
+
+
+ /* restore the initial cursor shape, in case you delete an action and
+ * the double arrow (for resizing) is displayed */
+
+ fl_cursor(FL_CURSOR_DEFAULT, FL_WHITE, FL_BLACK);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geAction::moveAction(int frame_a)
+{
+ /* easy one: delete previous action and record the new ones. As usual,
+ * SINGLE_PRESS requires two jobs. If frame_a is valid, use that frame
+ * value. */
+
+ delAction();
+
+ if (frame_a != -1)
+ this->frame_a = frame_a;
+ else
+ this->frame_a = xToFrame_a();
+
+
+ /* always check frame parity */
+
+ if (this->frame_a % 2 != 0)
+ this->frame_a++;
+
+ recorder::rec(parent->chan->index, type, this->frame_a);
+
+ if (ch->mode == SINGLE_PRESS) {
+ frame_b = xToFrame_b();
+ recorder::rec(parent->chan->index, G_ACTION_KEYREL, frame_b);
+ }
+
+ parent->chan->hasActions = true;
+
+ recorder::sortActions();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int geAction::absx()
+{
+ return x() - parent->ac->x();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int geAction::xToFrame_a()
+{
+ return (absx()) * parent->zoom;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int geAction::xToFrame_b()
+{
+ return (absx() + w()) * parent->zoom;
+}
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#ifndef GE_ACTION_H
+#define GE_ACTION_H
+
+
+#include <FL/Fl_Box.H>
+
+
+class gdActionEditor;
+class SampleChannel;
+
+
+class geAction : public Fl_Box
+{
+private:
+
+ bool selected;
+ unsigned index;
+ gdActionEditor *parent; // pointer to parent (geActionEditor)
+ SampleChannel *ch;
+ char type; // type of action
+
+public:
+
+ geAction(int x, int y, int h, int frame_a, unsigned index,
+ gdActionEditor *parent, SampleChannel *ch, bool record, char type);
+ void draw();
+ int handle(int e);
+ void addAction();
+ void delAction();
+
+ /* moveAction
+ * shift the action on the x-axis and update Recorder. If frame_a != -1
+ * use the new frame in input (used while snapping) */
+
+ void moveAction(int frame_a=-1);
+
+ /* absx
+ * x() is relative to scrolling position. absx() returns the absolute
+ * x value of the action, from the leftmost edge. */
+
+ int absx();
+
+ /* xToFrame_a,b
+ * return the real frames of x() position */
+
+ int xToFrame_a();
+ int xToFrame_b();
+
+ int frame_a; // initial frame (KEYPRESS for singlemode.press)
+ int frame_b; // terminal frame (KEYREL for singlemode.press, null for others)
+
+ bool onRightEdge;
+ bool onLeftEdge;
+
+ static const int MIN_WIDTH = 8;
+};
+
+
+#endif
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#include <FL/fl_draw.H>
+#include "../../../core/clock.h"
+#include "../../../core/sampleChannel.h"
+#include "../../dialogs/gd_mainWindow.h"
+#include "../../dialogs/gd_actionEditor.h"
+#include "../mainWindow/keyboard/keyboard.h"
+#include "action.h"
+#include "gridTool.h"
+#include "actionEditor.h"
+
+
+extern gdMainWindow *G_MainWin;
+
+
+using namespace giada::m;
+
+
+geActionEditor::geActionEditor(int x, int y, gdActionEditor *pParent, SampleChannel *ch)
+ : geBaseActionEditor(x, y, 200, 40, pParent),
+ ch (ch),
+ selected (nullptr)
+{
+ size(pParent->totalWidth, h());
+
+ /* add actions when the window opens. Their position is zoom-based;
+ * each frame is / 2 because we don't care about stereo infos. */
+
+ for (unsigned i=0; i<recorder::frames.size(); i++) {
+ for (unsigned j=0; j<recorder::global.at(i).size(); j++) {
+
+ recorder::action *action = recorder::global.at(i).at(j);
+
+ /* Don't show actions:
+ - that don't belong to the displayed channel (!= pParent->chan->index);
+ - that are covered by the grey area (> G_Mixer.totalFrames);
+ - of type G_ACTION_KILL in a SINGLE_PRESS channel. They cannot be
+ recorded in such mode, but they can exist if you change from another
+ mode to singlepress;
+ - of type G_ACTION_KEYREL in a SINGLE_PRESS channel. It's up to geAction to
+ find the other piece (namely frame_b)
+ - not of types G_ACTION_KEYPRESS | G_ACTION_KEYREL | G_ACTION_KILL */
+
+ if ((action->chan != pParent->chan->index) ||
+ (recorder::frames.at(i) > clock::getTotalFrames()) ||
+ (action->type == G_ACTION_KILL && ch->mode == SINGLE_PRESS) ||
+ (action->type == G_ACTION_KEYREL && ch->mode == SINGLE_PRESS) ||
+ (action->type & ~(G_ACTION_KEYPRESS | G_ACTION_KEYREL | G_ACTION_KILL))
+ )
+ continue;
+
+ int ax = x + (action->frame / pParent->zoom);
+ geAction *a = new geAction(
+ ax, // x
+ y + 4, // y
+ h() - 8, // h
+ action->frame, // frame_a
+ i, // n. of recordings
+ pParent, // pointer to the pParent window
+ ch, // pointer to SampleChannel
+ false, // record = false: don't record it, we are just displaying it!
+ action->type); // type of action
+ add(a);
+ }
+ }
+ end(); // mandatory when you add widgets to a fl_group, otherwise mega malfunctions
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+geAction *geActionEditor::getSelectedAction()
+{
+ for (int i=0; i<children(); i++) {
+ int action_x = ((geAction*)child(i))->x();
+ int action_w = ((geAction*)child(i))->w();
+ if (Fl::event_x() >= action_x && Fl::event_x() <= action_x + action_w)
+ return (geAction*)child(i);
+ }
+ return nullptr;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geActionEditor::updateActions()
+{
+ /* when zooming, don't delete and re-add actions, just MOVE them. This
+ * function shifts the action by a zoom factor. Those singlepress are
+ * stretched, as well */
+
+ geAction *a;
+ for (int i=0; i<children(); i++) {
+
+ a = (geAction*)child(i);
+ int newX = x() + (a->frame_a / pParent->zoom);
+
+ if (ch->mode == SINGLE_PRESS) {
+ int newW = ((a->frame_b - a->frame_a) / pParent->zoom);
+ if (newW < geAction::MIN_WIDTH)
+ newW = geAction::MIN_WIDTH;
+ a->resize(newX, a->y(), newW, a->h());
+ }
+ else
+ a->resize(newX, a->y(), geAction::MIN_WIDTH, a->h());
+ }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geActionEditor::draw()
+{
+ /* draw basic boundaries (+ beat bars) and hide the unused area. Then
+ * draw the children (the actions) */
+
+ baseDraw();
+
+ /* print label */
+
+ fl_color(COLOR_BG_1);
+ fl_font(FL_HELVETICA, 12);
+ if (active())
+ fl_draw("start/stop", x()+4, y(), w(), h(), (Fl_Align) (FL_ALIGN_LEFT | FL_ALIGN_CENTER)); /// FIXME h() is too much!
+ else
+ fl_draw("start/stop (disabled)", x()+4, y(), w(), h(), (Fl_Align) (FL_ALIGN_LEFT | FL_ALIGN_CENTER)); /// FIXME h() is too much!
+
+ draw_children();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int geActionEditor::handle(int e)
+{
+ int ret = Fl_Group::handle(e);
+
+ /* do nothing if the widget is deactivated. It could happen for loopmode
+ * channels */
+
+ if (!active())
+ return 1;
+
+ switch (e) {
+
+ case FL_DRAG: {
+
+ if (selected == nullptr) { // if you drag an empty area
+ ret = 1;
+ break;
+ }
+
+ /* if onLeftEdge o onRightEdge are true it means that you're resizing
+ * an action. Otherwise move the widget. */
+
+ if (selected->onLeftEdge || selected->onRightEdge) {
+
+ /* some checks: a) cannot resize an action < N pixels, b) no beyond zero,
+ * c) no beyond bar maxwidth. Checks for overlap are done in FL_RELEASE */
+
+ if (selected->onRightEdge) {
+
+ int aw = Fl::event_x()-selected->x();
+ int ah = selected->h();
+
+ if (Fl::event_x() < selected->x()+geAction::MIN_WIDTH)
+ aw = geAction::MIN_WIDTH;
+ else
+ if (Fl::event_x() > pParent->coverX)
+ aw = pParent->coverX-selected->x();
+
+ selected->size(aw, ah);
+ }
+ else {
+
+ int ax = Fl::event_x();
+ int ay = selected->y();
+ int aw = selected->x()-Fl::event_x()+selected->w();
+ int ah = selected->h();
+
+ if (Fl::event_x() < x()) {
+ ax = x();
+ aw = selected->w()+selected->x()-x();
+ }
+ else
+ if (Fl::event_x() > selected->x()+selected->w()-geAction::MIN_WIDTH) {
+ ax = selected->x()+selected->w()-geAction::MIN_WIDTH;
+ aw = geAction::MIN_WIDTH;
+ }
+ selected->resize(ax, ay, aw, ah);
+ }
+ }
+
+ /* move the widget around */
+
+ else {
+ int real_x = Fl::event_x() - actionPickPoint;
+ if (real_x < x()) // don't go beyond the left border
+ selected->position(x(), selected->y());
+ else
+ if (real_x+selected->w() > pParent->coverX+x()) // don't go beyond the right border
+ selected->position(pParent->coverX+x()-selected->w(), selected->y());
+ else {
+ if (pParent->gridTool->isOn()) {
+ int snpx = pParent->gridTool->getSnapPoint(real_x-x()) + x() -1;
+ selected->position(snpx, selected->y());
+ }
+ else
+ selected->position(real_x, selected->y());
+ }
+ }
+ redraw();
+ ret = 1;
+ break;
+ }
+
+ case FL_PUSH: {
+
+ if (Fl::event_button1()) {
+
+ /* avoid at all costs two overlapping actions. We use 'selected' because
+ * the selected action can be reused in FL_DRAG, in case you want to move
+ * it. */
+
+ selected = getSelectedAction();
+
+ if (selected == nullptr) {
+
+ /* avoid click on grey area */
+
+ if (Fl::event_x() >= pParent->coverX) {
+ ret = 1;
+ break;
+ }
+
+ /* snap function, if enabled */
+
+ int ax = Fl::event_x();
+ int fx = (ax - x()) * pParent->zoom;
+ if (pParent->gridTool->isOn()) {
+ ax = pParent->gridTool->getSnapPoint(ax-x()) + x() -1;
+ fx = pParent->gridTool->getSnapFrame(ax-x());
+
+ /* with snap=on an action can fall onto another */
+
+ if (actionCollides(fx)) {
+ ret = 1;
+ break;
+ }
+ }
+
+ geAction *a = new geAction(
+ ax, // x
+ y()+4, // y
+ h()-8, // h
+ fx, // frame_a
+ recorder::frames.size()-1, // n. of actions recorded
+ pParent, // pParent window pointer
+ ch, // pointer to SampleChannel
+ true, // record = true: record it!
+ pParent->getActionType()); // type of action
+ add(a);
+ G_MainWin->keyboard->setChannelWithActions((geSampleChannel*)ch->guiChannel); // mainWindow update
+ redraw();
+ ret = 1;
+ }
+ else {
+ actionOriginalX = selected->x();
+ actionOriginalW = selected->w();
+ actionPickPoint = Fl::event_x() - selected->x();
+ ret = 1; // for dragging
+ }
+ }
+ else
+ if (Fl::event_button3()) {
+ geAction *a = getSelectedAction();
+ if (a != nullptr) {
+ a->delAction();
+ remove(a);
+ delete a;
+ G_MainWin->keyboard->setChannelWithActions((geSampleChannel*)pParent->chan->guiChannel);
+ redraw();
+ ret = 1;
+ }
+ }
+ break;
+ }
+ case FL_RELEASE: {
+
+ if (selected == nullptr) {
+ ret = 1;
+ break;
+ }
+
+ /* noChanges = true when you click on an action without doing anything
+ * (dragging or moving it). */
+
+ bool noChanges = false;
+ if (actionOriginalX == selected->x())
+ noChanges = true;
+ if (ch->mode == SINGLE_PRESS &&
+ actionOriginalX+actionOriginalW != selected->x()+selected->w())
+ noChanges = false;
+
+ if (noChanges) {
+ ret = 1;
+ selected = nullptr;
+ break;
+ }
+
+ /* step 1: check if the action doesn't overlap with another one.
+ * In case of overlap the moved action returns to the original X
+ * value ("actionOriginalX"). */
+
+ bool overlap = false;
+ for (int i=0; i<children() && !overlap; i++) {
+
+ /* never check against itself. */
+
+ if ((geAction*)child(i) == selected)
+ continue;
+
+ int action_x = ((geAction*)child(i))->x();
+ int action_w = ((geAction*)child(i))->w();
+ if (ch->mode == SINGLE_PRESS) {
+
+ /* when 2 segments overlap?
+ * start = the highest value between the two starting points
+ * end = the lowest value between the two ending points
+ * if start < end then there's an overlap of end-start pixels. */
+
+ int start = action_x >= selected->x() ? action_x : selected->x();
+ int end = action_x+action_w < selected->x()+selected->w() ? action_x+action_w : selected->x()+selected->w();
+ if (start < end) {
+ selected->resize(actionOriginalX, selected->y(), actionOriginalW, selected->h());
+ redraw();
+ overlap = true; // one overlap: stop checking
+ }
+ }
+ else {
+ if (selected->x() == action_x) {
+ selected->position(actionOriginalX, selected->y());
+ redraw();
+ overlap = true; // one overlap: stop checking
+ }
+ }
+ }
+
+ /* step 2: no overlap? then update the coordinates of the action, ie
+ * delete the previous rec and create a new one.
+ * Anyway the selected action becomes nullptr, because when you release
+ * the mouse button the dragging process ends. */
+
+ if (!overlap) {
+ if (pParent->gridTool->isOn()) {
+ int f = pParent->gridTool->getSnapFrame(selected->absx());
+ selected->moveAction(f);
+ }
+ else
+ selected->moveAction();
+ }
+ selected = nullptr;
+ ret = 1;
+ break;
+ }
+ }
+
+ return ret;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+bool geActionEditor::actionCollides(int frame)
+{
+ /* if SINGLE_PRESS we check that the tail (frame_b) of the action doesn't
+ * overlap the head (frame) of the new one. First the general case, yet. */
+
+ bool collision = false;
+
+ for (int i=0; i<children() && !collision; i++)
+ if (((geAction*) child(i))->frame_a == frame)
+ collision = true;
+
+ if (ch->mode == SINGLE_PRESS) {
+ for (int i=0; i<children() && !collision; i++) {
+ geAction *c = ((geAction*) child(i));
+ if (frame <= c->frame_b && frame >= c->frame_a)
+ collision = true;
+ }
+ }
+
+ return collision;
+}
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#ifndef GE_ACTION_EDITOR_H
+#define GE_ACTION_EDITOR_H
+
+
+#include "baseActionEditor.h"
+
+
+class geAction;
+class SampleChannel;
+
+
+class geActionEditor : public geBaseActionEditor
+{
+
+private:
+
+ SampleChannel *ch;
+
+ /* getSelectedAction
+ * get the action under the mouse. nullptr if nothing found. */
+
+ geAction *getSelectedAction();
+
+ /* selected
+ * pointer to the selected action. Useful when dragging around. */
+
+ geAction *selected;
+
+ /* actionOriginalX, actionOriginalW
+ * x and w of the action, when moved. Useful for checking if the action
+ * overlaps another one: in that case the moved action returns to
+ * actionOriginalX (and to actionOriginalW if resized). */
+
+ int actionOriginalX;
+ int actionOriginalW;
+
+ /* actionPickPoint
+ * the precise x point in which the action has been picked with the mouse,
+ * before a dragging action. */
+
+ int actionPickPoint;
+
+
+ /* actionCollides
+ * true if an action collides with another. Used while adding new points
+ * with snap active.*/
+
+ bool actionCollides(int frame);
+
+public:
+
+ geActionEditor(int x, int y, gdActionEditor *pParent, SampleChannel *ch);
+ void draw();
+ int handle(int e);
+ void updateActions();
+};
+
+
+#endif
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * geBaseActionEditor
+ * Parent class of any widget inside the action editor.
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * 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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#include <FL/fl_draw.H>
+#include "../../../core/mixer.h"
+#include "../../../core/const.h"
+#include "../../dialogs/gd_actionEditor.h"
+#include "gridTool.h"
+#include "baseActionEditor.h"
+
+
+geBaseActionEditor::geBaseActionEditor(int x, int y, int w, int h,
+ gdActionEditor *pParent)
+ : Fl_Group(x, y, w, h), pParent(pParent) {}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+geBaseActionEditor::~geBaseActionEditor() {}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geBaseActionEditor::baseDraw(bool clear) {
+
+ /* clear the screen */
+
+ if (clear)
+ fl_rectf(x(), y(), w(), h(), COLOR_BG_MAIN);
+
+ /* draw the container */
+
+ fl_color(COLOR_BD_0);
+ fl_rect(x(), y(), w(), h());
+
+ /* grid drawing, if > 1 */
+
+ if (pParent->gridTool->getValue() > 1) {
+
+ fl_color(fl_rgb_color(54, 54, 54));
+ fl_line_style(FL_DASH, 0, nullptr);
+
+ for (int i=0; i<(int) pParent->gridTool->points.size(); i++) {
+ int px = pParent->gridTool->points.at(i)+x()-1;
+ fl_line(px, y()+1, px, y()+h()-2);
+ }
+ fl_line_style(0);
+ }
+
+ /* bars and beats drawing */
+
+ fl_color(COLOR_BD_0);
+ for (int i=0; i<(int) pParent->gridTool->beats.size(); i++) {
+ int px = pParent->gridTool->beats.at(i)+x()-1;
+ fl_line(px, y()+1, px, y()+h()-2);
+ }
+
+ fl_color(COLOR_BG_2);
+ for (int i=0; i<(int) pParent->gridTool->bars.size(); i++) {
+ int px = pParent->gridTool->bars.at(i)+x()-1;
+ fl_line(px, y()+1, px, y()+h()-2);
+ }
+
+ /* cover unused area. Avoid drawing cover if width == 0 (i.e. beats
+ * are 32) */
+
+ int coverWidth = pParent->totalWidth-pParent->coverX;
+ if (coverWidth != 0)
+ fl_rectf(pParent->coverX+x(), y()+1, coverWidth, h()-2, COLOR_BG_1);
+}
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * ge_actionWidget
+ *
+ * parent class of any tool inside the action editor.
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * 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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#ifndef GE_BASE_ACTION_EDITOR_H
+#define GE_BASE_ACTION_EDITOR_H
+
+
+#include <FL/Fl_Group.H>
+
+
+class gdActionEditor;
+
+
+class geBaseActionEditor : public Fl_Group
+{
+protected:
+
+ gdActionEditor *pParent;
+
+ void baseDraw(bool clear=true);
+
+public:
+
+ virtual void updateActions() = 0;
+
+ geBaseActionEditor(int x, int y, int w, int h, gdActionEditor *pParent);
+ ~geBaseActionEditor();
+};
+
+#endif
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * 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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#include "pianoRoll.h"
+#include "basePianoItem.h"
+
+
+geBasePianoItem::geBasePianoItem(int x, int y, int w, gdActionEditor *pParent)
+ : Fl_Box (x, y, w, gePianoRoll::CELL_H),
+ pParent (pParent),
+ selected(false)
+{
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int geBasePianoItem::handle(int e)
+{
+ int ret = 0;
+ switch (e) {
+ case FL_ENTER:
+ selected = true;
+ redraw();
+ ret = 1;
+ break;
+ case FL_LEAVE:
+ selected = false;
+ redraw();
+ ret = 1;
+ break;
+ }
+ return ret;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int geBasePianoItem::getY(int note)
+{
+ return (gePianoRoll::MAX_KEYS * gePianoRoll::CELL_H) - (note * gePianoRoll::CELL_H);
+}
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#ifndef GE_BASE_PIANO_ITEM_H
+#define GE_BASE_PIANO_ITEM_H
+
+
+#include <FL/Fl_Box.H>
+#include "../../../core/recorder.h"
+
+
+class gdActionEditor;
+
+
+class geBasePianoItem : public Fl_Box
+{
+protected:
+
+ geBasePianoItem(int x, int y, int w, gdActionEditor *pParent);
+
+ /* getY
+ * from a note, return the y position on piano roll */
+
+ int getY(int note);
+
+ gdActionEditor *pParent;
+
+ bool selected;
+
+public:
+
+ virtual void reposition(int pianoRollX) = 0;
+
+ int handle(int e) override;
+};
+
+
+#endif
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * envelopeEditor
+ *
+ * Parent class of any envelope controller, from volume to VST parameter
+ * automations.
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * 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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#include <FL/fl_draw.H>
+#include "../../../core/channel.h"
+#include "../../../core/recorder.h"
+#include "../../../core/mixer.h"
+#include "../../../core/clock.h"
+#include "../../dialogs/gd_actionEditor.h"
+#include "../../dialogs/gd_mainWindow.h"
+#include "../mainWindow/keyboard/keyboard.h"
+#include "gridTool.h"
+#include "envelopeEditor.h"
+
+
+extern gdMainWindow *G_MainWin;
+
+
+using namespace giada::m;
+
+
+geEnvelopeEditor::geEnvelopeEditor(int x, int y, gdActionEditor *pParent,
+ int type, int range, const char *l)
+ : geBaseActionEditor(x, y, 200, 80, pParent),
+ l (l),
+ type (type),
+ range (range),
+ selectedPoint (-1),
+ draggedPoint (-1)
+{
+ size(pParent->totalWidth, h());
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+geEnvelopeEditor::~geEnvelopeEditor() {
+ clearPoints();
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void geEnvelopeEditor::addPoint(int frame, int iValue, float fValue, int px, int py) {
+ point p;
+ p.frame = frame;
+ p.iValue = iValue;
+ p.fValue = fValue;
+ p.x = px;
+ p.y = py;
+ points.push_back(p);
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void geEnvelopeEditor::updateActions() {
+ for (unsigned i=0; i<points.size(); i++)
+ points.at(i).x = points.at(i).frame / pParent->zoom;
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void geEnvelopeEditor::draw() {
+
+ baseDraw();
+
+ /* print label */
+
+ fl_color(COLOR_BG_1);
+ fl_font(FL_HELVETICA, 12);
+ fl_draw(l, x()+4, y(), 80, h(), (Fl_Align) (FL_ALIGN_LEFT));
+
+ int pxOld = x()-3;
+ int pyOld = y()+1;
+ int pxNew = 0;
+ int pyNew = 0;
+
+ fl_color(COLOR_BG_2);
+
+ for (unsigned i=0; i<points.size(); i++) {
+
+ pxNew = points.at(i).x+x()-3;
+ pyNew = points.at(i).y+y();
+
+ if (selectedPoint == (int) i) {
+ fl_color(COLOR_BD_1);
+ fl_rectf(pxNew, pyNew, 7, 7);
+ fl_color(COLOR_BG_2);
+ }
+ else
+ fl_rectf(pxNew, pyNew, 7, 7);
+
+ if (i > 0)
+ fl_line(pxOld+3, pyOld+3, pxNew+3, pyNew+3);
+
+ pxOld = pxNew;
+ pyOld = pyNew;
+ }
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+int geEnvelopeEditor::handle(int e) {
+
+ /* Adding an action: no further checks required, just record it on frame
+ * mx*pParent->zoom. Deleting action is trickier: find the active
+ * point and derive from it the corresponding frame. */
+
+ int ret = 0;
+ int mx = Fl::event_x()-x(); // mouse x
+ int my = Fl::event_y()-y(); // mouse y
+
+ switch (e) {
+
+ case FL_ENTER: {
+ ret = 1;
+ break;
+ }
+
+ case FL_MOVE: {
+ selectedPoint = getSelectedPoint();
+ redraw();
+ ret = 1;
+ break;
+ }
+
+ case FL_LEAVE: {
+ draggedPoint = -1;
+ selectedPoint = -1;
+ redraw();
+ ret = 1;
+ break;
+ }
+
+ case FL_PUSH: {
+
+ /* left click on point: drag
+ * right click on point: delete
+ * left click on void: add */
+
+ if (Fl::event_button1()) {
+
+ if (selectedPoint != -1) {
+ draggedPoint = selectedPoint;
+ }
+ else {
+
+ /* top & border fix */
+
+ if (my > h()-8) my = h()-8;
+ if (mx > pParent->coverX-x()) mx = pParent->coverX-x();
+
+ if (range == G_RANGE_FLOAT) {
+
+ /* if this is the first point ever, add other two points at the beginning
+ * and the end of the range */
+
+ if (points.size() == 0) {
+ addPoint(0, 0, 1.0f, 0, 1);
+ recorder::rec(pParent->chan->index, type, 0, 0, 1.0f);
+ addPoint(clock::getTotalFrames(), 0, 1.0f, pParent->coverX, 1);
+ recorder::rec(pParent->chan->index, type, clock::getTotalFrames(), 0, 1.0f);
+ pParent->chan->hasActions = true;
+ }
+
+ /* line between 2 points y = (x-a) / (b-a); a = h() - 8; b = 1 */
+
+ int frame = mx * pParent->zoom;
+ float value = (my - h() + 8) / (float) (1 - h() + 8);
+ addPoint(frame, 0, value, mx, my);
+ recorder::rec(pParent->chan->index, type, frame, 0, value);
+ pParent->chan->hasActions = true;
+ recorder::sortActions();
+ sortPoints();
+ }
+ else {
+ /// TODO
+ }
+ G_MainWin->keyboard->setChannelWithActions((geSampleChannel*)pParent->chan->guiChannel); // update mainWindow
+ redraw();
+ }
+ }
+ else {
+
+ /* right click on point 0 or point size-1 deletes the entire envelope */
+
+ if (selectedPoint != -1) {
+ if (selectedPoint == 0 || (unsigned) selectedPoint == points.size()-1) {
+ recorder::clearAction(pParent->chan->index, type);
+ pParent->chan->hasActions = recorder::hasActions(pParent->chan->index);
+ points.clear();
+ }
+ else {
+ recorder::deleteAction(pParent->chan->index,
+ points.at(selectedPoint).frame, type, false, &mixer::mutex_recs);
+ pParent->chan->hasActions = recorder::hasActions(pParent->chan->index);
+ recorder::sortActions();
+ points.erase(points.begin() + selectedPoint);
+ }
+ G_MainWin->keyboard->setChannelWithActions((geSampleChannel*)pParent->chan->guiChannel); // update mainWindow
+ redraw();
+ }
+ }
+
+ ret = 1;
+ break;
+ }
+
+ case FL_RELEASE: {
+ if (draggedPoint != -1) {
+
+ if (points.at(draggedPoint).x == previousXPoint) {
+ //gu_log("nothing to do\n");
+ }
+ else {
+ int newFrame = points.at(draggedPoint).x * pParent->zoom;
+
+ /* x edge correction */
+
+ if (newFrame < 0)
+ newFrame = 0;
+ else if (newFrame > clock::getTotalFrames())
+ newFrame = clock::getTotalFrames();
+
+ /* vertical line check */
+
+ int vp = verticalPoint(points.at(draggedPoint));
+ if (vp == 1) newFrame -= 256;
+ else if (vp == -1) newFrame += 256;
+
+ /* delete previous point and record a new one */
+
+ recorder::deleteAction(pParent->chan->index,
+ points.at(draggedPoint).frame, type, false, &mixer::mutex_recs);
+ pParent->chan->hasActions = recorder::hasActions(pParent->chan->index);
+
+ if (range == G_RANGE_FLOAT) {
+ float value = (points.at(draggedPoint).y - h() + 8) / (float) (1 - h() + 8);
+ recorder::rec(pParent->chan->index, type, newFrame, 0, value);
+ pParent->chan->hasActions = true;
+ }
+ else {
+ /// TODO
+ }
+
+ recorder::sortActions();
+ points.at(draggedPoint).frame = newFrame;
+ draggedPoint = -1;
+ selectedPoint = -1;
+ }
+ }
+ ret = 1;
+ break;
+ }
+
+ case FL_DRAG: {
+
+ if (draggedPoint != -1) {
+
+ /* y constraint */
+
+ if (my > h()-8)
+ points.at(draggedPoint).y = h()-8;
+ else
+ if (my < 1)
+ points.at(draggedPoint).y = 1;
+ else
+ points.at(draggedPoint).y = my;
+
+ /* x constraint
+ * constrain the point between two ends (leftBorder-point, point-point,
+ * point-rightBorder). First & last points cannot be shifted on x */
+
+ if (draggedPoint == 0)
+ points.at(draggedPoint).x = x()-8;
+ else
+ if ((unsigned) draggedPoint == points.size()-1)
+ points.at(draggedPoint).x = pParent->coverX;
+ else {
+ int prevPoint = points.at(draggedPoint-1).x;
+ int nextPoint = points.at(draggedPoint+1).x;
+ if (mx <= prevPoint)
+ points.at(draggedPoint).x = prevPoint;
+ else
+ if (mx >= nextPoint)
+ points.at(draggedPoint).x = nextPoint;
+ //else
+ // points.at(draggedPoint).x = mx;
+ else {
+ if (pParent->gridTool->isOn())
+ points.at(draggedPoint).x = pParent->gridTool->getSnapPoint(mx)-1;
+ else
+ points.at(draggedPoint).x = mx;
+ }
+ }
+ redraw();
+ }
+
+ ret = 1;
+ break;
+ }
+ }
+
+ return ret;
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+int geEnvelopeEditor::verticalPoint(const point &p) {
+ for (unsigned i=0; i<points.size(); i++) {
+ if (&p == &points.at(i)) {
+ if (i == 0 || i == points.size()-1) // first or last point
+ return 0;
+ else {
+ if (points.at(i-1).x == p.x) // vertical with point[i-1]
+ return -1;
+ else
+ if (points.at(i+1).x == p.x) // vertical with point[i+1]
+ return 1;
+ }
+ break;
+ }
+ }
+ return 0;
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void geEnvelopeEditor::sortPoints() {
+ for (unsigned i=0; i<points.size(); i++)
+ for (unsigned j=0; j<points.size(); j++)
+ if (points.at(j).x > points.at(i).x)
+ std::swap(points.at(j), points.at(i));
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+int geEnvelopeEditor::getSelectedPoint() {
+
+ /* point is a 7x7 dot */
+
+ for (unsigned i=0; i<points.size(); i++) {
+ if (Fl::event_x() >= points.at(i).x+x()-4 &&
+ Fl::event_x() <= points.at(i).x+x()+4 &&
+ Fl::event_y() >= points.at(i).y+y() &&
+ Fl::event_y() <= points.at(i).y+y()+7)
+ return i;
+ }
+ return -1;
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void geEnvelopeEditor::fill() {
+ points.clear();
+ for (unsigned i=0; i<recorder::global.size(); i++)
+ for (unsigned j=0; j<recorder::global.at(i).size(); j++) {
+ recorder::action *a = recorder::global.at(i).at(j);
+ if (a->type == type && a->chan == pParent->chan->index) {
+ if (range == G_RANGE_FLOAT)
+ addPoint(
+ a->frame, // frame
+ 0, // int value (unused)
+ a->fValue, // float value
+ a->frame / pParent->zoom, // x
+ ((1-h()+8)*a->fValue)+h()-8); // y = (b-a)x + a (line between two points)
+ // else: TODO
+ }
+ }
+
+}
--- /dev/null
+/* ---------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * parent class of any envelope controller, from volume to VST parameter
+ * automations.
+ *
+ * ---------------------------------------------------------------------
+ *
+ * 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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * ------------------------------------------------------------------ */
+
+#ifndef GE_ENVELOPE_EDITOR_H
+#define GE_ENVELOPE_EDITOR_H
+
+
+#include <vector>
+#include "baseActionEditor.h"
+
+
+class geEnvelopeEditor : public geBaseActionEditor
+{
+ const char *l; // internal label
+ int type; // type of action
+ int range;
+
+ /* point
+ * a single dot in the graph. x = relative frame, y = relative value */
+
+ struct point
+ {
+ int frame;
+ int iValue;
+ float fValue;
+ int x;
+ int y;
+ };
+
+ /* points
+ * array of points, filled by fillPoints() */
+
+ std::vector<point> points;
+
+ /* selectedPoint
+ * which point we are selecting? */
+
+ int selectedPoint;
+
+ /* draggedPoint
+ * which point we are dragging? */
+
+ int draggedPoint;
+
+ /* previousXPoint
+ * x coordinate of point at time t-1. Used to check effective shifts */
+
+ int previousXPoint;
+
+ void draw();
+
+ int handle(int e);
+
+ int getSelectedPoint();
+
+ void sortPoints();
+
+ /* verticalPoint
+ * check if two points form a vertical line. In that case the frame value
+ * would be the same and recorder would go crazy, so shift by a small value
+ * of frames to create a minimal fadein/fadeout level. Return 0: no
+ * vertical points; return 1: vertical with the next one, return -1: vertical
+ * with the previous one. */
+
+ int verticalPoint(const point &p);
+
+public:
+
+ geEnvelopeEditor(int x, int y, gdActionEditor *pParent, int type, int range,
+ const char *l);
+ ~geEnvelopeEditor();
+
+ /* addPoint
+ * add a point made of frame+value to internal points[]. */
+
+ void addPoint(int frame, int iValue=0, float fValue=0.0f, int x=-1, int y=-1);
+
+ void updateActions();
+
+ /* fill
+ * parse recorder's stack and fill the widget with points. It's up to
+ * the caller to call this method as initialization. */
+
+ void fill();
+
+ inline void clearPoints() { points.clear(); }
+};
+
+#endif
--- /dev/null
+/* -----------------------------------------------------------------------------
+*
+* 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
+* <http://www.gnu.org/licenses/>.
+*
+* --------------------------------------------------------------------------- */
+
+
+#include <cmath>
+#include "../../../core/conf.h"
+#include "../../../core/const.h"
+#include "../../../core/clock.h"
+#include "../../dialogs/gd_actionEditor.h"
+#include "../basics/choice.h"
+#include "../basics/check.h"
+#include "actionEditor.h"
+#include "gridTool.h"
+
+
+using namespace giada::m;
+
+
+geGridTool::geGridTool(int x, int y, gdActionEditor *parent)
+ : Fl_Group(x, y, 80, 20), parent(parent)
+{
+ gridType = new geChoice(x, y, 40, 20);
+ gridType->add("1");
+ gridType->add("2");
+ gridType->add("3");
+ gridType->add("4");
+ gridType->add("6");
+ gridType->add("8");
+ gridType->add("16");
+ gridType->add("32");
+ gridType->value(0);
+ gridType->callback(cb_changeType, (void*)this);
+
+ active = new geCheck (x+44, y+4, 12, 12);
+
+ gridType->value(conf::actionEditorGridVal);
+ active->value(conf::actionEditorGridOn);
+
+ end();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+geGridTool::~geGridTool()
+{
+ conf::actionEditorGridVal = gridType->value();
+ conf::actionEditorGridOn = active->value();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geGridTool::cb_changeType(Fl_Widget *w, void *p) { ((geGridTool*)p)->__cb_changeType(); }
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geGridTool::__cb_changeType()
+{
+ calc();
+ parent->redraw();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+bool geGridTool::isOn()
+{
+ return active->value();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int geGridTool::getValue()
+{
+ switch (gridType->value()) {
+ case 0: return 1;
+ case 1: return 2;
+ case 2: return 3;
+ case 3: return 4;
+ case 4: return 6;
+ case 5: return 8;
+ case 6: return 16;
+ case 7: return 32;
+ }
+ return 0;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geGridTool::calc()
+{
+ points.clear();
+ frames.clear();
+ bars.clear();
+ beats.clear();
+
+ /* find beats, bars and grid. The method is the same of the waveform in sample
+ * editor. Take totalwidth (the width in pixel of the area to draw), knowing
+ * that totalWidth = totalFrames / zoom. Then, for each pixel of totalwidth,
+ * put a concentrate of each block (which is totalFrames / zoom) */
+
+ int j = 0;
+ int fpgc = floor(clock::getFramesPerBeat() / getValue()); // frames per grid cell
+
+ for (int i=1; i<parent->totalWidth; i++) { // if i=0, step=0 -> useless cycle
+ int step = parent->zoom*i;
+ while (j < step && j < clock::getTotalFrames()) {
+ if (j % fpgc == 0) {
+ points.push_back(i);
+ frames.push_back(j);
+ }
+ if (j % clock::getFramesPerBeat() == 0)
+ beats.push_back(i);
+ if (j % clock::getFramesPerBar() == 0 && i != 1)
+ bars.push_back(i);
+ if (j == clock::getTotalFrames() - 1)
+ parent->coverX = i;
+ j++;
+ }
+ j = step;
+ }
+
+ /* fix coverX if == 0, which means G_Mixer.beats == G_MAX_BEATS */
+
+ if (clock::getBeats() == G_MAX_BEATS)
+ parent->coverX = parent->totalWidth;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int geGridTool::getSnapPoint(int v)
+{
+ if (v == 0) return 0;
+
+ for (int i=0; i<(int)points.size(); i++) {
+
+ if (i == (int) points.size()-1)
+ return points.at(i);
+
+ int gp = points.at(i);
+ int gpn = points.at(i+1);
+
+ if (v >= gp && v < gpn)
+ return gp;
+ }
+ return v; // default value
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int geGridTool::getSnapFrame(int v)
+{
+ v *= parent->zoom; // transformation pixel -> frame
+
+ for (int i=0; i<(int)frames.size(); i++) {
+
+ if (i == (int) frames.size()-1)
+ return frames.at(i);
+
+ int gf = frames.at(i); // grid frame
+ int gfn = frames.at(i+1); // grid frame next
+
+ if (v >= gf && v < gfn) {
+
+ /* which one is the closest? gf < v < gfn */
+
+ if ((gfn - v) < (v - gf))
+ return gfn;
+ else
+ return gf;
+ }
+ }
+ return v; // default value
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int geGridTool::getCellSize()
+{
+ return (parent->coverX - parent->ac->x()) / clock::getBeats() / getValue();
+}
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * 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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#ifndef GE_GRID_TOOL_H
+#define GE_GRID_TOOL_H
+
+
+#include <vector>
+#include <FL/Fl_Group.H>
+
+
+class geChoice;
+class geCheck;
+class gdActionEditor;
+
+
+class geGridTool : public Fl_Group
+{
+private:
+
+ geChoice *gridType;
+ geCheck *active;
+
+ gdActionEditor *parent;
+
+ static void cb_changeType(Fl_Widget *w, void *p);
+ inline void __cb_changeType();
+
+public:
+
+ geGridTool(int x, int y, gdActionEditor *parent);
+ ~geGridTool();
+
+ int getValue();
+ bool isOn();
+ void calc();
+
+ /* getSnapPoint
+ * given a cursor position in input, return the x coordinates of the
+ * nearest snap point (in pixel, clean, ie. not x()-shifted) */
+
+ int getSnapPoint(int v);
+ int getSnapFrame(int v);
+
+ /* getCellSize
+ * return the size in pixel of a single cell of the grid. */
+
+ int getCellSize();
+
+ std::vector<int> points; // points of the grid
+ std::vector<int> frames; // frames of the grid
+
+ std::vector<int> bars;
+ std::vector<int> beats;
+};
+
+
+#endif
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * 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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#include <FL/fl_draw.H>
+#include "../../../core/recorder.h"
+#include "../../../core/mixer.h"
+#include "../../../core/channel.h"
+#include "../../../core/clock.h"
+#include "../../../glue/main.h"
+#include "../../../utils/log.h"
+#include "../../dialogs/gd_actionEditor.h"
+#include "../../dialogs/gd_mainWindow.h"
+#include "../mainWindow/keyboard/keyboard.h"
+#include "gridTool.h"
+#include "muteEditor.h"
+
+
+extern gdMainWindow *G_MainWin;
+
+
+using namespace giada::m;
+
+
+geMuteEditor::geMuteEditor(int x, int y, gdActionEditor *pParent)
+ : geBaseActionEditor(x, y, 200, 80, pParent),
+ draggedPoint (-1),
+ selectedPoint (-1)
+{
+ size(pParent->totalWidth, h());
+ extractPoints();
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void geMuteEditor::draw()
+{
+ baseDraw();
+
+ /* print label */
+
+ fl_color(COLOR_BG_1);
+ fl_font(FL_HELVETICA, 12);
+ fl_draw("mute", x()+4, y(), w(), h(), (Fl_Align) (FL_ALIGN_LEFT | FL_ALIGN_CENTER));
+
+ /* draw "on" and "off" labels. Must stay in background */
+
+ fl_color(COLOR_BG_1);
+ fl_font(FL_HELVETICA, 9);
+ fl_draw("on", x()+4, y(), w(), h(), (Fl_Align) (FL_ALIGN_LEFT | FL_ALIGN_TOP));
+ fl_draw("off", x()+4, y()+h()-14, w(), h(), (Fl_Align) (FL_ALIGN_LEFT | FL_ALIGN_TOP));
+
+ /* draw on-off points. On = higher rect, off = lower rect. It always
+ * starts with a note_off */
+
+ fl_color(COLOR_BG_2);
+
+ int pxOld = x()+1;
+ int pxNew = 0;
+ int py = y()+h()-5;
+ int pyDot = py-6;
+
+ for (unsigned i=0; i<points.size(); i++) {
+
+ /* next px */
+
+ pxNew = points.at(i).x+x();
+
+ /* draw line from pxOld to pxNew.
+ * i % 2 == 0: first point, mute_on
+ * i % 2 != 0: second point, mute_off */
+
+ fl_line(pxOld, py, pxNew, py);
+ pxOld = pxNew;
+
+ py = i % 2 == 0 ? y()+4 : y()+h()-5;
+
+ /* draw dots (handles) */
+
+ fl_line(pxNew, y()+h()-5, pxNew, y()+4);
+
+ if (selectedPoint == (int) i) {
+ fl_color(COLOR_BD_1);
+ fl_rectf(pxNew-3, pyDot, 7, 7);
+ fl_color(COLOR_BG_2);
+ }
+ else
+ fl_rectf(pxNew-3, pyDot, 7, 7);
+ }
+
+ /* last section */
+
+ py = y()+h()-5;
+ fl_line(pxNew+3, py, pParent->coverX+x()-1, py);
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void geMuteEditor::extractPoints()
+{
+ points.clear();
+
+ /* actions are already sorted by recorder::sortActions() */
+
+ for (unsigned i=0; i<recorder::frames.size(); i++) {
+ for (unsigned j=0; j<recorder::global.at(i).size(); j++) {
+ if (recorder::global.at(i).at(j)->chan == pParent->chan->index) {
+ if (recorder::global.at(i).at(j)->type & (G_ACTION_MUTEON | G_ACTION_MUTEOFF)) {
+ point p;
+ p.frame = recorder::frames.at(i);
+ p.type = recorder::global.at(i).at(j)->type;
+ p.x = p.frame / pParent->zoom;
+ points.push_back(p);
+ //gu_log("[geMuteEditor::extractPoints] point found, type=%d, frame=%d\n", p.type, p.frame);
+ }
+ }
+ }
+ }
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void geMuteEditor::updateActions() {
+ for (unsigned i=0; i<points.size(); i++)
+ points.at(i).x = points.at(i).frame / pParent->zoom;
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+int geMuteEditor::handle(int e) {
+
+ int ret = 0;
+ int mouseX = Fl::event_x()-x();
+
+ switch (e) {
+
+ case FL_ENTER: {
+ ret = 1;
+ break;
+ }
+
+ case FL_MOVE: {
+ selectedPoint = getSelectedPoint();
+ redraw();
+ ret = 1;
+ break;
+ }
+
+ case FL_LEAVE: {
+ draggedPoint = -1;
+ selectedPoint = -1;
+ redraw();
+ ret = 1;
+ break;
+ }
+
+ case FL_PUSH: {
+
+ /* left click on point: drag
+ * right click on point: delete
+ * left click on void: add */
+
+ if (Fl::event_button1()) {
+
+ if (selectedPoint != -1) {
+ draggedPoint = selectedPoint;
+ previousXPoint = points.at(selectedPoint).x;
+ }
+ else {
+
+ /* click on the grey area leads to nowhere */
+
+ if (mouseX > pParent->coverX) {
+ ret = 1;
+ break;
+ }
+
+ /* click in the middle of a long mute_on (between two points): new actions
+ * must be added in reverse: first mute_off then mute_on. Let's find the
+ * next point from here. */
+
+ unsigned nextPoint = points.size();
+ for (unsigned i=0; i<points.size(); i++) {
+ if (mouseX < points.at(i).x) {
+ nextPoint = i;
+ break;
+ }
+ }
+
+ /* next point odd = mute_on [click here] mute_off
+ * next point even = mute_off [click here] mute_on */
+
+ int frame_a = mouseX * pParent->zoom;
+ int frame_b = frame_a+2048;
+
+ if (pParent->gridTool->isOn()) {
+ frame_a = pParent->gridTool->getSnapFrame(mouseX);
+ frame_b = pParent->gridTool->getSnapFrame(mouseX + pParent->gridTool->getCellSize());
+
+ /* with snap=on a point can fall onto another */
+
+ if (pointCollides(frame_a) || pointCollides(frame_b)) {
+ ret = 1;
+ break;
+ }
+ }
+
+ /* ensure frame parity */
+
+ if (frame_a % 2 != 0) frame_a++;
+ if (frame_b % 2 != 0) frame_b++;
+
+ /* avoid overflow: frame_b must be within the sequencer range. In that
+ * case shift the ON-OFF block */
+
+ if (frame_b >= clock::getTotalFrames()) {
+ frame_b = clock::getTotalFrames();
+ frame_a = frame_b-2048;
+ }
+
+ if (nextPoint % 2 != 0) {
+ recorder::rec(pParent->chan->index, G_ACTION_MUTEOFF, frame_a);
+ recorder::rec(pParent->chan->index, G_ACTION_MUTEON, frame_b);
+ }
+ else {
+ recorder::rec(pParent->chan->index, G_ACTION_MUTEON, frame_a);
+ recorder::rec(pParent->chan->index, G_ACTION_MUTEOFF, frame_b);
+ }
+ pParent->chan->hasActions = true;
+ recorder::sortActions();
+
+ G_MainWin->keyboard->setChannelWithActions((geSampleChannel*)pParent->chan->guiChannel); // update mainWindow
+ extractPoints();
+ redraw();
+ }
+ }
+ else {
+
+ /* delete points pair */
+
+ if (selectedPoint != -1) {
+
+ unsigned a;
+ unsigned b;
+
+ if (points.at(selectedPoint).type == G_ACTION_MUTEOFF) {
+ a = selectedPoint-1;
+ b = selectedPoint;
+ }
+ else {
+ a = selectedPoint;
+ b = selectedPoint+1;
+ }
+
+ //gu_log("selected: a=%d, b=%d >>> frame_a=%d, frame_b=%d\n",
+ // a, b, points.at(a).frame, points.at(b).frame);
+
+ recorder::deleteAction(pParent->chan->index, points.at(a).frame,
+ points.at(a).type, false, &mixer::mutex_recs); // false = don't check vals
+ recorder::deleteAction(pParent->chan->index, points.at(b).frame,
+ points.at(b).type, false, &mixer::mutex_recs); // false = don't check vals
+ pParent->chan->hasActions = recorder::hasActions(pParent->chan->index);
+
+ recorder::sortActions();
+
+ G_MainWin->keyboard->setChannelWithActions((geSampleChannel*)pParent->chan->guiChannel); // update mainWindow
+ extractPoints();
+ redraw();
+ }
+ }
+ ret = 1;
+ break;
+ }
+
+ case FL_RELEASE: {
+
+ if (draggedPoint != -1) {
+
+ if (points.at(draggedPoint).x == previousXPoint) {
+ //gu_log("nothing to do\n");
+ }
+ else {
+
+ int newFrame = points.at(draggedPoint).x * pParent->zoom;
+
+ recorder::deleteAction(pParent->chan->index,
+ points.at(draggedPoint).frame, points.at(draggedPoint).type, false,
+ &mixer::mutex_recs); // don't check values
+ pParent->chan->hasActions = recorder::hasActions(pParent->chan->index);
+
+ recorder::rec(
+ pParent->chan->index,
+ points.at(draggedPoint).type,
+ newFrame);
+
+ pParent->chan->hasActions = true;
+ recorder::sortActions();
+
+ points.at(draggedPoint).frame = newFrame;
+ }
+ }
+ draggedPoint = -1;
+ selectedPoint = -1;
+
+ ret = 1;
+ break;
+ }
+
+ case FL_DRAG: {
+
+ if (draggedPoint != -1) {
+
+ /* constrain the point between two ends (leftBorder-point,
+ * point-point, point-rightBorder) */
+
+ int prevPoint;
+ int nextPoint;
+
+ if (draggedPoint == 0) {
+ prevPoint = 0;
+ nextPoint = points.at(draggedPoint+1).x - 1;
+ if (pParent->gridTool->isOn())
+ nextPoint -= pParent->gridTool->getCellSize();
+ }
+ else
+ if ((unsigned) draggedPoint == points.size()-1) {
+ prevPoint = points.at(draggedPoint-1).x + 1;
+ nextPoint = pParent->coverX-x();
+ if (pParent->gridTool->isOn())
+ prevPoint += pParent->gridTool->getCellSize();
+ }
+ else {
+ prevPoint = points.at(draggedPoint-1).x + 1;
+ nextPoint = points.at(draggedPoint+1).x - 1;
+ if (pParent->gridTool->isOn()) {
+ prevPoint += pParent->gridTool->getCellSize();
+ nextPoint -= pParent->gridTool->getCellSize();
+ }
+ }
+
+ if (mouseX <= prevPoint)
+ points.at(draggedPoint).x = prevPoint;
+ else
+ if (mouseX >= nextPoint)
+ points.at(draggedPoint).x = nextPoint;
+ else
+ if (pParent->gridTool->isOn())
+ points.at(draggedPoint).x = pParent->gridTool->getSnapPoint(mouseX)-1;
+ else
+ points.at(draggedPoint).x = mouseX;
+
+ redraw();
+ }
+ ret = 1;
+ break;
+ }
+ }
+
+
+ return ret;
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+bool geMuteEditor::pointCollides(int frame) {
+ for (unsigned i=0; i<points.size(); i++)
+ if (frame == points.at(i).frame)
+ return true;
+ return false;
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+int geMuteEditor::getSelectedPoint() {
+
+ /* point is a 7x7 dot */
+
+ for (unsigned i=0; i<points.size(); i++) {
+ if (Fl::event_x() >= points.at(i).x+x()-3 &&
+ Fl::event_x() <= points.at(i).x+x()+3)
+ return i;
+ }
+ return -1;
+}
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#ifndef GE_MUTE_TOOL_H
+#define GE_MUTE_TOOL_H
+
+
+#include <vector>
+#include "baseActionEditor.h"
+
+
+class gdActionEditor;
+
+
+class geMuteEditor : public geBaseActionEditor
+{
+private:
+
+ /* point
+ * a single dot in the graph. */
+
+ struct point
+ {
+ int frame;
+ char type;
+ int x;
+ };
+
+ /* points
+ * array of on/off points, in frames */
+
+ std::vector<point> points;
+
+ /* draggedPoint
+ * which point we are dragging? */
+
+ int draggedPoint;
+
+ /* selectedPoint
+ * which point we are selecting? */
+
+ int selectedPoint;
+
+ /* previousXPoint
+ * x coordinate of point at time t-1. Used to check effective shifts */
+
+ int previousXPoint;
+
+ /* extractPoints
+ * va a leggere l'array di azioni di Recorder ed estrae tutti i punti
+ * interessanti mute_on o mute_off. Li mette poi nel vector points. */
+ void extractPoints();
+
+ /* getSelectedPoint
+ * ritorna l'indice di points[] in base al punto selezionato (quello
+ * con il mouse hover). Ritorna -1 se non trova niente. */
+ int getSelectedPoint();
+
+ /* pointCollides
+ * true if a point collides with another. Used while adding new points
+ * with snap active.*/
+
+ bool pointCollides(int frame);
+
+public:
+
+ geMuteEditor(int x, int y, gdActionEditor *pParent);
+ void draw();
+ int handle(int e);
+
+ /* updateActions
+ * calculates new points affected by the zoom. Call this one after
+ * each zoom update. */
+
+ void updateActions();
+};
+
+#endif
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * 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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#include <FL/fl_draw.H>
+#include "../../../core/const.h"
+#include "../../../core/conf.h"
+#include "../../../utils/log.h"
+#include "../../dialogs/gd_actionEditor.h"
+#include "pianoItem.h"
+#include "pianoRoll.h"
+#include "noteEditor.h"
+
+
+using namespace giada::m;
+
+
+geNoteEditor::geNoteEditor(int x, int y, gdActionEditor *pParent)
+ : Fl_Scroll(x, y, 200, 422),
+ pParent (pParent)
+{
+ size(pParent->totalWidth, conf::pianoRollH);
+ pianoRoll = new gePianoRoll(x, y, pParent->totalWidth, pParent);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+geNoteEditor::~geNoteEditor()
+{
+ clear();
+ conf::pianoRollH = h();
+ conf::pianoRollY = pianoRoll->y();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geNoteEditor::updateActions()
+{
+ pianoRoll->updateActions();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geNoteEditor::draw()
+{
+ pianoRoll->size(this->w(), pianoRoll->h()); /// <--- not optimal
+
+ /* clear background */
+
+ fl_rectf(x(), y(), w(), h(), COLOR_BG_MAIN);
+
+ /* clip pianoRoll to pianoRollContainer size */
+
+ fl_push_clip(x(), y(), w(), h());
+ draw_child(*pianoRoll);
+ fl_pop_clip();
+
+ fl_color(COLOR_BD_0);
+ fl_line_style(0);
+ fl_rect(x(), y(), pParent->totalWidth, h());
+}
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * 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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#ifndef GE_NOTE_EDITOR_H
+#define GE_NOTE_EDITOR_H
+
+
+#include <FL/Fl_Scroll.H>
+
+
+class gdActionEditor;
+class gePianoRoll;
+
+
+class geNoteEditor : public Fl_Scroll
+{
+private:
+
+ gdActionEditor *pParent;
+ gePianoRoll *pianoRoll;
+
+public:
+
+ geNoteEditor(int x, int y, gdActionEditor *parent);
+ ~geNoteEditor();
+ void draw();
+ void updateActions();
+};
+
+
+#endif
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * 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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#include "../../../core/kernelMidi.h"
+#include "../../../core/const.h"
+#include "../../../core/mixer.h"
+#include "../../../core/channel.h"
+#include "../../../core/clock.h"
+#include "../../../core/midiChannel.h"
+#include "../../dialogs/gd_actionEditor.h"
+#include "noteEditor.h"
+#include "pianoRoll.h"
+#include "gridTool.h"
+#include "pianoItem.h"
+
+
+using namespace giada::m;
+
+
+gePianoItem::gePianoItem(int X, int Y, int rel_x, int rel_y, recorder::action *_a,
+ recorder::action *_b, gdActionEditor *pParent)
+ : geBasePianoItem(X, Y, MIN_WIDTH, pParent),
+ a (_a),
+ b (_b),
+ event_a (0x00),
+ event_b (0x00),
+ changed (false)
+{
+ /* a is a pointer: action exists, needs to be displayed */
+
+ if (a) {
+ note = kernelMidi::getB2(a->iValue);
+ frame_a = a->frame;
+ frame_b = b->frame;
+ event_a = a->iValue;
+ event_b = b->iValue;
+ int newX = rel_x + (frame_a / pParent->zoom);
+ int newY = rel_y + getY(note);
+ int newW = (frame_b - frame_a) / pParent->zoom;
+ resize(newX, newY, newW, h());
+ }
+
+ /* a is null: action needs to be recorded from scratch */
+
+ else {
+ note = getNote(rel_y);
+ frame_a = rel_x * pParent->zoom;
+ frame_b = (rel_x + 20) * pParent->zoom;
+ record();
+ size((frame_b - frame_a) / pParent->zoom, h());
+ }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gePianoItem::reposition(int pianoRollX)
+{
+ int newX = pianoRollX + (frame_a / pParent->zoom);
+ int newW = ((frame_b - frame_a) / pParent->zoom);
+ if (newW < MIN_WIDTH)
+ newW = MIN_WIDTH;
+ resize(newX, y(), newW, h());
+ redraw();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+bool gePianoItem::overlap()
+{
+ /* when 2 segments overlap?
+ * start = the highest value between the two starting points
+ * end = the lowest value between the two ending points
+ * if start < end then there's an overlap of end-start pixels. */
+
+ geNoteEditor *pPiano = static_cast<geNoteEditor*>(parent());
+
+ for (int i=0; i<pPiano->children(); i++) {
+
+ gePianoItem *pItem = static_cast<gePianoItem*>(pPiano->child(i));
+
+ /* don't check against itself and with different y positions */
+
+ if (pItem == this || pItem->y() != y())
+ continue;
+
+ int start = pItem->x() >= x() ? pItem->x() : x();
+ int end = pItem->x()+pItem->w() < x()+w() ? pItem->x()+pItem->w() : x()+w();
+ if (start < end)
+ return true;
+ }
+
+ return false;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gePianoItem::draw()
+{
+ int _w = w() > MIN_WIDTH ? w() : MIN_WIDTH;
+ fl_rectf(x(), y()+2, _w, h()-3, (Fl_Color) selected ? COLOR_BD_1 : COLOR_BG_2);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gePianoItem::record()
+{
+ /* avoid frame overflow */
+
+ int overflow = frame_b - clock::getTotalFrames();
+ if (overflow > 0) {
+ frame_b -= overflow;
+ frame_a -= overflow;
+ }
+
+ event_a |= (MIDI_NOTE_ON);
+ event_a |= (note << 16); // note value
+ event_a |= (MIDI_VELOCITY);
+ event_a |= (0x00);
+
+ event_b |= (MIDI_NOTE_OFF);
+ event_b |= (note << 16); // note value
+ event_b |= (MIDI_VELOCITY);
+ event_b |= (0x00);
+
+ recorder::rec(pParent->chan->index, G_ACTION_MIDI, frame_a, event_a);
+ recorder::rec(pParent->chan->index, G_ACTION_MIDI, frame_b, event_b);
+ pParent->chan->hasActions = true;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gePianoItem::remove()
+{
+ MidiChannel *ch = static_cast<MidiChannel*>(pParent->chan);
+ recorder::deleteAction(ch->index, frame_a, G_ACTION_MIDI, true,
+ &mixer::mutex_recs, event_a, 0.0);
+ recorder::deleteAction(ch->index, frame_b, G_ACTION_MIDI, true,
+ &mixer::mutex_recs, event_b, 0.0);
+
+ /* Send a note-off in case we are deleting it in a middle of a key_on/key_off
+ sequence. */
+
+ ch->sendMidi(event_b);
+ ch->hasActions = recorder::hasActions(ch->index);
+ static_cast<gePianoRoll*>(parent())->cursorOnItem = false;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int gePianoItem::handle(int e)
+{
+ int ret = 0;
+
+ switch (e) {
+
+ case FL_ENTER: {
+ static_cast<gePianoRoll*>(parent())->cursorOnItem = true;
+ selected = true;
+ ret = 1;
+ redraw();
+ break;
+ }
+
+ case FL_LEAVE: {
+ fl_cursor(FL_CURSOR_DEFAULT, FL_WHITE, FL_BLACK);
+ static_cast<gePianoRoll*>(parent())->cursorOnItem = false;
+ selected = false;
+ ret = 1;
+ redraw();
+ break;
+ }
+
+ case FL_MOVE: {
+ onLeftEdge = false;
+ onRightEdge = false;
+
+ if (Fl::event_x() >= x() && Fl::event_x() < x()+HANDLE_WIDTH) {
+ onLeftEdge = true;
+ fl_cursor(FL_CURSOR_WE, FL_WHITE, FL_BLACK);
+ }
+ else
+ if (Fl::event_x() >= x()+w()-HANDLE_WIDTH && Fl::event_x() <= x()+w()) {
+ onRightEdge = true;
+ fl_cursor(FL_CURSOR_WE, FL_WHITE, FL_BLACK);
+ }
+ else
+ fl_cursor(FL_CURSOR_DEFAULT, FL_WHITE, FL_BLACK);
+
+ ret = 1;
+ break;
+ }
+
+ case FL_PUSH: {
+
+ push_x = Fl::event_x() - x();
+ old_x = x();
+ old_w = w();
+
+ if (Fl::event_button3()) {
+ fl_cursor(FL_CURSOR_DEFAULT, FL_WHITE, FL_BLACK);
+ remove();
+ hide(); // for Windows
+ Fl::delete_widget(this);
+ static_cast<geNoteEditor*>(parent())->redraw();
+ }
+ ret = 1;
+ break;
+ }
+
+ case FL_DRAG: {
+
+ changed = true;
+
+ geNoteEditor *pr = static_cast<geNoteEditor*>(parent());
+ int coverX = pParent->coverX + pr->x(); // relative coverX
+ int nx, ny, nw;
+
+ if (onLeftEdge) {
+ nx = Fl::event_x();
+ ny = y();
+ nw = x()-Fl::event_x()+w();
+ if (nx < pr->x()) {
+ nx = pr->x();
+ nw = w()+x()-pr->x();
+ }
+ else
+ if (nx > x()+w()-MIN_WIDTH) {
+ nx = x()+w()-MIN_WIDTH;
+ nw = MIN_WIDTH;
+ }
+ resize(nx, ny, nw, h());
+ }
+ else
+ if (onRightEdge) {
+ nw = Fl::event_x()-x();
+ if (Fl::event_x() < x()+MIN_WIDTH)
+ nw = MIN_WIDTH;
+ else
+ if (Fl::event_x() > coverX)
+ nw = coverX-x();
+ size(nw, h());
+ }
+ else {
+ nx = Fl::event_x() - push_x;
+ if (nx < pr->x()+1)
+ nx = pr->x()+1;
+ else
+ if (nx+w() > coverX)
+ nx = coverX-w();
+
+ /* snapping */
+
+ if (pParent->gridTool->isOn())
+ nx = pParent->gridTool->getSnapPoint(nx-pr->x()) + pr->x() - 1;
+
+ position(nx, y());
+ }
+
+ /* update screen */
+
+ redraw();
+ static_cast<geNoteEditor*>(parent())->redraw();
+ ret = 1;
+ break;
+ }
+
+ case FL_RELEASE: {
+
+ /* delete & record the action, only if it doesn't overlap with
+ * another one */
+
+ if (overlap()) {
+ resize(old_x, y(), old_w, h());
+ redraw();
+ }
+ else
+ if (changed) {
+ remove();
+ note = getNote(getRelY());
+ frame_a = getRelX() * pParent->zoom;
+ frame_b = (getRelX()+w()) * pParent->zoom;
+ record();
+ changed = false;
+ }
+
+ static_cast<geNoteEditor*>(parent())->redraw();
+
+ ret = 1;
+ break;
+ }
+ }
+ return ret;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int gePianoItem::getNote(int rel_y)
+{
+ return gePianoRoll::MAX_KEYS - (rel_y / gePianoRoll::CELL_H);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int gePianoItem::getRelY()
+{
+ return y() - parent()->y();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int gePianoItem::getRelX()
+{
+ return x() - parent()->x();
+}
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * 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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#ifndef GE_PIANO_ITEM_H
+#define GE_PIANO_ITEM_H
+
+
+#include "../../../core/recorder.h"
+#include "basePianoItem.h"
+
+
+class gdActionEditor;
+
+
+class gePianoItem : public geBasePianoItem
+{
+private:
+
+ /* getRelX/Y
+ * return x/y point of this item, relative to piano roll (and not to
+ * entire screen) */
+
+ int getRelY();
+ int getRelX();
+
+ /* getNote
+ * from a relative_y return the real MIDI note, range 0-127. 15 is
+ * the hardcoded value for note height in pixels */
+
+ int getNote(int rel_y);
+
+ /* overlap
+ * check if this item don't overlap with another one. */
+
+ bool overlap();
+
+ struct giada::m::recorder::action *a;
+ struct giada::m::recorder::action *b;
+
+ int push_x;
+
+ /* MIDI note, start frame, end frame - Used only if it's a newly added
+ * action */ /** FIXME - is it true? */
+
+ int note;
+ int frame_a;
+ int frame_b;
+
+ /* event - bitmasked MIDI events, generated by record() or by ctor if
+ * not newly added action */
+
+ int event_a;
+ int event_b;
+
+ /* changed - if Item has been moved or resized: re-recording needed */
+
+ bool changed;
+
+ /* onLeft,RightEdge - if cursor is on a widget's edge */
+
+ bool onLeftEdge;
+ bool onRightEdge;
+
+ /* old_x, old_w - store previous width and position while dragging
+ * and moving, in order to restore it if overlap */
+
+ int old_x, old_w;
+
+public:
+
+ static const int MIN_WIDTH = 10;
+ static const int HANDLE_WIDTH = 5;
+
+ /* pianoItem ctor
+ * if action *a == nullptr, record a new action */
+
+ gePianoItem(int x, int y, int rel_x, int rel_y,
+ struct giada::m::recorder::action *a, struct giada::m::recorder::action *b,
+ gdActionEditor *pParent);
+
+ void draw() override;
+ int handle(int e) override;
+
+ void reposition(int pianoRollX) override;
+
+ void record();
+ void remove();
+};
+
+
+#endif
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * 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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#include "../../../core/const.h"
+#include "../../../core/kernelMidi.h"
+#include "../../../core/recorder.h"
+#include "../../../core/channel.h"
+#include "../../../core/midiChannel.h"
+#include "../../../core/mixer.h"
+#include "../../dialogs/gd_actionEditor.h"
+#include "pianoRoll.h"
+#include "noteEditor.h"
+#include "pianoItemOrphaned.h"
+
+
+using namespace giada::m;
+
+
+gePianoItemOrphaned::gePianoItemOrphaned(int x, int y, int xRel, int yRel,
+ recorder::action *action, gdActionEditor *pParent)
+ : geBasePianoItem(x, y, WIDTH, pParent),
+ action (action)
+{
+ note = kernelMidi::getB2(action->iValue);
+ frame = action->frame;
+ event = action->iValue;
+ int newX = xRel + (frame / pParent->zoom);
+ int newY = yRel + getY(note);
+ resize(newX, newY, w(), h());
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gePianoItemOrphaned::reposition(int pianoRollX)
+{
+ int newX = pianoRollX + (frame / pParent->zoom);
+ resize(newX, y(), WIDTH, h());
+ redraw();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int gePianoItemOrphaned::handle(int e)
+{
+ int ret = geBasePianoItem::handle(e);
+ if (e == FL_PUSH) {
+ remove();
+ ret = 1;
+ }
+ return ret;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gePianoItemOrphaned::remove()
+{
+ MidiChannel *ch = static_cast<MidiChannel*>(pParent->chan);
+ recorder::deleteAction(ch->index, frame, G_ACTION_MIDI, true,
+ &mixer::mutex_recs, event, 0.0);
+ hide(); // for Windows
+ Fl::delete_widget(this);
+ ch->hasActions = recorder::hasActions(ch->index);
+ static_cast<geNoteEditor*>(parent())->redraw();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gePianoItemOrphaned::draw()
+{
+ fl_rect(x(), y()+2, WIDTH, h()-3, (Fl_Color) selected ? COLOR_BD_1 : COLOR_BG_2);
+}
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * 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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#ifndef GE_PIANO_ITEM_ORPHANED_H
+#define GE_PIANO_ITEM_ORPHANED_H
+
+
+#include "../../../core/recorder.h"
+#include "basePianoItem.h"
+
+
+class gdActionEditor;
+
+
+class gePianoItemOrphaned : public geBasePianoItem
+{
+private:
+
+ struct giada::m::recorder::action *action;
+
+ int note;
+ int frame;
+ int event;
+
+public:
+
+ static const int WIDTH = 12;
+
+ gePianoItemOrphaned(int x, int y, int xRel, int yRel,
+ struct giada::m::recorder::action *action, gdActionEditor *pParent);
+
+ void draw() override;
+ int handle(int e) override;
+
+ void reposition(int pianoRollX) override;
+
+ void remove();
+};
+
+
+#endif
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * 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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#include "../../../core/conf.h"
+#include "../../../core/const.h"
+#include "../../../core/mixer.h"
+#include "../../../core/clock.h"
+#include "../../../core/channel.h"
+#include "../../../core/recorder.h"
+#include "../../../core/kernelMidi.h"
+#include "../../../utils/log.h"
+#include "../../dialogs/gd_actionEditor.h"
+#include "pianoItem.h"
+#include "pianoItemOrphaned.h"
+#include "noteEditor.h"
+#include "pianoRoll.h"
+
+
+using namespace giada::m;
+
+
+gePianoRoll::gePianoRoll(int X, int Y, int W, gdActionEditor *pParent)
+ : geBaseActionEditor(X, Y, W, 40, pParent),
+ cursorOnItem (false)
+{
+ resizable(nullptr); // don't resize children (i.e. pianoItem)
+ size(W, (MAX_KEYS+1) * CELL_H); // 128 MIDI channels * CELL_H height
+
+ if (conf::pianoRollY == -1)
+ position(x(), y()-(h()/2)); // center
+ else
+ position(x(), conf::pianoRollY);
+
+ drawSurface1();
+ drawSurface2();
+
+ /* Add actions when the window is opened. Position is zoom-based. MIDI actions
+ come always in pair: noteOn + noteOff. */
+
+ recorder::sortActions();
+
+ recorder::action *a2 = nullptr;
+ recorder::action *prev = nullptr;
+
+ for (unsigned i=0; i<recorder::frames.size(); i++) {
+
+ if (recorder::frames.at(i) > clock::getTotalFrames()) // don't show actions > gray area
+ continue;
+
+ for (unsigned j=0; j<recorder::global.at(i).size(); j++) {
+
+ recorder::action *a1 = recorder::global.at(i).at(j);
+
+ /* Skip action if:
+ - does not belong to this channel
+ - is not a MIDI action (we only want MIDI things here)
+ - is the previous one (we have already checked it)
+ - (later on) is not a MIDI Note On type. We don't want any other kind of
+ action here */
+
+ if (a1->chan != pParent->chan->index)
+ continue;
+ if (a1->type != G_ACTION_MIDI)
+ continue;
+ if (a1 == prev)
+ continue;
+
+ /* Extract MIDI infos from a1: if is note off skip it, we are looking for
+ noteOn only. */
+
+ int a1_type = kernelMidi::getB1(a1->iValue);
+ int a1_note = kernelMidi::getB2(a1->iValue);
+
+ /* If two same notes are found (noteOn-noteOn, noteOff-noteOff) or the
+ very first action is a noteOff, add orphaned item.*/
+
+ if ((prev && a1_type == prev->type) || a1_type == 0x80) {
+ gu_log("[geNoteEditor] invalid note pair! Add orphaned item\n");
+ new gePianoItemOrphaned(0, 0, x(), y(), a1, pParent);
+ a2 = nullptr;
+ continue;
+ }
+
+ /* Now skip anything that is not a noteOn. */
+
+ if (a1_type != 0x90)
+ continue;
+
+ /* Search for the next action. Must have: same channel, G_ACTION_MIDI,
+ greater than a1->frame and with MIDI properties of note_off (0x80), same
+ note of a1, any velocity value (0xFF) because we just don't care about the
+ velocity of a note_off. */
+
+ recorder::getNextAction(a1->chan, G_ACTION_MIDI, a1->frame, &a2,
+ kernelMidi::getIValue(0x80, a1_note, 0xFF));
+
+ /* Next action note_off found: add a new gePianoItem to piano roll. Add
+ an orphaned piano item otherwise. */
+
+ if (a2) {
+ new gePianoItem(0, 0, x(), y(), a1, a2, pParent);
+ prev = a2;
+ a2 = nullptr;
+ }
+ else {
+ gu_log("[geNoteEditor] noteOff not found! Add orphaned item\n");
+ new gePianoItemOrphaned(0, 0, x(), y(), a1, pParent);
+ }
+ }
+ }
+
+ end();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gePianoRoll::drawSurface1()
+{
+ surface1 = fl_create_offscreen(CELL_W, h());
+ fl_begin_offscreen(surface1);
+
+ /* warning: only w() and h() come from this widget, x and y coordinates
+ * are absolute, since we are writing in a memory chunk */
+
+ fl_rectf(0, 0, CELL_W, h(), COLOR_BG_MAIN);
+
+ fl_line_style(FL_DASH, 0, nullptr);
+ fl_font(FL_HELVETICA, GUI_FONT_SIZE_BASE);
+
+ int octave = MAX_OCTAVES;
+
+ for (int i=1; i<=MAX_KEYS+1; i++) {
+
+ /* print key note label. C C# D D# E F F# G G# A A# B */
+
+ char note[6];
+ switch (i % KEYS) {
+ case (int) Notes::G:
+ fl_rectf(0, i*CELL_H, CELL_W, CELL_H, COLOR_BG_RICH);
+ sprintf(note, "%dG", octave);
+ break;
+ case (int) Notes::FS:
+ sprintf(note, "%dF#", octave);
+ break;
+ case (int) Notes::F:
+ sprintf(note, "%dF", octave);
+ break;
+ case (int) Notes::E:
+ fl_rectf(0, i*CELL_H, CELL_W, CELL_H, COLOR_BG_RICH);
+ sprintf(note, "%dE", octave);
+ break;
+ case (int) Notes::DS:
+ sprintf(note, "%dD#", octave);
+ break;
+ case (int) Notes::D:
+ fl_rectf(0, i*CELL_H, CELL_W, CELL_H, COLOR_BG_RICH);
+ sprintf(note, "%dD", octave);
+ break;
+ case (int) Notes::CS:
+ sprintf(note, "%dC#", octave);
+ break;
+ case (int) Notes::C:
+ sprintf(note, "%dC", octave);
+ break;
+ case (int) Notes::B:
+ fl_rectf(0, i*CELL_H, CELL_W, CELL_H, COLOR_BG_RICH);
+ sprintf(note, "%dB", octave);
+ break;
+ case (int) Notes::AS:
+ sprintf(note, "%dA#", octave);
+ break;
+ case (int) Notes::A:
+ fl_rectf(0, i*CELL_H, CELL_W, CELL_H, COLOR_BG_RICH);
+ sprintf(note, "%dA", octave);
+ break;
+ case (int) Notes::GS:
+ sprintf(note, "%dG#", octave);
+ octave--;
+ break;
+ }
+
+ /* Print note name */
+
+ fl_color(COLOR_BG_LINE);
+ fl_draw(note, 4, ((i-1)*CELL_H)+1, CELL_W, CELL_H,
+ (Fl_Align) (FL_ALIGN_LEFT | FL_ALIGN_CENTER));
+
+ /* Print horizontal line */
+
+ if (i < MAX_KEYS+1)
+ fl_line(0, i*CELL_H, CELL_W, +i*CELL_H);
+ }
+
+ fl_line_style(0);
+ fl_end_offscreen();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gePianoRoll::drawSurface2()
+{
+ surface2 = fl_create_offscreen(CELL_W, h());
+ fl_begin_offscreen(surface2);
+ fl_rectf(0, 0, CELL_W, h(), COLOR_BG_MAIN);
+ fl_color(COLOR_BG_LINE);
+ fl_line_style(FL_DASH, 0, nullptr);
+ for (int i=1; i<=MAX_KEYS+1; i++) {
+ switch (i % KEYS) {
+ case (int) Notes::G:
+ case (int) Notes::E:
+ case (int) Notes::D:
+ case (int) Notes::B:
+ case (int) Notes::A:
+ fl_rectf(0, i*CELL_H, CELL_W, CELL_H, COLOR_BG_RICH);
+ break;
+ }
+ if (i < MAX_KEYS+1) {
+ fl_color(COLOR_BG_LINE);
+ fl_line(0, i*CELL_H, CELL_W, i*CELL_H);
+ }
+ }
+ fl_line_style(0);
+ fl_end_offscreen();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gePianoRoll::draw()
+{
+ fl_copy_offscreen(x(), y(), CELL_W, h(), surface1, 0, 0);
+
+#if defined(__APPLE__)
+ for (int i=36; i<pParent->totalWidth; i+=36) /// TODO: i < pParent->coverX is faster
+ fl_copy_offscreen(x()+i, y(), CELL_W, h(), surface2, 1, 0);
+#else
+ for (int i=CELL_W; i<pParent->totalWidth; i+=CELL_W) /// TODO: i < pParent->coverX is faster
+ fl_copy_offscreen(x()+i, y(), CELL_W, h(), surface2, 0, 0);
+#endif
+
+ baseDraw(false);
+ draw_children();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int gePianoRoll::handle(int e)
+{
+ int ret = Fl_Group::handle(e);
+
+ switch (e) {
+ case FL_PUSH: {
+
+ /* avoid click on grey area */
+
+ if (Fl::event_x() >= pParent->coverX) {
+ ret = 1;
+ break;
+ }
+
+
+ push_y = Fl::event_y() - y();
+
+ if (Fl::event_button1()) {
+
+ /* ax is driven by grid, ay by the height in px of each note */
+
+ int ax = Fl::event_x();
+ int ay = Fl::event_y();
+
+ /* vertical snap */
+
+ int edge = (ay-y()) % CELL_H;
+ if (edge != 0) ay -= edge;
+
+ /* if no overlap, add new piano item. Also check that it doesn't
+ * overflow on the grey area, by shifting it to the left if
+ * necessary. */
+
+ if (!cursorOnItem) {
+ int greyover = ax+20 - pParent->coverX-x();
+ if (greyover > 0)
+ ax -= greyover;
+ add(new gePianoItem(ax, ay, ax-x(), ay-y(), nullptr, nullptr, pParent));
+ redraw();
+ }
+ }
+ ret = 1;
+ break;
+ }
+ case FL_DRAG: {
+
+ if (Fl::event_button3()) {
+
+ geNoteEditor *prc = static_cast<geNoteEditor*>(parent());
+ position(x(), Fl::event_y() - push_y);
+
+ if (y() > prc->y())
+ position(x(), prc->y());
+ else
+ if (y() < prc->y()+prc->h()-h())
+ position(x(), prc->y()+prc->h()-h());
+
+ prc->redraw();
+ }
+ ret = 1;
+ break;
+ }
+ case FL_MOUSEWHEEL: { // nothing to do, just avoid small internal scroll
+ ret = 1;
+ break;
+ }
+ }
+ return ret;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gePianoRoll::updateActions()
+{
+ for (int k=0; k<children(); k++)
+ static_cast<geBasePianoItem*>(child(k))->reposition(x());
+}
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * 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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#ifndef GE_PIANO_ROLL_H
+#define GE_PIANO_ROLL_H
+
+
+#include <FL/fl_draw.H>
+#include "baseActionEditor.h"
+
+
+class gdActionEditor;
+
+
+class gePianoRoll : public geBaseActionEditor
+{
+private:
+
+ enum class Notes
+ {
+ G = 1, FS = 2, F = 3, E = 4, DS = 5, D = 6, CS = 7, C = 8, B = 9, AS = 10,
+ A = 11, GS = 0
+ };
+
+ /* drawSurface*
+ Generates a complex drawing in memory first and copy it to the screen at a
+ later point in time. Fl_Offscreen surface holds the necessary data. The first
+ call creates an offscreen surface of CELL_W pixel wide containing note values.
+ The second call creates another offscreen surface of CELL_W pixels wide
+ containing the rest of the piano roll. The latter will then be tiled during
+ the ::draw() call. */
+
+ void drawSurface1();
+ void drawSurface2();
+
+ int push_y;
+
+ Fl_Offscreen surface1; // notes, no repeat
+ Fl_Offscreen surface2; // lines, x-repeat
+
+public:
+
+ static const int MAX_KEYS = 127;
+ static const int MAX_OCTAVES = 9;
+ static const int KEYS = 12;
+ static const int CELL_H = 18;
+ static const int CELL_W = 40;
+
+ gePianoRoll(int x, int y, int w, gdActionEditor *pParent);
+
+ void draw();
+ int handle(int e);
+
+ /* updateActions
+ Repositions existing actions after a zoom gesture. */
+
+ void updateActions();
+
+ /* cursorOnItem
+ Defines wheter the cursor is over a piano item. This value is updated by each
+ gePianoItem sub-widget. */
+
+ bool cursorOnItem;
+};
+
+
+#endif
+++ /dev/null
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * ge_actionWidget
- *
- * pParent class of any widget inside the action editor.
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
- *
- * This file is part of Giada - Your Hardcore Loopmachine.
- *
- * Giada - Your Hardcore Loopmachine is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General
- * Public License as published by the Free Software Foundation, either
- * version 3 of the License, or (at your option) any later version.
- *
- * Giada - Your Hardcore Loopmachine is distributed in the hope that it
- * will be useful, but WITHOUT ANY WARRANTY; without even the implied
- * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Giada - Your Hardcore Loopmachine. If not, see
- * <http://www.gnu.org/licenses/>.
- *
- * -------------------------------------------------------------------------- */
-
-
-#include <FL/fl_draw.H>
-#include "../../core/mixer.h"
-#include "../dialogs/gd_actionEditor.h"
-#include "baseActionEditor.h"
-#include "ge_mixed.h"
-
-
-extern Mixer G_Mixer;
-
-
-geBaseActionEditor::geBaseActionEditor(int x, int y, int w, int h,
- gdActionEditor *pParent)
- : Fl_Group(x, y, w, h), pParent(pParent) {}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-geBaseActionEditor::~geBaseActionEditor() {}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void geBaseActionEditor::baseDraw(bool clear) {
-
- /* clear the screen */
-
- if (clear)
- fl_rectf(x(), y(), w(), h(), COLOR_BG_MAIN);
-
- /* draw the container */
-
- fl_color(COLOR_BD_0);
- fl_rect(x(), y(), w(), h());
-
- /* grid drawing, if > 1 */
-
- if (pParent->gridTool->getValue() > 1) {
-
- fl_color(fl_rgb_color(54, 54, 54));
- fl_line_style(FL_DASH, 0, NULL);
-
- for (int i=0; i<(int) pParent->gridTool->points.size(); i++) {
- int px = pParent->gridTool->points.at(i)+x()-1;
- fl_line(px, y()+1, px, y()+h()-2);
- }
- fl_line_style(0);
- }
-
- /* bars and beats drawing */
-
- fl_color(COLOR_BD_0);
- for (int i=0; i<(int) pParent->gridTool->beats.size(); i++) {
- int px = pParent->gridTool->beats.at(i)+x()-1;
- fl_line(px, y()+1, px, y()+h()-2);
- }
-
- fl_color(COLOR_BG_2);
- for (int i=0; i<(int) pParent->gridTool->bars.size(); i++) {
- int px = pParent->gridTool->bars.at(i)+x()-1;
- fl_line(px, y()+1, px, y()+h()-2);
- }
-
- /* cover unused area. Avoid drawing cover if width == 0 (i.e. beats
- * are 32) */
-
- int coverWidth = pParent->totalWidth-pParent->coverX;
- if (coverWidth != 0)
- fl_rectf(pParent->coverX+x(), y()+1, coverWidth, h()-2, COLOR_BG_1);
-}
+++ /dev/null
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * ge_actionWidget
- *
- * parent class of any tool inside the action editor.
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
- *
- * This file is part of Giada - Your Hardcore Loopmachine.
- *
- * Giada - Your Hardcore Loopmachine is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General
- * Public License as published by the Free Software Foundation, either
- * version 3 of the License, or (at your option) any later version.
- *
- * Giada - Your Hardcore Loopmachine is distributed in the hope that it
- * will be useful, but WITHOUT ANY WARRANTY; without even the implied
- * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Giada - Your Hardcore Loopmachine. If not, see
- * <http://www.gnu.org/licenses/>.
- *
- * -------------------------------------------------------------------------- */
-
-
-#ifndef __GE_ACTIONWIDGET_H__
-#define __GE_ACTIONWIDGET_H__
-
-#include <FL/Fl.H>
-#include <FL/Fl_Group.H>
-#include "../../core/const.h"
-
-
-class geBaseActionEditor : public Fl_Group
-{
-protected:
-
- class gdActionEditor *pParent;
- void baseDraw(bool clear=true);
-
-public:
-
- virtual void updateActions() = 0;
-
- geBaseActionEditor(int x, int y, int w, int h, gdActionEditor *pParent);
- ~geBaseActionEditor();
-};
-
-#endif
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * geBaseButton
+ * Base class for every button widget.
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * 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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#include <FL/fl_draw.H>
+#include "baseButton.h"
+
+
+geBaseButton::geBaseButton(int x, int y, int w, int h, const char *l)
+ : Fl_Button(x, y, w, h, l)
+{
+ initLabel = l ? l : "";
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geBaseButton::trimLabel()
+{
+ if (initLabel.empty())
+ return;
+
+ std::string out;
+ if (w() > 20) {
+ out = initLabel;
+ int len = initLabel.size();
+ while (fl_width(out.c_str(), out.size()) > w()) {
+ out = initLabel.substr(0, len) + "...";
+ len--;
+ }
+ }
+ else {
+ out = "";
+ }
+ copy_label(out.c_str());
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geBaseButton::label(const char *l)
+{
+ Fl_Button::label(l);
+ initLabel = l;
+ trimLabel();
+}
+
+
+const char *geBaseButton::label()
+{
+ return Fl_Button::label();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geBaseButton::resize(int X, int Y, int W, int H)
+{
+ trimLabel();
+ Fl_Button::resize(X, Y, W, H);
+}
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * geBaseButton
+ * Base class for every button widget.
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * 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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#ifndef GE_BASE_BUTTON_H
+#define GE_BASE_BUTTON_H
+
+
+#include <string>
+#include <FL/Fl_Button.H>
+
+
+class geBaseButton : public Fl_Button
+{
+private:
+
+ std::string initLabel;
+
+ void trimLabel();
+
+public:
+
+ geBaseButton(int x, int y, int w, int h, const char *l=0);
+
+ void resize(int x, int y, int w, int h) override;
+ void label(const char *l);
+ const char *label();
+};
+
+
+#endif
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * 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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#include "../../../core/const.h"
+#include "box.h"
+
+
+geBox::geBox(int x, int y, int w, int h, const char *l, Fl_Align al)
+: Fl_Box(x, y, w, h)
+{
+ copy_label(l);
+ labelsize(GUI_FONT_SIZE_BASE);
+ box(FL_NO_BOX);
+ labelcolor(COLOR_TEXT_0);
+ if (al != 0)
+ align(al | FL_ALIGN_INSIDE);
+}
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * 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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#ifndef GE_BOX_H
+#define GE_BOX_H
+
+
+#include <FL/Fl_Box.H>
+
+
+class geBox : public Fl_Box
+{
+public:
+
+ geBox(int x, int y, int w, int h, const char *l=0, Fl_Align al=FL_ALIGN_CENTER);
+};
+
+
+#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
* -------------------------------------------------------------------------- */
-#ifndef __BOXTYPES_H__
-#define __BOXTYPES_H__
+#ifndef GE_BOXTYPES_H
+#define GE_BOXTYPES_H
#include <FL/Fl.H>
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * geButton
+ * A regular button.
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * 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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#include <FL/fl_draw.H>
+#include "../../../core/const.h"
+#include "button.h"
+
+
+geButton::geButton(int x, int y, int w, int h, const char *L,
+ const char **imgOff, const char **imgOn)
+: geBaseButton(x, y, w, h, L),
+ imgOff (imgOff),
+ imgOn (imgOn),
+ bgColor0 (COLOR_BG_0),
+ bgColor1 (COLOR_BG_1),
+ bdColor (COLOR_BD_0),
+ txtColor (COLOR_TEXT_0)
+{
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geButton::draw()
+{
+ if (!active()) txtColor = bdColor;
+ else txtColor = COLOR_TEXT_0;
+
+ fl_rect(x(), y(), w(), h(), bdColor); // borders
+ if (value()) { // -- clicked
+ if (imgOn != nullptr)
+ fl_draw_pixmap(imgOn, x()+1, y()+1);
+ else
+ fl_rectf(x(), y(), w(), h(), bgColor1); // covers the border
+ }
+ else { // -- not clicked
+ fl_rectf(x()+1, y()+1, w()-2, h()-2, bgColor0); // bg inside the border
+ if (imgOff != nullptr)
+ fl_draw_pixmap(imgOff, x()+1, y()+1);
+ }
+ if (!active())
+ fl_color(FL_INACTIVE_COLOR);
+
+ fl_color(txtColor);
+ fl_font(FL_HELVETICA, GUI_FONT_SIZE_BASE);
+ fl_draw(label(), x()+2, y(), w()-2, h(), FL_ALIGN_CENTER);
+}
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * geButton
+ * A regular button.
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * 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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#ifndef GE_BUTTON_H
+#define GE_BUTTON_H
+
+
+#include "baseButton.h"
+
+
+class geButton : public geBaseButton
+{
+public:
+
+ geButton(int x, int y, int w, int h, const char *L=0,
+ const char **imgOff=nullptr, const char **imgOn=nullptr);
+
+ void draw() override;
+
+ const char **imgOff;
+ const char **imgOn;
+ Fl_Color bgColor0; // background not clicked
+ Fl_Color bgColor1; // background clicked
+ Fl_Color bdColor; // border
+ Fl_Color txtColor; // text
+};
+
+
+#endif
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * 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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#include <FL/fl_draw.H>
+#include "../../../core/const.h"
+#include "check.h"
+
+
+geCheck::geCheck(int x, int y, int w, int h, const char *l)
+: Fl_Check_Button(x, y, w, h, l)
+{
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geCheck::draw()
+{
+ int color = !active() ? FL_INACTIVE_COLOR : COLOR_BD_0;
+
+ if (value()) {
+ fl_rect(x(), y(), 12, 12, (Fl_Color) color);
+ fl_rectf(x(), y(), 12, 12, (Fl_Color) color);
+ }
+ else {
+ fl_rectf(x(), y(), 12, 12, FL_BACKGROUND_COLOR);
+ fl_rect(x(), y(), 12, 12, (Fl_Color) color);
+ }
+
+ fl_rectf(x()+20, y(), w(), h(), FL_BACKGROUND_COLOR); // clearer
+ fl_font(FL_HELVETICA, GUI_FONT_SIZE_BASE);
+ fl_color(!active() ? FL_INACTIVE_COLOR : COLOR_TEXT_0);
+ fl_draw(label(), x()+20, y(), w(), h(), (Fl_Align) (FL_ALIGN_LEFT | FL_ALIGN_TOP));
+}
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * 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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#ifndef GE_CHECK_H
+#define GE_CHECK_H
+
+
+#include <FL/Fl_Check_Button.H>
+
+
+class geCheck : public Fl_Check_Button
+{
+public:
+
+ geCheck(int x, int y, int w, int h, const char *l=0);
+
+ void draw();
+};
+
+
+#endif
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * 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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#include <string>
+#include <FL/fl_draw.H>
+#include "../../../core/const.h"
+#include "choice.h"
+
+
+geChoice::geChoice(int x, int y, int w, int h, const char *l, bool ang)
+ : Fl_Choice(x, y, w, h, l), angle(ang)
+{
+ labelsize(GUI_FONT_SIZE_BASE);
+ labelcolor(COLOR_TEXT_0);
+ box(FL_BORDER_BOX);
+ textsize(GUI_FONT_SIZE_BASE);
+ textcolor(COLOR_TEXT_0);
+ color(COLOR_BG_0);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geChoice::draw()
+{
+ fl_rectf(x(), y(), w(), h(), COLOR_BG_0); // bg
+ fl_rect(x(), y(), w(), h(), (Fl_Color) COLOR_BD_0); // border
+ if (angle)
+ fl_polygon(x()+w()-8, y()+h()-1, x()+w()-1, y()+h()-8, x()+w()-1, y()+h()-1);
+
+ /* pick up the text() from the selected item (value()) and print it in
+ * the box and avoid overflows */
+
+ fl_color(!active() ? COLOR_BD_0 : COLOR_TEXT_0);
+ if (value() != -1) {
+ if (fl_width(text(value())) < w()-8) {
+ fl_draw(text(value()), x(), y(), w(), h(), FL_ALIGN_CENTER);
+ }
+ else {
+ std::string tmp = text(value());
+ int size = tmp.size();
+ while (fl_width(tmp.c_str()) >= w()-16) {
+ tmp.resize(size);
+ size--;
+ }
+ tmp += "...";
+ fl_draw(tmp.c_str(), x(), y(), w(), h(), FL_ALIGN_CENTER);
+ }
+
+ }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geChoice::showItem(const char *c)
+{
+ value(find_index(c));
+}
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * 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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#ifndef GE_CHOICE_H
+#define GE_CHOICE_H
+
+
+#include <FL/Fl_Choice.H>
+
+
+class geChoice : public Fl_Choice
+{
+public:
+
+ geChoice(int X,int Y,int W,int H,const char *L=0, bool angle=true);
+ void draw();
+
+ void showItem(const char *c);
+
+ bool angle;
+ int id;
+};
+
+
+#endif
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * 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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#include <FL/fl_draw.H>
+#include "../../../core/const.h"
+#include "dial.h"
+
+
+geDial::geDial(int x, int y, int w, int h, const char *l)
+: Fl_Dial(x, y, w, h, l)
+{
+ labelsize(GUI_FONT_SIZE_BASE);
+ labelcolor(COLOR_TEXT_0);
+ align(FL_ALIGN_LEFT);
+ type(FL_FILL_DIAL);
+ angles(0, 360);
+ color(COLOR_BG_0); // background
+ selection_color(COLOR_BG_1); // selection
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geDial::draw()
+{
+ double angle = (angle2()-angle1())*(value()-minimum())/(maximum()-minimum()) + angle1();
+
+ fl_color(COLOR_BG_0);
+ fl_pie(x(), y(), w(), h(), 270-angle1(), angle > angle1() ? 360+270-angle : 270-360-angle);
+
+ fl_color(COLOR_BD_0);
+ fl_arc(x(), y(), w(), h(), 0, 360);
+ fl_pie(x(), y(), w(), h(), 270-angle, 270-angle1());
+}
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * 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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#ifndef GE_DIAL_H
+#define GE_DIAL_H
+
+
+#include <FL/Fl_Dial.H>
+
+
+class geDial : public Fl_Dial
+{
+public:
+
+ geDial(int x, int y, int w, int h, const char *l=0);
+
+ void draw();
+};
+
+
+#endif
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * geIdButton
+ * Exactly as geButton but with a unique id. Used for the buttons in channels
+ * and for FXs.
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * 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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#include "idButton.h"
+
+
+geIdButton::geIdButton(int X, int Y, int W, int H, const char *L,
+ const char **imgOff, const char **imgOn)
+ : geButton(X, Y, W, H, L, imgOff, imgOn)
+{
+}
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * geIdButton
+ * Exactly as geButton but with a unique id. Used for the buttons in channels
+ * and for FXs.
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * 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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#ifndef GE_ID_BUTTON_H
+#define GE_ID_BUTTON_H
+
+
+#include "button.h"
+
+
+class geIdButton : public geButton
+{
+public:
+
+ geIdButton(int X,int Y,int W,int H,const char *L=0,
+ const char **imgOff=nullptr, const char **imgOn=nullptr);
+
+ int key;
+ int id;
+};
+
+
+#endif
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * 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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#include "../../../core/const.h"
+#include "boxtypes.h"
+#include "input.h"
+
+
+geInput::geInput(int x, int y, int w, int h, const char *l)
+ : Fl_Input(x, y, w, h, l)
+{
+ //Fl::set_boxtype(G_CUSTOM_BORDER_BOX, gDrawBox, 1, 1, 2, 2);
+ box(G_CUSTOM_BORDER_BOX);
+ labelsize(GUI_FONT_SIZE_BASE);
+ labelcolor(COLOR_TEXT_0);
+ color(COLOR_BG_DARK);
+ textcolor(COLOR_TEXT_0);
+ cursor_color(COLOR_TEXT_0);
+ selection_color(COLOR_BD_0);
+ textsize(GUI_FONT_SIZE_BASE);
+}
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * 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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#ifndef GE_INPUT_H
+#define GE_INPUT_H
+
+
+#include <FL/Fl_Input.H>
+
+
+class geInput : public Fl_Input
+{
+public:
+
+ geInput(int x, int y, int w, int h, const char *l=0);
+};
+
+#endif
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * gLiquidScroll
+ * custom scroll that tells children to follow scroll's width when
+ * resized. Thanks to Greg Ercolano from FLTK dev team.
+ * http://seriss.com/people/erco/fltk/
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * 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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#include "../../../core/const.h"
+#include "boxtypes.h"
+#include "liquidScroll.h"
+
+
+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(COLOR_BG_0);
+ scrollbar.selection_color(COLOR_BG_1);
+ scrollbar.labelcolor(COLOR_BD_1);
+ scrollbar.slider(G_CUSTOM_BORDER_BOX);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geLiquidScroll::resize(int X, int Y, int W, int H)
+{
+ int nc = children()-2; // skip hscrollbar and vscrollbar
+ for ( int t=0; t<nc; t++) { // tell children to resize to our new width
+ Fl_Widget *c = child(t);
+ c->resize(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);
+}
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * gLiquidScroll
+ * custom scroll that tells children to follow scroll's width when
+ * resized. Thanks to Greg Ercolano from FLTK dev team.
+ * http://seriss.com/people/erco/fltk/
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * 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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#ifndef GE_LIQUID_SCROLL_H
+#define GE_LIQUID_SCROLL_H
+
+
+#include <FL/Fl_Scroll.H>
+
+
+class geLiquidScroll : public Fl_Scroll
+{
+public:
+
+ geLiquidScroll(int x, int y, int w, int h, const char *l=0);
+
+ void resize(int x, int y, int w, int h);
+};
+
+
+#endif
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * 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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#include "../../../core/const.h"
+#include "boxtypes.h"
+#include "progress.h"
+
+
+geProgress::geProgress(int x, int y, int w, int h, const char *l)
+: Fl_Progress(x, y, w, h, l)
+{
+ color(COLOR_BG_0, COLOR_BD_0);
+ box(G_CUSTOM_BORDER_BOX);
+}
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * 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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#ifndef GE_PROGRESS_H
+#define GE_PROGRESS_H
+
+
+#include <FL/Fl_Progress.H>
+
+
+class geProgress : public Fl_Progress
+{
+public:
+
+ geProgress(int x, int y, int w, int h, const char *l=0);
+};
+
+
+#endif
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * 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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#include <FL/fl_draw.H>
+#include "../../../core/const.h"
+#include "radio.h"
+
+
+geRadio::geRadio(int x, int y, int w, int h, const char *l)
+: Fl_Radio_Button(x, y, w, h, l)
+{
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geRadio::draw()
+{
+ int color = !active() ? FL_INACTIVE_COLOR : COLOR_BD_0;
+
+ if (value()) {
+ fl_rect(x(), y(), 12, 12, (Fl_Color) color);
+ fl_rectf(x(), y(), 12, 12, (Fl_Color) color);
+ }
+ else {
+ fl_rectf(x(), y(), 12, 12, FL_BACKGROUND_COLOR);
+ fl_rect(x(), y(), 12, 12, (Fl_Color) color);
+ }
+
+ fl_rectf(x()+20, y(), w(), h(), FL_BACKGROUND_COLOR); // clearer
+ fl_font(FL_HELVETICA, GUI_FONT_SIZE_BASE);
+ fl_color(COLOR_TEXT_0);
+ fl_draw(label(), x()+20, y(), w(), h(), (Fl_Align) (FL_ALIGN_LEFT | FL_ALIGN_TOP));
+}
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * 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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#ifndef GE_RADIO_H
+#define GE_RADIO_H
+
+
+#include <FL/Fl_Radio_Button.H>
+
+
+class geRadio : public Fl_Radio_Button
+{
+public:
+
+ geRadio(int x, int y, int w, int h, const char *l=0);
+
+ void draw();
+};
+
+#endif
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * geResizerBar
+ * 'resizer bar' between widgets Fl_Scroll. Thanks to Greg Ercolano from
+ * FLTK dev team. http://seriss.com/people/erco/fltk/
+ *
+ * Shows a resize cursor when hovered over.
+ * Assumes:
+ * - Parent is an Fl_Scroll
+ * - All children of Fl_Scroll are vertically arranged
+ * - The widget above us has a bottom edge touching our top edge
+ * ie. (w->y()+w->h() == this->y())
+ *
+ * When this widget is dragged:
+ * - The widget above us (with a common edge) will be /resized/
+ * vertically
+ * - All children below us will be /moved/ vertically
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * 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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#include <FL/Fl.H>
+#include <FL/Fl_Scroll.H>
+#include <FL/fl_draw.H>
+#include "resizerBar.h"
+
+
+geResizerBar::geResizerBar(int X,int Y,int W,int H, bool vertical)
+ : Fl_Box(X,Y,W,H), vertical(vertical)
+{
+ last_y = 0;
+ min_h = 30;
+ if (vertical) {
+ orig_h = H;
+ labelsize(H);
+ }
+ else {
+ orig_h = W;
+ labelsize(W);
+ }
+ align(FL_ALIGN_CENTER|FL_ALIGN_INSIDE);
+ labelfont(FL_COURIER);
+ visible_focus(0);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geResizerBar::HandleDrag(int diff)
+{
+ Fl_Scroll *grp = static_cast<Fl_Scroll*>(parent());
+ int top;
+ int bot;
+ if (vertical) {
+ top = y();
+ bot = y()+h();
+ }
+ else {
+ top = x();
+ bot = x()+w();
+ }
+
+ // First pass: find widget directly above us with common edge
+ // Possibly clamp 'diff' if widget would get too small..
+
+ for (int t=0; t<grp->children(); t++) {
+ Fl_Widget *wd = grp->child(t);
+ if (vertical) {
+ if ((wd->y()+wd->h()) == top) { // found widget directly above?
+ if ((wd->h()+diff) < min_h)
+ diff = wd->h() - min_h; // clamp
+ wd->resize(wd->x(), wd->y(), wd->w(), wd->h()+diff); // change height
+ break; // done with first pass
+ }
+ }
+ else {
+ if ((wd->x()+wd->w()) == top) { // found widget directly above?
+ if ((wd->w()+diff) < min_h)
+ diff = wd->w() - min_h; // clamp
+ wd->resize(wd->x(), wd->y(), wd->w()+diff, wd->h()); // change height
+ break; // done with first pass
+ }
+ }
+ }
+
+ // Second pass: find widgets below us, move based on clamped diff
+
+ for (int t=0; t<grp->children(); t++) {
+ Fl_Widget *wd = grp->child(t);
+ if (vertical) {
+ if (wd->y() >= bot) // found widget below us?
+ wd->resize(wd->x(), wd->y()+diff, wd->w(), wd->h()); // change position
+ }
+ else {
+ if (wd->x() >= bot)
+ wd->resize(wd->x()+diff, wd->y(), wd->w(), wd->h());
+ }
+ }
+
+ // Change our position last
+
+ if (vertical)
+ resize(x(), y()+diff, w(), h());
+ else
+ resize(x()+diff, y(), w(), h());
+
+ grp->init_sizes();
+ grp->redraw();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int geResizerBar::handle(int e)
+{
+ int ret = 0;
+ int this_y;
+ if (vertical)
+ this_y = Fl::event_y_root();
+ else
+ this_y = Fl::event_x_root();
+ switch (e) {
+ case FL_FOCUS:
+ ret = 1;
+ break;
+ case FL_ENTER:
+ ret = 1;
+ fl_cursor(vertical ? FL_CURSOR_NS : FL_CURSOR_WE);
+ break;
+ case FL_LEAVE:
+ ret = 1;
+ fl_cursor(FL_CURSOR_DEFAULT);
+ break;
+ case FL_PUSH:
+ ret = 1;
+ last_y = this_y;
+ break;
+ case FL_DRAG:
+ HandleDrag(this_y-last_y);
+ last_y = this_y;
+ ret = 1;
+ break;
+ default: break;
+ }
+ return(Fl_Box::handle(e) | ret);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geResizerBar::resize(int X,int Y,int W,int H)
+{
+ if (vertical)
+ Fl_Box::resize(X,Y,W,orig_h); // height of resizer stays constant size
+ else
+ Fl_Box::resize(X,Y,orig_h,H);
+}
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * geResizerBar
+ * 'resizer bar' between widgets Fl_Scroll. Thanks to Greg Ercolano from
+ * FLTK dev team. http://seriss.com/people/erco/fltk/
+ *
+ * Shows a resize cursor when hovered over.
+ * Assumes:
+ * - Parent is an Fl_Scroll
+ * - All children of Fl_Scroll are vertically arranged
+ * - The widget above us has a bottom edge touching our top edge
+ * ie. (w->y()+w->h() == this->y())
+ *
+ * When this widget is dragged:
+ * - The widget above us (with a common edge) will be /resized/
+ * vertically
+ * - All children below us will be /moved/ vertically
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * 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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#ifndef GE_RESIZER_BAR_H
+#define GE_RESIZER_BAR_H
+
+
+#include <FL/Fl_Box.H>
+
+
+class geResizerBar : public Fl_Box
+{
+private:
+
+ /* TODO - use more general variable names
+ * (last_y -> last_?, min_h -> min_?, ...) */
+
+ bool vertical;
+ int orig_h;
+ int last_y;
+ int min_h; // min height for widget above us
+
+ void HandleDrag(int diff);
+
+public:
+
+ /* 'vertical' defines the bar movement. Vertical=true: the bar moves
+ * vertically (up and down). */
+
+ geResizerBar(int x, int y, int w, int h, bool vertical=true);
+
+ inline void setMinSize(int val) { min_h = val; }
+ inline int getMinSize() { return min_h; }
+
+ int handle(int e);
+ void resize(int x, int y, int w, int h);
+};
+
+
+#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
* -------------------------------------------------------------------------- */
-#ifndef __GE_SCROLL_H__
-#define __GE_SCROLL_H__
+#ifndef GE_SCROLL_H
+#define GE_SCROLL_H
#include <FL/Fl_Scroll.H>
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * 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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#include "../../../core/const.h"
+#include "boxtypes.h"
+#include "slider.h"
+
+
+geSlider::geSlider(int x, int y, int w, int h, const char *l)
+ : Fl_Slider(x, y, w, h, l)
+{
+ type(FL_HOR_FILL_SLIDER);
+
+ labelsize(GUI_FONT_SIZE_BASE);
+ align(FL_ALIGN_LEFT);
+ labelcolor(COLOR_TEXT_0);
+
+ box(G_CUSTOM_BORDER_BOX);
+ color(COLOR_BG_0);
+ selection_color(COLOR_BD_0);
+}
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * 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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#ifndef GE_SLIDER_H
+#define GE_SLIDER_H
+
+
+#include <FL/Fl_Slider.H>
+
+
+class geSlider : public Fl_Slider
+{
+public:
+
+ geSlider(int x, int y, int w, int h, const char *l=0);
+
+ int id;
+};
+
+
+#endif
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * geStatusButton
+ * Simple geButton with a boolean 'status' parameter.
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * 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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#include <FL/fl_draw.H>
+#include "../../../core/const.h"
+#include "statusButton.h"
+
+
+geStatusButton::geStatusButton(int x, int y, int w, int h, const char **imgOff,
+ const char **imgOn)
+ : geButton(x, y, w, h, nullptr, imgOff, imgOn),
+ status (false)
+{
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geStatusButton::draw()
+{
+ geButton::draw();
+ if (status)
+ fl_draw_pixmap(imgOn, x()+1, y()+1, COLOR_BD_0);
+}
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * geStatusButton
+ * Simple geButton with a boolean 'status' parameter.
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * 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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#ifndef GE_STATUS_BUTTON_H
+#define GE_STATUS_BUTTON_H
+
+
+#include "button.h"
+
+
+class geStatusButton : public geButton
+{
+public:
+
+ geStatusButton(int x, int y, int w, int h, const char **imgOff=nullptr,
+ const char **imgOn=nullptr);
+
+ void draw() override;
+
+ bool status;
+};
+
+
+#endif
*
* Giada - Your Hardcore Loopmachine
*
- * ge_browser
- *
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
#include "../../core/const.h"
#include "../../utils/string.h"
-#include "../dialogs/gd_browser.h"
-#include "../elems/ge_mixed.h"
+#include "../dialogs/browser/browserBase.h"
#include "basics/boxtypes.h"
#include "browser.h"
+using std::string;
+
+
geBrowser::geBrowser(int x, int y, int w, int h)
: Fl_File_Browser(x, y, w, h),
showHiddenFiles(false)
select(value() - 1);
else
if (Fl::event_key(FL_Enter))
- ((gdBaseBrowser*) parent())->fireCallback();
+ ((gdBrowserBase*) parent())->fireCallback();
ret = 1;
break;
case FL_PUSH: // mouse
if (Fl::event_clicks() > 0) // double click
- ((gdBaseBrowser*) parent())->fireCallback();
+ ((gdBrowserBase*) parent())->fireCallback();
ret = 1;
break;
case FL_RELEASE: // mouse
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
#include <FL/Fl_File_Browser.H>
-using std::string;
-
-
class geBrowser : public Fl_File_Browser
{
private:
- string currentDir;
+ std::string currentDir;
bool showHiddenFiles;
/* normalize
- * Make sure the string never ends with a trailing slash. */
+ * Make sure the std::string never ends with a trailing slash. */
- string normalize(const string &s);
+ std::string normalize(const std::string &s);
public:
/* init
* Initialize browser and show 'dir' as initial directory. */
- void loadDir(const string &dir);
+ void loadDir(const std::string &dir);
/* getSelectedItem
* Return the full path or just the displayed name of the i-th selected item.
* Always with the trailing slash! */
- string getSelectedItem(bool fullPath=true);
+ std::string getSelectedItem(bool fullPath=true);
- string getCurrentDir();
+ std::string getCurrentDir();
void preselect(int position, int line);
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * 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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#include <string>
+#include "../../../deps/rtaudio-mod/RtAudio.h"
+#include "../../../core/const.h"
+#include "../../../core/conf.h"
+#include "../../../core/kernelAudio.h"
+#include "../../../utils/string.h"
+#include "../../../gui/dialogs/gd_devInfo.h"
+#include "../basics/box.h"
+#include "../basics/choice.h"
+#include "../basics/check.h"
+#include "../basics/input.h"
+#include "../basics/button.h"
+#include "tabAudio.h"
+
+
+using std::string;
+using namespace giada::m;
+
+
+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");
+ new geBox(x(), rsmpQuality->y()+rsmpQuality->h()+8, w(), 92,
+ "Restart Giada for the changes to take effect.");
+ end();
+ labelsize(GUI_FONT_SIZE_BASE);
+
+ soundsys->add("(none)");
+
+#if defined(__linux__)
+
+ if (kernelAudio::hasAPI(RtAudio::LINUX_ALSA))
+ soundsys->add("ALSA");
+ if (kernelAudio::hasAPI(RtAudio::UNIX_JACK))
+ soundsys->add("Jack");
+ if (kernelAudio::hasAPI(RtAudio::LINUX_PULSE))
+ soundsys->add("PulseAudio");
+
+ switch (conf::soundSystem) {
+ case G_SYS_API_NONE:
+ soundsys->showItem("(none)");
+ break;
+ case G_SYS_API_ALSA:
+ soundsys->showItem("ALSA");
+ break;
+ case G_SYS_API_JACK:
+ soundsys->showItem("Jack");
+ buffersize->deactivate();
+ samplerate->deactivate();
+ break;
+ case G_SYS_API_PULSE:
+ soundsys->showItem("PulseAudio");
+ break;
+ }
+
+#elif defined(_WIN32)
+
+ if (kernelAudio::hasAPI(RtAudio::WINDOWS_DS))
+ soundsys->add("DirectSound");
+ if (kernelAudio::hasAPI(RtAudio::WINDOWS_ASIO))
+ soundsys->add("ASIO");
+ if (kernelAudio::hasAPI(RtAudio::WINDOWS_WASAPI))
+ soundsys->add("WASAPI");
+
+ switch (conf::soundSystem) {
+ case G_SYS_API_NONE:
+ soundsys->showItem("(none)");
+ break;
+ case G_SYS_API_DS:
+ soundsys->showItem("DirectSound");
+ break;
+ case G_SYS_API_ASIO:
+ soundsys->showItem("ASIO");
+ break;
+ case G_SYS_API_WASAPI:
+ soundsys->showItem("WASAPI");
+ break;
+ }
+
+#elif defined (__APPLE__)
+
+ if (kernelAudio::hasAPI(RtAudio::MACOSX_CORE))
+ soundsys->add("CoreAudio");
+
+ switch (conf::soundSystem) {
+ case G_SYS_API_NONE:
+ soundsys->showItem("(none)");
+ break;
+ case G_SYS_API_CORE:
+ soundsys->showItem("CoreAudio");
+ break;
+ }
+
+#endif
+
+ soundsysInitValue = soundsys->value();
+
+ soundsys->callback(cb_deactivate_sounddev, (void*)this);
+
+ sounddevIn->callback(cb_fetchInChans, this);
+ sounddevOut->callback(cb_fetchOutChans, this);
+
+ devOutInfo->callback(cb_showOutputInfo, this);
+ devInInfo->callback(cb_showInputInfo, this);
+
+ if (conf::soundSystem != G_SYS_API_NONE) {
+ fetchSoundDevs();
+ fetchOutChans(sounddevOut->value());
+ fetchInChans(sounddevIn->value());
+
+ /* fill frequency dropdown menu */
+ /* TODO - add fetchFrequencies() */
+
+ int nfreq = kernelAudio::getTotalFreqs(sounddevOut->value());
+ for (int i=0; i<nfreq; i++) {
+ int freq = kernelAudio::getFreq(sounddevOut->value(), i);
+ samplerate->add(gu_itoa(freq).c_str());
+ if (freq == conf::samplerate)
+ samplerate->value(i);
+ }
+ }
+ else {
+ sounddevIn->deactivate();
+ sounddevOut->deactivate();
+ channelsIn->deactivate();
+ channelsOut->deactivate();
+ devOutInfo->deactivate();
+ devInInfo->deactivate();
+ samplerate->deactivate();
+ }
+
+ buffersize->add("8");
+ buffersize->add("16");
+ buffersize->add("32");
+ buffersize->add("64");
+ buffersize->add("128");
+ buffersize->add("256");
+ buffersize->add("512");
+ buffersize->add("1024");
+ buffersize->add("2048");
+ buffersize->add("4096");
+ buffersize->showItem(gu_itoa(conf::buffersize).c_str());
+
+ rsmpQuality->add("Sinc best quality (very slow)");
+ rsmpQuality->add("Sinc medium quality (slow)");
+ rsmpQuality->add("Sinc basic quality (medium)");
+ rsmpQuality->add("Zero Order Hold (fast)");
+ rsmpQuality->add("Linear (very fast)");
+ rsmpQuality->value(conf::rsmpQuality);
+
+ delayComp->value(gu_itoa(conf::delayComp).c_str());
+ delayComp->type(FL_INT_INPUT);
+ delayComp->maximum_size(5);
+
+ limitOutput->value(conf::limitOutput);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geTabAudio::cb_deactivate_sounddev(Fl_Widget *w, void *p) { ((geTabAudio*)p)->__cb_deactivate_sounddev(); }
+void geTabAudio::cb_fetchInChans(Fl_Widget *w, void *p) { ((geTabAudio*)p)->__cb_fetchInChans(); }
+void geTabAudio::cb_fetchOutChans(Fl_Widget *w, void *p) { ((geTabAudio*)p)->__cb_fetchOutChans(); }
+void geTabAudio::cb_showInputInfo(Fl_Widget *w, void *p) { ((geTabAudio*)p)->__cb_showInputInfo(); }
+void geTabAudio::cb_showOutputInfo(Fl_Widget *w, void *p) { ((geTabAudio*)p)->__cb_showOutputInfo(); }
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geTabAudio::__cb_fetchInChans()
+{
+ fetchInChans(sounddevIn->value());
+ channelsIn->value(0);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geTabAudio::__cb_fetchOutChans()
+{
+ fetchOutChans(sounddevOut->value());
+ channelsOut->value(0);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geTabAudio::__cb_showInputInfo()
+{
+ unsigned dev = kernelAudio::getDeviceByName(sounddevIn->text(sounddevIn->value()));
+ new gdDevInfo(dev);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geTabAudio::__cb_showOutputInfo()
+{
+ unsigned dev = kernelAudio::getDeviceByName(sounddevOut->text(sounddevOut->value()));
+ new gdDevInfo(dev);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geTabAudio::__cb_deactivate_sounddev()
+{
+ /* if the user changes sound system (eg ALSA->JACK) device menu deactivates.
+ * If it returns to the original sound system, we re-fill the list by
+ * querying kernelAudio. Watch out if soundsysInitValue == 0: you don't want
+ * to query kernelAudio for '(none)' soundsystem! */
+
+ if (soundsysInitValue == soundsys->value() && soundsysInitValue != 0) {
+ sounddevOut->clear();
+ sounddevIn->clear();
+
+ fetchSoundDevs();
+
+ /* the '?' button is added by fetchSoundDevs */
+
+ fetchOutChans(sounddevOut->value());
+ sounddevOut->activate();
+ channelsOut->activate();
+
+ /* chan menus and '?' button are activated by fetchInChans(...) */
+
+ fetchInChans(sounddevIn->value());
+ sounddevIn->activate();
+ samplerate->activate();
+ }
+ else {
+ sounddevOut->deactivate();
+ sounddevOut->clear();
+ sounddevOut->add("-- restart to fetch device(s) --");
+ sounddevOut->value(0);
+ channelsOut->deactivate();
+ devOutInfo->deactivate();
+ samplerate->deactivate();
+
+ sounddevIn->deactivate();
+ sounddevIn->clear();
+ sounddevIn->add("-- restart to fetch device(s) --");
+ sounddevIn->value(0);
+ channelsIn->deactivate();
+ devInInfo->deactivate();
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+
+void geTabAudio::fetchInChans(int menuItem)
+{
+ /* if menuItem==0 device in input is disabled. */
+
+ if (menuItem == 0) {
+ devInInfo ->deactivate();
+ channelsIn->deactivate();
+ delayComp ->deactivate();
+ return;
+ }
+
+ devInInfo ->activate();
+ channelsIn->activate();
+ delayComp ->activate();
+
+ channelsIn->clear();
+
+ unsigned dev = kernelAudio::getDeviceByName(sounddevIn->text(sounddevIn->value()));
+ unsigned chs = kernelAudio::getMaxInChans(dev);
+
+ if (chs == 0) {
+ channelsIn->add("none");
+ channelsIn->value(0);
+ return;
+ }
+ for (unsigned i=0; i<chs; i+=2) {
+ char str[16];
+ sprintf(str, "%d-%d", (i+1), (i+2));
+ channelsIn->add(str);
+ }
+ channelsIn->value(conf::channelsIn);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geTabAudio::fetchOutChans(int menuItem)
+{
+ channelsOut->clear();
+
+ unsigned dev = kernelAudio::getDeviceByName(sounddevOut->text(sounddevOut->value()));
+ unsigned chs = kernelAudio::getMaxOutChans(dev);
+
+ if (chs == 0) {
+ channelsOut->add("none");
+ channelsOut->value(0);
+ return;
+ }
+ for (unsigned i=0; i<chs; i+=2) {
+ char str[16];
+ sprintf(str, "%d-%d", (i+1), (i+2));
+ channelsOut->add(str);
+ }
+ channelsOut->value(conf::channelsOut);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int geTabAudio::findMenuDevice(geChoice *m, int device)
+{
+ if (device == -1)
+ return 0;
+
+ if (kernelAudio::getStatus() == false)
+ return 0;
+
+ for (int i=0; i<m->size(); i++) {
+ if (kernelAudio::getDeviceName(device) == "")
+ continue;
+ if (m->text(i) == nullptr)
+ continue;
+ if (m->text(i) == kernelAudio::getDeviceName(device))
+ return i;
+ }
+
+ return 0;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geTabAudio::fetchSoundDevs()
+{
+ if (kernelAudio::countDevices() == 0) {
+ sounddevOut->add("-- no devices found --");
+ sounddevOut->value(0);
+ sounddevIn->add("-- no devices found --");
+ sounddevIn->value(0);
+ devInInfo ->deactivate();
+ devOutInfo->deactivate();
+ }
+ else {
+
+ devInInfo ->activate();
+ devOutInfo->activate();
+
+ /* input device may be disabled: now device number -1 is the disabled
+ * one. KernelAudio knows how to handle it. */
+
+ sounddevIn->add("(disabled)");
+
+ for (unsigned i=0; i<kernelAudio::countDevices(); i++) {
+
+ /* escaping '/', very dangerous in FLTK (it creates a submenu) */
+
+ string tmp = kernelAudio::getDeviceName(i);
+ for (unsigned k=0; k<tmp.size(); k++)
+ if (tmp[k] == '/' || tmp[k] == '|' || tmp[k] == '&' || tmp[k] == '_')
+ tmp[k] = '-';
+
+ /* add to list devices with at least 1 channel available. In this
+ * way we can filter devices only for input or output, e.g. an input
+ * devices has 0 output channels. */
+
+ if (kernelAudio::getMaxOutChans(i) > 0)
+ sounddevOut->add(tmp.c_str());
+
+ if (kernelAudio::getMaxInChans(i) > 0)
+ sounddevIn->add(tmp.c_str());
+ }
+
+ /* we show the device saved in the configuration file. */
+
+ if (sounddevOut->size() == 0) {
+ sounddevOut->add("-- no devices found --");
+ sounddevOut->value(0);
+ devOutInfo->deactivate();
+ }
+ else {
+ int outMenuValue = findMenuDevice(sounddevOut, conf::soundDeviceOut);
+ sounddevOut->value(outMenuValue);
+ }
+
+ if (sounddevIn->size() == 0) {
+ sounddevIn->add("-- no devices found --");
+ sounddevIn->value(0);
+ devInInfo->deactivate();
+ }
+ else {
+ int inMenuValue = findMenuDevice(sounddevIn, conf::soundDeviceIn);
+ sounddevIn->value(inMenuValue);
+ }
+ }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geTabAudio::save()
+{
+ string text = soundsys->text(soundsys->value());
+
+ if (text == "(none)") {
+ conf::soundSystem = G_SYS_API_NONE;
+ return;
+ }
+
+#if defined(__linux__)
+
+ else if (text == "ALSA")
+ conf::soundSystem = G_SYS_API_ALSA;
+ else if (text == "Jack")
+ conf::soundSystem = G_SYS_API_JACK;
+ else if (text == "PulseAudio")
+ conf::soundSystem = G_SYS_API_PULSE;
+
+#elif defined(_WIN32)
+
+ else if (text == "DirectSound")
+ conf::soundSystem = G_SYS_API_DS;
+ else if (text == "ASIO")
+ conf::soundSystem = G_SYS_API_ASIO;
+ else if (text == "WASAPI")
+ conf::soundSystem = G_SYS_API_WASAPI;
+
+#elif defined (__APPLE__)
+
+ else if (text == "CoreAudio")
+ conf::soundSystem = G_SYS_API_CORE;
+
+#endif
+
+ /* use the device name to search into the drop down menu's */
+
+ conf::soundDeviceOut = kernelAudio::getDeviceByName(sounddevOut->text(sounddevOut->value()));
+ conf::soundDeviceIn = kernelAudio::getDeviceByName(sounddevIn->text(sounddevIn->value()));
+ conf::channelsOut = channelsOut->value();
+ conf::channelsIn = channelsIn->value();
+ conf::limitOutput = limitOutput->value();
+ conf::rsmpQuality = rsmpQuality->value();
+
+ /* if sounddevOut is disabled (because of system change e.g. alsa ->
+ * jack) its value is equal to -1. Change it! */
+
+ if (conf::soundDeviceOut == -1)
+ conf::soundDeviceOut = 0;
+
+ int bufsize = atoi(buffersize->text());
+ if (bufsize % 2 != 0) bufsize++;
+ if (bufsize < 8) bufsize = 8;
+ if (bufsize > 8192) bufsize = 8192;
+ conf::buffersize = bufsize;
+
+ const Fl_Menu_Item *i = nullptr;
+ i = samplerate->mvalue(); // mvalue() returns a pointer to the last menu item that was picked
+ if (i)
+ conf::samplerate = atoi(i->label());
+
+ conf::delayComp = atoi(delayComp->value());
+}
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * 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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#ifndef GE_TAB_AUDIO_H
+#define GE_TAB_AUDIO_H
+
+
+#include <FL/Fl_Group.H>
+
+
+class geChoice;
+class geCheck;
+class geButton;
+class geInput;
+
+
+class geTabAudio : public Fl_Group
+{
+private:
+
+ static void cb_deactivate_sounddev(Fl_Widget *w, void *p);
+ static void cb_fetchInChans (Fl_Widget *w, void *p);
+ static void cb_fetchOutChans (Fl_Widget *w, void *p);
+ static void cb_showInputInfo (Fl_Widget *w, void *p);
+ static void cb_showOutputInfo (Fl_Widget *w, void *p);
+ inline void __cb_deactivate_sounddev();
+ inline void __cb_fetchInChans();
+ inline void __cb_fetchOutChans();
+ inline void __cb_showInputInfo();
+ inline void __cb_showOutputInfo();
+
+ void fetchSoundDevs();
+ void fetchInChans(int menuItem);
+ void fetchOutChans(int menuItem);
+ int findMenuDevice(geChoice *m, int device);
+
+ int soundsysInitValue;
+
+public:
+
+ geChoice *soundsys;
+ geChoice *samplerate;
+ geChoice *rsmpQuality;
+ geChoice *sounddevIn;
+ geButton *devInInfo;
+ geChoice *channelsIn;
+ geChoice *sounddevOut;
+ geButton *devOutInfo;
+ geChoice *channelsOut;
+ geCheck *limitOutput;
+ geChoice *buffersize;
+ geInput *delayComp;
+
+ geTabAudio(int x, int y, int w, int h);
+
+ void save();
+};
+
+
+#endif
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * 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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#include <FL/Fl_Pack.H>
+#include "../../../core/const.h"
+#include "../../../core/conf.h"
+#include "../basics/box.h"
+#include "../basics/radio.h"
+#include "../basics/check.h"
+#include "tabBehaviors.h"
+
+
+using std::string;
+using namespace giada::m;
+
+
+geTabBehaviors::geTabBehaviors(int X, int Y, int W, int H)
+ : Fl_Group(X, Y, W, H, "Behaviors")
+{
+ begin();
+
+ Fl_Group *radioGrp_1 = new Fl_Group(x(), y()+10, w(), 70); // radio group for the mutex
+ new geBox(x(), y()+10, 70, 25, "When a channel with recorded actions is halted:", FL_ALIGN_LEFT);
+ recsStopOnChanHalt_1 = new geRadio(x()+25, y()+35, 280, 20, "stop it immediately");
+ recsStopOnChanHalt_0 = new geRadio(x()+25, y()+55, 280, 20, "play it until finished");
+ radioGrp_1->end();
+
+ Fl_Group *radioGrp_2 = new Fl_Group(x(), y()+70, w(), 70); // radio group for the mutex
+ new geBox(x(), y()+80, 70, 25, "When the sequencer is halted:", FL_ALIGN_LEFT);
+ chansStopOnSeqHalt_1 = new geRadio(x()+25, y()+105, 280, 20, "stop immediately all dynamic channels");
+ chansStopOnSeqHalt_0 = new geRadio(x()+25, y()+125, 280, 20, "play all dynamic channels until finished");
+ radioGrp_2->end();
+
+ treatRecsAsLoops = new geCheck(x(), y()+155, 280, 20, "Treat one shot channels with actions as loops");
+ inputMonitorDefaultOn = new geCheck(x(), y()+180, 280, 20, "New sample channels have input monitor on by default");
+
+ end();
+
+ labelsize(GUI_FONT_SIZE_BASE);
+
+ conf::recsStopOnChanHalt == 1 ? recsStopOnChanHalt_1->value(1) : recsStopOnChanHalt_0->value(1);
+ conf::chansStopOnSeqHalt == 1 ? chansStopOnSeqHalt_1->value(1) : chansStopOnSeqHalt_0->value(1);
+ treatRecsAsLoops->value(conf::treatRecsAsLoops);
+ inputMonitorDefaultOn->value(conf::inputMonitorDefaultOn);
+
+ recsStopOnChanHalt_1->callback(cb_radio_mutex, (void*)this);
+ recsStopOnChanHalt_0->callback(cb_radio_mutex, (void*)this);
+ chansStopOnSeqHalt_1->callback(cb_radio_mutex, (void*)this);
+ chansStopOnSeqHalt_0->callback(cb_radio_mutex, (void*)this);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geTabBehaviors::cb_radio_mutex(Fl_Widget *w, void *p) { ((geTabBehaviors*)p)->__cb_radio_mutex(w); }
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geTabBehaviors::__cb_radio_mutex(Fl_Widget *w)
+{
+ ((Fl_Button *)w)->type(FL_RADIO_BUTTON);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geTabBehaviors::save()
+{
+ conf::recsStopOnChanHalt = recsStopOnChanHalt_1->value() == 1 ? 1 : 0;
+ conf::chansStopOnSeqHalt = chansStopOnSeqHalt_1->value() == 1 ? 1 : 0;
+ conf::treatRecsAsLoops = treatRecsAsLoops->value() == 1 ? 1 : 0;
+ conf::inputMonitorDefaultOn = inputMonitorDefaultOn->value() == 1 ? 1 : 0;
+}
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * 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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#ifndef GE_TAB_BEHAVIORS_H
+#define GE_TAB_BEHAVIORS_H
+
+
+#include <FL/Fl_Group.H>
+
+
+class geRadio;
+class geCheck;
+
+
+class geTabBehaviors : public Fl_Group
+{
+private:
+
+ static void cb_radio_mutex (Fl_Widget *w, void *p);
+ inline void __cb_radio_mutex(Fl_Widget *w);
+
+public:
+
+ geRadio *recsStopOnChanHalt_1;
+ geRadio *recsStopOnChanHalt_0;
+ geRadio *chansStopOnSeqHalt_1;
+ geRadio *chansStopOnSeqHalt_0;
+ geCheck *treatRecsAsLoops;
+ geCheck *inputMonitorDefaultOn;
+
+ geTabBehaviors(int x, int y, int w, int h);
+
+ void save();
+};
+
+
+
+#endif
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * 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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#include <string>
+#include <rtmidi/RtMidi.h>
+#include "../../../core/const.h"
+#include "../../../core/conf.h"
+#include "../../../core/midiMapConf.h"
+#include "../../../core/kernelMidi.h"
+#include "../../../utils/gui.h"
+#include "../basics/box.h"
+#include "../basics/choice.h"
+#include "../basics/check.h"
+#include "tabMidi.h"
+
+
+using std::string;
+using namespace giada::m;
+
+
+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.");
+ end();
+
+ labelsize(GUI_FONT_SIZE_BASE);
+
+ system->callback(cb_changeSystem, (void*)this);
+
+ fetchSystems();
+ fetchOutPorts();
+ fetchInPorts();
+ fetchMidiMaps();
+
+ noNoteOff->value(conf::noNoteOff);
+
+ sync->add("(disabled)");
+ sync->add("MIDI Clock (master)");
+ sync->add("MTC (master)");
+ if (conf::midiSync == MIDI_SYNC_NONE)
+ sync->value(0);
+ else if (conf::midiSync == MIDI_SYNC_CLOCK_M)
+ sync->value(1);
+ else if (conf::midiSync == MIDI_SYNC_MTC_M)
+ sync->value(2);
+
+ systemInitValue = system->value();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geTabMidi::fetchOutPorts()
+{
+ if (kernelMidi::countOutPorts() == 0) {
+ portOut->add("-- no ports found --");
+ portOut->value(0);
+ portOut->deactivate();
+ }
+ else {
+
+ portOut->add("(disabled)");
+
+ for (unsigned i=0; i<kernelMidi::countOutPorts(); i++)
+ portOut->add(gu_removeFltkChars(kernelMidi::getOutPortName(i)).c_str());
+
+ portOut->value(conf::midiPortOut+1); // +1 because midiPortOut=-1 is '(disabled)'
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+
+void geTabMidi::fetchInPorts()
+{
+ if (kernelMidi::countInPorts() == 0) {
+ portIn->add("-- no ports found --");
+ portIn->value(0);
+ portIn->deactivate();
+ }
+ else {
+
+ portIn->add("(disabled)");
+
+ for (unsigned i=0; i<kernelMidi::countInPorts(); i++)
+ portIn->add(gu_removeFltkChars(kernelMidi::getInPortName(i)).c_str());
+
+ portIn->value(conf::midiPortIn+1); // +1 because midiPortIn=-1 is '(disabled)'
+ }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geTabMidi::fetchMidiMaps()
+{
+ if (midimap::maps.size() == 0) {
+ midiMap->add("(no MIDI maps available)");
+ midiMap->value(0);
+ midiMap->deactivate();
+ return;
+ }
+
+ for (unsigned i=0; i<midimap::maps.size(); i++) {
+ const char *imap = midimap::maps.at(i).c_str();
+ midiMap->add(imap);
+ if (conf::midiMapPath == imap)
+ midiMap->value(i);
+ }
+
+ /* Preselect the 0 midimap if nothing is selected but midimaps exist. */
+
+ if (midiMap->value() == -1 && midimap::maps.size() > 0)
+ midiMap->value(0);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geTabMidi::save()
+{
+ string text = system->text(system->value());
+
+ if (text == "ALSA")
+ conf::midiSystem = RtMidi::LINUX_ALSA;
+ else if (text == "Jack")
+ conf::midiSystem = RtMidi::UNIX_JACK;
+ else if (text == "Multimedia MIDI")
+ conf::midiSystem = RtMidi::WINDOWS_MM;
+ else if (text == "OSX Core MIDI")
+ conf::midiSystem = RtMidi::MACOSX_CORE;
+
+ conf::midiPortOut = portOut->value()-1; // -1 because midiPortOut=-1 is '(disabled)'
+ conf::midiPortIn = portIn->value()-1; // -1 because midiPortIn=-1 is '(disabled)'
+
+ conf::noNoteOff = noNoteOff->value();
+ conf::midiMapPath = midimap::maps.size() == 0 ? "" : midiMap->text(midiMap->value());
+
+ if (sync->value() == 0)
+ conf::midiSync = MIDI_SYNC_NONE;
+ else if (sync->value() == 1)
+ conf::midiSync = MIDI_SYNC_CLOCK_M;
+ else if (sync->value() == 2)
+ conf::midiSync = MIDI_SYNC_MTC_M;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geTabMidi::fetchSystems()
+{
+#if defined(__linux__)
+
+ if (kernelMidi::hasAPI(RtMidi::LINUX_ALSA))
+ system->add("ALSA");
+ if (kernelMidi::hasAPI(RtMidi::UNIX_JACK))
+ system->add("Jack");
+
+#elif defined(_WIN32)
+
+ if (kernelMidi::hasAPI(RtMidi::WINDOWS_MM))
+ system->add("Multimedia MIDI");
+
+#elif defined (__APPLE__)
+
+ system->add("OSX Core MIDI");
+
+#endif
+
+ switch (conf::midiSystem) {
+ case RtMidi::LINUX_ALSA: system->showItem("ALSA"); break;
+ case RtMidi::UNIX_JACK: system->showItem("Jack"); break;
+ case RtMidi::WINDOWS_MM: system->showItem("Multimedia MIDI"); break;
+ case RtMidi::MACOSX_CORE: system->showItem("OSX Core MIDI"); break;
+ default: system->value(0); break;
+ }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geTabMidi::cb_changeSystem(Fl_Widget *w, void *p) { ((geTabMidi*)p)->__cb_changeSystem(); }
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geTabMidi::__cb_changeSystem()
+{
+ /* if the user changes MIDI device (eg ALSA->JACK) device menu deactivates.
+ * If it returns to the original system, we re-fill the list by
+ * querying kernelMidi. */
+
+ if (systemInitValue == system->value()) {
+ portOut->clear();
+ fetchOutPorts();
+ portOut->activate();
+ portIn->clear();
+ fetchInPorts();
+ portIn->activate();
+ noNoteOff->activate();
+ sync->activate();
+ }
+ else {
+ portOut->deactivate();
+ portOut->clear();
+ portOut->add("-- restart to fetch device(s) --");
+ portOut->value(0);
+ portIn->deactivate();
+ portIn->clear();
+ portIn->add("-- restart to fetch device(s) --");
+ portIn->value(0);
+ noNoteOff->deactivate();
+ sync->deactivate();
+ }
+
+}
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * 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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#ifndef GE_TAB_MIDI_H
+#define GE_TAB_MIDI_H
+
+
+#include <FL/Fl_Group.H>
+
+
+class geChoice;
+class geCheck;
+
+
+class geTabMidi : public Fl_Group
+{
+private:
+
+ void fetchSystems();
+ void fetchOutPorts();
+ void fetchInPorts();
+ void fetchMidiMaps();
+
+ static void cb_changeSystem (Fl_Widget *w, void *p);
+ inline void __cb_changeSystem();
+
+ int systemInitValue;
+
+public:
+
+ geChoice *system;
+ geChoice *portOut;
+ geChoice *portIn;
+ geCheck *noNoteOff;
+ geChoice *midiMap;
+ geChoice *sync;
+
+ geTabMidi(int x, int y, int w, int h);
+
+ void save();
+};
+
+
+#endif
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * 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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#include "../../../core/const.h"
+#include "../../../core/conf.h"
+#include "../basics/choice.h"
+#include "tabMisc.h"
+
+
+using namespace giada::m;
+
+
+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");
+ end();
+
+ debugMsg->add("(disabled)");
+ debugMsg->add("To standard output");
+ debugMsg->add("To file");
+
+ labelsize(GUI_FONT_SIZE_BASE);
+
+ switch (conf::logMode) {
+ case LOG_MODE_MUTE:
+ debugMsg->value(0);
+ break;
+ case LOG_MODE_STDOUT:
+ debugMsg->value(1);
+ break;
+ case LOG_MODE_FILE:
+ debugMsg->value(2);
+ break;
+ }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geTabMisc::save()
+{
+ switch(debugMsg->value()) {
+ case 0:
+ conf::logMode = LOG_MODE_MUTE;
+ break;
+ case 1:
+ conf::logMode = LOG_MODE_STDOUT;
+ break;
+ case 2:
+ conf::logMode = LOG_MODE_FILE;
+ break;
+ }
+}
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * 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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#ifndef GE_TAB_MISC_H
+#define GE_TAB_MISC_H
+
+
+#include <FL/Fl_Group.H>
+
+
+class geChoice;
+
+
+class geTabMisc : public Fl_Group
+{
+public:
+
+ geChoice *debugMsg;
+
+ geTabMisc(int x, int y, int w, int h);
+
+ void save();
+};
+
+#endif
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * 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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#ifdef WITH_VST
+
+
+#include <FL/Fl.H>
+#include "../../core/const.h"
+#include "../../core/conf.h"
+#include "../../core/pluginHost.h"
+#include "../../utils/string.h"
+#include "../../utils/fs.h"
+#include "../basics/box.h"
+#include "../basics/radio.h"
+#include "../basics/check.h"
+#include "../basics/input.h"
+#include "../basics/button.h"
+#include "tabPlugins.h"
+
+
+using std::string;
+using namespace giada::m;
+
+
+geTabPlugins::geTabPlugins(int X, int Y, int W, int H)
+ : Fl_Group(X, Y, W, H, "Plugins")
+{
+ folderPath = new geInput(x()+w()-250, y()+8, 250, 20);
+ scanButton = new geButton(x()+w()-120, folderPath->y()+folderPath->h()+8, 120, 20);
+ info = new geBox(x(), scanButton->y()+scanButton->h()+8, w(), 242);
+
+ end();
+
+ labelsize(GUI_FONT_SIZE_BASE);
+
+ info->label("Scan in progress. Please wait...");
+ info->hide();
+
+ folderPath->value(conf::pluginPath.c_str());
+ folderPath->label("Plugins folder");
+
+ scanButton->callback(cb_scan, (void*) this);
+
+ updateCount();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geTabPlugins::updateCount()
+{
+ string scanLabel = "Scan (" + gu_itoa(pluginHost::countAvailablePlugins()) + " found)";
+ scanButton->label(scanLabel.c_str());
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geTabPlugins::cb_scan(Fl_Widget *w, void *p) { ((geTabPlugins*)p)->__cb_scan(w); }
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geTabPlugins::cb_onScan(float progress, void *p)
+{
+ string l = "Scan in progress (" + gu_itoa((int)(progress*100)) + "%). Please wait...";
+ ((geTabPlugins *)p)->info->label(l.c_str());
+ Fl::wait();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geTabPlugins::__cb_scan(Fl_Widget *w)
+{
+ info->show();
+ pluginHost::scanDir(folderPath->value(), cb_onScan, (void*) this);
+ pluginHost::saveList(gu_getHomePath() + G_SLASH + "plugins.xml");
+ info->hide();
+ updateCount();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geTabPlugins::save()
+{
+ conf::pluginPath = folderPath->value();
+}
+
+
+#endif // WITH_VST
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * 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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#ifndef GE_TAB_PLUGINS_H
+#define GE_TAB_PLUGINS_H
+
+
+#ifdef WITH_VST
+
+
+#include <FL/Fl_Group.H>
+
+
+class geInput;
+class geButton;
+class geBox;
+
+
+class geTabPlugins : public Fl_Group
+{
+private:
+
+ static void cb_scan (Fl_Widget *w, void *p);
+ static void cb_onScan(float progress, void *p);
+ inline void __cb_scan(Fl_Widget *w);
+
+ void updateCount();
+
+public:
+
+ geInput *folderPath;
+ geButton *scanButton;
+ geBox *info;
+
+ geTabPlugins(int x, int y, int w, int h);
+
+ void save();
+};
+
+
+#endif
+
+
+#endif
+++ /dev/null
-/* ---------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * ge_envelopeWidget
- *
- * Parent class of any envelope controller, from volume to VST parameter
- * automations.
- *
- * ---------------------------------------------------------------------
- *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
- *
- * This file is part of Giada - Your Hardcore Loopmachine.
- *
- * Giada - Your Hardcore Loopmachine is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General
- * Public License as published by the Free Software Foundation, either
- * version 3 of the License, or (at your option) any later version.
- *
- * Giada - Your Hardcore Loopmachine is distributed in the hope that it
- * will be useful, but WITHOUT ANY WARRANTY; without even the implied
- * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Giada - Your Hardcore Loopmachine. If not, see
- * <http://www.gnu.org/licenses/>.
- *
- * ------------------------------------------------------------------ */
-
-
-#include <FL/fl_draw.H>
-#include "../../core/channel.h"
-#include "../../core/recorder.h"
-#include "../../core/mixer.h"
-#include "../dialogs/gd_actionEditor.h"
-#include "../dialogs/gd_mainWindow.h"
-#include "mainWindow/keyboard/keyboard.h"
-#include "envelopeEditor.h"
-
-
-extern Mixer G_Mixer;
-extern Recorder G_Recorder;
-extern gdMainWindow *G_MainWin;
-
-
-geEnvelopeEditor::geEnvelopeEditor(int x, int y, gdActionEditor *pParent,
- int type, int range, const char *l)
- : geBaseActionEditor(x, y, 200, 80, pParent),
- l (l),
- type (type),
- range (range),
- selectedPoint (-1),
- draggedPoint (-1)
-{
- size(pParent->totalWidth, h());
-}
-
-
-/* ------------------------------------------------------------------ */
-
-
-geEnvelopeEditor::~geEnvelopeEditor() {
- clearPoints();
-}
-
-
-/* ------------------------------------------------------------------ */
-
-
-void geEnvelopeEditor::addPoint(int frame, int iValue, float fValue, int px, int py) {
- point p;
- p.frame = frame;
- p.iValue = iValue;
- p.fValue = fValue;
- p.x = px;
- p.y = py;
- points.push_back(p);
-}
-
-
-/* ------------------------------------------------------------------ */
-
-
-void geEnvelopeEditor::updateActions() {
- for (unsigned i=0; i<points.size(); i++)
- points.at(i).x = points.at(i).frame / pParent->zoom;
-}
-
-
-/* ------------------------------------------------------------------ */
-
-
-void geEnvelopeEditor::draw() {
-
- baseDraw();
-
- /* print label */
-
- fl_color(COLOR_BG_1);
- fl_font(FL_HELVETICA, 12);
- fl_draw(l, x()+4, y(), 80, h(), (Fl_Align) (FL_ALIGN_LEFT));
-
- int pxOld = x()-3;
- int pyOld = y()+1;
- int pxNew = 0;
- int pyNew = 0;
-
- fl_color(COLOR_BG_2);
-
- for (unsigned i=0; i<points.size(); i++) {
-
- pxNew = points.at(i).x+x()-3;
- pyNew = points.at(i).y+y();
-
- if (selectedPoint == (int) i) {
- fl_color(COLOR_BD_1);
- fl_rectf(pxNew, pyNew, 7, 7);
- fl_color(COLOR_BG_2);
- }
- else
- fl_rectf(pxNew, pyNew, 7, 7);
-
- if (i > 0)
- fl_line(pxOld+3, pyOld+3, pxNew+3, pyNew+3);
-
- pxOld = pxNew;
- pyOld = pyNew;
- }
-}
-
-
-/* ------------------------------------------------------------------ */
-
-
-int geEnvelopeEditor::handle(int e) {
-
- /* Adding an action: no further checks required, just record it on frame
- * mx*pParent->zoom. Deleting action is trickier: find the active
- * point and derive from it the corresponding frame. */
-
- int ret = 0;
- int mx = Fl::event_x()-x(); // mouse x
- int my = Fl::event_y()-y(); // mouse y
-
- switch (e) {
-
- case FL_ENTER: {
- ret = 1;
- break;
- }
-
- case FL_MOVE: {
- selectedPoint = getSelectedPoint();
- redraw();
- ret = 1;
- break;
- }
-
- case FL_LEAVE: {
- draggedPoint = -1;
- selectedPoint = -1;
- redraw();
- ret = 1;
- break;
- }
-
- case FL_PUSH: {
-
- /* left click on point: drag
- * right click on point: delete
- * left click on void: add */
-
- if (Fl::event_button1()) {
-
- if (selectedPoint != -1) {
- draggedPoint = selectedPoint;
- }
- else {
-
- /* top & border fix */
-
- if (my > h()-8) my = h()-8;
- if (mx > pParent->coverX-x()) mx = pParent->coverX-x();
-
- if (range == RANGE_FLOAT) {
-
- /* if this is the first point ever, add other two points at the beginning
- * and the end of the range */
-
- if (points.size() == 0) {
- addPoint(0, 0, 1.0f, 0, 1);
- G_Recorder.rec(pParent->chan->index, type, 0, 0, 1.0f);
- addPoint(G_Mixer.totalFrames, 0, 1.0f, pParent->coverX, 1);
- G_Recorder.rec(pParent->chan->index, type, G_Mixer.totalFrames, 0, 1.0f);
- pParent->chan->hasActions = true;
- }
-
- /* line between 2 points y = (x-a) / (b-a); a = h() - 8; b = 1 */
-
- int frame = mx * pParent->zoom;
- float value = (my - h() + 8) / (float) (1 - h() + 8);
- addPoint(frame, 0, value, mx, my);
- G_Recorder.rec(pParent->chan->index, type, frame, 0, value);
- pParent->chan->hasActions = true;
- G_Recorder.sortActions();
- sortPoints();
- }
- else {
- /// TODO
- }
- G_MainWin->keyboard->setChannelWithActions((geSampleChannel*)pParent->chan->guiChannel); // update mainWindow
- redraw();
- }
- }
- else {
-
- /* right click on point 0 or point size-1 deletes the entire envelope */
-
- if (selectedPoint != -1) {
- if (selectedPoint == 0 || (unsigned) selectedPoint == points.size()-1) {
- G_Recorder.clearAction(pParent->chan->index, type);
- pParent->chan->hasActions = G_Recorder.hasActions(pParent->chan->index);
- points.clear();
- }
- else {
- G_Recorder.deleteAction(pParent->chan->index,
- points.at(selectedPoint).frame, type, false, &G_Mixer.mutex_recs);
- pParent->chan->hasActions = G_Recorder.hasActions(pParent->chan->index);
- G_Recorder.sortActions();
- points.erase(points.begin() + selectedPoint);
- }
- G_MainWin->keyboard->setChannelWithActions((geSampleChannel*)pParent->chan->guiChannel); // update mainWindow
- redraw();
- }
- }
-
- ret = 1;
- break;
- }
-
- case FL_RELEASE: {
- if (draggedPoint != -1) {
-
- if (points.at(draggedPoint).x == previousXPoint) {
- //gu_log("nothing to do\n");
- }
- else {
- int newFrame = points.at(draggedPoint).x * pParent->zoom;
-
- /* x edge correction */
-
- if (newFrame < 0)
- newFrame = 0;
- else if (newFrame > G_Mixer.totalFrames)
- newFrame = G_Mixer.totalFrames;
-
- /* vertical line check */
-
- int vp = verticalPoint(points.at(draggedPoint));
- if (vp == 1) newFrame -= 256;
- else if (vp == -1) newFrame += 256;
-
- /* delete previous point and record a new one */
-
- G_Recorder.deleteAction(pParent->chan->index,
- points.at(draggedPoint).frame, type, false, &G_Mixer.mutex_recs);
- pParent->chan->hasActions = G_Recorder.hasActions(pParent->chan->index);
-
- if (range == RANGE_FLOAT) {
- float value = (points.at(draggedPoint).y - h() + 8) / (float) (1 - h() + 8);
- G_Recorder.rec(pParent->chan->index, type, newFrame, 0, value);
- pParent->chan->hasActions = true;
- }
- else {
- /// TODO
- }
-
- G_Recorder.sortActions();
- points.at(draggedPoint).frame = newFrame;
- draggedPoint = -1;
- selectedPoint = -1;
- }
- }
- ret = 1;
- break;
- }
-
- case FL_DRAG: {
-
- if (draggedPoint != -1) {
-
- /* y constraint */
-
- if (my > h()-8)
- points.at(draggedPoint).y = h()-8;
- else
- if (my < 1)
- points.at(draggedPoint).y = 1;
- else
- points.at(draggedPoint).y = my;
-
- /* x constraint
- * constrain the point between two ends (leftBorder-point, point-point,
- * point-rightBorder). First & last points cannot be shifted on x */
-
- if (draggedPoint == 0)
- points.at(draggedPoint).x = x()-8;
- else
- if ((unsigned) draggedPoint == points.size()-1)
- points.at(draggedPoint).x = pParent->coverX;
- else {
- int prevPoint = points.at(draggedPoint-1).x;
- int nextPoint = points.at(draggedPoint+1).x;
- if (mx <= prevPoint)
- points.at(draggedPoint).x = prevPoint;
- else
- if (mx >= nextPoint)
- points.at(draggedPoint).x = nextPoint;
- //else
- // points.at(draggedPoint).x = mx;
- else {
- if (pParent->gridTool->isOn())
- points.at(draggedPoint).x = pParent->gridTool->getSnapPoint(mx)-1;
- else
- points.at(draggedPoint).x = mx;
- }
- }
- redraw();
- }
-
- ret = 1;
- break;
- }
- }
-
- return ret;
-}
-
-
-/* ------------------------------------------------------------------ */
-
-
-int geEnvelopeEditor::verticalPoint(const point &p) {
- for (unsigned i=0; i<points.size(); i++) {
- if (&p == &points.at(i)) {
- if (i == 0 || i == points.size()-1) // first or last point
- return 0;
- else {
- if (points.at(i-1).x == p.x) // vertical with point[i-1]
- return -1;
- else
- if (points.at(i+1).x == p.x) // vertical with point[i+1]
- return 1;
- }
- break;
- }
- }
- return 0;
-}
-
-
-/* ------------------------------------------------------------------ */
-
-
-void geEnvelopeEditor::sortPoints() {
- for (unsigned i=0; i<points.size(); i++)
- for (unsigned j=0; j<points.size(); j++)
- if (points.at(j).x > points.at(i).x)
- std::swap(points.at(j), points.at(i));
-}
-
-
-/* ------------------------------------------------------------------ */
-
-
-int geEnvelopeEditor::getSelectedPoint() {
-
- /* point is a 7x7 dot */
-
- for (unsigned i=0; i<points.size(); i++) {
- if (Fl::event_x() >= points.at(i).x+x()-4 &&
- Fl::event_x() <= points.at(i).x+x()+4 &&
- Fl::event_y() >= points.at(i).y+y() &&
- Fl::event_y() <= points.at(i).y+y()+7)
- return i;
- }
- return -1;
-}
-
-
-/* ------------------------------------------------------------------ */
-
-
-void geEnvelopeEditor::fill() {
- points.clear();
- for (unsigned i=0; i<G_Recorder.global.size(); i++)
- for (unsigned j=0; j<G_Recorder.global.at(i).size(); j++) {
- Recorder::action *a = G_Recorder.global.at(i).at(j);
- if (a->type == type && a->chan == pParent->chan->index) {
- if (range == RANGE_FLOAT)
- addPoint(
- a->frame, // frame
- 0, // int value (unused)
- a->fValue, // float value
- a->frame / pParent->zoom, // x
- ((1-h()+8)*a->fValue)+h()-8); // y = (b-a)x + a (line between two points)
- // else: TODO
- }
- }
-
-}
+++ /dev/null
-/* ---------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * ge_envelopeWidget
- *
- * parent class of any envelope controller, from volume to VST parameter
- * automations.
- *
- * ---------------------------------------------------------------------
- *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
- *
- * This file is part of Giada - Your Hardcore Loopmachine.
- *
- * Giada - Your Hardcore Loopmachine is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General
- * Public License as published by the Free Software Foundation, either
- * version 3 of the License, or (at your option) any later version.
- *
- * Giada - Your Hardcore Loopmachine is distributed in the hope that it
- * will be useful, but WITHOUT ANY WARRANTY; without even the implied
- * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Giada - Your Hardcore Loopmachine. If not, see
- * <http://www.gnu.org/licenses/>.
- *
- * ------------------------------------------------------------------ */
-
-#ifndef __GE_ENVELOPECHANNEL_H__
-#define __GE_ENVELOPECHANNEL_H__
-
-
-#include <vector>
-#include <FL/Fl.H>
-#include <FL/Fl_Group.H>
-#include "../../utils/fs.h"
-#include "baseActionEditor.h"
-
-
-using std::vector;
-
-
-class geEnvelopeEditor : public geBaseActionEditor
-{
- const char *l; // internal label
- int type; // type of action
- int range;
-
- /* point
- * a single dot in the graph. x = relative frame, y = relative value */
-
- struct point
- {
- int frame;
- int iValue;
- float fValue;
- int x;
- int y;
- };
-
- /* points
- * array of points, filled by fillPoints() */
-
- vector<point> points;
-
- /* selectedPoint
- * which point we are selecting? */
-
- int selectedPoint;
-
- /* draggedPoint
- * which point we are dragging? */
-
- int draggedPoint;
-
- /* previousXPoint
- * x coordinate of point at time t-1. Used to check effective shifts */
-
- int previousXPoint;
-
- void draw();
-
- int handle(int e);
-
- int getSelectedPoint();
-
- void sortPoints();
-
- /* verticalPoint
- * check if two points form a vertical line. In that case the frame value
- * would be the same and recorder would go crazy, so shift by a small value
- * of frames to create a minimal fadein/fadeout level. Return 0: no
- * vertical points; return 1: vertical with the next one, return -1: vertical
- * with the previous one. */
-
- int verticalPoint(const point &p);
-
-public:
-
- geEnvelopeEditor(int x, int y, gdActionEditor *pParent, int type, int range, const char *l);
- ~geEnvelopeEditor();
-
- /* addPoint
- * add a point made of frame+value to internal points[]. */
-
- void addPoint(int frame, int iValue=0, float fValue=0.0f, int x=-1, int y=-1);
-
- void updateActions();
-
- /* fill
- * parse recorder's stack and fill the widget with points. It's up to
- * the caller to call this method as initialization. */
-
- void fill();
-
- inline void clearPoints() { points.clear(); }
-};
-
-#endif
+++ /dev/null
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * ge_mixed
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
- *
- * This file is part of Giada - Your Hardcore Loopmachine.
- *
- * Giada - Your Hardcore Loopmachine is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General
- * Public License as published by the Free Software Foundation, either
- * version 3 of the License, or (at your option) any later version.
- *
- * Giada - Your Hardcore Loopmachine is distributed in the hope that it
- * will be useful, but WITHOUT ANY WARRANTY; without even the implied
- * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Giada - Your Hardcore Loopmachine. If not, see
- * <http://www.gnu.org/licenses/>.
- *
- * -------------------------------------------------------------------------- */
-
-
-#include <math.h>
-#include "../../core/const.h"
-#include "../../core/mixer.h"
-#include "../../core/graphics.h"
-#include "../../core/recorder.h"
-#include "../../core/channel.h"
-#include "../../core/sampleChannel.h"
-#include "../../utils/gui.h"
-#include "../dialogs/gd_mainWindow.h"
-#include "basics/boxtypes.h"
-#include "ge_mixed.h"
-
-
-extern Mixer G_Mixer;
-extern unsigned G_beats;
-extern bool G_audio_status;
-extern gdMainWindow *mainWin;
-
-
-void __cb_window_closer(Fl_Widget *v, void *p)
-{
- delete (Fl_Window*)p;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-gButton::gButton(int X, int Y, int W, int H, const char *L, const char **imgOff, const char **imgOn)
- : gClick(X, Y, W, H, L, imgOff, imgOn) {}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-gClick::gClick(int x, int y, int w, int h, const char *L, const char **imgOff, const char **imgOn)
-: gBaseButton(x, y, w, h, L),
- imgOff(imgOff),
- imgOn(imgOn),
- bgColor0(COLOR_BG_0),
- bgColor1(COLOR_BG_1),
- bdColor(COLOR_BD_0),
- txtColor(COLOR_TEXT_0) {}
-
-void gClick::draw()
-{
- if (!active()) txtColor = bdColor;
- else txtColor = COLOR_TEXT_0;
-
- fl_rect(x(), y(), w(), h(), bdColor); // borders
- if (value()) { // -- clicked
- if (imgOn != NULL)
- fl_draw_pixmap(imgOn, x()+1, y()+1);
- else
- fl_rectf(x(), y(), w(), h(), bgColor1); // covers the border
- }
- else { // -- not clicked
- fl_rectf(x()+1, y()+1, w()-2, h()-2, bgColor0); // bg inside the border
- if (imgOff != NULL)
- fl_draw_pixmap(imgOff, x()+1, y()+1);
- }
- if (!active())
- fl_color(FL_INACTIVE_COLOR);
-
- fl_color(txtColor);
- fl_font(FL_HELVETICA, GUI_FONT_SIZE_BASE);
- fl_draw(label(), x()+2, y(), w()-2, h(), FL_ALIGN_CENTER);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-gClickRepeat::gClickRepeat(int x, int y, int w, int h, const char *L, const char **imgOff, const char **imgOn)
-: Fl_Repeat_Button(x, y, w, h, L), imgOff(imgOff), imgOn(imgOn) {}
-
-void gClickRepeat::draw()
-{
- if (value()) { // -- clicked
- fl_rectf(x(), y(), w(), h(), COLOR_BG_1); // bg
- if (imgOn != NULL)
- fl_draw_pixmap(imgOn, x()+1, y()+1);
- }
- else { // -- not clicked
- fl_rectf(x(), y(), w(), h(), COLOR_BG_0); // bg
- fl_rect(x(), y(), w(), h(), COLOR_BD_0); // border
- if (imgOff != NULL)
- fl_draw_pixmap(imgOff, x()+1, y()+1);
- }
- if (!active())
- fl_color(FL_INACTIVE_COLOR);
-
- fl_color(COLOR_TEXT_0);
- fl_font(FL_HELVETICA, GUI_FONT_SIZE_BASE);
- fl_draw(label(), x(), y(), w(), h(), FL_ALIGN_CENTER);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-gInput::gInput(int x, int y, int w, int h, const char *L)
-: Fl_Input(x, y, w, h, L)
-{
- //Fl::set_boxtype(G_CUSTOM_BORDER_BOX, gDrawBox, 1, 1, 2, 2);
- box(G_CUSTOM_BORDER_BOX);
- labelsize(GUI_FONT_SIZE_BASE);
- labelcolor(COLOR_TEXT_0);
- color(COLOR_BG_DARK);
- textcolor(COLOR_TEXT_0);
- cursor_color(COLOR_TEXT_0);
- selection_color(COLOR_BD_0);
- textsize(GUI_FONT_SIZE_BASE);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-gDial::gDial(int x, int y, int w, int h, const char *L)
-: Fl_Dial(x, y, w, h, L)
-{
- labelsize(GUI_FONT_SIZE_BASE);
- labelcolor(COLOR_TEXT_0);
- align(FL_ALIGN_LEFT);
- type(FL_FILL_DIAL);
- angles(0, 360);
- color(COLOR_BG_0); // background
- selection_color(COLOR_BG_1); // selection
-}
-
-void gDial::draw()
-{
- double angle = (angle2()-angle1())*(value()-minimum())/(maximum()-minimum()) + angle1();
-
- fl_color(COLOR_BG_0);
- fl_pie(x(), y(), w(), h(), 270-angle1(), angle > angle1() ? 360+270-angle : 270-360-angle);
-
- fl_color(COLOR_BD_0);
- fl_arc(x(), y(), w(), h(), 0, 360);
- fl_pie(x(), y(), w(), h(), 270-angle, 270-angle1());
-}
-
-/* -------------------------------------------------------------------------- */
-
-
-gBox::gBox(int x, int y, int w, int h, const char *L, Fl_Align al)
-: Fl_Box(x, y, w, h)
-{
- copy_label(L);
- labelsize(GUI_FONT_SIZE_BASE);
- box(FL_NO_BOX);
- labelcolor(COLOR_TEXT_0);
- if (al != 0)
- align(al | FL_ALIGN_INSIDE);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-gCheck::gCheck(int x, int y, int w, int h, const char *L)
-: Fl_Check_Button(x, y, w, h, L) {}
-
-void gCheck::draw()
-{
- int color = !active() ? FL_INACTIVE_COLOR : COLOR_BD_0;
-
- if (value()) {
- fl_rect(x(), y(), 12, 12, (Fl_Color) color);
- fl_rectf(x(), y(), 12, 12, (Fl_Color) color);
- }
- else {
- fl_rectf(x(), y(), 12, 12, FL_BACKGROUND_COLOR);
- fl_rect(x(), y(), 12, 12, (Fl_Color) color);
- }
-
- fl_rectf(x()+20, y(), w(), h(), FL_BACKGROUND_COLOR); // clearer
- fl_font(FL_HELVETICA, GUI_FONT_SIZE_BASE);
- fl_color(!active() ? FL_INACTIVE_COLOR : COLOR_TEXT_0);
- fl_draw(label(), x()+20, y(), w(), h(), (Fl_Align) (FL_ALIGN_LEFT | FL_ALIGN_TOP));
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-gRadio::gRadio(int x, int y, int w, int h, const char *L)
-: Fl_Radio_Button(x, y, w, h, L) {}
-
-void gRadio::draw()
-{
- int color = !active() ? FL_INACTIVE_COLOR : COLOR_BD_0;
-
- if (value()) {
- fl_rect(x(), y(), 12, 12, (Fl_Color) color);
- fl_rectf(x(), y(), 12, 12, (Fl_Color) color);
- }
- else {
- fl_rectf(x(), y(), 12, 12, FL_BACKGROUND_COLOR);
- fl_rect(x(), y(), 12, 12, (Fl_Color) color);
- }
-
- fl_rectf(x()+20, y(), w(), h(), FL_BACKGROUND_COLOR); // clearer
- fl_font(FL_HELVETICA, GUI_FONT_SIZE_BASE);
- fl_color(COLOR_TEXT_0);
- fl_draw(label(), x()+20, y(), w(), h(), (Fl_Align) (FL_ALIGN_LEFT | FL_ALIGN_TOP));
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-gProgress::gProgress(int x, int y, int w, int h, const char *L)
-: Fl_Progress(x, y, w, h, L) {
- color(COLOR_BG_0, COLOR_BD_0);
- box(G_CUSTOM_BORDER_BOX);
-
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-gSoundMeter::gSoundMeter(int x, int y, int w, int h, const char *L)
- : Fl_Box(x, y, w, h, L),
- clip(false),
- mixerPeak(0.0f),
- peak(0.0f),
- db_level(0.0f),
- db_level_old(0.0f) {}
-
-void gSoundMeter::draw()
-{
- fl_rect(x(), y(), w(), h(), COLOR_BD_0);
-
- /* peak = the highest value inside the frame */
-
- peak = 0.0f;
- float tmp_peak = 0.0f;
-
- tmp_peak = fabs(mixerPeak);
- if (tmp_peak > peak)
- peak = tmp_peak;
-
- clip = peak >= 1.0f ? true : false; // 1.0f is considered clip
-
-
- /* dBFS (full scale) calculation, plus decay of -2dB per frame */
-
- db_level = 20 * log10(peak);
- if (db_level < db_level_old)
- if (db_level_old > -DB_MIN_SCALE)
- db_level = db_level_old - 2.0f;
-
- db_level_old = db_level;
-
- /* graphical part */
-
- float px_level = 0.0f;
- if (db_level < 0.0f)
- px_level = ((w()/DB_MIN_SCALE) * db_level) + w();
- else
- px_level = w();
-
- fl_rectf(x()+1, y()+1, w()-2, h()-2, COLOR_BG_0);
- fl_rectf(x()+1, y()+1, (int) px_level, h()-2, clip || !G_audio_status ? COLOR_ALERT : COLOR_BD_0);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-gChoice::gChoice(int x, int y, int w, int h, const char *l, bool ang)
- : Fl_Choice(x, y, w, h, l), angle(ang)
-{
- labelsize(GUI_FONT_SIZE_BASE);
- labelcolor(COLOR_TEXT_0);
- box(FL_BORDER_BOX);
- textsize(GUI_FONT_SIZE_BASE);
- textcolor(COLOR_TEXT_0);
- color(COLOR_BG_0);
-}
-
-
-void gChoice::draw()
-{
- fl_rectf(x(), y(), w(), h(), COLOR_BG_0); // bg
- fl_rect(x(), y(), w(), h(), (Fl_Color) COLOR_BD_0); // border
- if (angle)
- fl_polygon(x()+w()-8, y()+h()-1, x()+w()-1, y()+h()-8, x()+w()-1, y()+h()-1);
-
- /* pick up the text() from the selected item (value()) and print it in
- * the box and avoid overflows */
-
- fl_color(!active() ? COLOR_BD_0 : COLOR_TEXT_0);
- if (value() != -1) {
- if (fl_width(text(value())) < w()-8) {
- fl_draw(text(value()), x(), y(), w(), h(), FL_ALIGN_CENTER);
- }
- else {
- std::string tmp = text(value());
- int size = tmp.size();
- while (fl_width(tmp.c_str()) >= w()-16) {
- tmp.resize(size);
- size--;
- }
- tmp += "...";
- fl_draw(tmp.c_str(), x(), y(), w(), h(), FL_ALIGN_CENTER);
- }
-
- }
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-gLiquidScroll::gLiquidScroll(int x, int y, int w, int h, const char *l)
- : Fl_Scroll(x, y, w, h, l)
-{
- type(Fl_Scroll::VERTICAL);
- scrollbar.color(COLOR_BG_0);
- scrollbar.selection_color(COLOR_BG_1);
- scrollbar.labelcolor(COLOR_BD_1);
- scrollbar.slider(G_CUSTOM_BORDER_BOX);
-}
-
-
-void gLiquidScroll::resize(int X, int Y, int W, int H)
-{
- int nc = children()-2; // skip hscrollbar and vscrollbar
- for ( int t=0; t<nc; t++) { // tell children to resize to our new width
- Fl_Widget *c = child(t);
- c->resize(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);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-gSlider::gSlider(int x, int y, int w, int h, const char *l)
- : Fl_Slider(x, y, w, h, l)
-{
- type(FL_HOR_FILL_SLIDER);
-
- labelsize(GUI_FONT_SIZE_BASE);
- align(FL_ALIGN_LEFT);
- labelcolor(COLOR_TEXT_0);
-
- box(G_CUSTOM_BORDER_BOX);
- color(COLOR_BG_0);
- selection_color(COLOR_BD_0);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-gResizerBar::gResizerBar(int X,int Y,int W,int H, bool vertical)
- : Fl_Box(X,Y,W,H), vertical(vertical)
-{
- last_y = 0;
- min_h = 30;
- if (vertical) {
- orig_h = H;
- labelsize(H);
- }
- else {
- orig_h = W;
- labelsize(W);
- }
- align(FL_ALIGN_CENTER|FL_ALIGN_INSIDE);
- labelfont(FL_COURIER);
- visible_focus(0);
-}
-
-/*
-gResizerBar::~gResizerBar()
-{
- gu_log("------ resizerbar %p destroyed\n", (void*)this);
-}
-*/
-
-void gResizerBar::HandleDrag(int diff)
-{
- Fl_Scroll *grp = (Fl_Scroll*)parent();
- int top;
- int bot;
- if (vertical) {
- top = y();
- bot = y()+h();
- }
- else {
- top = x();
- bot = x()+w();
- }
-
- // First pass: find widget directly above us with common edge
- // Possibly clamp 'diff' if widget would get too small..
-
- for (int t=0; t<grp->children(); t++) {
- Fl_Widget *wd = grp->child(t);
- if (vertical) {
- if ((wd->y()+wd->h()) == top) { // found widget directly above?
- if ((wd->h()+diff) < min_h)
- diff = wd->h() - min_h; // clamp
- wd->resize(wd->x(), wd->y(), wd->w(), wd->h()+diff); // change height
- break; // done with first pass
- }
- }
- else {
- if ((wd->x()+wd->w()) == top) { // found widget directly above?
- if ((wd->w()+diff) < min_h)
- diff = wd->w() - min_h; // clamp
- wd->resize(wd->x(), wd->y(), wd->w()+diff, wd->h()); // change height
- break; // done with first pass
- }
- }
- }
-
- // Second pass: find widgets below us, move based on clamped diff
-
- for (int t=0; t<grp->children(); t++) {
- Fl_Widget *wd = grp->child(t);
- if (vertical) {
- if (wd->y() >= bot) // found widget below us?
- wd->resize(wd->x(), wd->y()+diff, wd->w(), wd->h()); // change position
- }
- else {
- if (wd->x() >= bot)
- wd->resize(wd->x()+diff, wd->y(), wd->w(), wd->h());
- }
- }
-
- // Change our position last
-
- if (vertical)
- resize(x(), y()+diff, w(), h());
- else
- resize(x()+diff, y(), w(), h());
-
- grp->init_sizes();
- grp->redraw();
-}
-
-
-int gResizerBar::handle(int e)
-{
- int ret = 0;
- int this_y;
- if (vertical)
- this_y = Fl::event_y_root();
- else
- this_y = Fl::event_x_root();
- switch (e) {
- case FL_FOCUS:
- ret = 1;
- break;
- case FL_ENTER:
- ret = 1;
- fl_cursor(vertical ? FL_CURSOR_NS : FL_CURSOR_WE);
- break;
- case FL_LEAVE:
- ret = 1;
- fl_cursor(FL_CURSOR_DEFAULT);
- break;
- case FL_PUSH:
- ret = 1;
- last_y = this_y;
- break;
- case FL_DRAG:
- HandleDrag(this_y-last_y);
- last_y = this_y;
- ret = 1;
- break;
- default: break;
- }
- return(Fl_Box::handle(e) | ret);
-}
-
-
-void gResizerBar::resize(int X,int Y,int W,int H)
-{
- if (vertical)
- Fl_Box::resize(X,Y,W,orig_h); // height of resizer stays constant size
- else
- Fl_Box::resize(X,Y,orig_h,H);
-}
-
-
-/* -------------------------------------------------------------------------- */
-/* -------------------------------------------------------------------------- */
-/* -------------------------------------------------------------------------- */
-
-
-gBaseButton::gBaseButton(int x, int y, int w, int h, const char *l)
- : Fl_Button(x, y, w, h, l)
-{
- initLabel = l ? l : "";
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gBaseButton::trimLabel()
-{
- if (initLabel.empty())
- return;
-
- std::string out;
- if (w() > 20) {
- out = initLabel;
- int len = initLabel.size();
- while (fl_width(out.c_str(), out.size()) > w()) {
- out = initLabel.substr(0, len) + "...";
- len--;
- }
- }
- else {
- out = "";
- }
- copy_label(out.c_str());
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gBaseButton::label(const char *l)
-{
- Fl_Button::label(l);
- initLabel = l;
- trimLabel();
-}
-
-const char *gBaseButton::label()
-{
- return Fl_Button::label();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gBaseButton::resize(int X, int Y, int W, int H)
-{
- trimLabel();
- Fl_Button::resize(X, Y, W, H);
-}
-
-
-/* -------------------------------------------------------------------------- */
-/* -------------------------------------------------------------------------- */
-/* -------------------------------------------------------------------------- */
-
-
-gFxButton::gFxButton(int x, int y, int w, int h, const char **imgOff, const char **imgOn)
- : gClick(x, y, w, h, NULL, imgOff, imgOn), full(false) {}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gFxButton::draw()
-{
- gClick::draw();
- if (full)
- fl_draw_pixmap(imgOn, x()+1, y()+1, COLOR_BD_0);
-}
+++ /dev/null
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * ge_mixed
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
- *
- * This file is part of Giada - Your Hardcore Loopmachine.
- *
- * Giada - Your Hardcore Loopmachine is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General
- * Public License as published by the Free Software Foundation, either
- * version 3 of the License, or (at your option) any later version.
- *
- * Giada - Your Hardcore Loopmachine is distributed in the hope that it
- * will be useful, but WITHOUT ANY WARRANTY; without even the implied
- * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Giada - Your Hardcore Loopmachine. If not, see
- * <http://www.gnu.org/licenses/>.
- *
- * -------------------------------------------------------------------------- */
-
-
-#ifndef GE_MIXED_H
-#define GE_MIXED_H
-
-#include <stdio.h>
-#include <dirent.h>
-#include <stdint.h> // for intptr_t
-#include <string>
-#include <FL/Fl.H>
-#include <FL/Fl_Menu_Window.H>
-#include <FL/Fl_Button.H>
-#include <FL/Fl_Repeat_Button.H>
-#include <FL/Fl_Check_Button.H>
-#include <FL/Fl_Box.H>
-#include <FL/fl_draw.H>
-#include <FL/Fl_Dial.H>
-#include <FL/Fl_Pixmap.H>
-#include <FL/Fl_Menu_Button.H>
-#include <FL/Fl_Hold_Browser.H>
-#include <FL/Fl_Radio_Button.H>
-#include <FL/Fl_Progress.H>
-#include <FL/Fl_Input.H>
-#include <FL/Fl_Int_Input.H>
-#include <FL/Fl_Choice.H>
-#include <FL/Fl_Scroll.H>
-
-#ifdef _WIN32
- #include <shlobj.h> // for SHGetFolderPath
-#endif
-
-
-/* cb_window_closer
- * callback for when closing windows. Deletes the widget (delete). */
-
-void __cb_window_closer(Fl_Widget *v, void *p);
-
-
-/* -------------------------------------------------------------------------- */
-
-
-class gBaseButton : public Fl_Button
-{
-private:
- std::string initLabel;
-
- void trimLabel();
-
-public:
- gBaseButton(int x, int y, int w, int h, const char *l=0);
- void resize(int x, int y, int w, int h);
- void label(const char *l);
- const char *label();
-};
-
-
-/* -------------------------------------------------------------------------- */
-
-
-/* gClick
- * a normal button. */
-
-class gClick : public gBaseButton
-{
-public:
- gClick(int x, int y, int w, int h, const char *L=0, const char **imgOff=NULL, const char **imgOn=NULL);
- void draw();
- const char **imgOff;
- const char **imgOn;
- Fl_Color bgColor0; // background not clicked
- Fl_Color bgColor1; // background clicked
- Fl_Color bdColor; // border
- Fl_Color txtColor; // testo
-};
-
-
-/* -------------------------------------------------------------------------- */
-
-
-class gClickRepeat : public Fl_Repeat_Button
-{
-public:
- gClickRepeat(int x, int y, int w, int h, const char *L=0, const char **imgOff=NULL, const char **imgOn=NULL);
- void draw();
- const char **imgOff;
- const char **imgOn;
-};
-
-
-/* -------------------------------------------------------------------------- */
-
-
-/* gButton
- * exactly as gClick but with a unique id inside of it. Used for the buttons in
- * channels and for FXs. */
- /* TODO - is this really useful? */
-
-class gButton : public gClick
-{
-public:
- gButton(int X,int Y,int W,int H,const char *L=0, const char **imgOff=NULL, const char **imgOn=NULL);
- int key;
- int id;
-};
-
-
-/* -------------------------------------------------------------------------- */
-
-
-class gInput : public Fl_Input
-{
-public:
- gInput(int x, int y, int w, int h, const char *L=0);
-};
-
-
-/* -------------------------------------------------------------------------- */
-
-
-class gDial : public Fl_Dial
-{
-public:
- gDial(int x, int y, int w, int h, const char *L=0);
- void draw();
-};
-
-
-/* -------------------------------------------------------------------------- */
-
-
-class gBox : public Fl_Box
-{
-public:
- gBox(int x, int y, int w, int h, const char *L=0, Fl_Align al=FL_ALIGN_CENTER);
-};
-
-
-/* -------------------------------------------------------------------------- */
-
-
-class gCheck : public Fl_Check_Button
-{
-public:
- gCheck(int x, int y, int w, int h, const char *L=0);
- void draw();
-};
-
-
-/* -------------------------------------------------------------------------- */
-
-
-class gRadio : public Fl_Radio_Button
-{
-public:
- gRadio(int x, int y, int w, int h, const char *L=0);
- void draw();
-};
-
-
-/* -------------------------------------------------------------------------- */
-
-
-class gProgress : public Fl_Progress
-{
-public:
- gProgress(int x, int y, int w, int h, const char *L=0);
-};
-
-
-/* -------------------------------------------------------------------------- */
-
-
-class gSoundMeter : public Fl_Box
-{
-public:
- gSoundMeter(int X,int Y,int W,int H,const char *L=0);
- void draw();
- bool clip;
- float mixerPeak; // peak from mixer
-private:
- float peak;
- float db_level;
- float db_level_old;
-};
-
-
-/* -------------------------------------------------------------------------- */
-
-
-class gBeatMeter : public Fl_Box
-{
-public:
- gBeatMeter(int X,int Y,int W,int H,const char *L=0);
- void draw();
-};
-
-
-/* -------------------------------------------------------------------------- */
-
-
-class gChoice : public Fl_Choice
-{
-public:
-
- gChoice(int X,int Y,int W,int H,const char *L=0, bool angle=true);
- void draw();
-
- inline void showItem(const char *c) {value(find_index(c)); }
-
- bool angle;
- int id;
-};
-
-
-/* -------------------------------------------------------------------------- */
-
-
-/* gLiquidScroll
- * custom scroll that tells children to follow scroll's width when
- * resized. Thanks to Greg Ercolano from FLTK dev team.
- * http://seriss.com/people/erco/fltk/ */
-
-class gLiquidScroll : public Fl_Scroll
-{
-public:
- gLiquidScroll(int x, int y, int w, int h, const char *l=0);
- void resize(int x, int y, int w, int h);
-};
-
-
-/* -------------------------------------------------------------------------- */
-
-/* gResizerBar
- * 'resizer bar' between widgets Fl_Scroll. Thanks to Greg Ercolano from
- * FLTK dev team. http://seriss.com/people/erco/fltk/
- *
- * Shows a resize cursor when hovered over.
- * Assumes:
- * - Parent is an Fl_Scroll
- * - All children of Fl_Scroll are vertically arranged
- * - The widget above us has a bottom edge touching our top edge
- * ie. (w->y()+w->h() == this->y())
- *
- * When this widget is dragged:
- * - The widget above us (with a common edge) will be /resized/
- * vertically
- * - All children below us will be /moved/ vertically */
-
-/* TODO - use more general variable names
- * (last_y -> last_?, min_h -> min_?, ...) */
-
-class gResizerBar : public Fl_Box
-{
-private:
- bool vertical;
- int orig_h;
- int last_y;
- int min_h; // min height for widget above us
-
- void HandleDrag(int diff);
-
-public:
-
- /* 'vertical' defines the bar movement. Vertical=true: the bar moves
- * vertically (up and down). */
-
- gResizerBar(int x, int y, int w, int h, bool vertical=true);
- //~gResizerBar();
-
- inline void setMinSize(int val) { min_h = val; }
- inline int getMinSize() { return min_h; }
-
- int handle(int e);
- void resize(int x, int y, int w, int h);
-};
-
-
-/* -------------------------------------------------------------------------- */
-
-
-class gSlider : public Fl_Slider
-{
-public:
- gSlider(int x, int y, int w, int h, const char *l=0);
- int id;
-};
-
-
-/* -------------------------------------------------------------------------- */
-
-
-/* gFxButton
- * a simple gClick with 'full' parameter (i.e. has plugins). If 'full' is true,
- * draw something somewhere. */
-
-class gFxButton : public gClick
-{
-public:
- gFxButton(int x, int y, int w, int h, const char **imgOff=NULL, const char **imgOn=NULL);
- void draw();
- bool full;
-};
-
-
-#endif
+++ /dev/null
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * ge_pluginBrowser
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
- *
- * This file is part of Giada - Your Hardcore Loopmachine.
- *
- * Giada - Your Hardcore Loopmachine is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General
- * Public License as published by the Free Software Foundation, either
- * version 3 of the License, or (at your option) any later version.
- *
- * Giada - Your Hardcore Loopmachine is distributed in the hope that it
- * will be useful, but WITHOUT ANY WARRANTY; without even the implied
- * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Giada - Your Hardcore Loopmachine. If not, see
- * <http://www.gnu.org/licenses/>.
- *
- * -------------------------------------------------------------------------- */
-
-
-#ifdef WITH_VST
-
-
-#include "../../core/plugin.h"
-#include "../../core/const.h"
-#include "../../core/pluginHost.h"
-#include "ge_mixed.h"
-#include "basics/boxtypes.h"
-#include "ge_pluginBrowser.h"
-
-
-extern PluginHost G_PluginHost;
-
-
-using std::vector;
-
-
-gePluginBrowser::gePluginBrowser(int x, int y, int w, int h)
- : Fl_Browser(x, y, w, h)
-{
- box(G_CUSTOM_BORDER_BOX);
- textsize(GUI_FONT_SIZE_BASE);
- textcolor(COLOR_TEXT_0);
- selection_color(COLOR_BG_1);
- color(COLOR_BG_0);
-
- this->scrollbar.color(COLOR_BG_0);
- this->scrollbar.selection_color(COLOR_BG_1);
- this->scrollbar.labelcolor(COLOR_BD_1);
- this->scrollbar.slider(G_CUSTOM_BORDER_BOX);
-
- this->hscrollbar.color(COLOR_BG_0);
- this->hscrollbar.selection_color(COLOR_BG_1);
- this->hscrollbar.labelcolor(COLOR_BD_1);
- this->hscrollbar.slider(G_CUSTOM_BORDER_BOX);
-
- type(FL_HOLD_BROWSER);
-
- computeWidths();
-
- column_widths(widths);
- column_char('\t'); // tabs as column delimiters
-
- refresh();
-
- end();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gePluginBrowser::refresh()
-{
- clear();
-
- add("NAME\tMANUFACTURER\tCATEGORY\tFORMAT\tUID");
- add("---\t---\t---\t---\t---");
-
- for (int i=0; i<G_PluginHost.countAvailablePlugins(); i++) {
- PluginHost::PluginInfo pi = G_PluginHost.getAvailablePluginInfo(i);
- string m = G_PluginHost.doesPluginExist(pi.uid) ? "" : "@-";
- string s = m + pi.name + "\t" + m + pi.manufacturerName + "\t" + m +
- pi.category + "\t" + m + pi.format + "\t" + m + pi.uid;
- add(s.c_str());
- }
-
- for (unsigned i=0; i<G_PluginHost.countUnknownPlugins(); i++) {
- string s = "?\t?\t?\t?\t? " + G_PluginHost.getUnknownPluginInfo(i) + " ?";
- add(s.c_str());
- }
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gePluginBrowser::computeWidths()
-{
- int w0, w1, w3;
- for (int i=0; i<G_PluginHost.countAvailablePlugins(); i++) {
- PluginHost::PluginInfo pi = G_PluginHost.getAvailablePluginInfo(i);
- w0 = (int) fl_width(pi.name.c_str());
- w1 = (int) fl_width(pi.manufacturerName.c_str());
- w3 = (int) fl_width(pi.format.c_str());
- if (w0 > widths[0]) widths[0] = w0;
- if (w1 > widths[1]) widths[1] = w1;
- if (w3 > widths[3]) widths[3] = w3;
- }
- widths[0] += 60;
- widths[1] += 60;
- widths[2] = fl_width("CATEGORY") + 60;
- widths[3] += 60;
- widths[4] = 0;
-}
-
-
-#endif
+++ /dev/null
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * ge_pluginBrowser
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
- *
- * This file is part of Giada - Your Hardcore Loopmachine.
- *
- * Giada - Your Hardcore Loopmachine is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General
- * Public License as published by the Free Software Foundation, either
- * version 3 of the License, or (at your option) any later version.
- *
- * Giada - Your Hardcore Loopmachine is distributed in the hope that it
- * will be useful, but WITHOUT ANY WARRANTY; without even the implied
- * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Giada - Your Hardcore Loopmachine. If not, see
- * <http://www.gnu.org/licenses/>.
- *
- * -------------------------------------------------------------------------- */
-
-
-#ifdef WITH_VST
-
-#ifndef GE_PLUGIN_BROWSER_H
-#define GE_PLUGIN_BROWSER_H
-
-
-#include <vector>
-#include <FL/Fl.H>
-#include <FL/Fl_Browser.H>
-
-
-using std::vector;
-
-
-class gePluginBrowser : public Fl_Browser
-{
-private:
-
- int widths[5] = {0};
-
- void computeWidths();
-
-public:
-
- gePluginBrowser(int x, int y, int w, int h);
-
- void refresh();
-};
-
-#endif
-
-#endif
+++ /dev/null
-/* ---------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * gg_waveTools
- *
- * ---------------------------------------------------------------------
- *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
- *
- * This file is part of Giada - Your Hardcore Loopmachine.
- *
- * Giada - Your Hardcore Loopmachine is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General
- * Public License as published by the Free Software Foundation, either
- * version 3 of the License, or (at your option) any later version.
- *
- * Giada - Your Hardcore Loopmachine is distributed in the hope that it
- * will be useful, but WITHOUT ANY WARRANTY; without even the implied
- * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Giada - Your Hardcore Loopmachine. If not, see
- * <http://www.gnu.org/licenses/>.
- *
- * ------------------------------------------------------------------ */
-
-
-#include "../../core/graphics.h"
-#include "../../core/mixer.h"
-#include "../../core/const.h"
-#include "../elems/ge_mixed.h"
-#include "../elems/ge_waveform.h"
-#include "basics/boxtypes.h"
-#include "ge_waveTools.h"
-
-
-gWaveTools::gWaveTools(int x, int y, int w, int h, SampleChannel *ch, const char *l)
- : Fl_Scroll(x, y, w, h, l)
-{
- type(Fl_Scroll::HORIZONTAL_ALWAYS);
- hscrollbar.color(COLOR_BG_0);
- hscrollbar.selection_color(COLOR_BG_1);
- hscrollbar.labelcolor(COLOR_BD_1);
- hscrollbar.slider(G_CUSTOM_BORDER_BOX);
-
- waveform = new gWaveform(x, y, w, h-24, ch);
-
-
- //resizable(waveform);
-}
-
-
-
-/* ------------------------------------------------------------------ */
-
-
-void gWaveTools::updateWaveform()
-{
- waveform->alloc(w());
- waveform->redraw();
-}
-
-
-/* ------------------------------------------------------------------ */
-
-
-void gWaveTools::resize(int x, int y, int w, int h)
-{
- if (this->w() == w || (this->w() != w && this->h() != h)) { // vertical or both resize
- Fl_Widget::resize(x, y, w, h);
- waveform->resize(x, y, waveform->w(), h-24);
- updateWaveform();
- }
- else { // horizontal resize
- Fl_Widget::resize(x, y, w, h);
- }
-
- if (this->w() > waveform->w())
- waveform->stretchToWindow();
-
- int offset = waveform->x() + waveform->w() - this->w() - this->x();
- if (offset < 0)
- waveform->position(waveform->x()-offset, this->y());
-}
-
-
-/* ------------------------------------------------------------------ */
-
-
-int gWaveTools::handle(int e)
-{
- int ret = Fl_Group::handle(e);
- switch (e) {
- case FL_MOUSEWHEEL: {
- waveform->setZoom(Fl::event_dy());
- redraw();
- ret = 1;
- break;
- }
- }
- return ret;
-}
+++ /dev/null
-/* ---------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * gg_waveTools
- *
- * ---------------------------------------------------------------------
- *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
- *
- * This file is part of Giada - Your Hardcore Loopmachine.
- *
- * Giada - Your Hardcore Loopmachine is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General
- * Public License as published by the Free Software Foundation, either
- * version 3 of the License, or (at your option) any later version.
- *
- * Giada - Your Hardcore Loopmachine is distributed in the hope that it
- * will be useful, but WITHOUT ANY WARRANTY; without even the implied
- * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Giada - Your Hardcore Loopmachine. If not, see
- * <http://www.gnu.org/licenses/>.
- *
- * ------------------------------------------------------------------ */
-
-
-#ifndef GE_WAVETOOLS_H
-#define GE_WAVETOOLS_H
-
-#include <FL/Fl.H>
-#include <FL/Fl_Group.H>
-#include <FL/Fl_Scroll.H>
-
-
-class gWaveTools : public Fl_Scroll {
-public:
- class gWaveform *waveform;
-
- gWaveTools(int X,int Y,int W, int H, class SampleChannel *ch, const char *L=0);
- void resize(int x, int y, int w, int h);
- int handle(int e);
-
- void updateWaveform();
-};
-
-#endif
+++ /dev/null
-/* ---------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * ge_waveform
- * an element which represents a waveform.
- *
- * ---------------------------------------------------------------------
- *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
- *
- * This file is part of Giada - Your Hardcore Loopmachine.
- *
- * Giada - Your Hardcore Loopmachine is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General
- * Public License as published by the Free Software Foundation, either
- * version 3 of the License, or (at your option) any later version.
- *
- * Giada - Your Hardcore Loopmachine is distributed in the hope that it
- * will be useful, but WITHOUT ANY WARRANTY; without even the implied
- * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Giada - Your Hardcore Loopmachine. If not, see
- * <http://www.gnu.org/licenses/>.
- *
- * ------------------------------------------------------------------ */
-
-
-#include <FL/Fl_Menu_Item.H>
-#include <FL/Fl_Menu_Button.H>
-#include <samplerate.h>
-#include "../../core/wave.h"
-#include "../../core/conf.h"
-#include "../../core/const.h"
-#include "../../core/mixer.h"
-#include "../../core/waveFx.h"
-#include "../../core/channel.h"
-#include "../../core/sampleChannel.h"
-#include "../../glue/channel.h"
-#include "../dialogs/gd_editor.h"
-#include "ge_waveTools.h"
-#include "ge_mixed.h"
-#include "basics/boxtypes.h"
-#include "ge_waveform.h"
-
-
-extern Mixer G_Mixer;
-extern Conf G_Conf;
-
-
-gWaveform::gWaveform(int x, int y, int w, int h, class SampleChannel *ch, const char *l)
-: Fl_Widget(x, y, w, h, l),
- chan(ch),
- menuOpen(false),
- chanStart(0),
- chanStartLit(false),
- chanEnd(0),
- chanEndLit(false),
- ratio(0.0f),
- selectionA(0),
- selectionB(0),
- selectionA_abs(0),
- selectionB_abs(0)
-{
- data.sup = NULL;
- data.inf = NULL;
- data.size = 0;
-
- grid.snap = G_Conf.sampleEditorGridOn;
- grid.level = G_Conf.sampleEditorGridVal;
-
- stretchToWindow();
-}
-
-
-/* ------------------------------------------------------------------ */
-
-
-gWaveform::~gWaveform()
-{
- freeData();
-}
-
-
-/* ------------------------------------------------------------------ */
-
-
-void gWaveform::freeData()
-{
- if (data.sup != NULL) {
- free(data.sup);
- free(data.inf);
- data.sup = NULL;
- data.inf = NULL;
- data.size = 0;
- }
- grid.points.clear();
-}
-
-
-/* ------------------------------------------------------------------ */
-
-
-int gWaveform::alloc(int datasize)
-{
- ratio = chan->wave->size / (float) datasize;
-
- if (ratio < 2)
- return 0;
-
- freeData();
-
- data.size = datasize;
- data.sup = (int*) malloc(data.size * sizeof(int));
- data.inf = (int*) malloc(data.size * sizeof(int));
-
- int offset = h() / 2;
- int zero = y() + offset; // center, zero amplitude (-inf dB)
-
- /* grid frequency: store a grid point every 'gridFreq' pixel. Must be
- * even, as always */
-
- int gridFreq = 0;
- if (grid.level != 0) {
- gridFreq = chan->wave->size / grid.level;
- if (gridFreq % 2 != 0)
- gridFreq--;
- }
-
- for (int i=0; i<data.size; i++) {
-
- int pp; // point prev
- int pn; // point next
-
- /* resampling the waveform, hardcore way. Many thanks to
- * http://fourier.eng.hmc.edu/e161/lectures/resize/node3.html
- * Note: we use
- * p = j * (m-1 / n)
- * instead of
- * p = j * (m-1 / n-1)
- * in order to obtain 'datasize' cells to parse (and not datasize-1) */
-
- pp = i * ((chan->wave->size - 1) / (float) datasize);
- pn = (i+1) * ((chan->wave->size - 1) / (float) datasize);
-
- if (pp % 2 != 0) pp -= 1;
- if (pn % 2 != 0) pn -= 1;
-
- float peaksup = 0.0f;
- float peakinf = 0.0f;
-
- /* scan the original data in chunks */
-
- int k = pp;
- while (k < pn) {
-
- if (chan->wave->data[k] > peaksup)
- peaksup = chan->wave->data[k]; // FIXME - Left data only
- else
- if (chan->wave->data[k] <= peakinf)
- peakinf = chan->wave->data[k]; // FIXME - Left data only
-
- /* print grid */
-
- if (gridFreq != 0)
- if (k % gridFreq == 0 && k != 0)
- grid.points.push_back(i);
-
- k += 2;
- }
-
- data.sup[i] = zero - (peaksup * chan->boost * offset);
- data.inf[i] = zero - (peakinf * chan->boost * offset);
-
- // avoid window overflow
-
- if (data.sup[i] < y()) data.sup[i] = y();
- if (data.inf[i] > y()+h()-1) data.inf[i] = y()+h()-1;
- }
-
- recalcPoints();
- return 1;
-}
-
-
-/* ------------------------------------------------------------------ */
-
-
-void gWaveform::recalcPoints()
-{
- selectionA = relativePoint(selectionA_abs);
- selectionB = relativePoint(selectionB_abs);
- chanStart = relativePoint(chan->begin / 2);
-
- /* fix the rounding error when chanEnd is set on the very end of the
- * sample */
-
- if (chan->end == chan->wave->size)
- chanEnd = data.size - 2; // 2 px border
- else
- chanEnd = relativePoint(chan->end / 2);
-}
-
-
-/* ------------------------------------------------------------------ */
-
-
-void gWaveform::draw()
-{
- /* blank canvas */
-
- fl_rectf(x(), y(), w(), h(), COLOR_BG_0);
-
- /* draw selection (if any) */
-
- if (selectionA != selectionB) {
-
- int a_x = selectionA + x() - BORDER; // - start;
- int b_x = selectionB + x() - BORDER; // - start;
-
- if (a_x < 0)
- a_x = 0;
- if (b_x >= w()-1)
- b_x = w()-1;
-
- if (selectionA < selectionB)
- fl_rectf(a_x+BORDER, y(), b_x-a_x, h(), COLOR_BD_0);
- else
- fl_rectf(b_x+BORDER, y(), a_x-b_x, h(), COLOR_BD_0);
- }
-
- /* draw waveform from x1 (offset driven by the scrollbar) to x2
- * (width of parent window). We don't draw the entire waveform,
- * only the visibile part. */
-
- int offset = h() / 2;
- int zero = y() + offset; // sample zero (-inf dB)
-
- int wx1 = abs(x() - ((gWaveTools*)parent())->x());
- int wx2 = wx1 + ((gWaveTools*)parent())->w();
- if (x()+w() < ((gWaveTools*)parent())->w())
- wx2 = x() + w() - BORDER;
-
- fl_color(0, 0, 0);
- for (int i=wx1; i<wx2; i++) {
- fl_line(i+x(), zero, i+x(), data.sup[i]);
- fl_line(i+x(), zero, i+x(), data.inf[i]);
-
- /* print grid */
-
- for (unsigned k=0; k<grid.points.size(); k++) {
- if (grid.points.at(k) == i) {
- //gu_log("draw grid line at %d\n", i);
- fl_color(fl_rgb_color(54, 54, 54));
- fl_line_style(FL_DASH, 0, NULL);
- fl_line(i+x(), y(), i+x(), y()+h());
- fl_color(0, 0, 0);
- fl_line_style(FL_SOLID, 0, NULL);
- break;
- }
- }
- }
-
- /* border box */
-
- fl_rect(x(), y(), w(), h(), COLOR_BD_0);
-
- /* print chanStart */
-
- int lineX = x()+chanStart+1;
-
- if (chanStartLit) fl_color(COLOR_BD_1);
- else fl_color(COLOR_BD_0);
-
- /* vertical line */
-
- fl_line(lineX, y()+1, lineX, y()+h()-2);
-
- /* print flag and avoid overflow */
-
- if (lineX+FLAG_WIDTH > w()+x()-2)
- fl_rectf(lineX, y()+h()-FLAG_HEIGHT-1, w()-lineX+x()-1, FLAG_HEIGHT);
- else {
- fl_rectf(lineX, y()+h()-FLAG_HEIGHT-1, FLAG_WIDTH, FLAG_HEIGHT);
- fl_color(255, 255, 255);
- fl_draw("s", lineX+4, y()+h()-3);
- }
-
- /* print chanEnd */
-
- lineX = x()+chanEnd;
- if (chanEndLit) fl_color(COLOR_BD_1);
- else fl_color(COLOR_BD_0);
-
- fl_line(lineX, y()+1, lineX, y()+h()-2);
-
- if (lineX-FLAG_WIDTH < x())
- fl_rectf(x()+1, y()+1, lineX-x(), FLAG_HEIGHT);
- else {
- fl_rectf(lineX-FLAG_WIDTH, y()+1, FLAG_WIDTH, FLAG_HEIGHT);
- fl_color(255, 255, 255);
- fl_draw("e", lineX-10, y()+10);
- }
-}
-
-
-/* ------------------------------------------------------------------ */
-
-
-int gWaveform::handle(int e)
-{
- int ret = 0;
-
- switch (e) {
-
- case FL_PUSH: {
-
- mouseX = Fl::event_x();
- pushed = true;
-
- if (!mouseOnEnd() && !mouseOnStart()) {
-
- /* right button? show the menu. Don't set selectionA,B,etc */
-
- if (Fl::event_button3()) {
- openEditMenu();
- }
- else
- if (mouseOnSelectionA() || mouseOnSelectionB()) {
- resized = true;
- }
- else {
- dragged = true;
- selectionA = Fl::event_x() - x();
-
- if (selectionA >= data.size) selectionA = data.size;
-
- selectionB = selectionA;
- selectionA_abs = absolutePoint(selectionA);
- selectionB_abs = selectionA_abs;
- }
- }
-
- ret = 1;
- break;
- }
-
- case FL_RELEASE: {
-
- /* don't recompute points if something is selected */
-
- if (selectionA != selectionB) {
- pushed = false;
- dragged = false;
- ret = 1;
- break;
- }
-
- int realChanStart = chan->begin;
- int realChanEnd = chan->end;
-
- if (chanStartLit)
- realChanStart = absolutePoint(chanStart)*2;
- else
- if (chanEndLit)
- realChanEnd = absolutePoint(chanEnd)*2;
-
- glue_setBeginEndChannel((gdEditor *) window(), chan, realChanStart, realChanEnd, false);
-
- pushed = false;
- dragged = false;
-
- redraw();
- ret = 1;
- break;
- }
-
- case FL_ENTER: { // enables FL_DRAG
- ret = 1;
- break;
- }
-
- case FL_LEAVE: {
- if (chanStartLit || chanEndLit) {
- chanStartLit = false;
- chanEndLit = false;
- redraw();
- }
- ret = 1;
- break;
- }
-
- case FL_MOVE: {
- mouseX = Fl::event_x();
- mouseY = Fl::event_y();
-
- if (mouseOnStart()) {
- chanStartLit = true;
- redraw();
- }
- else
- if (chanStartLit) {
- chanStartLit = false;
- redraw();
- }
-
- if (mouseOnEnd()) {
- chanEndLit = true;
- redraw();
- }
- else
- if (chanEndLit) {
- chanEndLit = false;
- redraw();
- }
-
- if (mouseOnSelectionA())
- fl_cursor(FL_CURSOR_WE, FL_WHITE, FL_BLACK);
- else
- if (mouseOnSelectionB())
- fl_cursor(FL_CURSOR_WE, FL_WHITE, FL_BLACK);
- else
- fl_cursor(FL_CURSOR_DEFAULT, FL_WHITE, FL_BLACK);
-
- ret = 1;
- break;
- }
-
- case FL_DRAG: {
-
- /* here the mouse is on the chanStart tool */
-
- if (chanStartLit && pushed) {
-
- chanStart = Fl::event_x() - x();
-
- if (grid.snap)
- chanStart = applySnap(chanStart);
-
- if (chanStart < 0)
- chanStart = 0;
- else
- if (chanStart >= chanEnd)
- chanStart = chanEnd-2;
-
- redraw();
- }
- else
- if (chanEndLit && pushed) {
-
- chanEnd = Fl::event_x() - x();
-
- if (grid.snap)
- chanEnd = applySnap(chanEnd);
-
- if (chanEnd >= data.size - 2)
- chanEnd = data.size - 2;
- else
- if (chanEnd <= chanStart)
- chanEnd = chanStart + 2;
-
- redraw();
- }
-
- /* here the mouse is on the waveform, i.e. a selection */
-
- else
- if (dragged) {
-
- selectionB = Fl::event_x() - x();
-
- if (selectionB >= data.size)
- selectionB = data.size;
-
- if (selectionB <= 0)
- selectionB = 0;
-
- if (grid.snap)
- selectionB = applySnap(selectionB);
-
- selectionB_abs = absolutePoint(selectionB);
- redraw();
- }
-
- /* here the mouse is on a selection boundary i.e. resize */
-
- else
- if (resized) {
- int pos = Fl::event_x() - x();
- if (mouseOnSelectionA()) {
- selectionA = grid.snap ? applySnap(pos) : pos;
- selectionA_abs = absolutePoint(selectionA);
- }
- else
- if (mouseOnSelectionB()) {
- selectionB = grid.snap ? applySnap(pos) : pos;
- selectionB_abs = absolutePoint(selectionB);
- }
- redraw();
- }
- mouseX = Fl::event_x();
- ret = 1;
- break;
- }
- }
- return ret;
-}
-
-
-/* ------------------------------------------------------------------ */
-
-/* pixel snap disances (10px) must be equal to those defined in
- * gWaveform::mouseOnSelectionA() and gWaverfrom::mouseOnSelectionB() */
-/* TODO - use constant for 10px */
-
-int gWaveform::applySnap(int pos)
-{
- for (unsigned i=0; i<grid.points.size(); i++) {
- if (pos >= grid.points.at(i) - 10 &&
- pos <= grid.points.at(i) + 10)
- {
- return grid.points.at(i);
- }
- }
- return pos;
-}
-
-
-/* ------------------------------------------------------------------ */
-
-
-bool gWaveform::mouseOnStart()
-{
- return mouseX-10 > chanStart + x() - BORDER &&
- mouseX-10 <= chanStart + x() - BORDER + FLAG_WIDTH &&
- mouseY > h() + y() - FLAG_HEIGHT;
-}
-
-
-/* ------------------------------------------------------------------ */
-
-
-bool gWaveform::mouseOnEnd()
-{
- return mouseX-10 >= chanEnd + x() - BORDER - FLAG_WIDTH &&
- mouseX-10 <= chanEnd + x() - BORDER &&
- mouseY <= y() + FLAG_HEIGHT + 1;
-}
-
-
-/* ------------------------------------------------------------------ */
-
-/* pixel boundaries (10px) must be equal to the snap factor distance
- * defined in gWaveform::applySnap() */
-
-bool gWaveform::mouseOnSelectionA()
-{
- if (selectionA == selectionB)
- return false;
- return mouseX >= selectionA-10+x() && mouseX <= selectionA+10+x();
-}
-
-
-bool gWaveform::mouseOnSelectionB()
-{
- if (selectionA == selectionB)
- return false;
- return mouseX >= selectionB-10+x() && mouseX <= selectionB+10+x();
-}
-
-
-/* ------------------------------------------------------------------ */
-
-
-int gWaveform::absolutePoint(int p)
-{
- if (p <= 0)
- return 0;
-
- if (p > data.size)
- return chan->wave->size / 2;
-
- return (p * ratio) / 2;
-}
-
-
-/* ------------------------------------------------------------------ */
-
-
-int gWaveform::relativePoint(int p)
-{
- return (ceilf(p / ratio)) * 2;
-}
-
-
-/* ------------------------------------------------------------------ */
-
-
-void gWaveform::openEditMenu()
-{
- if (selectionA == selectionB)
- return;
-
- menuOpen = true;
-
- Fl_Menu_Item menu[] = {
- {"Cut"},
- {"Trim"},
- {"Silence"},
- {"Fade in"},
- {"Fade out"},
- {"Smooth edges"},
- {"Set start/end here"},
- {0}
- };
-
- if (chan->status == STATUS_PLAY) {
- menu[0].deactivate();
- menu[1].deactivate();
- }
-
- Fl_Menu_Button *b = new Fl_Menu_Button(0, 0, 100, 50);
- b->box(G_CUSTOM_BORDER_BOX);
- b->textsize(GUI_FONT_SIZE_BASE);
- b->textcolor(COLOR_TEXT_0);
- b->color(COLOR_BG_0);
-
- const Fl_Menu_Item *m = menu->popup(Fl::event_x(), Fl::event_y(), 0, 0, b);
- if (!m) {
- menuOpen = false;
- return;
- }
-
- /* straightSel() to ensure that point A is always lower than B */
-
- straightSel();
-
- if (strcmp(m->label(), "Silence") == 0) {
- wfx_silence(chan->wave, absolutePoint(selectionA), absolutePoint(selectionB));
-
- selectionA = 0;
- selectionB = 0;
-
- stretchToWindow();
- redraw();
- menuOpen = false;
- return;
- }
-
- if (strcmp(m->label(), "Set start/end here") == 0) {
-
- glue_setBeginEndChannel(
- (gdEditor *) window(), // parent
- chan,
- absolutePoint(selectionA) * 2, // stereo!
- absolutePoint(selectionB) * 2, // stereo!
- false, // no recalc (we do it here)
- false // don't check
- );
-
- selectionA = 0;
- selectionB = 0;
- selectionA_abs = 0;
- selectionB_abs = 0;
-
- recalcPoints();
- redraw();
- menuOpen = false;
- return;
- }
-
- if (strcmp(m->label(), "Cut") == 0) {
- wfx_cut(chan->wave, absolutePoint(selectionA), absolutePoint(selectionB));
-
- /* for convenience reset start/end points */
-
- glue_setBeginEndChannel(
- (gdEditor *) window(),
- chan,
- 0,
- chan->wave->size,
- false);
-
- selectionA = 0;
- selectionB = 0;
- selectionA_abs = 0;
- selectionB_abs = 0;
-
- setZoom(0);
-
- menuOpen = false;
- return;
- }
-
- if (strcmp(m->label(), "Trim") == 0) {
- wfx_trim(chan->wave, absolutePoint(selectionA), absolutePoint(selectionB));
-
- glue_setBeginEndChannel(
- (gdEditor *) window(),
- chan,
- 0,
- chan->wave->size,
- false);
-
- selectionA = 0;
- selectionB = 0;
- selectionA_abs = 0;
- selectionB_abs = 0;
-
- stretchToWindow();
- menuOpen = false;
- redraw();
- return;
- }
-
- if (!strcmp(m->label(), "Fade in") || !strcmp(m->label(), "Fade out")) {
-
- int type = !strcmp(m->label(), "Fade in") ? 0 : 1;
- wfx_fade(chan->wave, absolutePoint(selectionA), absolutePoint(selectionB), type);
-
- selectionA = 0;
- selectionB = 0;
-
- stretchToWindow();
- redraw();
- menuOpen = false;
- return;
- }
-
- if (!strcmp(m->label(), "Smooth edges")) {
-
- wfx_smooth(chan->wave, absolutePoint(selectionA), absolutePoint(selectionB));
-
- selectionA = 0;
- selectionB = 0;
-
- stretchToWindow();
- redraw();
- menuOpen = false;
- return;
- }
-}
-
-
-/* ------------------------------------------------------------------ */
-
-
-void gWaveform::straightSel()
-{
- if (selectionA > selectionB) {
- unsigned tmp = selectionB;
- selectionB = selectionA;
- selectionA = tmp;
- }
-}
-
-
-/* ------------------------------------------------------------------ */
-
-
-void gWaveform::setZoom(int type)
-{
- int newSize;
- if (type == -1) newSize = data.size*2; // zoom in
- else newSize = data.size/2; // zoom out
-
- if (alloc(newSize)) {
- size(data.size, h());
-
- /* zoom to pointer */
-
- int shift;
- if (x() > 0)
- shift = Fl::event_x() - x();
- else
- if (type == -1)
- shift = Fl::event_x() + abs(x());
- else
- shift = (Fl::event_x() + abs(x())) / -2;
-
- if (x() - shift > BORDER)
- shift = 0;
-
- position(x() - shift, y());
-
-
- /* avoid overflow when zooming out with scrollbar like that:
- * |----------[scrollbar]|
- *
- * offset vs smaller:
- * |[wave------------| offset > 0 smaller = false
- * |[wave----] | offset < 0, smaller = true
- * |-------------] | offset < 0, smaller = false */
-
- int parentW = ((gWaveTools*)parent())->w();
- int thisW = x() + w() - BORDER; // visible width, not full width
-
- if (thisW < parentW)
- position(x() + parentW - thisW, y());
- if (smaller())
- stretchToWindow();
-
- redraw();
- }
-}
-
-
-/* ------------------------------------------------------------------ */
-
-
-void gWaveform::stretchToWindow()
-{
- int s = ((gWaveTools*)parent())->w();
- alloc(s);
- position(BORDER, y());
- size(s, h());
-}
-
-
-/* ------------------------------------------------------------------ */
-
-
-bool gWaveform::smaller()
-{
- return w() < ((gWaveTools*)parent())->w();
-}
-
-
-/* ------------------------------------------------------------------ */
-
-
-void gWaveform::setGridLevel(int l)
-{
- grid.points.clear();
- grid.level = l;
- alloc(data.size);
- redraw();
-}
+++ /dev/null
-/* ---------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * ge_waveform
- * an element which represents a waveform.
- *
- * ---------------------------------------------------------------------
- *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
- *
- * This file is part of Giada - Your Hardcore Loopmachine.
- *
- * Giada - Your Hardcore Loopmachine is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General
- * Public License as published by the Free Software Foundation, either
- * version 3 of the License, or (at your option) any later version.
- *
- * Giada - Your Hardcore Loopmachine is distributed in the hope that it
- * will be useful, but WITHOUT ANY WARRANTY; without even the implied
- * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Giada - Your Hardcore Loopmachine. If not, see
- * <http://www.gnu.org/licenses/>.
- *
- * ------------------------------------------------------------------ */
-
-
-#ifndef GE_WAVEFORM_H
-#define GE_WAVEFORM_H
-
-
-#include <vector>
-#include <FL/Fl.H>
-#include <FL/Fl_Widget.H>
-#include <FL/fl_draw.H>
-#include <math.h>
-#include "../../utils/fs.h"
-
-
-using std::vector;
-
-
-#define FLAG_WIDTH 14
-#define FLAG_HEIGHT 12
-#define BORDER 8 // window border <-> widget border
-
-
-class gWaveform : public Fl_Widget {
-
-private:
-
- /* data
- * real graphic stuff from the underlying waveform */
-
- struct data {
- int *sup;
- int *inf;
- int size;
- } data;
-
- /* grid */
-
- struct grid {
- bool snap;
- int level;
- vector<int> points;
- } grid;
-
- /* chan
- * chan in use. */
-
- class SampleChannel *chan;
-
- /* menuOpen
- * is the menu open? */
-
- bool menuOpen;
-
- /* mouseOnStart/end
- * is mouse on start or end flag? */
-
- bool mouseOnStart();
- bool mouseOnEnd();
-
- /* mouseOnSelectionA/B
- * as above, for the selection */
-
- bool mouseOnSelectionA();
- bool mouseOnSelectionB();
-
- /* absolutePoint
- * from a relative 'p' point (zoom affected) returns the same point
- * zoom 1:1 based */
-
- int absolutePoint(int p);
-
- /* relativePoint
- * from an absolute 'p' point (1:1 zoom), returns the same point zoom
- * affected */
-
- int relativePoint(int p);
-
- /* straightSel
- * helper function which flattens the selection if it was made from
- * right to left (inverse selection) */
-
- void straightSel();
-
- /* freeData
- * destroy any graphical buffer */
-
- void freeData();
-
- /* smaller
- * is the waveform smaller than the parent window? */
-
- bool smaller();
-
- /* applySnap
- * snap a point at 'pos' pixel */
-
- int applySnap(int pos);
-
-public:
-
- gWaveform(int x, int y, int w, int h, class SampleChannel *ch, const char *l=0);
- ~gWaveform();
- void draw();
- int handle(int e);
-
- /* alloc
- * allocate memory for the picture */
-
- int alloc(int datasize=0);
-
- /* recalcPoints
- * re-calc chanStart, chanEnd, ... */
-
- void recalcPoints();
-
- /* openEditMenu
- * show edit menu on right-click */
-
- void openEditMenu();
-
- /* displayRatio
- * how much of the waveform is being displayed on screen */
-
- inline float displayRatio() { return 1.0f / (data.size / (float) w()); };
-
- /* zoom
- * type == 1 : zoom out, type == -1: zoom in */
-
- void setZoom(int type);
-
- /* strecthToWindow
- * shrink or enlarge the waveform to match parent's width (gWaveTools) */
-
- void stretchToWindow();
-
- /* setGridLevel
- * set a new frequency level for the grid. 0 means disabled. */
-
- void setGridLevel(int l);
-
- inline void setSnap(bool v) { grid.snap = v; }
- inline bool getSnap() { return grid.snap; }
-
- inline int getSize() { return data.size; }
-
- int chanStart;
- bool chanStartLit;
- int chanEnd;
- bool chanEndLit;
- bool pushed;
- bool dragged;
- bool resized;
-
- float ratio;
-
- /* TODO - useless! use Fl::mouse_x() and Fl::mouse_y() instead */
- int mouseX; // mouse pos for drag.n.drop
- int mouseY;
-
- /* selectionA/B = portion of the selected wave
- * " " "" " _abs = selectionA/B not affected by zoom */
- /** TODO - change selectionA to selectionA_rel
- TODO - change selectionB to selectionB_rel */
- int selectionA;
- int selectionB;
- int selectionA_abs;
- int selectionB_abs;
-};
-
-
-#endif
+++ /dev/null
-/* ---------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * ge_window
- *
- * ---------------------------------------------------------------------
- *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
- *
- * This file is part of Giada - Your Hardcore Loopmachine.
- *
- * Giada - Your Hardcore Loopmachine is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General
- * Public License as published by the Free Software Foundation, either
- * version 3 of the License, or (at your option) any later version.
- *
- * Giada - Your Hardcore Loopmachine is distributed in the hope that it
- * will be useful, but WITHOUT ANY WARRANTY; without even the implied
- * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Giada - Your Hardcore Loopmachine. If not, see
- * <http://www.gnu.org/licenses/>.
- *
- * ------------------------------------------------------------------ */
-
-
-#include "ge_window.h"
-#include "../../utils/log.h"
-
-
-gWindow::gWindow(int x, int y, int w, int h, const char *title, int id)
- : Fl_Double_Window(x, y, w, h, title), id(id), parent(NULL) { }
-
-
-/* ------------------------------------------------------------------ */
-
-
-gWindow::gWindow(int w, int h, const char *title, int id)
- : Fl_Double_Window(w, h, title), id(id), parent(NULL) { }
-
-
-/* ------------------------------------------------------------------ */
-
-
-gWindow::~gWindow() {
-
- /* delete all subwindows in order to empty the stack */
-
- for (unsigned i=0; i<subWindows.size(); i++)
- delete subWindows.at(i);
- subWindows.clear();
-}
-
-
-/* ------------------------------------------------------------------ */
-
-/* this is the default callback of each window, fired when the user closes
- * the window with the 'x'. Watch out: is the parent that calls delSubWIndow */
-
-void gWindow::cb_closeChild(Fl_Widget *v, void *p) {
- gWindow *child = (gWindow*) v;
- if (child->getParent() != NULL)
- (child->getParent())->delSubWindow(child);
-}
-
-
-/* ------------------------------------------------------------------ */
-
-
-void gWindow::addSubWindow(gWindow *w) {
-
- /** TODO - useless: delete ---------------------------------------- */
- for (unsigned i=0; i<subWindows.size(); i++)
- if (w->getId() == subWindows.at(i)->getId()) {
- //gu_log("[gWindow] window %p (id=%d) exists, not added (and deleted)\n", (void*)w, w->getId());
- delete w;
- return;
- }
- /** --------------------------------------------------------------- */
-
- w->setParent(this);
- w->callback(cb_closeChild); // you can pass params: w->callback(cb_closeChild, (void*)params)
- subWindows.push_back(w);
- //debug();
-}
-
-
-/* ------------------------------------------------------------------ */
-
-
-void gWindow::delSubWindow(gWindow *w) {
- for (unsigned i=0; i<subWindows.size(); i++)
- if (w->getId() == subWindows.at(i)->getId()) {
- delete subWindows.at(i);
- subWindows.erase(subWindows.begin() + i);
- //debug();
- return;
- }
- //debug();
-}
-
-
-/* ------------------------------------------------------------------ */
-
-
-void gWindow::delSubWindow(int id) {
- for (unsigned i=0; i<subWindows.size(); i++)
- if (subWindows.at(i)->getId() == id) {
- delete subWindows.at(i);
- subWindows.erase(subWindows.begin() + i);
- //debug();
- return;
- }
- //debug();
-}
-
-
-/* ------------------------------------------------------------------ */
-
-
-int gWindow::getId() {
- return id;
-}
-
-
-/* ------------------------------------------------------------------ */
-
-
-void gWindow::setId(int id) {
- this->id = id;
-}
-
-
-/* ------------------------------------------------------------------ */
-
-
-void gWindow::debug() {
- gu_log("---- window stack (id=%d): ----\n", getId());
- for (unsigned i=0; i<subWindows.size(); i++)
- gu_log("[gWindow] %p (id=%d)\n", (void*)subWindows.at(i), subWindows.at(i)->getId());
- gu_log("----\n");
-}
-
-
-/* ------------------------------------------------------------------ */
-
-
-gWindow *gWindow::getParent() {
- return parent;
-}
-
-
-/* ------------------------------------------------------------------ */
-
-
-void gWindow::setParent(gWindow *w) {
- parent = w;
-}
-
-
-/* ------------------------------------------------------------------ */
-
-
-bool gWindow::hasWindow(int id) {
- for (unsigned i=0; i<subWindows.size(); i++)
- if (id == subWindows.at(i)->getId())
- return true;
- return false;
-}
-
-
-/* ------------------------------------------------------------------ */
-
-
-gWindow *gWindow::getChild(int id) {
- for (unsigned i=0; i<subWindows.size(); i++)
- if (id == subWindows.at(i)->getId())
- return subWindows.at(i);
- return NULL;
-}
+++ /dev/null
-/* ---------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * ge_window
- * A custom window.
- *
- * ---------------------------------------------------------------------
- *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
- *
- * This file is part of Giada - Your Hardcore Loopmachine.
- *
- * Giada - Your Hardcore Loopmachine is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General
- * Public License as published by the Free Software Foundation, either
- * version 3 of the License, or (at your option) any later version.
- *
- * Giada - Your Hardcore Loopmachine is distributed in the hope that it
- * will be useful, but WITHOUT ANY WARRANTY; without even the implied
- * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Giada - Your Hardcore Loopmachine. If not, see
- * <http://www.gnu.org/licenses/>.
- *
- * ------------------------------------------------------------------ */
-
-
-#ifndef __GE_WINDOW_H__
-#define __GE_WINDOW_H__
-
-
-#include <vector>
-#include <FL/Fl_Double_Window.H>
-#include "../../utils/fs.h"
-
-
-using std::vector;
-
-
-class gWindow : public Fl_Double_Window {
-
-protected:
- vector <gWindow *> subWindows;
- int id;
- gWindow *parent;
-
-public:
- gWindow(int x, int y, int w, int h, const char *title=0, int id=0);
- gWindow(int w, int h, const char *title=0, int id=0);
- ~gWindow();
-
- static void cb_closeChild(Fl_Widget *v, void *p);
-
- void addSubWindow(gWindow *w);
- void delSubWindow(gWindow *w);
- void delSubWindow(int id);
-
- int getId();
- void setId(int id);
- void debug();
-
- void setParent(gWindow *);
- gWindow *getParent();
- gWindow *getChild(int id);
-
- /* hasWindow
- * true if the window with id 'id' exists in the stack. */
-
- bool hasWindow(int id);
-
-};
-
-
-#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
#include <FL/fl_draw.H>
#include "../../../core/const.h"
#include "../../../core/mixer.h"
+#include "../../../core/clock.h"
#include "beatMeter.h"
-extern Mixer G_Mixer;
+using namespace giada::m;
geBeatMeter::geBeatMeter(int x, int y, int w, int h, const char *L)
void geBeatMeter::draw()
{
- int cursorW = w() / MAX_BEATS;
- int greyX = G_Mixer.beats * cursorW;
+ int cursorW = w() / G_MAX_BEATS;
+ int greyX = clock::getBeats() * cursorW;
fl_rect(x(), y(), w(), h(), COLOR_BD_0); // border
fl_rectf(x()+1, y()+1, w()-2, h()-2, FL_BACKGROUND_COLOR); // bg
- fl_rectf(x()+(G_Mixer.actualBeat*cursorW)+3, y()+3, cursorW-5, h()-6,
+ fl_rectf(x()+(clock::getCurrentBeat()*cursorW)+3, y()+3, cursorW-5, h()-6,
COLOR_BG_2); // cursor
/* beat cells */
fl_color(COLOR_BD_0);
- for (int i=1; i<=G_Mixer.beats; i++)
+ for (int i=1; i<=clock::getBeats(); i++)
fl_line(x()+cursorW*i, y()+1, x()+cursorW*i, y()+h()-2);
/* bar line */
fl_color(COLOR_BG_2);
- int delta = G_Mixer.beats / G_Mixer.bars;
- for (int i=1; i<G_Mixer.bars; i++)
+ int delta = clock::getBeats() / clock::getBars();
+ for (int i=1; i<clock::getBars(); i++)
fl_line(x()+cursorW*(i*delta), y()+1, x()+cursorW*(i*delta), y()+h()-2);
/* unused grey area */
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* Giada - Your Hardcore Loopmachine
*
- * ge_channel
- *
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
* -------------------------------------------------------------------------- */
+#include <FL/Fl.H>
#include "../../../../core/const.h"
#include "../../../../core/channel.h"
#include "../../../../core/graphics.h"
#include "../../../../glue/channel.h"
#include "../../../dialogs/gd_mainWindow.h"
#include "../../../dialogs/gd_pluginList.h"
+#include "../../basics/idButton.h"
+#include "../../basics/dial.h"
#include "column.h"
#include "channelButton.h"
#include "channel.h"
extern gdMainWindow *G_MainWin;
+using namespace giada::m;
+
+
geChannel::geChannel(int X, int Y, int W, int H, int type, Channel *ch)
- : Fl_Group(X, Y, W, H, NULL),
+ : Fl_Group(X, Y, W, H, nullptr),
ch (ch),
type (type)
{
void geChannel::__cb_changeVol()
{
- glue_setChanVol(ch, vol->value());
+ glue_setVolume(ch, vol->value());
}
#ifdef WITH_VST
void geChannel::__cb_openFxWindow()
{
- gu_openSubWindow(G_MainWin, new gdPluginList(PluginHost::CHANNEL, ch), WID_FX_LIST);
+ gu_openSubWindow(G_MainWin, new gdPluginList(pluginHost::CHANNEL, ch), WID_FX_LIST);
}
#endif
*
* Giada - Your Hardcore Loopmachine
*
- * ge_channel
- *
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
#include <FL/Fl_Group.H>
+class Channel;
+class geIdButton;
+class geChannelStatus;
+class geButton;
+class geChannelButton;
+class geDial;
+#ifdef WITH_VST
+class geStatusButton;
+#endif
+
+
class geChannel : public Fl_Group
{
protected:
public:
- geChannel(int x, int y, int w, int h, int type, class Channel *ch);
+ geChannel(int x, int y, int w, int h, int type, Channel *ch);
/* reset
* reset channel to initial status. */
int getColumnIndex();
- class Channel *ch;
+ Channel *ch;
- class gButton *button;
- class geChannelStatus *status;
- class gClick *arm;
- class geChannelButton *mainButton;
- class gClick *mute;
- class gClick *solo;
- class gDial *vol;
+ geIdButton *button;
+ geChannelStatus *status;
+ geButton *arm;
+ geChannelButton *mainButton;
+ geButton *mute;
+ geButton *solo;
+ geDial *vol;
#ifdef WITH_VST
- class gFxButton *fx;
+ geStatusButton *fx;
#endif
int type;
*
* Giada - Your Hardcore Loopmachine
*
- * ge_channelButton
- *
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
* -------------------------------------------------------------------------- */
+#include <FL/fl_draw.H>
#include "../../../../core/const.h"
#include "channelButton.h"
geChannelButton::geChannelButton(int x, int y, int w, int h, const char *l)
- : gClick(x, y, w, h, l), key("") {}
+ : geButton(x, y, w, h, l), key("") {}
/* -------------------------------------------------------------------------- */
void geChannelButton::draw()
{
- gClick::draw();
+ geButton::draw();
if (key == "")
return;
*
* Giada - Your Hardcore Loopmachine
*
- * ge_channelButton
- *
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
#define GE_CHANNEL_BUTTON_H
-#include "../../ge_mixed.h"
+#include "../../basics/button.h"
-class geChannelButton : public gClick
+class geChannelButton : public geButton
{
private:
virtual int handle(int e) = 0;
- void draw();
+ void draw() override;
+
void setKey(const std::string &k);
void setKey(int k);
void setPlayMode();
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* Giada - Your Hardcore Loopmachine
*
- * ge_status
- *
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
#include <FL/fl_draw.H>
#include "../../../../core/mixer.h"
+#include "../../../../core/clock.h"
#include "../../../../core/sampleChannel.h"
#include "../../../../core/recorder.h"
#include "../../../../core/const.h"
#include "channelStatus.h"
-extern Mixer G_Mixer;
-extern Recorder G_Recorder;
+using namespace giada::m;
geChannelStatus::geChannelStatus(int x, int y, int w, int h, SampleChannel *ch,
fl_rect(x(), y(), w(), h(), COLOR_BD_0); // reset border
fl_rectf(x()+1, y()+1, w()-2, h()-2, COLOR_BG_0); // reset background
- if (ch != NULL) {
+ if (ch != nullptr) {
if (ch->status & (STATUS_WAIT | STATUS_ENDING | REC_ENDING | REC_WAITING) ||
ch->recStatus & (REC_WAITING | REC_ENDING))
{
fl_rectf(x()+1, y()+1, w()-2, h()-2, COLOR_BG_0); // status empty
- if (G_Mixer.recording && ch->armed)
+ if (mixer::recording && ch->armed)
fl_rectf(x()+1, y()+1, w()-2, h()-2, COLOR_BG_3); // take in progress
else
- if (G_Recorder.active && G_Recorder.canRec(ch, &G_Mixer))
+ if (recorder::active && recorder::canRec(ch, clock::isRunning(), mixer::recording))
fl_rectf(x()+1, y()+1, w()-2, h()-2, COLOR_BG_4); // action record
/* equation for the progress bar:
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* Giada - Your Hardcore Loopmachine
*
- * ge_column
- *
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
* -------------------------------------------------------------------------- */
+#include <FL/fl_draw.H>
+#include <FL/Fl_Menu_Button.H>
#include "../../../../core/sampleChannel.h"
#include "../../../../glue/channel.h"
#include "../../../../utils/log.h"
+#include "../../../../utils/fs.h"
#include "../../../../utils/string.h"
#include "../../../dialogs/gd_warnings.h"
#include "../../../elems/basics/boxtypes.h"
+#include "../../../elems/basics/resizerBar.h"
#include "keyboard.h"
#include "sampleChannel.h"
#include "midiChannel.h"
#include "column.h"
+using std::vector;
+
+
geColumn::geColumn(int X, int Y, int W, int H, int index, geKeyboard *parent)
: Fl_Group(X, Y, W, H), parent(parent), index(index)
{
/* geColumn does a bit of a mess: we pass a pointer to its parent (geKeyboard) and
the geColumn itself deals with the creation of another widget, outside geColumn
- and inside geKeyboard, which handles the vertical resize bar (gResizerBar).
+ and inside geKeyboard, which handles the vertical resize bar (geResizerBar).
The resizer cannot stay inside geColumn: it needs a broader view on the other
side widgets. The view can be obtained from geKeyboard only (the upper level).
- Unfortunately, parent() can be NULL: at this point (i.e the constructor)
+ Unfortunately, parent() can be nullptr: at this point (i.e the constructor)
geColumn is still detached from any parent. We use a custom geKeyboard *parent
instead. */
begin();
- addChannelBtn = new gClick(x(), y(), w(), 20, "Add new channel");
+ addChannelBtn = new geButton(x(), y(), w(), 20, "Add new channel");
end();
- resizer = new gResizerBar(x()+w(), y(), 16, h(), false);
- resizer->setMinSize(MIN_COLUMN_WIDTH);
+ resizer = new geResizerBar(x()+w(), y(), 16, h(), false);
+ resizer->setMinSize(G_MIN_COLUMN_WIDTH);
parent->add(resizer);
addChannelBtn->callback(cb_addChannel, (void*)this);
geChannel *geColumn::addChannel(Channel *ch)
{
int currentY = y() + children() * 24;
- geChannel *gch = NULL;
+ geChannel *gch = nullptr;
if (ch->type == CHANNEL_SAMPLE)
gch = (geSampleChannel*) new geSampleChannel(x(), currentY, w(), 20, (SampleChannel*) ch);
else
*
* Giada - Your Hardcore Loopmachine
*
- * ge_column
- *
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
#include <FL/Fl_Group.H>
+class Channel;
+class geButton;
+class geChannel;
+class geResizerBar;
+class geKeyboard;
+
+
class geColumn : public Fl_Group
{
private:
int openTypeMenu();
- class gClick *addChannelBtn;
- class gResizerBar *resizer;
- class geKeyboard *parent;
+ geButton *addChannelBtn;
+ geResizerBar *resizer;
+ geKeyboard *parent;
int index;
public:
- geColumn(int x, int y, int w, int h, int index, class geKeyboard *parent);
+ geColumn(int x, int y, int w, int h, int index, geKeyboard *parent);
~geColumn();
/* addChannel
* add a new channel in this column and set the internal pointer
* to channel to 'ch'. */
- class geChannel *addChannel(class Channel *ch);
+ geChannel *addChannel(Channel *ch);
/* handle */
*
* Giada - Your Hardcore Loopmachine
*
- * gg_keyboard
- *
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
#include "../../../../core/sampleChannel.h"
-#include "../../../../glue/main.h"
+#include "../../../../glue/transport.h"
#include "../../../../glue/io.h"
#include "../../../../utils/log.h"
#include "../../../dialogs/gd_warnings.h"
#include "../../basics/boxtypes.h"
#include "column.h"
#include "sampleChannel.h"
+#include "channelButton.h"
#include "keyboard.h"
bckspcPressed(false),
endPressed (false),
spacePressed (false),
- addColumnBtn (NULL)
+ addColumnBtn (nullptr)
{
color(COLOR_BG_MAIN);
type(Fl_Scroll::BOTH_ALWAYS);
hscrollbar.labelcolor(COLOR_BD_1);
hscrollbar.slider(G_CUSTOM_BORDER_BOX);
- addColumnBtn = new gClick(8, y(), 200, 20, "Add new column");
+ addColumnBtn = new geButton(8, y(), 200, 20, "Add new column");
addColumnBtn->callback(cb_addColumn, (void*) this);
add(addColumnBtn);
void geKeyboard::cb_addColumn(Fl_Widget *v, void *p)
{
- ((geKeyboard*)p)->__cb_addColumn(DEFAULT_COLUMN_WIDTH);
+ ((geKeyboard*)p)->__cb_addColumn(G_DEFAULT_COLUMN_WIDTH);
}
for (unsigned i=0; i<columns.size(); i++)
if (columns.at(i)->getIndex() == index)
return columns.at(i);
- return NULL;
+ return nullptr;
}
if (e == FL_KEYDOWN) {
if (Fl::event_key() == FL_BackSpace && !bckspcPressed) {
bckspcPressed = true;
- glue_rewindSeq();
+ glue_rewindSeq(false); // not from GUI
ret = 1;
break;
}
else if (Fl::event_key() == FL_End && !endPressed) {
endPressed = true;
- glue_startStopInputRec(false); // update gui
+ glue_startStopInputRec(false); // not from GUI
ret = 1;
break;
}
else if (Fl::event_key() == FL_Enter && !enterPressed) {
enterPressed = true;
- glue_startStopActionRec(false); // update gui
+ glue_startStopActionRec(false); // not from GUI
ret = 1;
break;
}
else if (Fl::event_key() == ' ' && !spacePressed) {
spacePressed = true;
- glue_startStopSeq(false); // update gui
+ glue_startStopSeq(false); // unot from GUI
ret = 1;
break;
}
*
* Giada - Your Hardcore Loopmachine
*
- * gg_keyboard
- *
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
#include "../../../../core/const.h"
+class Channel;
+class geButton;
+class geColumn;
+class geChannel;
+class geSampleChannel;
+
+
class geKeyboard : public Fl_Scroll
{
private:
void refreshColIndexes();
static void cb_addColumn (Fl_Widget *v, void *p);
- inline void __cb_addColumn(int width=DEFAULT_COLUMN_WIDTH);
+ inline void __cb_addColumn(int width=G_DEFAULT_COLUMN_WIDTH);
bool bckspcPressed;
bool endPressed;
static int indexColumn;
- class gClick *addColumnBtn;
+ geButton *addColumnBtn;
/* columns
* a vector of columns which in turn contain channels. */
- std::vector<class geColumn*> columns;
+ std::vector<geColumn*> columns;
public:
* set to true, also generate the corresponding column if column (index) does
* not exist yet. */
- class geChannel *addChannel(int column, class Channel *ch, bool build=false);
+ geChannel *addChannel(int column, Channel *ch, bool build=false);
/* addColumn
* add a new column to the top of the stack. */
void refreshColumns();
/* getColumnByIndex
- * return the column with index 'index', or NULL if not found. */
+ * return the column with index 'index', or nullptr if not found. */
geColumn *getColumnByIndex(int index);
/* setChannelWithActions
* add 'R' button if channel has actions, and set recorder to active. */
- void setChannelWithActions(class geSampleChannel *gch);
+ void setChannelWithActions(geSampleChannel *gch);
/* printChannelMessage
* given any output by glue_loadChannel, print the message on screen
*
* Giada - Your Hardcore Loopmachine
*
- * ge_midiChannel
- *
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
* -------------------------------------------------------------------------- */
+#include <FL/Fl_Menu_Button.H>
#include "../../../../core/const.h"
#include "../../../../core/graphics.h"
#include "../../../../core/midiChannel.h"
+#include "../../../../utils/gui.h"
#include "../../../../glue/channel.h"
#include "../../../../glue/io.h"
+#include "../../../../glue/recorder.h"
#include "../../../dialogs/gd_mainWindow.h"
-#include "../../../dialogs/gd_editor.h"
+#include "../../../dialogs/sampleEditor.h"
#include "../../../dialogs/gd_actionEditor.h"
#include "../../../dialogs/gd_warnings.h"
-#include "../../../dialogs/gd_browser.h"
#include "../../../dialogs/gd_keyGrabber.h"
#include "../../../dialogs/gd_pluginList.h"
#include "../../../dialogs/midiIO/midiInputChannel.h"
#include "../../../dialogs/midiIO/midiOutputMidiCh.h"
#include "../../basics/boxtypes.h"
+#include "../../basics/idButton.h"
+#include "../../basics/statusButton.h"
+#include "../../basics/dial.h"
#include "midiChannel.h"
-extern Recorder G_Recorder;
extern gdMainWindow *G_MainWin;
+using namespace giada::m;
+
+
+namespace
+{
+enum class Menu
+{
+ EDIT_ACTIONS = 0,
+ CLEAR_ACTIONS,
+ CLEAR_ACTIONS_ALL,
+ __END_SUBMENU__,
+ SETUP_KEYBOARD_INPUT,
+ SETUP_MIDI_INPUT,
+ SETUP_MIDI_OUTPUT,
+ CLONE_CHANNEL,
+ DELETE_CHANNEL
+};
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void menuCallback(Fl_Widget *w, void *v)
+{
+ geMidiChannel *gch = static_cast<geMidiChannel*>(w);
+ Menu selectedItem = (Menu) (intptr_t) v;
+
+ switch (selectedItem)
+ {
+ case Menu::CLEAR_ACTIONS:
+ case Menu::__END_SUBMENU__:
+ break;
+ case Menu::EDIT_ACTIONS:
+ gu_openSubWindow(G_MainWin, new gdActionEditor(gch->ch), WID_ACTION_EDITOR);
+ break;
+ case Menu::CLEAR_ACTIONS_ALL:
+ glue_clearAllActions(gch);
+ break;
+ case Menu::SETUP_KEYBOARD_INPUT:
+ gu_openSubWindow(G_MainWin, new gdKeyGrabber(gch->ch), 0);
+ break;
+ case Menu::SETUP_MIDI_INPUT:
+ gu_openSubWindow(G_MainWin, new gdMidiInputChannel(gch->ch), 0);
+ break;
+ case Menu::SETUP_MIDI_OUTPUT:
+ gu_openSubWindow(G_MainWin,
+ new gdMidiOutputMidiCh(static_cast<MidiChannel*>(gch->ch)), 0);
+ break;
+ case Menu::CLONE_CHANNEL:
+ glue_cloneChannel(gch->ch);
+ break;
+ case Menu::DELETE_CHANNEL:
+ glue_deleteChannel(gch->ch);
+ break;
+ }
+}
+
+}; // {namespace}
+
+
+/* -------------------------------------------------------------------------- */
+
+
geMidiChannel::geMidiChannel(int X, int Y, int W, int H, MidiChannel *ch)
: geChannel(X, Y, W, H, CHANNEL_MIDI, ch)
{
int delta = 120; // (5 widgets * 20) + (5 paddings * 4)
#endif
- button = new gButton(x(), y(), 20, 20, "", channelStop_xpm, channelPlay_xpm);
- arm = new gClick(button->x()+button->w()+4, y(), 20, 20, "", armOff_xpm, armOn_xpm);
+ button = new geIdButton(x(), y(), 20, 20, "", channelStop_xpm, channelPlay_xpm);
+ arm = new geButton(button->x()+button->w()+4, y(), 20, 20, "", armOff_xpm, armOn_xpm);
mainButton = new geMidiChannelButton(arm->x()+arm->w()+4, y(), w() - delta, 20, "-- MIDI --");
- mute = new gClick(mainButton->x()+mainButton->w()+4, y(), 20, 20, "", muteOff_xpm, muteOn_xpm);
- solo = new gClick(mute->x()+mute->w()+4, y(), 20, 20, "", soloOff_xpm, soloOn_xpm);
+ mute = new geButton(mainButton->x()+mainButton->w()+4, y(), 20, 20, "", muteOff_xpm, muteOn_xpm);
+ solo = new geButton(mute->x()+mute->w()+4, y(), 20, 20, "", soloOff_xpm, soloOn_xpm);
#if defined(WITH_VST)
- fx = new gFxButton(solo->x()+solo->w()+4, y(), 20, 20, fxOff_xpm, fxOn_xpm);
- vol = new gDial(fx->x()+fx->w()+4, y(), 20, 20);
+ fx = new geStatusButton(solo->x()+solo->w()+4, y(), 20, 20, fxOff_xpm, fxOn_xpm);
+ vol = new geDial(fx->x()+fx->w()+4, y(), 20, 20);
#else
- vol = new gDial(solo->x()+solo->w()+4, y(), 20, 20);
+ vol = new geDial(solo->x()+solo->w()+4, y(), 20, 20);
#endif
end();
void geMidiChannel::__cb_openMenu()
{
Fl_Menu_Item rclick_menu[] = {
- {"Edit actions..."}, // 0
- {"Clear actions", 0, 0, 0, FL_SUBMENU}, // 1
- {"All"}, // 2
- {0}, // 3
- {"Setup keyboard input..."}, // 5
- {"Setup MIDI input..."}, // 6
- {"Setup MIDI output..."}, // 7
- {"Clone channel"}, // 8
- {"Delete channel"}, // 9
+ {"Edit actions...", 0, menuCallback, (void*) Menu::EDIT_ACTIONS},
+ {"Clear actions", 0, menuCallback, (void*) Menu::CLEAR_ACTIONS, FL_SUBMENU},
+ {"All", 0, menuCallback, (void*) Menu::CLEAR_ACTIONS_ALL},
+ {0},
+ {"Setup keyboard input...", 0, menuCallback, (void*) Menu::SETUP_KEYBOARD_INPUT},
+ {"Setup MIDI input...", 0, menuCallback, (void*) Menu::SETUP_MIDI_INPUT},
+ {"Setup MIDI output...", 0, menuCallback, (void*) Menu::SETUP_MIDI_OUTPUT},
+ {"Clone channel", 0, menuCallback, (void*) Menu::CLONE_CHANNEL},
+ {"Delete channel", 0, menuCallback, (void*) Menu::DELETE_CHANNEL},
{0}
};
- /* no 'clear actions' if there are no actions */
+ /* No 'clear actions' if there are no actions. */
if (!ch->hasActions)
- rclick_menu[1].deactivate();
+ rclick_menu[(int)Menu::CLEAR_ACTIONS].deactivate();
Fl_Menu_Button *b = new Fl_Menu_Button(0, 0, 100, 50);
b->box(G_CUSTOM_BORDER_BOX);
b->textcolor(COLOR_TEXT_0);
b->color(COLOR_BG_0);
- const Fl_Menu_Item *m = rclick_menu->popup(Fl::event_x(), Fl::event_y(), 0, 0, b);
- if (!m) return;
-
- if (strcmp(m->label(), "Delete channel") == 0) {
- if (!gdConfirmWin("Warning", "Delete channel: are you sure?"))
- return;
- glue_deleteChannel(ch);
- return;
- }
-
- if (strcmp(m->label(), "Clone channel") == 0) {
- glue_cloneChannel(ch);
- return;
- }
-
- if (strcmp(m->label(), "Setup keyboard input...") == 0) {
- gu_openSubWindow(G_MainWin, new gdKeyGrabber(ch), 0);
- //new gdKeyGrabber(ch);
- return;
- }
-
- if (strcmp(m->label(), "All") == 0) {
- if (!gdConfirmWin("Warning", "Clear all actions: are you sure?"))
- return;
- G_Recorder.clearChan(ch->index);
- ch->hasActions = false;
- gu_refreshActionEditor(); // refresh a.editor window, it could be open
- return;
- }
-
- if (strcmp(m->label(), "Edit actions...") == 0) {
- gu_openSubWindow(G_MainWin, new gdActionEditor(ch), WID_ACTION_EDITOR);
- return;
- }
-
- if (strcmp(m->label(), "Setup MIDI input...") == 0) {
- gu_openSubWindow(G_MainWin, new gdMidiInputChannel(ch), 0);
- return;
- }
-
- if (strcmp(m->label(), "Setup MIDI output...") == 0) {
- //gu_openSubWindow(G_MainWin, new gdMidiGrabberChannel(ch, GrabForOutput), 0);
- gu_openSubWindow(G_MainWin, new gdMidiOutputMidiCh((MidiChannel*) ch), 0);
- return;
- }
+ 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;
}
mainButton->setKey(ch->key);
#ifdef WITH_VST
- fx->full = ch->plugins.size() > 0;
+ fx->status = ch->plugins.size() > 0;
fx->redraw();
#endif
}
int geMidiChannelButton::handle(int e)
{
// MIDI drag-n-drop does nothing so far.
- return gClick::handle(e);
+ return geButton::handle(e);
}
*
* Giada - Your Hardcore Loopmachine
*
- * ge_midiChannel
- *
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
#include "channelButton.h"
+class MidiChannel;
+
+
class geMidiChannel : public geChannel
{
private:
public:
- geMidiChannel(int x, int y, int w, int h, class MidiChannel *ch);
+ geMidiChannel(int x, int y, int w, int h, MidiChannel *ch);
void reset ();
void update ();
*
* Giada - Your Hardcore Loopmachine
*
- * ge_sampleChannel
- *
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
#include "../../../../core/mixer.h"
#include "../../../../core/conf.h"
-#include "../../../../core/recorder.h"
+#include "../../../../core/clock.h"
#include "../../../../core/graphics.h"
#include "../../../../core/wave.h"
#include "../../../../core/sampleChannel.h"
-#include "../../../../glue/main.h"
#include "../../../../glue/io.h"
#include "../../../../glue/channel.h"
+#include "../../../../glue/recorder.h"
#include "../../../../glue/storage.h"
-#include "../../../../utils/string.h"
+#include "../../../../utils/gui.h"
#include "../../../dialogs/gd_mainWindow.h"
#include "../../../dialogs/gd_keyGrabber.h"
-#include "../../../dialogs/gd_editor.h"
+#include "../../../dialogs/sampleEditor.h"
#include "../../../dialogs/gd_actionEditor.h"
#include "../../../dialogs/gd_warnings.h"
-#include "../../../dialogs/gd_browser.h"
+#include "../../../dialogs/browser/browserSave.h"
+#include "../../../dialogs/browser/browserLoad.h"
#include "../../../dialogs/midiIO/midiOutputSampleCh.h"
#include "../../../dialogs/midiIO/midiInputChannel.h"
#include "../../basics/boxtypes.h"
+#include "../../basics/idButton.h"
+#include "../../basics/statusButton.h"
+#include "../../basics/dial.h"
#include "channelStatus.h"
#include "channelMode.h"
+#include "sampleChannelButton.h"
#include "keyboard.h"
#include "sampleChannel.h"
-extern Mixer G_Mixer;
-extern Conf G_Conf;
-extern Recorder G_Recorder;
extern gdMainWindow *G_MainWin;
+using namespace giada::m;
+
+
+namespace
+{
+enum class Menu
+{
+ INPUT_MONITOR = 0,
+ LOAD_SAMPLE,
+ EXPORT_SAMPLE,
+ SETUP_KEYBOARD_INPUT,
+ SETUP_MIDI_INPUT,
+ SETUP_MIDI_OUTPUT,
+ EDIT_SAMPLE,
+ EDIT_ACTIONS,
+ CLEAR_ACTIONS,
+ CLEAR_ACTIONS_ALL,
+ CLEAR_ACTIONS_MUTE,
+ CLEAR_ACTIONS_VOLUME,
+ CLEAR_ACTIONS_START_STOP,
+ __END_SUBMENU__,
+ CLONE_CHANNEL,
+ FREE_CHANNEL,
+ DELETE_CHANNEL
+};
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void menuCallback(Fl_Widget *w, void *v)
+{
+ geSampleChannel *gch = static_cast<geSampleChannel*>(w);
+ Menu selectedItem = (Menu) (intptr_t) v;
+
+ switch (selectedItem)
+ {
+ case Menu::INPUT_MONITOR: {
+ glue_toggleInputMonitor(gch->ch);
+ break;
+ }
+ case Menu::LOAD_SAMPLE: {
+ gdWindow *w = new gdBrowserLoad(conf::browserX, conf::browserY,
+ conf::browserW, conf::browserH, "Browse sample",
+ conf::samplePath.c_str(), glue_loadSample, gch->ch);
+ gu_openSubWindow(G_MainWin, w, WID_FILE_BROWSER);
+ break;
+ }
+ case Menu::EXPORT_SAMPLE: {
+ gdWindow *w = new gdBrowserSave(conf::browserX, conf::browserY,
+ conf::browserW, conf::browserH, "Save sample",
+ conf::samplePath.c_str(), "", glue_saveSample, gch->ch);
+ gu_openSubWindow(G_MainWin, w, WID_FILE_BROWSER);
+ break;
+ }
+ case Menu::SETUP_KEYBOARD_INPUT: {
+ new gdKeyGrabber(gch->ch); // FIXME - use gu_openSubWindow
+ break;
+ }
+ case Menu::SETUP_MIDI_INPUT: {
+ gu_openSubWindow(G_MainWin, new gdMidiInputChannel(gch->ch), 0);
+ break;
+ }
+ case Menu::SETUP_MIDI_OUTPUT: {
+ gu_openSubWindow(G_MainWin, new gdMidiOutputSampleCh(static_cast<SampleChannel*>(gch->ch)), 0);
+ break;
+ }
+ case Menu::EDIT_SAMPLE: {
+ gu_openSubWindow(G_MainWin, new gdSampleEditor(static_cast<SampleChannel*>(gch->ch)), WID_SAMPLE_EDITOR);
+ break;
+ }
+ case Menu::EDIT_ACTIONS: {
+ gu_openSubWindow(G_MainWin, new gdActionEditor(gch->ch), WID_ACTION_EDITOR);
+ break;
+ }
+ case Menu::CLEAR_ACTIONS:
+ case Menu::__END_SUBMENU__:
+ break;
+ case Menu::CLEAR_ACTIONS_ALL: {
+ glue_clearAllActions(gch);
+ break;
+ }
+ case Menu::CLEAR_ACTIONS_MUTE: {
+ glue_clearMuteActions(gch);
+ break;
+ }
+ case Menu::CLEAR_ACTIONS_VOLUME: {
+ glue_clearVolumeActions(gch);
+ break;
+ }
+ case Menu::CLEAR_ACTIONS_START_STOP: {
+ glue_clearStartStopActions(gch);
+ break;
+ }
+ case Menu::CLONE_CHANNEL: {
+ glue_cloneChannel(gch->ch);
+ break;
+ }
+ case Menu::FREE_CHANNEL: {
+ glue_freeChannel(gch->ch);
+ break;
+ }
+ case Menu::DELETE_CHANNEL: {
+ glue_deleteChannel(gch->ch);
+ break;
+ }
+ }
+}
+
+}; // {namespace}
+
+
+/* -------------------------------------------------------------------------- */
+
+
geSampleChannel::geSampleChannel(int X, int Y, int W, int H, SampleChannel *ch)
: geChannel(X, Y, W, H, CHANNEL_SAMPLE, (Channel*) ch)
{
begin();
- button = new gButton(x(), y(), 20, 20, "", channelStop_xpm, channelPlay_xpm);
- arm = new gClick(button->x()+button->w()+4, y(), 20, 20, "", armOff_xpm, armOn_xpm);
+ button = new geIdButton(x(), y(), 20, 20, "", channelStop_xpm, channelPlay_xpm);
+ arm = new geButton(button->x()+button->w()+4, y(), 20, 20, "", armOff_xpm, armOn_xpm);
status = new geChannelStatus(arm->x()+arm->w()+4, y(), 20, 20, ch);
mainButton = new geSampleChannelButton(status->x()+status->w()+4, y(), 20, 20, "-- no sample --");
- readActions = new gClick(mainButton->x()+mainButton->w()+4, y(), 20, 20, "", readActionOff_xpm, readActionOn_xpm);
+ readActions = new geButton(mainButton->x()+mainButton->w()+4, y(), 20, 20, "", readActionOff_xpm, readActionOn_xpm);
modeBox = new geChannelMode(readActions->x()+readActions->w()+4, y(), 20, 20, ch);
- mute = new gClick(modeBox->x()+modeBox->w()+4, y(), 20, 20, "", muteOff_xpm, muteOn_xpm);
- solo = new gClick(mute->x()+mute->w()+4, y(), 20, 20, "", soloOff_xpm, soloOn_xpm);
+ mute = new geButton(modeBox->x()+modeBox->w()+4, y(), 20, 20, "", muteOff_xpm, muteOn_xpm);
+ solo = new geButton(mute->x()+mute->w()+4, y(), 20, 20, "", soloOff_xpm, soloOn_xpm);
#ifdef WITH_VST
- fx = new gFxButton(solo->x()+solo->w()+4, y(), 20, 20, fxOff_xpm, fxOn_xpm);
- vol = new gDial(fx->x()+fx->w()+4, y(), 20, 20);
+ fx = new geStatusButton(solo->x()+solo->w()+4, y(), 20, 20, fxOff_xpm, fxOn_xpm);
+ vol = new geDial(fx->x()+fx->w()+4, y(), 20, 20);
#else
- vol = new gDial(solo->x()+solo->w()+4, y(), 20, 20);
+ vol = new geDial(solo->x()+solo->w()+4, y(), 20, 20);
#endif
end();
/* If you're recording (input or actions) no menu is allowed; you can't do
anything, especially deallocate the channel */
- if (G_Mixer.recording || G_Recorder.active)
+ if (mixer::recording || recorder::active)
return;
- /* the following is a trash workaround for a FLTK menu. We need a gMenu
- * widget asap */
-
Fl_Menu_Item rclick_menu[] = {
- {"Load new sample..."}, // 0
- {"Export sample to file..."}, // 1
- {"Setup keyboard input..."}, // 2
- {"Setup MIDI input..."}, // 3
- {"Setup MIDI output..."}, // 4
- {"Edit sample..."}, // 5
- {"Edit actions..."}, // 6
- {"Clear actions", 0, 0, 0, FL_SUBMENU}, // 7
- {"All"}, // 8
- {"Mute"}, // 9
- {"Volume"}, // 10
- {"Start/Stop"}, // 11
- {0}, // 12
- {"Clone channel"}, // 13
- {"Free channel"}, // 14
- {"Delete channel"}, // 15
+ {"Input monitor", 0, menuCallback, (void*) Menu::INPUT_MONITOR,
+ FL_MENU_TOGGLE | FL_MENU_DIVIDER | (static_cast<SampleChannel*>(ch)->inputMonitor ? FL_MENU_VALUE : 0)},
+ {"Load new sample...", 0, menuCallback, (void*) Menu::LOAD_SAMPLE},
+ {"Export sample to file...", 0, menuCallback, (void*) Menu::EXPORT_SAMPLE},
+ {"Setup keyboard input...", 0, menuCallback, (void*) Menu::SETUP_KEYBOARD_INPUT},
+ {"Setup MIDI input...", 0, menuCallback, (void*) Menu::SETUP_MIDI_INPUT},
+ {"Setup MIDI output...", 0, menuCallback, (void*) Menu::SETUP_MIDI_OUTPUT},
+ {"Edit sample...", 0, menuCallback, (void*) Menu::EDIT_SAMPLE},
+ {"Edit actions...", 0, menuCallback, (void*) Menu::EDIT_ACTIONS},
+ {"Clear actions", 0, menuCallback, (void*) Menu::CLEAR_ACTIONS, FL_SUBMENU},
+ {"All", 0, menuCallback, (void*) Menu::CLEAR_ACTIONS_ALL},
+ {"Mute", 0, menuCallback, (void*) Menu::CLEAR_ACTIONS_MUTE},
+ {"Volume", 0, menuCallback, (void*) Menu::CLEAR_ACTIONS_VOLUME},
+ {"Start/Stop", 0, menuCallback, (void*) Menu::CLEAR_ACTIONS_START_STOP},
+ {0},
+ {"Clone channel", 0, menuCallback, (void*) Menu::CLONE_CHANNEL},
+ {"Free channel", 0, menuCallback, (void*) Menu::FREE_CHANNEL},
+ {"Delete channel", 0, menuCallback, (void*) Menu::DELETE_CHANNEL},
{0}
};
if (ch->status & (STATUS_EMPTY | STATUS_MISSING)) {
- rclick_menu[1].deactivate();
- rclick_menu[5].deactivate();
- rclick_menu[14].deactivate();
+ rclick_menu[(int) Menu::EXPORT_SAMPLE].deactivate();
+ rclick_menu[(int) Menu::EDIT_SAMPLE].deactivate();
+ rclick_menu[(int) Menu::FREE_CHANNEL].deactivate();
}
- /* no 'clear actions' if there are no actions */
-
if (!ch->hasActions)
- rclick_menu[7].deactivate();
+ rclick_menu[(int) Menu::CLEAR_ACTIONS].deactivate();
- /* no 'clear start/stop actions' for those channels in loop mode:
- * they cannot have start/stop actions. */
+ /* No 'clear start/stop actions' for those channels in loop mode: they cannot
+ have start/stop actions. */
- if (((SampleChannel*)ch)->mode & LOOP_ANY)
- rclick_menu[11].deactivate();
+ if (static_cast<SampleChannel*>(ch)->mode & LOOP_ANY)
+ rclick_menu[(int) Menu::CLEAR_ACTIONS_START_STOP].deactivate();
Fl_Menu_Button *b = new Fl_Menu_Button(0, 0, 100, 50);
b->box(G_CUSTOM_BORDER_BOX);
b->color(COLOR_BG_0);
const Fl_Menu_Item *m = rclick_menu->popup(Fl::event_x(), Fl::event_y(), 0, 0, b);
- if (!m) return;
-
- if (strcmp(m->label(), "Load new sample...") == 0) {
- gWindow *w = new gdLoadBrowser(G_Conf.browserX, G_Conf.browserY,
- G_Conf.browserW, G_Conf.browserH, "Browse sample",
- G_Conf.samplePath.c_str(), glue_loadSample, ch);
- gu_openSubWindow(G_MainWin, w, WID_FILE_BROWSER);
- return;
- }
-
- if (strcmp(m->label(), "Setup keyboard input...") == 0) {
- new gdKeyGrabber(ch); /// FIXME - use gu_openSubWindow
- return;
- }
-
- if (strcmp(m->label(), "Setup MIDI input...") == 0) {
- gu_openSubWindow(G_MainWin, new gdMidiInputChannel(ch), 0);
- return;
- }
-
- if (strcmp(m->label(), "Setup MIDI output...") == 0) {
- gu_openSubWindow(G_MainWin, new gdMidiOutputSampleCh((SampleChannel*) ch), 0);
- return;
- }
-
- if (strcmp(m->label(), "Edit sample...") == 0) {
- gu_openSubWindow(G_MainWin, new gdEditor((SampleChannel*) ch), WID_SAMPLE_EDITOR); /// FIXME title it's up to gdEditor
- return;
- }
-
- if (strcmp(m->label(), "Export sample to file...") == 0) {
- gWindow *w = new gdSaveBrowser(G_Conf.browserX, G_Conf.browserY,
- G_Conf.browserW, G_Conf.browserH, "Save sample", \
- G_Conf.samplePath.c_str(), "", glue_saveSample, ch);
- gu_openSubWindow(G_MainWin, w, WID_FILE_BROWSER);
- return;
- }
-
- if (strcmp(m->label(), "Delete channel") == 0) {
- if (!gdConfirmWin("Warning", "Delete channel: are you sure?"))
- return;
- glue_deleteChannel(ch);
- return;
- }
-
- if (strcmp(m->label(), "Free channel") == 0) {
- if (ch->status == STATUS_PLAY) {
- if (!gdConfirmWin("Warning", "This action will stop the channel: are you sure?"))
- return;
- }
- else if (!gdConfirmWin("Warning", "Free channel: are you sure?"))
- return;
-
- glue_freeChannel(ch);
-
- /* delete any related subwindow */
-
- /** FIXME - use gu_closeAllSubwindows() */
-
- G_MainWin->delSubWindow(WID_FILE_BROWSER);
- G_MainWin->delSubWindow(WID_ACTION_EDITOR);
- G_MainWin->delSubWindow(WID_SAMPLE_EDITOR);
- G_MainWin->delSubWindow(WID_FX_LIST);
-
- return;
- }
-
- if (strcmp(m->label(), "Clone channel") == 0) {
- glue_cloneChannel(ch);
- return;
- }
-
- if (strcmp(m->label(), "Mute") == 0) {
- if (!gdConfirmWin("Warning", "Clear all mute actions: are you sure?"))
- return;
- G_Recorder.clearAction(ch->index, ACTION_MUTEON | ACTION_MUTEOFF);
- ch->hasActions = G_Recorder.hasActions(ch->index);
-
- if (!ch->hasActions)
- hideActionButton();
-
- /* TODO - set mute=false */
-
- gu_refreshActionEditor(); // refresh a.editor window, it could be open
- return;
- }
-
- if (strcmp(m->label(), "Start/Stop") == 0) {
- if (!gdConfirmWin("Warning", "Clear all start/stop actions: are you sure?"))
- return;
- G_Recorder.clearAction(ch->index, ACTION_KEYPRESS | ACTION_KEYREL | ACTION_KILLCHAN);
- ch->hasActions = G_Recorder.hasActions(ch->index);
-
- if (!ch->hasActions)
- hideActionButton();
- gu_refreshActionEditor(); // refresh a.editor window, it could be open
- return;
- }
-
- if (strcmp(m->label(), "Volume") == 0) {
- if (!gdConfirmWin("Warning", "Clear all volume actions: are you sure?"))
- return;
- G_Recorder.clearAction(ch->index, ACTION_VOLUME);
- ch->hasActions = G_Recorder.hasActions(ch->index);
- if (!ch->hasActions)
- hideActionButton();
- gu_refreshActionEditor(); // refresh a.editor window, it could be open
- return;
- }
-
- if (strcmp(m->label(), "All") == 0) {
- if (!gdConfirmWin("Warning", "Clear all actions: are you sure?"))
- return;
- G_Recorder.clearChan(ch->index);
- ch->hasActions = false;
- hideActionButton();
- gu_refreshActionEditor(); // refresh a.editor window, it could be open
- return;
- }
-
- if (strcmp(m->label(), "Edit actions...") == 0) {
- gu_openSubWindow(G_MainWin, new gdActionEditor(ch), WID_ACTION_EDITOR);
- return;
- }
+ if (m)
+ m->do_callback(this, m->user_data());
+ return;
}
setColorsByStatus(ch->status, ch->recStatus);
- if (((SampleChannel*) ch)->wave != NULL) {
- if (G_Mixer.recording && ch->armed)
+ if (((SampleChannel*) ch)->wave != nullptr) {
+ if (mixer::recording && ch->armed)
mainButton->setInputRecordMode();
- if (G_Recorder.active) {
- if (G_Recorder.canRec(ch, &G_Mixer))
+ if (recorder::active) {
+ if (recorder::canRec(ch, clock::isRunning(), mixer::recording))
mainButton->setActionRecordMode();
}
status->redraw(); // status invisible? sampleButton too (see below)
mainButton->setKey(ch->key);
#ifdef WITH_VST
- fx->full = ch->plugins.size() > 0;
+ fx->status = ch->plugins.size() > 0;
fx->redraw();
#endif
}
packWidgets();
}
-
-
-/* -------------------------------------------------------------------------- */
-/* -------------------------------------------------------------------------- */
-/* -------------------------------------------------------------------------- */
-
-
-geSampleChannelButton::geSampleChannelButton(int x, int y, int w, int h, const char *l)
- : geChannelButton(x, y, w, h, l) {}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-int geSampleChannelButton::handle(int e)
-{
- int ret = gClick::handle(e);
- switch (e) {
- case FL_DND_ENTER:
- case FL_DND_DRAG:
- case FL_DND_RELEASE: {
- ret = 1;
- break;
- }
- case FL_PASTE: {
- geSampleChannel *gch = (geSampleChannel*) parent(); // parent is geSampleChannel
- SampleChannel *ch = (SampleChannel*) gch->ch;
- int result = glue_loadChannel(ch, gu_trim(gu_stripFileUrl(Fl::event_text())).c_str());
- if (result != SAMPLE_LOADED_OK)
- G_MainWin->keyboard->printChannelMessage(result);
- ret = 1;
- break;
- }
- }
- return ret;
-}
*
* Giada - Your Hardcore Loopmachine
*
- * ge_sampleChannel
- *
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
#include "channel.h"
-#include "channelButton.h"
+
+
+class SampleChannel;
+class geChannelMode;
+class geButton;
class geSampleChannel : public geChannel
public:
- geSampleChannel(int x, int y, int w, int h, class SampleChannel *ch);
+ geSampleChannel(int x, int y, int w, int h, SampleChannel *ch);
void reset ();
void update ();
void showActionButton();
void hideActionButton();
- class geChannelMode *modeBox;
- class gClick *readActions;
-};
-
-
-/* -------------------------------------------------------------------------- */
-
-
-class geSampleChannelButton : public geChannelButton
-{
-public:
- geSampleChannelButton(int x, int y, int w, int h, const char *l=0);
- int handle(int e);
+ geChannelMode *modeBox;
+ geButton *readActions;
};
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * 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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#include <FL/Fl.H>
+#include "../../../../core/const.h"
+#include "../../../../core/sampleChannel.h"
+#include "../../../../utils/string.h"
+#include "../../../../utils/fs.h"
+#include "../../../../glue/channel.h"
+#include "../../../dialogs/gd_mainWindow.h"
+#include "sampleChannel.h"
+#include "keyboard.h"
+#include "sampleChannelButton.h"
+
+
+extern gdMainWindow *G_MainWin;
+
+
+geSampleChannelButton::geSampleChannelButton(int x, int y, int w, int h,
+ const char *l)
+ : geChannelButton(x, y, w, h, l)
+{
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int geSampleChannelButton::handle(int e)
+{
+ int ret = geButton::handle(e);
+ switch (e) {
+ case FL_DND_ENTER:
+ case FL_DND_DRAG:
+ case FL_DND_RELEASE: {
+ ret = 1;
+ break;
+ }
+ case FL_PASTE: {
+ geSampleChannel *gch = static_cast<geSampleChannel*>(parent());
+ SampleChannel *ch = static_cast<SampleChannel*>(gch->ch);
+ int result = glue_loadChannel(ch, gu_trim(gu_stripFileUrl(Fl::event_text())).c_str());
+ if (result != SAMPLE_LOADED_OK)
+ G_MainWin->keyboard->printChannelMessage(result);
+ ret = 1;
+ break;
+ }
+ }
+ return ret;
+}
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * 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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#ifndef GE_SAMPLE_CHANNEL_BUTTON_H
+#define GE_SAMPLE_CHANNEL_BUTTON_H
+
+
+#include "channelButton.h"
+
+
+class geSampleChannelButton : public geChannelButton
+{
+public:
+
+ geSampleChannelButton(int x, int y, int w, int h, const char *l=0);
+ int handle(int e);
+};
+
+
+#endif
*
* Giada - Your Hardcore Loopmachine
*
-* mainIO
-*
* -----------------------------------------------------------------------------
*
-* Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+* Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
#include "../../../core/pluginHost.h"
#include "../../../glue/main.h"
#include "../../../utils/gui.h"
-#include "../../elems/ge_mixed.h"
+#include "../../elems/soundMeter.h"
+#include "../../elems/basics/statusButton.h"
+#include "../../elems/basics/dial.h"
#include "../../dialogs/gd_mainWindow.h"
#include "../../dialogs/gd_pluginList.h"
#include "mainIO.h"
-extern Mixer G_Mixer;
extern gdMainWindow *G_MainWin;
+using namespace giada::m;
+
+
geMainIO::geMainIO(int x, int y)
: Fl_Group(x, y, 396, 20)
{
begin();
#if defined(WITH_VST)
- masterFxIn = new gFxButton (x, y, 20, 20, fxOff_xpm, fxOn_xpm);
- inVol = new gDial (masterFxIn->x()+masterFxIn->w()+4, y, 20, 20);
- inMeter = new gSoundMeter(inVol->x()+inVol->w()+4, y+4, 140, 12);
- inToOut = new gClick (inMeter->x()+inMeter->w()+4, y+4, 12, 12, "", inputToOutputOff_xpm, inputToOutputOn_xpm);
- outMeter = new gSoundMeter(inToOut->x()+inToOut->w()+4, y+4, 140, 12);
- outVol = new gDial (outMeter->x()+outMeter->w()+4, y, 20, 20);
- masterFxOut = new gFxButton (outVol->x()+outVol->w()+4, y, 20, 20, fxOff_xpm, fxOn_xpm);
+ masterFxIn = new geStatusButton (x, y, 20, 20, fxOff_xpm, fxOn_xpm);
+ inVol = new geDial (masterFxIn->x()+masterFxIn->w()+4, y, 20, 20);
+ inMeter = new geSoundMeter(inVol->x()+inVol->w()+4, y+4, 140, 12);
+ inToOut = new geButton (inMeter->x()+inMeter->w()+4, y+4, 12, 12, "", inputToOutputOff_xpm, inputToOutputOn_xpm);
+ outMeter = new geSoundMeter(inToOut->x()+inToOut->w()+4, y+4, 140, 12);
+ outVol = new geDial (outMeter->x()+outMeter->w()+4, y, 20, 20);
+ masterFxOut = new geStatusButton (outVol->x()+outVol->w()+4, y, 20, 20, fxOff_xpm, fxOn_xpm);
#else
- inVol = new gDial (x+62, y, 20, 20);
- inMeter = new gSoundMeter(inVol->x()+inVol->w()+4, y+5, 140, 12);
- outMeter = new gSoundMeter(inMeter->x()+inMeter->w()+4, y+5, 140, 12);
- outVol = new gDial (outMeter->x()+outMeter->w()+4, y, 20, 20);
+ inVol = new geDial (x+62, y, 20, 20);
+ inMeter = new geSoundMeter(inVol->x()+inVol->w()+4, y+5, 140, 12);
+ outMeter = new geSoundMeter(inMeter->x()+inMeter->w()+4, y+5, 140, 12);
+ outVol = new geDial (outMeter->x()+outMeter->w()+4, y, 20, 20);
#endif
end();
- resizable(NULL); // don't resize any widget
+ resizable(nullptr); // don't resize any widget
outVol->callback(cb_outVol, (void*)this);
- outVol->value(G_Mixer.outVol);
+ outVol->value(mixer::outVol);
inVol->callback(cb_inVol, (void*)this);
- inVol->value(G_Mixer.inVol);
+ inVol->value(mixer::inVol);
#ifdef WITH_VST
masterFxOut->callback(cb_masterFxOut, (void*)this);
#ifdef WITH_VST
void geMainIO::__cb_masterFxOut()
{
- gu_openSubWindow(G_MainWin, new gdPluginList(PluginHost::MASTER_OUT), WID_FX_LIST);
+ gu_openSubWindow(G_MainWin, new gdPluginList(pluginHost::MASTER_OUT), WID_FX_LIST);
}
void geMainIO::__cb_masterFxIn()
{
- gu_openSubWindow(G_MainWin, new gdPluginList(PluginHost::MASTER_IN), WID_FX_LIST);
+ gu_openSubWindow(G_MainWin, new gdPluginList(pluginHost::MASTER_IN), WID_FX_LIST);
}
void geMainIO::__cb_inToOut()
{
- G_Mixer.inToOut = inToOut->value();
+ mixer::inToOut = inToOut->value();
}
#endif
void geMainIO::setMasterFxOutFull(bool v)
{
- masterFxOut->full = v;
+ masterFxOut->status = v;
masterFxOut->redraw();
}
void geMainIO::setMasterFxInFull(bool v)
{
- masterFxIn->full = v;
+ masterFxIn->status = v;
masterFxIn->redraw();
}
void geMainIO::refresh()
{
- outMeter->mixerPeak = G_Mixer.peakOut;
- inMeter->mixerPeak = G_Mixer.peakIn;
+ outMeter->mixerPeak = mixer::peakOut;
+ inMeter->mixerPeak = mixer::peakIn;
outMeter->redraw();
inMeter->redraw();
}
*
* Giada - Your Hardcore Loopmachine
*
- * mainIO
- *
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
#include <FL/Fl_Group.H>
+class geSoundMeter;
+class geDial;
+#ifdef WITH_VST
+class geStatusButton;
+class geButton;
+#endif
class geMainIO : public Fl_Group
{
private:
- class gSoundMeter *outMeter;
- class gSoundMeter *inMeter;
- class gDial *outVol;
- class gDial *inVol;
+ geSoundMeter *outMeter;
+ geSoundMeter *inMeter;
+ geDial *outVol;
+ geDial *inVol;
#ifdef WITH_VST
- class gFxButton *masterFxOut;
- class gFxButton *masterFxIn;
- class gClick *inToOut;
+ geStatusButton *masterFxOut;
+ geStatusButton *masterFxIn;
+ geButton *inToOut;
#endif
static void cb_outVol (Fl_Widget *v, void *p);
*
* Giada - Your Hardcore Loopmachine
*
- * mainMenu
- *
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
* -------------------------------------------------------------------------- */
+#include <FL/Fl_Menu_Button.H>
#include "../../../core/const.h"
#include "../../../core/mixer.h"
+#include "../../../core/mixerHandler.h"
#include "../../../core/conf.h"
#include "../../../core/patch.h"
#include "../../../core/channel.h"
#include "../../../utils/gui.h"
#include "../../../glue/storage.h"
#include "../../../glue/main.h"
-#include "../../elems/ge_mixed.h"
#include "../../elems/basics/boxtypes.h"
+#include "../../elems/basics/button.h"
#include "../../dialogs/gd_mainWindow.h"
#include "../../dialogs/gd_about.h"
#include "../../dialogs/gd_config.h"
-#include "../../dialogs/gd_browser.h"
#include "../../dialogs/gd_warnings.h"
+#include "../../dialogs/browser/browserLoad.h"
+#include "../../dialogs/browser/browserSave.h"
#include "../../dialogs/midiIO/midiInputMaster.h"
#include "keyboard/keyboard.h"
#include "mainMenu.h"
-extern Mixer G_Mixer;
-extern Patch G_Patch;
-extern Conf G_Conf;
extern gdMainWindow *G_MainWin;
+using namespace giada::m;
+
+
geMainMenu::geMainMenu(int x, int y)
: Fl_Group(x, y, 300, 20)
{
begin();
- file = new gClick(x, y, 70, 21, "file");
- edit = new gClick(file->x()+file->w()+4, y, 70, 21, "edit");
- config = new gClick(edit->x()+edit->w()+4, y, 70, 21, "config");
- about = new gClick(config->x()+config->w()+4, y, 70, 21, "about");
+ file = new geButton(x, y, 70, 21, "file");
+ edit = new geButton(file->x()+file->w()+4, y, 70, 21, "edit");
+ config = new geButton(edit->x()+edit->w()+4, y, 70, 21, "config");
+ about = new geButton(config->x()+config->w()+4, y, 70, 21, "about");
end();
- resizable(NULL); // don't resize any widget
+ resizable(nullptr); // don't resize any widget
about->callback(cb_about, (void*)this);
file->callback(cb_file, (void*)this);
if (!m) return;
if (strcmp(m->label(), "Open patch or project...") == 0) {
- gWindow *childWin = new gdLoadBrowser(G_Conf.browserX, G_Conf.browserY,
- G_Conf.browserW, G_Conf.browserH, "Load patch or project",
- G_Conf.patchPath, glue_loadPatch, NULL);
+ gdWindow *childWin = new gdBrowserLoad(conf::browserX, conf::browserY,
+ conf::browserW, conf::browserH, "Load patch or project",
+ conf::patchPath, glue_loadPatch, nullptr);
gu_openSubWindow(G_MainWin, childWin, WID_FILE_BROWSER);
return;
}
if (strcmp(m->label(), "Save patch...") == 0) {
- if (G_Mixer.hasLogicalSamples() || G_Mixer.hasEditedSamples())
+ if (mh::hasLogicalSamples() || mh::hasEditedSamples())
if (!gdConfirmWin("Warning", "You should save a project in order to store\nyour takes and/or processed samples."))
return;
- gWindow *childWin = new gdSaveBrowser(G_Conf.browserX, G_Conf.browserY,
- G_Conf.browserW, G_Conf.browserH, "Save patch",
- G_Conf.patchPath, G_Patch.name, glue_savePatch, NULL);
+ gdWindow *childWin = new gdBrowserSave(conf::browserX, conf::browserY,
+ conf::browserW, conf::browserH, "Save patch",
+ conf::patchPath, patch::name, glue_savePatch, nullptr);
gu_openSubWindow(G_MainWin, childWin, WID_FILE_BROWSER);
return;
}
if (strcmp(m->label(), "Save project...") == 0) {
- gWindow *childWin = new gdSaveBrowser(G_Conf.browserX, G_Conf.browserY,
- G_Conf.browserW, G_Conf.browserH, "Save project",
- G_Conf.patchPath, G_Patch.name, glue_saveProject, NULL);
+ gdWindow *childWin = new gdBrowserSave(conf::browserX, conf::browserY,
+ conf::browserW, conf::browserH, "Save project",
+ conf::patchPath, patch::name, glue_saveProject, nullptr);
gu_openSubWindow(G_MainWin, childWin, WID_FILE_BROWSER);
return;
}
menu[1].deactivate();
- for (unsigned i=0; i<G_Mixer.channels.size(); i++)
- if (G_Mixer.channels.at(i)->hasActions) {
+ for (unsigned i=0; i<mixer::channels.size(); i++)
+ if (mixer::channels.at(i)->hasActions) {
menu[1].activate();
break;
}
- for (unsigned i=0; i<G_Mixer.channels.size(); i++)
- if (G_Mixer.channels.at(i)->type == CHANNEL_SAMPLE)
- if (((SampleChannel*)G_Mixer.channels.at(i))->wave != NULL) {
+ for (unsigned i=0; i<mixer::channels.size(); i++)
+ if (mixer::channels.at(i)->type == CHANNEL_SAMPLE)
+ if (((SampleChannel*)mixer::channels.at(i))->wave != nullptr) {
menu[0].activate();
break;
}
*
* Giada - Your Hardcore Loopmachine
*
- * mainMenu
- *
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
#include <FL/Fl_Group.H>
+class geButton;
+
+
class geMainMenu : public Fl_Group
{
private:
- class gClick *file;
- class gClick *edit;
- class gClick *config;
- class gClick *about;
+ geButton *file;
+ geButton *edit;
+ geButton *config;
+ geButton *about;
static void cb_about (Fl_Widget *v, void *p);
static void cb_config(Fl_Widget *v, void *p);
*
* Giada - Your Hardcore Loopmachine
*
- * mainTimer
- *
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
#include "../../../core/const.h"
#include "../../../core/mixer.h"
#include "../../../core/graphics.h"
+#include "../../../core/clock.h"
#include "../../../glue/main.h"
#include "../../../utils/gui.h"
-#include "../../elems/ge_mixed.h"
+#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 "mainTimer.h"
-extern Mixer G_Mixer;
extern gdMainWindow *G_MainWin;
+using namespace giada::m;
+
+
geMainTimer::geMainTimer(int x, int y)
: Fl_Group(x, y, 180, 20)
{
begin();
- quantizer = new gChoice(x, y, 40, 20, "", false);
- bpm = new gClick (quantizer->x()+quantizer->w()+4, y, 40, 20);
- meter = new gClick (bpm->x()+bpm->w()+8, y, 40, 20, "4/1");
- multiplier = new gClick (meter->x()+meter->w()+4, y, 20, 20, "", multiplyOff_xpm, multiplyOn_xpm);
- divider = new gClick (multiplier->x()+multiplier->w()+4, y, 20, 20, "", divideOff_xpm, divideOn_xpm);
+ quantizer = new geChoice(x, y, 40, 20, "", false);
+ bpm = new geButton (quantizer->x()+quantizer->w()+4, y, 40, 20);
+ meter = new geButton (bpm->x()+bpm->w()+8, y, 40, 20, "4/1");
+ multiplier = new geButton (meter->x()+meter->w()+4, y, 20, 20, "", multiplyOff_xpm, multiplyOn_xpm);
+ divider = new geButton (multiplier->x()+multiplier->w()+4, y, 20, 20, "", divideOff_xpm, divideOn_xpm);
end();
- resizable(NULL); // don't resize any widget
+ resizable(nullptr); // don't resize any widget
- char buf[6]; snprintf(buf, 6, "%f", G_Mixer.bpm);
+ char buf[6]; snprintf(buf, 6, "%f", clock::getBpm());
bpm->copy_label(buf);
bpm->callback(cb_bpm, (void*)this);
*
* Giada - Your Hardcore Loopmachine
*
- * mainTimer
- *
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
#include <FL/Fl_Group.H>
+class geButton;
+class geChoice;
+
+
class geMainTimer : public Fl_Group
{
private:
- class gClick *bpm;
- class gClick *meter;
- class gChoice *quantizer;
- class gClick *multiplier;
- class gClick *divider;
+ geButton *bpm;
+ geButton *meter;
+ geChoice *quantizer;
+ geButton *multiplier;
+ geButton *divider;
static void cb_bpm (Fl_Widget *v, void *p);
static void cb_meter (Fl_Widget *v, void *p);
*
* Giada - Your Hardcore Loopmachine
*
- * mainTransport
- *
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
#include "../../../core/graphics.h"
-#include "../../../glue/main.h"
+#include "../../../glue/transport.h"
#include "../../../glue/io.h"
-#include "../ge_mixed.h"
+#include "../../elems/basics/button.h"
#include "mainTransport.h"
{
begin();
- rewind = new gClick(x, y, 25, 25, "", rewindOff_xpm, rewindOn_xpm);
- play = new gClick(rewind->x()+rewind->w()+4, y, 25, 25, "", play_xpm, pause_xpm);
- recAction = new gClick(play->x()+play->w()+4, y, 25, 25, "", recOff_xpm, recOn_xpm);
- recInput = new gClick(recAction->x()+recAction->w()+4, y, 25, 25, "", inputRecOff_xpm, inputRecOn_xpm);
- metronome = new gClick(recInput->x()+recInput->w()+4, y+10, 15, 15, "", metronomeOff_xpm, metronomeOn_xpm);
+ rewind = new geButton(x, y, 25, 25, "", rewindOff_xpm, rewindOn_xpm);
+ play = new geButton(rewind->x()+rewind->w()+4, y, 25, 25, "", play_xpm, pause_xpm);
+ recAction = new geButton(play->x()+play->w()+4, y, 25, 25, "", recOff_xpm, recOn_xpm);
+ recInput = new geButton(recAction->x()+recAction->w()+4, y, 25, 25, "", inputRecOff_xpm, inputRecOn_xpm);
+ metronome = new geButton(recInput->x()+recInput->w()+4, y+10, 15, 15, "", metronomeOff_xpm, metronomeOn_xpm);
end();
- resizable(NULL); // don't resize any widget
+ resizable(nullptr); // don't resize any widget
rewind->callback(cb_rewind, (void*)this);
void geMainTransport::__cb_rewind()
{
- glue_rewindSeq();
+ glue_rewindSeq(true);
}
*
* Giada - Your Hardcore Loopmachine
*
- * mainTransport
- *
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
#include <FL/Fl_Group.H>
+class geButton;
+
+
class geMainTransport : public Fl_Group
{
private:
- class gClick *rewind;
- class gClick *play;
- class gClick *recAction;
- class gClick *recInput;
- class gClick *metronome;
+ geButton *rewind;
+ geButton *play;
+ geButton *recAction;
+ geButton *recInput;
+ geButton *metronome;
static void cb_rewind (Fl_Widget *v, void *p);
static void cb_play (Fl_Widget *v, void *p);
*
* Giada - Your Hardcore Loopmachine
*
- * midiLearner
- *
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
* -------------------------------------------------------------------------- */
-#include "ge_mixed.h"
#include "basics/boxtypes.h"
+#include "basics/button.h"
+#include "basics/box.h"
#include "midiLearner.h"
-extern KernelMidi G_KernelMidi;
+using namespace giada::m;
geMidiLearner::geMidiLearner(int X, int Y, int W, const char *l,
- KernelMidi::cb_midiLearn *cb, uint32_t *param)
+ kernelMidi::cb_midiLearn *cb, uint32_t *param)
: Fl_Group(X, Y, W, 20),
callback(cb),
param (param)
{
begin();
- text = new gBox(x(), y(), 156, 20, l);
- value = new gClick(text->x()+text->w()+4, y(), 80, 20);
- button = new gButton(value->x()+value->w()+4, y(), 40, 20, "learn");
+ text = new geBox(x(), y(), 156, 20, l);
+ value = new geButton(text->x()+text->w()+4, y(), 80, 20);
+ button = new geButton(value->x()+value->w()+4, y(), 40, 20, "learn");
end();
text->box(G_CUSTOM_BORDER_BOX);
if (button->value() == 1) {
cbData.window = (gdMidiInput*) parent(); // parent = gdMidiInput
cbData.learner = this;
- G_KernelMidi.startMidiLearn(callback, (void*)&cbData);
+ kernelMidi::startMidiLearn(callback, (void*)&cbData);
}
else
- G_KernelMidi.stopMidiLearn();
+ kernelMidi::stopMidiLearn();
}
*
* Giada - Your Hardcore Loopmachine
*
- * midiLearner
- *
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
* -------------------------------------------------------------------------- */
-#ifndef GE_LEARNER_H
-#define GE_LEARNER_H
+#ifndef GE_MIDI_LEARNER_H
+#define GE_MIDI_LEARNER_H
#include <FL/Fl_Group.H>
#include "../../core/kernelMidi.h"
-extern KernelMidi G_KernelMidi;
+class gdMidiInput;
+class geMidiLearner;
+class geBox;
+class geButton;
class geMidiLearner : public Fl_Group
* uint32_t msg - MIDI message
* void *data - extra data */
- KernelMidi::cb_midiLearn *callback;
+ giada::m::kernelMidi::cb_midiLearn *callback;
- class gBox *text;
- class gClick *value;
- class gButton *button;
+ geBox *text;
+ geButton *value;
+ geButton *button;
static void cb_button(Fl_Widget *v, void *p);
static void cb_value (Fl_Widget *v, void *p);
struct cbData_t
{
- class gdMidiInput *window;
- class geMidiLearner *learner;
+ gdMidiInput *window;
+ geMidiLearner *learner;
} cbData;
/* param
uint32_t *param;
- geMidiLearner(int x, int y, int w, const char *l, KernelMidi::cb_midiLearn *cb,
- uint32_t *param);
+ geMidiLearner(int x, int y, int w, const char *l,
+ giada::m::kernelMidi::cb_midiLearn *cb, uint32_t *param);
void updateValue();
};
+++ /dev/null
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
- *
- * This file is part of Giada - Your Hardcore Loopmachine.
- *
- * Giada - Your Hardcore Loopmachine is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General
- * Public License as published by the Free Software Foundation, either
- * version 3 of the License, or (at your option) any later version.
- *
- * Giada - Your Hardcore Loopmachine is distributed in the hope that it
- * will be useful, but WITHOUT ANY WARRANTY; without even the implied
- * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Giada - Your Hardcore Loopmachine. If not, see
- * <http://www.gnu.org/licenses/>.
- *
- * -------------------------------------------------------------------------- */
-
-
-#include "../../core/recorder.h"
-#include "../../core/mixer.h"
-#include "../../core/channel.h"
-#include "../../glue/main.h"
-#include "../../utils/log.h"
-#include "../dialogs/gd_actionEditor.h"
-#include "../dialogs/gd_mainWindow.h"
-#include "mainWindow/keyboard/keyboard.h"
-#include "muteEditor.h"
-
-
-extern gdMainWindow *G_MainWin;
-extern Mixer G_Mixer;
-extern Recorder G_Recorder;
-
-
-geMuteEditor::geMuteEditor(int x, int y, gdActionEditor *pParent)
- : geBaseActionEditor(x, y, 200, 80, pParent),
- draggedPoint (-1),
- selectedPoint (-1)
-{
- size(pParent->totalWidth, h());
- extractPoints();
-}
-
-
-/* ------------------------------------------------------------------ */
-
-
-void geMuteEditor::draw()
-{
- baseDraw();
-
- /* print label */
-
- fl_color(COLOR_BG_1);
- fl_font(FL_HELVETICA, 12);
- fl_draw("mute", x()+4, y(), w(), h(), (Fl_Align) (FL_ALIGN_LEFT | FL_ALIGN_CENTER));
-
- /* draw "on" and "off" labels. Must stay in background */
-
- fl_color(COLOR_BG_1);
- fl_font(FL_HELVETICA, 9);
- fl_draw("on", x()+4, y(), w(), h(), (Fl_Align) (FL_ALIGN_LEFT | FL_ALIGN_TOP));
- fl_draw("off", x()+4, y()+h()-14, w(), h(), (Fl_Align) (FL_ALIGN_LEFT | FL_ALIGN_TOP));
-
- /* draw on-off points. On = higher rect, off = lower rect. It always
- * starts with a note_off */
-
- fl_color(COLOR_BG_2);
-
- int pxOld = x()+1;
- int pxNew = 0;
- int py = y()+h()-5;
- int pyDot = py-6;
-
- for (unsigned i=0; i<points.size(); i++) {
-
- /* next px */
-
- pxNew = points.at(i).x+x();
-
- /* draw line from pxOld to pxNew.
- * i % 2 == 0: first point, mute_on
- * i % 2 != 0: second point, mute_off */
-
- fl_line(pxOld, py, pxNew, py);
- pxOld = pxNew;
-
- py = i % 2 == 0 ? y()+4 : y()+h()-5;
-
- /* draw dots (handles) */
-
- fl_line(pxNew, y()+h()-5, pxNew, y()+4);
-
- if (selectedPoint == (int) i) {
- fl_color(COLOR_BD_1);
- fl_rectf(pxNew-3, pyDot, 7, 7);
- fl_color(COLOR_BG_2);
- }
- else
- fl_rectf(pxNew-3, pyDot, 7, 7);
- }
-
- /* last section */
-
- py = y()+h()-5;
- fl_line(pxNew+3, py, pParent->coverX+x()-1, py);
-}
-
-
-/* ------------------------------------------------------------------ */
-
-
-void geMuteEditor::extractPoints()
-{
- points.clear();
-
- /* actions are already sorted by G_Recorder.sortActions() */
-
- for (unsigned i=0; i<G_Recorder.frames.size(); i++) {
- for (unsigned j=0; j<G_Recorder.global.at(i).size(); j++) {
- if (G_Recorder.global.at(i).at(j)->chan == pParent->chan->index) {
- if (G_Recorder.global.at(i).at(j)->type & (ACTION_MUTEON | ACTION_MUTEOFF)) {
- point p;
- p.frame = G_Recorder.frames.at(i);
- p.type = G_Recorder.global.at(i).at(j)->type;
- p.x = p.frame / pParent->zoom;
- points.push_back(p);
- //gu_log("[geMuteEditor::extractPoints] point found, type=%d, frame=%d\n", p.type, p.frame);
- }
- }
- }
- }
-}
-
-
-/* ------------------------------------------------------------------ */
-
-
-void geMuteEditor::updateActions() {
- for (unsigned i=0; i<points.size(); i++)
- points.at(i).x = points.at(i).frame / pParent->zoom;
-}
-
-
-/* ------------------------------------------------------------------ */
-
-
-int geMuteEditor::handle(int e) {
-
- int ret = 0;
- int mouseX = Fl::event_x()-x();
-
- switch (e) {
-
- case FL_ENTER: {
- ret = 1;
- break;
- }
-
- case FL_MOVE: {
- selectedPoint = getSelectedPoint();
- redraw();
- ret = 1;
- break;
- }
-
- case FL_LEAVE: {
- draggedPoint = -1;
- selectedPoint = -1;
- redraw();
- ret = 1;
- break;
- }
-
- case FL_PUSH: {
-
- /* left click on point: drag
- * right click on point: delete
- * left click on void: add */
-
- if (Fl::event_button1()) {
-
- if (selectedPoint != -1) {
- draggedPoint = selectedPoint;
- previousXPoint = points.at(selectedPoint).x;
- }
- else {
-
- /* click on the grey area leads to nowhere */
-
- if (mouseX > pParent->coverX) {
- ret = 1;
- break;
- }
-
- /* click in the middle of a long mute_on (between two points): new actions
- * must be added in reverse: first mute_off then mute_on. Let's find the
- * next point from here. */
-
- unsigned nextPoint = points.size();
- for (unsigned i=0; i<points.size(); i++) {
- if (mouseX < points.at(i).x) {
- nextPoint = i;
- break;
- }
- }
-
- /* next point odd = mute_on [click here] mute_off
- * next point even = mute_off [click here] mute_on */
-
- int frame_a = mouseX * pParent->zoom;
- int frame_b = frame_a+2048;
-
- if (pParent->gridTool->isOn()) {
- frame_a = pParent->gridTool->getSnapFrame(mouseX);
- frame_b = pParent->gridTool->getSnapFrame(mouseX + pParent->gridTool->getCellSize());
-
- /* with snap=on a point can fall onto another */
-
- if (pointCollides(frame_a) || pointCollides(frame_b)) {
- ret = 1;
- break;
- }
- }
-
- /* ensure frame parity */
-
- if (frame_a % 2 != 0) frame_a++;
- if (frame_b % 2 != 0) frame_b++;
-
- /* avoid overflow: frame_b must be within the sequencer range. In that
- * case shift the ON-OFF block */
-
- if (frame_b >= G_Mixer.totalFrames) {
- frame_b = G_Mixer.totalFrames;
- frame_a = frame_b-2048;
- }
-
- if (nextPoint % 2 != 0) {
- G_Recorder.rec(pParent->chan->index, ACTION_MUTEOFF, frame_a);
- G_Recorder.rec(pParent->chan->index, ACTION_MUTEON, frame_b);
- }
- else {
- G_Recorder.rec(pParent->chan->index, ACTION_MUTEON, frame_a);
- G_Recorder.rec(pParent->chan->index, ACTION_MUTEOFF, frame_b);
- }
- pParent->chan->hasActions = true;
- G_Recorder.sortActions();
-
- G_MainWin->keyboard->setChannelWithActions((geSampleChannel*)pParent->chan->guiChannel); // update mainWindow
- extractPoints();
- redraw();
- }
- }
- else {
-
- /* delete points pair */
-
- if (selectedPoint != -1) {
-
- unsigned a;
- unsigned b;
-
- if (points.at(selectedPoint).type == ACTION_MUTEOFF) {
- a = selectedPoint-1;
- b = selectedPoint;
- }
- else {
- a = selectedPoint;
- b = selectedPoint+1;
- }
-
- //gu_log("selected: a=%d, b=%d >>> frame_a=%d, frame_b=%d\n",
- // a, b, points.at(a).frame, points.at(b).frame);
-
- G_Recorder.deleteAction(pParent->chan->index, points.at(a).frame,
- points.at(a).type, false, &G_Mixer.mutex_recs); // false = don't check vals
- G_Recorder.deleteAction(pParent->chan->index, points.at(b).frame,
- points.at(b).type, false, &G_Mixer.mutex_recs); // false = don't check vals
- pParent->chan->hasActions = G_Recorder.hasActions(pParent->chan->index);
-
- G_Recorder.sortActions();
-
- G_MainWin->keyboard->setChannelWithActions((geSampleChannel*)pParent->chan->guiChannel); // update mainWindow
- extractPoints();
- redraw();
- }
- }
- ret = 1;
- break;
- }
-
- case FL_RELEASE: {
-
- if (draggedPoint != -1) {
-
- if (points.at(draggedPoint).x == previousXPoint) {
- //gu_log("nothing to do\n");
- }
- else {
-
- int newFrame = points.at(draggedPoint).x * pParent->zoom;
-
- G_Recorder.deleteAction(pParent->chan->index,
- points.at(draggedPoint).frame, points.at(draggedPoint).type, false,
- &G_Mixer.mutex_recs); // don't check values
- pParent->chan->hasActions = G_Recorder.hasActions(pParent->chan->index);
-
- G_Recorder.rec(
- pParent->chan->index,
- points.at(draggedPoint).type,
- newFrame);
-
- pParent->chan->hasActions = true;
- G_Recorder.sortActions();
-
- points.at(draggedPoint).frame = newFrame;
- }
- }
- draggedPoint = -1;
- selectedPoint = -1;
-
- ret = 1;
- break;
- }
-
- case FL_DRAG: {
-
- if (draggedPoint != -1) {
-
- /* constrain the point between two ends (leftBorder-point,
- * point-point, point-rightBorder) */
-
- int prevPoint;
- int nextPoint;
-
- if (draggedPoint == 0) {
- prevPoint = 0;
- nextPoint = points.at(draggedPoint+1).x - 1;
- if (pParent->gridTool->isOn())
- nextPoint -= pParent->gridTool->getCellSize();
- }
- else
- if ((unsigned) draggedPoint == points.size()-1) {
- prevPoint = points.at(draggedPoint-1).x + 1;
- nextPoint = pParent->coverX-x();
- if (pParent->gridTool->isOn())
- prevPoint += pParent->gridTool->getCellSize();
- }
- else {
- prevPoint = points.at(draggedPoint-1).x + 1;
- nextPoint = points.at(draggedPoint+1).x - 1;
- if (pParent->gridTool->isOn()) {
- prevPoint += pParent->gridTool->getCellSize();
- nextPoint -= pParent->gridTool->getCellSize();
- }
- }
-
- if (mouseX <= prevPoint)
- points.at(draggedPoint).x = prevPoint;
- else
- if (mouseX >= nextPoint)
- points.at(draggedPoint).x = nextPoint;
- else
- if (pParent->gridTool->isOn())
- points.at(draggedPoint).x = pParent->gridTool->getSnapPoint(mouseX)-1;
- else
- points.at(draggedPoint).x = mouseX;
-
- redraw();
- }
- ret = 1;
- break;
- }
- }
-
-
- return ret;
-}
-
-
-/* ------------------------------------------------------------------ */
-
-
-bool geMuteEditor::pointCollides(int frame) {
- for (unsigned i=0; i<points.size(); i++)
- if (frame == points.at(i).frame)
- return true;
- return false;
-}
-
-
-/* ------------------------------------------------------------------ */
-
-
-int geMuteEditor::getSelectedPoint() {
-
- /* point is a 7x7 dot */
-
- for (unsigned i=0; i<points.size(); i++) {
- if (Fl::event_x() >= points.at(i).x+x()-3 &&
- Fl::event_x() <= points.at(i).x+x()+3)
- return i;
- }
- return -1;
-}
+++ /dev/null
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
- *
- * This file is part of Giada - Your Hardcore Loopmachine.
- *
- * Giada - Your Hardcore Loopmachine is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General
- * Public License as published by the Free Software Foundation, either
- * version 3 of the License, or (at your option) any later version.
- *
- * Giada - Your Hardcore Loopmachine is distributed in the hope that it
- * will be useful, but WITHOUT ANY WARRANTY; without even the implied
- * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Giada - Your Hardcore Loopmachine. If not, see
- * <http://www.gnu.org/licenses/>.
- *
- * -------------------------------------------------------------------------- */
-
-
-#ifndef GE_MUTECHANNEL_H
-#define GE_MUTECHANNEL_H
-
-
-#include <vector>
-#include <FL/Fl.H>
-#include <FL/Fl_Widget.H>
-#include <FL/fl_draw.H>
-#include "../../utils/fs.h"
-#include "baseActionEditor.h"
-
-
-using std::vector;
-
-
-class geMuteEditor : public geBaseActionEditor
-{
-private:
-
- /* point
- * a single dot in the graph. */
-
- struct point
- {
- int frame;
- char type;
- int x;
- };
-
- /* points
- * array of on/off points, in frames */
-
- vector<point> points;
-
- /* draggedPoint
- * which point we are dragging? */
-
- int draggedPoint;
-
- /* selectedPoint
- * which point we are selecting? */
-
- int selectedPoint;
-
- /* previousXPoint
- * x coordinate of point at time t-1. Used to check effective shifts */
-
- int previousXPoint;
-
- /* extractPoints
- * va a leggere l'array di azioni di Recorder ed estrae tutti i punti
- * interessanti mute_on o mute_off. Li mette poi nel vector points. */
- void extractPoints();
-
- /* getSelectedPoint
- * ritorna l'indice di points[] in base al punto selezionato (quello
- * con il mouse hover). Ritorna -1 se non trova niente. */
- int getSelectedPoint();
-
- /* pointCollides
- * true if a point collides with another. Used while adding new points
- * with snap active.*/
-
- bool pointCollides(int frame);
-
-public:
-
- geMuteEditor(int x, int y, class gdActionEditor *pParent);
- void draw();
- int handle(int e);
-
- /* updateActions
- * calculates new points affected by the zoom. Call this one after
- * each zoom update. */
-
- void updateActions();
-};
-
-#endif
+++ /dev/null
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
- *
- * This file is part of Giada - Your Hardcore Loopmachine.
- *
- * Giada - Your Hardcore Loopmachine is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General
- * Public License as published by the Free Software Foundation, either
- * version 3 of the License, or (at your option) any later version.
- *
- * Giada - Your Hardcore Loopmachine is distributed in the hope that it
- * will be useful, but WITHOUT ANY WARRANTY; without even the implied
- * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Giada - Your Hardcore Loopmachine. If not, see
- * <http://www.gnu.org/licenses/>.
- *
- * -------------------------------------------------------------------------- */
-
-
-#include <FL/fl_draw.H>
-#include "../../core/const.h"
-#include "../../core/conf.h"
-#include "../../utils/log.h"
-#include "../dialogs/gd_actionEditor.h"
-#include "pianoItem.h"
-#include "pianoRoll.h"
-#include "noteEditor.h"
-
-
-extern Conf G_Conf;
-
-
-geNoteEditor::geNoteEditor(int x, int y, gdActionEditor *pParent)
- : Fl_Scroll(x, y, 200, 422),
- pParent (pParent)
-{
- size(pParent->totalWidth, G_Conf.pianoRollH);
- pianoRoll = new gePianoRoll(x, y, pParent->totalWidth, pParent);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-geNoteEditor::~geNoteEditor()
-{
- clear();
- G_Conf.pianoRollH = h();
- G_Conf.pianoRollY = pianoRoll->y();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void geNoteEditor::updateActions()
-{
- pianoRoll->updateActions();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void geNoteEditor::draw()
-{
- pianoRoll->size(this->w(), pianoRoll->h()); /// <--- not optimal
-
- /* clear background */
-
- fl_rectf(x(), y(), w(), h(), COLOR_BG_MAIN);
-
- /* clip pianoRoll to pianoRollContainer size */
-
- fl_push_clip(x(), y(), w(), h());
- draw_child(*pianoRoll);
- fl_pop_clip();
-
- fl_color(COLOR_BD_0);
- fl_line_style(0);
- fl_rect(x(), y(), pParent->totalWidth, h());
-}
+++ /dev/null
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
- *
- * This file is part of Giada - Your Hardcore Loopmachine.
- *
- * Giada - Your Hardcore Loopmachine is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General
- * Public License as published by the Free Software Foundation, either
- * version 3 of the License, or (at your option) any later version.
- *
- * Giada - Your Hardcore Loopmachine is distributed in the hope that it
- * will be useful, but WITHOUT ANY WARRANTY; without even the implied
- * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Giada - Your Hardcore Loopmachine. If not, see
- * <http://www.gnu.org/licenses/>.
- *
- * -------------------------------------------------------------------------- */
-
-
-#ifndef GE_NOTE_EDITOR_H
-#define GE_NOTE_EDITOR_H
-
-#include <FL/Fl.H>
-#include <FL/fl_draw.H>
-#include <FL/Fl_Scroll.H>
-#include <FL/Fl_Box.H>
-#include "../../core/recorder.h"
-
-
-class geNoteEditor : public Fl_Scroll
-{
-private:
-
- class gdActionEditor *pParent;
- class gePianoRoll *pianoRoll;
-
-public:
-
- geNoteEditor(int x, int y, class gdActionEditor *parent);
- ~geNoteEditor();
- void draw();
- void updateActions();
-};
-
-
-#endif
+++ /dev/null
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
- *
- * This file is part of Giada - Your Hardcore Loopmachine.
- *
- * Giada - Your Hardcore Loopmachine is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General
- * Public License as published by the Free Software Foundation, either
- * version 3 of the License, or (at your option) any later version.
- *
- * Giada - Your Hardcore Loopmachine is distributed in the hope that it
- * will be useful, but WITHOUT ANY WARRANTY; without even the implied
- * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Giada - Your Hardcore Loopmachine. If not, see
- * <http://www.gnu.org/licenses/>.
- *
- * -------------------------------------------------------------------------- */
-
-
-#include "../../core/kernelMidi.h"
-#include "../../core/mixer.h"
-#include "../../core/channel.h"
-#include "../../core/midiChannel.h"
-#include "../dialogs/gd_actionEditor.h"
-#include "noteEditor.h"
-#include "pianoRoll.h"
-#include "pianoItem.h"
-
-
-extern KernelMidi G_KernelMidi;
-extern Mixer G_Mixer;
-extern Recorder G_Recorder;
-
-
-gePianoItem::gePianoItem(int X, int Y, int rel_x, int rel_y, Recorder::action *_a,
- Recorder::action *_b, gdActionEditor *pParent)
- : Fl_Box (X, Y, MIN_WIDTH, gePianoRoll::CELL_H),
- a (_a),
- b (_b),
- pParent (pParent),
- selected(false),
- event_a (0x00),
- event_b (0x00),
- changed (false)
-{
- /* a is a pointer: action exists, needs to be displayed */
-
- if (a) {
- note = G_KernelMidi.getB2(a->iValue);
- frame_a = a->frame;
- frame_b = b->frame;
- event_a = a->iValue;
- event_b = b->iValue;
- int newX = rel_x + (frame_a / pParent->zoom);
- int newY = rel_y + getY(note);
- int newW = (frame_b - frame_a) / pParent->zoom;
- resize(newX, newY, newW, h());
- }
-
- /* a is null: action needs to be recorded from scratch */
-
- else {
- note = getNote(rel_y);
- frame_a = rel_x * pParent->zoom;
- frame_b = (rel_x + 20) * pParent->zoom;
- record();
- size((frame_b - frame_a) / pParent->zoom, h());
- }
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-bool gePianoItem::overlap()
-{
- /* when 2 segments overlap?
- * start = the highest value between the two starting points
- * end = the lowest value between the two ending points
- * if start < end then there's an overlap of end-start pixels. */
-
- geNoteEditor *pPiano = (geNoteEditor*) parent();
-
- for (int i=0; i<pPiano->children(); i++) {
-
- gePianoItem *pItem = (gePianoItem*) pPiano->child(i);
-
- /* don't check against itself and with different y positions */
-
- if (pItem == this || pItem->y() != y())
- continue;
-
- int start = pItem->x() >= x() ? pItem->x() : x();
- int end = pItem->x()+pItem->w() < x()+w() ? pItem->x()+pItem->w() : x()+w();
- if (start < end)
- return true;
- }
-
- return false;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gePianoItem::draw()
-{
- int _w = w() > MIN_WIDTH ? w() : MIN_WIDTH;
- fl_rectf(x(), y()+2, _w, h()-3, (Fl_Color) selected ? COLOR_BD_1 : COLOR_BG_2);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gePianoItem::record()
-{
- /* avoid frame overflow */
-
- int overflow = frame_b - G_Mixer.totalFrames;
- if (overflow > 0) {
- frame_b -= overflow;
- frame_a -= overflow;
- }
-
- event_a |= (MIDI_NOTE_ON);
- event_a |= (note << 16); // note value
- event_a |= (MIDI_VELOCITY);
- event_a |= (0x00);
-
- event_b |= (MIDI_NOTE_OFF);
- event_b |= (note << 16); // note value
- event_b |= (MIDI_VELOCITY);
- event_b |= (0x00);
-
- G_Recorder.rec(pParent->chan->index, ACTION_MIDI, frame_a, event_a);
- G_Recorder.rec(pParent->chan->index, ACTION_MIDI, frame_b, event_b);
- pParent->chan->hasActions = true;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gePianoItem::remove()
-{
- G_Recorder.deleteAction(pParent->chan->index, frame_a, ACTION_MIDI, true,
- &G_Mixer.mutex_recs, event_a, 0.0);
- G_Recorder.deleteAction(pParent->chan->index, frame_b, ACTION_MIDI, true,
- &G_Mixer.mutex_recs, event_b, 0.0);
-
- /* send a note-off in case we are deleting it in a middle of a key_on
- * key_off sequence. */
-
- ((MidiChannel*) pParent->chan)->sendMidi(event_b);
-
- ((gePianoRoll*) parent())->cursorOnItem = false;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-int gePianoItem::handle(int e)
-{
- int ret = 0;
-
- switch (e) {
-
- case FL_ENTER: {
- ((gePianoRoll*) parent())->cursorOnItem = true;
- selected = true;
- ret = 1;
- redraw();
- break;
- }
-
- case FL_LEAVE: {
- fl_cursor(FL_CURSOR_DEFAULT, FL_WHITE, FL_BLACK);
- ((gePianoRoll*) parent())->cursorOnItem = false;
- selected = false;
- ret = 1;
- redraw();
- break;
- }
-
- case FL_MOVE: {
- onLeftEdge = false;
- onRightEdge = false;
-
- if (Fl::event_x() >= x() && Fl::event_x() < x()+HANDLE_WIDTH) {
- onLeftEdge = true;
- fl_cursor(FL_CURSOR_WE, FL_WHITE, FL_BLACK);
- }
- else
- if (Fl::event_x() >= x()+w()-HANDLE_WIDTH && Fl::event_x() <= x()+w()) {
- onRightEdge = true;
- fl_cursor(FL_CURSOR_WE, FL_WHITE, FL_BLACK);
- }
- else
- fl_cursor(FL_CURSOR_DEFAULT, FL_WHITE, FL_BLACK);
-
- ret = 1;
- break;
- }
-
- case FL_PUSH: {
-
- push_x = Fl::event_x() - x();
- old_x = x();
- old_w = w();
-
- if (Fl::event_button3()) {
- fl_cursor(FL_CURSOR_DEFAULT, FL_WHITE, FL_BLACK);
- remove();
- hide(); // for Windows
- Fl::delete_widget(this);
- ((geNoteEditor*)parent())->redraw();
- }
- ret = 1;
- break;
- }
-
- case FL_DRAG: {
-
- changed = true;
-
- geNoteEditor *pr = (geNoteEditor*) parent();
- int coverX = pParent->coverX + pr->x(); // relative coverX
- int nx, ny, nw;
-
- if (onLeftEdge) {
- nx = Fl::event_x();
- ny = y();
- nw = x()-Fl::event_x()+w();
- if (nx < pr->x()) {
- nx = pr->x();
- nw = w()+x()-pr->x();
- }
- else
- if (nx > x()+w()-MIN_WIDTH) {
- nx = x()+w()-MIN_WIDTH;
- nw = MIN_WIDTH;
- }
- resize(nx, ny, nw, h());
- }
- else
- if (onRightEdge) {
- nw = Fl::event_x()-x();
- if (Fl::event_x() < x()+MIN_WIDTH)
- nw = MIN_WIDTH;
- else
- if (Fl::event_x() > coverX)
- nw = coverX-x();
- size(nw, h());
- }
- else {
- nx = Fl::event_x() - push_x;
- if (nx < pr->x()+1)
- nx = pr->x()+1;
- else
- if (nx+w() > coverX)
- nx = coverX-w();
-
- /* snapping */
-
- if (pParent->gridTool->isOn())
- nx = pParent->gridTool->getSnapPoint(nx-pr->x()) + pr->x() - 1;
-
- position(nx, y());
- }
-
- /* update screen */
-
- redraw();
- ((geNoteEditor*)parent())->redraw();
- ret = 1;
- break;
- }
-
- case FL_RELEASE: {
-
- /* delete & record the action, only if it doesn't overlap with
- * another one */
-
- if (overlap()) {
- resize(old_x, y(), old_w, h());
- redraw();
- }
- else
- if (changed) {
- remove();
- note = getNote(getRelY());
- frame_a = getRelX() * pParent->zoom;
- frame_b = (getRelX()+w()) * pParent->zoom;
- record();
- changed = false;
- }
-
- ((geNoteEditor*)parent())->redraw();
-
- ret = 1;
- break;
- }
- }
- return ret;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-int gePianoItem::getNote(int rel_y)
-{
- return gePianoRoll::MAX_KEYS - (rel_y / gePianoRoll::CELL_H);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-int gePianoItem::getRelY()
-{
- return y() - parent()->y();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-int gePianoItem::getRelX()
-{
- return x() - parent()->x();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-int gePianoItem::getY(int note)
-{
- return (gePianoRoll::MAX_KEYS * gePianoRoll::CELL_H) - (note * gePianoRoll::CELL_H);
-}
+++ /dev/null
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
- *
- * This file is part of Giada - Your Hardcore Loopmachine.
- *
- * Giada - Your Hardcore Loopmachine is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General
- * Public License as published by the Free Software Foundation, either
- * version 3 of the License, or (at your option) any later version.
- *
- * Giada - Your Hardcore Loopmachine is distributed in the hope that it
- * will be useful, but WITHOUT ANY WARRANTY; without even the implied
- * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Giada - Your Hardcore Loopmachine. If not, see
- * <http://www.gnu.org/licenses/>.
- *
- * -------------------------------------------------------------------------- */
-
-
-#ifndef GE_PIANO_ITEM_H
-#define GE_PIANO_ITEM_H
-
-
-#include <FL/Fl_Box.H>
-#include "../../core/recorder.h"
-
-
-class gePianoItem : public Fl_Box
-{
-private:
-
- /* getRelX/Y
- * return x/y point of this item, relative to piano roll (and not to
- * entire screen) */
-
- int getRelY();
- int getRelX();
-
- /* getNote
- * from a relative_y return the real MIDI note, range 0-127. 15 is
- * the hardcoded value for note height in pixels */
-
- int getNote(int rel_y);
-
- /* getY
- * from a note, return the y position on piano roll */
-
- int getY(int note);
-
- /* overlap
- * check if this item don't overlap with another one. */
-
- bool overlap();
-
- struct Recorder::action *a;
- struct Recorder::action *b;
- class gdActionEditor *pParent;
-
- bool selected;
- int push_x;
-
- /* MIDI note, start frame, end frame - Used only if it's a newly added
- * action */ /** FIXME - is it true? */
-
- int note;
- int frame_a;
- int frame_b;
-
- /* event - bitmasked MIDI events, generated by record() or by ctor if
- * not newly added action */
-
- int event_a;
- int event_b;
-
- /* changed - if Item has been moved or resized: re-recording needed */
-
- bool changed;
-
- /* onLeft,RightEdge - if cursor is on a widget's edge */
-
- bool onLeftEdge;
- bool onRightEdge;
-
- /* old_x, old_w - store previous width and position while dragging
- * and moving, in order to restore it if overlap */
-
- int old_x, old_w;
-
-public:
-
- static const int MIN_WIDTH = 10;
- static const int HANDLE_WIDTH = 5;
-
- /* pianoItem ctor
- * if action *a == NULL, record a new action */
-
- gePianoItem(int x, int y, int rel_x, int rel_y, struct Recorder::action *a,
- struct Recorder::action *b, class gdActionEditor *pParent);
-
- void draw();
- int handle(int e);
- void record();
- void remove();
-
- int getFrame_a() { return frame_a; }
- int getFrame_b() { return frame_b; }
- int getNote() { return note; }
-};
-
-
-#endif
+++ /dev/null
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
- *
- * This file is part of Giada - Your Hardcore Loopmachine.
- *
- * Giada - Your Hardcore Loopmachine is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General
- * Public License as published by the Free Software Foundation, either
- * version 3 of the License, or (at your option) any later version.
- *
- * Giada - Your Hardcore Loopmachine is distributed in the hope that it
- * will be useful, but WITHOUT ANY WARRANTY; without even the implied
- * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Giada - Your Hardcore Loopmachine. If not, see
- * <http://www.gnu.org/licenses/>.
- *
- * -------------------------------------------------------------------------- */
-
-
-#include "../../core/conf.h"
-#include "../../core/mixer.h"
-#include "../../core/channel.h"
-#include "../../core/recorder.h"
-#include "../../core/kernelMidi.h"
-#include "../../utils/log.h"
-#include "../dialogs/gd_actionEditor.h"
-#include "pianoItem.h"
-#include "noteEditor.h"
-#include "pianoRoll.h"
-
-
-extern Conf G_Conf;
-extern Recorder G_Recorder;
-extern Mixer G_Mixer;
-extern KernelMidi G_KernelMidi;
-
-
-gePianoRoll::gePianoRoll(int X, int Y, int W, gdActionEditor *pParent)
- : geBaseActionEditor(X, Y, W, 40, pParent),
- cursorOnItem (false)
-{
- resizable(nullptr); // don't resize children (i.e. pianoItem)
- size(W, (MAX_KEYS+1) * CELL_H); // 128 MIDI channels * CELL_H height
-
- if (G_Conf.pianoRollY == -1)
- position(x(), y()-(h()/2)); // center
- else
- position(x(), G_Conf.pianoRollY);
-
- drawSurface1();
- drawSurface2();
-
- /* add actions when the window is opened. Position is zoom-based. MIDI
- * actions come always in pair: start + end. */
-
- G_Recorder.sortActions();
-
- Recorder::action *a2 = nullptr;
- Recorder::action *prev = nullptr;
-
- for (unsigned i=0; i<G_Recorder.frames.size(); i++) {
- for (unsigned j=0; j<G_Recorder.global.at(i).size(); j++) {
-
- /* don't show actions > than the grey area */
- /** FIXME - can we move this to the outer cycle? */
-
- if (G_Recorder.frames.at(i) > G_Mixer.totalFrames)
- continue;
-
- Recorder::action *a1 = G_Recorder.global.at(i).at(j);
-
- /* Skip action if:
- - does not belong to this channel
- - is not a MIDI action (we only want MIDI things here)
- - is the previous one (we have already checked it)
- - (later on) if it's NOTE_OFF (0x80): we want note on only */
-
- if (a1->chan != pParent->chan->index)
- continue;
- if (a1->type != ACTION_MIDI)
- continue;
- if (a1 == prev)
- continue;
-
- /* extract MIDI infos from a1: if is note off skip it, we are looking
- * for note on only */
-
- int a1_type = G_KernelMidi.getB1(a1->iValue);
- int a1_note = G_KernelMidi.getB2(a1->iValue);
-
- if (a1_type == 0x80) // NOTE_OFF
- continue;
-
- /* Search for the next action. Must have: same channel, ACTION_MIDI,
- greater than a1->frame and with MIDI properties of note_off (0x80), same
- note of a1, any velocity value (0xFF) because we just don't care about the
- velocity of a note_off. */
-
- G_Recorder.getNextAction(a1->chan, ACTION_MIDI, a1->frame, &a2,
- G_KernelMidi.getIValue(0x80, a1_note, 0xFF));
-
- /* next action note_off found: add a new gePianoItem to piano roll */
-
- if (a2) {
- new gePianoItem(0, 0, x(), y(), a1, a2, pParent);
- prev = a2;
- a2 = nullptr;
- }
- else
- gu_log("[geNoteEditor] recorder didn't find requested action!\n");
- // TODO - create new gOrphanedPianoItem
- }
- }
-
- end();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gePianoRoll::drawSurface1()
-{
- surface1 = fl_create_offscreen(CELL_W, h());
- fl_begin_offscreen(surface1);
-
- /* warning: only w() and h() come from this widget, x and y coordinates
- * are absolute, since we are writing in a memory chunk */
-
- fl_rectf(0, 0, CELL_W, h(), COLOR_BG_MAIN);
-
- fl_line_style(FL_DASH, 0, nullptr);
- fl_font(FL_HELVETICA, GUI_FONT_SIZE_BASE);
-
- int octave = MAX_OCTAVES;
-
- for (int i=1; i<=MAX_KEYS+1; i++) {
-
- /* print key note label. C C# D D# E F F# G G# A A# B */
-
- char note[6];
- switch (i % KEYS) {
- case (int) Notes::G:
- fl_rectf(0, i*CELL_H, CELL_W, CELL_H, COLOR_BG_RICH);
- sprintf(note, "%dG", octave);
- break;
- case (int) Notes::FS:
- sprintf(note, "%dF#", octave);
- break;
- case (int) Notes::F:
- sprintf(note, "%dF", octave);
- break;
- case (int) Notes::E:
- fl_rectf(0, i*CELL_H, CELL_W, CELL_H, COLOR_BG_RICH);
- sprintf(note, "%dE", octave);
- break;
- case (int) Notes::DS:
- sprintf(note, "%dD#", octave);
- break;
- case (int) Notes::D:
- fl_rectf(0, i*CELL_H, CELL_W, CELL_H, COLOR_BG_RICH);
- sprintf(note, "%dD", octave);
- break;
- case (int) Notes::CS:
- sprintf(note, "%dC#", octave);
- break;
- case (int) Notes::C:
- sprintf(note, "%dC", octave);
- break;
- case (int) Notes::B:
- fl_rectf(0, i*CELL_H, CELL_W, CELL_H, COLOR_BG_RICH);
- sprintf(note, "%dB", octave);
- break;
- case (int) Notes::AS:
- sprintf(note, "%dA#", octave);
- break;
- case (int) Notes::A:
- fl_rectf(0, i*CELL_H, CELL_W, CELL_H, COLOR_BG_RICH);
- sprintf(note, "%dA", octave);
- break;
- case (int) Notes::GS:
- sprintf(note, "%dG#", octave);
- octave--;
- break;
- }
-
- /* Print note name */
-
- fl_color(COLOR_BG_LINE);
- fl_draw(note, 4, ((i-1)*CELL_H)+1, CELL_W, CELL_H,
- (Fl_Align) (FL_ALIGN_LEFT | FL_ALIGN_CENTER));
-
- /* Print horizontal line */
-
- if (i < MAX_KEYS+1)
- fl_line(0, i*CELL_H, CELL_W, +i*CELL_H);
- }
-
- fl_line_style(0);
- fl_end_offscreen();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gePianoRoll::drawSurface2()
-{
- surface2 = fl_create_offscreen(CELL_W, h());
- fl_begin_offscreen(surface2);
- fl_rectf(0, 0, CELL_W, h(), COLOR_BG_MAIN);
- fl_color(COLOR_BG_LINE);
- fl_line_style(FL_DASH, 0, nullptr);
- for (int i=1; i<=MAX_KEYS+1; i++) {
- switch (i % KEYS) {
- case (int) Notes::G:
- case (int) Notes::E:
- case (int) Notes::D:
- case (int) Notes::B:
- case (int) Notes::A:
- fl_rectf(0, i*CELL_H, CELL_W, CELL_H, COLOR_BG_RICH);
- break;
- }
- if (i < MAX_KEYS+1) {
- fl_color(COLOR_BG_LINE);
- fl_line(0, i*CELL_H, CELL_W, i*CELL_H);
- }
- }
- fl_line_style(0);
- fl_end_offscreen();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gePianoRoll::draw()
-{
- fl_copy_offscreen(x(), y(), CELL_W, h(), surface1, 0, 0);
-
-#if defined(__APPLE__)
- for (int i=36; i<pParent->totalWidth; i+=36) /// TODO: i < pParent->coverX is faster
- fl_copy_offscreen(x()+i, y(), CELL_W, h(), surface2, 1, 0);
-#else
- for (int i=CELL_W; i<pParent->totalWidth; i+=CELL_W) /// TODO: i < pParent->coverX is faster
- fl_copy_offscreen(x()+i, y(), CELL_W, h(), surface2, 0, 0);
-#endif
-
- baseDraw(false);
- draw_children();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-int gePianoRoll::handle(int e)
-{
- int ret = Fl_Group::handle(e);
-
- switch (e) {
- case FL_PUSH: {
-
- /* avoid click on grey area */
-
- if (Fl::event_x() >= pParent->coverX) {
- ret = 1;
- break;
- }
-
-
- push_y = Fl::event_y() - y();
-
- if (Fl::event_button1()) {
-
- /* ax is driven by grid, ay by the height in px of each note */
-
- int ax = Fl::event_x();
- int ay = Fl::event_y();
-
- /* vertical snap */
-
- int edge = (ay-y()) % CELL_H;
- if (edge != 0) ay -= edge;
-
- /* if no overlap, add new piano item. Also check that it doesn't
- * overflow on the grey area, by shifting it to the left if
- * necessary. */
-
- if (!cursorOnItem) {
- int greyover = ax+20 - pParent->coverX-x();
- if (greyover > 0)
- ax -= greyover;
- add(new gePianoItem(ax, ay, ax-x(), ay-y(), nullptr, nullptr, pParent));
- redraw();
- }
- }
- ret = 1;
- break;
- }
- case FL_DRAG: {
-
- if (Fl::event_button3()) {
-
- geNoteEditor *prc = (geNoteEditor*) parent();
- position(x(), Fl::event_y() - push_y);
-
- if (y() > prc->y())
- position(x(), prc->y());
- else
- if (y() < prc->y()+prc->h()-h())
- position(x(), prc->y()+prc->h()-h());
-
- prc->redraw();
- }
- ret = 1;
- break;
- }
- case FL_MOUSEWHEEL: { // nothing to do, just avoid small internal scroll
- ret = 1;
- break;
- }
- }
- return ret;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gePianoRoll::updateActions()
-{
- /* when zooming, don't delete and re-add actions, just MOVE them. This
- * function shifts the action by a zoom factor. Those singlepress are
- * stretched, as well */
-
- gePianoItem *i;
- for (int k=0; k<children(); k++) {
- i = (gePianoItem*) child(k);
-
- //gu_log("found point %p, frame_a=%d frame_b=%d, x()=%d\n", (void*) i, i->getFrame_a(), i->getFrame_b(), i->x());
-
- int newX = x() + (i->getFrame_a() / pParent->zoom);
- int newW = ((i->getFrame_b() - i->getFrame_a()) / pParent->zoom);
- if (newW < 8)
- newW = 8;
- i->resize(newX, i->y(), newW, i->h());
- i->redraw();
-
- //gu_log("update point %p, frame_a=%d frame_b=%d, x()=%d\n", (void*) i, i->getFrame_a(), i->getFrame_b(), i->x());
- }
-}
+++ /dev/null
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
- *
- * This file is part of Giada - Your Hardcore Loopmachine.
- *
- * Giada - Your Hardcore Loopmachine is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General
- * Public License as published by the Free Software Foundation, either
- * version 3 of the License, or (at your option) any later version.
- *
- * Giada - Your Hardcore Loopmachine is distributed in the hope that it
- * will be useful, but WITHOUT ANY WARRANTY; without even the implied
- * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Giada - Your Hardcore Loopmachine. If not, see
- * <http://www.gnu.org/licenses/>.
- *
- * -------------------------------------------------------------------------- */
-
-
-#ifndef GE_PIANO_ROLL_H
-#define GE_PIANO_ROLL_H
-
-
-#include <FL/fl_draw.H>
-#include "baseActionEditor.h"
-
-
-class gePianoRoll : public geBaseActionEditor
-{
-private:
-
- enum class Notes
- {
- G = 1, FS = 2, F = 3, E = 4, DS = 5, D = 6, CS = 7, C = 8, B = 9, AS = 10,
- A = 11, GS = 0
- };
-
- /* drawSurface*
- Generates a complex drawing in memory first and copy it to the screen at a
- later point in time. Fl_Offscreen surface holds the necessary data. The first
- call creates an offscreen surface of CELL_W pixel wide containing note values.
- The second call creates another offscreen surface of CELL_W pixels wide
- containing the rest of the piano roll. The latter will then be tiled during
- the ::draw() call. */
-
- void drawSurface1();
- void drawSurface2();
-
- int push_y;
-
- Fl_Offscreen surface1; // notes, no repeat
- Fl_Offscreen surface2; // lines, x-repeat
-
-public:
-
- static const int MAX_KEYS = 127;
- static const int MAX_OCTAVES = 9;
- static const int KEYS = 12;
- static const int CELL_H = 18;
- static const int CELL_W = 40;
-
- gePianoRoll(int x, int y, int w, class gdActionEditor *pParent);
-
- void draw();
- int handle(int e);
- void updateActions();
-
- /* cursorOnItem
- Defines wheter the cursor is over a piano item. This value is updated by each
- gePianoItem sub-widget. */
-
- bool cursorOnItem;
-};
-
-
-#endif
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * 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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#ifdef WITH_VST
+
+
+#include <FL/fl_draw.H>
+#include "../../core/plugin.h"
+#include "../../core/const.h"
+#include "../../core/pluginHost.h"
+#include "basics/boxtypes.h"
+#include "pluginBrowser.h"
+
+
+using std::vector;
+using std::string;
+using namespace giada::m;
+
+
+gePluginBrowser::gePluginBrowser(int x, int y, int w, int h)
+ : Fl_Browser(x, y, w, h)
+{
+ box(G_CUSTOM_BORDER_BOX);
+ textsize(GUI_FONT_SIZE_BASE);
+ textcolor(COLOR_TEXT_0);
+ selection_color(COLOR_BG_1);
+ color(COLOR_BG_0);
+
+ this->scrollbar.color(COLOR_BG_0);
+ this->scrollbar.selection_color(COLOR_BG_1);
+ this->scrollbar.labelcolor(COLOR_BD_1);
+ this->scrollbar.slider(G_CUSTOM_BORDER_BOX);
+
+ this->hscrollbar.color(COLOR_BG_0);
+ this->hscrollbar.selection_color(COLOR_BG_1);
+ this->hscrollbar.labelcolor(COLOR_BD_1);
+ this->hscrollbar.slider(G_CUSTOM_BORDER_BOX);
+
+ type(FL_HOLD_BROWSER);
+
+ computeWidths();
+
+ column_widths(widths);
+ column_char('\t'); // tabs as column delimiters
+
+ refresh();
+
+ end();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gePluginBrowser::refresh()
+{
+ clear();
+
+ add("NAME\tMANUFACTURER\tCATEGORY\tFORMAT\tUID");
+ add("---\t---\t---\t---\t---");
+
+ for (int i=0; i<pluginHost::countAvailablePlugins(); i++) {
+ pluginHost::PluginInfo pi = pluginHost::getAvailablePluginInfo(i);
+ string m = pluginHost::doesPluginExist(pi.uid) ? "" : "@-";
+ string s = m + pi.name + "\t" + m + pi.manufacturerName + "\t" + m +
+ pi.category + "\t" + m + pi.format + "\t" + m + pi.uid;
+ add(s.c_str());
+ }
+
+ for (unsigned i=0; i<pluginHost::countUnknownPlugins(); i++) {
+ string s = "?\t?\t?\t?\t? " + pluginHost::getUnknownPluginInfo(i) + " ?";
+ add(s.c_str());
+ }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gePluginBrowser::computeWidths()
+{
+ int w0, w1, w3;
+ for (int i=0; i<pluginHost::countAvailablePlugins(); i++) {
+ pluginHost::PluginInfo pi = pluginHost::getAvailablePluginInfo(i);
+ w0 = (int) fl_width(pi.name.c_str());
+ w1 = (int) fl_width(pi.manufacturerName.c_str());
+ w3 = (int) fl_width(pi.format.c_str());
+ if (w0 > widths[0]) widths[0] = w0;
+ if (w1 > widths[1]) widths[1] = w1;
+ if (w3 > widths[3]) widths[3] = w3;
+ }
+ widths[0] += 60;
+ widths[1] += 60;
+ widths[2] = fl_width("CATEGORY") + 60;
+ widths[3] += 60;
+ widths[4] = 0;
+}
+
+
+#endif
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * 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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#ifdef WITH_VST
+
+#ifndef GE_PLUGIN_BROWSER_H
+#define GE_PLUGIN_BROWSER_H
+
+
+#include <FL/Fl_Browser.H>
+
+
+class gePluginBrowser : public Fl_Browser
+{
+private:
+
+ int widths[5] = {0};
+
+ void computeWidths();
+
+public:
+
+ gePluginBrowser(int x, int y, int w, int h);
+
+ void refresh();
+};
+
+#endif
+
+#endif
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * 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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#include <FL/Fl.H>
+#include "../../../core/sampleChannel.h"
+#include "../../../core/const.h"
+#include "../../../core/waveFx.h"
+#include "../../../glue/channel.h"
+#include "../../../utils/gui.h"
+#include "../../../utils/math.h"
+#include "../../dialogs/sampleEditor.h"
+#include "../basics/dial.h"
+#include "../basics/input.h"
+#include "../basics/box.h"
+#include "../basics/button.h"
+#include "waveTools.h"
+#include "boostTool.h"
+
+
+geBoostTool::geBoostTool(int X, int Y, SampleChannel *ch)
+ : Fl_Group(X, Y, 220, 20),
+ ch (ch)
+{
+ begin();
+ label = new geBox(x(), y(), gu_getStringWidth("Boost"), 20, "Boost", FL_ALIGN_RIGHT);
+ dial = new geDial(label->x()+label->w()+4, y(), 20, 20);
+ input = new geInput(dial->x()+dial->w()+4, y(), 70, 20);
+ normalize = new geButton(input->x()+input->w()+4, y(), 70, 20, "Normalize");
+ end();
+
+ dial->range(1.0f, 10.0f);
+ dial->callback(cb_setBoost, (void*)this);
+ dial->when(FL_WHEN_CHANGED | FL_WHEN_RELEASE);
+
+ input->callback(cb_setBoostNum, (void*)this);
+
+ normalize->callback(cb_normalize, (void*)this);
+
+ refresh();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geBoostTool::refresh()
+{
+ char buf[16];
+ float dB = gu_linearToDB(ch->getBoost());
+ sprintf(buf, "%.2f", dB);
+ input->value(buf);
+ // a dial > than it's max value goes crazy
+ dial->value(ch->getBoost() <= 10.0f ? ch->getBoost() : 10.0f);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geBoostTool::cb_setBoost (Fl_Widget *w, void *p) { ((geBoostTool*)p)->__cb_setBoost(); }
+void geBoostTool::cb_setBoostNum(Fl_Widget *w, void *p) { ((geBoostTool*)p)->__cb_setBoostNum(); }
+void geBoostTool::cb_normalize (Fl_Widget *w, void *p) { ((geBoostTool*)p)->__cb_normalize(); }
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geBoostTool::__cb_setBoost()
+{
+ if (Fl::event() == FL_DRAG)
+ glue_setBoost(ch, dial->value());
+ else
+ if (Fl::event() == FL_RELEASE) {
+ glue_setBoost(ch, dial->value());
+ static_cast<gdSampleEditor*>(parent()->parent())->waveTools->updateWaveform();
+ }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geBoostTool::__cb_setBoostNum()
+{
+ glue_setBoost(ch, gu_dBtoLinear(atof(input->value())));
+ static_cast<gdSampleEditor*>(parent()->parent())->waveTools->updateWaveform();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geBoostTool::__cb_normalize()
+{
+ float val = wfx_normalizeSoft(ch->wave);
+ glue_setBoost(ch, val); // it's like a fake user moving the dial
+ static_cast<gdSampleEditor*>(parent()->parent())->waveTools->updateWaveform();
+}
+
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * 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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#ifndef GE_BOOST_TOOL_H
+#define GE_BOOST_TOOL_H
+
+
+#include <FL/Fl_Group.H>
+
+
+class SampleChannel;
+class geDial;
+class geInput;
+class geButton;
+class geBox;
+
+
+class geBoostTool : public Fl_Group
+{
+private:
+
+ SampleChannel *ch;
+
+ geBox *label;
+ geDial *dial;
+ geInput *input;
+ geButton *normalize;
+
+ static void cb_setBoost (Fl_Widget *w, void *p);
+ static void cb_setBoostNum(Fl_Widget *w, void *p);
+ static void cb_normalize (Fl_Widget *w, void *p);
+ inline void __cb_setBoost ();
+ inline void __cb_setBoostNum();
+ inline void __cb_normalize ();
+
+public:
+
+ geBoostTool(int x, int y, SampleChannel *ch);
+
+ void refresh();
+};
+
+
+#endif
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * 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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#include <FL/Fl.H>
+#include "../../../core/sampleChannel.h"
+#include "../../../core/const.h"
+#include "../../../core/waveFx.h"
+#include "../../../glue/channel.h"
+#include "../../../utils/gui.h"
+#include "../../../utils/math.h"
+#include "../../dialogs/sampleEditor.h"
+#include "../basics/dial.h"
+#include "../basics/input.h"
+#include "../basics/box.h"
+#include "../basics/button.h"
+#include "waveTools.h"
+#include "panTool.h"
+
+
+gePanTool::gePanTool(int x, int y, SampleChannel *ch)
+ : Fl_Group(x, y, 200, 20),
+ ch (ch)
+{
+ begin();
+ label = new geBox(x, y, gu_getStringWidth("Pan"), 20, "Pan", FL_ALIGN_RIGHT);
+ dial = new geDial(label->x()+label->w()+4, y, 20, 20);
+ input = new geInput(dial->x()+dial->w()+4, y, 70, 20);
+ reset = new geButton(input->x()+input->w()+4, y, 70, 20, "Reset");
+ end();
+
+ dial->range(0.0f, 1.0f);
+ dial->callback(cb_panning, (void*)this);
+
+ input->align(FL_ALIGN_RIGHT);
+ input->readonly(1);
+ input->cursor_color(FL_WHITE);
+
+ reset->callback(cb_panReset, (void*)this);
+
+ refresh();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gePanTool::refresh()
+{
+ dial->value(ch->getPan());
+ char buf[8];
+ if (ch->getPan() < 0.5f) {
+ sprintf(buf, "%d L", (int) ((-ch->getPan() * 200.0f) + 100.0f));
+ input->value(buf);
+ }
+ else
+ if (ch->getPan() == 0.5)
+ input->value("C");
+ else {
+ sprintf(buf, "%d R", (int) ((ch->getPan() * 200.0f) - 100.0f));
+ input->value(buf);
+ }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gePanTool::cb_panning (Fl_Widget *w, void *p) { ((gePanTool*)p)->__cb_panning(); }
+void gePanTool::cb_panReset(Fl_Widget *w, void *p) { ((gePanTool*)p)->__cb_panReset(); }
+
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gePanTool::__cb_panning()
+{
+ glue_setPanning(ch, dial->value());
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gePanTool::__cb_panReset()
+{
+ glue_setPanning(ch, 0.5f);
+}
\ No newline at end of file
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * 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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#ifndef GE_PAN_TOOL_H
+#define GE_PAN_TOOL_H
+
+
+#include <FL/Fl_Group.H>
+
+
+class SampleChannel;
+class geDial;
+class geInput;
+class geButton;
+class geBox;
+
+
+class gePanTool : public Fl_Group
+{
+private:
+
+ SampleChannel *ch;
+
+ geBox *label;
+ geDial *dial;
+ geInput *input;
+ geButton *reset;
+
+ static void cb_panning (Fl_Widget *w, void *p);
+ static void cb_panReset(Fl_Widget *w, void *p);
+ inline void __cb_panning();
+ inline void __cb_panReset();
+
+public:
+
+ gePanTool(int x, int y, SampleChannel *ch);
+
+ void refresh();
+};
+
+
+#endif
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * 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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#include <FL/Fl.H>
+#include "../../../core/sampleChannel.h"
+#include "../../../core/const.h"
+#include "../../../core/graphics.h"
+#include "../../../core/clock.h"
+#include "../../../glue/channel.h"
+#include "../../../utils/gui.h"
+#include "../../dialogs/sampleEditor.h"
+#include "../basics/dial.h"
+#include "../basics/input.h"
+#include "../basics/box.h"
+#include "../basics/button.h"
+#include "pitchTool.h"
+
+
+using namespace giada::m;
+
+
+gePitchTool::gePitchTool(int x, int y, SampleChannel *ch)
+ : Fl_Group(x, y, 600, 20),
+ ch (ch)
+{
+ begin();
+ label = new geBox(x, y, gu_getStringWidth("Pitch"), 20, "Pitch", FL_ALIGN_RIGHT);
+ dial = new geDial(label->x()+label->w()+4, y, 20, 20);
+ input = new geInput(dial->x()+dial->w()+4, y, 70, 20);
+ pitchToBar = new geButton(input->x()+input->w()+4, y, 70, 20, "To bar");
+ pitchToSong = new geButton(pitchToBar->x()+pitchToBar->w()+4, y, 70, 20, "To song");
+ pitchHalf = new geButton(pitchToSong->x()+pitchToSong->w()+4, y, 20, 20, "", divideOff_xpm, divideOn_xpm);
+ pitchDouble = new geButton(pitchHalf->x()+pitchHalf->w()+4, y, 20, 20, "", multiplyOff_xpm, multiplyOn_xpm);
+ pitchReset = new geButton(pitchDouble->x()+pitchDouble->w()+4, y, 70, 20, "Reset");
+ end();
+
+ dial->range(0.01f, 4.0f);
+ dial->callback(cb_setPitch, (void*)this);
+ dial->when(FL_WHEN_RELEASE);
+
+ input->align(FL_ALIGN_RIGHT);
+ input->callback(cb_setPitchNum, (void*)this);
+ input->when(FL_WHEN_RELEASE | FL_WHEN_ENTER_KEY);
+
+ pitchToBar->callback(cb_setPitchToBar, (void*)this);
+ pitchToSong->callback(cb_setPitchToSong, (void*)this);
+ pitchHalf->callback(cb_setPitchHalf, (void*)this);
+ pitchDouble->callback(cb_setPitchDouble, (void*)this);
+ pitchReset->callback(cb_resetPitch, (void*)this);
+
+ refresh();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gePitchTool::refresh()
+{
+ dial->value(ch->getPitch());
+ char buf[16];
+ sprintf(buf, "%.4f", ch->getPitch()); // 4 digits
+ input->value(buf);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gePitchTool::cb_setPitch (Fl_Widget *w, void *p) { ((gePitchTool*)p)->__cb_setPitch(); }
+void gePitchTool::cb_setPitchToBar (Fl_Widget *w, void *p) { ((gePitchTool*)p)->__cb_setPitchToBar(); }
+void gePitchTool::cb_setPitchToSong(Fl_Widget *w, void *p) { ((gePitchTool*)p)->__cb_setPitchToSong(); }
+void gePitchTool::cb_setPitchHalf (Fl_Widget *w, void *p) { ((gePitchTool*)p)->__cb_setPitchHalf(); }
+void gePitchTool::cb_setPitchDouble(Fl_Widget *w, void *p) { ((gePitchTool*)p)->__cb_setPitchDouble(); }
+void gePitchTool::cb_resetPitch (Fl_Widget *w, void *p) { ((gePitchTool*)p)->__cb_resetPitch(); }
+void gePitchTool::cb_setPitchNum (Fl_Widget *w, void *p) { ((gePitchTool*)p)->__cb_setPitchNum(); }
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gePitchTool::__cb_setPitch()
+{
+ glue_setPitch(ch, dial->value());
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gePitchTool::__cb_setPitchNum()
+{
+ glue_setPitch(ch, atof(input->value()));
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gePitchTool::__cb_setPitchHalf()
+{
+ glue_setPitch(ch, dial->value()/2);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gePitchTool::__cb_setPitchDouble()
+{
+ glue_setPitch(ch, dial->value()*2);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gePitchTool::__cb_setPitchToBar()
+{
+ glue_setPitch(ch, ch->end / (float) clock::getFramesPerBar());
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gePitchTool::__cb_setPitchToSong()
+{
+ glue_setPitch(ch, ch->end / (float) clock::getTotalFrames());
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gePitchTool::__cb_resetPitch()
+{
+ glue_setPitch(ch, G_DEFAULT_PITCH);
+}
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * 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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#ifndef GE_PITCH_TOOL_H
+#define GE_PITCH_TOOL_H
+
+
+#include <FL/Fl_Group.H>
+
+
+class SampleChannel;
+class geDial;
+class geInput;
+class geButton;
+class geBox;
+
+
+class gePitchTool : public Fl_Group
+{
+private:
+
+ SampleChannel *ch;
+
+ geBox *label;
+ geDial *dial;
+ geInput *input;
+ geButton *pitchToBar;
+ geButton *pitchToSong;
+ geButton *pitchHalf;
+ geButton *pitchDouble;
+ geButton *pitchReset;
+
+ static void cb_setPitch (Fl_Widget *w, void *p);
+ static void cb_setPitchToBar (Fl_Widget *w, void *p);
+ static void cb_setPitchToSong(Fl_Widget *w, void *p);
+ static void cb_setPitchHalf (Fl_Widget *w, void *p);
+ static void cb_setPitchDouble(Fl_Widget *w, void *p);
+ static void cb_resetPitch (Fl_Widget *w, void *p);
+ static void cb_setPitchNum (Fl_Widget *w, void *p);
+ inline void __cb_setPitch();
+ inline void __cb_setPitchToBar();
+ inline void __cb_setPitchToSong();
+ inline void __cb_setPitchHalf();
+ inline void __cb_setPitchDouble();
+ inline void __cb_resetPitch();
+ inline void __cb_setPitchNum();
+
+public:
+
+ gePitchTool(int x, int y, SampleChannel *ch);
+
+ void refresh();
+};
+
+
+#endif
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * 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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#include <FL/Fl.H>
+#include "../../../core/sampleChannel.h"
+#include "../../../core/wave.h"
+#include "../../../glue/channel.h"
+#include "../../../utils/gui.h"
+#include "../../../utils/string.h"
+#include "../../dialogs/sampleEditor.h"
+#include "../basics/input.h"
+#include "../basics/box.h"
+#include "../basics/button.h"
+#include "waveTools.h"
+#include "rangeTool.h"
+
+
+geRangeTool::geRangeTool(int x, int y, SampleChannel *ch)
+ : Fl_Group(x, y, 300, 20),
+ ch (ch)
+{
+ begin();
+ label = new geBox (x, y, gu_getStringWidth("Range"), 20, "Range", FL_ALIGN_RIGHT);
+ begin_ = new geInput(label->x()+label->w()+4, y, 70, 20);
+ end_ = new geInput(begin_->x()+begin_->w()+4, y, 70, 20);
+ reset = new geButton(end_->x()+end_->w()+4, y, 70, 20, "Reset");
+ end();
+
+ begin_->type(FL_INT_INPUT);
+ begin_->when(FL_WHEN_RELEASE | FL_WHEN_ENTER_KEY); // on focus lost or enter key
+ begin_->callback(cb_setChanPos, this);
+
+ end_->type(FL_INT_INPUT);
+ end_->when(FL_WHEN_RELEASE | FL_WHEN_ENTER_KEY); // on focus lost or enter key
+ end_->callback(cb_setChanPos, this);
+
+ reset->callback(cb_resetStartEnd, this);
+
+ refresh();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geRangeTool::refresh()
+{
+ begin_->value(gu_itoa(ch->begin / 2).c_str());
+ end_->value(gu_itoa(ch->end / 2).c_str());
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geRangeTool::cb_setChanPos (Fl_Widget *w, void *p) { ((geRangeTool*)p)->__cb_setChanPos(); }
+void geRangeTool::cb_resetStartEnd(Fl_Widget *w, void *p) { ((geRangeTool*)p)->__cb_resetStartEnd(); }
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geRangeTool::__cb_setChanPos()
+{
+ glue_setBeginEndChannel(ch, atoi(begin_->value())*2, atoi(end_->value())*2);
+ static_cast<gdSampleEditor*>(parent()->parent())->waveTools->updateWaveform();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geRangeTool::__cb_resetStartEnd()
+{
+ glue_setBeginEndChannel(ch, 0, ch->wave->size);
+ static_cast<gdSampleEditor*>(parent()->parent())->waveTools->updateWaveform();
+}
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * 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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#ifndef GE_RANGE_TOOL_H
+#define GE_RANGE_TOOL_H
+
+
+#include <FL/Fl_Group.H>
+
+
+class SampleChannel;
+class geInput;
+class geButton;
+class geBox;
+
+
+class geRangeTool : public Fl_Group
+{
+private:
+
+ SampleChannel *ch;
+
+ geBox *label;
+ geInput *begin_;
+ geInput *end_;
+ geButton *reset;
+
+ static void cb_setChanPos (Fl_Widget *w, void *p);
+ static void cb_resetStartEnd(Fl_Widget *w, void *p);
+ inline void __cb_setChanPos();
+ inline void __cb_resetStartEnd();
+
+public:
+
+ geRangeTool(int x, int y, SampleChannel *ch);
+
+ void refresh();
+};
+
+
+#endif
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * 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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#include <cmath>
+#include <FL/Fl_Pack.H>
+#include "../../../core/sampleChannel.h"
+#include "../../../core/const.h"
+#include "../../../glue/channel.h"
+#include "../../../utils/gui.h"
+#include "../../../utils/math.h"
+#include "../basics/dial.h"
+#include "../basics/input.h"
+#include "../basics/box.h"
+#include "../mainWindow/keyboard/channel.h"
+#include "volumeTool.h"
+
+
+geVolumeTool::geVolumeTool(int X, int Y, SampleChannel *ch)
+ : Fl_Group(X, Y, 150, 20),
+ ch (ch)
+{
+ begin();
+ label = new geBox (x(), y(), gu_getStringWidth("Volume"), 20, "Volume", FL_ALIGN_RIGHT);
+ dial = new geDial (label->x()+label->w()+4, y(), 20, 20);
+ input = new geInput(dial->x()+dial->w()+4, y(), 70, 20);
+ end();
+
+ dial->range(0.0f, 1.0f);
+ dial->callback(cb_setVolume, (void*)this);
+
+ input->callback(cb_setVolumeNum, (void*)this);
+
+ refresh();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geVolumeTool::refresh()
+{
+ char buf[16];
+ float dB = gu_linearToDB(ch->volume);
+ if (dB > -INFINITY) sprintf(buf, "%.2f", dB);
+ else sprintf(buf, "-inf");
+ input->value(buf);
+ dial->value(ch->guiChannel->vol->value());
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geVolumeTool::cb_setVolume (Fl_Widget *w, void *p) { ((geVolumeTool*)p)->__cb_setVolume(); }
+void geVolumeTool::cb_setVolumeNum(Fl_Widget *w, void *p) { ((geVolumeTool*)p)->__cb_setVolumeNum(); }
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geVolumeTool::__cb_setVolume()
+{
+ glue_setVolume(ch, dial->value(), false, true);
+ refresh();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geVolumeTool::__cb_setVolumeNum()
+{
+ float value = pow(10, (atof(input->value()) / 20)); // linear = 10^(dB/20)
+ glue_setVolume(ch, value, false, true);
+ dial->value(ch->guiChannel->vol->value());
+}
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * 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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#ifndef GE_VOLUME_TOOL_H
+#define GE_VOLUME_TOOL_H
+
+
+#include <FL/Fl_Group.H>
+
+
+class SampleChannel;
+class geDial;
+class geInput;
+class geBox;
+
+
+class geVolumeTool : public Fl_Group
+{
+private:
+
+ SampleChannel *ch;
+
+ geBox *label;
+ geDial *dial;
+ geInput *input;
+
+ static void cb_setVolume (Fl_Widget *w, void *p);
+ static void cb_setVolumeNum(Fl_Widget *w, void *p);
+ inline void __cb_setVolume ();
+ inline void __cb_setVolumeNum();
+
+public:
+
+ geVolumeTool(int x, int y, SampleChannel *ch);
+
+ void refresh();
+};
+
+
+#endif
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * gg_waveTools
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * 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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#include "../../../core/const.h"
+#include "../basics/boxtypes.h"
+#include "waveform.h"
+#include "waveTools.h"
+
+
+geWaveTools::geWaveTools(int x, int y, int w, int h, SampleChannel *ch, const char *l)
+ : Fl_Scroll(x, y, w, h, l)
+{
+ type(Fl_Scroll::HORIZONTAL_ALWAYS);
+ hscrollbar.color(COLOR_BG_0);
+ hscrollbar.selection_color(COLOR_BG_1);
+ hscrollbar.labelcolor(COLOR_BD_1);
+ hscrollbar.slider(G_CUSTOM_BORDER_BOX);
+
+ waveform = new geWaveform(x, y, w, h-24, ch);
+
+
+ //resizable(waveform);
+}
+
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geWaveTools::updateWaveform()
+{
+ waveform->alloc(w());
+ waveform->redraw();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geWaveTools::resize(int x, int y, int w, int h)
+{
+ if (this->w() == w || (this->w() != w && this->h() != h)) { // vertical or both resize
+ Fl_Widget::resize(x, y, w, h);
+ waveform->resize(x, y, waveform->w(), h-24);
+ updateWaveform();
+ }
+ else { // horizontal resize
+ Fl_Widget::resize(x, y, w, h);
+ }
+
+ if (this->w() > waveform->w())
+ waveform->stretchToWindow();
+
+ int offset = waveform->x() + waveform->w() - this->w() - this->x();
+ if (offset < 0)
+ waveform->position(waveform->x()-offset, this->y());
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+int geWaveTools::handle(int e)
+{
+ int ret = Fl_Group::handle(e);
+ switch (e) {
+ case FL_MOUSEWHEEL: {
+ waveform->setZoom(Fl::event_dy());
+ redraw();
+ ret = 1;
+ break;
+ }
+ }
+ return ret;
+}
--- /dev/null
+/* ---------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * gg_waveTools
+ *
+ * ---------------------------------------------------------------------
+ *
+ * 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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * ------------------------------------------------------------------ */
+
+
+#ifndef GE_WAVE_TOOLS_H
+#define GE_WAVE_TOOLS_H
+
+
+#include <FL/Fl_Scroll.H>
+
+
+class SampleChannel;
+class geWaveform;
+
+
+class geWaveTools : public Fl_Scroll
+{
+public:
+
+ geWaveform *waveform;
+
+ geWaveTools(int X,int Y,int W, int H, SampleChannel *ch, const char *L=0);
+ void resize(int x, int y, int w, int h);
+ int handle(int e);
+
+ void updateWaveform();
+};
+
+#endif
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * geWaveform
+ * An element which represents a waveform.
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * 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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#include <cmath>
+#include <FL/fl_draw.H>
+#include <FL/Fl_Menu_Button.H>
+#include "../../../core/wave.h"
+#include "../../../core/conf.h"
+#include "../../../core/const.h"
+#include "../../../core/mixer.h"
+#include "../../../core/waveFx.h"
+#include "../../../core/sampleChannel.h"
+#include "../../../glue/channel.h"
+#include "../basics/boxtypes.h"
+#include "waveTools.h"
+#include "waveform.h"
+
+
+using namespace giada::m;
+
+
+geWaveform::geWaveform(int x, int y, int w, int h, SampleChannel *ch, const char *l)
+: Fl_Widget(x, y, w, h, l),
+ chan(ch),
+ menuOpen(false),
+ chanStart(0),
+ chanStartLit(false),
+ chanEnd(0),
+ chanEndLit(false),
+ ratio(0.0f),
+ selectionA(0),
+ selectionB(0),
+ selectionA_abs(0),
+ selectionB_abs(0)
+{
+ data.sup = nullptr;
+ data.inf = nullptr;
+ data.size = 0;
+
+ grid.snap = conf::sampleEditorGridOn;
+ grid.level = conf::sampleEditorGridVal;
+
+ stretchToWindow();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+geWaveform::~geWaveform()
+{
+ freeData();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geWaveform::freeData()
+{
+ if (data.sup != nullptr) {
+ free(data.sup);
+ free(data.inf);
+ data.sup = nullptr;
+ data.inf = nullptr;
+ data.size = 0;
+ }
+ grid.points.clear();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int geWaveform::alloc(int datasize)
+{
+ ratio = chan->wave->size / (float) datasize;
+
+ if (ratio < 2)
+ return 0;
+
+ freeData();
+
+ data.size = datasize;
+ data.sup = (int*) malloc(data.size * sizeof(int));
+ data.inf = (int*) malloc(data.size * sizeof(int));
+
+ int offset = h() / 2;
+ int zero = y() + offset; // center, zero amplitude (-inf dB)
+
+ /* grid frequency: store a grid point every 'gridFreq' pixel. Must be
+ * even, as always */
+
+ int gridFreq = 0;
+ if (grid.level != 0) {
+ gridFreq = chan->wave->size / grid.level;
+ if (gridFreq % 2 != 0)
+ gridFreq--;
+ }
+
+ for (int i=0; i<data.size; i++) {
+
+ int pp; // point prev
+ int pn; // point next
+
+ /* resampling the waveform, hardcore way. Many thanks to
+ * http://fourier.eng.hmc.edu/e161/lectures/resize/node3.html
+ * Note: we use
+ * p = j * (m-1 / n)
+ * instead of
+ * p = j * (m-1 / n-1)
+ * in order to obtain 'datasize' cells to parse (and not datasize-1) */
+
+ pp = i * ((chan->wave->size - 1) / (float) datasize);
+ pn = (i+1) * ((chan->wave->size - 1) / (float) datasize);
+
+ if (pp % 2 != 0) pp -= 1;
+ if (pn % 2 != 0) pn -= 1;
+
+ float peaksup = 0.0f;
+ float peakinf = 0.0f;
+
+ /* scan the original data in chunks */
+
+ int k = pp;
+ while (k < pn) {
+
+ if (chan->wave->data[k] > peaksup)
+ peaksup = chan->wave->data[k]; // FIXME - Left data only
+ else
+ if (chan->wave->data[k] <= peakinf)
+ peakinf = chan->wave->data[k]; // FIXME - Left data only
+
+ /* print grid */
+
+ if (gridFreq != 0)
+ if (k % gridFreq == 0 && k != 0)
+ grid.points.push_back(i);
+
+ k += 2;
+ }
+
+ data.sup[i] = zero - (peaksup * chan->getBoost() * offset);
+ data.inf[i] = zero - (peakinf * chan->getBoost() * offset);
+
+ // avoid window overflow
+
+ if (data.sup[i] < y()) data.sup[i] = y();
+ if (data.inf[i] > y()+h()-1) data.inf[i] = y()+h()-1;
+ }
+
+ recalcPoints();
+ return 1;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geWaveform::recalcPoints()
+{
+ selectionA = relativePoint(selectionA_abs);
+ selectionB = relativePoint(selectionB_abs);
+ chanStart = relativePoint(chan->begin / 2);
+
+ /* fix the rounding error when chanEnd is set on the very end of the
+ * sample */
+
+ if (chan->end == chan->wave->size)
+ chanEnd = data.size - 2; // 2 px border
+ else
+ chanEnd = relativePoint(chan->end / 2);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geWaveform::draw()
+{
+ /* blank canvas */
+
+ fl_rectf(x(), y(), w(), h(), COLOR_BG_0);
+
+ /* draw selection (if any) */
+
+ if (selectionA != selectionB) {
+
+ int a_x = selectionA + x() - BORDER; // - start;
+ int b_x = selectionB + x() - BORDER; // - start;
+
+ if (a_x < 0)
+ a_x = 0;
+ if (b_x >= w()-1)
+ b_x = w()-1;
+
+ if (selectionA < selectionB)
+ fl_rectf(a_x+BORDER, y(), b_x-a_x, h(), COLOR_BD_0);
+ else
+ fl_rectf(b_x+BORDER, y(), a_x-b_x, h(), COLOR_BD_0);
+ }
+
+ /* draw waveform from x1 (offset driven by the scrollbar) to x2
+ * (width of parent window). We don't draw the entire waveform,
+ * only the visibile part. */
+
+ int offset = h() / 2;
+ int zero = y() + offset; // sample zero (-inf dB)
+
+ int wx1 = abs(x() - ((geWaveTools*)parent())->x());
+ int wx2 = wx1 + ((geWaveTools*)parent())->w();
+ if (x()+w() < ((geWaveTools*)parent())->w())
+ wx2 = x() + w() - BORDER;
+
+ fl_color(0, 0, 0);
+ for (int i=wx1; i<wx2; i++) {
+ fl_line(i+x(), zero, i+x(), data.sup[i]);
+ fl_line(i+x(), zero, i+x(), data.inf[i]);
+
+ /* print grid */
+
+ for (unsigned k=0; k<grid.points.size(); k++) {
+ if (grid.points.at(k) == i) {
+ //gu_log("draw grid line at %d\n", i);
+ fl_color(fl_rgb_color(54, 54, 54));
+ fl_line_style(FL_DASH, 0, nullptr);
+ fl_line(i+x(), y(), i+x(), y()+h());
+ fl_color(0, 0, 0);
+ fl_line_style(FL_SOLID, 0, nullptr);
+ break;
+ }
+ }
+ }
+
+ /* border box */
+
+ fl_rect(x(), y(), w(), h(), COLOR_BD_0);
+
+ /* print chanStart */
+
+ int lineX = x()+chanStart+1;
+
+ if (chanStartLit) fl_color(COLOR_BD_1);
+ else fl_color(COLOR_BD_0);
+
+ /* vertical line */
+
+ fl_line(lineX, y()+1, lineX, y()+h()-2);
+
+ /* print flag and avoid overflow */
+
+ if (lineX+FLAG_WIDTH > w()+x()-2)
+ fl_rectf(lineX, y()+h()-FLAG_HEIGHT-1, w()-lineX+x()-1, FLAG_HEIGHT);
+ else
+ fl_rectf(lineX, y()+h()-FLAG_HEIGHT-1, FLAG_WIDTH, FLAG_HEIGHT);
+
+ /* print chanEnd */
+
+ lineX = x()+chanEnd;
+ if (chanEndLit) fl_color(COLOR_BD_1);
+ else fl_color(COLOR_BD_0);
+
+ fl_line(lineX, y()+1, lineX, y()+h()-2);
+
+ if (lineX-FLAG_WIDTH < x())
+ fl_rectf(x()+1, y()+1, lineX-x(), FLAG_HEIGHT);
+ else
+ fl_rectf(lineX-FLAG_WIDTH, y()+1, FLAG_WIDTH, FLAG_HEIGHT);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int geWaveform::handle(int e)
+{
+ int ret = 0;
+
+ switch (e) {
+
+ case FL_PUSH: {
+
+ mouseX = Fl::event_x();
+ pushed = true;
+
+ if (!mouseOnEnd() && !mouseOnStart()) {
+
+ /* right button? show the menu. Don't set selectionA,B,etc */
+
+ if (Fl::event_button3()) {
+ openEditMenu();
+ }
+ else
+ if (mouseOnSelectionA() || mouseOnSelectionB()) {
+ resized = true;
+ }
+ else {
+ dragged = true;
+ selectionA = Fl::event_x() - x();
+
+ if (selectionA >= data.size) selectionA = data.size;
+
+ selectionB = selectionA;
+ selectionA_abs = absolutePoint(selectionA);
+ selectionB_abs = selectionA_abs;
+ }
+ }
+
+ ret = 1;
+ break;
+ }
+
+ case FL_RELEASE: {
+
+ int realChanStart = chan->begin;
+ int realChanEnd = chan->end;
+
+ if (chanStartLit)
+ realChanStart = absolutePoint(chanStart)*2;
+ else
+ if (chanEndLit)
+ realChanEnd = absolutePoint(chanEnd)*2;
+
+ glue_setBeginEndChannel(chan, realChanStart, realChanEnd);
+
+ pushed = false;
+ dragged = false;
+
+ redraw();
+ ret = 1;
+ break;
+ }
+
+ case FL_ENTER: { // enables FL_DRAG
+ ret = 1;
+ break;
+ }
+
+ case FL_LEAVE: {
+ if (chanStartLit || chanEndLit) {
+ chanStartLit = false;
+ chanEndLit = false;
+ redraw();
+ }
+ ret = 1;
+ break;
+ }
+
+ case FL_MOVE: {
+ mouseX = Fl::event_x();
+ mouseY = Fl::event_y();
+
+ if (mouseOnStart()) {
+ chanStartLit = true;
+ redraw();
+ }
+ else
+ if (chanStartLit) {
+ chanStartLit = false;
+ redraw();
+ }
+
+ if (mouseOnEnd()) {
+ chanEndLit = true;
+ redraw();
+ }
+ else
+ if (chanEndLit) {
+ chanEndLit = false;
+ redraw();
+ }
+
+ if (mouseOnSelectionA())
+ fl_cursor(FL_CURSOR_WE, FL_WHITE, FL_BLACK);
+ else
+ if (mouseOnSelectionB())
+ fl_cursor(FL_CURSOR_WE, FL_WHITE, FL_BLACK);
+ else
+ fl_cursor(FL_CURSOR_DEFAULT, FL_WHITE, FL_BLACK);
+
+ ret = 1;
+ break;
+ }
+
+ case FL_DRAG: {
+
+ /* here the mouse is on the chanStart tool */
+
+ if (chanStartLit && pushed) {
+
+ chanStart = Fl::event_x() - x();
+
+ if (grid.snap)
+ chanStart = applySnap(chanStart);
+
+ if (chanStart < 0)
+ chanStart = 0;
+ else
+ if (chanStart >= chanEnd)
+ chanStart = chanEnd-2;
+
+ redraw();
+ }
+ else
+ if (chanEndLit && pushed) {
+
+ chanEnd = Fl::event_x() - x();
+
+ if (grid.snap)
+ chanEnd = applySnap(chanEnd);
+
+ if (chanEnd >= data.size - 2)
+ chanEnd = data.size - 2;
+ else
+ if (chanEnd <= chanStart)
+ chanEnd = chanStart + 2;
+
+ redraw();
+ }
+
+ /* here the mouse is on the waveform, i.e. a selection */
+
+ else
+ if (dragged) {
+
+ selectionB = Fl::event_x() - x();
+
+ if (selectionB >= data.size)
+ selectionB = data.size;
+
+ if (selectionB <= 0)
+ selectionB = 0;
+
+ if (grid.snap)
+ selectionB = applySnap(selectionB);
+
+ selectionB_abs = absolutePoint(selectionB);
+ redraw();
+ }
+
+ /* here the mouse is on a selection boundary i.e. resize */
+
+ else
+ if (resized) {
+ int pos = Fl::event_x() - x();
+ if (mouseOnSelectionA()) {
+ selectionA = grid.snap ? applySnap(pos) : pos;
+ selectionA_abs = absolutePoint(selectionA);
+ }
+ else
+ if (mouseOnSelectionB()) {
+ selectionB = grid.snap ? applySnap(pos) : pos;
+ selectionB_abs = absolutePoint(selectionB);
+ }
+ redraw();
+ }
+ mouseX = Fl::event_x();
+ ret = 1;
+ break;
+ }
+ }
+ return ret;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+/* pixel snap disances (10px) must be equal to those defined in
+ * geWaveform::mouseOnSelectionA() and gWaverfrom::mouseOnSelectionB() */
+/* TODO - use constant for 10px */
+
+int geWaveform::applySnap(int pos)
+{
+ for (unsigned i=0; i<grid.points.size(); i++) {
+ if (pos >= grid.points.at(i) - SNAPPING &&
+ pos <= grid.points.at(i) + SNAPPING)
+ {
+ return grid.points.at(i);
+ }
+ }
+ return pos;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+bool geWaveform::mouseOnStart()
+{
+ return mouseX-10 > chanStart + x() - BORDER &&
+ mouseX-10 <= chanStart + x() - BORDER + FLAG_WIDTH &&
+ mouseY > h() + y() - FLAG_HEIGHT;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+bool geWaveform::mouseOnEnd()
+{
+ return mouseX-10 >= chanEnd + x() - BORDER - FLAG_WIDTH &&
+ mouseX-10 <= chanEnd + x() - BORDER &&
+ mouseY <= y() + FLAG_HEIGHT + 1;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+/* pixel boundaries (10px) must be equal to the snap factor distance
+ * defined in geWaveform::applySnap() */
+
+bool geWaveform::mouseOnSelectionA()
+{
+ if (selectionA == selectionB)
+ return false;
+ return mouseX >= selectionA-10+x() && mouseX <= selectionA+10+x();
+}
+
+
+bool geWaveform::mouseOnSelectionB()
+{
+ if (selectionA == selectionB)
+ return false;
+ return mouseX >= selectionB-10+x() && mouseX <= selectionB+10+x();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int geWaveform::absolutePoint(int p)
+{
+ if (p <= 0)
+ return 0;
+
+ if (p > data.size)
+ return chan->wave->size / 2;
+
+ return (p * ratio) / 2;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int geWaveform::relativePoint(int p)
+{
+ return (ceil(p / ratio)) * 2;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geWaveform::openEditMenu()
+{
+ if (selectionA == selectionB)
+ return;
+
+ menuOpen = true;
+
+ Fl_Menu_Item menu[] = {
+ {"Cut"},
+ {"Trim"},
+ {"Silence"},
+ {"Fade in"},
+ {"Fade out"},
+ {"Smooth edges"},
+ {"Set start/end here"},
+ {0}
+ };
+
+ if (chan->status == STATUS_PLAY) {
+ menu[0].deactivate();
+ menu[1].deactivate();
+ }
+
+ Fl_Menu_Button *b = new Fl_Menu_Button(0, 0, 100, 50);
+ b->box(G_CUSTOM_BORDER_BOX);
+ b->textsize(GUI_FONT_SIZE_BASE);
+ b->textcolor(COLOR_TEXT_0);
+ b->color(COLOR_BG_0);
+
+ const Fl_Menu_Item *m = menu->popup(Fl::event_x(), Fl::event_y(), 0, 0, b);
+ if (!m) {
+ menuOpen = false;
+ return;
+ }
+
+ /* straightSel() to ensure that point A is always lower than B */
+
+ straightSel();
+
+ if (strcmp(m->label(), "Silence") == 0) {
+ wfx_silence(chan->wave, absolutePoint(selectionA), absolutePoint(selectionB));
+
+ selectionA = 0;
+ selectionB = 0;
+
+ stretchToWindow();
+ redraw();
+ menuOpen = false;
+ return;
+ }
+
+ if (strcmp(m->label(), "Set start/end here") == 0) {
+
+ glue_setBeginEndChannel(chan, absolutePoint(selectionA) * 2,
+ absolutePoint(selectionB) * 2); // stereo values
+
+ selectionA = 0;
+ selectionB = 0;
+ selectionA_abs = 0;
+ selectionB_abs = 0;
+
+ recalcPoints();
+ redraw();
+ menuOpen = false;
+ return;
+ }
+
+ if (strcmp(m->label(), "Cut") == 0) {
+ wfx_cut(chan->wave, absolutePoint(selectionA), absolutePoint(selectionB));
+
+ /* for convenience reset start/end points */
+
+ glue_setBeginEndChannel(chan, 0, chan->wave->size);
+
+ selectionA = 0;
+ selectionB = 0;
+ selectionA_abs = 0;
+ selectionB_abs = 0;
+
+ setZoom(0);
+
+ menuOpen = false;
+ return;
+ }
+
+ if (strcmp(m->label(), "Trim") == 0) {
+ wfx_trim(chan->wave, absolutePoint(selectionA), absolutePoint(selectionB));
+
+ glue_setBeginEndChannel(chan, 0, chan->wave->size);
+
+ selectionA = 0;
+ selectionB = 0;
+ selectionA_abs = 0;
+ selectionB_abs = 0;
+
+ stretchToWindow();
+ menuOpen = false;
+ redraw();
+ return;
+ }
+
+ if (!strcmp(m->label(), "Fade in") || !strcmp(m->label(), "Fade out")) {
+
+ int type = !strcmp(m->label(), "Fade in") ? 0 : 1;
+ wfx_fade(chan->wave, absolutePoint(selectionA), absolutePoint(selectionB), type);
+
+ selectionA = 0;
+ selectionB = 0;
+
+ stretchToWindow();
+ redraw();
+ menuOpen = false;
+ return;
+ }
+
+ if (!strcmp(m->label(), "Smooth edges")) {
+
+ wfx_smooth(chan->wave, absolutePoint(selectionA), absolutePoint(selectionB));
+
+ selectionA = 0;
+ selectionB = 0;
+
+ stretchToWindow();
+ redraw();
+ menuOpen = false;
+ return;
+ }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geWaveform::straightSel()
+{
+ if (selectionA > selectionB) {
+ unsigned tmp = selectionB;
+ selectionB = selectionA;
+ selectionA = tmp;
+ }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geWaveform::setZoom(int type)
+{
+ int newSize;
+ if (type == -1) newSize = data.size*2; // zoom in
+ else newSize = data.size/2; // zoom out
+
+ if (alloc(newSize)) {
+ size(data.size, h());
+
+ /* zoom to pointer */
+
+ int shift;
+ if (x() > 0)
+ shift = Fl::event_x() - x();
+ else
+ if (type == -1)
+ shift = Fl::event_x() + abs(x());
+ else
+ shift = (Fl::event_x() + abs(x())) / -2;
+
+ if (x() - shift > BORDER)
+ shift = 0;
+
+ position(x() - shift, y());
+
+
+ /* avoid overflow when zooming out with scrollbar like that:
+ * |----------[scrollbar]|
+ *
+ * offset vs smaller:
+ * |[wave------------| offset > 0 smaller = false
+ * |[wave----] | offset < 0, smaller = true
+ * |-------------] | offset < 0, smaller = false */
+
+ int parentW = ((geWaveTools*)parent())->w();
+ int thisW = x() + w() - BORDER; // visible width, not full width
+
+ if (thisW < parentW)
+ position(x() + parentW - thisW, y());
+ if (smaller())
+ stretchToWindow();
+
+ redraw();
+ }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geWaveform::stretchToWindow()
+{
+ int s = ((geWaveTools*)parent())->w();
+ alloc(s);
+ position(BORDER, y());
+ size(s, h());
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+bool geWaveform::smaller()
+{
+ return w() < ((geWaveTools*)parent())->w();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+float geWaveform::displayRatio()
+{
+ return 1.0f / (data.size / (float) w());
+};
+
+/* -------------------------------------------------------------------------- */
+
+
+void geWaveform::setGridLevel(int l)
+{
+ grid.points.clear();
+ grid.level = l;
+ alloc(data.size);
+ redraw();
+}
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * ge_waveform
+ * an element which represents a waveform.
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * 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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#ifndef GE_WAVEFORM_H
+#define GE_WAVEFORM_H
+
+
+#include <vector>
+#include <FL/Fl_Widget.H>
+
+
+class SampleChannel;
+
+
+class geWaveform : public Fl_Widget
+{
+private:
+
+ static const int FLAG_WIDTH = 20;
+ static const int FLAG_HEIGHT = 20;
+ static const int BORDER = 8; // window border <-> widget border
+ static const int SNAPPING = 10;
+
+ /* data
+ * real graphic stuff from the underlying waveform */
+
+ struct data
+ {
+ int *sup;
+ int *inf;
+ int size;
+ } data;
+
+ /* grid */
+
+ struct grid
+ {
+ bool snap;
+ int level;
+ std::vector<int> points;
+ } grid;
+
+ /* chan
+ * chan in use. */
+
+ SampleChannel *chan;
+
+ /* menuOpen
+ * is the menu open? */
+
+ bool menuOpen;
+
+ /* mouseOnStart/end
+ * is mouse on start or end flag? */
+
+ bool mouseOnStart();
+ bool mouseOnEnd();
+
+ /* mouseOnSelectionA/B
+ * as above, for the selection */
+
+ bool mouseOnSelectionA();
+ bool mouseOnSelectionB();
+
+ /* absolutePoint
+ * from a relative 'p' point (zoom affected) returns the same point
+ * zoom 1:1 based */
+
+ int absolutePoint(int p);
+
+ /* relativePoint
+ * from an absolute 'p' point (1:1 zoom), returns the same point zoom
+ * affected */
+
+ int relativePoint(int p);
+
+ /* straightSel
+ * helper function which flattens the selection if it was made from
+ * right to left (inverse selection) */
+
+ void straightSel();
+
+ /* freeData
+ * destroy any graphical buffer */
+
+ void freeData();
+
+ /* smaller
+ * is the waveform smaller than the parent window? */
+
+ bool smaller();
+
+ /* applySnap
+ * snap a point at 'pos' pixel */
+
+ int applySnap(int pos);
+
+public:
+
+ geWaveform(int x, int y, int w, int h, SampleChannel *ch, const char *l=0);
+ ~geWaveform();
+ void draw();
+ int handle(int e);
+
+ /* alloc
+ * allocate memory for the picture */
+
+ int alloc(int datasize=0);
+
+ /* recalcPoints
+ * re-calc chanStart, chanEnd, ... */
+
+ void recalcPoints();
+
+ /* openEditMenu
+ * show edit menu on right-click */
+
+ void openEditMenu();
+
+ /* displayRatio
+ * how much of the waveform is being displayed on screen */
+
+ float displayRatio();
+
+ /* zoom
+ * type == 1 : zoom out, type == -1: zoom in */
+
+ void setZoom(int type);
+
+ /* strecthToWindow
+ * shrink or enlarge the waveform to match parent's width (gWaveTools) */
+
+ void stretchToWindow();
+
+ /* setGridLevel
+ * set a new frequency level for the grid. 0 means disabled. */
+
+ void setGridLevel(int l);
+
+ void setSnap(bool v) { grid.snap = v; }
+ bool getSnap() { return grid.snap; }
+
+ inline int getSize() { return data.size; }
+
+ int chanStart;
+ bool chanStartLit;
+ int chanEnd;
+ bool chanEndLit;
+ bool pushed;
+ bool dragged;
+ bool resized;
+
+ float ratio;
+
+ /* TODO - useless! use Fl::mouse_x() and Fl::mouse_y() instead */
+ int mouseX; // mouse pos for drag.n.drop
+ int mouseY;
+
+ /* selectionA/B = portion of the selected wave
+ * " " "" " _abs = selectionA/B not affected by zoom */
+ /** TODO - change selectionA to selectionA_rel
+ TODO - change selectionB to selectionB_rel */
+ int selectionA;
+ int selectionB;
+ int selectionA_abs;
+ int selectionB_abs;
+};
+
+
+#endif
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * 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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#include <cmath>
+#include <FL/fl_draw.H>
+#include "../../core/const.h"
+#include "../../core/kernelAudio.h"
+#include "soundMeter.h"
+
+
+using namespace giada::m;
+
+
+geSoundMeter::geSoundMeter(int x, int y, int w, int h, const char *L)
+ : Fl_Box (x, y, w, h, L),
+ clip (false),
+ mixerPeak (0.0f),
+ peak (0.0f),
+ dbLevel (0.0f),
+ dbLevelOld(0.0f)
+{
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geSoundMeter::draw()
+{
+ fl_rect(x(), y(), w(), h(), COLOR_BD_0);
+
+ /* peak = the highest value inside the frame */
+
+ peak = 0.0f;
+ float tmp_peak = 0.0f;
+
+ tmp_peak = fabs(mixerPeak);
+ if (tmp_peak > peak)
+ peak = tmp_peak;
+
+ clip = peak >= 1.0f ? true : false; // 1.0f is considered clip
+
+
+ /* dBFS (full scale) calculation, plus decay of -2dB per frame */
+
+ dbLevel = 20 * log10(peak);
+ if (dbLevel < dbLevelOld)
+ if (dbLevelOld > -G_DB_MIN_SCALE)
+ dbLevel = dbLevelOld - 2.0f;
+
+ dbLevelOld = dbLevel;
+
+ /* graphical part */
+
+ float px_level = 0.0f;
+ if (dbLevel < 0.0f)
+ px_level = ((w()/G_DB_MIN_SCALE) * dbLevel) + w();
+ else
+ px_level = w();
+
+ fl_rectf(x()+1, y()+1, w()-2, h()-2, COLOR_BG_0);
+ fl_rectf(x()+1, y()+1, (int) px_level, h()-2, clip || !kernelAudio::getStatus() ? COLOR_ALERT : COLOR_BD_0);
+}
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * 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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#ifndef GE_SOUND_METER_H
+#define GE_SOUND_METER_H
+
+
+#include <FL/Fl_Box.H>
+
+
+class geSoundMeter : public Fl_Box
+{
+public:
+
+ geSoundMeter(int X, int Y, int W, int H, const char *L=0);
+
+ void draw() override;
+
+ bool clip;
+ float mixerPeak; // peak from mixer
+
+private:
+
+ float peak;
+ float dbLevel;
+ float dbLevelOld;
+};
+
+
+#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
#if defined(__linux__) || defined(__APPLE__)
#include <unistd.h>
#endif
+#include <FL/Fl.H>
#include "core/init.h"
#include "core/const.h"
-#include "core/patch_DEPR_.h"
#include "core/patch.h"
#include "core/conf.h"
#include "core/midiMapConf.h"
#include "core/mixer.h"
+#include "core/clock.h"
#include "core/mixerHandler.h"
#include "core/kernelAudio.h"
#include "core/kernelMidi.h"
#include "core/pluginHost.h"
-/* global variables. Yeah, we are nasty */
-
pthread_t G_videoThread;
-KernelAudio G_KernelAudio;
-Mixer G_Mixer;
-Recorder G_Recorder;
-KernelMidi G_KernelMidi;
bool G_quit;
-bool G_audio_status;
-bool G_midiStatus;
-Patch_DEPR_ G_Patch_DEPR_;
-Patch G_Patch;
-Conf G_Conf;
-MidiMapConf G_MidiMap;
gdMainWindow *G_MainWin;
-#ifdef WITH_VST
-PluginHost G_PluginHost;
-#endif
-
void *videoThreadCb(void *arg);
init_prepareKernelAudio();
init_prepareKernelMIDI();
init_startGUI(argc, argv);
- Fl::lock();
- pthread_create(&G_videoThread, NULL, videoThreadCb, NULL);
+
+ Fl::lock();
+ pthread_create(&G_videoThread, nullptr, videoThreadCb, nullptr);
init_startKernelAudio();
#ifdef WITH_VST
juce::shutdownJuce_GUI();
#endif
- pthread_join(G_videoThread, NULL);
+ pthread_join(G_videoThread, nullptr);
return ret;
}
void *videoThreadCb(void *arg)
{
- if (G_audio_status)
+ if (giada::m::kernelAudio::getStatus())
while (!G_quit) {
gu_refreshUI();
#ifdef _WIN32
usleep(GUI_SLEEP);
#endif
}
- pthread_exit(NULL);
+ pthread_exit(nullptr);
return 0;
}
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
* -------------------------------------------------------------------------- */
+#ifndef G_UTILS_COCOA_H
+#define G_UTILS_COCOA_H
+
+
/* fl_xid() from FLTK returns a pointer to NSWindow, but plugins on OS X want a
* pointer to NSView. The function does the hard conversion. */
/* A bug on on OS X seems to misalign plugins' UI. The function takes care of
* fixing the positioning. */
-
+
void cocoa_setWindowSize(void *p, int w, int h);
+
+
+#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * 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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#include <rtmidi/RtMidi.h>
+#include <sndfile.h>
+#include "../deps/rtaudio-mod/RtAudio.h"
+#include "deps.h"
+
+
+using std::string;
+
+
+namespace giada {
+namespace u {
+namespace deps
+{
+string getLibsndfileVersion()
+{
+ char buffer[128];
+ sf_command(NULL, SFC_GET_LIB_VERSION, buffer, sizeof(buffer));
+ return string(buffer);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+string getRtAudioVersion()
+{
+ return RtAudio::getVersion();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+string getRtMidiVersion()
+{
+ return RtMidi::getVersion();
+}
+
+}}}; // giada::u::deps::
\ No newline at end of file
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * 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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#ifndef G_UTILS_DEPS_H
+#define G_UTILS_DEPS_H
+
+
+#include <string>
+
+
+namespace giada {
+namespace u {
+namespace deps
+{
+std::string getLibsndfileVersion();
+std::string getRtAudioVersion();
+std::string getRtMidiVersion();
+}}}; // giada::u::deps::
+
+#endif
\ No newline at end of file
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
#include <cstdarg>
#include <sys/stat.h> // stat (gu_dirExists)
#include <errno.h>
-#include <stdlib.h>
-#include <stdint.h>
+#include <cstdlib>
+#ifdef __APPLE__ // our Clans still doesn't know about cstdint (c++11 stuff)
+ #include <stdint.h>
+#else
+ #include <cstdint>
+#endif
#include <string>
-#include <string.h>
-#include <limits.h>
-#if defined(__APPLE__)
+#include <cstring>
+#include <climits>
+#ifdef __APPLE__
#include <libgen.h> // basename unix
#include <pwd.h> // getpwuid
#endif
{
char buf[PATH_MAX];
#if defined(__WIN32)
- if (_getcwd(buf, PATH_MAX) != NULL)
+ if (_getcwd(buf, PATH_MAX) != nullptr)
#else
- if (getcwd(buf, PATH_MAX) != NULL)
+ if (getcwd(buf, PATH_MAX) != nullptr)
#endif
return buf;
else
#elif defined(__APPLE__)
struct passwd *p = getpwuid(getuid());
- if (p == NULL) {
+ if (p == nullptr) {
gu_log("[gu_getHomePath] unable to fetch user infos\n");
return "";
}
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
* -------------------------------------------------------------------------- */
-#ifndef UTILS_H
-#define UTILS_H
+#ifndef G_UTILS_FS_H
+#define G_UTILS_FS_H
#include <string>
-#include <vector>
-using std::string;
-using std::vector;
-
-
-bool gu_fileExists(const string &path);
-
-bool gu_dirExists(const string &path);
-
-bool gu_isDir(const string &path);
-
-bool gu_isProject(const string &path);
-
-bool gu_mkdir(const string &path);
-
-string gu_getCurrentPath();
-
-string gu_getHomePath();
-
-string gu_basename(const string &s);
-
-string gu_dirname(const string &s);
-
-string gu_getExt(const string &s);
-
-string gu_stripExt(const string &s);
-
-string gu_stripFileUrl(const string &s);
+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);
#endif
*
* Giada - Your Hardcore Loopmachine
*
- * gui_utils
- *
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
* -------------------------------------------------------------------------- */
+#include <string>
+#include <FL/fl_draw.H>
+#if defined(_WIN32)
+ #include "../ext/resource.h"
+#elif defined(__linux__)
+ #include <X11/xpm.h>
+#endif
#include "../core/mixer.h"
-#include "../core/patch_DEPR_.h"
#include "../core/recorder.h"
#include "../core/wave.h"
+#include "../core/clock.h"
#include "../core/pluginHost.h"
#include "../core/channel.h"
#include "../core/conf.h"
#include "../gui/dialogs/gd_warnings.h"
#include "../gui/dialogs/gd_mainWindow.h"
#include "../gui/dialogs/gd_actionEditor.h"
-#include "../gui/elems/ge_window.h"
+#include "../gui/dialogs/window.h"
#include "../gui/elems/mainWindow/mainIO.h"
#include "../gui/elems/mainWindow/mainTimer.h"
#include "../gui/elems/mainWindow/mainTransport.h"
#include "gui.h"
-extern Mixer G_Mixer;
-extern unsigned G_beats;
-extern bool G_audio_status;
-extern Patch_DEPR_ G_patch;
-extern Conf G_conf;
-extern uint32_t G_time;
extern gdMainWindow *G_MainWin;
-#ifdef WITH_VST
-extern PluginHost G_PluginHost;
-#endif
+
+
+using std::string;
+using namespace giada::m;
static int blinker = 0;
void gu_updateControls()
{
- for (unsigned i=0; i<G_Mixer.channels.size(); i++)
- G_Mixer.channels.at(i)->guiChannel->update();
+ for (unsigned i=0; i<mixer::channels.size(); i++)
+ mixer::channels.at(i)->guiChannel->update();
- G_MainWin->mainIO->setOutVol(G_Mixer.outVol);
- G_MainWin->mainIO->setInVol(G_Mixer.inVol);
+ G_MainWin->mainIO->setOutVol(mixer::outVol);
+ G_MainWin->mainIO->setInVol(mixer::inVol);
#ifdef WITH_VST
- G_MainWin->mainIO->setMasterFxOutFull(G_PluginHost.getStack(PluginHost::MASTER_OUT)->size() > 0);
- G_MainWin->mainIO->setMasterFxInFull(G_PluginHost.getStack(PluginHost::MASTER_IN)->size() > 0);
+ G_MainWin->mainIO->setMasterFxOutFull(pluginHost::getStack(pluginHost::MASTER_OUT)->size() > 0);
+ G_MainWin->mainIO->setMasterFxInFull(pluginHost::getStack(pluginHost::MASTER_IN)->size() > 0);
#endif
- G_MainWin->mainTimer->setMeter(G_Mixer.beats, G_Mixer.bars);
- G_MainWin->mainTimer->setBpm(G_Mixer.bpm);
+ G_MainWin->mainTimer->setMeter(clock::getBeats(), clock::getBars());
+ G_MainWin->mainTimer->setBpm(clock::getBpm());
- G_MainWin->mainTransport->updatePlay(G_Mixer.running);
- G_MainWin->mainTransport->updateMetronome(G_Mixer.metronome);
+ G_MainWin->mainTransport->updatePlay(clock::isRunning());
+ G_MainWin->mainTransport->updateMetronome(mixer::metronome);
}
fl_open_display();
Pixmap p, mask;
XpmCreatePixmapFromData(fl_display, DefaultRootWindow(fl_display),
- (char **) giada_icon, &p, &mask, NULL);
+ (char **) giada_icon, &p, &mask, nullptr);
w->icon((char *)p);
#elif defined(_WIN32)
/* -------------------------------------------------------------------------- */
-void gu_openSubWindow(gWindow *parent, gWindow *child, int id)
+void gu_openSubWindow(gdWindow *parent, gdWindow *child, int id)
{
if (parent->hasWindow(id)) {
gu_log("[GU] parent has subwindow with id=%d, deleting\n", id);
/* -------------------------------------------------------------------------- */
-gWindow *gu_getSubwindow(gWindow *parent, int id)
+gdWindow *gu_getSubwindow(gdWindow *parent, int id)
{
if (parent->hasWindow(id))
return parent->getChild(id);
else
- return NULL;
+ return nullptr;
}
/* -------------------------------------------------------------------------- */
+int gu_getStringWidth(const std::string &s)
+{
+ int w = 0;
+ int h = 0;
+ fl_measure(s.c_str(), w, h);
+ return w;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
string gu_removeFltkChars(const string &s)
{
string out = gu_replace(s, "/", "-");
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -------------------------------------------------------------------------- */
-#ifndef GUI_UTILS_H
-#define GUI_UTILS_H
-#include <dirent.h>
-#include <string>
-#include <FL/x.H>
-#include <FL/Fl.H>
-#ifdef __APPLE__
- #include <libgen.h> // in osx, for basename() (but linux?)
-#endif
+#ifndef G_UTILS_GUI_H
+#define G_UTILS_GUI_H
-/* including stuff for the favicon */
-#if defined(_WIN32)
- #include "../ext/resource.h"
-#elif defined(__linux__)
- #include <X11/xpm.h>
-#endif
+#include <string>
-using std::string;
+class Fl_Window;
+class gdWindow;
/* refresh
/* update_win_label
* update the name of the main window */
-void gu_updateMainWinLabel(const string &s);
+void gu_updateMainWinLabel(const std::string &s);
void gu_setFavicon(Fl_Window *w);
-void gu_openSubWindow(class gWindow *parent, gWindow *child, int id);
+void gu_openSubWindow(gdWindow *parent, gdWindow *child, int id);
/* refreshActionEditor
* reload the action editor window by closing and reopening it. It's used
void gu_refreshActionEditor();
-
/* closeAllSubwindows
* close all subwindows attached to mainWin. */
void gu_closeAllSubwindows();
-
/* getSubwindow
- * return a pointer to an open subwindow, otherwise NULL. */
+ * return a pointer to an open subwindow, otherwise nullptr. */
-gWindow *gu_getSubwindow(class gWindow *parent, int id);
+gdWindow *gu_getSubwindow(gdWindow *parent, int id);
/* removeFltkChars
* Strip special chars used by FLTK to split menus into sub-menus. */
-string gu_removeFltkChars(const string &s);
+std::string gu_removeFltkChars(const std::string &s);
+
+int gu_getStringWidth(const std::string &s);
+
#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
* -------------------------------------------------------------------------- */
-#ifndef __LOG_H__
-#define __LOG_H__
+#ifndef G_UTILS_LOG_H
+#define G_UTILS_LOG_H
/* init
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * 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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#include <cmath>
+#include "math.h"
+
+
+float gu_linearToDB(float f)
+{
+ return 20 * std::log10(f);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+float gu_dBtoLinear(float f)
+{
+ return std::pow(10, f/20.0f);
+}
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * 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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#ifndef G_UTILS_MATH_H
+#define G_UTILS_MATH_H
+
+
+float gu_linearToDB(float f);
+
+float gu_dBtoLinear(float f);
+
+
+#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
* -------------------------------------------------------------------------- */
-#include <limits.h>
+#include <climits>
#include <sstream>
#include "string.h"
using std::string;
+using std::vector;
string gu_getRealPath(const string &path)
#if defined(__linux__) || defined(__APPLE__)
- char *buf = realpath(path.c_str(), NULL);
+ char *buf = realpath(path.c_str(), nullptr);
#else // Windows
- char *buf = _fullpath(NULL, path.c_str(), PATH_MAX);
+ char *buf = _fullpath(nullptr, path.c_str(), PATH_MAX);
#endif
string gu_itoa(int i)
{
+ // TODO - use std::to_string -> http://stackoverflow.com/questions/191757/how-to-concatenate-a-stdstring-and-an-int?rq=1
std::stringstream out;
out << i;
return out.str();
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
* -------------------------------------------------------------------------- */
-#ifndef __UTILS_STRING_H__
-#define __UTILS_STRING_H__
+#ifndef G_UTILS_STRING_H
+#define G_UTILS_STRING_H
#include <string>
#include <vector>
-using std::string;
-using std::vector;
+std::string gu_getRealPath(const std::string &path);
+std::string gu_replace(std::string in, const std::string &search,
+ const std::string &replace);
-string gu_getRealPath(const string &path);
+std::string gu_trim(const std::string &s);
-string gu_replace(string in, const string &search, const string &replace);
+// TODO - use std::to_string -> http://stackoverflow.com/questions/191757/how-to-concatenate-a-stdstring-and-an-int?rq=1
+std::string gu_itoa(int i);
-string gu_trim(const string &s);
-
-string gu_itoa(int i);
-
-void gu_split(string in, string sep, vector<string> *v);
+void gu_split(std::string in, std::string sep, std::vector<std::string> *v);
#endif
+++ /dev/null
-/*
- * Catch v1.2.1
- * Generated: 2015-06-30 18:23:27.961086
- * ----------------------------------------------------------
- * This file has been merged from multiple headers. Please don't edit it directly
- * Copyright (c) 2012 Two Blue Cubes Ltd. All rights reserved.
- *
- * Distributed under the Boost Software License, Version 1.0. (See accompanying
- * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
- */
-#ifndef TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED
-#define TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED
-
-#define TWOBLUECUBES_CATCH_HPP_INCLUDED
-
-#ifdef __clang__
-# pragma clang system_header
-#elif defined __GNUC__
-# pragma GCC system_header
-#endif
-
-// #included from: internal/catch_suppress_warnings.h
-
-#define TWOBLUECUBES_CATCH_SUPPRESS_WARNINGS_H_INCLUDED
-
-#ifdef __clang__
-# ifdef __ICC // icpc defines the __clang__ macro
-# pragma warning(push)
-# pragma warning(disable: 161 1682)
-# else // __ICC
-# pragma clang diagnostic ignored "-Wglobal-constructors"
-# pragma clang diagnostic ignored "-Wvariadic-macros"
-# pragma clang diagnostic ignored "-Wc99-extensions"
-# pragma clang diagnostic ignored "-Wunused-variable"
-# pragma clang diagnostic push
-# pragma clang diagnostic ignored "-Wpadded"
-# pragma clang diagnostic ignored "-Wc++98-compat"
-# pragma clang diagnostic ignored "-Wc++98-compat-pedantic"
-# pragma clang diagnostic ignored "-Wswitch-enum"
-# endif
-#elif defined __GNUC__
-# pragma GCC diagnostic ignored "-Wvariadic-macros"
-# pragma GCC diagnostic ignored "-Wunused-variable"
-# pragma GCC diagnostic push
-# pragma GCC diagnostic ignored "-Wpadded"
-#endif
-
-#if defined(CATCH_CONFIG_MAIN) || defined(CATCH_CONFIG_RUNNER)
-# define CATCH_IMPL
-#endif
-
-#ifdef CATCH_IMPL
-# ifndef CLARA_CONFIG_MAIN
-# define CLARA_CONFIG_MAIN_NOT_DEFINED
-# define CLARA_CONFIG_MAIN
-# endif
-#endif
-
-// #included from: internal/catch_notimplemented_exception.h
-#define TWOBLUECUBES_CATCH_NOTIMPLEMENTED_EXCEPTION_H_INCLUDED
-
-// #included from: catch_common.h
-#define TWOBLUECUBES_CATCH_COMMON_H_INCLUDED
-
-#define INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) name##line
-#define INTERNAL_CATCH_UNIQUE_NAME_LINE( name, line ) INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line )
-#define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __LINE__ )
-
-#define INTERNAL_CATCH_STRINGIFY2( expr ) #expr
-#define INTERNAL_CATCH_STRINGIFY( expr ) INTERNAL_CATCH_STRINGIFY2( expr )
-
-#include <sstream>
-#include <stdexcept>
-#include <algorithm>
-
-// #included from: catch_compiler_capabilities.h
-#define TWOBLUECUBES_CATCH_COMPILER_CAPABILITIES_HPP_INCLUDED
-
-// Detect a number of compiler features - mostly C++11/14 conformance - by compiler
-// The following features are defined:
-//
-// CATCH_CONFIG_CPP11_NULLPTR : is nullptr supported?
-// CATCH_CONFIG_CPP11_NOEXCEPT : is noexcept supported?
-// CATCH_CONFIG_CPP11_GENERATED_METHODS : The delete and default keywords for compiler generated methods
-// CATCH_CONFIG_CPP11_IS_ENUM : std::is_enum is supported?
-// CATCH_CONFIG_CPP11_TUPLE : std::tuple is supported
-
-// CATCH_CONFIG_CPP11_OR_GREATER : Is C++11 supported?
-
-// CATCH_CONFIG_VARIADIC_MACROS : are variadic macros supported?
-
-// In general each macro has a _NO_<feature name> form
-// (e.g. CATCH_CONFIG_CPP11_NO_NULLPTR) which disables the feature.
-// Many features, at point of detection, define an _INTERNAL_ macro, so they
-// can be combined, en-mass, with the _NO_ forms later.
-
-// All the C++11 features can be disabled with CATCH_CONFIG_NO_CPP11
-
-#ifdef __clang__
-
-# if __has_feature(cxx_nullptr)
-# define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR
-# endif
-
-# if __has_feature(cxx_noexcept)
-# define CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT
-# endif
-
-#endif // __clang__
-
-////////////////////////////////////////////////////////////////////////////////
-// Borland
-#ifdef __BORLANDC__
-
-#endif // __BORLANDC__
-
-////////////////////////////////////////////////////////////////////////////////
-// EDG
-#ifdef __EDG_VERSION__
-
-#endif // __EDG_VERSION__
-
-////////////////////////////////////////////////////////////////////////////////
-// Digital Mars
-#ifdef __DMC__
-
-#endif // __DMC__
-
-////////////////////////////////////////////////////////////////////////////////
-// GCC
-#ifdef __GNUC__
-
-#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6 && defined(__GXX_EXPERIMENTAL_CXX0X__) )
-# define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR
-#endif
-
-#endif // __GNUC__
-
-////////////////////////////////////////////////////////////////////////////////
-// Visual C++
-#ifdef _MSC_VER
-
-#if (_MSC_VER >= 1600)
-# define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR
-#endif
-
-#if (_MSC_VER >= 1900 ) // (VC++ 13 (VS2015))
-#define CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT
-#define CATCH_INTERNAL_CONFIG_CPP11_GENERATED_METHODS
-#endif
-
-#endif // _MSC_VER
-
-// Use variadic macros if the compiler supports them
-#if ( defined _MSC_VER && _MSC_VER > 1400 && !defined __EDGE__) || \
- ( defined __WAVE__ && __WAVE_HAS_VARIADICS ) || \
- ( defined __GNUC__ && __GNUC__ >= 3 ) || \
- ( !defined __cplusplus && __STDC_VERSION__ >= 199901L || __cplusplus >= 201103L )
-
-#define CATCH_INTERNAL_CONFIG_VARIADIC_MACROS
-
-#endif
-
-////////////////////////////////////////////////////////////////////////////////
-// C++ language feature support
-
-// catch all support for C++11
-#if (__cplusplus >= 201103L)
-
-# define CATCH_CPP11_OR_GREATER
-
-# if !defined(CATCH_INTERNAL_CONFIG_CPP11_NULLPTR)
-# define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR
-# endif
-
-# ifndef CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT
-# define CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT
-# endif
-
-# ifndef CATCH_INTERNAL_CONFIG_CPP11_GENERATED_METHODS
-# define CATCH_INTERNAL_CONFIG_CPP11_GENERATED_METHODS
-# endif
-
-# ifndef CATCH_INTERNAL_CONFIG_CPP11_IS_ENUM
-# define CATCH_INTERNAL_CONFIG_CPP11_IS_ENUM
-# endif
-
-# ifndef CATCH_INTERNAL_CONFIG_CPP11_TUPLE
-# define CATCH_INTERNAL_CONFIG_CPP11_TUPLE
-# endif
-
-# ifndef CATCH_INTERNAL_CONFIG_VARIADIC_MACROS
-# define CATCH_INTERNAL_CONFIG_VARIADIC_MACROS
-# endif
-
-#endif // __cplusplus >= 201103L
-
-// Now set the actual defines based on the above + anything the user has configured
-#if defined(CATCH_INTERNAL_CONFIG_CPP11_NULLPTR) && !defined(CATCH_CONFIG_CPP11_NO_NULLPTR) && !defined(CATCH_CONFIG_CPP11_NULLPTR) && !defined(CATCH_CONFIG_NO_CPP11)
-# define CATCH_CONFIG_CPP11_NULLPTR
-#endif
-#if defined(CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT) && !defined(CATCH_CONFIG_CPP11_NO_NOEXCEPT) && !defined(CATCH_CONFIG_CPP11_NOEXCEPT) && !defined(CATCH_CONFIG_NO_CPP11)
-# define CATCH_CONFIG_CPP11_NOEXCEPT
-#endif
-#if defined(CATCH_INTERNAL_CONFIG_CPP11_GENERATED_METHODS) && !defined(CATCH_CONFIG_CPP11_NO_GENERATED_METHODS) && !defined(CATCH_CONFIG_CPP11_GENERATED_METHODS) && !defined(CATCH_CONFIG_NO_CPP11)
-# define CATCH_CONFIG_CPP11_GENERATED_METHODS
-#endif
-#if defined(CATCH_INTERNAL_CONFIG_CPP11_IS_ENUM) && !defined(CATCH_CONFIG_CPP11_NO_IS_ENUM) && !defined(CATCH_CONFIG_CPP11_IS_ENUM) && !defined(CATCH_CONFIG_NO_CPP11)
-# define CATCH_CONFIG_CPP11_IS_ENUM
-#endif
-#if defined(CATCH_INTERNAL_CONFIG_CPP11_TUPLE) && !defined(CATCH_CONFIG_CPP11_NO_TUPLE) && !defined(CATCH_CONFIG_CPP11_TUPLE) && !defined(CATCH_CONFIG_NO_CPP11)
-# define CATCH_CONFIG_CPP11_TUPLE
-#endif
-#if defined(CATCH_INTERNAL_CONFIG_VARIADIC_MACROS) && !defined(CATCH_CONFIG_NO_VARIADIC_MACROS) && !defined(CATCH_CONFIG_VARIADIC_MACROS)
-#define CATCH_CONFIG_VARIADIC_MACROS
-#endif
-
-// noexcept support:
-#if defined(CATCH_CONFIG_CPP11_NOEXCEPT) && !defined(CATCH_NOEXCEPT)
-# define CATCH_NOEXCEPT noexcept
-# define CATCH_NOEXCEPT_IS(x) noexcept(x)
-#else
-# define CATCH_NOEXCEPT throw()
-# define CATCH_NOEXCEPT_IS(x)
-#endif
-
-namespace Catch {
-
- class NonCopyable {
-#ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS
- NonCopyable( NonCopyable const& ) = delete;
- NonCopyable( NonCopyable && ) = delete;
- NonCopyable& operator = ( NonCopyable const& ) = delete;
- NonCopyable& operator = ( NonCopyable && ) = delete;
-#else
- NonCopyable( NonCopyable const& info );
- NonCopyable& operator = ( NonCopyable const& );
-#endif
-
- protected:
- NonCopyable() {}
- virtual ~NonCopyable();
- };
-
- class SafeBool {
- public:
- typedef void (SafeBool::*type)() const;
-
- static type makeSafe( bool value ) {
- return value ? &SafeBool::trueValue : 0;
- }
- private:
- void trueValue() const {}
- };
-
- template<typename ContainerT>
- inline void deleteAll( ContainerT& container ) {
- typename ContainerT::const_iterator it = container.begin();
- typename ContainerT::const_iterator itEnd = container.end();
- for(; it != itEnd; ++it )
- delete *it;
- }
- template<typename AssociativeContainerT>
- inline void deleteAllValues( AssociativeContainerT& container ) {
- typename AssociativeContainerT::const_iterator it = container.begin();
- typename AssociativeContainerT::const_iterator itEnd = container.end();
- for(; it != itEnd; ++it )
- delete it->second;
- }
-
- bool startsWith( std::string const& s, std::string const& prefix );
- bool endsWith( std::string const& s, std::string const& suffix );
- bool contains( std::string const& s, std::string const& infix );
- void toLowerInPlace( std::string& s );
- std::string toLower( std::string const& s );
- std::string trim( std::string const& str );
- bool replaceInPlace( std::string& str, std::string const& replaceThis, std::string const& withThis );
-
- struct pluralise {
- pluralise( std::size_t count, std::string const& label );
-
- friend std::ostream& operator << ( std::ostream& os, pluralise const& pluraliser );
-
- std::size_t m_count;
- std::string m_label;
- };
-
- struct SourceLineInfo {
-
- SourceLineInfo();
- SourceLineInfo( char const* _file, std::size_t _line );
- SourceLineInfo( SourceLineInfo const& other );
-# ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS
- SourceLineInfo( SourceLineInfo && ) = default;
- SourceLineInfo& operator = ( SourceLineInfo const& ) = default;
- SourceLineInfo& operator = ( SourceLineInfo && ) = default;
-# endif
- bool empty() const;
- bool operator == ( SourceLineInfo const& other ) const;
- bool operator < ( SourceLineInfo const& other ) const;
-
- std::string file;
- std::size_t line;
- };
-
- std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info );
-
- // This is just here to avoid compiler warnings with macro constants and boolean literals
- inline bool isTrue( bool value ){ return value; }
- inline bool alwaysTrue() { return true; }
- inline bool alwaysFalse() { return false; }
-
- void throwLogicError( std::string const& message, SourceLineInfo const& locationInfo );
-
- // Use this in variadic streaming macros to allow
- // >> +StreamEndStop
- // as well as
- // >> stuff +StreamEndStop
- struct StreamEndStop {
- std::string operator+() {
- return std::string();
- }
- };
- template<typename T>
- T const& operator + ( T const& value, StreamEndStop ) {
- return value;
- }
-}
-
-#define CATCH_INTERNAL_LINEINFO ::Catch::SourceLineInfo( __FILE__, static_cast<std::size_t>( __LINE__ ) )
-#define CATCH_INTERNAL_ERROR( msg ) ::Catch::throwLogicError( msg, CATCH_INTERNAL_LINEINFO );
-
-#include <ostream>
-
-namespace Catch {
-
- class NotImplementedException : public std::exception
- {
- public:
- NotImplementedException( SourceLineInfo const& lineInfo );
- NotImplementedException( NotImplementedException const& ) {}
-
- virtual ~NotImplementedException() CATCH_NOEXCEPT {}
-
- virtual const char* what() const CATCH_NOEXCEPT;
-
- private:
- std::string m_what;
- SourceLineInfo m_lineInfo;
- };
-
-} // end namespace Catch
-
-///////////////////////////////////////////////////////////////////////////////
-#define CATCH_NOT_IMPLEMENTED throw Catch::NotImplementedException( CATCH_INTERNAL_LINEINFO )
-
-// #included from: internal/catch_context.h
-#define TWOBLUECUBES_CATCH_CONTEXT_H_INCLUDED
-
-// #included from: catch_interfaces_generators.h
-#define TWOBLUECUBES_CATCH_INTERFACES_GENERATORS_H_INCLUDED
-
-#include <string>
-
-namespace Catch {
-
- struct IGeneratorInfo {
- virtual ~IGeneratorInfo();
- virtual bool moveNext() = 0;
- virtual std::size_t getCurrentIndex() const = 0;
- };
-
- struct IGeneratorsForTest {
- virtual ~IGeneratorsForTest();
-
- virtual IGeneratorInfo& getGeneratorInfo( std::string const& fileInfo, std::size_t size ) = 0;
- virtual bool moveNext() = 0;
- };
-
- IGeneratorsForTest* createGeneratorsForTest();
-
-} // end namespace Catch
-
-// #included from: catch_ptr.hpp
-#define TWOBLUECUBES_CATCH_PTR_HPP_INCLUDED
-
-#ifdef __clang__
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wpadded"
-#endif
-
-namespace Catch {
-
- // An intrusive reference counting smart pointer.
- // T must implement addRef() and release() methods
- // typically implementing the IShared interface
- template<typename T>
- class Ptr {
- public:
- Ptr() : m_p( NULL ){}
- Ptr( T* p ) : m_p( p ){
- if( m_p )
- m_p->addRef();
- }
- Ptr( Ptr const& other ) : m_p( other.m_p ){
- if( m_p )
- m_p->addRef();
- }
- ~Ptr(){
- if( m_p )
- m_p->release();
- }
- void reset() {
- if( m_p )
- m_p->release();
- m_p = NULL;
- }
- Ptr& operator = ( T* p ){
- Ptr temp( p );
- swap( temp );
- return *this;
- }
- Ptr& operator = ( Ptr const& other ){
- Ptr temp( other );
- swap( temp );
- return *this;
- }
- void swap( Ptr& other ) { std::swap( m_p, other.m_p ); }
- T* get() { return m_p; }
- const T* get() const{ return m_p; }
- T& operator*() const { return *m_p; }
- T* operator->() const { return m_p; }
- bool operator !() const { return m_p == NULL; }
- operator SafeBool::type() const { return SafeBool::makeSafe( m_p != NULL ); }
-
- private:
- T* m_p;
- };
-
- struct IShared : NonCopyable {
- virtual ~IShared();
- virtual void addRef() const = 0;
- virtual void release() const = 0;
- };
-
- template<typename T = IShared>
- struct SharedImpl : T {
-
- SharedImpl() : m_rc( 0 ){}
-
- virtual void addRef() const {
- ++m_rc;
- }
- virtual void release() const {
- if( --m_rc == 0 )
- delete this;
- }
-
- mutable unsigned int m_rc;
- };
-
-} // end namespace Catch
-
-#ifdef __clang__
-#pragma clang diagnostic pop
-#endif
-
-#include <memory>
-#include <vector>
-#include <stdlib.h>
-
-namespace Catch {
-
- class TestCase;
- class Stream;
- struct IResultCapture;
- struct IRunner;
- struct IGeneratorsForTest;
- struct IConfig;
-
- struct IContext
- {
- virtual ~IContext();
-
- virtual IResultCapture* getResultCapture() = 0;
- virtual IRunner* getRunner() = 0;
- virtual size_t getGeneratorIndex( std::string const& fileInfo, size_t totalSize ) = 0;
- virtual bool advanceGeneratorsForCurrentTest() = 0;
- virtual Ptr<IConfig const> getConfig() const = 0;
- };
-
- struct IMutableContext : IContext
- {
- virtual ~IMutableContext();
- virtual void setResultCapture( IResultCapture* resultCapture ) = 0;
- virtual void setRunner( IRunner* runner ) = 0;
- virtual void setConfig( Ptr<IConfig const> const& config ) = 0;
- };
-
- IContext& getCurrentContext();
- IMutableContext& getCurrentMutableContext();
- void cleanUpContext();
- Stream createStream( std::string const& streamName );
-
-}
-
-// #included from: internal/catch_test_registry.hpp
-#define TWOBLUECUBES_CATCH_TEST_REGISTRY_HPP_INCLUDED
-
-// #included from: catch_interfaces_testcase.h
-#define TWOBLUECUBES_CATCH_INTERFACES_TESTCASE_H_INCLUDED
-
-#include <vector>
-
-namespace Catch {
-
- class TestSpec;
-
- struct ITestCase : IShared {
- virtual void invoke () const = 0;
- protected:
- virtual ~ITestCase();
- };
-
- class TestCase;
- struct IConfig;
-
- struct ITestCaseRegistry {
- virtual ~ITestCaseRegistry();
- virtual std::vector<TestCase> const& getAllTests() const = 0;
- virtual void getFilteredTests( TestSpec const& testSpec, IConfig const& config, std::vector<TestCase>& matchingTestCases, bool negated = false ) const = 0;
-
- };
-}
-
-namespace Catch {
-
-template<typename C>
-class MethodTestCase : public SharedImpl<ITestCase> {
-
-public:
- MethodTestCase( void (C::*method)() ) : m_method( method ) {}
-
- virtual void invoke() const {
- C obj;
- (obj.*m_method)();
- }
-
-private:
- virtual ~MethodTestCase() {}
-
- void (C::*m_method)();
-};
-
-typedef void(*TestFunction)();
-
-struct NameAndDesc {
- NameAndDesc( const char* _name = "", const char* _description= "" )
- : name( _name ), description( _description )
- {}
-
- const char* name;
- const char* description;
-};
-
-struct AutoReg {
-
- AutoReg( TestFunction function,
- SourceLineInfo const& lineInfo,
- NameAndDesc const& nameAndDesc );
-
- template<typename C>
- AutoReg( void (C::*method)(),
- char const* className,
- NameAndDesc const& nameAndDesc,
- SourceLineInfo const& lineInfo ) {
- registerTestCase( new MethodTestCase<C>( method ),
- className,
- nameAndDesc,
- lineInfo );
- }
-
- void registerTestCase( ITestCase* testCase,
- char const* className,
- NameAndDesc const& nameAndDesc,
- SourceLineInfo const& lineInfo );
-
- ~AutoReg();
-
-private:
- AutoReg( AutoReg const& );
- void operator= ( AutoReg const& );
-};
-
-} // end namespace Catch
-
-#ifdef CATCH_CONFIG_VARIADIC_MACROS
- ///////////////////////////////////////////////////////////////////////////////
- #define INTERNAL_CATCH_TESTCASE( ... ) \
- static void INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )(); \
- namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( __VA_ARGS__ ) ); }\
- static void INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )()
-
- ///////////////////////////////////////////////////////////////////////////////
- #define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, ... ) \
- namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &QualifiedMethod, "&" #QualifiedMethod, Catch::NameAndDesc( __VA_ARGS__ ), CATCH_INTERNAL_LINEINFO ); }
-
- ///////////////////////////////////////////////////////////////////////////////
- #define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, ... )\
- namespace{ \
- struct INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ) : ClassName{ \
- void test(); \
- }; \
- Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( &INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )::test, #ClassName, Catch::NameAndDesc( __VA_ARGS__ ), CATCH_INTERNAL_LINEINFO ); \
- } \
- void INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )::test()
-
-#else
- ///////////////////////////////////////////////////////////////////////////////
- #define INTERNAL_CATCH_TESTCASE( Name, Desc ) \
- static void INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )(); \
- namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( Name, Desc ) ); }\
- static void INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )()
-
- ///////////////////////////////////////////////////////////////////////////////
- #define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, Name, Desc ) \
- namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &QualifiedMethod, "&" #QualifiedMethod, Catch::NameAndDesc( Name, Desc ), CATCH_INTERNAL_LINEINFO ); }
-
- ///////////////////////////////////////////////////////////////////////////////
- #define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, TestName, Desc )\
- namespace{ \
- struct INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ) : ClassName{ \
- void test(); \
- }; \
- Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( &INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )::test, #ClassName, Catch::NameAndDesc( TestName, Desc ), CATCH_INTERNAL_LINEINFO ); \
- } \
- void INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )::test()
-
-#endif
-
-// #included from: internal/catch_capture.hpp
-#define TWOBLUECUBES_CATCH_CAPTURE_HPP_INCLUDED
-
-// #included from: catch_result_builder.h
-#define TWOBLUECUBES_CATCH_RESULT_BUILDER_H_INCLUDED
-
-// #included from: catch_result_type.h
-#define TWOBLUECUBES_CATCH_RESULT_TYPE_H_INCLUDED
-
-namespace Catch {
-
- // ResultWas::OfType enum
- struct ResultWas { enum OfType {
- Unknown = -1,
- Ok = 0,
- Info = 1,
- Warning = 2,
-
- FailureBit = 0x10,
-
- ExpressionFailed = FailureBit | 1,
- ExplicitFailure = FailureBit | 2,
-
- Exception = 0x100 | FailureBit,
-
- ThrewException = Exception | 1,
- DidntThrowException = Exception | 2,
-
- FatalErrorCondition = 0x200 | FailureBit
-
- }; };
-
- inline bool isOk( ResultWas::OfType resultType ) {
- return ( resultType & ResultWas::FailureBit ) == 0;
- }
- inline bool isJustInfo( int flags ) {
- return flags == ResultWas::Info;
- }
-
- // ResultDisposition::Flags enum
- struct ResultDisposition { enum Flags {
- Normal = 0x01,
-
- ContinueOnFailure = 0x02, // Failures fail test, but execution continues
- FalseTest = 0x04, // Prefix expression with !
- SuppressFail = 0x08 // Failures are reported but do not fail the test
- }; };
-
- inline ResultDisposition::Flags operator | ( ResultDisposition::Flags lhs, ResultDisposition::Flags rhs ) {
- return static_cast<ResultDisposition::Flags>( static_cast<int>( lhs ) | static_cast<int>( rhs ) );
- }
-
- inline bool shouldContinueOnFailure( int flags ) { return ( flags & ResultDisposition::ContinueOnFailure ) != 0; }
- inline bool isFalseTest( int flags ) { return ( flags & ResultDisposition::FalseTest ) != 0; }
- inline bool shouldSuppressFailure( int flags ) { return ( flags & ResultDisposition::SuppressFail ) != 0; }
-
-} // end namespace Catch
-
-// #included from: catch_assertionresult.h
-#define TWOBLUECUBES_CATCH_ASSERTIONRESULT_H_INCLUDED
-
-#include <string>
-
-namespace Catch {
-
- struct AssertionInfo
- {
- AssertionInfo() {}
- AssertionInfo( std::string const& _macroName,
- SourceLineInfo const& _lineInfo,
- std::string const& _capturedExpression,
- ResultDisposition::Flags _resultDisposition );
-
- std::string macroName;
- SourceLineInfo lineInfo;
- std::string capturedExpression;
- ResultDisposition::Flags resultDisposition;
- };
-
- struct AssertionResultData
- {
- AssertionResultData() : resultType( ResultWas::Unknown ) {}
-
- std::string reconstructedExpression;
- std::string message;
- ResultWas::OfType resultType;
- };
-
- class AssertionResult {
- public:
- AssertionResult();
- AssertionResult( AssertionInfo const& info, AssertionResultData const& data );
- ~AssertionResult();
-# ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS
- AssertionResult( AssertionResult const& ) = default;
- AssertionResult( AssertionResult && ) = default;
- AssertionResult& operator = ( AssertionResult const& ) = default;
- AssertionResult& operator = ( AssertionResult && ) = default;
-# endif
-
- bool isOk() const;
- bool succeeded() const;
- ResultWas::OfType getResultType() const;
- bool hasExpression() const;
- bool hasMessage() const;
- std::string getExpression() const;
- std::string getExpressionInMacro() const;
- bool hasExpandedExpression() const;
- std::string getExpandedExpression() const;
- std::string getMessage() const;
- SourceLineInfo getSourceInfo() const;
- std::string getTestMacroName() const;
-
- protected:
- AssertionInfo m_info;
- AssertionResultData m_resultData;
- };
-
-} // end namespace Catch
-
-namespace Catch {
-
- struct TestFailureException{};
-
- template<typename T> class ExpressionLhs;
-
- struct STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison;
-
- struct CopyableStream {
- CopyableStream() {}
- CopyableStream( CopyableStream const& other ) {
- oss << other.oss.str();
- }
- CopyableStream& operator=( CopyableStream const& other ) {
- oss.str("");
- oss << other.oss.str();
- return *this;
- }
- std::ostringstream oss;
- };
-
- class ResultBuilder {
- public:
- ResultBuilder( char const* macroName,
- SourceLineInfo const& lineInfo,
- char const* capturedExpression,
- ResultDisposition::Flags resultDisposition );
-
- template<typename T>
- ExpressionLhs<T const&> operator <= ( T const& operand );
- ExpressionLhs<bool> operator <= ( bool value );
-
- template<typename T>
- ResultBuilder& operator << ( T const& value ) {
- m_stream.oss << value;
- return *this;
- }
-
- template<typename RhsT> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator && ( RhsT const& );
- template<typename RhsT> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator || ( RhsT const& );
-
- ResultBuilder& setResultType( ResultWas::OfType result );
- ResultBuilder& setResultType( bool result );
- ResultBuilder& setLhs( std::string const& lhs );
- ResultBuilder& setRhs( std::string const& rhs );
- ResultBuilder& setOp( std::string const& op );
-
- void endExpression();
-
- std::string reconstructExpression() const;
- AssertionResult build() const;
-
- void useActiveException( ResultDisposition::Flags resultDisposition = ResultDisposition::Normal );
- void captureResult( ResultWas::OfType resultType );
- void captureExpression();
- void react();
- bool shouldDebugBreak() const;
- bool allowThrows() const;
-
- private:
- AssertionInfo m_assertionInfo;
- AssertionResultData m_data;
- struct ExprComponents {
- ExprComponents() : testFalse( false ) {}
- bool testFalse;
- std::string lhs, rhs, op;
- } m_exprComponents;
- CopyableStream m_stream;
-
- bool m_shouldDebugBreak;
- bool m_shouldThrow;
- };
-
-} // namespace Catch
-
-// Include after due to circular dependency:
-// #included from: catch_expression_lhs.hpp
-#define TWOBLUECUBES_CATCH_EXPRESSION_LHS_HPP_INCLUDED
-
-// #included from: catch_evaluate.hpp
-#define TWOBLUECUBES_CATCH_EVALUATE_HPP_INCLUDED
-
-#ifdef _MSC_VER
-#pragma warning(push)
-#pragma warning(disable:4389) // '==' : signed/unsigned mismatch
-#endif
-
-#include <cstddef>
-
-namespace Catch {
-namespace Internal {
-
- enum Operator {
- IsEqualTo,
- IsNotEqualTo,
- IsLessThan,
- IsGreaterThan,
- IsLessThanOrEqualTo,
- IsGreaterThanOrEqualTo
- };
-
- template<Operator Op> struct OperatorTraits { static const char* getName(){ return "*error*"; } };
- template<> struct OperatorTraits<IsEqualTo> { static const char* getName(){ return "=="; } };
- template<> struct OperatorTraits<IsNotEqualTo> { static const char* getName(){ return "!="; } };
- template<> struct OperatorTraits<IsLessThan> { static const char* getName(){ return "<"; } };
- template<> struct OperatorTraits<IsGreaterThan> { static const char* getName(){ return ">"; } };
- template<> struct OperatorTraits<IsLessThanOrEqualTo> { static const char* getName(){ return "<="; } };
- template<> struct OperatorTraits<IsGreaterThanOrEqualTo>{ static const char* getName(){ return ">="; } };
-
- template<typename T>
- inline T& opCast(T const& t) { return const_cast<T&>(t); }
-
-// nullptr_t support based on pull request #154 from Konstantin Baumann
-#ifdef CATCH_CONFIG_CPP11_NULLPTR
- inline std::nullptr_t opCast(std::nullptr_t) { return nullptr; }
-#endif // CATCH_CONFIG_CPP11_NULLPTR
-
- // So the compare overloads can be operator agnostic we convey the operator as a template
- // enum, which is used to specialise an Evaluator for doing the comparison.
- template<typename T1, typename T2, Operator Op>
- class Evaluator{};
-
- template<typename T1, typename T2>
- struct Evaluator<T1, T2, IsEqualTo> {
- static bool evaluate( T1 const& lhs, T2 const& rhs) {
- return opCast( lhs ) == opCast( rhs );
- }
- };
- template<typename T1, typename T2>
- struct Evaluator<T1, T2, IsNotEqualTo> {
- static bool evaluate( T1 const& lhs, T2 const& rhs ) {
- return opCast( lhs ) != opCast( rhs );
- }
- };
- template<typename T1, typename T2>
- struct Evaluator<T1, T2, IsLessThan> {
- static bool evaluate( T1 const& lhs, T2 const& rhs ) {
- return opCast( lhs ) < opCast( rhs );
- }
- };
- template<typename T1, typename T2>
- struct Evaluator<T1, T2, IsGreaterThan> {
- static bool evaluate( T1 const& lhs, T2 const& rhs ) {
- return opCast( lhs ) > opCast( rhs );
- }
- };
- template<typename T1, typename T2>
- struct Evaluator<T1, T2, IsGreaterThanOrEqualTo> {
- static bool evaluate( T1 const& lhs, T2 const& rhs ) {
- return opCast( lhs ) >= opCast( rhs );
- }
- };
- template<typename T1, typename T2>
- struct Evaluator<T1, T2, IsLessThanOrEqualTo> {
- static bool evaluate( T1 const& lhs, T2 const& rhs ) {
- return opCast( lhs ) <= opCast( rhs );
- }
- };
-
- template<Operator Op, typename T1, typename T2>
- bool applyEvaluator( T1 const& lhs, T2 const& rhs ) {
- return Evaluator<T1, T2, Op>::evaluate( lhs, rhs );
- }
-
- // This level of indirection allows us to specialise for integer types
- // to avoid signed/ unsigned warnings
-
- // "base" overload
- template<Operator Op, typename T1, typename T2>
- bool compare( T1 const& lhs, T2 const& rhs ) {
- return Evaluator<T1, T2, Op>::evaluate( lhs, rhs );
- }
-
- // unsigned X to int
- template<Operator Op> bool compare( unsigned int lhs, int rhs ) {
- return applyEvaluator<Op>( lhs, static_cast<unsigned int>( rhs ) );
- }
- template<Operator Op> bool compare( unsigned long lhs, int rhs ) {
- return applyEvaluator<Op>( lhs, static_cast<unsigned int>( rhs ) );
- }
- template<Operator Op> bool compare( unsigned char lhs, int rhs ) {
- return applyEvaluator<Op>( lhs, static_cast<unsigned int>( rhs ) );
- }
-
- // unsigned X to long
- template<Operator Op> bool compare( unsigned int lhs, long rhs ) {
- return applyEvaluator<Op>( lhs, static_cast<unsigned long>( rhs ) );
- }
- template<Operator Op> bool compare( unsigned long lhs, long rhs ) {
- return applyEvaluator<Op>( lhs, static_cast<unsigned long>( rhs ) );
- }
- template<Operator Op> bool compare( unsigned char lhs, long rhs ) {
- return applyEvaluator<Op>( lhs, static_cast<unsigned long>( rhs ) );
- }
-
- // int to unsigned X
- template<Operator Op> bool compare( int lhs, unsigned int rhs ) {
- return applyEvaluator<Op>( static_cast<unsigned int>( lhs ), rhs );
- }
- template<Operator Op> bool compare( int lhs, unsigned long rhs ) {
- return applyEvaluator<Op>( static_cast<unsigned int>( lhs ), rhs );
- }
- template<Operator Op> bool compare( int lhs, unsigned char rhs ) {
- return applyEvaluator<Op>( static_cast<unsigned int>( lhs ), rhs );
- }
-
- // long to unsigned X
- template<Operator Op> bool compare( long lhs, unsigned int rhs ) {
- return applyEvaluator<Op>( static_cast<unsigned long>( lhs ), rhs );
- }
- template<Operator Op> bool compare( long lhs, unsigned long rhs ) {
- return applyEvaluator<Op>( static_cast<unsigned long>( lhs ), rhs );
- }
- template<Operator Op> bool compare( long lhs, unsigned char rhs ) {
- return applyEvaluator<Op>( static_cast<unsigned long>( lhs ), rhs );
- }
-
- // pointer to long (when comparing against NULL)
- template<Operator Op, typename T> bool compare( long lhs, T* rhs ) {
- return Evaluator<T*, T*, Op>::evaluate( reinterpret_cast<T*>( lhs ), rhs );
- }
- template<Operator Op, typename T> bool compare( T* lhs, long rhs ) {
- return Evaluator<T*, T*, Op>::evaluate( lhs, reinterpret_cast<T*>( rhs ) );
- }
-
- // pointer to int (when comparing against NULL)
- template<Operator Op, typename T> bool compare( int lhs, T* rhs ) {
- return Evaluator<T*, T*, Op>::evaluate( reinterpret_cast<T*>( lhs ), rhs );
- }
- template<Operator Op, typename T> bool compare( T* lhs, int rhs ) {
- return Evaluator<T*, T*, Op>::evaluate( lhs, reinterpret_cast<T*>( rhs ) );
- }
-
-#ifdef CATCH_CONFIG_CPP11_NULLPTR
- // pointer to nullptr_t (when comparing against nullptr)
- template<Operator Op, typename T> bool compare( std::nullptr_t, T* rhs ) {
- return Evaluator<T*, T*, Op>::evaluate( NULL, rhs );
- }
- template<Operator Op, typename T> bool compare( T* lhs, std::nullptr_t ) {
- return Evaluator<T*, T*, Op>::evaluate( lhs, NULL );
- }
-#endif // CATCH_CONFIG_CPP11_NULLPTR
-
-} // end of namespace Internal
-} // end of namespace Catch
-
-#ifdef _MSC_VER
-#pragma warning(pop)
-#endif
-
-// #included from: catch_tostring.h
-#define TWOBLUECUBES_CATCH_TOSTRING_H_INCLUDED
-
-#include <sstream>
-#include <iomanip>
-#include <limits>
-#include <vector>
-#include <cstddef>
-
-#ifdef __OBJC__
-// #included from: catch_objc_arc.hpp
-#define TWOBLUECUBES_CATCH_OBJC_ARC_HPP_INCLUDED
-
-#import <Foundation/Foundation.h>
-
-#ifdef __has_feature
-#define CATCH_ARC_ENABLED __has_feature(objc_arc)
-#else
-#define CATCH_ARC_ENABLED 0
-#endif
-
-void arcSafeRelease( NSObject* obj );
-id performOptionalSelector( id obj, SEL sel );
-
-#if !CATCH_ARC_ENABLED
-inline void arcSafeRelease( NSObject* obj ) {
- [obj release];
-}
-inline id performOptionalSelector( id obj, SEL sel ) {
- if( [obj respondsToSelector: sel] )
- return [obj performSelector: sel];
- return nil;
-}
-#define CATCH_UNSAFE_UNRETAINED
-#define CATCH_ARC_STRONG
-#else
-inline void arcSafeRelease( NSObject* ){}
-inline id performOptionalSelector( id obj, SEL sel ) {
-#ifdef __clang__
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
-#endif
- if( [obj respondsToSelector: sel] )
- return [obj performSelector: sel];
-#ifdef __clang__
-#pragma clang diagnostic pop
-#endif
- return nil;
-}
-#define CATCH_UNSAFE_UNRETAINED __unsafe_unretained
-#define CATCH_ARC_STRONG __strong
-#endif
-
-#endif
-
-#ifdef CATCH_CONFIG_CPP11_TUPLE
-#include <tuple>
-#endif
-
-#ifdef CATCH_CONFIG_CPP11_IS_ENUM
-#include <type_traits>
-#endif
-
-namespace Catch {
-
-// Why we're here.
-template<typename T>
-std::string toString( T const& value );
-
-// Built in overloads
-
-std::string toString( std::string const& value );
-std::string toString( std::wstring const& value );
-std::string toString( const char* const value );
-std::string toString( char* const value );
-std::string toString( const wchar_t* const value );
-std::string toString( wchar_t* const value );
-std::string toString( int value );
-std::string toString( unsigned long value );
-std::string toString( unsigned int value );
-std::string toString( const double value );
-std::string toString( const float value );
-std::string toString( bool value );
-std::string toString( char value );
-std::string toString( signed char value );
-std::string toString( unsigned char value );
-
-#ifdef CATCH_CONFIG_CPP11_NULLPTR
-std::string toString( std::nullptr_t );
-#endif
-
-#ifdef __OBJC__
- std::string toString( NSString const * const& nsstring );
- std::string toString( NSString * CATCH_ARC_STRONG const& nsstring );
- std::string toString( NSObject* const& nsObject );
-#endif
-
-namespace Detail {
-
- extern std::string unprintableString;
-
- struct BorgType {
- template<typename T> BorgType( T const& );
- };
-
- struct TrueType { char sizer[1]; };
- struct FalseType { char sizer[2]; };
-
- TrueType& testStreamable( std::ostream& );
- FalseType testStreamable( FalseType );
-
- FalseType operator<<( std::ostream const&, BorgType const& );
-
- template<typename T>
- struct IsStreamInsertable {
- static std::ostream &s;
- static T const&t;
- enum { value = sizeof( testStreamable(s << t) ) == sizeof( TrueType ) };
- };
-
-#if defined(CATCH_CONFIG_CPP11_IS_ENUM)
- template<typename T,
- bool IsEnum = std::is_enum<T>::value
- >
- struct EnumStringMaker
- {
- static std::string convert( T const& ) { return unprintableString; }
- };
-
- template<typename T>
- struct EnumStringMaker<T,true>
- {
- static std::string convert( T const& v )
- {
- return ::Catch::toString(
- static_cast<typename std::underlying_type<T>::type>(v)
- );
- }
- };
-#endif
- template<bool C>
- struct StringMakerBase {
-#if defined(CATCH_CONFIG_CPP11_IS_ENUM)
- template<typename T>
- static std::string convert( T const& v )
- {
- return EnumStringMaker<T>::convert( v );
- }
-#else
- template<typename T>
- static std::string convert( T const& ) { return unprintableString; }
-#endif
- };
-
- template<>
- struct StringMakerBase<true> {
- template<typename T>
- static std::string convert( T const& _value ) {
- std::ostringstream oss;
- oss << _value;
- return oss.str();
- }
- };
-
- std::string rawMemoryToString( const void *object, std::size_t size );
-
- template<typename T>
- inline std::string rawMemoryToString( const T& object ) {
- return rawMemoryToString( &object, sizeof(object) );
- }
-
-} // end namespace Detail
-
-template<typename T>
-struct StringMaker :
- Detail::StringMakerBase<Detail::IsStreamInsertable<T>::value> {};
-
-template<typename T>
-struct StringMaker<T*> {
- template<typename U>
- static std::string convert( U* p ) {
- if( !p )
- return INTERNAL_CATCH_STRINGIFY( NULL );
- else
- return Detail::rawMemoryToString( p );
- }
-};
-
-template<typename R, typename C>
-struct StringMaker<R C::*> {
- static std::string convert( R C::* p ) {
- if( !p )
- return INTERNAL_CATCH_STRINGIFY( NULL );
- else
- return Detail::rawMemoryToString( p );
- }
-};
-
-namespace Detail {
- template<typename InputIterator>
- std::string rangeToString( InputIterator first, InputIterator last );
-}
-
-//template<typename T, typename Allocator>
-//struct StringMaker<std::vector<T, Allocator> > {
-// static std::string convert( std::vector<T,Allocator> const& v ) {
-// return Detail::rangeToString( v.begin(), v.end() );
-// }
-//};
-
-template<typename T, typename Allocator>
-std::string toString( std::vector<T,Allocator> const& v ) {
- return Detail::rangeToString( v.begin(), v.end() );
-}
-
-#ifdef CATCH_CONFIG_CPP11_TUPLE
-
-// toString for tuples
-namespace TupleDetail {
- template<
- typename Tuple,
- std::size_t N = 0,
- bool = (N < std::tuple_size<Tuple>::value)
- >
- struct ElementPrinter {
- static void print( const Tuple& tuple, std::ostream& os )
- {
- os << ( N ? ", " : " " )
- << Catch::toString(std::get<N>(tuple));
- ElementPrinter<Tuple,N+1>::print(tuple,os);
- }
- };
-
- template<
- typename Tuple,
- std::size_t N
- >
- struct ElementPrinter<Tuple,N,false> {
- static void print( const Tuple&, std::ostream& ) {}
- };
-
-}
-
-template<typename ...Types>
-struct StringMaker<std::tuple<Types...>> {
-
- static std::string convert( const std::tuple<Types...>& tuple )
- {
- std::ostringstream os;
- os << '{';
- TupleDetail::ElementPrinter<std::tuple<Types...>>::print( tuple, os );
- os << " }";
- return os.str();
- }
-};
-#endif // CATCH_CONFIG_CPP11_TUPLE
-
-namespace Detail {
- template<typename T>
- std::string makeString( T const& value ) {
- return StringMaker<T>::convert( value );
- }
-} // end namespace Detail
-
-/// \brief converts any type to a string
-///
-/// The default template forwards on to ostringstream - except when an
-/// ostringstream overload does not exist - in which case it attempts to detect
-/// that and writes {?}.
-/// Overload (not specialise) this template for custom typs that you don't want
-/// to provide an ostream overload for.
-template<typename T>
-std::string toString( T const& value ) {
- return StringMaker<T>::convert( value );
-}
-
- namespace Detail {
- template<typename InputIterator>
- std::string rangeToString( InputIterator first, InputIterator last ) {
- std::ostringstream oss;
- oss << "{ ";
- if( first != last ) {
- oss << Catch::toString( *first );
- for( ++first ; first != last ; ++first )
- oss << ", " << Catch::toString( *first );
- }
- oss << " }";
- return oss.str();
- }
-}
-
-} // end namespace Catch
-
-namespace Catch {
-
-// Wraps the LHS of an expression and captures the operator and RHS (if any) -
-// wrapping them all in a ResultBuilder object
-template<typename T>
-class ExpressionLhs {
- ExpressionLhs& operator = ( ExpressionLhs const& );
-# ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS
- ExpressionLhs& operator = ( ExpressionLhs && ) = delete;
-# endif
-
-public:
- ExpressionLhs( ResultBuilder& rb, T lhs ) : m_rb( rb ), m_lhs( lhs ) {}
-# ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS
- ExpressionLhs( ExpressionLhs const& ) = default;
- ExpressionLhs( ExpressionLhs && ) = default;
-# endif
-
- template<typename RhsT>
- ResultBuilder& operator == ( RhsT const& rhs ) {
- return captureExpression<Internal::IsEqualTo>( rhs );
- }
-
- template<typename RhsT>
- ResultBuilder& operator != ( RhsT const& rhs ) {
- return captureExpression<Internal::IsNotEqualTo>( rhs );
- }
-
- template<typename RhsT>
- ResultBuilder& operator < ( RhsT const& rhs ) {
- return captureExpression<Internal::IsLessThan>( rhs );
- }
-
- template<typename RhsT>
- ResultBuilder& operator > ( RhsT const& rhs ) {
- return captureExpression<Internal::IsGreaterThan>( rhs );
- }
-
- template<typename RhsT>
- ResultBuilder& operator <= ( RhsT const& rhs ) {
- return captureExpression<Internal::IsLessThanOrEqualTo>( rhs );
- }
-
- template<typename RhsT>
- ResultBuilder& operator >= ( RhsT const& rhs ) {
- return captureExpression<Internal::IsGreaterThanOrEqualTo>( rhs );
- }
-
- ResultBuilder& operator == ( bool rhs ) {
- return captureExpression<Internal::IsEqualTo>( rhs );
- }
-
- ResultBuilder& operator != ( bool rhs ) {
- return captureExpression<Internal::IsNotEqualTo>( rhs );
- }
-
- void endExpression() {
- bool value = m_lhs ? true : false;
- m_rb
- .setLhs( Catch::toString( value ) )
- .setResultType( value )
- .endExpression();
- }
-
- // Only simple binary expressions are allowed on the LHS.
- // If more complex compositions are required then place the sub expression in parentheses
- template<typename RhsT> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator + ( RhsT const& );
- template<typename RhsT> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator - ( RhsT const& );
- template<typename RhsT> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator / ( RhsT const& );
- template<typename RhsT> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator * ( RhsT const& );
- template<typename RhsT> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator && ( RhsT const& );
- template<typename RhsT> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator || ( RhsT const& );
-
-private:
- template<Internal::Operator Op, typename RhsT>
- ResultBuilder& captureExpression( RhsT const& rhs ) {
- return m_rb
- .setResultType( Internal::compare<Op>( m_lhs, rhs ) )
- .setLhs( Catch::toString( m_lhs ) )
- .setRhs( Catch::toString( rhs ) )
- .setOp( Internal::OperatorTraits<Op>::getName() );
- }
-
-private:
- ResultBuilder& m_rb;
- T m_lhs;
-};
-
-} // end namespace Catch
-
-
-namespace Catch {
-
- template<typename T>
- inline ExpressionLhs<T const&> ResultBuilder::operator <= ( T const& operand ) {
- return ExpressionLhs<T const&>( *this, operand );
- }
-
- inline ExpressionLhs<bool> ResultBuilder::operator <= ( bool value ) {
- return ExpressionLhs<bool>( *this, value );
- }
-
-} // namespace Catch
-
-// #included from: catch_message.h
-#define TWOBLUECUBES_CATCH_MESSAGE_H_INCLUDED
-
-#include <string>
-
-namespace Catch {
-
- struct MessageInfo {
- MessageInfo( std::string const& _macroName,
- SourceLineInfo const& _lineInfo,
- ResultWas::OfType _type );
-
- std::string macroName;
- SourceLineInfo lineInfo;
- ResultWas::OfType type;
- std::string message;
- unsigned int sequence;
-
- bool operator == ( MessageInfo const& other ) const {
- return sequence == other.sequence;
- }
- bool operator < ( MessageInfo const& other ) const {
- return sequence < other.sequence;
- }
- private:
- static unsigned int globalCount;
- };
-
- struct MessageBuilder {
- MessageBuilder( std::string const& macroName,
- SourceLineInfo const& lineInfo,
- ResultWas::OfType type )
- : m_info( macroName, lineInfo, type )
- {}
-
- template<typename T>
- MessageBuilder& operator << ( T const& value ) {
- m_stream << value;
- return *this;
- }
-
- MessageInfo m_info;
- std::ostringstream m_stream;
- };
-
- class ScopedMessage {
- public:
- ScopedMessage( MessageBuilder const& builder );
- ScopedMessage( ScopedMessage const& other );
- ~ScopedMessage();
-
- MessageInfo m_info;
- };
-
-} // end namespace Catch
-
-// #included from: catch_interfaces_capture.h
-#define TWOBLUECUBES_CATCH_INTERFACES_CAPTURE_H_INCLUDED
-
-#include <string>
-
-namespace Catch {
-
- class TestCase;
- class AssertionResult;
- struct AssertionInfo;
- struct SectionInfo;
- struct MessageInfo;
- class ScopedMessageBuilder;
- struct Counts;
-
- struct IResultCapture {
-
- virtual ~IResultCapture();
-
- virtual void assertionEnded( AssertionResult const& result ) = 0;
- virtual bool sectionStarted( SectionInfo const& sectionInfo,
- Counts& assertions ) = 0;
- virtual void sectionEnded( SectionInfo const& name, Counts const& assertions, double _durationInSeconds ) = 0;
- virtual void pushScopedMessage( MessageInfo const& message ) = 0;
- virtual void popScopedMessage( MessageInfo const& message ) = 0;
-
- virtual std::string getCurrentTestName() const = 0;
- virtual const AssertionResult* getLastResult() const = 0;
-
- virtual void handleFatalErrorCondition( std::string const& message ) = 0;
- };
-
- IResultCapture& getResultCapture();
-}
-
-// #included from: catch_debugger.h
-#define TWOBLUECUBES_CATCH_DEBUGGER_H_INCLUDED
-
-// #included from: catch_platform.h
-#define TWOBLUECUBES_CATCH_PLATFORM_H_INCLUDED
-
-#if defined(__MAC_OS_X_VERSION_MIN_REQUIRED)
-#define CATCH_PLATFORM_MAC
-#elif defined(__IPHONE_OS_VERSION_MIN_REQUIRED)
-#define CATCH_PLATFORM_IPHONE
-#elif defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER)
-#define CATCH_PLATFORM_WINDOWS
-#endif
-
-#include <string>
-
-namespace Catch{
-
- bool isDebuggerActive();
- void writeToDebugConsole( std::string const& text );
-}
-
-#ifdef CATCH_PLATFORM_MAC
-
- // The following code snippet based on:
- // http://cocoawithlove.com/2008/03/break-into-debugger.html
- #ifdef DEBUG
- #if defined(__ppc64__) || defined(__ppc__)
- #define CATCH_BREAK_INTO_DEBUGGER() \
- if( Catch::isDebuggerActive() ) { \
- __asm__("li r0, 20\nsc\nnop\nli r0, 37\nli r4, 2\nsc\nnop\n" \
- : : : "memory","r0","r3","r4" ); \
- }
- #else
- #define CATCH_BREAK_INTO_DEBUGGER() if( Catch::isDebuggerActive() ) {__asm__("int $3\n" : : );}
- #endif
- #endif
-
-#elif defined(_MSC_VER)
- #define CATCH_BREAK_INTO_DEBUGGER() if( Catch::isDebuggerActive() ) { __debugbreak(); }
-#elif defined(__MINGW32__)
- extern "C" __declspec(dllimport) void __stdcall DebugBreak();
- #define CATCH_BREAK_INTO_DEBUGGER() if( Catch::isDebuggerActive() ) { DebugBreak(); }
-#endif
-
-#ifndef CATCH_BREAK_INTO_DEBUGGER
-#define CATCH_BREAK_INTO_DEBUGGER() Catch::alwaysTrue();
-#endif
-
-// #included from: catch_interfaces_runner.h
-#define TWOBLUECUBES_CATCH_INTERFACES_RUNNER_H_INCLUDED
-
-namespace Catch {
- class TestCase;
-
- struct IRunner {
- virtual ~IRunner();
- virtual bool aborting() const = 0;
- };
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// In the event of a failure works out if the debugger needs to be invoked
-// and/or an exception thrown and takes appropriate action.
-// This needs to be done as a macro so the debugger will stop in the user
-// source code rather than in Catch library code
-#define INTERNAL_CATCH_REACT( resultBuilder ) \
- if( resultBuilder.shouldDebugBreak() ) CATCH_BREAK_INTO_DEBUGGER(); \
- resultBuilder.react();
-
-///////////////////////////////////////////////////////////////////////////////
-#define INTERNAL_CATCH_TEST( expr, resultDisposition, macroName ) \
- do { \
- Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition ); \
- try { \
- ( __catchResult <= expr ).endExpression(); \
- } \
- catch( ... ) { \
- __catchResult.useActiveException( Catch::ResultDisposition::Normal ); \
- } \
- INTERNAL_CATCH_REACT( __catchResult ) \
- } while( Catch::isTrue( false && (expr) ) ) // expr here is never evaluated at runtime but it forces the compiler to give it a look
-
-///////////////////////////////////////////////////////////////////////////////
-#define INTERNAL_CATCH_IF( expr, resultDisposition, macroName ) \
- INTERNAL_CATCH_TEST( expr, resultDisposition, macroName ); \
- if( Catch::getResultCapture().getLastResult()->succeeded() )
-
-///////////////////////////////////////////////////////////////////////////////
-#define INTERNAL_CATCH_ELSE( expr, resultDisposition, macroName ) \
- INTERNAL_CATCH_TEST( expr, resultDisposition, macroName ); \
- if( !Catch::getResultCapture().getLastResult()->succeeded() )
-
-///////////////////////////////////////////////////////////////////////////////
-#define INTERNAL_CATCH_NO_THROW( expr, resultDisposition, macroName ) \
- do { \
- Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition ); \
- try { \
- expr; \
- __catchResult.captureResult( Catch::ResultWas::Ok ); \
- } \
- catch( ... ) { \
- __catchResult.useActiveException( resultDisposition ); \
- } \
- INTERNAL_CATCH_REACT( __catchResult ) \
- } while( Catch::alwaysFalse() )
-
-///////////////////////////////////////////////////////////////////////////////
-#define INTERNAL_CATCH_THROWS( expr, resultDisposition, macroName ) \
- do { \
- Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition ); \
- if( __catchResult.allowThrows() ) \
- try { \
- expr; \
- __catchResult.captureResult( Catch::ResultWas::DidntThrowException ); \
- } \
- catch( ... ) { \
- __catchResult.captureResult( Catch::ResultWas::Ok ); \
- } \
- else \
- __catchResult.captureResult( Catch::ResultWas::Ok ); \
- INTERNAL_CATCH_REACT( __catchResult ) \
- } while( Catch::alwaysFalse() )
-
-///////////////////////////////////////////////////////////////////////////////
-#define INTERNAL_CATCH_THROWS_AS( expr, exceptionType, resultDisposition, macroName ) \
- do { \
- Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition ); \
- if( __catchResult.allowThrows() ) \
- try { \
- expr; \
- __catchResult.captureResult( Catch::ResultWas::DidntThrowException ); \
- } \
- catch( exceptionType ) { \
- __catchResult.captureResult( Catch::ResultWas::Ok ); \
- } \
- catch( ... ) { \
- __catchResult.useActiveException( resultDisposition ); \
- } \
- else \
- __catchResult.captureResult( Catch::ResultWas::Ok ); \
- INTERNAL_CATCH_REACT( __catchResult ) \
- } while( Catch::alwaysFalse() )
-
-///////////////////////////////////////////////////////////////////////////////
-#ifdef CATCH_CONFIG_VARIADIC_MACROS
- #define INTERNAL_CATCH_MSG( messageType, resultDisposition, macroName, ... ) \
- do { \
- Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, "", resultDisposition ); \
- __catchResult << __VA_ARGS__ + ::Catch::StreamEndStop(); \
- __catchResult.captureResult( messageType ); \
- INTERNAL_CATCH_REACT( __catchResult ) \
- } while( Catch::alwaysFalse() )
-#else
- #define INTERNAL_CATCH_MSG( messageType, resultDisposition, macroName, log ) \
- do { \
- Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, "", resultDisposition ); \
- __catchResult << log + ::Catch::StreamEndStop(); \
- __catchResult.captureResult( messageType ); \
- INTERNAL_CATCH_REACT( __catchResult ) \
- } while( Catch::alwaysFalse() )
-#endif
-
-///////////////////////////////////////////////////////////////////////////////
-#define INTERNAL_CATCH_INFO( log, macroName ) \
- Catch::ScopedMessage INTERNAL_CATCH_UNIQUE_NAME( scopedMessage ) = Catch::MessageBuilder( macroName, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info ) << log;
-
-///////////////////////////////////////////////////////////////////////////////
-#define INTERNAL_CHECK_THAT( arg, matcher, resultDisposition, macroName ) \
- do { \
- Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #arg " " #matcher, resultDisposition ); \
- try { \
- std::string matcherAsString = ::Catch::Matchers::matcher.toString(); \
- __catchResult \
- .setLhs( Catch::toString( arg ) ) \
- .setRhs( matcherAsString == Catch::Detail::unprintableString ? #matcher : matcherAsString ) \
- .setOp( "matches" ) \
- .setResultType( ::Catch::Matchers::matcher.match( arg ) ); \
- __catchResult.captureExpression(); \
- } catch( ... ) { \
- __catchResult.useActiveException( resultDisposition | Catch::ResultDisposition::ContinueOnFailure ); \
- } \
- INTERNAL_CATCH_REACT( __catchResult ) \
- } while( Catch::alwaysFalse() )
-
-// #included from: internal/catch_section.h
-#define TWOBLUECUBES_CATCH_SECTION_H_INCLUDED
-
-// #included from: catch_section_info.h
-#define TWOBLUECUBES_CATCH_SECTION_INFO_H_INCLUDED
-
-namespace Catch {
-
- struct SectionInfo {
- SectionInfo
- ( SourceLineInfo const& _lineInfo,
- std::string const& _name,
- std::string const& _description = std::string() );
-
- std::string name;
- std::string description;
- SourceLineInfo lineInfo;
- };
-
-} // end namespace Catch
-
-// #included from: catch_totals.hpp
-#define TWOBLUECUBES_CATCH_TOTALS_HPP_INCLUDED
-
-#include <cstddef>
-
-namespace Catch {
-
- struct Counts {
- Counts() : passed( 0 ), failed( 0 ), failedButOk( 0 ) {}
-
- Counts operator - ( Counts const& other ) const {
- Counts diff;
- diff.passed = passed - other.passed;
- diff.failed = failed - other.failed;
- diff.failedButOk = failedButOk - other.failedButOk;
- return diff;
- }
- Counts& operator += ( Counts const& other ) {
- passed += other.passed;
- failed += other.failed;
- failedButOk += other.failedButOk;
- return *this;
- }
-
- std::size_t total() const {
- return passed + failed + failedButOk;
- }
- bool allPassed() const {
- return failed == 0 && failedButOk == 0;
- }
- bool allOk() const {
- return failed == 0;
- }
-
- std::size_t passed;
- std::size_t failed;
- std::size_t failedButOk;
- };
-
- struct Totals {
-
- Totals operator - ( Totals const& other ) const {
- Totals diff;
- diff.assertions = assertions - other.assertions;
- diff.testCases = testCases - other.testCases;
- return diff;
- }
-
- Totals delta( Totals const& prevTotals ) const {
- Totals diff = *this - prevTotals;
- if( diff.assertions.failed > 0 )
- ++diff.testCases.failed;
- else if( diff.assertions.failedButOk > 0 )
- ++diff.testCases.failedButOk;
- else
- ++diff.testCases.passed;
- return diff;
- }
-
- Totals& operator += ( Totals const& other ) {
- assertions += other.assertions;
- testCases += other.testCases;
- return *this;
- }
-
- Counts assertions;
- Counts testCases;
- };
-}
-
-// #included from: catch_timer.h
-#define TWOBLUECUBES_CATCH_TIMER_H_INCLUDED
-
-#ifdef CATCH_PLATFORM_WINDOWS
-typedef unsigned long long uint64_t;
-#else
-#include <stdint.h>
-#endif
-
-namespace Catch {
-
- class Timer {
- public:
- Timer() : m_ticks( 0 ) {}
- void start();
- unsigned int getElapsedMicroseconds() const;
- unsigned int getElapsedMilliseconds() const;
- double getElapsedSeconds() const;
-
- private:
- uint64_t m_ticks;
- };
-
-} // namespace Catch
-
-#include <string>
-
-namespace Catch {
-
- class Section : NonCopyable {
- public:
- Section( SectionInfo const& info );
- ~Section();
-
- // This indicates whether the section should be executed or not
- operator bool() const;
-
- private:
- SectionInfo m_info;
-
- std::string m_name;
- Counts m_assertions;
- bool m_sectionIncluded;
- Timer m_timer;
- };
-
-} // end namespace Catch
-
-#ifdef CATCH_CONFIG_VARIADIC_MACROS
- #define INTERNAL_CATCH_SECTION( ... ) \
- if( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( catch_internal_Section ) = Catch::SectionInfo( CATCH_INTERNAL_LINEINFO, __VA_ARGS__ ) )
-#else
- #define INTERNAL_CATCH_SECTION( name, desc ) \
- if( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( catch_internal_Section ) = Catch::SectionInfo( CATCH_INTERNAL_LINEINFO, name, desc ) )
-#endif
-
-// #included from: internal/catch_generators.hpp
-#define TWOBLUECUBES_CATCH_GENERATORS_HPP_INCLUDED
-
-#include <iterator>
-#include <vector>
-#include <string>
-#include <stdlib.h>
-
-namespace Catch {
-
-template<typename T>
-struct IGenerator {
- virtual ~IGenerator() {}
- virtual T getValue( std::size_t index ) const = 0;
- virtual std::size_t size () const = 0;
-};
-
-template<typename T>
-class BetweenGenerator : public IGenerator<T> {
-public:
- BetweenGenerator( T from, T to ) : m_from( from ), m_to( to ){}
-
- virtual T getValue( std::size_t index ) const {
- return m_from+static_cast<int>( index );
- }
-
- virtual std::size_t size() const {
- return static_cast<std::size_t>( 1+m_to-m_from );
- }
-
-private:
-
- T m_from;
- T m_to;
-};
-
-template<typename T>
-class ValuesGenerator : public IGenerator<T> {
-public:
- ValuesGenerator(){}
-
- void add( T value ) {
- m_values.push_back( value );
- }
-
- virtual T getValue( std::size_t index ) const {
- return m_values[index];
- }
-
- virtual std::size_t size() const {
- return m_values.size();
- }
-
-private:
- std::vector<T> m_values;
-};
-
-template<typename T>
-class CompositeGenerator {
-public:
- CompositeGenerator() : m_totalSize( 0 ) {}
-
- // *** Move semantics, similar to auto_ptr ***
- CompositeGenerator( CompositeGenerator& other )
- : m_fileInfo( other.m_fileInfo ),
- m_totalSize( 0 )
- {
- move( other );
- }
-
- CompositeGenerator& setFileInfo( const char* fileInfo ) {
- m_fileInfo = fileInfo;
- return *this;
- }
-
- ~CompositeGenerator() {
- deleteAll( m_composed );
- }
-
- operator T () const {
- size_t overallIndex = getCurrentContext().getGeneratorIndex( m_fileInfo, m_totalSize );
-
- typename std::vector<const IGenerator<T>*>::const_iterator it = m_composed.begin();
- typename std::vector<const IGenerator<T>*>::const_iterator itEnd = m_composed.end();
- for( size_t index = 0; it != itEnd; ++it )
- {
- const IGenerator<T>* generator = *it;
- if( overallIndex >= index && overallIndex < index + generator->size() )
- {
- return generator->getValue( overallIndex-index );
- }
- index += generator->size();
- }
- CATCH_INTERNAL_ERROR( "Indexed past end of generated range" );
- return T(); // Suppress spurious "not all control paths return a value" warning in Visual Studio - if you know how to fix this please do so
- }
-
- void add( const IGenerator<T>* generator ) {
- m_totalSize += generator->size();
- m_composed.push_back( generator );
- }
-
- CompositeGenerator& then( CompositeGenerator& other ) {
- move( other );
- return *this;
- }
-
- CompositeGenerator& then( T value ) {
- ValuesGenerator<T>* valuesGen = new ValuesGenerator<T>();
- valuesGen->add( value );
- add( valuesGen );
- return *this;
- }
-
-private:
-
- void move( CompositeGenerator& other ) {
- std::copy( other.m_composed.begin(), other.m_composed.end(), std::back_inserter( m_composed ) );
- m_totalSize += other.m_totalSize;
- other.m_composed.clear();
- }
-
- std::vector<const IGenerator<T>*> m_composed;
- std::string m_fileInfo;
- size_t m_totalSize;
-};
-
-namespace Generators
-{
- template<typename T>
- CompositeGenerator<T> between( T from, T to ) {
- CompositeGenerator<T> generators;
- generators.add( new BetweenGenerator<T>( from, to ) );
- return generators;
- }
-
- template<typename T>
- CompositeGenerator<T> values( T val1, T val2 ) {
- CompositeGenerator<T> generators;
- ValuesGenerator<T>* valuesGen = new ValuesGenerator<T>();
- valuesGen->add( val1 );
- valuesGen->add( val2 );
- generators.add( valuesGen );
- return generators;
- }
-
- template<typename T>
- CompositeGenerator<T> values( T val1, T val2, T val3 ){
- CompositeGenerator<T> generators;
- ValuesGenerator<T>* valuesGen = new ValuesGenerator<T>();
- valuesGen->add( val1 );
- valuesGen->add( val2 );
- valuesGen->add( val3 );
- generators.add( valuesGen );
- return generators;
- }
-
- template<typename T>
- CompositeGenerator<T> values( T val1, T val2, T val3, T val4 ) {
- CompositeGenerator<T> generators;
- ValuesGenerator<T>* valuesGen = new ValuesGenerator<T>();
- valuesGen->add( val1 );
- valuesGen->add( val2 );
- valuesGen->add( val3 );
- valuesGen->add( val4 );
- generators.add( valuesGen );
- return generators;
- }
-
-} // end namespace Generators
-
-using namespace Generators;
-
-} // end namespace Catch
-
-#define INTERNAL_CATCH_LINESTR2( line ) #line
-#define INTERNAL_CATCH_LINESTR( line ) INTERNAL_CATCH_LINESTR2( line )
-
-#define INTERNAL_CATCH_GENERATE( expr ) expr.setFileInfo( __FILE__ "(" INTERNAL_CATCH_LINESTR( __LINE__ ) ")" )
-
-// #included from: internal/catch_interfaces_exception.h
-#define TWOBLUECUBES_CATCH_INTERFACES_EXCEPTION_H_INCLUDED
-
-#include <string>
-// #included from: catch_interfaces_registry_hub.h
-#define TWOBLUECUBES_CATCH_INTERFACES_REGISTRY_HUB_H_INCLUDED
-
-#include <string>
-
-namespace Catch {
-
- class TestCase;
- struct ITestCaseRegistry;
- struct IExceptionTranslatorRegistry;
- struct IExceptionTranslator;
- struct IReporterRegistry;
- struct IReporterFactory;
-
- struct IRegistryHub {
- virtual ~IRegistryHub();
-
- virtual IReporterRegistry const& getReporterRegistry() const = 0;
- virtual ITestCaseRegistry const& getTestCaseRegistry() const = 0;
- virtual IExceptionTranslatorRegistry& getExceptionTranslatorRegistry() = 0;
- };
-
- struct IMutableRegistryHub {
- virtual ~IMutableRegistryHub();
- virtual void registerReporter( std::string const& name, IReporterFactory* factory ) = 0;
- virtual void registerTest( TestCase const& testInfo ) = 0;
- virtual void registerTranslator( const IExceptionTranslator* translator ) = 0;
- };
-
- IRegistryHub& getRegistryHub();
- IMutableRegistryHub& getMutableRegistryHub();
- void cleanUp();
- std::string translateActiveException();
-
-}
-
-
-namespace Catch {
-
- typedef std::string(*exceptionTranslateFunction)();
-
- struct IExceptionTranslator {
- virtual ~IExceptionTranslator();
- virtual std::string translate() const = 0;
- };
-
- struct IExceptionTranslatorRegistry {
- virtual ~IExceptionTranslatorRegistry();
-
- virtual std::string translateActiveException() const = 0;
- };
-
- class ExceptionTranslatorRegistrar {
- template<typename T>
- class ExceptionTranslator : public IExceptionTranslator {
- public:
-
- ExceptionTranslator( std::string(*translateFunction)( T& ) )
- : m_translateFunction( translateFunction )
- {}
-
- virtual std::string translate() const {
- try {
- throw;
- }
- catch( T& ex ) {
- return m_translateFunction( ex );
- }
- }
-
- protected:
- std::string(*m_translateFunction)( T& );
- };
-
- public:
- template<typename T>
- ExceptionTranslatorRegistrar( std::string(*translateFunction)( T& ) ) {
- getMutableRegistryHub().registerTranslator
- ( new ExceptionTranslator<T>( translateFunction ) );
- }
- };
-}
-
-///////////////////////////////////////////////////////////////////////////////
-#define INTERNAL_CATCH_TRANSLATE_EXCEPTION( signature ) \
- static std::string INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionTranslator )( signature ); \
- namespace{ Catch::ExceptionTranslatorRegistrar INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionRegistrar )( &INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionTranslator ) ); }\
- static std::string INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionTranslator )( signature )
-
-// #included from: internal/catch_approx.hpp
-#define TWOBLUECUBES_CATCH_APPROX_HPP_INCLUDED
-
-#include <cmath>
-#include <limits>
-
-namespace Catch {
-namespace Detail {
-
- class Approx {
- public:
- explicit Approx ( double value )
- : m_epsilon( std::numeric_limits<float>::epsilon()*100 ),
- m_scale( 1.0 ),
- m_value( value )
- {}
-
- Approx( Approx const& other )
- : m_epsilon( other.m_epsilon ),
- m_scale( other.m_scale ),
- m_value( other.m_value )
- {}
-
- static Approx custom() {
- return Approx( 0 );
- }
-
- Approx operator()( double value ) {
- Approx approx( value );
- approx.epsilon( m_epsilon );
- approx.scale( m_scale );
- return approx;
- }
-
- friend bool operator == ( double lhs, Approx const& rhs ) {
- // Thanks to Richard Harris for his help refining this formula
- return fabs( lhs - rhs.m_value ) < rhs.m_epsilon * (rhs.m_scale + (std::max)( fabs(lhs), fabs(rhs.m_value) ) );
- }
-
- friend bool operator == ( Approx const& lhs, double rhs ) {
- return operator==( rhs, lhs );
- }
-
- friend bool operator != ( double lhs, Approx const& rhs ) {
- return !operator==( lhs, rhs );
- }
-
- friend bool operator != ( Approx const& lhs, double rhs ) {
- return !operator==( rhs, lhs );
- }
-
- Approx& epsilon( double newEpsilon ) {
- m_epsilon = newEpsilon;
- return *this;
- }
-
- Approx& scale( double newScale ) {
- m_scale = newScale;
- return *this;
- }
-
- std::string toString() const {
- std::ostringstream oss;
- oss << "Approx( " << Catch::toString( m_value ) << " )";
- return oss.str();
- }
-
- private:
- double m_epsilon;
- double m_scale;
- double m_value;
- };
-}
-
-template<>
-inline std::string toString<Detail::Approx>( Detail::Approx const& value ) {
- return value.toString();
-}
-
-} // end namespace Catch
-
-// #included from: internal/catch_matchers.hpp
-#define TWOBLUECUBES_CATCH_MATCHERS_HPP_INCLUDED
-
-namespace Catch {
-namespace Matchers {
- namespace Impl {
-
- template<typename ExpressionT>
- struct Matcher : SharedImpl<IShared>
- {
- typedef ExpressionT ExpressionType;
-
- virtual ~Matcher() {}
- virtual Ptr<Matcher> clone() const = 0;
- virtual bool match( ExpressionT const& expr ) const = 0;
- virtual std::string toString() const = 0;
- };
-
- template<typename DerivedT, typename ExpressionT>
- struct MatcherImpl : Matcher<ExpressionT> {
-
- virtual Ptr<Matcher<ExpressionT> > clone() const {
- return Ptr<Matcher<ExpressionT> >( new DerivedT( static_cast<DerivedT const&>( *this ) ) );
- }
- };
-
- namespace Generic {
-
- template<typename ExpressionT>
- class AllOf : public MatcherImpl<AllOf<ExpressionT>, ExpressionT> {
- public:
-
- AllOf() {}
- AllOf( AllOf const& other ) : m_matchers( other.m_matchers ) {}
-
- AllOf& add( Matcher<ExpressionT> const& matcher ) {
- m_matchers.push_back( matcher.clone() );
- return *this;
- }
- virtual bool match( ExpressionT const& expr ) const
- {
- for( std::size_t i = 0; i < m_matchers.size(); ++i )
- if( !m_matchers[i]->match( expr ) )
- return false;
- return true;
- }
- virtual std::string toString() const {
- std::ostringstream oss;
- oss << "( ";
- for( std::size_t i = 0; i < m_matchers.size(); ++i ) {
- if( i != 0 )
- oss << " and ";
- oss << m_matchers[i]->toString();
- }
- oss << " )";
- return oss.str();
- }
-
- private:
- std::vector<Ptr<Matcher<ExpressionT> > > m_matchers;
- };
-
- template<typename ExpressionT>
- class AnyOf : public MatcherImpl<AnyOf<ExpressionT>, ExpressionT> {
- public:
-
- AnyOf() {}
- AnyOf( AnyOf const& other ) : m_matchers( other.m_matchers ) {}
-
- AnyOf& add( Matcher<ExpressionT> const& matcher ) {
- m_matchers.push_back( matcher.clone() );
- return *this;
- }
- virtual bool match( ExpressionT const& expr ) const
- {
- for( std::size_t i = 0; i < m_matchers.size(); ++i )
- if( m_matchers[i]->match( expr ) )
- return true;
- return false;
- }
- virtual std::string toString() const {
- std::ostringstream oss;
- oss << "( ";
- for( std::size_t i = 0; i < m_matchers.size(); ++i ) {
- if( i != 0 )
- oss << " or ";
- oss << m_matchers[i]->toString();
- }
- oss << " )";
- return oss.str();
- }
-
- private:
- std::vector<Ptr<Matcher<ExpressionT> > > m_matchers;
- };
-
- }
-
- namespace StdString {
-
- inline std::string makeString( std::string const& str ) { return str; }
- inline std::string makeString( const char* str ) { return str ? std::string( str ) : std::string(); }
-
- struct Equals : MatcherImpl<Equals, std::string> {
- Equals( std::string const& str ) : m_str( str ){}
- Equals( Equals const& other ) : m_str( other.m_str ){}
-
- virtual ~Equals();
-
- virtual bool match( std::string const& expr ) const {
- return m_str == expr;
- }
- virtual std::string toString() const {
- return "equals: \"" + m_str + "\"";
- }
-
- std::string m_str;
- };
-
- struct Contains : MatcherImpl<Contains, std::string> {
- Contains( std::string const& substr ) : m_substr( substr ){}
- Contains( Contains const& other ) : m_substr( other.m_substr ){}
-
- virtual ~Contains();
-
- virtual bool match( std::string const& expr ) const {
- return expr.find( m_substr ) != std::string::npos;
- }
- virtual std::string toString() const {
- return "contains: \"" + m_substr + "\"";
- }
-
- std::string m_substr;
- };
-
- struct StartsWith : MatcherImpl<StartsWith, std::string> {
- StartsWith( std::string const& substr ) : m_substr( substr ){}
- StartsWith( StartsWith const& other ) : m_substr( other.m_substr ){}
-
- virtual ~StartsWith();
-
- virtual bool match( std::string const& expr ) const {
- return expr.find( m_substr ) == 0;
- }
- virtual std::string toString() const {
- return "starts with: \"" + m_substr + "\"";
- }
-
- std::string m_substr;
- };
-
- struct EndsWith : MatcherImpl<EndsWith, std::string> {
- EndsWith( std::string const& substr ) : m_substr( substr ){}
- EndsWith( EndsWith const& other ) : m_substr( other.m_substr ){}
-
- virtual ~EndsWith();
-
- virtual bool match( std::string const& expr ) const {
- return expr.find( m_substr ) == expr.size() - m_substr.size();
- }
- virtual std::string toString() const {
- return "ends with: \"" + m_substr + "\"";
- }
-
- std::string m_substr;
- };
- } // namespace StdString
- } // namespace Impl
-
- // The following functions create the actual matcher objects.
- // This allows the types to be inferred
- template<typename ExpressionT>
- inline Impl::Generic::AllOf<ExpressionT> AllOf( Impl::Matcher<ExpressionT> const& m1,
- Impl::Matcher<ExpressionT> const& m2 ) {
- return Impl::Generic::AllOf<ExpressionT>().add( m1 ).add( m2 );
- }
- template<typename ExpressionT>
- inline Impl::Generic::AllOf<ExpressionT> AllOf( Impl::Matcher<ExpressionT> const& m1,
- Impl::Matcher<ExpressionT> const& m2,
- Impl::Matcher<ExpressionT> const& m3 ) {
- return Impl::Generic::AllOf<ExpressionT>().add( m1 ).add( m2 ).add( m3 );
- }
- template<typename ExpressionT>
- inline Impl::Generic::AnyOf<ExpressionT> AnyOf( Impl::Matcher<ExpressionT> const& m1,
- Impl::Matcher<ExpressionT> const& m2 ) {
- return Impl::Generic::AnyOf<ExpressionT>().add( m1 ).add( m2 );
- }
- template<typename ExpressionT>
- inline Impl::Generic::AnyOf<ExpressionT> AnyOf( Impl::Matcher<ExpressionT> const& m1,
- Impl::Matcher<ExpressionT> const& m2,
- Impl::Matcher<ExpressionT> const& m3 ) {
- return Impl::Generic::AnyOf<ExpressionT>().add( m1 ).add( m2 ).add( m3 );
- }
-
- inline Impl::StdString::Equals Equals( std::string const& str ) {
- return Impl::StdString::Equals( str );
- }
- inline Impl::StdString::Equals Equals( const char* str ) {
- return Impl::StdString::Equals( Impl::StdString::makeString( str ) );
- }
- inline Impl::StdString::Contains Contains( std::string const& substr ) {
- return Impl::StdString::Contains( substr );
- }
- inline Impl::StdString::Contains Contains( const char* substr ) {
- return Impl::StdString::Contains( Impl::StdString::makeString( substr ) );
- }
- inline Impl::StdString::StartsWith StartsWith( std::string const& substr ) {
- return Impl::StdString::StartsWith( substr );
- }
- inline Impl::StdString::StartsWith StartsWith( const char* substr ) {
- return Impl::StdString::StartsWith( Impl::StdString::makeString( substr ) );
- }
- inline Impl::StdString::EndsWith EndsWith( std::string const& substr ) {
- return Impl::StdString::EndsWith( substr );
- }
- inline Impl::StdString::EndsWith EndsWith( const char* substr ) {
- return Impl::StdString::EndsWith( Impl::StdString::makeString( substr ) );
- }
-
-} // namespace Matchers
-
-using namespace Matchers;
-
-} // namespace Catch
-
-// #included from: internal/catch_interfaces_tag_alias_registry.h
-#define TWOBLUECUBES_CATCH_INTERFACES_TAG_ALIAS_REGISTRY_H_INCLUDED
-
-// #included from: catch_tag_alias.h
-#define TWOBLUECUBES_CATCH_TAG_ALIAS_H_INCLUDED
-
-#include <string>
-
-namespace Catch {
-
- struct TagAlias {
- TagAlias( std::string _tag, SourceLineInfo _lineInfo ) : tag( _tag ), lineInfo( _lineInfo ) {}
-
- std::string tag;
- SourceLineInfo lineInfo;
- };
-
- struct RegistrarForTagAliases {
- RegistrarForTagAliases( char const* alias, char const* tag, SourceLineInfo const& lineInfo );
- };
-
-} // end namespace Catch
-
-#define CATCH_REGISTER_TAG_ALIAS( alias, spec ) namespace{ Catch::RegistrarForTagAliases INTERNAL_CATCH_UNIQUE_NAME( AutoRegisterTagAlias )( alias, spec, CATCH_INTERNAL_LINEINFO ); }
-// #included from: catch_option.hpp
-#define TWOBLUECUBES_CATCH_OPTION_HPP_INCLUDED
-
-namespace Catch {
-
- // An optional type
- template<typename T>
- class Option {
- public:
- Option() : nullableValue( NULL ) {}
- Option( T const& _value )
- : nullableValue( new( storage ) T( _value ) )
- {}
- Option( Option const& _other )
- : nullableValue( _other ? new( storage ) T( *_other ) : NULL )
- {}
-
- ~Option() {
- reset();
- }
-
- Option& operator= ( Option const& _other ) {
- if( &_other != this ) {
- reset();
- if( _other )
- nullableValue = new( storage ) T( *_other );
- }
- return *this;
- }
- Option& operator = ( T const& _value ) {
- reset();
- nullableValue = new( storage ) T( _value );
- return *this;
- }
-
- void reset() {
- if( nullableValue )
- nullableValue->~T();
- nullableValue = NULL;
- }
-
- T& operator*() { return *nullableValue; }
- T const& operator*() const { return *nullableValue; }
- T* operator->() { return nullableValue; }
- const T* operator->() const { return nullableValue; }
-
- T valueOr( T const& defaultValue ) const {
- return nullableValue ? *nullableValue : defaultValue;
- }
-
- bool some() const { return nullableValue != NULL; }
- bool none() const { return nullableValue == NULL; }
-
- bool operator !() const { return nullableValue == NULL; }
- operator SafeBool::type() const {
- return SafeBool::makeSafe( some() );
- }
-
- private:
- T* nullableValue;
- char storage[sizeof(T)];
- };
-
-} // end namespace Catch
-
-namespace Catch {
-
- struct ITagAliasRegistry {
- virtual ~ITagAliasRegistry();
- virtual Option<TagAlias> find( std::string const& alias ) const = 0;
- virtual std::string expandAliases( std::string const& unexpandedTestSpec ) const = 0;
-
- static ITagAliasRegistry const& get();
- };
-
-} // end namespace Catch
-
-// These files are included here so the single_include script doesn't put them
-// in the conditionally compiled sections
-// #included from: internal/catch_test_case_info.h
-#define TWOBLUECUBES_CATCH_TEST_CASE_INFO_H_INCLUDED
-
-#include <string>
-#include <set>
-
-#ifdef __clang__
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wpadded"
-#endif
-
-namespace Catch {
-
- struct ITestCase;
-
- struct TestCaseInfo {
- enum SpecialProperties{
- None = 0,
- IsHidden = 1 << 1,
- ShouldFail = 1 << 2,
- MayFail = 1 << 3,
- Throws = 1 << 4
- };
-
- TestCaseInfo( std::string const& _name,
- std::string const& _className,
- std::string const& _description,
- std::set<std::string> const& _tags,
- SourceLineInfo const& _lineInfo );
-
- TestCaseInfo( TestCaseInfo const& other );
-
- bool isHidden() const;
- bool throws() const;
- bool okToFail() const;
- bool expectedToFail() const;
-
- std::string name;
- std::string className;
- std::string description;
- std::set<std::string> tags;
- std::set<std::string> lcaseTags;
- std::string tagsAsString;
- SourceLineInfo lineInfo;
- SpecialProperties properties;
- };
-
- class TestCase : public TestCaseInfo {
- public:
-
- TestCase( ITestCase* testCase, TestCaseInfo const& info );
- TestCase( TestCase const& other );
-
- TestCase withName( std::string const& _newName ) const;
-
- void invoke() const;
-
- TestCaseInfo const& getTestCaseInfo() const;
-
- void swap( TestCase& other );
- bool operator == ( TestCase const& other ) const;
- bool operator < ( TestCase const& other ) const;
- TestCase& operator = ( TestCase const& other );
-
- private:
- Ptr<ITestCase> test;
- };
-
- TestCase makeTestCase( ITestCase* testCase,
- std::string const& className,
- std::string const& name,
- std::string const& description,
- SourceLineInfo const& lineInfo );
-}
-
-#ifdef __clang__
-#pragma clang diagnostic pop
-#endif
-
-
-#ifdef __OBJC__
-// #included from: internal/catch_objc.hpp
-#define TWOBLUECUBES_CATCH_OBJC_HPP_INCLUDED
-
-#import <objc/runtime.h>
-
-#include <string>
-
-// NB. Any general catch headers included here must be included
-// in catch.hpp first to make sure they are included by the single
-// header for non obj-usage
-
-///////////////////////////////////////////////////////////////////////////////
-// This protocol is really only here for (self) documenting purposes, since
-// all its methods are optional.
-@protocol OcFixture
-
-@optional
-
--(void) setUp;
--(void) tearDown;
-
-@end
-
-namespace Catch {
-
- class OcMethod : public SharedImpl<ITestCase> {
-
- public:
- OcMethod( Class cls, SEL sel ) : m_cls( cls ), m_sel( sel ) {}
-
- virtual void invoke() const {
- id obj = [[m_cls alloc] init];
-
- performOptionalSelector( obj, @selector(setUp) );
- performOptionalSelector( obj, m_sel );
- performOptionalSelector( obj, @selector(tearDown) );
-
- arcSafeRelease( obj );
- }
- private:
- virtual ~OcMethod() {}
-
- Class m_cls;
- SEL m_sel;
- };
-
- namespace Detail{
-
- inline std::string getAnnotation( Class cls,
- std::string const& annotationName,
- std::string const& testCaseName ) {
- NSString* selStr = [[NSString alloc] initWithFormat:@"Catch_%s_%s", annotationName.c_str(), testCaseName.c_str()];
- SEL sel = NSSelectorFromString( selStr );
- arcSafeRelease( selStr );
- id value = performOptionalSelector( cls, sel );
- if( value )
- return [(NSString*)value UTF8String];
- return "";
- }
- }
-
- inline size_t registerTestMethods() {
- size_t noTestMethods = 0;
- int noClasses = objc_getClassList( NULL, 0 );
-
- Class* classes = (CATCH_UNSAFE_UNRETAINED Class *)malloc( sizeof(Class) * noClasses);
- objc_getClassList( classes, noClasses );
-
- for( int c = 0; c < noClasses; c++ ) {
- Class cls = classes[c];
- {
- u_int count;
- Method* methods = class_copyMethodList( cls, &count );
- for( u_int m = 0; m < count ; m++ ) {
- SEL selector = method_getName(methods[m]);
- std::string methodName = sel_getName(selector);
- if( startsWith( methodName, "Catch_TestCase_" ) ) {
- std::string testCaseName = methodName.substr( 15 );
- std::string name = Detail::getAnnotation( cls, "Name", testCaseName );
- std::string desc = Detail::getAnnotation( cls, "Description", testCaseName );
- const char* className = class_getName( cls );
-
- getMutableRegistryHub().registerTest( makeTestCase( new OcMethod( cls, selector ), className, name.c_str(), desc.c_str(), SourceLineInfo() ) );
- noTestMethods++;
- }
- }
- free(methods);
- }
- }
- return noTestMethods;
- }
-
- namespace Matchers {
- namespace Impl {
- namespace NSStringMatchers {
-
- template<typename MatcherT>
- struct StringHolder : MatcherImpl<MatcherT, NSString*>{
- StringHolder( NSString* substr ) : m_substr( [substr copy] ){}
- StringHolder( StringHolder const& other ) : m_substr( [other.m_substr copy] ){}
- StringHolder() {
- arcSafeRelease( m_substr );
- }
-
- NSString* m_substr;
- };
-
- struct Equals : StringHolder<Equals> {
- Equals( NSString* substr ) : StringHolder( substr ){}
-
- virtual bool match( ExpressionType const& str ) const {
- return (str != nil || m_substr == nil ) &&
- [str isEqualToString:m_substr];
- }
-
- virtual std::string toString() const {
- return "equals string: " + Catch::toString( m_substr );
- }
- };
-
- struct Contains : StringHolder<Contains> {
- Contains( NSString* substr ) : StringHolder( substr ){}
-
- virtual bool match( ExpressionType const& str ) const {
- return (str != nil || m_substr == nil ) &&
- [str rangeOfString:m_substr].location != NSNotFound;
- }
-
- virtual std::string toString() const {
- return "contains string: " + Catch::toString( m_substr );
- }
- };
-
- struct StartsWith : StringHolder<StartsWith> {
- StartsWith( NSString* substr ) : StringHolder( substr ){}
-
- virtual bool match( ExpressionType const& str ) const {
- return (str != nil || m_substr == nil ) &&
- [str rangeOfString:m_substr].location == 0;
- }
-
- virtual std::string toString() const {
- return "starts with: " + Catch::toString( m_substr );
- }
- };
- struct EndsWith : StringHolder<EndsWith> {
- EndsWith( NSString* substr ) : StringHolder( substr ){}
-
- virtual bool match( ExpressionType const& str ) const {
- return (str != nil || m_substr == nil ) &&
- [str rangeOfString:m_substr].location == [str length] - [m_substr length];
- }
-
- virtual std::string toString() const {
- return "ends with: " + Catch::toString( m_substr );
- }
- };
-
- } // namespace NSStringMatchers
- } // namespace Impl
-
- inline Impl::NSStringMatchers::Equals
- Equals( NSString* substr ){ return Impl::NSStringMatchers::Equals( substr ); }
-
- inline Impl::NSStringMatchers::Contains
- Contains( NSString* substr ){ return Impl::NSStringMatchers::Contains( substr ); }
-
- inline Impl::NSStringMatchers::StartsWith
- StartsWith( NSString* substr ){ return Impl::NSStringMatchers::StartsWith( substr ); }
-
- inline Impl::NSStringMatchers::EndsWith
- EndsWith( NSString* substr ){ return Impl::NSStringMatchers::EndsWith( substr ); }
-
- } // namespace Matchers
-
- using namespace Matchers;
-
-} // namespace Catch
-
-///////////////////////////////////////////////////////////////////////////////
-#define OC_TEST_CASE( name, desc )\
-+(NSString*) INTERNAL_CATCH_UNIQUE_NAME( Catch_Name_test ) \
-{\
-return @ name; \
-}\
-+(NSString*) INTERNAL_CATCH_UNIQUE_NAME( Catch_Description_test ) \
-{ \
-return @ desc; \
-} \
--(void) INTERNAL_CATCH_UNIQUE_NAME( Catch_TestCase_test )
-
-#endif
-
-#ifdef CATCH_IMPL
-// #included from: internal/catch_impl.hpp
-#define TWOBLUECUBES_CATCH_IMPL_HPP_INCLUDED
-
-// Collect all the implementation files together here
-// These are the equivalent of what would usually be cpp files
-
-#ifdef __clang__
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wweak-vtables"
-#endif
-
-// #included from: ../catch_runner.hpp
-#define TWOBLUECUBES_CATCH_RUNNER_HPP_INCLUDED
-
-// #included from: internal/catch_commandline.hpp
-#define TWOBLUECUBES_CATCH_COMMANDLINE_HPP_INCLUDED
-
-// #included from: catch_config.hpp
-#define TWOBLUECUBES_CATCH_CONFIG_HPP_INCLUDED
-
-// #included from: catch_test_spec_parser.hpp
-#define TWOBLUECUBES_CATCH_TEST_SPEC_PARSER_HPP_INCLUDED
-
-#ifdef __clang__
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wpadded"
-#endif
-
-// #included from: catch_test_spec.hpp
-#define TWOBLUECUBES_CATCH_TEST_SPEC_HPP_INCLUDED
-
-#ifdef __clang__
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wpadded"
-#endif
-
-#include <string>
-#include <vector>
-
-namespace Catch {
-
- class TestSpec {
- struct Pattern : SharedImpl<> {
- virtual ~Pattern();
- virtual bool matches( TestCaseInfo const& testCase ) const = 0;
- };
- class NamePattern : public Pattern {
- enum WildcardPosition {
- NoWildcard = 0,
- WildcardAtStart = 1,
- WildcardAtEnd = 2,
- WildcardAtBothEnds = WildcardAtStart | WildcardAtEnd
- };
-
- public:
- NamePattern( std::string const& name ) : m_name( toLower( name ) ), m_wildcard( NoWildcard ) {
- if( startsWith( m_name, "*" ) ) {
- m_name = m_name.substr( 1 );
- m_wildcard = WildcardAtStart;
- }
- if( endsWith( m_name, "*" ) ) {
- m_name = m_name.substr( 0, m_name.size()-1 );
- m_wildcard = static_cast<WildcardPosition>( m_wildcard | WildcardAtEnd );
- }
- }
- virtual ~NamePattern();
- virtual bool matches( TestCaseInfo const& testCase ) const {
- switch( m_wildcard ) {
- case NoWildcard:
- return m_name == toLower( testCase.name );
- case WildcardAtStart:
- return endsWith( toLower( testCase.name ), m_name );
- case WildcardAtEnd:
- return startsWith( toLower( testCase.name ), m_name );
- case WildcardAtBothEnds:
- return contains( toLower( testCase.name ), m_name );
- }
-
-#ifdef __clang__
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wunreachable-code"
-#endif
- throw std::logic_error( "Unknown enum" );
-#ifdef __clang__
-#pragma clang diagnostic pop
-#endif
- }
- private:
- std::string m_name;
- WildcardPosition m_wildcard;
- };
- class TagPattern : public Pattern {
- public:
- TagPattern( std::string const& tag ) : m_tag( toLower( tag ) ) {}
- virtual ~TagPattern();
- virtual bool matches( TestCaseInfo const& testCase ) const {
- return testCase.lcaseTags.find( m_tag ) != testCase.lcaseTags.end();
- }
- private:
- std::string m_tag;
- };
- class ExcludedPattern : public Pattern {
- public:
- ExcludedPattern( Ptr<Pattern> const& underlyingPattern ) : m_underlyingPattern( underlyingPattern ) {}
- virtual ~ExcludedPattern();
- virtual bool matches( TestCaseInfo const& testCase ) const { return !m_underlyingPattern->matches( testCase ); }
- private:
- Ptr<Pattern> m_underlyingPattern;
- };
-
- struct Filter {
- std::vector<Ptr<Pattern> > m_patterns;
-
- bool matches( TestCaseInfo const& testCase ) const {
- // All patterns in a filter must match for the filter to be a match
- for( std::vector<Ptr<Pattern> >::const_iterator it = m_patterns.begin(), itEnd = m_patterns.end(); it != itEnd; ++it )
- if( !(*it)->matches( testCase ) )
- return false;
- return true;
- }
- };
-
- public:
- bool hasFilters() const {
- return !m_filters.empty();
- }
- bool matches( TestCaseInfo const& testCase ) const {
- // A TestSpec matches if any filter matches
- for( std::vector<Filter>::const_iterator it = m_filters.begin(), itEnd = m_filters.end(); it != itEnd; ++it )
- if( it->matches( testCase ) )
- return true;
- return false;
- }
-
- private:
- std::vector<Filter> m_filters;
-
- friend class TestSpecParser;
- };
-}
-
-#ifdef __clang__
-#pragma clang diagnostic pop
-#endif
-
-namespace Catch {
-
- class TestSpecParser {
- enum Mode{ None, Name, QuotedName, Tag };
- Mode m_mode;
- bool m_exclusion;
- std::size_t m_start, m_pos;
- std::string m_arg;
- TestSpec::Filter m_currentFilter;
- TestSpec m_testSpec;
- ITagAliasRegistry const* m_tagAliases;
-
- public:
- TestSpecParser( ITagAliasRegistry const& tagAliases ) : m_tagAliases( &tagAliases ) {}
-
- TestSpecParser& parse( std::string const& arg ) {
- m_mode = None;
- m_exclusion = false;
- m_start = std::string::npos;
- m_arg = m_tagAliases->expandAliases( arg );
- for( m_pos = 0; m_pos < m_arg.size(); ++m_pos )
- visitChar( m_arg[m_pos] );
- if( m_mode == Name )
- addPattern<TestSpec::NamePattern>();
- return *this;
- }
- TestSpec testSpec() {
- addFilter();
- return m_testSpec;
- }
- private:
- void visitChar( char c ) {
- if( m_mode == None ) {
- switch( c ) {
- case ' ': return;
- case '~': m_exclusion = true; return;
- case '[': return startNewMode( Tag, ++m_pos );
- case '"': return startNewMode( QuotedName, ++m_pos );
- default: startNewMode( Name, m_pos ); break;
- }
- }
- if( m_mode == Name ) {
- if( c == ',' ) {
- addPattern<TestSpec::NamePattern>();
- addFilter();
- }
- else if( c == '[' ) {
- if( subString() == "exclude:" )
- m_exclusion = true;
- else
- addPattern<TestSpec::NamePattern>();
- startNewMode( Tag, ++m_pos );
- }
- }
- else if( m_mode == QuotedName && c == '"' )
- addPattern<TestSpec::NamePattern>();
- else if( m_mode == Tag && c == ']' )
- addPattern<TestSpec::TagPattern>();
- }
- void startNewMode( Mode mode, std::size_t start ) {
- m_mode = mode;
- m_start = start;
- }
- std::string subString() const { return m_arg.substr( m_start, m_pos - m_start ); }
- template<typename T>
- void addPattern() {
- std::string token = subString();
- if( startsWith( token, "exclude:" ) ) {
- m_exclusion = true;
- token = token.substr( 8 );
- }
- if( !token.empty() ) {
- Ptr<TestSpec::Pattern> pattern = new T( token );
- if( m_exclusion )
- pattern = new TestSpec::ExcludedPattern( pattern );
- m_currentFilter.m_patterns.push_back( pattern );
- }
- m_exclusion = false;
- m_mode = None;
- }
- void addFilter() {
- if( !m_currentFilter.m_patterns.empty() ) {
- m_testSpec.m_filters.push_back( m_currentFilter );
- m_currentFilter = TestSpec::Filter();
- }
- }
- };
- inline TestSpec parseTestSpec( std::string const& arg ) {
- return TestSpecParser( ITagAliasRegistry::get() ).parse( arg ).testSpec();
- }
-
-} // namespace Catch
-
-#ifdef __clang__
-#pragma clang diagnostic pop
-#endif
-
-// #included from: catch_interfaces_config.h
-#define TWOBLUECUBES_CATCH_INTERFACES_CONFIG_H_INCLUDED
-
-#include <iostream>
-#include <string>
-#include <vector>
-
-namespace Catch {
-
- struct Verbosity { enum Level {
- NoOutput = 0,
- Quiet,
- Normal
- }; };
-
- struct WarnAbout { enum What {
- Nothing = 0x00,
- NoAssertions = 0x01
- }; };
-
- struct ShowDurations { enum OrNot {
- DefaultForReporter,
- Always,
- Never
- }; };
- struct RunTests { enum InWhatOrder {
- InDeclarationOrder,
- InLexicographicalOrder,
- InRandomOrder
- }; };
-
- class TestSpec;
-
- struct IConfig : IShared {
-
- virtual ~IConfig();
-
- virtual bool allowThrows() const = 0;
- virtual std::ostream& stream() const = 0;
- virtual std::string name() const = 0;
- virtual bool includeSuccessfulResults() const = 0;
- virtual bool shouldDebugBreak() const = 0;
- virtual bool warnAboutMissingAssertions() const = 0;
- virtual int abortAfter() const = 0;
- virtual bool showInvisibles() const = 0;
- virtual ShowDurations::OrNot showDurations() const = 0;
- virtual TestSpec const& testSpec() const = 0;
- virtual RunTests::InWhatOrder runOrder() const = 0;
- virtual unsigned int rngSeed() const = 0;
- virtual bool forceColour() const = 0;
- };
-}
-
-// #included from: catch_stream.h
-#define TWOBLUECUBES_CATCH_STREAM_H_INCLUDED
-
-#include <streambuf>
-
-#ifdef __clang__
-#pragma clang diagnostic ignored "-Wpadded"
-#endif
-
-namespace Catch {
-
- class Stream {
- public:
- Stream();
- Stream( std::streambuf* _streamBuf, bool _isOwned );
- void release();
-
- std::streambuf* streamBuf;
-
- private:
- bool isOwned;
- };
-
- std::ostream& cout();
- std::ostream& cerr();
-}
-
-#include <memory>
-#include <vector>
-#include <string>
-#include <iostream>
-#include <ctime>
-
-#ifndef CATCH_CONFIG_CONSOLE_WIDTH
-#define CATCH_CONFIG_CONSOLE_WIDTH 80
-#endif
-
-namespace Catch {
-
- struct ConfigData {
-
- ConfigData()
- : listTests( false ),
- listTags( false ),
- listReporters( false ),
- listTestNamesOnly( false ),
- showSuccessfulTests( false ),
- shouldDebugBreak( false ),
- noThrow( false ),
- showHelp( false ),
- showInvisibles( false ),
- forceColour( false ),
- abortAfter( -1 ),
- rngSeed( 0 ),
- verbosity( Verbosity::Normal ),
- warnings( WarnAbout::Nothing ),
- showDurations( ShowDurations::DefaultForReporter ),
- runOrder( RunTests::InDeclarationOrder )
- {}
-
- bool listTests;
- bool listTags;
- bool listReporters;
- bool listTestNamesOnly;
-
- bool showSuccessfulTests;
- bool shouldDebugBreak;
- bool noThrow;
- bool showHelp;
- bool showInvisibles;
- bool forceColour;
-
- int abortAfter;
- unsigned int rngSeed;
-
- Verbosity::Level verbosity;
- WarnAbout::What warnings;
- ShowDurations::OrNot showDurations;
- RunTests::InWhatOrder runOrder;
-
- std::string reporterName;
- std::string outputFilename;
- std::string name;
- std::string processName;
-
- std::vector<std::string> testsOrTags;
- };
-
- class Config : public SharedImpl<IConfig> {
- private:
- Config( Config const& other );
- Config& operator = ( Config const& other );
- virtual void dummy();
- public:
-
- Config()
- : m_os( Catch::cout().rdbuf() )
- {}
-
- Config( ConfigData const& data )
- : m_data( data ),
- m_os( Catch::cout().rdbuf() )
- {
- if( !data.testsOrTags.empty() ) {
- TestSpecParser parser( ITagAliasRegistry::get() );
- for( std::size_t i = 0; i < data.testsOrTags.size(); ++i )
- parser.parse( data.testsOrTags[i] );
- m_testSpec = parser.testSpec();
- }
- }
-
- virtual ~Config() {
- m_os.rdbuf( Catch::cout().rdbuf() );
- m_stream.release();
- }
-
- void setFilename( std::string const& filename ) {
- m_data.outputFilename = filename;
- }
-
- std::string const& getFilename() const {
- return m_data.outputFilename ;
- }
-
- bool listTests() const { return m_data.listTests; }
- bool listTestNamesOnly() const { return m_data.listTestNamesOnly; }
- bool listTags() const { return m_data.listTags; }
- bool listReporters() const { return m_data.listReporters; }
-
- std::string getProcessName() const { return m_data.processName; }
-
- bool shouldDebugBreak() const { return m_data.shouldDebugBreak; }
-
- void setStreamBuf( std::streambuf* buf ) {
- m_os.rdbuf( buf ? buf : Catch::cout().rdbuf() );
- }
-
- void useStream( std::string const& streamName ) {
- Stream stream = createStream( streamName );
- setStreamBuf( stream.streamBuf );
- m_stream.release();
- m_stream = stream;
- }
-
- std::string getReporterName() const { return m_data.reporterName; }
-
- int abortAfter() const { return m_data.abortAfter; }
-
- TestSpec const& testSpec() const { return m_testSpec; }
-
- bool showHelp() const { return m_data.showHelp; }
- bool showInvisibles() const { return m_data.showInvisibles; }
-
- // IConfig interface
- virtual bool allowThrows() const { return !m_data.noThrow; }
- virtual std::ostream& stream() const { return m_os; }
- virtual std::string name() const { return m_data.name.empty() ? m_data.processName : m_data.name; }
- virtual bool includeSuccessfulResults() const { return m_data.showSuccessfulTests; }
- virtual bool warnAboutMissingAssertions() const { return m_data.warnings & WarnAbout::NoAssertions; }
- virtual ShowDurations::OrNot showDurations() const { return m_data.showDurations; }
- virtual RunTests::InWhatOrder runOrder() const { return m_data.runOrder; }
- virtual unsigned int rngSeed() const { return m_data.rngSeed; }
- virtual bool forceColour() const { return m_data.forceColour; }
-
- private:
- ConfigData m_data;
-
- Stream m_stream;
- mutable std::ostream m_os;
- TestSpec m_testSpec;
- };
-
-} // end namespace Catch
-
-// #included from: catch_clara.h
-#define TWOBLUECUBES_CATCH_CLARA_H_INCLUDED
-
-// Use Catch's value for console width (store Clara's off to the side, if present)
-#ifdef CLARA_CONFIG_CONSOLE_WIDTH
-#define CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH CLARA_CONFIG_CONSOLE_WIDTH
-#undef CLARA_CONFIG_CONSOLE_WIDTH
-#endif
-#define CLARA_CONFIG_CONSOLE_WIDTH CATCH_CONFIG_CONSOLE_WIDTH
-
-// Declare Clara inside the Catch namespace
-#define STITCH_CLARA_OPEN_NAMESPACE namespace Catch {
-// #included from: ../external/clara.h
-
-// Only use header guard if we are not using an outer namespace
-#if !defined(TWOBLUECUBES_CLARA_H_INCLUDED) || defined(STITCH_CLARA_OPEN_NAMESPACE)
-
-#ifndef STITCH_CLARA_OPEN_NAMESPACE
-#define TWOBLUECUBES_CLARA_H_INCLUDED
-#define STITCH_CLARA_OPEN_NAMESPACE
-#define STITCH_CLARA_CLOSE_NAMESPACE
-#else
-#define STITCH_CLARA_CLOSE_NAMESPACE }
-#endif
-
-#define STITCH_TBC_TEXT_FORMAT_OPEN_NAMESPACE STITCH_CLARA_OPEN_NAMESPACE
-
-// ----------- #included from tbc_text_format.h -----------
-
-// Only use header guard if we are not using an outer namespace
-#if !defined(TBC_TEXT_FORMAT_H_INCLUDED) || defined(STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE)
-#ifndef STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE
-#define TBC_TEXT_FORMAT_H_INCLUDED
-#endif
-
-#include <string>
-#include <vector>
-#include <sstream>
-
-// Use optional outer namespace
-#ifdef STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE
-namespace STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE {
-#endif
-
-namespace Tbc {
-
-#ifdef TBC_TEXT_FORMAT_CONSOLE_WIDTH
- const unsigned int consoleWidth = TBC_TEXT_FORMAT_CONSOLE_WIDTH;
-#else
- const unsigned int consoleWidth = 80;
-#endif
-
- struct TextAttributes {
- TextAttributes()
- : initialIndent( std::string::npos ),
- indent( 0 ),
- width( consoleWidth-1 ),
- tabChar( '\t' )
- {}
-
- TextAttributes& setInitialIndent( std::size_t _value ) { initialIndent = _value; return *this; }
- TextAttributes& setIndent( std::size_t _value ) { indent = _value; return *this; }
- TextAttributes& setWidth( std::size_t _value ) { width = _value; return *this; }
- TextAttributes& setTabChar( char _value ) { tabChar = _value; return *this; }
-
- std::size_t initialIndent; // indent of first line, or npos
- std::size_t indent; // indent of subsequent lines, or all if initialIndent is npos
- std::size_t width; // maximum width of text, including indent. Longer text will wrap
- char tabChar; // If this char is seen the indent is changed to current pos
- };
-
- class Text {
- public:
- Text( std::string const& _str, TextAttributes const& _attr = TextAttributes() )
- : attr( _attr )
- {
- std::string wrappableChars = " [({.,/|\\-";
- std::size_t indent = _attr.initialIndent != std::string::npos
- ? _attr.initialIndent
- : _attr.indent;
- std::string remainder = _str;
-
- while( !remainder.empty() ) {
- if( lines.size() >= 1000 ) {
- lines.push_back( "... message truncated due to excessive size" );
- return;
- }
- std::size_t tabPos = std::string::npos;
- std::size_t width = (std::min)( remainder.size(), _attr.width - indent );
- std::size_t pos = remainder.find_first_of( '\n' );
- if( pos <= width ) {
- width = pos;
- }
- pos = remainder.find_last_of( _attr.tabChar, width );
- if( pos != std::string::npos ) {
- tabPos = pos;
- if( remainder[width] == '\n' )
- width--;
- remainder = remainder.substr( 0, tabPos ) + remainder.substr( tabPos+1 );
- }
-
- if( width == remainder.size() ) {
- spliceLine( indent, remainder, width );
- }
- else if( remainder[width] == '\n' ) {
- spliceLine( indent, remainder, width );
- if( width <= 1 || remainder.size() != 1 )
- remainder = remainder.substr( 1 );
- indent = _attr.indent;
- }
- else {
- pos = remainder.find_last_of( wrappableChars, width );
- if( pos != std::string::npos && pos > 0 ) {
- spliceLine( indent, remainder, pos );
- if( remainder[0] == ' ' )
- remainder = remainder.substr( 1 );
- }
- else {
- spliceLine( indent, remainder, width-1 );
- lines.back() += "-";
- }
- if( lines.size() == 1 )
- indent = _attr.indent;
- if( tabPos != std::string::npos )
- indent += tabPos;
- }
- }
- }
-
- void spliceLine( std::size_t _indent, std::string& _remainder, std::size_t _pos ) {
- lines.push_back( std::string( _indent, ' ' ) + _remainder.substr( 0, _pos ) );
- _remainder = _remainder.substr( _pos );
- }
-
- typedef std::vector<std::string>::const_iterator const_iterator;
-
- const_iterator begin() const { return lines.begin(); }
- const_iterator end() const { return lines.end(); }
- std::string const& last() const { return lines.back(); }
- std::size_t size() const { return lines.size(); }
- std::string const& operator[]( std::size_t _index ) const { return lines[_index]; }
- std::string toString() const {
- std::ostringstream oss;
- oss << *this;
- return oss.str();
- }
-
- inline friend std::ostream& operator << ( std::ostream& _stream, Text const& _text ) {
- for( Text::const_iterator it = _text.begin(), itEnd = _text.end();
- it != itEnd; ++it ) {
- if( it != _text.begin() )
- _stream << "\n";
- _stream << *it;
- }
- return _stream;
- }
-
- private:
- std::string str;
- TextAttributes attr;
- std::vector<std::string> lines;
- };
-
-} // end namespace Tbc
-
-#ifdef STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE
-} // end outer namespace
-#endif
-
-#endif // TBC_TEXT_FORMAT_H_INCLUDED
-
-// ----------- end of #include from tbc_text_format.h -----------
-// ........... back in /Users/philnash/Dev/OSS/Clara/srcs/clara.h
-
-#undef STITCH_TBC_TEXT_FORMAT_OPEN_NAMESPACE
-
-#include <map>
-#include <algorithm>
-#include <stdexcept>
-#include <memory>
-
-// Use optional outer namespace
-#ifdef STITCH_CLARA_OPEN_NAMESPACE
-STITCH_CLARA_OPEN_NAMESPACE
-#endif
-
-namespace Clara {
-
- struct UnpositionalTag {};
-
- extern UnpositionalTag _;
-
-#ifdef CLARA_CONFIG_MAIN
- UnpositionalTag _;
-#endif
-
- namespace Detail {
-
-#ifdef CLARA_CONSOLE_WIDTH
- const unsigned int consoleWidth = CLARA_CONFIG_CONSOLE_WIDTH;
-#else
- const unsigned int consoleWidth = 80;
-#endif
-
- using namespace Tbc;
-
- inline bool startsWith( std::string const& str, std::string const& prefix ) {
- return str.size() >= prefix.size() && str.substr( 0, prefix.size() ) == prefix;
- }
-
- template<typename T> struct RemoveConstRef{ typedef T type; };
- template<typename T> struct RemoveConstRef<T&>{ typedef T type; };
- template<typename T> struct RemoveConstRef<T const&>{ typedef T type; };
- template<typename T> struct RemoveConstRef<T const>{ typedef T type; };
-
- template<typename T> struct IsBool { static const bool value = false; };
- template<> struct IsBool<bool> { static const bool value = true; };
-
- template<typename T>
- void convertInto( std::string const& _source, T& _dest ) {
- std::stringstream ss;
- ss << _source;
- ss >> _dest;
- if( ss.fail() )
- throw std::runtime_error( "Unable to convert " + _source + " to destination type" );
- }
- inline void convertInto( std::string const& _source, std::string& _dest ) {
- _dest = _source;
- }
- inline void convertInto( std::string const& _source, bool& _dest ) {
- std::string sourceLC = _source;
- std::transform( sourceLC.begin(), sourceLC.end(), sourceLC.begin(), ::tolower );
- if( sourceLC == "y" || sourceLC == "1" || sourceLC == "true" || sourceLC == "yes" || sourceLC == "on" )
- _dest = true;
- else if( sourceLC == "n" || sourceLC == "0" || sourceLC == "false" || sourceLC == "no" || sourceLC == "off" )
- _dest = false;
- else
- throw std::runtime_error( "Expected a boolean value but did not recognise:\n '" + _source + "'" );
- }
- inline void convertInto( bool _source, bool& _dest ) {
- _dest = _source;
- }
- template<typename T>
- inline void convertInto( bool, T& ) {
- throw std::runtime_error( "Invalid conversion" );
- }
-
- template<typename ConfigT>
- struct IArgFunction {
- virtual ~IArgFunction() {}
-# ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS
- IArgFunction() = default;
- IArgFunction( IArgFunction const& ) = default;
-# endif
- virtual void set( ConfigT& config, std::string const& value ) const = 0;
- virtual void setFlag( ConfigT& config ) const = 0;
- virtual bool takesArg() const = 0;
- virtual IArgFunction* clone() const = 0;
- };
-
- template<typename ConfigT>
- class BoundArgFunction {
- public:
- BoundArgFunction() : functionObj( NULL ) {}
- BoundArgFunction( IArgFunction<ConfigT>* _functionObj ) : functionObj( _functionObj ) {}
- BoundArgFunction( BoundArgFunction const& other ) : functionObj( other.functionObj ? other.functionObj->clone() : NULL ) {}
- BoundArgFunction& operator = ( BoundArgFunction const& other ) {
- IArgFunction<ConfigT>* newFunctionObj = other.functionObj ? other.functionObj->clone() : NULL;
- delete functionObj;
- functionObj = newFunctionObj;
- return *this;
- }
- ~BoundArgFunction() { delete functionObj; }
-
- void set( ConfigT& config, std::string const& value ) const {
- functionObj->set( config, value );
- }
- void setFlag( ConfigT& config ) const {
- functionObj->setFlag( config );
- }
- bool takesArg() const { return functionObj->takesArg(); }
-
- bool isSet() const {
- return functionObj != NULL;
- }
- private:
- IArgFunction<ConfigT>* functionObj;
- };
-
- template<typename C>
- struct NullBinder : IArgFunction<C>{
- virtual void set( C&, std::string const& ) const {}
- virtual void setFlag( C& ) const {}
- virtual bool takesArg() const { return true; }
- virtual IArgFunction<C>* clone() const { return new NullBinder( *this ); }
- };
-
- template<typename C, typename M>
- struct BoundDataMember : IArgFunction<C>{
- BoundDataMember( M C::* _member ) : member( _member ) {}
- virtual void set( C& p, std::string const& stringValue ) const {
- convertInto( stringValue, p.*member );
- }
- virtual void setFlag( C& p ) const {
- convertInto( true, p.*member );
- }
- virtual bool takesArg() const { return !IsBool<M>::value; }
- virtual IArgFunction<C>* clone() const { return new BoundDataMember( *this ); }
- M C::* member;
- };
- template<typename C, typename M>
- struct BoundUnaryMethod : IArgFunction<C>{
- BoundUnaryMethod( void (C::*_member)( M ) ) : member( _member ) {}
- virtual void set( C& p, std::string const& stringValue ) const {
- typename RemoveConstRef<M>::type value;
- convertInto( stringValue, value );
- (p.*member)( value );
- }
- virtual void setFlag( C& p ) const {
- typename RemoveConstRef<M>::type value;
- convertInto( true, value );
- (p.*member)( value );
- }
- virtual bool takesArg() const { return !IsBool<M>::value; }
- virtual IArgFunction<C>* clone() const { return new BoundUnaryMethod( *this ); }
- void (C::*member)( M );
- };
- template<typename C>
- struct BoundNullaryMethod : IArgFunction<C>{
- BoundNullaryMethod( void (C::*_member)() ) : member( _member ) {}
- virtual void set( C& p, std::string const& stringValue ) const {
- bool value;
- convertInto( stringValue, value );
- if( value )
- (p.*member)();
- }
- virtual void setFlag( C& p ) const {
- (p.*member)();
- }
- virtual bool takesArg() const { return false; }
- virtual IArgFunction<C>* clone() const { return new BoundNullaryMethod( *this ); }
- void (C::*member)();
- };
-
- template<typename C>
- struct BoundUnaryFunction : IArgFunction<C>{
- BoundUnaryFunction( void (*_function)( C& ) ) : function( _function ) {}
- virtual void set( C& obj, std::string const& stringValue ) const {
- bool value;
- convertInto( stringValue, value );
- if( value )
- function( obj );
- }
- virtual void setFlag( C& p ) const {
- function( p );
- }
- virtual bool takesArg() const { return false; }
- virtual IArgFunction<C>* clone() const { return new BoundUnaryFunction( *this ); }
- void (*function)( C& );
- };
-
- template<typename C, typename T>
- struct BoundBinaryFunction : IArgFunction<C>{
- BoundBinaryFunction( void (*_function)( C&, T ) ) : function( _function ) {}
- virtual void set( C& obj, std::string const& stringValue ) const {
- typename RemoveConstRef<T>::type value;
- convertInto( stringValue, value );
- function( obj, value );
- }
- virtual void setFlag( C& obj ) const {
- typename RemoveConstRef<T>::type value;
- convertInto( true, value );
- function( obj, value );
- }
- virtual bool takesArg() const { return !IsBool<T>::value; }
- virtual IArgFunction<C>* clone() const { return new BoundBinaryFunction( *this ); }
- void (*function)( C&, T );
- };
-
- } // namespace Detail
-
- struct Parser {
- Parser() : separators( " \t=:" ) {}
-
- struct Token {
- enum Type { Positional, ShortOpt, LongOpt };
- Token( Type _type, std::string const& _data ) : type( _type ), data( _data ) {}
- Type type;
- std::string data;
- };
-
- void parseIntoTokens( int argc, char const * const * argv, std::vector<Parser::Token>& tokens ) const {
- const std::string doubleDash = "--";
- for( int i = 1; i < argc && argv[i] != doubleDash; ++i )
- parseIntoTokens( argv[i] , tokens);
- }
- void parseIntoTokens( std::string arg, std::vector<Parser::Token>& tokens ) const {
- while( !arg.empty() ) {
- Parser::Token token( Parser::Token::Positional, arg );
- arg = "";
- if( token.data[0] == '-' ) {
- if( token.data.size() > 1 && token.data[1] == '-' ) {
- token = Parser::Token( Parser::Token::LongOpt, token.data.substr( 2 ) );
- }
- else {
- token = Parser::Token( Parser::Token::ShortOpt, token.data.substr( 1 ) );
- if( token.data.size() > 1 && separators.find( token.data[1] ) == std::string::npos ) {
- arg = "-" + token.data.substr( 1 );
- token.data = token.data.substr( 0, 1 );
- }
- }
- }
- if( token.type != Parser::Token::Positional ) {
- std::size_t pos = token.data.find_first_of( separators );
- if( pos != std::string::npos ) {
- arg = token.data.substr( pos+1 );
- token.data = token.data.substr( 0, pos );
- }
- }
- tokens.push_back( token );
- }
- }
- std::string separators;
- };
-
- template<typename ConfigT>
- struct CommonArgProperties {
- CommonArgProperties() {}
- CommonArgProperties( Detail::BoundArgFunction<ConfigT> const& _boundField ) : boundField( _boundField ) {}
-
- Detail::BoundArgFunction<ConfigT> boundField;
- std::string description;
- std::string detail;
- std::string placeholder; // Only value if boundField takes an arg
-
- bool takesArg() const {
- return !placeholder.empty();
- }
- void validate() const {
- if( !boundField.isSet() )
- throw std::logic_error( "option not bound" );
- }
- };
- struct OptionArgProperties {
- std::vector<std::string> shortNames;
- std::string longName;
-
- bool hasShortName( std::string const& shortName ) const {
- return std::find( shortNames.begin(), shortNames.end(), shortName ) != shortNames.end();
- }
- bool hasLongName( std::string const& _longName ) const {
- return _longName == longName;
- }
- };
- struct PositionalArgProperties {
- PositionalArgProperties() : position( -1 ) {}
- int position; // -1 means non-positional (floating)
-
- bool isFixedPositional() const {
- return position != -1;
- }
- };
-
- template<typename ConfigT>
- class CommandLine {
-
- struct Arg : CommonArgProperties<ConfigT>, OptionArgProperties, PositionalArgProperties {
- Arg() {}
- Arg( Detail::BoundArgFunction<ConfigT> const& _boundField ) : CommonArgProperties<ConfigT>( _boundField ) {}
-
- using CommonArgProperties<ConfigT>::placeholder; // !TBD
-
- std::string dbgName() const {
- if( !longName.empty() )
- return "--" + longName;
- if( !shortNames.empty() )
- return "-" + shortNames[0];
- return "positional args";
- }
- std::string commands() const {
- std::ostringstream oss;
- bool first = true;
- std::vector<std::string>::const_iterator it = shortNames.begin(), itEnd = shortNames.end();
- for(; it != itEnd; ++it ) {
- if( first )
- first = false;
- else
- oss << ", ";
- oss << "-" << *it;
- }
- if( !longName.empty() ) {
- if( !first )
- oss << ", ";
- oss << "--" << longName;
- }
- if( !placeholder.empty() )
- oss << " <" << placeholder << ">";
- return oss.str();
- }
- };
-
- // NOTE: std::auto_ptr is deprecated in c++11/c++0x
-#if defined(__cplusplus) && __cplusplus > 199711L
- typedef std::unique_ptr<Arg> ArgAutoPtr;
-#else
- typedef std::auto_ptr<Arg> ArgAutoPtr;
-#endif
-
- friend void addOptName( Arg& arg, std::string const& optName )
- {
- if( optName.empty() )
- return;
- if( Detail::startsWith( optName, "--" ) ) {
- if( !arg.longName.empty() )
- throw std::logic_error( "Only one long opt may be specified. '"
- + arg.longName
- + "' already specified, now attempting to add '"
- + optName + "'" );
- arg.longName = optName.substr( 2 );
- }
- else if( Detail::startsWith( optName, "-" ) )
- arg.shortNames.push_back( optName.substr( 1 ) );
- else
- throw std::logic_error( "option must begin with - or --. Option was: '" + optName + "'" );
- }
- friend void setPositionalArg( Arg& arg, int position )
- {
- arg.position = position;
- }
-
- class ArgBuilder {
- public:
- ArgBuilder( Arg* arg ) : m_arg( arg ) {}
-
- // Bind a non-boolean data member (requires placeholder string)
- template<typename C, typename M>
- void bind( M C::* field, std::string const& placeholder ) {
- m_arg->boundField = new Detail::BoundDataMember<C,M>( field );
- m_arg->placeholder = placeholder;
- }
- // Bind a boolean data member (no placeholder required)
- template<typename C>
- void bind( bool C::* field ) {
- m_arg->boundField = new Detail::BoundDataMember<C,bool>( field );
- }
-
- // Bind a method taking a single, non-boolean argument (requires a placeholder string)
- template<typename C, typename M>
- void bind( void (C::* unaryMethod)( M ), std::string const& placeholder ) {
- m_arg->boundField = new Detail::BoundUnaryMethod<C,M>( unaryMethod );
- m_arg->placeholder = placeholder;
- }
-
- // Bind a method taking a single, boolean argument (no placeholder string required)
- template<typename C>
- void bind( void (C::* unaryMethod)( bool ) ) {
- m_arg->boundField = new Detail::BoundUnaryMethod<C,bool>( unaryMethod );
- }
-
- // Bind a method that takes no arguments (will be called if opt is present)
- template<typename C>
- void bind( void (C::* nullaryMethod)() ) {
- m_arg->boundField = new Detail::BoundNullaryMethod<C>( nullaryMethod );
- }
-
- // Bind a free function taking a single argument - the object to operate on (no placeholder string required)
- template<typename C>
- void bind( void (* unaryFunction)( C& ) ) {
- m_arg->boundField = new Detail::BoundUnaryFunction<C>( unaryFunction );
- }
-
- // Bind a free function taking a single argument - the object to operate on (requires a placeholder string)
- template<typename C, typename T>
- void bind( void (* binaryFunction)( C&, T ), std::string const& placeholder ) {
- m_arg->boundField = new Detail::BoundBinaryFunction<C, T>( binaryFunction );
- m_arg->placeholder = placeholder;
- }
-
- ArgBuilder& describe( std::string const& description ) {
- m_arg->description = description;
- return *this;
- }
- ArgBuilder& detail( std::string const& detail ) {
- m_arg->detail = detail;
- return *this;
- }
-
- protected:
- Arg* m_arg;
- };
-
- class OptBuilder : public ArgBuilder {
- public:
- OptBuilder( Arg* arg ) : ArgBuilder( arg ) {}
- OptBuilder( OptBuilder& other ) : ArgBuilder( other ) {}
-
- OptBuilder& operator[]( std::string const& optName ) {
- addOptName( *ArgBuilder::m_arg, optName );
- return *this;
- }
- };
-
- public:
-
- CommandLine()
- : m_boundProcessName( new Detail::NullBinder<ConfigT>() ),
- m_highestSpecifiedArgPosition( 0 ),
- m_throwOnUnrecognisedTokens( false )
- {}
- CommandLine( CommandLine const& other )
- : m_boundProcessName( other.m_boundProcessName ),
- m_options ( other.m_options ),
- m_positionalArgs( other.m_positionalArgs ),
- m_highestSpecifiedArgPosition( other.m_highestSpecifiedArgPosition ),
- m_throwOnUnrecognisedTokens( other.m_throwOnUnrecognisedTokens )
- {
- if( other.m_floatingArg.get() )
- m_floatingArg.reset( new Arg( *other.m_floatingArg ) );
- }
-
- CommandLine& setThrowOnUnrecognisedTokens( bool shouldThrow = true ) {
- m_throwOnUnrecognisedTokens = shouldThrow;
- return *this;
- }
-
- OptBuilder operator[]( std::string const& optName ) {
- m_options.push_back( Arg() );
- addOptName( m_options.back(), optName );
- OptBuilder builder( &m_options.back() );
- return builder;
- }
-
- ArgBuilder operator[]( int position ) {
- m_positionalArgs.insert( std::make_pair( position, Arg() ) );
- if( position > m_highestSpecifiedArgPosition )
- m_highestSpecifiedArgPosition = position;
- setPositionalArg( m_positionalArgs[position], position );
- ArgBuilder builder( &m_positionalArgs[position] );
- return builder;
- }
-
- // Invoke this with the _ instance
- ArgBuilder operator[]( UnpositionalTag ) {
- if( m_floatingArg.get() )
- throw std::logic_error( "Only one unpositional argument can be added" );
- m_floatingArg.reset( new Arg() );
- ArgBuilder builder( m_floatingArg.get() );
- return builder;
- }
-
- template<typename C, typename M>
- void bindProcessName( M C::* field ) {
- m_boundProcessName = new Detail::BoundDataMember<C,M>( field );
- }
- template<typename C, typename M>
- void bindProcessName( void (C::*_unaryMethod)( M ) ) {
- m_boundProcessName = new Detail::BoundUnaryMethod<C,M>( _unaryMethod );
- }
-
- void optUsage( std::ostream& os, std::size_t indent = 0, std::size_t width = Detail::consoleWidth ) const {
- typename std::vector<Arg>::const_iterator itBegin = m_options.begin(), itEnd = m_options.end(), it;
- std::size_t maxWidth = 0;
- for( it = itBegin; it != itEnd; ++it )
- maxWidth = (std::max)( maxWidth, it->commands().size() );
-
- for( it = itBegin; it != itEnd; ++it ) {
- Detail::Text usage( it->commands(), Detail::TextAttributes()
- .setWidth( maxWidth+indent )
- .setIndent( indent ) );
- Detail::Text desc( it->description, Detail::TextAttributes()
- .setWidth( width - maxWidth - 3 ) );
-
- for( std::size_t i = 0; i < (std::max)( usage.size(), desc.size() ); ++i ) {
- std::string usageCol = i < usage.size() ? usage[i] : "";
- os << usageCol;
-
- if( i < desc.size() && !desc[i].empty() )
- os << std::string( indent + 2 + maxWidth - usageCol.size(), ' ' )
- << desc[i];
- os << "\n";
- }
- }
- }
- std::string optUsage() const {
- std::ostringstream oss;
- optUsage( oss );
- return oss.str();
- }
-
- void argSynopsis( std::ostream& os ) const {
- for( int i = 1; i <= m_highestSpecifiedArgPosition; ++i ) {
- if( i > 1 )
- os << " ";
- typename std::map<int, Arg>::const_iterator it = m_positionalArgs.find( i );
- if( it != m_positionalArgs.end() )
- os << "<" << it->second.placeholder << ">";
- else if( m_floatingArg.get() )
- os << "<" << m_floatingArg->placeholder << ">";
- else
- throw std::logic_error( "non consecutive positional arguments with no floating args" );
- }
- // !TBD No indication of mandatory args
- if( m_floatingArg.get() ) {
- if( m_highestSpecifiedArgPosition > 1 )
- os << " ";
- os << "[<" << m_floatingArg->placeholder << "> ...]";
- }
- }
- std::string argSynopsis() const {
- std::ostringstream oss;
- argSynopsis( oss );
- return oss.str();
- }
-
- void usage( std::ostream& os, std::string const& procName ) const {
- validate();
- os << "usage:\n " << procName << " ";
- argSynopsis( os );
- if( !m_options.empty() ) {
- os << " [options]\n\nwhere options are: \n";
- optUsage( os, 2 );
- }
- os << "\n";
- }
- std::string usage( std::string const& procName ) const {
- std::ostringstream oss;
- usage( oss, procName );
- return oss.str();
- }
-
- ConfigT parse( int argc, char const * const * argv ) const {
- ConfigT config;
- parseInto( argc, argv, config );
- return config;
- }
-
- std::vector<Parser::Token> parseInto( int argc, char const * const * argv, ConfigT& config ) const {
- std::string processName = argv[0];
- std::size_t lastSlash = processName.find_last_of( "/\\" );
- if( lastSlash != std::string::npos )
- processName = processName.substr( lastSlash+1 );
- m_boundProcessName.set( config, processName );
- std::vector<Parser::Token> tokens;
- Parser parser;
- parser.parseIntoTokens( argc, argv, tokens );
- return populate( tokens, config );
- }
-
- std::vector<Parser::Token> populate( std::vector<Parser::Token> const& tokens, ConfigT& config ) const {
- validate();
- std::vector<Parser::Token> unusedTokens = populateOptions( tokens, config );
- unusedTokens = populateFixedArgs( unusedTokens, config );
- unusedTokens = populateFloatingArgs( unusedTokens, config );
- return unusedTokens;
- }
-
- std::vector<Parser::Token> populateOptions( std::vector<Parser::Token> const& tokens, ConfigT& config ) const {
- std::vector<Parser::Token> unusedTokens;
- std::vector<std::string> errors;
- for( std::size_t i = 0; i < tokens.size(); ++i ) {
- Parser::Token const& token = tokens[i];
- typename std::vector<Arg>::const_iterator it = m_options.begin(), itEnd = m_options.end();
- for(; it != itEnd; ++it ) {
- Arg const& arg = *it;
-
- try {
- if( ( token.type == Parser::Token::ShortOpt && arg.hasShortName( token.data ) ) ||
- ( token.type == Parser::Token::LongOpt && arg.hasLongName( token.data ) ) ) {
- if( arg.takesArg() ) {
- if( i == tokens.size()-1 || tokens[i+1].type != Parser::Token::Positional )
- errors.push_back( "Expected argument to option: " + token.data );
- else
- arg.boundField.set( config, tokens[++i].data );
- }
- else {
- arg.boundField.setFlag( config );
- }
- break;
- }
- }
- catch( std::exception& ex ) {
- errors.push_back( std::string( ex.what() ) + "\n- while parsing: (" + arg.commands() + ")" );
- }
- }
- if( it == itEnd ) {
- if( token.type == Parser::Token::Positional || !m_throwOnUnrecognisedTokens )
- unusedTokens.push_back( token );
- else if( errors.empty() && m_throwOnUnrecognisedTokens )
- errors.push_back( "unrecognised option: " + token.data );
- }
- }
- if( !errors.empty() ) {
- std::ostringstream oss;
- for( std::vector<std::string>::const_iterator it = errors.begin(), itEnd = errors.end();
- it != itEnd;
- ++it ) {
- if( it != errors.begin() )
- oss << "\n";
- oss << *it;
- }
- throw std::runtime_error( oss.str() );
- }
- return unusedTokens;
- }
- std::vector<Parser::Token> populateFixedArgs( std::vector<Parser::Token> const& tokens, ConfigT& config ) const {
- std::vector<Parser::Token> unusedTokens;
- int position = 1;
- for( std::size_t i = 0; i < tokens.size(); ++i ) {
- Parser::Token const& token = tokens[i];
- typename std::map<int, Arg>::const_iterator it = m_positionalArgs.find( position );
- if( it != m_positionalArgs.end() )
- it->second.boundField.set( config, token.data );
- else
- unusedTokens.push_back( token );
- if( token.type == Parser::Token::Positional )
- position++;
- }
- return unusedTokens;
- }
- std::vector<Parser::Token> populateFloatingArgs( std::vector<Parser::Token> const& tokens, ConfigT& config ) const {
- if( !m_floatingArg.get() )
- return tokens;
- std::vector<Parser::Token> unusedTokens;
- for( std::size_t i = 0; i < tokens.size(); ++i ) {
- Parser::Token const& token = tokens[i];
- if( token.type == Parser::Token::Positional )
- m_floatingArg->boundField.set( config, token.data );
- else
- unusedTokens.push_back( token );
- }
- return unusedTokens;
- }
-
- void validate() const
- {
- if( m_options.empty() && m_positionalArgs.empty() && !m_floatingArg.get() )
- throw std::logic_error( "No options or arguments specified" );
-
- for( typename std::vector<Arg>::const_iterator it = m_options.begin(),
- itEnd = m_options.end();
- it != itEnd; ++it )
- it->validate();
- }
-
- private:
- Detail::BoundArgFunction<ConfigT> m_boundProcessName;
- std::vector<Arg> m_options;
- std::map<int, Arg> m_positionalArgs;
- ArgAutoPtr m_floatingArg;
- int m_highestSpecifiedArgPosition;
- bool m_throwOnUnrecognisedTokens;
- };
-
-} // end namespace Clara
-
-STITCH_CLARA_CLOSE_NAMESPACE
-#undef STITCH_CLARA_OPEN_NAMESPACE
-#undef STITCH_CLARA_CLOSE_NAMESPACE
-
-#endif // TWOBLUECUBES_CLARA_H_INCLUDED
-#undef STITCH_CLARA_OPEN_NAMESPACE
-
-// Restore Clara's value for console width, if present
-#ifdef CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH
-#define CLARA_CONFIG_CONSOLE_WIDTH CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH
-#undef CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH
-#endif
-
-#include <fstream>
-
-namespace Catch {
-
- inline void abortAfterFirst( ConfigData& config ) { config.abortAfter = 1; }
- inline void abortAfterX( ConfigData& config, int x ) {
- if( x < 1 )
- throw std::runtime_error( "Value after -x or --abortAfter must be greater than zero" );
- config.abortAfter = x;
- }
- inline void addTestOrTags( ConfigData& config, std::string const& _testSpec ) { config.testsOrTags.push_back( _testSpec ); }
-
- inline void addWarning( ConfigData& config, std::string const& _warning ) {
- if( _warning == "NoAssertions" )
- config.warnings = static_cast<WarnAbout::What>( config.warnings | WarnAbout::NoAssertions );
- else
- throw std::runtime_error( "Unrecognised warning: '" + _warning + "'" );
- }
- inline void setOrder( ConfigData& config, std::string const& order ) {
- if( startsWith( "declared", order ) )
- config.runOrder = RunTests::InDeclarationOrder;
- else if( startsWith( "lexical", order ) )
- config.runOrder = RunTests::InLexicographicalOrder;
- else if( startsWith( "random", order ) )
- config.runOrder = RunTests::InRandomOrder;
- else
- throw std::runtime_error( "Unrecognised ordering: '" + order + "'" );
- }
- inline void setRngSeed( ConfigData& config, std::string const& seed ) {
- if( seed == "time" ) {
- config.rngSeed = static_cast<unsigned int>( std::time(0) );
- }
- else {
- std::stringstream ss;
- ss << seed;
- ss >> config.rngSeed;
- if( ss.fail() )
- throw std::runtime_error( "Argment to --rng-seed should be the word 'time' or a number" );
- }
- }
- inline void setVerbosity( ConfigData& config, int level ) {
- // !TBD: accept strings?
- config.verbosity = static_cast<Verbosity::Level>( level );
- }
- inline void setShowDurations( ConfigData& config, bool _showDurations ) {
- config.showDurations = _showDurations
- ? ShowDurations::Always
- : ShowDurations::Never;
- }
- inline void loadTestNamesFromFile( ConfigData& config, std::string const& _filename ) {
- std::ifstream f( _filename.c_str() );
- if( !f.is_open() )
- throw std::domain_error( "Unable to load input file: " + _filename );
-
- std::string line;
- while( std::getline( f, line ) ) {
- line = trim(line);
- if( !line.empty() && !startsWith( line, "#" ) )
- addTestOrTags( config, "\"" + line + "\"," );
- }
- }
-
- inline Clara::CommandLine<ConfigData> makeCommandLineParser() {
-
- using namespace Clara;
- CommandLine<ConfigData> cli;
-
- cli.bindProcessName( &ConfigData::processName );
-
- cli["-?"]["-h"]["--help"]
- .describe( "display usage information" )
- .bind( &ConfigData::showHelp );
-
- cli["-l"]["--list-tests"]
- .describe( "list all/matching test cases" )
- .bind( &ConfigData::listTests );
-
- cli["-t"]["--list-tags"]
- .describe( "list all/matching tags" )
- .bind( &ConfigData::listTags );
-
- cli["-s"]["--success"]
- .describe( "include successful tests in output" )
- .bind( &ConfigData::showSuccessfulTests );
-
- cli["-b"]["--break"]
- .describe( "break into debugger on failure" )
- .bind( &ConfigData::shouldDebugBreak );
-
- cli["-e"]["--nothrow"]
- .describe( "skip exception tests" )
- .bind( &ConfigData::noThrow );
-
- cli["-i"]["--invisibles"]
- .describe( "show invisibles (tabs, newlines)" )
- .bind( &ConfigData::showInvisibles );
-
- cli["-o"]["--out"]
- .describe( "output filename" )
- .bind( &ConfigData::outputFilename, "filename" );
-
- cli["-r"]["--reporter"]
-// .placeholder( "name[:filename]" )
- .describe( "reporter to use (defaults to console)" )
- .bind( &ConfigData::reporterName, "name" );
-
- cli["-n"]["--name"]
- .describe( "suite name" )
- .bind( &ConfigData::name, "name" );
-
- cli["-a"]["--abort"]
- .describe( "abort at first failure" )
- .bind( &abortAfterFirst );
-
- cli["-x"]["--abortx"]
- .describe( "abort after x failures" )
- .bind( &abortAfterX, "no. failures" );
-
- cli["-w"]["--warn"]
- .describe( "enable warnings" )
- .bind( &addWarning, "warning name" );
-
-// - needs updating if reinstated
-// cli.into( &setVerbosity )
-// .describe( "level of verbosity (0=no output)" )
-// .shortOpt( "v")
-// .longOpt( "verbosity" )
-// .placeholder( "level" );
-
- cli[_]
- .describe( "which test or tests to use" )
- .bind( &addTestOrTags, "test name, pattern or tags" );
-
- cli["-d"]["--durations"]
- .describe( "show test durations" )
- .bind( &setShowDurations, "yes/no" );
-
- cli["-f"]["--input-file"]
- .describe( "load test names to run from a file" )
- .bind( &loadTestNamesFromFile, "filename" );
-
- // Less common commands which don't have a short form
- cli["--list-test-names-only"]
- .describe( "list all/matching test cases names only" )
- .bind( &ConfigData::listTestNamesOnly );
-
- cli["--list-reporters"]
- .describe( "list all reporters" )
- .bind( &ConfigData::listReporters );
-
- cli["--order"]
- .describe( "test case order (defaults to decl)" )
- .bind( &setOrder, "decl|lex|rand" );
-
- cli["--rng-seed"]
- .describe( "set a specific seed for random numbers" )
- .bind( &setRngSeed, "'time'|number" );
-
- cli["--force-colour"]
- .describe( "force colourised output" )
- .bind( &ConfigData::forceColour );
-
- return cli;
- }
-
-} // end namespace Catch
-
-// #included from: internal/catch_list.hpp
-#define TWOBLUECUBES_CATCH_LIST_HPP_INCLUDED
-
-// #included from: catch_text.h
-#define TWOBLUECUBES_CATCH_TEXT_H_INCLUDED
-
-#define TBC_TEXT_FORMAT_CONSOLE_WIDTH CATCH_CONFIG_CONSOLE_WIDTH
-
-#define CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE Catch
-// #included from: ../external/tbc_text_format.h
-// Only use header guard if we are not using an outer namespace
-#ifndef CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE
-# ifdef TWOBLUECUBES_TEXT_FORMAT_H_INCLUDED
-# ifndef TWOBLUECUBES_TEXT_FORMAT_H_ALREADY_INCLUDED
-# define TWOBLUECUBES_TEXT_FORMAT_H_ALREADY_INCLUDED
-# endif
-# else
-# define TWOBLUECUBES_TEXT_FORMAT_H_INCLUDED
-# endif
-#endif
-#ifndef TWOBLUECUBES_TEXT_FORMAT_H_ALREADY_INCLUDED
-#include <string>
-#include <vector>
-#include <sstream>
-
-// Use optional outer namespace
-#ifdef CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE
-namespace CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE {
-#endif
-
-namespace Tbc {
-
-#ifdef TBC_TEXT_FORMAT_CONSOLE_WIDTH
- const unsigned int consoleWidth = TBC_TEXT_FORMAT_CONSOLE_WIDTH;
-#else
- const unsigned int consoleWidth = 80;
-#endif
-
- struct TextAttributes {
- TextAttributes()
- : initialIndent( std::string::npos ),
- indent( 0 ),
- width( consoleWidth-1 ),
- tabChar( '\t' )
- {}
-
- TextAttributes& setInitialIndent( std::size_t _value ) { initialIndent = _value; return *this; }
- TextAttributes& setIndent( std::size_t _value ) { indent = _value; return *this; }
- TextAttributes& setWidth( std::size_t _value ) { width = _value; return *this; }
- TextAttributes& setTabChar( char _value ) { tabChar = _value; return *this; }
-
- std::size_t initialIndent; // indent of first line, or npos
- std::size_t indent; // indent of subsequent lines, or all if initialIndent is npos
- std::size_t width; // maximum width of text, including indent. Longer text will wrap
- char tabChar; // If this char is seen the indent is changed to current pos
- };
-
- class Text {
- public:
- Text( std::string const& _str, TextAttributes const& _attr = TextAttributes() )
- : attr( _attr )
- {
- std::string wrappableChars = " [({.,/|\\-";
- std::size_t indent = _attr.initialIndent != std::string::npos
- ? _attr.initialIndent
- : _attr.indent;
- std::string remainder = _str;
-
- while( !remainder.empty() ) {
- if( lines.size() >= 1000 ) {
- lines.push_back( "... message truncated due to excessive size" );
- return;
- }
- std::size_t tabPos = std::string::npos;
- std::size_t width = (std::min)( remainder.size(), _attr.width - indent );
- std::size_t pos = remainder.find_first_of( '\n' );
- if( pos <= width ) {
- width = pos;
- }
- pos = remainder.find_last_of( _attr.tabChar, width );
- if( pos != std::string::npos ) {
- tabPos = pos;
- if( remainder[width] == '\n' )
- width--;
- remainder = remainder.substr( 0, tabPos ) + remainder.substr( tabPos+1 );
- }
-
- if( width == remainder.size() ) {
- spliceLine( indent, remainder, width );
- }
- else if( remainder[width] == '\n' ) {
- spliceLine( indent, remainder, width );
- if( width <= 1 || remainder.size() != 1 )
- remainder = remainder.substr( 1 );
- indent = _attr.indent;
- }
- else {
- pos = remainder.find_last_of( wrappableChars, width );
- if( pos != std::string::npos && pos > 0 ) {
- spliceLine( indent, remainder, pos );
- if( remainder[0] == ' ' )
- remainder = remainder.substr( 1 );
- }
- else {
- spliceLine( indent, remainder, width-1 );
- lines.back() += "-";
- }
- if( lines.size() == 1 )
- indent = _attr.indent;
- if( tabPos != std::string::npos )
- indent += tabPos;
- }
- }
- }
-
- void spliceLine( std::size_t _indent, std::string& _remainder, std::size_t _pos ) {
- lines.push_back( std::string( _indent, ' ' ) + _remainder.substr( 0, _pos ) );
- _remainder = _remainder.substr( _pos );
- }
-
- typedef std::vector<std::string>::const_iterator const_iterator;
-
- const_iterator begin() const { return lines.begin(); }
- const_iterator end() const { return lines.end(); }
- std::string const& last() const { return lines.back(); }
- std::size_t size() const { return lines.size(); }
- std::string const& operator[]( std::size_t _index ) const { return lines[_index]; }
- std::string toString() const {
- std::ostringstream oss;
- oss << *this;
- return oss.str();
- }
-
- inline friend std::ostream& operator << ( std::ostream& _stream, Text const& _text ) {
- for( Text::const_iterator it = _text.begin(), itEnd = _text.end();
- it != itEnd; ++it ) {
- if( it != _text.begin() )
- _stream << "\n";
- _stream << *it;
- }
- return _stream;
- }
-
- private:
- std::string str;
- TextAttributes attr;
- std::vector<std::string> lines;
- };
-
-} // end namespace Tbc
-
-#ifdef CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE
-} // end outer namespace
-#endif
-
-#endif // TWOBLUECUBES_TEXT_FORMAT_H_ALREADY_INCLUDED
-#undef CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE
-
-namespace Catch {
- using Tbc::Text;
- using Tbc::TextAttributes;
-}
-
-// #included from: catch_console_colour.hpp
-#define TWOBLUECUBES_CATCH_CONSOLE_COLOUR_HPP_INCLUDED
-
-namespace Catch {
-
- struct Colour {
- enum Code {
- None = 0,
-
- White,
- Red,
- Green,
- Blue,
- Cyan,
- Yellow,
- Grey,
-
- Bright = 0x10,
-
- BrightRed = Bright | Red,
- BrightGreen = Bright | Green,
- LightGrey = Bright | Grey,
- BrightWhite = Bright | White,
-
- // By intention
- FileName = LightGrey,
- Warning = Yellow,
- ResultError = BrightRed,
- ResultSuccess = BrightGreen,
- ResultExpectedFailure = Warning,
-
- Error = BrightRed,
- Success = Green,
-
- OriginalExpression = Cyan,
- ReconstructedExpression = Yellow,
-
- SecondaryText = LightGrey,
- Headers = White
- };
-
- // Use constructed object for RAII guard
- Colour( Code _colourCode );
- Colour( Colour const& other );
- ~Colour();
-
- // Use static method for one-shot changes
- static void use( Code _colourCode );
-
- private:
- bool m_moved;
- };
-
- inline std::ostream& operator << ( std::ostream& os, Colour const& ) { return os; }
-
-} // end namespace Catch
-
-// #included from: catch_interfaces_reporter.h
-#define TWOBLUECUBES_CATCH_INTERFACES_REPORTER_H_INCLUDED
-
-#include <string>
-#include <ostream>
-#include <map>
-#include <assert.h>
-
-namespace Catch
-{
- struct ReporterConfig {
- explicit ReporterConfig( Ptr<IConfig> const& _fullConfig )
- : m_stream( &_fullConfig->stream() ), m_fullConfig( _fullConfig ) {}
-
- ReporterConfig( Ptr<IConfig> const& _fullConfig, std::ostream& _stream )
- : m_stream( &_stream ), m_fullConfig( _fullConfig ) {}
-
- std::ostream& stream() const { return *m_stream; }
- Ptr<IConfig> fullConfig() const { return m_fullConfig; }
-
- private:
- std::ostream* m_stream;
- Ptr<IConfig> m_fullConfig;
- };
-
- struct ReporterPreferences {
- ReporterPreferences()
- : shouldRedirectStdOut( false )
- {}
-
- bool shouldRedirectStdOut;
- };
-
- template<typename T>
- struct LazyStat : Option<T> {
- LazyStat() : used( false ) {}
- LazyStat& operator=( T const& _value ) {
- Option<T>::operator=( _value );
- used = false;
- return *this;
- }
- void reset() {
- Option<T>::reset();
- used = false;
- }
- bool used;
- };
-
- struct TestRunInfo {
- TestRunInfo( std::string const& _name ) : name( _name ) {}
- std::string name;
- };
- struct GroupInfo {
- GroupInfo( std::string const& _name,
- std::size_t _groupIndex,
- std::size_t _groupsCount )
- : name( _name ),
- groupIndex( _groupIndex ),
- groupsCounts( _groupsCount )
- {}
-
- std::string name;
- std::size_t groupIndex;
- std::size_t groupsCounts;
- };
-
- struct AssertionStats {
- AssertionStats( AssertionResult const& _assertionResult,
- std::vector<MessageInfo> const& _infoMessages,
- Totals const& _totals )
- : assertionResult( _assertionResult ),
- infoMessages( _infoMessages ),
- totals( _totals )
- {
- if( assertionResult.hasMessage() ) {
- // Copy message into messages list.
- // !TBD This should have been done earlier, somewhere
- MessageBuilder builder( assertionResult.getTestMacroName(), assertionResult.getSourceInfo(), assertionResult.getResultType() );
- builder << assertionResult.getMessage();
- builder.m_info.message = builder.m_stream.str();
-
- infoMessages.push_back( builder.m_info );
- }
- }
- virtual ~AssertionStats();
-
-# ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS
- AssertionStats( AssertionStats const& ) = default;
- AssertionStats( AssertionStats && ) = default;
- AssertionStats& operator = ( AssertionStats const& ) = default;
- AssertionStats& operator = ( AssertionStats && ) = default;
-# endif
-
- AssertionResult assertionResult;
- std::vector<MessageInfo> infoMessages;
- Totals totals;
- };
-
- struct SectionStats {
- SectionStats( SectionInfo const& _sectionInfo,
- Counts const& _assertions,
- double _durationInSeconds,
- bool _missingAssertions )
- : sectionInfo( _sectionInfo ),
- assertions( _assertions ),
- durationInSeconds( _durationInSeconds ),
- missingAssertions( _missingAssertions )
- {}
- virtual ~SectionStats();
-# ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS
- SectionStats( SectionStats const& ) = default;
- SectionStats( SectionStats && ) = default;
- SectionStats& operator = ( SectionStats const& ) = default;
- SectionStats& operator = ( SectionStats && ) = default;
-# endif
-
- SectionInfo sectionInfo;
- Counts assertions;
- double durationInSeconds;
- bool missingAssertions;
- };
-
- struct TestCaseStats {
- TestCaseStats( TestCaseInfo const& _testInfo,
- Totals const& _totals,
- std::string const& _stdOut,
- std::string const& _stdErr,
- bool _aborting )
- : testInfo( _testInfo ),
- totals( _totals ),
- stdOut( _stdOut ),
- stdErr( _stdErr ),
- aborting( _aborting )
- {}
- virtual ~TestCaseStats();
-
-# ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS
- TestCaseStats( TestCaseStats const& ) = default;
- TestCaseStats( TestCaseStats && ) = default;
- TestCaseStats& operator = ( TestCaseStats const& ) = default;
- TestCaseStats& operator = ( TestCaseStats && ) = default;
-# endif
-
- TestCaseInfo testInfo;
- Totals totals;
- std::string stdOut;
- std::string stdErr;
- bool aborting;
- };
-
- struct TestGroupStats {
- TestGroupStats( GroupInfo const& _groupInfo,
- Totals const& _totals,
- bool _aborting )
- : groupInfo( _groupInfo ),
- totals( _totals ),
- aborting( _aborting )
- {}
- TestGroupStats( GroupInfo const& _groupInfo )
- : groupInfo( _groupInfo ),
- aborting( false )
- {}
- virtual ~TestGroupStats();
-
-# ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS
- TestGroupStats( TestGroupStats const& ) = default;
- TestGroupStats( TestGroupStats && ) = default;
- TestGroupStats& operator = ( TestGroupStats const& ) = default;
- TestGroupStats& operator = ( TestGroupStats && ) = default;
-# endif
-
- GroupInfo groupInfo;
- Totals totals;
- bool aborting;
- };
-
- struct TestRunStats {
- TestRunStats( TestRunInfo const& _runInfo,
- Totals const& _totals,
- bool _aborting )
- : runInfo( _runInfo ),
- totals( _totals ),
- aborting( _aborting )
- {}
- virtual ~TestRunStats();
-
-# ifndef CATCH_CONFIG_CPP11_GENERATED_METHODS
- TestRunStats( TestRunStats const& _other )
- : runInfo( _other.runInfo ),
- totals( _other.totals ),
- aborting( _other.aborting )
- {}
-# else
- TestRunStats( TestRunStats const& ) = default;
- TestRunStats( TestRunStats && ) = default;
- TestRunStats& operator = ( TestRunStats const& ) = default;
- TestRunStats& operator = ( TestRunStats && ) = default;
-# endif
-
- TestRunInfo runInfo;
- Totals totals;
- bool aborting;
- };
-
- struct IStreamingReporter : IShared {
- virtual ~IStreamingReporter();
-
- // Implementing class must also provide the following static method:
- // static std::string getDescription();
-
- virtual ReporterPreferences getPreferences() const = 0;
-
- virtual void noMatchingTestCases( std::string const& spec ) = 0;
-
- virtual void testRunStarting( TestRunInfo const& testRunInfo ) = 0;
- virtual void testGroupStarting( GroupInfo const& groupInfo ) = 0;
-
- virtual void testCaseStarting( TestCaseInfo const& testInfo ) = 0;
- virtual void sectionStarting( SectionInfo const& sectionInfo ) = 0;
-
- virtual void assertionStarting( AssertionInfo const& assertionInfo ) = 0;
-
- // The return value indicates if the messages buffer should be cleared:
- virtual bool assertionEnded( AssertionStats const& assertionStats ) = 0;
- virtual void sectionEnded( SectionStats const& sectionStats ) = 0;
- virtual void testCaseEnded( TestCaseStats const& testCaseStats ) = 0;
- virtual void testGroupEnded( TestGroupStats const& testGroupStats ) = 0;
- virtual void testRunEnded( TestRunStats const& testRunStats ) = 0;
-
- virtual void skipTest( TestCaseInfo const& testInfo ) = 0;
- };
-
- struct IReporterFactory {
- virtual ~IReporterFactory();
- virtual IStreamingReporter* create( ReporterConfig const& config ) const = 0;
- virtual std::string getDescription() const = 0;
- };
-
- struct IReporterRegistry {
- typedef std::map<std::string, IReporterFactory*> FactoryMap;
-
- virtual ~IReporterRegistry();
- virtual IStreamingReporter* create( std::string const& name, Ptr<IConfig> const& config ) const = 0;
- virtual FactoryMap const& getFactories() const = 0;
- };
-
-}
-
-#include <limits>
-#include <algorithm>
-
-namespace Catch {
-
- inline std::size_t listTests( Config const& config ) {
-
- TestSpec testSpec = config.testSpec();
- if( config.testSpec().hasFilters() )
- Catch::cout() << "Matching test cases:\n";
- else {
- Catch::cout() << "All available test cases:\n";
- testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "*" ).testSpec();
- }
-
- std::size_t matchedTests = 0;
- TextAttributes nameAttr, tagsAttr;
- nameAttr.setInitialIndent( 2 ).setIndent( 4 );
- tagsAttr.setIndent( 6 );
-
- std::vector<TestCase> matchedTestCases;
- getRegistryHub().getTestCaseRegistry().getFilteredTests( testSpec, config, matchedTestCases );
- for( std::vector<TestCase>::const_iterator it = matchedTestCases.begin(), itEnd = matchedTestCases.end();
- it != itEnd;
- ++it ) {
- matchedTests++;
- TestCaseInfo const& testCaseInfo = it->getTestCaseInfo();
- Colour::Code colour = testCaseInfo.isHidden()
- ? Colour::SecondaryText
- : Colour::None;
- Colour colourGuard( colour );
-
- Catch::cout() << Text( testCaseInfo.name, nameAttr ) << std::endl;
- if( !testCaseInfo.tags.empty() )
- Catch::cout() << Text( testCaseInfo.tagsAsString, tagsAttr ) << std::endl;
- }
-
- if( !config.testSpec().hasFilters() )
- Catch::cout() << pluralise( matchedTests, "test case" ) << "\n" << std::endl;
- else
- Catch::cout() << pluralise( matchedTests, "matching test case" ) << "\n" << std::endl;
- return matchedTests;
- }
-
- inline std::size_t listTestsNamesOnly( Config const& config ) {
- TestSpec testSpec = config.testSpec();
- if( !config.testSpec().hasFilters() )
- testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "*" ).testSpec();
- std::size_t matchedTests = 0;
- std::vector<TestCase> matchedTestCases;
- getRegistryHub().getTestCaseRegistry().getFilteredTests( testSpec, config, matchedTestCases );
- for( std::vector<TestCase>::const_iterator it = matchedTestCases.begin(), itEnd = matchedTestCases.end();
- it != itEnd;
- ++it ) {
- matchedTests++;
- TestCaseInfo const& testCaseInfo = it->getTestCaseInfo();
- Catch::cout() << testCaseInfo.name << std::endl;
- }
- return matchedTests;
- }
-
- struct TagInfo {
- TagInfo() : count ( 0 ) {}
- void add( std::string const& spelling ) {
- ++count;
- spellings.insert( spelling );
- }
- std::string all() const {
- std::string out;
- for( std::set<std::string>::const_iterator it = spellings.begin(), itEnd = spellings.end();
- it != itEnd;
- ++it )
- out += "[" + *it + "]";
- return out;
- }
- std::set<std::string> spellings;
- std::size_t count;
- };
-
- inline std::size_t listTags( Config const& config ) {
- TestSpec testSpec = config.testSpec();
- if( config.testSpec().hasFilters() )
- Catch::cout() << "Tags for matching test cases:\n";
- else {
- Catch::cout() << "All available tags:\n";
- testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "*" ).testSpec();
- }
-
- std::map<std::string, TagInfo> tagCounts;
-
- std::vector<TestCase> matchedTestCases;
- getRegistryHub().getTestCaseRegistry().getFilteredTests( testSpec, config, matchedTestCases );
- for( std::vector<TestCase>::const_iterator it = matchedTestCases.begin(), itEnd = matchedTestCases.end();
- it != itEnd;
- ++it ) {
- for( std::set<std::string>::const_iterator tagIt = it->getTestCaseInfo().tags.begin(),
- tagItEnd = it->getTestCaseInfo().tags.end();
- tagIt != tagItEnd;
- ++tagIt ) {
- std::string tagName = *tagIt;
- std::string lcaseTagName = toLower( tagName );
- std::map<std::string, TagInfo>::iterator countIt = tagCounts.find( lcaseTagName );
- if( countIt == tagCounts.end() )
- countIt = tagCounts.insert( std::make_pair( lcaseTagName, TagInfo() ) ).first;
- countIt->second.add( tagName );
- }
- }
-
- for( std::map<std::string, TagInfo>::const_iterator countIt = tagCounts.begin(),
- countItEnd = tagCounts.end();
- countIt != countItEnd;
- ++countIt ) {
- std::ostringstream oss;
- oss << " " << std::setw(2) << countIt->second.count << " ";
- Text wrapper( countIt->second.all(), TextAttributes()
- .setInitialIndent( 0 )
- .setIndent( oss.str().size() )
- .setWidth( CATCH_CONFIG_CONSOLE_WIDTH-10 ) );
- Catch::cout() << oss.str() << wrapper << "\n";
- }
- Catch::cout() << pluralise( tagCounts.size(), "tag" ) << "\n" << std::endl;
- return tagCounts.size();
- }
-
- inline std::size_t listReporters( Config const& /*config*/ ) {
- Catch::cout() << "Available reporters:\n";
- IReporterRegistry::FactoryMap const& factories = getRegistryHub().getReporterRegistry().getFactories();
- IReporterRegistry::FactoryMap::const_iterator itBegin = factories.begin(), itEnd = factories.end(), it;
- std::size_t maxNameLen = 0;
- for(it = itBegin; it != itEnd; ++it )
- maxNameLen = (std::max)( maxNameLen, it->first.size() );
-
- for(it = itBegin; it != itEnd; ++it ) {
- Text wrapper( it->second->getDescription(), TextAttributes()
- .setInitialIndent( 0 )
- .setIndent( 7+maxNameLen )
- .setWidth( CATCH_CONFIG_CONSOLE_WIDTH - maxNameLen-8 ) );
- Catch::cout() << " "
- << it->first
- << ":"
- << std::string( maxNameLen - it->first.size() + 2, ' ' )
- << wrapper << "\n";
- }
- Catch::cout() << std::endl;
- return factories.size();
- }
-
- inline Option<std::size_t> list( Config const& config ) {
- Option<std::size_t> listedCount;
- if( config.listTests() )
- listedCount = listedCount.valueOr(0) + listTests( config );
- if( config.listTestNamesOnly() )
- listedCount = listedCount.valueOr(0) + listTestsNamesOnly( config );
- if( config.listTags() )
- listedCount = listedCount.valueOr(0) + listTags( config );
- if( config.listReporters() )
- listedCount = listedCount.valueOr(0) + listReporters( config );
- return listedCount;
- }
-
-} // end namespace Catch
-
-// #included from: internal/catch_runner_impl.hpp
-#define TWOBLUECUBES_CATCH_RUNNER_IMPL_HPP_INCLUDED
-
-// #included from: catch_test_case_tracker.hpp
-#define TWOBLUECUBES_CATCH_TEST_CASE_TRACKER_HPP_INCLUDED
-
-#include <map>
-#include <string>
-#include <assert.h>
-
-namespace Catch {
-namespace SectionTracking {
-
- class TrackedSection {
-
- typedef std::map<std::string, TrackedSection> TrackedSections;
-
- public:
- enum RunState {
- NotStarted,
- Executing,
- ExecutingChildren,
- Completed
- };
-
- TrackedSection( std::string const& name, TrackedSection* parent )
- : m_name( name ), m_runState( NotStarted ), m_parent( parent )
- {}
-
- RunState runState() const { return m_runState; }
-
- TrackedSection* findChild( std::string const& childName );
- TrackedSection* acquireChild( std::string const& childName );
-
- void enter() {
- if( m_runState == NotStarted )
- m_runState = Executing;
- }
- void leave();
-
- TrackedSection* getParent() {
- return m_parent;
- }
- bool hasChildren() const {
- return !m_children.empty();
- }
-
- private:
- std::string m_name;
- RunState m_runState;
- TrackedSections m_children;
- TrackedSection* m_parent;
- };
-
- inline TrackedSection* TrackedSection::findChild( std::string const& childName ) {
- TrackedSections::iterator it = m_children.find( childName );
- return it != m_children.end()
- ? &it->second
- : NULL;
- }
- inline TrackedSection* TrackedSection::acquireChild( std::string const& childName ) {
- if( TrackedSection* child = findChild( childName ) )
- return child;
- m_children.insert( std::make_pair( childName, TrackedSection( childName, this ) ) );
- return findChild( childName );
- }
- inline void TrackedSection::leave() {
- for( TrackedSections::const_iterator it = m_children.begin(), itEnd = m_children.end();
- it != itEnd;
- ++it )
- if( it->second.runState() != Completed ) {
- m_runState = ExecutingChildren;
- return;
- }
- m_runState = Completed;
- }
-
- class TestCaseTracker {
- public:
- TestCaseTracker( std::string const& testCaseName )
- : m_testCase( testCaseName, NULL ),
- m_currentSection( &m_testCase ),
- m_completedASectionThisRun( false )
- {}
-
- bool enterSection( std::string const& name ) {
- TrackedSection* child = m_currentSection->acquireChild( name );
- if( m_completedASectionThisRun || child->runState() == TrackedSection::Completed )
- return false;
-
- m_currentSection = child;
- m_currentSection->enter();
- return true;
- }
- void leaveSection() {
- m_currentSection->leave();
- m_currentSection = m_currentSection->getParent();
- assert( m_currentSection != NULL );
- m_completedASectionThisRun = true;
- }
-
- bool currentSectionHasChildren() const {
- return m_currentSection->hasChildren();
- }
- bool isCompleted() const {
- return m_testCase.runState() == TrackedSection::Completed;
- }
-
- class Guard {
- public:
- Guard( TestCaseTracker& tracker ) : m_tracker( tracker ) {
- m_tracker.enterTestCase();
- }
- ~Guard() {
- m_tracker.leaveTestCase();
- }
- private:
- Guard( Guard const& );
- void operator = ( Guard const& );
- TestCaseTracker& m_tracker;
- };
-
- private:
- void enterTestCase() {
- m_currentSection = &m_testCase;
- m_completedASectionThisRun = false;
- m_testCase.enter();
- }
- void leaveTestCase() {
- m_testCase.leave();
- }
-
- TrackedSection m_testCase;
- TrackedSection* m_currentSection;
- bool m_completedASectionThisRun;
- };
-
-} // namespace SectionTracking
-
-using SectionTracking::TestCaseTracker;
-
-} // namespace Catch
-
-// #included from: catch_fatal_condition.hpp
-#define TWOBLUECUBES_CATCH_FATAL_CONDITION_H_INCLUDED
-
-namespace Catch {
-
- // Report the error condition then exit the process
- inline void fatal( std::string const& message, int exitCode ) {
- IContext& context = Catch::getCurrentContext();
- IResultCapture* resultCapture = context.getResultCapture();
- resultCapture->handleFatalErrorCondition( message );
-
- if( Catch::alwaysTrue() ) // avoids "no return" warnings
- exit( exitCode );
- }
-
-} // namespace Catch
-
-#if defined ( CATCH_PLATFORM_WINDOWS ) /////////////////////////////////////////
-
-namespace Catch {
-
- struct FatalConditionHandler {
- void reset() {}
- };
-
-} // namespace Catch
-
-#else // Not Windows - assumed to be POSIX compatible //////////////////////////
-
-#include <signal.h>
-
-namespace Catch {
-
- struct SignalDefs { int id; const char* name; };
- extern SignalDefs signalDefs[];
- SignalDefs signalDefs[] = {
- { SIGINT, "SIGINT - Terminal interrupt signal" },
- { SIGILL, "SIGILL - Illegal instruction signal" },
- { SIGFPE, "SIGFPE - Floating point error signal" },
- { SIGSEGV, "SIGSEGV - Segmentation violation signal" },
- { SIGTERM, "SIGTERM - Termination request signal" },
- { SIGABRT, "SIGABRT - Abort (abnormal termination) signal" }
- };
-
- struct FatalConditionHandler {
-
- static void handleSignal( int sig ) {
- for( std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i )
- if( sig == signalDefs[i].id )
- fatal( signalDefs[i].name, -sig );
- fatal( "<unknown signal>", -sig );
- }
-
- FatalConditionHandler() : m_isSet( true ) {
- for( std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i )
- signal( signalDefs[i].id, handleSignal );
- }
- ~FatalConditionHandler() {
- reset();
- }
- void reset() {
- if( m_isSet ) {
- for( std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i )
- signal( signalDefs[i].id, SIG_DFL );
- m_isSet = false;
- }
- }
-
- bool m_isSet;
- };
-
-} // namespace Catch
-
-#endif // not Windows
-
-#include <set>
-#include <string>
-
-namespace Catch {
-
- class StreamRedirect {
-
- public:
- StreamRedirect( std::ostream& stream, std::string& targetString )
- : m_stream( stream ),
- m_prevBuf( stream.rdbuf() ),
- m_targetString( targetString )
- {
- stream.rdbuf( m_oss.rdbuf() );
- }
-
- ~StreamRedirect() {
- m_targetString += m_oss.str();
- m_stream.rdbuf( m_prevBuf );
- }
-
- private:
- std::ostream& m_stream;
- std::streambuf* m_prevBuf;
- std::ostringstream m_oss;
- std::string& m_targetString;
- };
-
- ///////////////////////////////////////////////////////////////////////////
-
- class RunContext : public IResultCapture, public IRunner {
-
- RunContext( RunContext const& );
- void operator =( RunContext const& );
-
- public:
-
- explicit RunContext( Ptr<IConfig const> const& config, Ptr<IStreamingReporter> const& reporter )
- : m_runInfo( config->name() ),
- m_context( getCurrentMutableContext() ),
- m_activeTestCase( NULL ),
- m_config( config ),
- m_reporter( reporter ),
- m_prevRunner( m_context.getRunner() ),
- m_prevResultCapture( m_context.getResultCapture() ),
- m_prevConfig( m_context.getConfig() )
- {
- m_context.setRunner( this );
- m_context.setConfig( m_config );
- m_context.setResultCapture( this );
- m_reporter->testRunStarting( m_runInfo );
- }
-
- virtual ~RunContext() {
- m_reporter->testRunEnded( TestRunStats( m_runInfo, m_totals, aborting() ) );
- m_context.setRunner( m_prevRunner );
- m_context.setConfig( NULL );
- m_context.setResultCapture( m_prevResultCapture );
- m_context.setConfig( m_prevConfig );
- }
-
- void testGroupStarting( std::string const& testSpec, std::size_t groupIndex, std::size_t groupsCount ) {
- m_reporter->testGroupStarting( GroupInfo( testSpec, groupIndex, groupsCount ) );
- }
- void testGroupEnded( std::string const& testSpec, Totals const& totals, std::size_t groupIndex, std::size_t groupsCount ) {
- m_reporter->testGroupEnded( TestGroupStats( GroupInfo( testSpec, groupIndex, groupsCount ), totals, aborting() ) );
- }
-
- Totals runTest( TestCase const& testCase ) {
- Totals prevTotals = m_totals;
-
- std::string redirectedCout;
- std::string redirectedCerr;
-
- TestCaseInfo testInfo = testCase.getTestCaseInfo();
-
- m_reporter->testCaseStarting( testInfo );
-
- m_activeTestCase = &testCase;
- m_testCaseTracker = TestCaseTracker( testInfo.name );
-
- do {
- do {
- runCurrentTest( redirectedCout, redirectedCerr );
- }
- while( !m_testCaseTracker->isCompleted() && !aborting() );
- }
- while( getCurrentContext().advanceGeneratorsForCurrentTest() && !aborting() );
-
- Totals deltaTotals = m_totals.delta( prevTotals );
- m_totals.testCases += deltaTotals.testCases;
- m_reporter->testCaseEnded( TestCaseStats( testInfo,
- deltaTotals,
- redirectedCout,
- redirectedCerr,
- aborting() ) );
-
- m_activeTestCase = NULL;
- m_testCaseTracker.reset();
-
- return deltaTotals;
- }
-
- Ptr<IConfig const> config() const {
- return m_config;
- }
-
- private: // IResultCapture
-
- virtual void assertionEnded( AssertionResult const& result ) {
- if( result.getResultType() == ResultWas::Ok ) {
- m_totals.assertions.passed++;
- }
- else if( !result.isOk() ) {
- m_totals.assertions.failed++;
- }
-
- if( m_reporter->assertionEnded( AssertionStats( result, m_messages, m_totals ) ) )
- m_messages.clear();
-
- // Reset working state
- m_lastAssertionInfo = AssertionInfo( "", m_lastAssertionInfo.lineInfo, "{Unknown expression after the reported line}" , m_lastAssertionInfo.resultDisposition );
- m_lastResult = result;
- }
-
- virtual bool sectionStarted (
- SectionInfo const& sectionInfo,
- Counts& assertions
- )
- {
- std::ostringstream oss;
- oss << sectionInfo.name << "@" << sectionInfo.lineInfo;
-
- if( !m_testCaseTracker->enterSection( oss.str() ) )
- return false;
-
- m_lastAssertionInfo.lineInfo = sectionInfo.lineInfo;
-
- m_reporter->sectionStarting( sectionInfo );
-
- assertions = m_totals.assertions;
-
- return true;
- }
- bool testForMissingAssertions( Counts& assertions ) {
- if( assertions.total() != 0 ||
- !m_config->warnAboutMissingAssertions() ||
- m_testCaseTracker->currentSectionHasChildren() )
- return false;
- m_totals.assertions.failed++;
- assertions.failed++;
- return true;
- }
-
- virtual void sectionEnded( SectionInfo const& info, Counts const& prevAssertions, double _durationInSeconds ) {
- if( std::uncaught_exception() ) {
- m_unfinishedSections.push_back( UnfinishedSections( info, prevAssertions, _durationInSeconds ) );
- return;
- }
-
- Counts assertions = m_totals.assertions - prevAssertions;
- bool missingAssertions = testForMissingAssertions( assertions );
-
- m_testCaseTracker->leaveSection();
-
- m_reporter->sectionEnded( SectionStats( info, assertions, _durationInSeconds, missingAssertions ) );
- m_messages.clear();
- }
-
- virtual void pushScopedMessage( MessageInfo const& message ) {
- m_messages.push_back( message );
- }
-
- virtual void popScopedMessage( MessageInfo const& message ) {
- m_messages.erase( std::remove( m_messages.begin(), m_messages.end(), message ), m_messages.end() );
- }
-
- virtual std::string getCurrentTestName() const {
- return m_activeTestCase
- ? m_activeTestCase->getTestCaseInfo().name
- : "";
- }
-
- virtual const AssertionResult* getLastResult() const {
- return &m_lastResult;
- }
-
- virtual void handleFatalErrorCondition( std::string const& message ) {
- ResultBuilder resultBuilder = makeUnexpectedResultBuilder();
- resultBuilder.setResultType( ResultWas::FatalErrorCondition );
- resultBuilder << message;
- resultBuilder.captureExpression();
-
- handleUnfinishedSections();
-
- // Recreate section for test case (as we will lose the one that was in scope)
- TestCaseInfo const& testCaseInfo = m_activeTestCase->getTestCaseInfo();
- SectionInfo testCaseSection( testCaseInfo.lineInfo, testCaseInfo.name, testCaseInfo.description );
-
- Counts assertions;
- assertions.failed = 1;
- SectionStats testCaseSectionStats( testCaseSection, assertions, 0, false );
- m_reporter->sectionEnded( testCaseSectionStats );
-
- TestCaseInfo testInfo = m_activeTestCase->getTestCaseInfo();
-
- Totals deltaTotals;
- deltaTotals.testCases.failed = 1;
- m_reporter->testCaseEnded( TestCaseStats( testInfo,
- deltaTotals,
- "",
- "",
- false ) );
- m_totals.testCases.failed++;
- testGroupEnded( "", m_totals, 1, 1 );
- m_reporter->testRunEnded( TestRunStats( m_runInfo, m_totals, false ) );
- }
-
- public:
- // !TBD We need to do this another way!
- bool aborting() const {
- return m_totals.assertions.failed == static_cast<std::size_t>( m_config->abortAfter() );
- }
-
- private:
-
- void runCurrentTest( std::string& redirectedCout, std::string& redirectedCerr ) {
- TestCaseInfo const& testCaseInfo = m_activeTestCase->getTestCaseInfo();
- SectionInfo testCaseSection( testCaseInfo.lineInfo, testCaseInfo.name, testCaseInfo.description );
- m_reporter->sectionStarting( testCaseSection );
- Counts prevAssertions = m_totals.assertions;
- double duration = 0;
- try {
- m_lastAssertionInfo = AssertionInfo( "TEST_CASE", testCaseInfo.lineInfo, "", ResultDisposition::Normal );
- TestCaseTracker::Guard guard( *m_testCaseTracker );
-
- Timer timer;
- timer.start();
- if( m_reporter->getPreferences().shouldRedirectStdOut ) {
- StreamRedirect coutRedir( Catch::cout(), redirectedCout );
- StreamRedirect cerrRedir( Catch::cerr(), redirectedCerr );
- invokeActiveTestCase();
- }
- else {
- invokeActiveTestCase();
- }
- duration = timer.getElapsedSeconds();
- }
- catch( TestFailureException& ) {
- // This just means the test was aborted due to failure
- }
- catch(...) {
- makeUnexpectedResultBuilder().useActiveException();
- }
- handleUnfinishedSections();
- m_messages.clear();
-
- Counts assertions = m_totals.assertions - prevAssertions;
- bool missingAssertions = testForMissingAssertions( assertions );
-
- if( testCaseInfo.okToFail() ) {
- std::swap( assertions.failedButOk, assertions.failed );
- m_totals.assertions.failed -= assertions.failedButOk;
- m_totals.assertions.failedButOk += assertions.failedButOk;
- }
-
- SectionStats testCaseSectionStats( testCaseSection, assertions, duration, missingAssertions );
- m_reporter->sectionEnded( testCaseSectionStats );
- }
-
- void invokeActiveTestCase() {
- FatalConditionHandler fatalConditionHandler; // Handle signals
- m_activeTestCase->invoke();
- fatalConditionHandler.reset();
- }
-
- private:
-
- ResultBuilder makeUnexpectedResultBuilder() const {
- return ResultBuilder( m_lastAssertionInfo.macroName.c_str(),
- m_lastAssertionInfo.lineInfo,
- m_lastAssertionInfo.capturedExpression.c_str(),
- m_lastAssertionInfo.resultDisposition );
- }
-
- void handleUnfinishedSections() {
- // If sections ended prematurely due to an exception we stored their
- // infos here so we can tear them down outside the unwind process.
- for( std::vector<UnfinishedSections>::const_reverse_iterator it = m_unfinishedSections.rbegin(),
- itEnd = m_unfinishedSections.rend();
- it != itEnd;
- ++it )
- sectionEnded( it->info, it->prevAssertions, it->durationInSeconds );
- m_unfinishedSections.clear();
- }
-
- struct UnfinishedSections {
- UnfinishedSections( SectionInfo const& _info, Counts const& _prevAssertions, double _durationInSeconds )
- : info( _info ), prevAssertions( _prevAssertions ), durationInSeconds( _durationInSeconds )
- {}
-
- SectionInfo info;
- Counts prevAssertions;
- double durationInSeconds;
- };
-
- TestRunInfo m_runInfo;
- IMutableContext& m_context;
- TestCase const* m_activeTestCase;
- Option<TestCaseTracker> m_testCaseTracker;
- AssertionResult m_lastResult;
-
- Ptr<IConfig const> m_config;
- Totals m_totals;
- Ptr<IStreamingReporter> m_reporter;
- std::vector<MessageInfo> m_messages;
- IRunner* m_prevRunner;
- IResultCapture* m_prevResultCapture;
- Ptr<IConfig const> m_prevConfig;
- AssertionInfo m_lastAssertionInfo;
- std::vector<UnfinishedSections> m_unfinishedSections;
- };
-
- IResultCapture& getResultCapture() {
- if( IResultCapture* capture = getCurrentContext().getResultCapture() )
- return *capture;
- else
- throw std::logic_error( "No result capture instance" );
- }
-
-} // end namespace Catch
-
-// #included from: internal/catch_version.h
-#define TWOBLUECUBES_CATCH_VERSION_H_INCLUDED
-
-namespace Catch {
-
- // Versioning information
- struct Version {
- Version( unsigned int _majorVersion,
- unsigned int _minorVersion,
- unsigned int _patchNumber,
- std::string const& _branchName,
- unsigned int _buildNumber );
-
- unsigned int const majorVersion;
- unsigned int const minorVersion;
- unsigned int const patchNumber;
-
- // buildNumber is only used if branchName is not null
- std::string const branchName;
- unsigned int const buildNumber;
-
- friend std::ostream& operator << ( std::ostream& os, Version const& version );
-
- private:
- void operator=( Version const& );
- };
-
- extern Version libraryVersion;
-}
-
-#include <fstream>
-#include <stdlib.h>
-#include <limits>
-
-namespace Catch {
-
- class Runner {
-
- public:
- Runner( Ptr<Config> const& config )
- : m_config( config )
- {
- openStream();
- makeReporter();
- }
-
- Totals runTests() {
-
- RunContext context( m_config.get(), m_reporter );
-
- Totals totals;
-
- context.testGroupStarting( "all tests", 1, 1 ); // deprecated?
-
- TestSpec testSpec = m_config->testSpec();
- if( !testSpec.hasFilters() )
- testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "~[.]" ).testSpec(); // All not hidden tests
-
- std::vector<TestCase> testCases;
- getRegistryHub().getTestCaseRegistry().getFilteredTests( testSpec, *m_config, testCases );
-
- int testsRunForGroup = 0;
- for( std::vector<TestCase>::const_iterator it = testCases.begin(), itEnd = testCases.end();
- it != itEnd;
- ++it ) {
- testsRunForGroup++;
- if( m_testsAlreadyRun.find( *it ) == m_testsAlreadyRun.end() ) {
-
- if( context.aborting() )
- break;
-
- totals += context.runTest( *it );
- m_testsAlreadyRun.insert( *it );
- }
- }
- std::vector<TestCase> skippedTestCases;
- getRegistryHub().getTestCaseRegistry().getFilteredTests( testSpec, *m_config, skippedTestCases, true );
-
- for( std::vector<TestCase>::const_iterator it = skippedTestCases.begin(), itEnd = skippedTestCases.end();
- it != itEnd;
- ++it )
- m_reporter->skipTest( *it );
-
- context.testGroupEnded( "all tests", totals, 1, 1 );
- return totals;
- }
-
- private:
- void openStream() {
- // Open output file, if specified
- if( !m_config->getFilename().empty() ) {
- m_ofs.open( m_config->getFilename().c_str() );
- if( m_ofs.fail() ) {
- std::ostringstream oss;
- oss << "Unable to open file: '" << m_config->getFilename() << "'";
- throw std::domain_error( oss.str() );
- }
- m_config->setStreamBuf( m_ofs.rdbuf() );
- }
- }
- void makeReporter() {
- std::string reporterName = m_config->getReporterName().empty()
- ? "console"
- : m_config->getReporterName();
-
- m_reporter = getRegistryHub().getReporterRegistry().create( reporterName, m_config.get() );
- if( !m_reporter ) {
- std::ostringstream oss;
- oss << "No reporter registered with name: '" << reporterName << "'";
- throw std::domain_error( oss.str() );
- }
- }
-
- private:
- Ptr<Config> m_config;
- std::ofstream m_ofs;
- Ptr<IStreamingReporter> m_reporter;
- std::set<TestCase> m_testsAlreadyRun;
- };
-
- class Session : NonCopyable {
- static bool alreadyInstantiated;
-
- public:
-
- struct OnUnusedOptions { enum DoWhat { Ignore, Fail }; };
-
- Session()
- : m_cli( makeCommandLineParser() ) {
- if( alreadyInstantiated ) {
- std::string msg = "Only one instance of Catch::Session can ever be used";
- Catch::cerr() << msg << std::endl;
- throw std::logic_error( msg );
- }
- alreadyInstantiated = true;
- }
- ~Session() {
- Catch::cleanUp();
- }
-
- void showHelp( std::string const& processName ) {
- Catch::cout() << "\nCatch v" << libraryVersion << "\n";
-
- m_cli.usage( Catch::cout(), processName );
- Catch::cout() << "For more detail usage please see the project docs\n" << std::endl;
- }
-
- int applyCommandLine( int argc, char* const argv[], OnUnusedOptions::DoWhat unusedOptionBehaviour = OnUnusedOptions::Fail ) {
- try {
- m_cli.setThrowOnUnrecognisedTokens( unusedOptionBehaviour == OnUnusedOptions::Fail );
- m_unusedTokens = m_cli.parseInto( argc, argv, m_configData );
- if( m_configData.showHelp )
- showHelp( m_configData.processName );
- m_config.reset();
- }
- catch( std::exception& ex ) {
- {
- Colour colourGuard( Colour::Red );
- Catch::cerr()
- << "\nError(s) in input:\n"
- << Text( ex.what(), TextAttributes().setIndent(2) )
- << "\n\n";
- }
- m_cli.usage( Catch::cout(), m_configData.processName );
- return (std::numeric_limits<int>::max)();
- }
- return 0;
- }
-
- void useConfigData( ConfigData const& _configData ) {
- m_configData = _configData;
- m_config.reset();
- }
-
- int run( int argc, char* const argv[] ) {
-
- int returnCode = applyCommandLine( argc, argv );
- if( returnCode == 0 )
- returnCode = run();
- return returnCode;
- }
-
- int run() {
- if( m_configData.showHelp )
- return 0;
-
- try
- {
- config(); // Force config to be constructed
-
- std::srand( m_configData.rngSeed );
-
- Runner runner( m_config );
-
- // Handle list request
- if( Option<std::size_t> listed = list( config() ) )
- return static_cast<int>( *listed );
-
- return static_cast<int>( runner.runTests().assertions.failed );
- }
- catch( std::exception& ex ) {
- Catch::cerr() << ex.what() << std::endl;
- return (std::numeric_limits<int>::max)();
- }
- }
-
- Clara::CommandLine<ConfigData> const& cli() const {
- return m_cli;
- }
- std::vector<Clara::Parser::Token> const& unusedTokens() const {
- return m_unusedTokens;
- }
- ConfigData& configData() {
- return m_configData;
- }
- Config& config() {
- if( !m_config )
- m_config = new Config( m_configData );
- return *m_config;
- }
-
- private:
- Clara::CommandLine<ConfigData> m_cli;
- std::vector<Clara::Parser::Token> m_unusedTokens;
- ConfigData m_configData;
- Ptr<Config> m_config;
- };
-
- bool Session::alreadyInstantiated = false;
-
-} // end namespace Catch
-
-// #included from: catch_registry_hub.hpp
-#define TWOBLUECUBES_CATCH_REGISTRY_HUB_HPP_INCLUDED
-
-// #included from: catch_test_case_registry_impl.hpp
-#define TWOBLUECUBES_CATCH_TEST_CASE_REGISTRY_IMPL_HPP_INCLUDED
-
-#include <vector>
-#include <set>
-#include <sstream>
-#include <iostream>
-#include <algorithm>
-
-namespace Catch {
-
- class TestRegistry : public ITestCaseRegistry {
- struct LexSort {
- bool operator() (TestCase i,TestCase j) const { return (i<j);}
- };
- struct RandomNumberGenerator {
- int operator()( int n ) const { return std::rand() % n; }
- };
-
- public:
- TestRegistry() : m_unnamedCount( 0 ) {}
- virtual ~TestRegistry();
-
- virtual void registerTest( TestCase const& testCase ) {
- std::string name = testCase.getTestCaseInfo().name;
- if( name == "" ) {
- std::ostringstream oss;
- oss << "Anonymous test case " << ++m_unnamedCount;
- return registerTest( testCase.withName( oss.str() ) );
- }
-
- if( m_functions.find( testCase ) == m_functions.end() ) {
- m_functions.insert( testCase );
- m_functionsInOrder.push_back( testCase );
- if( !testCase.isHidden() )
- m_nonHiddenFunctions.push_back( testCase );
- }
- else {
- TestCase const& prev = *m_functions.find( testCase );
- {
- Colour colourGuard( Colour::Red );
- Catch::cerr() << "error: TEST_CASE( \"" << name << "\" ) already defined.\n"
- << "\tFirst seen at " << prev.getTestCaseInfo().lineInfo << "\n"
- << "\tRedefined at " << testCase.getTestCaseInfo().lineInfo << std::endl;
- }
- exit(1);
- }
- }
-
- virtual std::vector<TestCase> const& getAllTests() const {
- return m_functionsInOrder;
- }
-
- virtual std::vector<TestCase> const& getAllNonHiddenTests() const {
- return m_nonHiddenFunctions;
- }
-
- virtual void getFilteredTests( TestSpec const& testSpec, IConfig const& config, std::vector<TestCase>& matchingTestCases, bool negated = false ) const {
-
- for( std::vector<TestCase>::const_iterator it = m_functionsInOrder.begin(),
- itEnd = m_functionsInOrder.end();
- it != itEnd;
- ++it ) {
- bool includeTest = testSpec.matches( *it ) && ( config.allowThrows() || !it->throws() );
- if( includeTest != negated )
- matchingTestCases.push_back( *it );
- }
- sortTests( config, matchingTestCases );
- }
-
- private:
-
- static void sortTests( IConfig const& config, std::vector<TestCase>& matchingTestCases ) {
-
- switch( config.runOrder() ) {
- case RunTests::InLexicographicalOrder:
- std::sort( matchingTestCases.begin(), matchingTestCases.end(), LexSort() );
- break;
- case RunTests::InRandomOrder:
- {
- RandomNumberGenerator rng;
- std::random_shuffle( matchingTestCases.begin(), matchingTestCases.end(), rng );
- }
- break;
- case RunTests::InDeclarationOrder:
- // already in declaration order
- break;
- }
- }
- std::set<TestCase> m_functions;
- std::vector<TestCase> m_functionsInOrder;
- std::vector<TestCase> m_nonHiddenFunctions;
- size_t m_unnamedCount;
- };
-
- ///////////////////////////////////////////////////////////////////////////
-
- class FreeFunctionTestCase : public SharedImpl<ITestCase> {
- public:
-
- FreeFunctionTestCase( TestFunction fun ) : m_fun( fun ) {}
-
- virtual void invoke() const {
- m_fun();
- }
-
- private:
- virtual ~FreeFunctionTestCase();
-
- TestFunction m_fun;
- };
-
- inline std::string extractClassName( std::string const& classOrQualifiedMethodName ) {
- std::string className = classOrQualifiedMethodName;
- if( startsWith( className, "&" ) )
- {
- std::size_t lastColons = className.rfind( "::" );
- std::size_t penultimateColons = className.rfind( "::", lastColons-1 );
- if( penultimateColons == std::string::npos )
- penultimateColons = 1;
- className = className.substr( penultimateColons, lastColons-penultimateColons );
- }
- return className;
- }
-
- ///////////////////////////////////////////////////////////////////////////
-
- AutoReg::AutoReg( TestFunction function,
- SourceLineInfo const& lineInfo,
- NameAndDesc const& nameAndDesc ) {
- registerTestCase( new FreeFunctionTestCase( function ), "", nameAndDesc, lineInfo );
- }
-
- AutoReg::~AutoReg() {}
-
- void AutoReg::registerTestCase( ITestCase* testCase,
- char const* classOrQualifiedMethodName,
- NameAndDesc const& nameAndDesc,
- SourceLineInfo const& lineInfo ) {
-
- getMutableRegistryHub().registerTest
- ( makeTestCase( testCase,
- extractClassName( classOrQualifiedMethodName ),
- nameAndDesc.name,
- nameAndDesc.description,
- lineInfo ) );
- }
-
-} // end namespace Catch
-
-// #included from: catch_reporter_registry.hpp
-#define TWOBLUECUBES_CATCH_REPORTER_REGISTRY_HPP_INCLUDED
-
-#include <map>
-
-namespace Catch {
-
- class ReporterRegistry : public IReporterRegistry {
-
- public:
-
- virtual ~ReporterRegistry() {
- deleteAllValues( m_factories );
- }
-
- virtual IStreamingReporter* create( std::string const& name, Ptr<IConfig> const& config ) const {
- FactoryMap::const_iterator it = m_factories.find( name );
- if( it == m_factories.end() )
- return NULL;
- return it->second->create( ReporterConfig( config ) );
- }
-
- void registerReporter( std::string const& name, IReporterFactory* factory ) {
- m_factories.insert( std::make_pair( name, factory ) );
- }
-
- FactoryMap const& getFactories() const {
- return m_factories;
- }
-
- private:
- FactoryMap m_factories;
- };
-}
-
-// #included from: catch_exception_translator_registry.hpp
-#define TWOBLUECUBES_CATCH_EXCEPTION_TRANSLATOR_REGISTRY_HPP_INCLUDED
-
-#ifdef __OBJC__
-#import "Foundation/Foundation.h"
-#endif
-
-namespace Catch {
-
- class ExceptionTranslatorRegistry : public IExceptionTranslatorRegistry {
- public:
- ~ExceptionTranslatorRegistry() {
- deleteAll( m_translators );
- }
-
- virtual void registerTranslator( const IExceptionTranslator* translator ) {
- m_translators.push_back( translator );
- }
-
- virtual std::string translateActiveException() const {
- try {
-#ifdef __OBJC__
- // In Objective-C try objective-c exceptions first
- @try {
- throw;
- }
- @catch (NSException *exception) {
- return Catch::toString( [exception description] );
- }
-#else
- throw;
-#endif
- }
- catch( TestFailureException& ) {
- throw;
- }
- catch( std::exception& ex ) {
- return ex.what();
- }
- catch( std::string& msg ) {
- return msg;
- }
- catch( const char* msg ) {
- return msg;
- }
- catch(...) {
- return tryTranslators( m_translators.begin() );
- }
- }
-
- std::string tryTranslators( std::vector<const IExceptionTranslator*>::const_iterator it ) const {
- if( it == m_translators.end() )
- return "Unknown exception";
-
- try {
- return (*it)->translate();
- }
- catch(...) {
- return tryTranslators( it+1 );
- }
- }
-
- private:
- std::vector<const IExceptionTranslator*> m_translators;
- };
-}
-
-namespace Catch {
-
- namespace {
-
- class RegistryHub : public IRegistryHub, public IMutableRegistryHub {
-
- RegistryHub( RegistryHub const& );
- void operator=( RegistryHub const& );
-
- public: // IRegistryHub
- RegistryHub() {
- }
- virtual IReporterRegistry const& getReporterRegistry() const {
- return m_reporterRegistry;
- }
- virtual ITestCaseRegistry const& getTestCaseRegistry() const {
- return m_testCaseRegistry;
- }
- virtual IExceptionTranslatorRegistry& getExceptionTranslatorRegistry() {
- return m_exceptionTranslatorRegistry;
- }
-
- public: // IMutableRegistryHub
- virtual void registerReporter( std::string const& name, IReporterFactory* factory ) {
- m_reporterRegistry.registerReporter( name, factory );
- }
- virtual void registerTest( TestCase const& testInfo ) {
- m_testCaseRegistry.registerTest( testInfo );
- }
- virtual void registerTranslator( const IExceptionTranslator* translator ) {
- m_exceptionTranslatorRegistry.registerTranslator( translator );
- }
-
- private:
- TestRegistry m_testCaseRegistry;
- ReporterRegistry m_reporterRegistry;
- ExceptionTranslatorRegistry m_exceptionTranslatorRegistry;
- };
-
- // Single, global, instance
- inline RegistryHub*& getTheRegistryHub() {
- static RegistryHub* theRegistryHub = NULL;
- if( !theRegistryHub )
- theRegistryHub = new RegistryHub();
- return theRegistryHub;
- }
- }
-
- IRegistryHub& getRegistryHub() {
- return *getTheRegistryHub();
- }
- IMutableRegistryHub& getMutableRegistryHub() {
- return *getTheRegistryHub();
- }
- void cleanUp() {
- delete getTheRegistryHub();
- getTheRegistryHub() = NULL;
- cleanUpContext();
- }
- std::string translateActiveException() {
- return getRegistryHub().getExceptionTranslatorRegistry().translateActiveException();
- }
-
-} // end namespace Catch
-
-// #included from: catch_notimplemented_exception.hpp
-#define TWOBLUECUBES_CATCH_NOTIMPLEMENTED_EXCEPTION_HPP_INCLUDED
-
-#include <ostream>
-
-namespace Catch {
-
- NotImplementedException::NotImplementedException( SourceLineInfo const& lineInfo )
- : m_lineInfo( lineInfo ) {
- std::ostringstream oss;
- oss << lineInfo << ": function ";
- oss << "not implemented";
- m_what = oss.str();
- }
-
- const char* NotImplementedException::what() const CATCH_NOEXCEPT {
- return m_what.c_str();
- }
-
-} // end namespace Catch
-
-// #included from: catch_context_impl.hpp
-#define TWOBLUECUBES_CATCH_CONTEXT_IMPL_HPP_INCLUDED
-
-// #included from: catch_stream.hpp
-#define TWOBLUECUBES_CATCH_STREAM_HPP_INCLUDED
-
-// #included from: catch_streambuf.h
-#define TWOBLUECUBES_CATCH_STREAMBUF_H_INCLUDED
-
-#include <streambuf>
-
-namespace Catch {
-
- class StreamBufBase : public std::streambuf {
- public:
- virtual ~StreamBufBase() CATCH_NOEXCEPT;
- };
-}
-
-#include <stdexcept>
-#include <cstdio>
-#include <iostream>
-
-namespace Catch {
-
- template<typename WriterF, size_t bufferSize=256>
- class StreamBufImpl : public StreamBufBase {
- char data[bufferSize];
- WriterF m_writer;
-
- public:
- StreamBufImpl() {
- setp( data, data + sizeof(data) );
- }
-
- ~StreamBufImpl() CATCH_NOEXCEPT {
- sync();
- }
-
- private:
- int overflow( int c ) {
- sync();
-
- if( c != EOF ) {
- if( pbase() == epptr() )
- m_writer( std::string( 1, static_cast<char>( c ) ) );
- else
- sputc( static_cast<char>( c ) );
- }
- return 0;
- }
-
- int sync() {
- if( pbase() != pptr() ) {
- m_writer( std::string( pbase(), static_cast<std::string::size_type>( pptr() - pbase() ) ) );
- setp( pbase(), epptr() );
- }
- return 0;
- }
- };
-
- ///////////////////////////////////////////////////////////////////////////
-
- struct OutputDebugWriter {
-
- void operator()( std::string const&str ) {
- writeToDebugConsole( str );
- }
- };
-
- Stream::Stream()
- : streamBuf( NULL ), isOwned( false )
- {}
-
- Stream::Stream( std::streambuf* _streamBuf, bool _isOwned )
- : streamBuf( _streamBuf ), isOwned( _isOwned )
- {}
-
- void Stream::release() {
- if( isOwned ) {
- delete streamBuf;
- streamBuf = NULL;
- isOwned = false;
- }
- }
-
-#ifndef CATCH_CONFIG_NOSTDOUT // If you #define this you must implement this functions
- std::ostream& cout() {
- return std::cout;
- }
- std::ostream& cerr() {
- return std::cerr;
- }
-#endif
-}
-
-namespace Catch {
-
- class Context : public IMutableContext {
-
- Context() : m_config( NULL ), m_runner( NULL ), m_resultCapture( NULL ) {}
- Context( Context const& );
- void operator=( Context const& );
-
- public: // IContext
- virtual IResultCapture* getResultCapture() {
- return m_resultCapture;
- }
- virtual IRunner* getRunner() {
- return m_runner;
- }
- virtual size_t getGeneratorIndex( std::string const& fileInfo, size_t totalSize ) {
- return getGeneratorsForCurrentTest()
- .getGeneratorInfo( fileInfo, totalSize )
- .getCurrentIndex();
- }
- virtual bool advanceGeneratorsForCurrentTest() {
- IGeneratorsForTest* generators = findGeneratorsForCurrentTest();
- return generators && generators->moveNext();
- }
-
- virtual Ptr<IConfig const> getConfig() const {
- return m_config;
- }
-
- public: // IMutableContext
- virtual void setResultCapture( IResultCapture* resultCapture ) {
- m_resultCapture = resultCapture;
- }
- virtual void setRunner( IRunner* runner ) {
- m_runner = runner;
- }
- virtual void setConfig( Ptr<IConfig const> const& config ) {
- m_config = config;
- }
-
- friend IMutableContext& getCurrentMutableContext();
-
- private:
- IGeneratorsForTest* findGeneratorsForCurrentTest() {
- std::string testName = getResultCapture()->getCurrentTestName();
-
- std::map<std::string, IGeneratorsForTest*>::const_iterator it =
- m_generatorsByTestName.find( testName );
- return it != m_generatorsByTestName.end()
- ? it->second
- : NULL;
- }
-
- IGeneratorsForTest& getGeneratorsForCurrentTest() {
- IGeneratorsForTest* generators = findGeneratorsForCurrentTest();
- if( !generators ) {
- std::string testName = getResultCapture()->getCurrentTestName();
- generators = createGeneratorsForTest();
- m_generatorsByTestName.insert( std::make_pair( testName, generators ) );
- }
- return *generators;
- }
-
- private:
- Ptr<IConfig const> m_config;
- IRunner* m_runner;
- IResultCapture* m_resultCapture;
- std::map<std::string, IGeneratorsForTest*> m_generatorsByTestName;
- };
-
- namespace {
- Context* currentContext = NULL;
- }
- IMutableContext& getCurrentMutableContext() {
- if( !currentContext )
- currentContext = new Context();
- return *currentContext;
- }
- IContext& getCurrentContext() {
- return getCurrentMutableContext();
- }
-
- Stream createStream( std::string const& streamName ) {
- if( streamName == "stdout" ) return Stream( Catch::cout().rdbuf(), false );
- if( streamName == "stderr" ) return Stream( Catch::cerr().rdbuf(), false );
- if( streamName == "debug" ) return Stream( new StreamBufImpl<OutputDebugWriter>, true );
-
- throw std::domain_error( "Unknown stream: " + streamName );
- }
-
- void cleanUpContext() {
- delete currentContext;
- currentContext = NULL;
- }
-}
-
-// #included from: catch_console_colour_impl.hpp
-#define TWOBLUECUBES_CATCH_CONSOLE_COLOUR_IMPL_HPP_INCLUDED
-
-namespace Catch {
- namespace {
-
- struct IColourImpl {
- virtual ~IColourImpl() {}
- virtual void use( Colour::Code _colourCode ) = 0;
- };
-
- struct NoColourImpl : IColourImpl {
- void use( Colour::Code ) {}
-
- static IColourImpl* instance() {
- static NoColourImpl s_instance;
- return &s_instance;
- }
- };
-
- } // anon namespace
-} // namespace Catch
-
-#if !defined( CATCH_CONFIG_COLOUR_NONE ) && !defined( CATCH_CONFIG_COLOUR_WINDOWS ) && !defined( CATCH_CONFIG_COLOUR_ANSI )
-# ifdef CATCH_PLATFORM_WINDOWS
-# define CATCH_CONFIG_COLOUR_WINDOWS
-# else
-# define CATCH_CONFIG_COLOUR_ANSI
-# endif
-#endif
-
-#if defined ( CATCH_CONFIG_COLOUR_WINDOWS ) /////////////////////////////////////////
-
-#ifndef NOMINMAX
-#define NOMINMAX
-#endif
-
-#ifdef __AFXDLL
-#include <AfxWin.h>
-#else
-#include <windows.h>
-#endif
-
-namespace Catch {
-namespace {
-
- class Win32ColourImpl : public IColourImpl {
- public:
- Win32ColourImpl() : stdoutHandle( GetStdHandle(STD_OUTPUT_HANDLE) )
- {
- CONSOLE_SCREEN_BUFFER_INFO csbiInfo;
- GetConsoleScreenBufferInfo( stdoutHandle, &csbiInfo );
- originalAttributes = csbiInfo.wAttributes;
- }
-
- virtual void use( Colour::Code _colourCode ) {
- switch( _colourCode ) {
- case Colour::None: return setTextAttribute( originalAttributes );
- case Colour::White: return setTextAttribute( FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE );
- case Colour::Red: return setTextAttribute( FOREGROUND_RED );
- case Colour::Green: return setTextAttribute( FOREGROUND_GREEN );
- case Colour::Blue: return setTextAttribute( FOREGROUND_BLUE );
- case Colour::Cyan: return setTextAttribute( FOREGROUND_BLUE | FOREGROUND_GREEN );
- case Colour::Yellow: return setTextAttribute( FOREGROUND_RED | FOREGROUND_GREEN );
- case Colour::Grey: return setTextAttribute( 0 );
-
- case Colour::LightGrey: return setTextAttribute( FOREGROUND_INTENSITY );
- case Colour::BrightRed: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_RED );
- case Colour::BrightGreen: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_GREEN );
- case Colour::BrightWhite: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE );
-
- case Colour::Bright: throw std::logic_error( "not a colour" );
- }
- }
-
- private:
- void setTextAttribute( WORD _textAttribute ) {
- SetConsoleTextAttribute( stdoutHandle, _textAttribute );
- }
- HANDLE stdoutHandle;
- WORD originalAttributes;
- };
-
- IColourImpl* platformColourInstance() {
- static Win32ColourImpl s_instance;
- return &s_instance;
- }
-
-} // end anon namespace
-} // end namespace Catch
-
-#elif defined( CATCH_CONFIG_COLOUR_ANSI ) //////////////////////////////////////
-
-#include <unistd.h>
-
-namespace Catch {
-namespace {
-
- // use POSIX/ ANSI console terminal codes
- // Thanks to Adam Strzelecki for original contribution
- // (http://github.com/nanoant)
- // https://github.com/philsquared/Catch/pull/131
- class PosixColourImpl : public IColourImpl {
- public:
- virtual void use( Colour::Code _colourCode ) {
- switch( _colourCode ) {
- case Colour::None:
- case Colour::White: return setColour( "[0m" );
- case Colour::Red: return setColour( "[0;31m" );
- case Colour::Green: return setColour( "[0;32m" );
- case Colour::Blue: return setColour( "[0:34m" );
- case Colour::Cyan: return setColour( "[0;36m" );
- case Colour::Yellow: return setColour( "[0;33m" );
- case Colour::Grey: return setColour( "[1;30m" );
-
- case Colour::LightGrey: return setColour( "[0;37m" );
- case Colour::BrightRed: return setColour( "[1;31m" );
- case Colour::BrightGreen: return setColour( "[1;32m" );
- case Colour::BrightWhite: return setColour( "[1;37m" );
-
- case Colour::Bright: throw std::logic_error( "not a colour" );
- }
- }
- static IColourImpl* instance() {
- static PosixColourImpl s_instance;
- return &s_instance;
- }
-
- private:
- void setColour( const char* _escapeCode ) {
- Catch::cout() << '\033' << _escapeCode;
- }
- };
-
- IColourImpl* platformColourInstance() {
- Ptr<IConfig const> config = getCurrentContext().getConfig();
- return (config && config->forceColour()) || isatty(STDOUT_FILENO)
- ? PosixColourImpl::instance()
- : NoColourImpl::instance();
- }
-
-} // end anon namespace
-} // end namespace Catch
-
-#else // not Windows or ANSI ///////////////////////////////////////////////
-
-namespace Catch {
-
- static IColourImpl* platformColourInstance() { return NoColourImpl::instance(); }
-
-} // end namespace Catch
-
-#endif // Windows/ ANSI/ None
-
-namespace Catch {
-
- Colour::Colour( Code _colourCode ) : m_moved( false ) { use( _colourCode ); }
- Colour::Colour( Colour const& _other ) : m_moved( false ) { const_cast<Colour&>( _other ).m_moved = true; }
- Colour::~Colour(){ if( !m_moved ) use( None ); }
-
- void Colour::use( Code _colourCode ) {
- static IColourImpl* impl = isDebuggerActive()
- ? NoColourImpl::instance()
- : platformColourInstance();
- impl->use( _colourCode );
- }
-
-} // end namespace Catch
-
-// #included from: catch_generators_impl.hpp
-#define TWOBLUECUBES_CATCH_GENERATORS_IMPL_HPP_INCLUDED
-
-#include <vector>
-#include <string>
-#include <map>
-
-namespace Catch {
-
- struct GeneratorInfo : IGeneratorInfo {
-
- GeneratorInfo( std::size_t size )
- : m_size( size ),
- m_currentIndex( 0 )
- {}
-
- bool moveNext() {
- if( ++m_currentIndex == m_size ) {
- m_currentIndex = 0;
- return false;
- }
- return true;
- }
-
- std::size_t getCurrentIndex() const {
- return m_currentIndex;
- }
-
- std::size_t m_size;
- std::size_t m_currentIndex;
- };
-
- ///////////////////////////////////////////////////////////////////////////
-
- class GeneratorsForTest : public IGeneratorsForTest {
-
- public:
- ~GeneratorsForTest() {
- deleteAll( m_generatorsInOrder );
- }
-
- IGeneratorInfo& getGeneratorInfo( std::string const& fileInfo, std::size_t size ) {
- std::map<std::string, IGeneratorInfo*>::const_iterator it = m_generatorsByName.find( fileInfo );
- if( it == m_generatorsByName.end() ) {
- IGeneratorInfo* info = new GeneratorInfo( size );
- m_generatorsByName.insert( std::make_pair( fileInfo, info ) );
- m_generatorsInOrder.push_back( info );
- return *info;
- }
- return *it->second;
- }
-
- bool moveNext() {
- std::vector<IGeneratorInfo*>::const_iterator it = m_generatorsInOrder.begin();
- std::vector<IGeneratorInfo*>::const_iterator itEnd = m_generatorsInOrder.end();
- for(; it != itEnd; ++it ) {
- if( (*it)->moveNext() )
- return true;
- }
- return false;
- }
-
- private:
- std::map<std::string, IGeneratorInfo*> m_generatorsByName;
- std::vector<IGeneratorInfo*> m_generatorsInOrder;
- };
-
- IGeneratorsForTest* createGeneratorsForTest()
- {
- return new GeneratorsForTest();
- }
-
-} // end namespace Catch
-
-// #included from: catch_assertionresult.hpp
-#define TWOBLUECUBES_CATCH_ASSERTIONRESULT_HPP_INCLUDED
-
-namespace Catch {
-
- AssertionInfo::AssertionInfo( std::string const& _macroName,
- SourceLineInfo const& _lineInfo,
- std::string const& _capturedExpression,
- ResultDisposition::Flags _resultDisposition )
- : macroName( _macroName ),
- lineInfo( _lineInfo ),
- capturedExpression( _capturedExpression ),
- resultDisposition( _resultDisposition )
- {}
-
- AssertionResult::AssertionResult() {}
-
- AssertionResult::AssertionResult( AssertionInfo const& info, AssertionResultData const& data )
- : m_info( info ),
- m_resultData( data )
- {}
-
- AssertionResult::~AssertionResult() {}
-
- // Result was a success
- bool AssertionResult::succeeded() const {
- return Catch::isOk( m_resultData.resultType );
- }
-
- // Result was a success, or failure is suppressed
- bool AssertionResult::isOk() const {
- return Catch::isOk( m_resultData.resultType ) || shouldSuppressFailure( m_info.resultDisposition );
- }
-
- ResultWas::OfType AssertionResult::getResultType() const {
- return m_resultData.resultType;
- }
-
- bool AssertionResult::hasExpression() const {
- return !m_info.capturedExpression.empty();
- }
-
- bool AssertionResult::hasMessage() const {
- return !m_resultData.message.empty();
- }
-
- std::string AssertionResult::getExpression() const {
- if( isFalseTest( m_info.resultDisposition ) )
- return "!" + m_info.capturedExpression;
- else
- return m_info.capturedExpression;
- }
- std::string AssertionResult::getExpressionInMacro() const {
- if( m_info.macroName.empty() )
- return m_info.capturedExpression;
- else
- return m_info.macroName + "( " + m_info.capturedExpression + " )";
- }
-
- bool AssertionResult::hasExpandedExpression() const {
- return hasExpression() && getExpandedExpression() != getExpression();
- }
-
- std::string AssertionResult::getExpandedExpression() const {
- return m_resultData.reconstructedExpression;
- }
-
- std::string AssertionResult::getMessage() const {
- return m_resultData.message;
- }
- SourceLineInfo AssertionResult::getSourceInfo() const {
- return m_info.lineInfo;
- }
-
- std::string AssertionResult::getTestMacroName() const {
- return m_info.macroName;
- }
-
-} // end namespace Catch
-
-// #included from: catch_test_case_info.hpp
-#define TWOBLUECUBES_CATCH_TEST_CASE_INFO_HPP_INCLUDED
-
-namespace Catch {
-
- inline TestCaseInfo::SpecialProperties parseSpecialTag( std::string const& tag ) {
- if( startsWith( tag, "." ) ||
- tag == "hide" ||
- tag == "!hide" )
- return TestCaseInfo::IsHidden;
- else if( tag == "!throws" )
- return TestCaseInfo::Throws;
- else if( tag == "!shouldfail" )
- return TestCaseInfo::ShouldFail;
- else if( tag == "!mayfail" )
- return TestCaseInfo::MayFail;
- else
- return TestCaseInfo::None;
- }
- inline bool isReservedTag( std::string const& tag ) {
- return parseSpecialTag( tag ) == TestCaseInfo::None && tag.size() > 0 && !isalnum( tag[0] );
- }
- inline void enforceNotReservedTag( std::string const& tag, SourceLineInfo const& _lineInfo ) {
- if( isReservedTag( tag ) ) {
- {
- Colour colourGuard( Colour::Red );
- Catch::cerr()
- << "Tag name [" << tag << "] not allowed.\n"
- << "Tag names starting with non alpha-numeric characters are reserved\n";
- }
- {
- Colour colourGuard( Colour::FileName );
- Catch::cerr() << _lineInfo << std::endl;
- }
- exit(1);
- }
- }
-
- TestCase makeTestCase( ITestCase* _testCase,
- std::string const& _className,
- std::string const& _name,
- std::string const& _descOrTags,
- SourceLineInfo const& _lineInfo )
- {
- bool isHidden( startsWith( _name, "./" ) ); // Legacy support
-
- // Parse out tags
- std::set<std::string> tags;
- std::string desc, tag;
- bool inTag = false;
- for( std::size_t i = 0; i < _descOrTags.size(); ++i ) {
- char c = _descOrTags[i];
- if( !inTag ) {
- if( c == '[' )
- inTag = true;
- else
- desc += c;
- }
- else {
- if( c == ']' ) {
- TestCaseInfo::SpecialProperties prop = parseSpecialTag( tag );
- if( prop == TestCaseInfo::IsHidden )
- isHidden = true;
- else if( prop == TestCaseInfo::None )
- enforceNotReservedTag( tag, _lineInfo );
-
- tags.insert( tag );
- tag.clear();
- inTag = false;
- }
- else
- tag += c;
- }
- }
- if( isHidden ) {
- tags.insert( "hide" );
- tags.insert( "." );
- }
-
- TestCaseInfo info( _name, _className, desc, tags, _lineInfo );
- return TestCase( _testCase, info );
- }
-
- TestCaseInfo::TestCaseInfo( std::string const& _name,
- std::string const& _className,
- std::string const& _description,
- std::set<std::string> const& _tags,
- SourceLineInfo const& _lineInfo )
- : name( _name ),
- className( _className ),
- description( _description ),
- tags( _tags ),
- lineInfo( _lineInfo ),
- properties( None )
- {
- std::ostringstream oss;
- for( std::set<std::string>::const_iterator it = _tags.begin(), itEnd = _tags.end(); it != itEnd; ++it ) {
- oss << "[" << *it << "]";
- std::string lcaseTag = toLower( *it );
- properties = static_cast<SpecialProperties>( properties | parseSpecialTag( lcaseTag ) );
- lcaseTags.insert( lcaseTag );
- }
- tagsAsString = oss.str();
- }
-
- TestCaseInfo::TestCaseInfo( TestCaseInfo const& other )
- : name( other.name ),
- className( other.className ),
- description( other.description ),
- tags( other.tags ),
- lcaseTags( other.lcaseTags ),
- tagsAsString( other.tagsAsString ),
- lineInfo( other.lineInfo ),
- properties( other.properties )
- {}
-
- bool TestCaseInfo::isHidden() const {
- return ( properties & IsHidden ) != 0;
- }
- bool TestCaseInfo::throws() const {
- return ( properties & Throws ) != 0;
- }
- bool TestCaseInfo::okToFail() const {
- return ( properties & (ShouldFail | MayFail ) ) != 0;
- }
- bool TestCaseInfo::expectedToFail() const {
- return ( properties & (ShouldFail ) ) != 0;
- }
-
- TestCase::TestCase( ITestCase* testCase, TestCaseInfo const& info ) : TestCaseInfo( info ), test( testCase ) {}
-
- TestCase::TestCase( TestCase const& other )
- : TestCaseInfo( other ),
- test( other.test )
- {}
-
- TestCase TestCase::withName( std::string const& _newName ) const {
- TestCase other( *this );
- other.name = _newName;
- return other;
- }
-
- void TestCase::swap( TestCase& other ) {
- test.swap( other.test );
- name.swap( other.name );
- className.swap( other.className );
- description.swap( other.description );
- tags.swap( other.tags );
- lcaseTags.swap( other.lcaseTags );
- tagsAsString.swap( other.tagsAsString );
- std::swap( TestCaseInfo::properties, static_cast<TestCaseInfo&>( other ).properties );
- std::swap( lineInfo, other.lineInfo );
- }
-
- void TestCase::invoke() const {
- test->invoke();
- }
-
- bool TestCase::operator == ( TestCase const& other ) const {
- return test.get() == other.test.get() &&
- name == other.name &&
- className == other.className;
- }
-
- bool TestCase::operator < ( TestCase const& other ) const {
- return name < other.name;
- }
- TestCase& TestCase::operator = ( TestCase const& other ) {
- TestCase temp( other );
- swap( temp );
- return *this;
- }
-
- TestCaseInfo const& TestCase::getTestCaseInfo() const
- {
- return *this;
- }
-
-} // end namespace Catch
-
-// #included from: catch_version.hpp
-#define TWOBLUECUBES_CATCH_VERSION_HPP_INCLUDED
-
-namespace Catch {
-
- Version::Version
- ( unsigned int _majorVersion,
- unsigned int _minorVersion,
- unsigned int _patchNumber,
- std::string const& _branchName,
- unsigned int _buildNumber )
- : majorVersion( _majorVersion ),
- minorVersion( _minorVersion ),
- patchNumber( _patchNumber ),
- branchName( _branchName ),
- buildNumber( _buildNumber )
- {}
-
- std::ostream& operator << ( std::ostream& os, Version const& version ) {
- os << version.majorVersion << "."
- << version.minorVersion << "."
- << version.patchNumber;
-
- if( !version.branchName.empty() ) {
- os << "-" << version.branchName
- << "." << version.buildNumber;
- }
- return os;
- }
-
- Version libraryVersion( 1, 2, 1, "", 0 );
-
-}
-
-// #included from: catch_message.hpp
-#define TWOBLUECUBES_CATCH_MESSAGE_HPP_INCLUDED
-
-namespace Catch {
-
- MessageInfo::MessageInfo( std::string const& _macroName,
- SourceLineInfo const& _lineInfo,
- ResultWas::OfType _type )
- : macroName( _macroName ),
- lineInfo( _lineInfo ),
- type( _type ),
- sequence( ++globalCount )
- {}
-
- // This may need protecting if threading support is added
- unsigned int MessageInfo::globalCount = 0;
-
- ////////////////////////////////////////////////////////////////////////////
-
- ScopedMessage::ScopedMessage( MessageBuilder const& builder )
- : m_info( builder.m_info )
- {
- m_info.message = builder.m_stream.str();
- getResultCapture().pushScopedMessage( m_info );
- }
- ScopedMessage::ScopedMessage( ScopedMessage const& other )
- : m_info( other.m_info )
- {}
-
- ScopedMessage::~ScopedMessage() {
- getResultCapture().popScopedMessage( m_info );
- }
-
-} // end namespace Catch
-
-// #included from: catch_legacy_reporter_adapter.hpp
-#define TWOBLUECUBES_CATCH_LEGACY_REPORTER_ADAPTER_HPP_INCLUDED
-
-// #included from: catch_legacy_reporter_adapter.h
-#define TWOBLUECUBES_CATCH_LEGACY_REPORTER_ADAPTER_H_INCLUDED
-
-namespace Catch
-{
- // Deprecated
- struct IReporter : IShared {
- virtual ~IReporter();
-
- virtual bool shouldRedirectStdout() const = 0;
-
- virtual void StartTesting() = 0;
- virtual void EndTesting( Totals const& totals ) = 0;
- virtual void StartGroup( std::string const& groupName ) = 0;
- virtual void EndGroup( std::string const& groupName, Totals const& totals ) = 0;
- virtual void StartTestCase( TestCaseInfo const& testInfo ) = 0;
- virtual void EndTestCase( TestCaseInfo const& testInfo, Totals const& totals, std::string const& stdOut, std::string const& stdErr ) = 0;
- virtual void StartSection( std::string const& sectionName, std::string const& description ) = 0;
- virtual void EndSection( std::string const& sectionName, Counts const& assertions ) = 0;
- virtual void NoAssertionsInSection( std::string const& sectionName ) = 0;
- virtual void NoAssertionsInTestCase( std::string const& testName ) = 0;
- virtual void Aborted() = 0;
- virtual void Result( AssertionResult const& result ) = 0;
- };
-
- class LegacyReporterAdapter : public SharedImpl<IStreamingReporter>
- {
- public:
- LegacyReporterAdapter( Ptr<IReporter> const& legacyReporter );
- virtual ~LegacyReporterAdapter();
-
- virtual ReporterPreferences getPreferences() const;
- virtual void noMatchingTestCases( std::string const& );
- virtual void testRunStarting( TestRunInfo const& );
- virtual void testGroupStarting( GroupInfo const& groupInfo );
- virtual void testCaseStarting( TestCaseInfo const& testInfo );
- virtual void sectionStarting( SectionInfo const& sectionInfo );
- virtual void assertionStarting( AssertionInfo const& );
- virtual bool assertionEnded( AssertionStats const& assertionStats );
- virtual void sectionEnded( SectionStats const& sectionStats );
- virtual void testCaseEnded( TestCaseStats const& testCaseStats );
- virtual void testGroupEnded( TestGroupStats const& testGroupStats );
- virtual void testRunEnded( TestRunStats const& testRunStats );
- virtual void skipTest( TestCaseInfo const& );
-
- private:
- Ptr<IReporter> m_legacyReporter;
- };
-}
-
-namespace Catch
-{
- LegacyReporterAdapter::LegacyReporterAdapter( Ptr<IReporter> const& legacyReporter )
- : m_legacyReporter( legacyReporter )
- {}
- LegacyReporterAdapter::~LegacyReporterAdapter() {}
-
- ReporterPreferences LegacyReporterAdapter::getPreferences() const {
- ReporterPreferences prefs;
- prefs.shouldRedirectStdOut = m_legacyReporter->shouldRedirectStdout();
- return prefs;
- }
-
- void LegacyReporterAdapter::noMatchingTestCases( std::string const& ) {}
- void LegacyReporterAdapter::testRunStarting( TestRunInfo const& ) {
- m_legacyReporter->StartTesting();
- }
- void LegacyReporterAdapter::testGroupStarting( GroupInfo const& groupInfo ) {
- m_legacyReporter->StartGroup( groupInfo.name );
- }
- void LegacyReporterAdapter::testCaseStarting( TestCaseInfo const& testInfo ) {
- m_legacyReporter->StartTestCase( testInfo );
- }
- void LegacyReporterAdapter::sectionStarting( SectionInfo const& sectionInfo ) {
- m_legacyReporter->StartSection( sectionInfo.name, sectionInfo.description );
- }
- void LegacyReporterAdapter::assertionStarting( AssertionInfo const& ) {
- // Not on legacy interface
- }
-
- bool LegacyReporterAdapter::assertionEnded( AssertionStats const& assertionStats ) {
- if( assertionStats.assertionResult.getResultType() != ResultWas::Ok ) {
- for( std::vector<MessageInfo>::const_iterator it = assertionStats.infoMessages.begin(), itEnd = assertionStats.infoMessages.end();
- it != itEnd;
- ++it ) {
- if( it->type == ResultWas::Info ) {
- ResultBuilder rb( it->macroName.c_str(), it->lineInfo, "", ResultDisposition::Normal );
- rb << it->message;
- rb.setResultType( ResultWas::Info );
- AssertionResult result = rb.build();
- m_legacyReporter->Result( result );
- }
- }
- }
- m_legacyReporter->Result( assertionStats.assertionResult );
- return true;
- }
- void LegacyReporterAdapter::sectionEnded( SectionStats const& sectionStats ) {
- if( sectionStats.missingAssertions )
- m_legacyReporter->NoAssertionsInSection( sectionStats.sectionInfo.name );
- m_legacyReporter->EndSection( sectionStats.sectionInfo.name, sectionStats.assertions );
- }
- void LegacyReporterAdapter::testCaseEnded( TestCaseStats const& testCaseStats ) {
- m_legacyReporter->EndTestCase
- ( testCaseStats.testInfo,
- testCaseStats.totals,
- testCaseStats.stdOut,
- testCaseStats.stdErr );
- }
- void LegacyReporterAdapter::testGroupEnded( TestGroupStats const& testGroupStats ) {
- if( testGroupStats.aborting )
- m_legacyReporter->Aborted();
- m_legacyReporter->EndGroup( testGroupStats.groupInfo.name, testGroupStats.totals );
- }
- void LegacyReporterAdapter::testRunEnded( TestRunStats const& testRunStats ) {
- m_legacyReporter->EndTesting( testRunStats.totals );
- }
- void LegacyReporterAdapter::skipTest( TestCaseInfo const& ) {
- }
-}
-
-// #included from: catch_timer.hpp
-
-#ifdef __clang__
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wc++11-long-long"
-#endif
-
-#ifdef CATCH_PLATFORM_WINDOWS
-#include <windows.h>
-#else
-#include <sys/time.h>
-#endif
-
-namespace Catch {
-
- namespace {
-#ifdef CATCH_PLATFORM_WINDOWS
- uint64_t getCurrentTicks() {
- static uint64_t hz=0, hzo=0;
- if (!hz) {
- QueryPerformanceFrequency( reinterpret_cast<LARGE_INTEGER*>( &hz ) );
- QueryPerformanceCounter( reinterpret_cast<LARGE_INTEGER*>( &hzo ) );
- }
- uint64_t t;
- QueryPerformanceCounter( reinterpret_cast<LARGE_INTEGER*>( &t ) );
- return ((t-hzo)*1000000)/hz;
- }
-#else
- uint64_t getCurrentTicks() {
- timeval t;
- gettimeofday(&t,NULL);
- return static_cast<uint64_t>( t.tv_sec ) * 1000000ull + static_cast<uint64_t>( t.tv_usec );
- }
-#endif
- }
-
- void Timer::start() {
- m_ticks = getCurrentTicks();
- }
- unsigned int Timer::getElapsedMicroseconds() const {
- return static_cast<unsigned int>(getCurrentTicks() - m_ticks);
- }
- unsigned int Timer::getElapsedMilliseconds() const {
- return static_cast<unsigned int>(getElapsedMicroseconds()/1000);
- }
- double Timer::getElapsedSeconds() const {
- return getElapsedMicroseconds()/1000000.0;
- }
-
-} // namespace Catch
-
-#ifdef __clang__
-#pragma clang diagnostic pop
-#endif
-// #included from: catch_common.hpp
-#define TWOBLUECUBES_CATCH_COMMON_HPP_INCLUDED
-
-namespace Catch {
-
- bool startsWith( std::string const& s, std::string const& prefix ) {
- return s.size() >= prefix.size() && s.substr( 0, prefix.size() ) == prefix;
- }
- bool endsWith( std::string const& s, std::string const& suffix ) {
- return s.size() >= suffix.size() && s.substr( s.size()-suffix.size(), suffix.size() ) == suffix;
- }
- bool contains( std::string const& s, std::string const& infix ) {
- return s.find( infix ) != std::string::npos;
- }
- void toLowerInPlace( std::string& s ) {
- std::transform( s.begin(), s.end(), s.begin(), ::tolower );
- }
- std::string toLower( std::string const& s ) {
- std::string lc = s;
- toLowerInPlace( lc );
- return lc;
- }
- std::string trim( std::string const& str ) {
- static char const* whitespaceChars = "\n\r\t ";
- std::string::size_type start = str.find_first_not_of( whitespaceChars );
- std::string::size_type end = str.find_last_not_of( whitespaceChars );
-
- return start != std::string::npos ? str.substr( start, 1+end-start ) : "";
- }
-
- bool replaceInPlace( std::string& str, std::string const& replaceThis, std::string const& withThis ) {
- bool replaced = false;
- std::size_t i = str.find( replaceThis );
- while( i != std::string::npos ) {
- replaced = true;
- str = str.substr( 0, i ) + withThis + str.substr( i+replaceThis.size() );
- if( i < str.size()-withThis.size() )
- i = str.find( replaceThis, i+withThis.size() );
- else
- i = std::string::npos;
- }
- return replaced;
- }
-
- pluralise::pluralise( std::size_t count, std::string const& label )
- : m_count( count ),
- m_label( label )
- {}
-
- std::ostream& operator << ( std::ostream& os, pluralise const& pluraliser ) {
- os << pluraliser.m_count << " " << pluraliser.m_label;
- if( pluraliser.m_count != 1 )
- os << "s";
- return os;
- }
-
- SourceLineInfo::SourceLineInfo() : line( 0 ){}
- SourceLineInfo::SourceLineInfo( char const* _file, std::size_t _line )
- : file( _file ),
- line( _line )
- {}
- SourceLineInfo::SourceLineInfo( SourceLineInfo const& other )
- : file( other.file ),
- line( other.line )
- {}
- bool SourceLineInfo::empty() const {
- return file.empty();
- }
- bool SourceLineInfo::operator == ( SourceLineInfo const& other ) const {
- return line == other.line && file == other.file;
- }
- bool SourceLineInfo::operator < ( SourceLineInfo const& other ) const {
- return line < other.line || ( line == other.line && file < other.file );
- }
-
- std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info ) {
-#ifndef __GNUG__
- os << info.file << "(" << info.line << ")";
-#else
- os << info.file << ":" << info.line;
-#endif
- return os;
- }
-
- void throwLogicError( std::string const& message, SourceLineInfo const& locationInfo ) {
- std::ostringstream oss;
- oss << locationInfo << ": Internal Catch error: '" << message << "'";
- if( alwaysTrue() )
- throw std::logic_error( oss.str() );
- }
-}
-
-// #included from: catch_section.hpp
-#define TWOBLUECUBES_CATCH_SECTION_HPP_INCLUDED
-
-namespace Catch {
-
- SectionInfo::SectionInfo
- ( SourceLineInfo const& _lineInfo,
- std::string const& _name,
- std::string const& _description )
- : name( _name ),
- description( _description ),
- lineInfo( _lineInfo )
- {}
-
- Section::Section( SectionInfo const& info )
- : m_info( info ),
- m_sectionIncluded( getResultCapture().sectionStarted( m_info, m_assertions ) )
- {
- m_timer.start();
- }
-
- Section::~Section() {
- if( m_sectionIncluded )
- getResultCapture().sectionEnded( m_info, m_assertions, m_timer.getElapsedSeconds() );
- }
-
- // This indicates whether the section should be executed or not
- Section::operator bool() const {
- return m_sectionIncluded;
- }
-
-} // end namespace Catch
-
-// #included from: catch_debugger.hpp
-#define TWOBLUECUBES_CATCH_DEBUGGER_HPP_INCLUDED
-
-#include <iostream>
-
-#ifdef CATCH_PLATFORM_MAC
-
- #include <assert.h>
- #include <stdbool.h>
- #include <sys/types.h>
- #include <unistd.h>
- #include <sys/sysctl.h>
-
- namespace Catch{
-
- // The following function is taken directly from the following technical note:
- // http://developer.apple.com/library/mac/#qa/qa2004/qa1361.html
-
- // Returns true if the current process is being debugged (either
- // running under the debugger or has a debugger attached post facto).
- bool isDebuggerActive(){
-
- int mib[4];
- struct kinfo_proc info;
- size_t size;
-
- // Initialize the flags so that, if sysctl fails for some bizarre
- // reason, we get a predictable result.
-
- info.kp_proc.p_flag = 0;
-
- // Initialize mib, which tells sysctl the info we want, in this case
- // we're looking for information about a specific process ID.
-
- mib[0] = CTL_KERN;
- mib[1] = KERN_PROC;
- mib[2] = KERN_PROC_PID;
- mib[3] = getpid();
-
- // Call sysctl.
-
- size = sizeof(info);
- if( sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, NULL, 0) != 0 ) {
- Catch::cerr() << "\n** Call to sysctl failed - unable to determine if debugger is active **\n" << std::endl;
- return false;
- }
-
- // We're being debugged if the P_TRACED flag is set.
-
- return ( (info.kp_proc.p_flag & P_TRACED) != 0 );
- }
- } // namespace Catch
-
-#elif defined(_MSC_VER)
- extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent();
- namespace Catch {
- bool isDebuggerActive() {
- return IsDebuggerPresent() != 0;
- }
- }
-#elif defined(__MINGW32__)
- extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent();
- namespace Catch {
- bool isDebuggerActive() {
- return IsDebuggerPresent() != 0;
- }
- }
-#else
- namespace Catch {
- inline bool isDebuggerActive() { return false; }
- }
-#endif // Platform
-
-#ifdef CATCH_PLATFORM_WINDOWS
- extern "C" __declspec(dllimport) void __stdcall OutputDebugStringA( const char* );
- namespace Catch {
- void writeToDebugConsole( std::string const& text ) {
- ::OutputDebugStringA( text.c_str() );
- }
- }
-#else
- namespace Catch {
- void writeToDebugConsole( std::string const& text ) {
- // !TBD: Need a version for Mac/ XCode and other IDEs
- Catch::cout() << text;
- }
- }
-#endif // Platform
-
-// #included from: catch_tostring.hpp
-#define TWOBLUECUBES_CATCH_TOSTRING_HPP_INCLUDED
-
-namespace Catch {
-
-namespace Detail {
-
- std::string unprintableString = "{?}";
-
- namespace {
- struct Endianness {
- enum Arch { Big, Little };
-
- static Arch which() {
- union _{
- int asInt;
- char asChar[sizeof (int)];
- } u;
-
- u.asInt = 1;
- return ( u.asChar[sizeof(int)-1] == 1 ) ? Big : Little;
- }
- };
- }
-
- std::string rawMemoryToString( const void *object, std::size_t size )
- {
- // Reverse order for little endian architectures
- int i = 0, end = static_cast<int>( size ), inc = 1;
- if( Endianness::which() == Endianness::Little ) {
- i = end-1;
- end = inc = -1;
- }
-
- unsigned char const *bytes = static_cast<unsigned char const *>(object);
- std::ostringstream os;
- os << "0x" << std::setfill('0') << std::hex;
- for( ; i != end; i += inc )
- os << std::setw(2) << static_cast<unsigned>(bytes[i]);
- return os.str();
- }
-}
-
-std::string toString( std::string const& value ) {
- std::string s = value;
- if( getCurrentContext().getConfig()->showInvisibles() ) {
- for(size_t i = 0; i < s.size(); ++i ) {
- std::string subs;
- switch( s[i] ) {
- case '\n': subs = "\\n"; break;
- case '\t': subs = "\\t"; break;
- default: break;
- }
- if( !subs.empty() ) {
- s = s.substr( 0, i ) + subs + s.substr( i+1 );
- ++i;
- }
- }
- }
- return "\"" + s + "\"";
-}
-std::string toString( std::wstring const& value ) {
-
- std::string s;
- s.reserve( value.size() );
- for(size_t i = 0; i < value.size(); ++i )
- s += value[i] <= 0xff ? static_cast<char>( value[i] ) : '?';
- return Catch::toString( s );
-}
-
-std::string toString( const char* const value ) {
- return value ? Catch::toString( std::string( value ) ) : std::string( "{null string}" );
-}
-
-std::string toString( char* const value ) {
- return Catch::toString( static_cast<const char*>( value ) );
-}
-
-std::string toString( const wchar_t* const value )
-{
- return value ? Catch::toString( std::wstring(value) ) : std::string( "{null string}" );
-}
-
-std::string toString( wchar_t* const value )
-{
- return Catch::toString( static_cast<const wchar_t*>( value ) );
-}
-
-std::string toString( int value ) {
- std::ostringstream oss;
- oss << value;
- if( value >= 255 )
- oss << " (0x" << std::hex << value << ")";
- return oss.str();
-}
-
-std::string toString( unsigned long value ) {
- std::ostringstream oss;
- oss << value;
- if( value >= 255 )
- oss << " (0x" << std::hex << value << ")";
- return oss.str();
-}
-
-std::string toString( unsigned int value ) {
- return Catch::toString( static_cast<unsigned long>( value ) );
-}
-
-template<typename T>
-std::string fpToString( T value, int precision ) {
- std::ostringstream oss;
- oss << std::setprecision( precision )
- << std::fixed
- << value;
- std::string d = oss.str();
- std::size_t i = d.find_last_not_of( '0' );
- if( i != std::string::npos && i != d.size()-1 ) {
- if( d[i] == '.' )
- i++;
- d = d.substr( 0, i+1 );
- }
- return d;
-}
-
-std::string toString( const double value ) {
- return fpToString( value, 10 );
-}
-std::string toString( const float value ) {
- return fpToString( value, 5 ) + "f";
-}
-
-std::string toString( bool value ) {
- return value ? "true" : "false";
-}
-
-std::string toString( char value ) {
- return value < ' '
- ? toString( static_cast<unsigned int>( value ) )
- : Detail::makeString( value );
-}
-
-std::string toString( signed char value ) {
- return toString( static_cast<char>( value ) );
-}
-
-std::string toString( unsigned char value ) {
- return toString( static_cast<char>( value ) );
-}
-
-#ifdef CATCH_CONFIG_CPP11_NULLPTR
-std::string toString( std::nullptr_t ) {
- return "nullptr";
-}
-#endif
-
-#ifdef __OBJC__
- std::string toString( NSString const * const& nsstring ) {
- if( !nsstring )
- return "nil";
- return "@" + toString([nsstring UTF8String]);
- }
- std::string toString( NSString * CATCH_ARC_STRONG const& nsstring ) {
- if( !nsstring )
- return "nil";
- return "@" + toString([nsstring UTF8String]);
- }
- std::string toString( NSObject* const& nsObject ) {
- return toString( [nsObject description] );
- }
-#endif
-
-} // end namespace Catch
-
-// #included from: catch_result_builder.hpp
-#define TWOBLUECUBES_CATCH_RESULT_BUILDER_HPP_INCLUDED
-
-namespace Catch {
-
- ResultBuilder::ResultBuilder( char const* macroName,
- SourceLineInfo const& lineInfo,
- char const* capturedExpression,
- ResultDisposition::Flags resultDisposition )
- : m_assertionInfo( macroName, lineInfo, capturedExpression, resultDisposition ),
- m_shouldDebugBreak( false ),
- m_shouldThrow( false )
- {}
-
- ResultBuilder& ResultBuilder::setResultType( ResultWas::OfType result ) {
- m_data.resultType = result;
- return *this;
- }
- ResultBuilder& ResultBuilder::setResultType( bool result ) {
- m_data.resultType = result ? ResultWas::Ok : ResultWas::ExpressionFailed;
- return *this;
- }
- ResultBuilder& ResultBuilder::setLhs( std::string const& lhs ) {
- m_exprComponents.lhs = lhs;
- return *this;
- }
- ResultBuilder& ResultBuilder::setRhs( std::string const& rhs ) {
- m_exprComponents.rhs = rhs;
- return *this;
- }
- ResultBuilder& ResultBuilder::setOp( std::string const& op ) {
- m_exprComponents.op = op;
- return *this;
- }
-
- void ResultBuilder::endExpression() {
- m_exprComponents.testFalse = isFalseTest( m_assertionInfo.resultDisposition );
- captureExpression();
- }
-
- void ResultBuilder::useActiveException( ResultDisposition::Flags resultDisposition ) {
- m_assertionInfo.resultDisposition = resultDisposition;
- m_stream.oss << Catch::translateActiveException();
- captureResult( ResultWas::ThrewException );
- }
-
- void ResultBuilder::captureResult( ResultWas::OfType resultType ) {
- setResultType( resultType );
- captureExpression();
- }
-
- void ResultBuilder::captureExpression() {
- AssertionResult result = build();
- getResultCapture().assertionEnded( result );
-
- if( !result.isOk() ) {
- if( getCurrentContext().getConfig()->shouldDebugBreak() )
- m_shouldDebugBreak = true;
- if( getCurrentContext().getRunner()->aborting() || (m_assertionInfo.resultDisposition & ResultDisposition::Normal) )
- m_shouldThrow = true;
- }
- }
- void ResultBuilder::react() {
- if( m_shouldThrow )
- throw Catch::TestFailureException();
- }
-
- bool ResultBuilder::shouldDebugBreak() const { return m_shouldDebugBreak; }
- bool ResultBuilder::allowThrows() const { return getCurrentContext().getConfig()->allowThrows(); }
-
- AssertionResult ResultBuilder::build() const
- {
- assert( m_data.resultType != ResultWas::Unknown );
-
- AssertionResultData data = m_data;
-
- // Flip bool results if testFalse is set
- if( m_exprComponents.testFalse ) {
- if( data.resultType == ResultWas::Ok )
- data.resultType = ResultWas::ExpressionFailed;
- else if( data.resultType == ResultWas::ExpressionFailed )
- data.resultType = ResultWas::Ok;
- }
-
- data.message = m_stream.oss.str();
- data.reconstructedExpression = reconstructExpression();
- if( m_exprComponents.testFalse ) {
- if( m_exprComponents.op == "" )
- data.reconstructedExpression = "!" + data.reconstructedExpression;
- else
- data.reconstructedExpression = "!(" + data.reconstructedExpression + ")";
- }
- return AssertionResult( m_assertionInfo, data );
- }
- std::string ResultBuilder::reconstructExpression() const {
- if( m_exprComponents.op == "" )
- return m_exprComponents.lhs.empty() ? m_assertionInfo.capturedExpression : m_exprComponents.op + m_exprComponents.lhs;
- else if( m_exprComponents.op == "matches" )
- return m_exprComponents.lhs + " " + m_exprComponents.rhs;
- else if( m_exprComponents.op != "!" ) {
- if( m_exprComponents.lhs.size() + m_exprComponents.rhs.size() < 40 &&
- m_exprComponents.lhs.find("\n") == std::string::npos &&
- m_exprComponents.rhs.find("\n") == std::string::npos )
- return m_exprComponents.lhs + " " + m_exprComponents.op + " " + m_exprComponents.rhs;
- else
- return m_exprComponents.lhs + "\n" + m_exprComponents.op + "\n" + m_exprComponents.rhs;
- }
- else
- return "{can't expand - use " + m_assertionInfo.macroName + "_FALSE( " + m_assertionInfo.capturedExpression.substr(1) + " ) instead of " + m_assertionInfo.macroName + "( " + m_assertionInfo.capturedExpression + " ) for better diagnostics}";
- }
-
-} // end namespace Catch
-
-// #included from: catch_tag_alias_registry.hpp
-#define TWOBLUECUBES_CATCH_TAG_ALIAS_REGISTRY_HPP_INCLUDED
-
-// #included from: catch_tag_alias_registry.h
-#define TWOBLUECUBES_CATCH_TAG_ALIAS_REGISTRY_H_INCLUDED
-
-#include <map>
-
-namespace Catch {
-
- class TagAliasRegistry : public ITagAliasRegistry {
- public:
- virtual ~TagAliasRegistry();
- virtual Option<TagAlias> find( std::string const& alias ) const;
- virtual std::string expandAliases( std::string const& unexpandedTestSpec ) const;
- void add( char const* alias, char const* tag, SourceLineInfo const& lineInfo );
- static TagAliasRegistry& get();
-
- private:
- std::map<std::string, TagAlias> m_registry;
- };
-
-} // end namespace Catch
-
-#include <map>
-#include <iostream>
-
-namespace Catch {
-
- TagAliasRegistry::~TagAliasRegistry() {}
-
- Option<TagAlias> TagAliasRegistry::find( std::string const& alias ) const {
- std::map<std::string, TagAlias>::const_iterator it = m_registry.find( alias );
- if( it != m_registry.end() )
- return it->second;
- else
- return Option<TagAlias>();
- }
-
- std::string TagAliasRegistry::expandAliases( std::string const& unexpandedTestSpec ) const {
- std::string expandedTestSpec = unexpandedTestSpec;
- for( std::map<std::string, TagAlias>::const_iterator it = m_registry.begin(), itEnd = m_registry.end();
- it != itEnd;
- ++it ) {
- std::size_t pos = expandedTestSpec.find( it->first );
- if( pos != std::string::npos ) {
- expandedTestSpec = expandedTestSpec.substr( 0, pos ) +
- it->second.tag +
- expandedTestSpec.substr( pos + it->first.size() );
- }
- }
- return expandedTestSpec;
- }
-
- void TagAliasRegistry::add( char const* alias, char const* tag, SourceLineInfo const& lineInfo ) {
-
- if( !startsWith( alias, "[@" ) || !endsWith( alias, "]" ) ) {
- std::ostringstream oss;
- oss << "error: tag alias, \"" << alias << "\" is not of the form [@alias name].\n" << lineInfo;
- throw std::domain_error( oss.str().c_str() );
- }
- if( !m_registry.insert( std::make_pair( alias, TagAlias( tag, lineInfo ) ) ).second ) {
- std::ostringstream oss;
- oss << "error: tag alias, \"" << alias << "\" already registered.\n"
- << "\tFirst seen at " << find(alias)->lineInfo << "\n"
- << "\tRedefined at " << lineInfo;
- throw std::domain_error( oss.str().c_str() );
- }
- }
-
- TagAliasRegistry& TagAliasRegistry::get() {
- static TagAliasRegistry instance;
- return instance;
-
- }
-
- ITagAliasRegistry::~ITagAliasRegistry() {}
- ITagAliasRegistry const& ITagAliasRegistry::get() { return TagAliasRegistry::get(); }
-
- RegistrarForTagAliases::RegistrarForTagAliases( char const* alias, char const* tag, SourceLineInfo const& lineInfo ) {
- try {
- TagAliasRegistry::get().add( alias, tag, lineInfo );
- }
- catch( std::exception& ex ) {
- Colour colourGuard( Colour::Red );
- Catch::cerr() << ex.what() << std::endl;
- exit(1);
- }
- }
-
-} // end namespace Catch
-
-// #included from: ../reporters/catch_reporter_xml.hpp
-#define TWOBLUECUBES_CATCH_REPORTER_XML_HPP_INCLUDED
-
-// #included from: catch_reporter_bases.hpp
-#define TWOBLUECUBES_CATCH_REPORTER_BASES_HPP_INCLUDED
-
-#include <cstring>
-
-namespace Catch {
-
- struct StreamingReporterBase : SharedImpl<IStreamingReporter> {
-
- StreamingReporterBase( ReporterConfig const& _config )
- : m_config( _config.fullConfig() ),
- stream( _config.stream() )
- {}
-
- virtual ~StreamingReporterBase();
-
- virtual void noMatchingTestCases( std::string const& ) {}
-
- virtual void testRunStarting( TestRunInfo const& _testRunInfo ) {
- currentTestRunInfo = _testRunInfo;
- }
- virtual void testGroupStarting( GroupInfo const& _groupInfo ) {
- currentGroupInfo = _groupInfo;
- }
-
- virtual void testCaseStarting( TestCaseInfo const& _testInfo ) {
- currentTestCaseInfo = _testInfo;
- }
- virtual void sectionStarting( SectionInfo const& _sectionInfo ) {
- m_sectionStack.push_back( _sectionInfo );
- }
-
- virtual void sectionEnded( SectionStats const& /* _sectionStats */ ) {
- m_sectionStack.pop_back();
- }
- virtual void testCaseEnded( TestCaseStats const& /* _testCaseStats */ ) {
- currentTestCaseInfo.reset();
- }
- virtual void testGroupEnded( TestGroupStats const& /* _testGroupStats */ ) {
- currentGroupInfo.reset();
- }
- virtual void testRunEnded( TestRunStats const& /* _testRunStats */ ) {
- currentTestCaseInfo.reset();
- currentGroupInfo.reset();
- currentTestRunInfo.reset();
- }
-
- virtual void skipTest( TestCaseInfo const& ) {
- // Don't do anything with this by default.
- // It can optionally be overridden in the derived class.
- }
-
- Ptr<IConfig> m_config;
- std::ostream& stream;
-
- LazyStat<TestRunInfo> currentTestRunInfo;
- LazyStat<GroupInfo> currentGroupInfo;
- LazyStat<TestCaseInfo> currentTestCaseInfo;
-
- std::vector<SectionInfo> m_sectionStack;
- };
-
- struct CumulativeReporterBase : SharedImpl<IStreamingReporter> {
- template<typename T, typename ChildNodeT>
- struct Node : SharedImpl<> {
- explicit Node( T const& _value ) : value( _value ) {}
- virtual ~Node() {}
-
- typedef std::vector<Ptr<ChildNodeT> > ChildNodes;
- T value;
- ChildNodes children;
- };
- struct SectionNode : SharedImpl<> {
- explicit SectionNode( SectionStats const& _stats ) : stats( _stats ) {}
- virtual ~SectionNode();
-
- bool operator == ( SectionNode const& other ) const {
- return stats.sectionInfo.lineInfo == other.stats.sectionInfo.lineInfo;
- }
- bool operator == ( Ptr<SectionNode> const& other ) const {
- return operator==( *other );
- }
-
- SectionStats stats;
- typedef std::vector<Ptr<SectionNode> > ChildSections;
- typedef std::vector<AssertionStats> Assertions;
- ChildSections childSections;
- Assertions assertions;
- std::string stdOut;
- std::string stdErr;
- };
-
- struct BySectionInfo {
- BySectionInfo( SectionInfo const& other ) : m_other( other ) {}
- BySectionInfo( BySectionInfo const& other ) : m_other( other.m_other ) {}
- bool operator() ( Ptr<SectionNode> const& node ) const {
- return node->stats.sectionInfo.lineInfo == m_other.lineInfo;
- }
- private:
- void operator=( BySectionInfo const& );
- SectionInfo const& m_other;
- };
-
- typedef Node<TestCaseStats, SectionNode> TestCaseNode;
- typedef Node<TestGroupStats, TestCaseNode> TestGroupNode;
- typedef Node<TestRunStats, TestGroupNode> TestRunNode;
-
- CumulativeReporterBase( ReporterConfig const& _config )
- : m_config( _config.fullConfig() ),
- stream( _config.stream() )
- {}
- ~CumulativeReporterBase();
-
- virtual void testRunStarting( TestRunInfo const& ) {}
- virtual void testGroupStarting( GroupInfo const& ) {}
-
- virtual void testCaseStarting( TestCaseInfo const& ) {}
-
- virtual void sectionStarting( SectionInfo const& sectionInfo ) {
- SectionStats incompleteStats( sectionInfo, Counts(), 0, false );
- Ptr<SectionNode> node;
- if( m_sectionStack.empty() ) {
- if( !m_rootSection )
- m_rootSection = new SectionNode( incompleteStats );
- node = m_rootSection;
- }
- else {
- SectionNode& parentNode = *m_sectionStack.back();
- SectionNode::ChildSections::const_iterator it =
- std::find_if( parentNode.childSections.begin(),
- parentNode.childSections.end(),
- BySectionInfo( sectionInfo ) );
- if( it == parentNode.childSections.end() ) {
- node = new SectionNode( incompleteStats );
- parentNode.childSections.push_back( node );
- }
- else
- node = *it;
- }
- m_sectionStack.push_back( node );
- m_deepestSection = node;
- }
-
- virtual void assertionStarting( AssertionInfo const& ) {}
-
- virtual bool assertionEnded( AssertionStats const& assertionStats ) {
- assert( !m_sectionStack.empty() );
- SectionNode& sectionNode = *m_sectionStack.back();
- sectionNode.assertions.push_back( assertionStats );
- return true;
- }
- virtual void sectionEnded( SectionStats const& sectionStats ) {
- assert( !m_sectionStack.empty() );
- SectionNode& node = *m_sectionStack.back();
- node.stats = sectionStats;
- m_sectionStack.pop_back();
- }
- virtual void testCaseEnded( TestCaseStats const& testCaseStats ) {
- Ptr<TestCaseNode> node = new TestCaseNode( testCaseStats );
- assert( m_sectionStack.size() == 0 );
- node->children.push_back( m_rootSection );
- m_testCases.push_back( node );
- m_rootSection.reset();
-
- assert( m_deepestSection );
- m_deepestSection->stdOut = testCaseStats.stdOut;
- m_deepestSection->stdErr = testCaseStats.stdErr;
- }
- virtual void testGroupEnded( TestGroupStats const& testGroupStats ) {
- Ptr<TestGroupNode> node = new TestGroupNode( testGroupStats );
- node->children.swap( m_testCases );
- m_testGroups.push_back( node );
- }
- virtual void testRunEnded( TestRunStats const& testRunStats ) {
- Ptr<TestRunNode> node = new TestRunNode( testRunStats );
- node->children.swap( m_testGroups );
- m_testRuns.push_back( node );
- testRunEndedCumulative();
- }
- virtual void testRunEndedCumulative() = 0;
-
- virtual void skipTest( TestCaseInfo const& ) {}
-
- Ptr<IConfig> m_config;
- std::ostream& stream;
- std::vector<AssertionStats> m_assertions;
- std::vector<std::vector<Ptr<SectionNode> > > m_sections;
- std::vector<Ptr<TestCaseNode> > m_testCases;
- std::vector<Ptr<TestGroupNode> > m_testGroups;
-
- std::vector<Ptr<TestRunNode> > m_testRuns;
-
- Ptr<SectionNode> m_rootSection;
- Ptr<SectionNode> m_deepestSection;
- std::vector<Ptr<SectionNode> > m_sectionStack;
-
- };
-
- template<char C>
- char const* getLineOfChars() {
- static char line[CATCH_CONFIG_CONSOLE_WIDTH] = {0};
- if( !*line ) {
- memset( line, C, CATCH_CONFIG_CONSOLE_WIDTH-1 );
- line[CATCH_CONFIG_CONSOLE_WIDTH-1] = 0;
- }
- return line;
- }
-
-} // end namespace Catch
-
-// #included from: ../internal/catch_reporter_registrars.hpp
-#define TWOBLUECUBES_CATCH_REPORTER_REGISTRARS_HPP_INCLUDED
-
-namespace Catch {
-
- template<typename T>
- class LegacyReporterRegistrar {
-
- class ReporterFactory : public IReporterFactory {
- virtual IStreamingReporter* create( ReporterConfig const& config ) const {
- return new LegacyReporterAdapter( new T( config ) );
- }
-
- virtual std::string getDescription() const {
- return T::getDescription();
- }
- };
-
- public:
-
- LegacyReporterRegistrar( std::string const& name ) {
- getMutableRegistryHub().registerReporter( name, new ReporterFactory() );
- }
- };
-
- template<typename T>
- class ReporterRegistrar {
-
- class ReporterFactory : public IReporterFactory {
-
- // *** Please Note ***:
- // - If you end up here looking at a compiler error because it's trying to register
- // your custom reporter class be aware that the native reporter interface has changed
- // to IStreamingReporter. The "legacy" interface, IReporter, is still supported via
- // an adapter. Just use REGISTER_LEGACY_REPORTER to take advantage of the adapter.
- // However please consider updating to the new interface as the old one is now
- // deprecated and will probably be removed quite soon!
- // Please contact me via github if you have any questions at all about this.
- // In fact, ideally, please contact me anyway to let me know you've hit this - as I have
- // no idea who is actually using custom reporters at all (possibly no-one!).
- // The new interface is designed to minimise exposure to interface changes in the future.
- virtual IStreamingReporter* create( ReporterConfig const& config ) const {
- return new T( config );
- }
-
- virtual std::string getDescription() const {
- return T::getDescription();
- }
- };
-
- public:
-
- ReporterRegistrar( std::string const& name ) {
- getMutableRegistryHub().registerReporter( name, new ReporterFactory() );
- }
- };
-}
-
-#define INTERNAL_CATCH_REGISTER_LEGACY_REPORTER( name, reporterType ) \
- namespace{ Catch::LegacyReporterRegistrar<reporterType> catch_internal_RegistrarFor##reporterType( name ); }
-#define INTERNAL_CATCH_REGISTER_REPORTER( name, reporterType ) \
- namespace{ Catch::ReporterRegistrar<reporterType> catch_internal_RegistrarFor##reporterType( name ); }
-
-// #included from: ../internal/catch_xmlwriter.hpp
-#define TWOBLUECUBES_CATCH_XMLWRITER_HPP_INCLUDED
-
-#include <sstream>
-#include <string>
-#include <vector>
-
-namespace Catch {
-
- class XmlWriter {
- public:
-
- class ScopedElement {
- public:
- ScopedElement( XmlWriter* writer )
- : m_writer( writer )
- {}
-
- ScopedElement( ScopedElement const& other )
- : m_writer( other.m_writer ){
- other.m_writer = NULL;
- }
-
- ~ScopedElement() {
- if( m_writer )
- m_writer->endElement();
- }
-
- ScopedElement& writeText( std::string const& text, bool indent = true ) {
- m_writer->writeText( text, indent );
- return *this;
- }
-
- template<typename T>
- ScopedElement& writeAttribute( std::string const& name, T const& attribute ) {
- m_writer->writeAttribute( name, attribute );
- return *this;
- }
-
- private:
- mutable XmlWriter* m_writer;
- };
-
- XmlWriter()
- : m_tagIsOpen( false ),
- m_needsNewline( false ),
- m_os( &Catch::cout() )
- {}
-
- XmlWriter( std::ostream& os )
- : m_tagIsOpen( false ),
- m_needsNewline( false ),
- m_os( &os )
- {}
-
- ~XmlWriter() {
- while( !m_tags.empty() )
- endElement();
- }
-
- XmlWriter& startElement( std::string const& name ) {
- ensureTagClosed();
- newlineIfNecessary();
- stream() << m_indent << "<" << name;
- m_tags.push_back( name );
- m_indent += " ";
- m_tagIsOpen = true;
- return *this;
- }
-
- ScopedElement scopedElement( std::string const& name ) {
- ScopedElement scoped( this );
- startElement( name );
- return scoped;
- }
-
- XmlWriter& endElement() {
- newlineIfNecessary();
- m_indent = m_indent.substr( 0, m_indent.size()-2 );
- if( m_tagIsOpen ) {
- stream() << "/>\n";
- m_tagIsOpen = false;
- }
- else {
- stream() << m_indent << "</" << m_tags.back() << ">\n";
- }
- m_tags.pop_back();
- return *this;
- }
-
- XmlWriter& writeAttribute( std::string const& name, std::string const& attribute ) {
- if( !name.empty() && !attribute.empty() ) {
- stream() << " " << name << "=\"";
- writeEncodedText( attribute );
- stream() << "\"";
- }
- return *this;
- }
-
- XmlWriter& writeAttribute( std::string const& name, bool attribute ) {
- stream() << " " << name << "=\"" << ( attribute ? "true" : "false" ) << "\"";
- return *this;
- }
-
- template<typename T>
- XmlWriter& writeAttribute( std::string const& name, T const& attribute ) {
- if( !name.empty() )
- stream() << " " << name << "=\"" << attribute << "\"";
- return *this;
- }
-
- XmlWriter& writeText( std::string const& text, bool indent = true ) {
- if( !text.empty() ){
- bool tagWasOpen = m_tagIsOpen;
- ensureTagClosed();
- if( tagWasOpen && indent )
- stream() << m_indent;
- writeEncodedText( text );
- m_needsNewline = true;
- }
- return *this;
- }
-
- XmlWriter& writeComment( std::string const& text ) {
- ensureTagClosed();
- stream() << m_indent << "<!--" << text << "-->";
- m_needsNewline = true;
- return *this;
- }
-
- XmlWriter& writeBlankLine() {
- ensureTagClosed();
- stream() << "\n";
- return *this;
- }
-
- void setStream( std::ostream& os ) {
- m_os = &os;
- }
-
- private:
- XmlWriter( XmlWriter const& );
- void operator=( XmlWriter const& );
-
- std::ostream& stream() {
- return *m_os;
- }
-
- void ensureTagClosed() {
- if( m_tagIsOpen ) {
- stream() << ">\n";
- m_tagIsOpen = false;
- }
- }
-
- void newlineIfNecessary() {
- if( m_needsNewline ) {
- stream() << "\n";
- m_needsNewline = false;
- }
- }
-
- void writeEncodedText( std::string const& text ) {
- static const char* charsToEncode = "<&\"";
- std::string mtext = text;
- std::string::size_type pos = mtext.find_first_of( charsToEncode );
- while( pos != std::string::npos ) {
- stream() << mtext.substr( 0, pos );
-
- switch( mtext[pos] ) {
- case '<':
- stream() << "<";
- break;
- case '&':
- stream() << "&";
- break;
- case '\"':
- stream() << """;
- break;
- }
- mtext = mtext.substr( pos+1 );
- pos = mtext.find_first_of( charsToEncode );
- }
- stream() << mtext;
- }
-
- bool m_tagIsOpen;
- bool m_needsNewline;
- std::vector<std::string> m_tags;
- std::string m_indent;
- std::ostream* m_os;
- };
-
-}
-namespace Catch {
- class XmlReporter : public StreamingReporterBase {
- public:
- XmlReporter( ReporterConfig const& _config )
- : StreamingReporterBase( _config ),
- m_sectionDepth( 0 )
- {}
-
- virtual ~XmlReporter();
-
- static std::string getDescription() {
- return "Reports test results as an XML document";
- }
-
- public: // StreamingReporterBase
- virtual ReporterPreferences getPreferences() const {
- ReporterPreferences prefs;
- prefs.shouldRedirectStdOut = true;
- return prefs;
- }
-
- virtual void noMatchingTestCases( std::string const& s ) {
- StreamingReporterBase::noMatchingTestCases( s );
- }
-
- virtual void testRunStarting( TestRunInfo const& testInfo ) {
- StreamingReporterBase::testRunStarting( testInfo );
- m_xml.setStream( stream );
- m_xml.startElement( "Catch" );
- if( !m_config->name().empty() )
- m_xml.writeAttribute( "name", m_config->name() );
- }
-
- virtual void testGroupStarting( GroupInfo const& groupInfo ) {
- StreamingReporterBase::testGroupStarting( groupInfo );
- m_xml.startElement( "Group" )
- .writeAttribute( "name", groupInfo.name );
- }
-
- virtual void testCaseStarting( TestCaseInfo const& testInfo ) {
- StreamingReporterBase::testCaseStarting(testInfo);
- m_xml.startElement( "TestCase" ).writeAttribute( "name", trim( testInfo.name ) );
-
- if ( m_config->showDurations() == ShowDurations::Always )
- m_testCaseTimer.start();
- }
-
- virtual void sectionStarting( SectionInfo const& sectionInfo ) {
- StreamingReporterBase::sectionStarting( sectionInfo );
- if( m_sectionDepth++ > 0 ) {
- m_xml.startElement( "Section" )
- .writeAttribute( "name", trim( sectionInfo.name ) )
- .writeAttribute( "description", sectionInfo.description );
- }
- }
-
- virtual void assertionStarting( AssertionInfo const& ) { }
-
- virtual bool assertionEnded( AssertionStats const& assertionStats ) {
- const AssertionResult& assertionResult = assertionStats.assertionResult;
-
- // Print any info messages in <Info> tags.
- if( assertionStats.assertionResult.getResultType() != ResultWas::Ok ) {
- for( std::vector<MessageInfo>::const_iterator it = assertionStats.infoMessages.begin(), itEnd = assertionStats.infoMessages.end();
- it != itEnd;
- ++it ) {
- if( it->type == ResultWas::Info ) {
- m_xml.scopedElement( "Info" )
- .writeText( it->message );
- } else if ( it->type == ResultWas::Warning ) {
- m_xml.scopedElement( "Warning" )
- .writeText( it->message );
- }
- }
- }
-
- // Drop out if result was successful but we're not printing them.
- if( !m_config->includeSuccessfulResults() && isOk(assertionResult.getResultType()) )
- return true;
-
- // Print the expression if there is one.
- if( assertionResult.hasExpression() ) {
- m_xml.startElement( "Expression" )
- .writeAttribute( "success", assertionResult.succeeded() )
- .writeAttribute( "type", assertionResult.getTestMacroName() )
- .writeAttribute( "filename", assertionResult.getSourceInfo().file )
- .writeAttribute( "line", assertionResult.getSourceInfo().line );
-
- m_xml.scopedElement( "Original" )
- .writeText( assertionResult.getExpression() );
- m_xml.scopedElement( "Expanded" )
- .writeText( assertionResult.getExpandedExpression() );
- }
-
- // And... Print a result applicable to each result type.
- switch( assertionResult.getResultType() ) {
- case ResultWas::ThrewException:
- m_xml.scopedElement( "Exception" )
- .writeAttribute( "filename", assertionResult.getSourceInfo().file )
- .writeAttribute( "line", assertionResult.getSourceInfo().line )
- .writeText( assertionResult.getMessage() );
- break;
- case ResultWas::FatalErrorCondition:
- m_xml.scopedElement( "Fatal Error Condition" )
- .writeAttribute( "filename", assertionResult.getSourceInfo().file )
- .writeAttribute( "line", assertionResult.getSourceInfo().line )
- .writeText( assertionResult.getMessage() );
- break;
- case ResultWas::Info:
- m_xml.scopedElement( "Info" )
- .writeText( assertionResult.getMessage() );
- break;
- case ResultWas::Warning:
- // Warning will already have been written
- break;
- case ResultWas::ExplicitFailure:
- m_xml.scopedElement( "Failure" )
- .writeText( assertionResult.getMessage() );
- break;
- default:
- break;
- }
-
- if( assertionResult.hasExpression() )
- m_xml.endElement();
-
- return true;
- }
-
- virtual void sectionEnded( SectionStats const& sectionStats ) {
- StreamingReporterBase::sectionEnded( sectionStats );
- if( --m_sectionDepth > 0 ) {
- XmlWriter::ScopedElement e = m_xml.scopedElement( "OverallResults" );
- e.writeAttribute( "successes", sectionStats.assertions.passed );
- e.writeAttribute( "failures", sectionStats.assertions.failed );
- e.writeAttribute( "expectedFailures", sectionStats.assertions.failedButOk );
-
- if ( m_config->showDurations() == ShowDurations::Always )
- e.writeAttribute( "durationInSeconds", sectionStats.durationInSeconds );
-
- m_xml.endElement();
- }
- }
-
- virtual void testCaseEnded( TestCaseStats const& testCaseStats ) {
- StreamingReporterBase::testCaseEnded( testCaseStats );
- XmlWriter::ScopedElement e = m_xml.scopedElement( "OverallResult" );
- e.writeAttribute( "success", testCaseStats.totals.assertions.allOk() );
-
- if ( m_config->showDurations() == ShowDurations::Always )
- e.writeAttribute( "durationInSeconds", m_testCaseTimer.getElapsedSeconds() );
-
- m_xml.endElement();
- }
-
- virtual void testGroupEnded( TestGroupStats const& testGroupStats ) {
- StreamingReporterBase::testGroupEnded( testGroupStats );
- // TODO: Check testGroupStats.aborting and act accordingly.
- m_xml.scopedElement( "OverallResults" )
- .writeAttribute( "successes", testGroupStats.totals.assertions.passed )
- .writeAttribute( "failures", testGroupStats.totals.assertions.failed )
- .writeAttribute( "expectedFailures", testGroupStats.totals.assertions.failedButOk );
- m_xml.endElement();
- }
-
- virtual void testRunEnded( TestRunStats const& testRunStats ) {
- StreamingReporterBase::testRunEnded( testRunStats );
- m_xml.scopedElement( "OverallResults" )
- .writeAttribute( "successes", testRunStats.totals.assertions.passed )
- .writeAttribute( "failures", testRunStats.totals.assertions.failed )
- .writeAttribute( "expectedFailures", testRunStats.totals.assertions.failedButOk );
- m_xml.endElement();
- }
-
- private:
- Timer m_testCaseTimer;
- XmlWriter m_xml;
- int m_sectionDepth;
- };
-
- INTERNAL_CATCH_REGISTER_REPORTER( "xml", XmlReporter )
-
-} // end namespace Catch
-
-// #included from: ../reporters/catch_reporter_junit.hpp
-#define TWOBLUECUBES_CATCH_REPORTER_JUNIT_HPP_INCLUDED
-
-#include <assert.h>
-
-namespace Catch {
-
- class JunitReporter : public CumulativeReporterBase {
- public:
- JunitReporter( ReporterConfig const& _config )
- : CumulativeReporterBase( _config ),
- xml( _config.stream() )
- {}
-
- ~JunitReporter();
-
- static std::string getDescription() {
- return "Reports test results in an XML format that looks like Ant's junitreport target";
- }
-
- virtual void noMatchingTestCases( std::string const& /*spec*/ ) {}
-
- virtual ReporterPreferences getPreferences() const {
- ReporterPreferences prefs;
- prefs.shouldRedirectStdOut = true;
- return prefs;
- }
-
- virtual void testRunStarting( TestRunInfo const& runInfo ) {
- CumulativeReporterBase::testRunStarting( runInfo );
- xml.startElement( "testsuites" );
- }
-
- virtual void testGroupStarting( GroupInfo const& groupInfo ) {
- suiteTimer.start();
- stdOutForSuite.str("");
- stdErrForSuite.str("");
- unexpectedExceptions = 0;
- CumulativeReporterBase::testGroupStarting( groupInfo );
- }
-
- virtual bool assertionEnded( AssertionStats const& assertionStats ) {
- if( assertionStats.assertionResult.getResultType() == ResultWas::ThrewException )
- unexpectedExceptions++;
- return CumulativeReporterBase::assertionEnded( assertionStats );
- }
-
- virtual void testCaseEnded( TestCaseStats const& testCaseStats ) {
- stdOutForSuite << testCaseStats.stdOut;
- stdErrForSuite << testCaseStats.stdErr;
- CumulativeReporterBase::testCaseEnded( testCaseStats );
- }
-
- virtual void testGroupEnded( TestGroupStats const& testGroupStats ) {
- double suiteTime = suiteTimer.getElapsedSeconds();
- CumulativeReporterBase::testGroupEnded( testGroupStats );
- writeGroup( *m_testGroups.back(), suiteTime );
- }
-
- virtual void testRunEndedCumulative() {
- xml.endElement();
- }
-
- void writeGroup( TestGroupNode const& groupNode, double suiteTime ) {
- XmlWriter::ScopedElement e = xml.scopedElement( "testsuite" );
- TestGroupStats const& stats = groupNode.value;
- xml.writeAttribute( "name", stats.groupInfo.name );
- xml.writeAttribute( "errors", unexpectedExceptions );
- xml.writeAttribute( "failures", stats.totals.assertions.failed-unexpectedExceptions );
- xml.writeAttribute( "tests", stats.totals.assertions.total() );
- xml.writeAttribute( "hostname", "tbd" ); // !TBD
- if( m_config->showDurations() == ShowDurations::Never )
- xml.writeAttribute( "time", "" );
- else
- xml.writeAttribute( "time", suiteTime );
- xml.writeAttribute( "timestamp", "tbd" ); // !TBD
-
- // Write test cases
- for( TestGroupNode::ChildNodes::const_iterator
- it = groupNode.children.begin(), itEnd = groupNode.children.end();
- it != itEnd;
- ++it )
- writeTestCase( **it );
-
- xml.scopedElement( "system-out" ).writeText( trim( stdOutForSuite.str() ), false );
- xml.scopedElement( "system-err" ).writeText( trim( stdErrForSuite.str() ), false );
- }
-
- void writeTestCase( TestCaseNode const& testCaseNode ) {
- TestCaseStats const& stats = testCaseNode.value;
-
- // All test cases have exactly one section - which represents the
- // test case itself. That section may have 0-n nested sections
- assert( testCaseNode.children.size() == 1 );
- SectionNode const& rootSection = *testCaseNode.children.front();
-
- std::string className = stats.testInfo.className;
-
- if( className.empty() ) {
- if( rootSection.childSections.empty() )
- className = "global";
- }
- writeSection( className, "", rootSection );
- }
-
- void writeSection( std::string const& className,
- std::string const& rootName,
- SectionNode const& sectionNode ) {
- std::string name = trim( sectionNode.stats.sectionInfo.name );
- if( !rootName.empty() )
- name = rootName + "/" + name;
-
- if( !sectionNode.assertions.empty() ||
- !sectionNode.stdOut.empty() ||
- !sectionNode.stdErr.empty() ) {
- XmlWriter::ScopedElement e = xml.scopedElement( "testcase" );
- if( className.empty() ) {
- xml.writeAttribute( "classname", name );
- xml.writeAttribute( "name", "root" );
- }
- else {
- xml.writeAttribute( "classname", className );
- xml.writeAttribute( "name", name );
- }
- xml.writeAttribute( "time", Catch::toString( sectionNode.stats.durationInSeconds ) );
-
- writeAssertions( sectionNode );
-
- if( !sectionNode.stdOut.empty() )
- xml.scopedElement( "system-out" ).writeText( trim( sectionNode.stdOut ), false );
- if( !sectionNode.stdErr.empty() )
- xml.scopedElement( "system-err" ).writeText( trim( sectionNode.stdErr ), false );
- }
- for( SectionNode::ChildSections::const_iterator
- it = sectionNode.childSections.begin(),
- itEnd = sectionNode.childSections.end();
- it != itEnd;
- ++it )
- if( className.empty() )
- writeSection( name, "", **it );
- else
- writeSection( className, name, **it );
- }
-
- void writeAssertions( SectionNode const& sectionNode ) {
- for( SectionNode::Assertions::const_iterator
- it = sectionNode.assertions.begin(), itEnd = sectionNode.assertions.end();
- it != itEnd;
- ++it )
- writeAssertion( *it );
- }
- void writeAssertion( AssertionStats const& stats ) {
- AssertionResult const& result = stats.assertionResult;
- if( !result.isOk() ) {
- std::string elementName;
- switch( result.getResultType() ) {
- case ResultWas::ThrewException:
- case ResultWas::FatalErrorCondition:
- elementName = "error";
- break;
- case ResultWas::ExplicitFailure:
- elementName = "failure";
- break;
- case ResultWas::ExpressionFailed:
- elementName = "failure";
- break;
- case ResultWas::DidntThrowException:
- elementName = "failure";
- break;
-
- // We should never see these here:
- case ResultWas::Info:
- case ResultWas::Warning:
- case ResultWas::Ok:
- case ResultWas::Unknown:
- case ResultWas::FailureBit:
- case ResultWas::Exception:
- elementName = "internalError";
- break;
- }
-
- XmlWriter::ScopedElement e = xml.scopedElement( elementName );
-
- xml.writeAttribute( "message", result.getExpandedExpression() );
- xml.writeAttribute( "type", result.getTestMacroName() );
-
- std::ostringstream oss;
- if( !result.getMessage().empty() )
- oss << result.getMessage() << "\n";
- for( std::vector<MessageInfo>::const_iterator
- it = stats.infoMessages.begin(),
- itEnd = stats.infoMessages.end();
- it != itEnd;
- ++it )
- if( it->type == ResultWas::Info )
- oss << it->message << "\n";
-
- oss << "at " << result.getSourceInfo();
- xml.writeText( oss.str(), false );
- }
- }
-
- XmlWriter xml;
- Timer suiteTimer;
- std::ostringstream stdOutForSuite;
- std::ostringstream stdErrForSuite;
- unsigned int unexpectedExceptions;
- };
-
- INTERNAL_CATCH_REGISTER_REPORTER( "junit", JunitReporter )
-
-} // end namespace Catch
-
-// #included from: ../reporters/catch_reporter_console.hpp
-#define TWOBLUECUBES_CATCH_REPORTER_CONSOLE_HPP_INCLUDED
-
-namespace Catch {
-
- struct ConsoleReporter : StreamingReporterBase {
- ConsoleReporter( ReporterConfig const& _config )
- : StreamingReporterBase( _config ),
- m_headerPrinted( false )
- {}
-
- virtual ~ConsoleReporter();
- static std::string getDescription() {
- return "Reports test results as plain lines of text";
- }
- virtual ReporterPreferences getPreferences() const {
- ReporterPreferences prefs;
- prefs.shouldRedirectStdOut = false;
- return prefs;
- }
-
- virtual void noMatchingTestCases( std::string const& spec ) {
- stream << "No test cases matched '" << spec << "'" << std::endl;
- }
-
- virtual void assertionStarting( AssertionInfo const& ) {
- }
-
- virtual bool assertionEnded( AssertionStats const& _assertionStats ) {
- AssertionResult const& result = _assertionStats.assertionResult;
-
- bool printInfoMessages = true;
-
- // Drop out if result was successful and we're not printing those
- if( !m_config->includeSuccessfulResults() && result.isOk() ) {
- if( result.getResultType() != ResultWas::Warning )
- return false;
- printInfoMessages = false;
- }
-
- lazyPrint();
-
- AssertionPrinter printer( stream, _assertionStats, printInfoMessages );
- printer.print();
- stream << std::endl;
- return true;
- }
-
- virtual void sectionStarting( SectionInfo const& _sectionInfo ) {
- m_headerPrinted = false;
- StreamingReporterBase::sectionStarting( _sectionInfo );
- }
- virtual void sectionEnded( SectionStats const& _sectionStats ) {
- if( _sectionStats.missingAssertions ) {
- lazyPrint();
- Colour colour( Colour::ResultError );
- if( m_sectionStack.size() > 1 )
- stream << "\nNo assertions in section";
- else
- stream << "\nNo assertions in test case";
- stream << " '" << _sectionStats.sectionInfo.name << "'\n" << std::endl;
- }
- if( m_headerPrinted ) {
- if( m_config->showDurations() == ShowDurations::Always )
- stream << "Completed in " << _sectionStats.durationInSeconds << "s" << std::endl;
- m_headerPrinted = false;
- }
- else {
- if( m_config->showDurations() == ShowDurations::Always )
- stream << _sectionStats.sectionInfo.name << " completed in " << _sectionStats.durationInSeconds << "s" << std::endl;
- }
- StreamingReporterBase::sectionEnded( _sectionStats );
- }
-
- virtual void testCaseEnded( TestCaseStats const& _testCaseStats ) {
- StreamingReporterBase::testCaseEnded( _testCaseStats );
- m_headerPrinted = false;
- }
- virtual void testGroupEnded( TestGroupStats const& _testGroupStats ) {
- if( currentGroupInfo.used ) {
- printSummaryDivider();
- stream << "Summary for group '" << _testGroupStats.groupInfo.name << "':\n";
- printTotals( _testGroupStats.totals );
- stream << "\n" << std::endl;
- }
- StreamingReporterBase::testGroupEnded( _testGroupStats );
- }
- virtual void testRunEnded( TestRunStats const& _testRunStats ) {
- printTotalsDivider( _testRunStats.totals );
- printTotals( _testRunStats.totals );
- stream << std::endl;
- StreamingReporterBase::testRunEnded( _testRunStats );
- }
-
- private:
-
- class AssertionPrinter {
- void operator= ( AssertionPrinter const& );
- public:
- AssertionPrinter( std::ostream& _stream, AssertionStats const& _stats, bool _printInfoMessages )
- : stream( _stream ),
- stats( _stats ),
- result( _stats.assertionResult ),
- colour( Colour::None ),
- message( result.getMessage() ),
- messages( _stats.infoMessages ),
- printInfoMessages( _printInfoMessages )
- {
- switch( result.getResultType() ) {
- case ResultWas::Ok:
- colour = Colour::Success;
- passOrFail = "PASSED";
- //if( result.hasMessage() )
- if( _stats.infoMessages.size() == 1 )
- messageLabel = "with message";
- if( _stats.infoMessages.size() > 1 )
- messageLabel = "with messages";
- break;
- case ResultWas::ExpressionFailed:
- if( result.isOk() ) {
- colour = Colour::Success;
- passOrFail = "FAILED - but was ok";
- }
- else {
- colour = Colour::Error;
- passOrFail = "FAILED";
- }
- if( _stats.infoMessages.size() == 1 )
- messageLabel = "with message";
- if( _stats.infoMessages.size() > 1 )
- messageLabel = "with messages";
- break;
- case ResultWas::ThrewException:
- colour = Colour::Error;
- passOrFail = "FAILED";
- messageLabel = "due to unexpected exception with message";
- break;
- case ResultWas::FatalErrorCondition:
- colour = Colour::Error;
- passOrFail = "FAILED";
- messageLabel = "due to a fatal error condition";
- break;
- case ResultWas::DidntThrowException:
- colour = Colour::Error;
- passOrFail = "FAILED";
- messageLabel = "because no exception was thrown where one was expected";
- break;
- case ResultWas::Info:
- messageLabel = "info";
- break;
- case ResultWas::Warning:
- messageLabel = "warning";
- break;
- case ResultWas::ExplicitFailure:
- passOrFail = "FAILED";
- colour = Colour::Error;
- if( _stats.infoMessages.size() == 1 )
- messageLabel = "explicitly with message";
- if( _stats.infoMessages.size() > 1 )
- messageLabel = "explicitly with messages";
- break;
- // These cases are here to prevent compiler warnings
- case ResultWas::Unknown:
- case ResultWas::FailureBit:
- case ResultWas::Exception:
- passOrFail = "** internal error **";
- colour = Colour::Error;
- break;
- }
- }
-
- void print() const {
- printSourceInfo();
- if( stats.totals.assertions.total() > 0 ) {
- if( result.isOk() )
- stream << "\n";
- printResultType();
- printOriginalExpression();
- printReconstructedExpression();
- }
- else {
- stream << "\n";
- }
- printMessage();
- }
-
- private:
- void printResultType() const {
- if( !passOrFail.empty() ) {
- Colour colourGuard( colour );
- stream << passOrFail << ":\n";
- }
- }
- void printOriginalExpression() const {
- if( result.hasExpression() ) {
- Colour colourGuard( Colour::OriginalExpression );
- stream << " ";
- stream << result.getExpressionInMacro();
- stream << "\n";
- }
- }
- void printReconstructedExpression() const {
- if( result.hasExpandedExpression() ) {
- stream << "with expansion:\n";
- Colour colourGuard( Colour::ReconstructedExpression );
- stream << Text( result.getExpandedExpression(), TextAttributes().setIndent(2) ) << "\n";
- }
- }
- void printMessage() const {
- if( !messageLabel.empty() )
- stream << messageLabel << ":" << "\n";
- for( std::vector<MessageInfo>::const_iterator it = messages.begin(), itEnd = messages.end();
- it != itEnd;
- ++it ) {
- // If this assertion is a warning ignore any INFO messages
- if( printInfoMessages || it->type != ResultWas::Info )
- stream << Text( it->message, TextAttributes().setIndent(2) ) << "\n";
- }
- }
- void printSourceInfo() const {
- Colour colourGuard( Colour::FileName );
- stream << result.getSourceInfo() << ": ";
- }
-
- std::ostream& stream;
- AssertionStats const& stats;
- AssertionResult const& result;
- Colour::Code colour;
- std::string passOrFail;
- std::string messageLabel;
- std::string message;
- std::vector<MessageInfo> messages;
- bool printInfoMessages;
- };
-
- void lazyPrint() {
-
- if( !currentTestRunInfo.used )
- lazyPrintRunInfo();
- if( !currentGroupInfo.used )
- lazyPrintGroupInfo();
-
- if( !m_headerPrinted ) {
- printTestCaseAndSectionHeader();
- m_headerPrinted = true;
- }
- }
- void lazyPrintRunInfo() {
- stream << "\n" << getLineOfChars<'~'>() << "\n";
- Colour colour( Colour::SecondaryText );
- stream << currentTestRunInfo->name
- << " is a Catch v" << libraryVersion << " host application.\n"
- << "Run with -? for options\n\n";
-
- if( m_config->rngSeed() != 0 )
- stream << "Randomness seeded to: " << m_config->rngSeed() << "\n\n";
-
- currentTestRunInfo.used = true;
- }
- void lazyPrintGroupInfo() {
- if( !currentGroupInfo->name.empty() && currentGroupInfo->groupsCounts > 1 ) {
- printClosedHeader( "Group: " + currentGroupInfo->name );
- currentGroupInfo.used = true;
- }
- }
- void printTestCaseAndSectionHeader() {
- assert( !m_sectionStack.empty() );
- printOpenHeader( currentTestCaseInfo->name );
-
- if( m_sectionStack.size() > 1 ) {
- Colour colourGuard( Colour::Headers );
-
- std::vector<SectionInfo>::const_iterator
- it = m_sectionStack.begin()+1, // Skip first section (test case)
- itEnd = m_sectionStack.end();
- for( ; it != itEnd; ++it )
- printHeaderString( it->name, 2 );
- }
-
- SourceLineInfo lineInfo = m_sectionStack.front().lineInfo;
-
- if( !lineInfo.empty() ){
- stream << getLineOfChars<'-'>() << "\n";
- Colour colourGuard( Colour::FileName );
- stream << lineInfo << "\n";
- }
- stream << getLineOfChars<'.'>() << "\n" << std::endl;
- }
-
- void printClosedHeader( std::string const& _name ) {
- printOpenHeader( _name );
- stream << getLineOfChars<'.'>() << "\n";
- }
- void printOpenHeader( std::string const& _name ) {
- stream << getLineOfChars<'-'>() << "\n";
- {
- Colour colourGuard( Colour::Headers );
- printHeaderString( _name );
- }
- }
-
- // if string has a : in first line will set indent to follow it on
- // subsequent lines
- void printHeaderString( std::string const& _string, std::size_t indent = 0 ) {
- std::size_t i = _string.find( ": " );
- if( i != std::string::npos )
- i+=2;
- else
- i = 0;
- stream << Text( _string, TextAttributes()
- .setIndent( indent+i)
- .setInitialIndent( indent ) ) << "\n";
- }
-
- struct SummaryColumn {
-
- SummaryColumn( std::string const& _label, Colour::Code _colour )
- : label( _label ),
- colour( _colour )
- {}
- SummaryColumn addRow( std::size_t count ) {
- std::ostringstream oss;
- oss << count;
- std::string row = oss.str();
- for( std::vector<std::string>::iterator it = rows.begin(); it != rows.end(); ++it ) {
- while( it->size() < row.size() )
- *it = " " + *it;
- while( it->size() > row.size() )
- row = " " + row;
- }
- rows.push_back( row );
- return *this;
- }
-
- std::string label;
- Colour::Code colour;
- std::vector<std::string> rows;
-
- };
-
- void printTotals( Totals const& totals ) {
- if( totals.testCases.total() == 0 ) {
- stream << Colour( Colour::Warning ) << "No tests ran\n";
- }
- else if( totals.assertions.total() > 0 && totals.assertions.allPassed() ) {
- stream << Colour( Colour::ResultSuccess ) << "All tests passed";
- stream << " ("
- << pluralise( totals.assertions.passed, "assertion" ) << " in "
- << pluralise( totals.testCases.passed, "test case" ) << ")"
- << "\n";
- }
- else {
-
- std::vector<SummaryColumn> columns;
- columns.push_back( SummaryColumn( "", Colour::None )
- .addRow( totals.testCases.total() )
- .addRow( totals.assertions.total() ) );
- columns.push_back( SummaryColumn( "passed", Colour::Success )
- .addRow( totals.testCases.passed )
- .addRow( totals.assertions.passed ) );
- columns.push_back( SummaryColumn( "failed", Colour::ResultError )
- .addRow( totals.testCases.failed )
- .addRow( totals.assertions.failed ) );
- columns.push_back( SummaryColumn( "failed as expected", Colour::ResultExpectedFailure )
- .addRow( totals.testCases.failedButOk )
- .addRow( totals.assertions.failedButOk ) );
-
- printSummaryRow( "test cases", columns, 0 );
- printSummaryRow( "assertions", columns, 1 );
- }
- }
- void printSummaryRow( std::string const& label, std::vector<SummaryColumn> const& cols, std::size_t row ) {
- for( std::vector<SummaryColumn>::const_iterator it = cols.begin(); it != cols.end(); ++it ) {
- std::string value = it->rows[row];
- if( it->label.empty() ) {
- stream << label << ": ";
- if( value != "0" )
- stream << value;
- else
- stream << Colour( Colour::Warning ) << "- none -";
- }
- else if( value != "0" ) {
- stream << Colour( Colour::LightGrey ) << " | ";
- stream << Colour( it->colour )
- << value << " " << it->label;
- }
- }
- stream << "\n";
- }
-
- static std::size_t makeRatio( std::size_t number, std::size_t total ) {
- std::size_t ratio = total > 0 ? CATCH_CONFIG_CONSOLE_WIDTH * number/ total : 0;
- return ( ratio == 0 && number > 0 ) ? 1 : ratio;
- }
- static std::size_t& findMax( std::size_t& i, std::size_t& j, std::size_t& k ) {
- if( i > j && i > k )
- return i;
- else if( j > k )
- return j;
- else
- return k;
- }
-
- void printTotalsDivider( Totals const& totals ) {
- if( totals.testCases.total() > 0 ) {
- std::size_t failedRatio = makeRatio( totals.testCases.failed, totals.testCases.total() );
- std::size_t failedButOkRatio = makeRatio( totals.testCases.failedButOk, totals.testCases.total() );
- std::size_t passedRatio = makeRatio( totals.testCases.passed, totals.testCases.total() );
- while( failedRatio + failedButOkRatio + passedRatio < CATCH_CONFIG_CONSOLE_WIDTH-1 )
- findMax( failedRatio, failedButOkRatio, passedRatio )++;
- while( failedRatio + failedButOkRatio + passedRatio > CATCH_CONFIG_CONSOLE_WIDTH-1 )
- findMax( failedRatio, failedButOkRatio, passedRatio )--;
-
- stream << Colour( Colour::Error ) << std::string( failedRatio, '=' );
- stream << Colour( Colour::ResultExpectedFailure ) << std::string( failedButOkRatio, '=' );
- if( totals.testCases.allPassed() )
- stream << Colour( Colour::ResultSuccess ) << std::string( passedRatio, '=' );
- else
- stream << Colour( Colour::Success ) << std::string( passedRatio, '=' );
- }
- else {
- stream << Colour( Colour::Warning ) << std::string( CATCH_CONFIG_CONSOLE_WIDTH-1, '=' );
- }
- stream << "\n";
- }
- void printSummaryDivider() {
- stream << getLineOfChars<'-'>() << "\n";
- }
-
- private:
- bool m_headerPrinted;
- };
-
- INTERNAL_CATCH_REGISTER_REPORTER( "console", ConsoleReporter )
-
-} // end namespace Catch
-
-// #included from: ../reporters/catch_reporter_compact.hpp
-#define TWOBLUECUBES_CATCH_REPORTER_COMPACT_HPP_INCLUDED
-
-namespace Catch {
-
- struct CompactReporter : StreamingReporterBase {
-
- CompactReporter( ReporterConfig const& _config )
- : StreamingReporterBase( _config )
- {}
-
- virtual ~CompactReporter();
-
- static std::string getDescription() {
- return "Reports test results on a single line, suitable for IDEs";
- }
-
- virtual ReporterPreferences getPreferences() const {
- ReporterPreferences prefs;
- prefs.shouldRedirectStdOut = false;
- return prefs;
- }
-
- virtual void noMatchingTestCases( std::string const& spec ) {
- stream << "No test cases matched '" << spec << "'" << std::endl;
- }
-
- virtual void assertionStarting( AssertionInfo const& ) {
- }
-
- virtual bool assertionEnded( AssertionStats const& _assertionStats ) {
- AssertionResult const& result = _assertionStats.assertionResult;
-
- bool printInfoMessages = true;
-
- // Drop out if result was successful and we're not printing those
- if( !m_config->includeSuccessfulResults() && result.isOk() ) {
- if( result.getResultType() != ResultWas::Warning )
- return false;
- printInfoMessages = false;
- }
-
- AssertionPrinter printer( stream, _assertionStats, printInfoMessages );
- printer.print();
-
- stream << std::endl;
- return true;
- }
-
- virtual void testRunEnded( TestRunStats const& _testRunStats ) {
- printTotals( _testRunStats.totals );
- stream << "\n" << std::endl;
- StreamingReporterBase::testRunEnded( _testRunStats );
- }
-
- private:
- class AssertionPrinter {
- void operator= ( AssertionPrinter const& );
- public:
- AssertionPrinter( std::ostream& _stream, AssertionStats const& _stats, bool _printInfoMessages )
- : stream( _stream )
- , stats( _stats )
- , result( _stats.assertionResult )
- , messages( _stats.infoMessages )
- , itMessage( _stats.infoMessages.begin() )
- , printInfoMessages( _printInfoMessages )
- {}
-
- void print() {
- printSourceInfo();
-
- itMessage = messages.begin();
-
- switch( result.getResultType() ) {
- case ResultWas::Ok:
- printResultType( Colour::ResultSuccess, passedString() );
- printOriginalExpression();
- printReconstructedExpression();
- if ( ! result.hasExpression() )
- printRemainingMessages( Colour::None );
- else
- printRemainingMessages();
- break;
- case ResultWas::ExpressionFailed:
- if( result.isOk() )
- printResultType( Colour::ResultSuccess, failedString() + std::string( " - but was ok" ) );
- else
- printResultType( Colour::Error, failedString() );
- printOriginalExpression();
- printReconstructedExpression();
- printRemainingMessages();
- break;
- case ResultWas::ThrewException:
- printResultType( Colour::Error, failedString() );
- printIssue( "unexpected exception with message:" );
- printMessage();
- printExpressionWas();
- printRemainingMessages();
- break;
- case ResultWas::FatalErrorCondition:
- printResultType( Colour::Error, failedString() );
- printIssue( "fatal error condition with message:" );
- printMessage();
- printExpressionWas();
- printRemainingMessages();
- break;
- case ResultWas::DidntThrowException:
- printResultType( Colour::Error, failedString() );
- printIssue( "expected exception, got none" );
- printExpressionWas();
- printRemainingMessages();
- break;
- case ResultWas::Info:
- printResultType( Colour::None, "info" );
- printMessage();
- printRemainingMessages();
- break;
- case ResultWas::Warning:
- printResultType( Colour::None, "warning" );
- printMessage();
- printRemainingMessages();
- break;
- case ResultWas::ExplicitFailure:
- printResultType( Colour::Error, failedString() );
- printIssue( "explicitly" );
- printRemainingMessages( Colour::None );
- break;
- // These cases are here to prevent compiler warnings
- case ResultWas::Unknown:
- case ResultWas::FailureBit:
- case ResultWas::Exception:
- printResultType( Colour::Error, "** internal error **" );
- break;
- }
- }
-
- private:
- // Colour::LightGrey
-
- static Colour::Code dimColour() { return Colour::FileName; }
-
-#ifdef CATCH_PLATFORM_MAC
- static const char* failedString() { return "FAILED"; }
- static const char* passedString() { return "PASSED"; }
-#else
- static const char* failedString() { return "failed"; }
- static const char* passedString() { return "passed"; }
-#endif
-
- void printSourceInfo() const {
- Colour colourGuard( Colour::FileName );
- stream << result.getSourceInfo() << ":";
- }
-
- void printResultType( Colour::Code colour, std::string passOrFail ) const {
- if( !passOrFail.empty() ) {
- {
- Colour colourGuard( colour );
- stream << " " << passOrFail;
- }
- stream << ":";
- }
- }
-
- void printIssue( std::string issue ) const {
- stream << " " << issue;
- }
-
- void printExpressionWas() {
- if( result.hasExpression() ) {
- stream << ";";
- {
- Colour colour( dimColour() );
- stream << " expression was:";
- }
- printOriginalExpression();
- }
- }
-
- void printOriginalExpression() const {
- if( result.hasExpression() ) {
- stream << " " << result.getExpression();
- }
- }
-
- void printReconstructedExpression() const {
- if( result.hasExpandedExpression() ) {
- {
- Colour colour( dimColour() );
- stream << " for: ";
- }
- stream << result.getExpandedExpression();
- }
- }
-
- void printMessage() {
- if ( itMessage != messages.end() ) {
- stream << " '" << itMessage->message << "'";
- ++itMessage;
- }
- }
-
- void printRemainingMessages( Colour::Code colour = dimColour() ) {
- if ( itMessage == messages.end() )
- return;
-
- // using messages.end() directly yields compilation error:
- std::vector<MessageInfo>::const_iterator itEnd = messages.end();
- const std::size_t N = static_cast<std::size_t>( std::distance( itMessage, itEnd ) );
-
- {
- Colour colourGuard( colour );
- stream << " with " << pluralise( N, "message" ) << ":";
- }
-
- for(; itMessage != itEnd; ) {
- // If this assertion is a warning ignore any INFO messages
- if( printInfoMessages || itMessage->type != ResultWas::Info ) {
- stream << " '" << itMessage->message << "'";
- if ( ++itMessage != itEnd ) {
- Colour colourGuard( dimColour() );
- stream << " and";
- }
- }
- }
- }
-
- private:
- std::ostream& stream;
- AssertionStats const& stats;
- AssertionResult const& result;
- std::vector<MessageInfo> messages;
- std::vector<MessageInfo>::const_iterator itMessage;
- bool printInfoMessages;
- };
-
- // Colour, message variants:
- // - white: No tests ran.
- // - red: Failed [both/all] N test cases, failed [both/all] M assertions.
- // - white: Passed [both/all] N test cases (no assertions).
- // - red: Failed N tests cases, failed M assertions.
- // - green: Passed [both/all] N tests cases with M assertions.
-
- std::string bothOrAll( std::size_t count ) const {
- return count == 1 ? "" : count == 2 ? "both " : "all " ;
- }
-
- void printTotals( const Totals& totals ) const {
- if( totals.testCases.total() == 0 ) {
- stream << "No tests ran.";
- }
- else if( totals.testCases.failed == totals.testCases.total() ) {
- Colour colour( Colour::ResultError );
- const std::string qualify_assertions_failed =
- totals.assertions.failed == totals.assertions.total() ?
- bothOrAll( totals.assertions.failed ) : "";
- stream <<
- "Failed " << bothOrAll( totals.testCases.failed )
- << pluralise( totals.testCases.failed, "test case" ) << ", "
- "failed " << qualify_assertions_failed <<
- pluralise( totals.assertions.failed, "assertion" ) << ".";
- }
- else if( totals.assertions.total() == 0 ) {
- stream <<
- "Passed " << bothOrAll( totals.testCases.total() )
- << pluralise( totals.testCases.total(), "test case" )
- << " (no assertions).";
- }
- else if( totals.assertions.failed ) {
- Colour colour( Colour::ResultError );
- stream <<
- "Failed " << pluralise( totals.testCases.failed, "test case" ) << ", "
- "failed " << pluralise( totals.assertions.failed, "assertion" ) << ".";
- }
- else {
- Colour colour( Colour::ResultSuccess );
- stream <<
- "Passed " << bothOrAll( totals.testCases.passed )
- << pluralise( totals.testCases.passed, "test case" ) <<
- " with " << pluralise( totals.assertions.passed, "assertion" ) << ".";
- }
- }
- };
-
- INTERNAL_CATCH_REGISTER_REPORTER( "compact", CompactReporter )
-
-} // end namespace Catch
-
-namespace Catch {
- NonCopyable::~NonCopyable() {}
- IShared::~IShared() {}
- StreamBufBase::~StreamBufBase() CATCH_NOEXCEPT {}
- IContext::~IContext() {}
- IResultCapture::~IResultCapture() {}
- ITestCase::~ITestCase() {}
- ITestCaseRegistry::~ITestCaseRegistry() {}
- IRegistryHub::~IRegistryHub() {}
- IMutableRegistryHub::~IMutableRegistryHub() {}
- IExceptionTranslator::~IExceptionTranslator() {}
- IExceptionTranslatorRegistry::~IExceptionTranslatorRegistry() {}
- IReporter::~IReporter() {}
- IReporterFactory::~IReporterFactory() {}
- IReporterRegistry::~IReporterRegistry() {}
- IStreamingReporter::~IStreamingReporter() {}
- AssertionStats::~AssertionStats() {}
- SectionStats::~SectionStats() {}
- TestCaseStats::~TestCaseStats() {}
- TestGroupStats::~TestGroupStats() {}
- TestRunStats::~TestRunStats() {}
- CumulativeReporterBase::SectionNode::~SectionNode() {}
- CumulativeReporterBase::~CumulativeReporterBase() {}
-
- StreamingReporterBase::~StreamingReporterBase() {}
- ConsoleReporter::~ConsoleReporter() {}
- CompactReporter::~CompactReporter() {}
- IRunner::~IRunner() {}
- IMutableContext::~IMutableContext() {}
- IConfig::~IConfig() {}
- XmlReporter::~XmlReporter() {}
- JunitReporter::~JunitReporter() {}
- TestRegistry::~TestRegistry() {}
- FreeFunctionTestCase::~FreeFunctionTestCase() {}
- IGeneratorInfo::~IGeneratorInfo() {}
- IGeneratorsForTest::~IGeneratorsForTest() {}
- TestSpec::Pattern::~Pattern() {}
- TestSpec::NamePattern::~NamePattern() {}
- TestSpec::TagPattern::~TagPattern() {}
- TestSpec::ExcludedPattern::~ExcludedPattern() {}
-
- Matchers::Impl::StdString::Equals::~Equals() {}
- Matchers::Impl::StdString::Contains::~Contains() {}
- Matchers::Impl::StdString::StartsWith::~StartsWith() {}
- Matchers::Impl::StdString::EndsWith::~EndsWith() {}
-
- void Config::dummy() {}
-}
-
-#ifdef __clang__
-#pragma clang diagnostic pop
-#endif
-
-#endif
-
-#ifdef CATCH_CONFIG_MAIN
-// #included from: internal/catch_default_main.hpp
-#define TWOBLUECUBES_CATCH_DEFAULT_MAIN_HPP_INCLUDED
-
-#ifndef __OBJC__
-
-// Standard C/C++ main entry point
-int main (int argc, char * const argv[]) {
- return Catch::Session().run( argc, argv );
-}
-
-#else // __OBJC__
-
-// Objective-C entry point
-int main (int argc, char * const argv[]) {
-#if !CATCH_ARC_ENABLED
- NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
-#endif
-
- Catch::registerTestMethods();
- int result = Catch::Session().run( argc, (char* const*)argv );
-
-#if !CATCH_ARC_ENABLED
- [pool drain];
-#endif
-
- return result;
-}
-
-#endif // __OBJC__
-
-#endif
-
-#ifdef CLARA_CONFIG_MAIN_NOT_DEFINED
-# undef CLARA_CONFIG_MAIN
-#endif
-
-//////
-
-// If this config identifier is defined then all CATCH macros are prefixed with CATCH_
-#ifdef CATCH_CONFIG_PREFIX_ALL
-
-#define CATCH_REQUIRE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::Normal, "CATCH_REQUIRE" )
-#define CATCH_REQUIRE_FALSE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, "CATCH_REQUIRE_FALSE" )
-
-#define CATCH_REQUIRE_THROWS( expr ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::Normal, "CATCH_REQUIRE_THROWS" )
-#define CATCH_REQUIRE_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( expr, exceptionType, Catch::ResultDisposition::Normal, "CATCH_REQUIRE_THROWS_AS" )
-#define CATCH_REQUIRE_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( expr, Catch::ResultDisposition::Normal, "CATCH_REQUIRE_NOTHROW" )
-
-#define CATCH_CHECK( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK" )
-#define CATCH_CHECK_FALSE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::FalseTest, "CATCH_CHECK_FALSE" )
-#define CATCH_CHECKED_IF( expr ) INTERNAL_CATCH_IF( expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECKED_IF" )
-#define CATCH_CHECKED_ELSE( expr ) INTERNAL_CATCH_ELSE( expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECKED_ELSE" )
-#define CATCH_CHECK_NOFAIL( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, "CATCH_CHECK_NOFAIL" )
-
-#define CATCH_CHECK_THROWS( expr ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK_THROWS" )
-#define CATCH_CHECK_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( expr, exceptionType, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK_THROWS_AS" )
-#define CATCH_CHECK_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK_NOTHROW" )
-
-#define CHECK_THAT( arg, matcher ) INTERNAL_CHECK_THAT( arg, matcher, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK_THAT" )
-#define CATCH_REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT( arg, matcher, Catch::ResultDisposition::Normal, "CATCH_REQUIRE_THAT" )
-
-#define CATCH_INFO( msg ) INTERNAL_CATCH_INFO( msg, "CATCH_INFO" )
-#define CATCH_WARN( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, "CATCH_WARN", msg )
-#define CATCH_SCOPED_INFO( msg ) INTERNAL_CATCH_INFO( msg, "CATCH_INFO" )
-#define CATCH_CAPTURE( msg ) INTERNAL_CATCH_INFO( #msg " := " << msg, "CATCH_CAPTURE" )
-#define CATCH_SCOPED_CAPTURE( msg ) INTERNAL_CATCH_INFO( #msg " := " << msg, "CATCH_CAPTURE" )
-
-#ifdef CATCH_CONFIG_VARIADIC_MACROS
- #define CATCH_TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE( __VA_ARGS__ )
- #define CATCH_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, __VA_ARGS__ )
- #define CATCH_METHOD_AS_TEST_CASE( method, ... ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, __VA_ARGS__ )
- #define CATCH_SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ )
- #define CATCH_FAIL( ... ) INTERNAL_CATCH_MSG( Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, "CATCH_FAIL", __VA_ARGS__ )
- #define CATCH_SUCCEED( ... ) INTERNAL_CATCH_MSG( Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, "CATCH_SUCCEED", __VA_ARGS__ )
-#else
- #define CATCH_TEST_CASE( name, description ) INTERNAL_CATCH_TESTCASE( name, description )
- #define CATCH_TEST_CASE_METHOD( className, name, description ) INTERNAL_CATCH_TEST_CASE_METHOD( className, name, description )
- #define CATCH_METHOD_AS_TEST_CASE( method, name, description ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, name, description )
- #define CATCH_SECTION( name, description ) INTERNAL_CATCH_SECTION( name, description )
- #define CATCH_FAIL( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, "CATCH_FAIL", msg )
- #define CATCH_SUCCEED( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, "CATCH_SUCCEED", msg )
-#endif
-#define CATCH_ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE( "", "" )
-
-#define CATCH_REGISTER_REPORTER( name, reporterType ) INTERNAL_CATCH_REGISTER_REPORTER( name, reporterType )
-#define CATCH_REGISTER_LEGACY_REPORTER( name, reporterType ) INTERNAL_CATCH_REGISTER_LEGACY_REPORTER( name, reporterType )
-
-#define CATCH_GENERATE( expr) INTERNAL_CATCH_GENERATE( expr )
-
-// "BDD-style" convenience wrappers
-#ifdef CATCH_CONFIG_VARIADIC_MACROS
-#define CATCH_SCENARIO( ... ) CATCH_TEST_CASE( "Scenario: " __VA_ARGS__ )
-#define CATCH_SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " __VA_ARGS__ )
-#else
-#define CATCH_SCENARIO( name, tags ) CATCH_TEST_CASE( "Scenario: " name, tags )
-#define CATCH_SCENARIO_METHOD( className, name, tags ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " name, tags )
-#endif
-#define CATCH_GIVEN( desc ) CATCH_SECTION( "Given: " desc, "" )
-#define CATCH_WHEN( desc ) CATCH_SECTION( " When: " desc, "" )
-#define CATCH_AND_WHEN( desc ) CATCH_SECTION( " And: " desc, "" )
-#define CATCH_THEN( desc ) CATCH_SECTION( " Then: " desc, "" )
-#define CATCH_AND_THEN( desc ) CATCH_SECTION( " And: " desc, "" )
-
-// If CATCH_CONFIG_PREFIX_ALL is not defined then the CATCH_ prefix is not required
-#else
-
-#define REQUIRE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::Normal, "REQUIRE" )
-#define REQUIRE_FALSE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, "REQUIRE_FALSE" )
-
-#define REQUIRE_THROWS( expr ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::Normal, "REQUIRE_THROWS" )
-#define REQUIRE_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( expr, exceptionType, Catch::ResultDisposition::Normal, "REQUIRE_THROWS_AS" )
-#define REQUIRE_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( expr, Catch::ResultDisposition::Normal, "REQUIRE_NOTHROW" )
-
-#define CHECK( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure, "CHECK" )
-#define CHECK_FALSE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::FalseTest, "CHECK_FALSE" )
-#define CHECKED_IF( expr ) INTERNAL_CATCH_IF( expr, Catch::ResultDisposition::ContinueOnFailure, "CHECKED_IF" )
-#define CHECKED_ELSE( expr ) INTERNAL_CATCH_ELSE( expr, Catch::ResultDisposition::ContinueOnFailure, "CHECKED_ELSE" )
-#define CHECK_NOFAIL( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, "CHECK_NOFAIL" )
-
-#define CHECK_THROWS( expr ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::ContinueOnFailure, "CHECK_THROWS" )
-#define CHECK_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( expr, exceptionType, Catch::ResultDisposition::ContinueOnFailure, "CHECK_THROWS_AS" )
-#define CHECK_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( expr, Catch::ResultDisposition::ContinueOnFailure, "CHECK_NOTHROW" )
-
-#define CHECK_THAT( arg, matcher ) INTERNAL_CHECK_THAT( arg, matcher, Catch::ResultDisposition::ContinueOnFailure, "CHECK_THAT" )
-#define REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT( arg, matcher, Catch::ResultDisposition::Normal, "REQUIRE_THAT" )
-
-#define INFO( msg ) INTERNAL_CATCH_INFO( msg, "INFO" )
-#define WARN( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, "WARN", msg )
-#define SCOPED_INFO( msg ) INTERNAL_CATCH_INFO( msg, "INFO" )
-#define CAPTURE( msg ) INTERNAL_CATCH_INFO( #msg " := " << msg, "CAPTURE" )
-#define SCOPED_CAPTURE( msg ) INTERNAL_CATCH_INFO( #msg " := " << msg, "CAPTURE" )
-
-#ifdef CATCH_CONFIG_VARIADIC_MACROS
- #define TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE( __VA_ARGS__ )
- #define TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, __VA_ARGS__ )
- #define METHOD_AS_TEST_CASE( method, ... ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, __VA_ARGS__ )
- #define SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ )
- #define FAIL( ... ) INTERNAL_CATCH_MSG( Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, "FAIL", __VA_ARGS__ )
- #define SUCCEED( ... ) INTERNAL_CATCH_MSG( Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, "SUCCEED", __VA_ARGS__ )
-#else
- #define TEST_CASE( name, description ) INTERNAL_CATCH_TESTCASE( name, description )
- #define TEST_CASE_METHOD( className, name, description ) INTERNAL_CATCH_TEST_CASE_METHOD( className, name, description )
- #define METHOD_AS_TEST_CASE( method, name, description ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, name, description )
- #define SECTION( name, description ) INTERNAL_CATCH_SECTION( name, description )
- #define FAIL( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, "FAIL", msg )
- #define SUCCEED( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, "SUCCEED", msg )
-#endif
-#define ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE( "", "" )
-
-#define REGISTER_REPORTER( name, reporterType ) INTERNAL_CATCH_REGISTER_REPORTER( name, reporterType )
-#define REGISTER_LEGACY_REPORTER( name, reporterType ) INTERNAL_CATCH_REGISTER_LEGACY_REPORTER( name, reporterType )
-
-#define GENERATE( expr) INTERNAL_CATCH_GENERATE( expr )
-
-#endif
-
-#define CATCH_TRANSLATE_EXCEPTION( signature ) INTERNAL_CATCH_TRANSLATE_EXCEPTION( signature )
-
-// "BDD-style" convenience wrappers
-#ifdef CATCH_CONFIG_VARIADIC_MACROS
-#define SCENARIO( ... ) TEST_CASE( "Scenario: " __VA_ARGS__ )
-#define SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " __VA_ARGS__ )
-#else
-#define SCENARIO( name, tags ) TEST_CASE( "Scenario: " name, tags )
-#define SCENARIO_METHOD( className, name, tags ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " name, tags )
-#endif
-#define GIVEN( desc ) SECTION( " Given: " desc, "" )
-#define WHEN( desc ) SECTION( " When: " desc, "" )
-#define AND_WHEN( desc ) SECTION( "And when: " desc, "" )
-#define THEN( desc ) SECTION( " Then: " desc, "" )
-#define AND_THEN( desc ) SECTION( " And: " desc, "" )
-
-using Catch::Detail::Approx;
-
-// #included from: internal/catch_reenable_warnings.h
-
-#define TWOBLUECUBES_CATCH_REENABLE_WARNINGS_H_INCLUDED
-
-#ifdef __clang__
-# ifdef __ICC // icpc defines the __clang__ macro
-# pragma warning(pop)
-# else
-# pragma clang diagnostic pop
-# endif
-#elif defined __GNUC__
-# pragma GCC diagnostic pop
-#endif
-
-#endif // TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED
-
#include "../src/core/const.h"
#include "../src/core/conf.h"
-#include "catch.hpp"
+#include "catch/single_include/catch.hpp"
using std::string;
+using namespace giada::m;
TEST_CASE("Test Conf class")
{
- Conf conf;
-
+ conf::init();
+
SECTION("test write")
{
- conf.header = "GIADACONFTEST";
- conf.logMode = 1;
- conf.soundSystem = 2;
- conf.soundDeviceOut = 3;
- conf.soundDeviceIn = 4;
- conf.channelsOut = 5;
- conf.channelsIn = 6;
- conf.samplerate = 7;
- conf.buffersize = 8;
- conf.delayComp = 9;
- conf.limitOutput = true;
- conf.rsmpQuality = 10;
- conf.midiSystem = 11;
- conf.midiPortOut = 12;
- conf.midiPortIn = 13;
- conf.noNoteOff = false;
- conf.midiMapPath = "path/to/midi/map";
- conf.lastFileMap = "path/to/last/midi/map";
- conf.midiSync = 14;
- conf.midiTCfps = 15.1f;
- conf.midiInRewind = 16;
- conf.midiInStartStop = 17;
- conf.midiInActionRec = 18;
- conf.midiInInputRec = 19;
- conf.midiInMetronome = 20;
- conf.midiInVolumeIn = 21;
- conf.midiInVolumeOut = 22;
- conf.midiInBeatDouble = 23;
- conf.midiInBeatHalf = 24;
- conf.recsStopOnChanHalt = true;
- conf.chansStopOnSeqHalt = false;
- conf.treatRecsAsLoops = true;
- conf.resizeRecordings = false;
- conf.pluginPath = "path/to/plugins";
- conf.patchPath = "path/to/patches";
- conf.samplePath = "path/to/samples";
- conf.mainWindowX = 0;
- conf.mainWindowY = 0;
- conf.mainWindowW = 800;
- conf.mainWindowH = 600;
- conf.browserX = 0;
- conf.browserY = 0;
- conf.browserW = 800;
- conf.browserH = 600;
- conf.actionEditorX = 0;
- conf.actionEditorY = 0;
- conf.actionEditorW = 800;
- conf.actionEditorH = 600;
- conf.actionEditorZoom = 1;
- conf.actionEditorGridVal = 10;
- conf.actionEditorGridOn = 1;
- conf.sampleEditorX = 0;
- conf.sampleEditorY = 0;
- conf.sampleEditorW = 800;
- conf.sampleEditorH = 600;
- conf.sampleEditorGridVal = 4;
- conf.sampleEditorGridOn = 0;
- conf.pianoRollY = 0;
- conf.pianoRollH = 900;
- conf.pluginListX = 0;
- conf.pluginListY = 50;
- conf.configX = 20;
- conf.configY = 20;
- conf.bpmX = 30;
- conf.bpmY = 36;
- conf.beatsX = 1;
- conf.beatsY = 1;
- conf.aboutX = 2;
- conf.aboutY = 2;
+ conf::header = "GIADACONFTEST";
+ conf::logMode = 1;
+ conf::soundSystem = 2;
+ conf::soundDeviceOut = 3;
+ conf::soundDeviceIn = 4;
+ conf::channelsOut = 5;
+ conf::channelsIn = 6;
+ conf::samplerate = 7;
+ conf::buffersize = 8;
+ conf::delayComp = 9;
+ conf::limitOutput = true;
+ conf::rsmpQuality = 10;
+ conf::midiSystem = 11;
+ conf::midiPortOut = 12;
+ conf::midiPortIn = 13;
+ conf::noNoteOff = false;
+ conf::midiMapPath = "path/to/midi/map";
+ conf::lastFileMap = "path/to/last/midi/map";
+ conf::midiSync = 14;
+ conf::midiTCfps = 15.1f;
+ conf::midiInRewind = 16;
+ conf::midiInStartStop = 17;
+ conf::midiInActionRec = 18;
+ conf::midiInInputRec = 19;
+ conf::midiInMetronome = 20;
+ conf::midiInVolumeIn = 21;
+ conf::midiInVolumeOut = 22;
+ conf::midiInBeatDouble = 23;
+ conf::midiInBeatHalf = 24;
+ conf::recsStopOnChanHalt = true;
+ conf::chansStopOnSeqHalt = false;
+ conf::treatRecsAsLoops = true;
+ conf::resizeRecordings = false;
+ conf::pluginPath = "path/to/plugins";
+ conf::patchPath = "path/to/patches";
+ conf::samplePath = "path/to/samples";
+ conf::mainWindowX = 0;
+ conf::mainWindowY = 0;
+ conf::mainWindowW = 800;
+ conf::mainWindowH = 600;
+ conf::browserX = 0;
+ conf::browserY = 0;
+ conf::browserW = 800;
+ conf::browserH = 600;
+ conf::actionEditorX = 0;
+ conf::actionEditorY = 0;
+ conf::actionEditorW = 800;
+ conf::actionEditorH = 600;
+ conf::actionEditorZoom = 1;
+ conf::actionEditorGridVal = 10;
+ conf::actionEditorGridOn = 1;
+ conf::sampleEditorX = 0;
+ conf::sampleEditorY = 0;
+ conf::sampleEditorW = 800;
+ conf::sampleEditorH = 600;
+ conf::sampleEditorGridVal = 4;
+ conf::sampleEditorGridOn = 0;
+ conf::pianoRollY = 0;
+ conf::pianoRollH = 900;
+ conf::pluginListX = 0;
+ conf::pluginListY = 50;
+ conf::configX = 20;
+ conf::configY = 20;
+ conf::bpmX = 30;
+ conf::bpmY = 36;
+ conf::beatsX = 1;
+ conf::beatsY = 1;
+ conf::aboutX = 2;
+ conf::aboutY = 2;
- REQUIRE(conf.write() == 1);
+ REQUIRE(conf::write() == 1);
}
SECTION("test read")
{
- REQUIRE(conf.read() == 1);
- REQUIRE(conf.header == "GIADACONFTEST");
- REQUIRE(conf.logMode == 1);
- REQUIRE(conf.soundSystem == 2);
- REQUIRE(conf.soundDeviceOut == 3);
- REQUIRE(conf.soundDeviceIn == 4);
- REQUIRE(conf.channelsOut == 5);
- REQUIRE(conf.channelsIn == 6);
- REQUIRE(conf.samplerate == 44100); // sanitized
- REQUIRE(conf.buffersize == 8);
- REQUIRE(conf.delayComp == 9);
- REQUIRE(conf.limitOutput == true);
- REQUIRE(conf.rsmpQuality == 0); // sanitized
- REQUIRE(conf.midiSystem == 11);
- REQUIRE(conf.midiPortOut == 12);
- REQUIRE(conf.midiPortIn == 13);
- REQUIRE(conf.noNoteOff == false);
- REQUIRE(conf.midiMapPath == "path/to/midi/map");
- REQUIRE(conf.lastFileMap == "path/to/last/midi/map");
- REQUIRE(conf.midiSync == 14);
- REQUIRE(conf.midiTCfps == Approx(15.1));
- REQUIRE(conf.midiInRewind == 16);
- REQUIRE(conf.midiInStartStop == 17);
- REQUIRE(conf.midiInActionRec == 18);
- REQUIRE(conf.midiInInputRec == 19);
- REQUIRE(conf.midiInMetronome == 20);
- REQUIRE(conf.midiInVolumeIn == 21);
- REQUIRE(conf.midiInVolumeOut == 22);
- REQUIRE(conf.midiInBeatDouble == 23);
- REQUIRE(conf.midiInBeatHalf == 24);
- REQUIRE(conf.recsStopOnChanHalt == true);
- REQUIRE(conf.chansStopOnSeqHalt == false);
- REQUIRE(conf.treatRecsAsLoops == true);
- REQUIRE(conf.resizeRecordings == false);
- REQUIRE(conf.pluginPath == "path/to/plugins");
- REQUIRE(conf.patchPath == "path/to/patches");
- REQUIRE(conf.samplePath == "path/to/samples");
- REQUIRE(conf.mainWindowX == 0);
- REQUIRE(conf.mainWindowY == 0);
- REQUIRE(conf.mainWindowW == 800);
- REQUIRE(conf.mainWindowH == 600);
- REQUIRE(conf.browserX == 0);
- REQUIRE(conf.browserY == 0);
- REQUIRE(conf.browserW == 800);
- REQUIRE(conf.browserH == 600);
- REQUIRE(conf.actionEditorX == 0);
- REQUIRE(conf.actionEditorY == 0);
- REQUIRE(conf.actionEditorW == 800);
- REQUIRE(conf.actionEditorH == 600);
- REQUIRE(conf.actionEditorZoom == 100); // sanitized
- REQUIRE(conf.actionEditorGridVal == 10);
- REQUIRE(conf.actionEditorGridOn == 1);
- REQUIRE(conf.sampleEditorX == 0);
- REQUIRE(conf.sampleEditorY == 0);
- REQUIRE(conf.sampleEditorW == 800);
- REQUIRE(conf.sampleEditorH == 600);
- REQUIRE(conf.sampleEditorGridVal == 4);
- REQUIRE(conf.sampleEditorGridOn == 0);
- REQUIRE(conf.pianoRollY == 0);
- REQUIRE(conf.pianoRollH == 900);
- REQUIRE(conf.pluginListX == 0);
- REQUIRE(conf.pluginListY == 50);
- REQUIRE(conf.configX == 20);
- REQUIRE(conf.configY == 20);
- REQUIRE(conf.bpmX == 30);
- REQUIRE(conf.bpmY == 36);
- REQUIRE(conf.beatsX == 1);
- REQUIRE(conf.beatsY == 1);
- REQUIRE(conf.aboutX == 2);
- REQUIRE(conf.aboutY == 2);
+ REQUIRE(conf::read() == 1);
+ REQUIRE(conf::header == "GIADACONFTEST");
+ REQUIRE(conf::logMode == 1);
+ REQUIRE(conf::soundSystem == 2);
+ REQUIRE(conf::soundDeviceOut == 3);
+ REQUIRE(conf::soundDeviceIn == 4);
+ REQUIRE(conf::channelsOut == 5);
+ REQUIRE(conf::channelsIn == 6);
+ REQUIRE(conf::samplerate == 44100); // sanitized
+ REQUIRE(conf::buffersize == 8);
+ REQUIRE(conf::delayComp == 9);
+ REQUIRE(conf::limitOutput == true);
+ REQUIRE(conf::rsmpQuality == 0); // sanitized
+ REQUIRE(conf::midiSystem == 11);
+ REQUIRE(conf::midiPortOut == 12);
+ REQUIRE(conf::midiPortIn == 13);
+ REQUIRE(conf::noNoteOff == false);
+ REQUIRE(conf::midiMapPath == "path/to/midi/map");
+ REQUIRE(conf::lastFileMap == "path/to/last/midi/map");
+ REQUIRE(conf::midiSync == 14);
+ REQUIRE(conf::midiTCfps == Approx(15.1));
+ REQUIRE(conf::midiInRewind == 16);
+ REQUIRE(conf::midiInStartStop == 17);
+ REQUIRE(conf::midiInActionRec == 18);
+ REQUIRE(conf::midiInInputRec == 19);
+ REQUIRE(conf::midiInMetronome == 20);
+ REQUIRE(conf::midiInVolumeIn == 21);
+ REQUIRE(conf::midiInVolumeOut == 22);
+ REQUIRE(conf::midiInBeatDouble == 23);
+ REQUIRE(conf::midiInBeatHalf == 24);
+ REQUIRE(conf::recsStopOnChanHalt == true);
+ REQUIRE(conf::chansStopOnSeqHalt == false);
+ REQUIRE(conf::treatRecsAsLoops == true);
+ REQUIRE(conf::resizeRecordings == false);
+ REQUIRE(conf::pluginPath == "path/to/plugins");
+ REQUIRE(conf::patchPath == "path/to/patches");
+ REQUIRE(conf::samplePath == "path/to/samples");
+ REQUIRE(conf::mainWindowX == 0);
+ REQUIRE(conf::mainWindowY == 0);
+ REQUIRE(conf::mainWindowW == 800);
+ REQUIRE(conf::mainWindowH == 600);
+ REQUIRE(conf::browserX == 0);
+ REQUIRE(conf::browserY == 0);
+ REQUIRE(conf::browserW == 800);
+ REQUIRE(conf::browserH == 600);
+ REQUIRE(conf::actionEditorX == 0);
+ REQUIRE(conf::actionEditorY == 0);
+ REQUIRE(conf::actionEditorW == 800);
+ REQUIRE(conf::actionEditorH == 600);
+ REQUIRE(conf::actionEditorZoom == 100); // sanitized
+ REQUIRE(conf::actionEditorGridVal == 10);
+ REQUIRE(conf::actionEditorGridOn == 1);
+ REQUIRE(conf::sampleEditorX == 0);
+ REQUIRE(conf::sampleEditorY == 0);
+ REQUIRE(conf::sampleEditorW == 800);
+ REQUIRE(conf::sampleEditorH == 600);
+ REQUIRE(conf::sampleEditorGridVal == 4);
+ REQUIRE(conf::sampleEditorGridOn == 0);
+ REQUIRE(conf::pianoRollY == 0);
+ REQUIRE(conf::pianoRollH == 900);
+ REQUIRE(conf::pluginListX == 0);
+ REQUIRE(conf::pluginListY == 50);
+ REQUIRE(conf::configX == 20);
+ REQUIRE(conf::configY == 20);
+ REQUIRE(conf::bpmX == 30);
+ REQUIRE(conf::bpmY == 36);
+ REQUIRE(conf::beatsX == 1);
+ REQUIRE(conf::beatsY == 1);
+ REQUIRE(conf::aboutX == 2);
+ REQUIRE(conf::aboutY == 2);
}
}
-#define CATCH_CONFIG_MAIN
-#include "catch.hpp"
+#define CATCH_CONFIG_MAIN
+#define CATCH_CONFIG_FAST_COMPILE
+#include "catch/single_include/catch.hpp"
#include "../src/core/const.h"
#include "../src/core/midiMapConf.h"
-#include "catch.hpp"
+#include "catch/single_include/catch.hpp"
using std::string;
+using namespace giada::m;
-TEST_CASE("Test MidiMapConf class")
+TEST_CASE("Test MidiMapConf")
{
- MidiMapConf midimap;
-
SECTION("test default values")
{
- midimap.setDefault();
- REQUIRE(midimap.brand == "");
- REQUIRE(midimap.device == "");
- REQUIRE(midimap.muteOn.channel == 0);
- REQUIRE(midimap.muteOn.valueStr == "");
- REQUIRE(midimap.muteOn.offset == -1);
- REQUIRE(midimap.muteOn.value == 0);
- REQUIRE(midimap.muteOff.channel == 0);
- REQUIRE(midimap.muteOff.valueStr == "");
- REQUIRE(midimap.muteOff.offset == -1);
- REQUIRE(midimap.muteOff.value == 0);
- REQUIRE(midimap.soloOn.channel == 0);
- REQUIRE(midimap.soloOn.valueStr == "");
- REQUIRE(midimap.soloOn.offset == -1);
- REQUIRE(midimap.soloOn.value == 0);
- REQUIRE(midimap.soloOff.channel == 0);
- REQUIRE(midimap.soloOff.valueStr == "");
- REQUIRE(midimap.soloOff.offset == -1);
- REQUIRE(midimap.soloOff.value == 0);
- REQUIRE(midimap.waiting.channel == 0);
- REQUIRE(midimap.waiting.valueStr == "");
- REQUIRE(midimap.waiting.offset == -1);
- REQUIRE(midimap.waiting.value == 0);
- REQUIRE(midimap.playing.channel == 0);
- REQUIRE(midimap.playing.valueStr == "");
- REQUIRE(midimap.playing.offset == -1);
- REQUIRE(midimap.playing.value == 0);
- REQUIRE(midimap.stopping.channel == 0);
- REQUIRE(midimap.stopping.valueStr == "");
- REQUIRE(midimap.stopping.offset == -1);
- REQUIRE(midimap.stopping.value == 0);
- REQUIRE(midimap.stopped.channel == 0);
- REQUIRE(midimap.stopped.valueStr == "");
- REQUIRE(midimap.stopped.offset == -1);
- REQUIRE(midimap.stopped.value == 0);
+ midimap::setDefault();
+ REQUIRE(midimap::brand == "");
+ REQUIRE(midimap::device == "");
+ REQUIRE(midimap::muteOn.channel == 0);
+ REQUIRE(midimap::muteOn.valueStr == "");
+ REQUIRE(midimap::muteOn.offset == -1);
+ REQUIRE(midimap::muteOn.value == 0);
+ REQUIRE(midimap::muteOff.channel == 0);
+ REQUIRE(midimap::muteOff.valueStr == "");
+ REQUIRE(midimap::muteOff.offset == -1);
+ REQUIRE(midimap::muteOff.value == 0);
+ REQUIRE(midimap::soloOn.channel == 0);
+ REQUIRE(midimap::soloOn.valueStr == "");
+ REQUIRE(midimap::soloOn.offset == -1);
+ REQUIRE(midimap::soloOn.value == 0);
+ REQUIRE(midimap::soloOff.channel == 0);
+ REQUIRE(midimap::soloOff.valueStr == "");
+ REQUIRE(midimap::soloOff.offset == -1);
+ REQUIRE(midimap::soloOff.value == 0);
+ REQUIRE(midimap::waiting.channel == 0);
+ REQUIRE(midimap::waiting.valueStr == "");
+ REQUIRE(midimap::waiting.offset == -1);
+ REQUIRE(midimap::waiting.value == 0);
+ REQUIRE(midimap::playing.channel == 0);
+ REQUIRE(midimap::playing.valueStr == "");
+ REQUIRE(midimap::playing.offset == -1);
+ REQUIRE(midimap::playing.value == 0);
+ REQUIRE(midimap::stopping.channel == 0);
+ REQUIRE(midimap::stopping.valueStr == "");
+ REQUIRE(midimap::stopping.offset == -1);
+ REQUIRE(midimap::stopping.value == 0);
+ REQUIRE(midimap::stopped.channel == 0);
+ REQUIRE(midimap::stopped.valueStr == "");
+ REQUIRE(midimap::stopped.offset == -1);
+ REQUIRE(midimap::stopped.value == 0);
}
#ifdef RUN_TESTS_WITH_LOCAL_FILES
SECTION("test read")
{
- midimap.init();
- midimap.setDefault();
+ midimap::init();
+ midimap::setDefault();
/* expect more than 2 midifiles */
- REQUIRE(midimap.maps.size() >= 2);
+ REQUIRE(midimap::maps.size() >= 2);
/* try with deprecated mode */
- int res = midimap.read("akai-lpd8.giadamap");
+ int res = midimap::read("akai-lpd8.giadamap");
if (res != MIDIMAP_READ_OK)
- res = midimap.readMap_DEPR_("akai-lpd8.giadamap");
+ res = midimap::readMap_DEPR_("akai-lpd8.giadamap");
REQUIRE(res == MIDIMAP_READ_OK);
- REQUIRE(midimap.brand == "AKAI");
- REQUIRE(midimap.device == "LPD8");
+ REQUIRE(midimap::brand == "AKAI");
+ REQUIRE(midimap::device == "LPD8");
- REQUIRE(midimap.initCommands.size() == 2);
- REQUIRE(midimap.initCommands[0].channel == 0);
- REQUIRE(midimap.initCommands[0].value == 0xB0000000);
- REQUIRE(midimap.initCommands[1].channel == 0);
- REQUIRE(midimap.initCommands[1].value == 0xB0002800);
+ REQUIRE(midimap::initCommands.size() == 2);
+ REQUIRE(midimap::initCommands[0].channel == 0);
+ REQUIRE(midimap::initCommands[0].value == 0xB0000000);
+ REQUIRE(midimap::initCommands[1].channel == 0);
+ REQUIRE(midimap::initCommands[1].value == 0xB0002800);
/* TODO - can't check 'valueStr' until deprecated methods are alive */
- REQUIRE(midimap.muteOn.channel == 0);
- //REQUIRE(midimap.muteOn.valueStr == "90nn3F00");
- REQUIRE(midimap.muteOn.offset == 16);
- REQUIRE(midimap.muteOn.value == 0x90003F00);
-
- REQUIRE(midimap.muteOff.channel == 0);
- //REQUIRE(midimap.muteOff.valueStr == "90nn0C00");
- REQUIRE(midimap.muteOff.offset == 16);
- REQUIRE(midimap.muteOff.value == 0x90000C00);
-
- REQUIRE(midimap.soloOn.channel == 0);
- //REQUIRE(midimap.soloOn.valueStr == "90nn0F00");
- REQUIRE(midimap.soloOn.offset == 16);
- REQUIRE(midimap.soloOn.value == 0x90000F00);
-
- REQUIRE(midimap.soloOff.channel == 0);
- //REQUIRE(midimap.soloOff.valueStr == "90nn0C00");
- REQUIRE(midimap.soloOff.offset == 16);
- REQUIRE(midimap.soloOff.value == 0x90000C00);
-
- REQUIRE(midimap.waiting.channel == 0);
- //REQUIRE(midimap.waiting.valueStr == "90nn7f00");
- REQUIRE(midimap.waiting.offset == 16);
- REQUIRE(midimap.waiting.value == 0x90007f00);
-
- REQUIRE(midimap.playing.channel == 0);
- //REQUIRE(midimap.playing.valueStr == "90nn7f00");
- REQUIRE(midimap.playing.offset == 16);
- REQUIRE(midimap.playing.value == 0x90007f00);
-
- REQUIRE(midimap.stopping.channel == 0);
- //REQUIRE(midimap.stopping.valueStr == "90nn7f00");
- REQUIRE(midimap.stopping.offset == 16);
- REQUIRE(midimap.stopping.value == 0x90007f00);
-
- REQUIRE(midimap.stopped.channel == 0);
- //REQUIRE(midimap.stopped.valueStr == "80nn7f00");
- REQUIRE(midimap.stopped.offset == 16);
- REQUIRE(midimap.stopped.value == 0x80007f00);
+ REQUIRE(midimap::muteOn.channel == 0);
+ //REQUIRE(midimap::muteOn.valueStr == "90nn3F00");
+ REQUIRE(midimap::muteOn.offset == 16);
+ REQUIRE(midimap::muteOn.value == 0x90003F00);
+
+ REQUIRE(midimap::muteOff.channel == 0);
+ //REQUIRE(midimap::muteOff.valueStr == "90nn0C00");
+ REQUIRE(midimap::muteOff.offset == 16);
+ REQUIRE(midimap::muteOff.value == 0x90000C00);
+
+ REQUIRE(midimap::soloOn.channel == 0);
+ //REQUIRE(midimap::soloOn.valueStr == "90nn0F00");
+ REQUIRE(midimap::soloOn.offset == 16);
+ REQUIRE(midimap::soloOn.value == 0x90000F00);
+
+ REQUIRE(midimap::soloOff.channel == 0);
+ //REQUIRE(midimap::soloOff.valueStr == "90nn0C00");
+ REQUIRE(midimap::soloOff.offset == 16);
+ REQUIRE(midimap::soloOff.value == 0x90000C00);
+
+ REQUIRE(midimap::waiting.channel == 0);
+ //REQUIRE(midimap::waiting.valueStr == "90nn7f00");
+ REQUIRE(midimap::waiting.offset == 16);
+ REQUIRE(midimap::waiting.value == 0x90007f00);
+
+ REQUIRE(midimap::playing.channel == 0);
+ //REQUIRE(midimap::playing.valueStr == "90nn7f00");
+ REQUIRE(midimap::playing.offset == 16);
+ REQUIRE(midimap::playing.value == 0x90007f00);
+
+ REQUIRE(midimap::stopping.channel == 0);
+ //REQUIRE(midimap::stopping.valueStr == "90nn7f00");
+ REQUIRE(midimap::stopping.offset == 16);
+ REQUIRE(midimap::stopping.value == 0x90007f00);
+
+ REQUIRE(midimap::stopped.channel == 0);
+ //REQUIRE(midimap::stopped.valueStr == "80nn7f00");
+ REQUIRE(midimap::stopped.offset == 16);
+ REQUIRE(midimap::stopped.value == 0x80007f00);
}
#endif // #ifdef RUN_TESTS_WITH_LOCAL_FILES
#include "../src/core/patch.h"
#include "../src/core/const.h"
-#include "catch.hpp"
+#include "catch/single_include/catch.hpp"
using std::string;
using std::vector;
+using namespace giada::m;
TEST_CASE("Test Patch class")
{
- Patch patch;
string filename = "./test-patch.json";
SECTION("test write")
{
- Patch::action_t action1;
- Patch::action_t action2;
- Patch::channel_t channel1;
- Patch::channel_t channel2;
- Patch::column_t column;
+ patch::action_t action1;
+ patch::action_t action2;
+ patch::channel_t channel1;
+ patch::channel_t channel2;
+ patch::column_t column;
#ifdef WITH_VST
- Patch::plugin_t plugin1;
- Patch::plugin_t plugin2;
- Patch::plugin_t plugin3;
+ patch::plugin_t plugin1;
+ patch::plugin_t plugin2;
+ patch::plugin_t plugin3;
#endif
action1.type = 0;
channel1.mute_s = 0;
channel1.solo = 0;
channel1.volume = 1.0f;
- channel1.panLeft = 0.5f;
- channel1.panRight = 0.5f;
+ channel1.pan = 0.5f;
channel1.midiIn = true;
channel1.midiInKeyPress = UINT32_MAX; // check maximum value
channel1.midiInKeyRel = 1;
channel1.midiInPitch = 0;
channel1.midiOut = 0;
channel1.midiOutChan = 5;
- patch.channels.push_back(channel1);
+ patch::channels.push_back(channel1);
column.index = 0;
column.width = 500;
- patch.columns.push_back(column);
-
- patch.header = "GPTCH";
- patch.version = "1.0";
- patch.versionMajor = 6;
- patch.versionMinor = 6;
- patch.versionPatch = 6;
- patch.name = "test patch";
- patch.bpm = 100.0f;
- patch.bars = 4;
- patch.beats = 23;
- patch.quantize = 1;
- patch.masterVolIn = 1.0f;
- patch.masterVolOut = 0.7f;
- patch.metronome = 0;
- patch.lastTakeId = 0;
- patch.samplerate = 44100;
+ patch::columns.push_back(column);
+
+ patch::header = "GPTCH";
+ patch::version = "1.0";
+ patch::versionMajor = 6;
+ patch::versionMinor = 6;
+ patch::versionPatch = 6;
+ patch::name = "test patch";
+ patch::bpm = 100.0f;
+ patch::bars = 4;
+ patch::beats = 23;
+ patch::quantize = 1;
+ patch::masterVolIn = 1.0f;
+ patch::masterVolOut = 0.7f;
+ patch::metronome = 0;
+ patch::lastTakeId = 0;
+ patch::samplerate = 44100;
#ifdef WITH_VST
- patch.masterInPlugins.push_back(plugin1);
- patch.masterOutPlugins.push_back(plugin2);
+ patch::masterInPlugins.push_back(plugin1);
+ patch::masterOutPlugins.push_back(plugin2);
#endif
- REQUIRE(patch.write(filename) == 1);
+ REQUIRE(patch::write(filename) == 1);
}
SECTION("test read")
{
- REQUIRE(patch.read(filename) == PATCH_READ_OK);
- REQUIRE(patch.header == "GPTCH");
- REQUIRE(patch.version == "1.0");
- REQUIRE(patch.versionMajor == 6);
- REQUIRE(patch.versionMinor == 6);
- REQUIRE(patch.versionPatch == 6);
- REQUIRE(patch.name == "test patch");
- REQUIRE(patch.bpm == Approx(100.0f));
- REQUIRE(patch.bars == 4);
- REQUIRE(patch.beats == 23);
- REQUIRE(patch.quantize == 1);
- REQUIRE(patch.masterVolIn == Approx(1.0f));
- REQUIRE(patch.masterVolOut == Approx(0.7f));
- REQUIRE(patch.metronome == 0);
- REQUIRE(patch.lastTakeId == 0);
- REQUIRE(patch.samplerate == 44100);
-
- Patch::column_t column0 = patch.columns.at(0);
+ REQUIRE(patch::read(filename) == PATCH_READ_OK);
+ REQUIRE(patch::header == "GPTCH");
+ REQUIRE(patch::version == "1.0");
+ REQUIRE(patch::versionMajor == 6);
+ REQUIRE(patch::versionMinor == 6);
+ REQUIRE(patch::versionPatch == 6);
+ REQUIRE(patch::name == "test patch");
+ REQUIRE(patch::bpm == Approx(100.0f));
+ REQUIRE(patch::bars == 4);
+ REQUIRE(patch::beats == 23);
+ REQUIRE(patch::quantize == 1);
+ REQUIRE(patch::masterVolIn == Approx(1.0f));
+ REQUIRE(patch::masterVolOut == Approx(0.7f));
+ REQUIRE(patch::metronome == 0);
+ REQUIRE(patch::lastTakeId == 0);
+ REQUIRE(patch::samplerate == 44100);
+
+ patch::column_t column0 = patch::columns.at(0);
REQUIRE(column0.index == 0);
REQUIRE(column0.width == 500);
- Patch::channel_t channel0 = patch.channels.at(0);
+ patch::channel_t channel0 = patch::channels.at(0);
REQUIRE(channel0.type == CHANNEL_SAMPLE);
REQUIRE(channel0.index == 666);
REQUIRE(channel0.column == 0);
REQUIRE(channel0.mute_s == 0);
REQUIRE(channel0.solo == 0);
REQUIRE(channel0.volume == Approx(1.0f));
- REQUIRE(channel0.panLeft == Approx(0.5f));
- REQUIRE(channel0.panRight == Approx(0.5f));
+ REQUIRE(channel0.pan == Approx(0.5f));
REQUIRE(channel0.midiIn == true);
REQUIRE(channel0.midiInKeyPress == UINT32_MAX);
REQUIRE(channel0.midiInKeyRel == 1);
REQUIRE(channel0.mode == 0);
REQUIRE(channel0.begin == 0);
REQUIRE(channel0.end == 0);
- REQUIRE(channel0.boost == 0);
+ REQUIRE(channel0.boost == 1.0f);
REQUIRE(channel0.recActive == 0);
REQUIRE(channel0.pitch == Approx(1.2f));
REQUIRE(channel0.midiInReadActions == 0);
REQUIRE(channel0.midiOut == 0);
REQUIRE(channel0.midiOutChan == 5);
- Patch::action_t action0 = channel0.actions.at(0);
+ patch::action_t action0 = channel0.actions.at(0);
REQUIRE(action0.type == 0);
REQUIRE(action0.frame == 50000);
REQUIRE(action0.fValue == Approx(0.3f));
REQUIRE(action0.iValue == 1000);
- Patch::action_t action1 = channel0.actions.at(1);
+ patch::action_t action1 = channel0.actions.at(1);
REQUIRE(action1.type == 2);
REQUIRE(action1.frame == 589);
REQUIRE(action1.fValue == Approx(1.0f));
REQUIRE(action1.iValue == 130);
#ifdef WITH_VST
- Patch::plugin_t plugin0 = channel0.plugins.at(0);
+ patch::plugin_t plugin0 = channel0.plugins.at(0);
REQUIRE(plugin0.path == "/path/to/plugin1");
REQUIRE(plugin0.bypass == false);
REQUIRE(plugin0.params.at(0) == Approx(0.0f));
REQUIRE(plugin0.params.at(1) == Approx(0.1f));
REQUIRE(plugin0.params.at(2) == Approx(0.2f));
- Patch::plugin_t plugin1 = channel0.plugins.at(1);
+ patch::plugin_t plugin1 = channel0.plugins.at(1);
REQUIRE(plugin1.path == "/another/path/to/plugin2");
REQUIRE(plugin1.bypass == true);
REQUIRE(plugin1.params.at(0) == Approx(0.6f));
REQUIRE(plugin1.params.at(5) == Approx(1.0f));
REQUIRE(plugin1.params.at(6) == Approx(0.333f));
- Patch::plugin_t masterPlugin0 = patch.masterInPlugins.at(0);
+ patch::plugin_t masterPlugin0 = patch::masterInPlugins.at(0);
REQUIRE(masterPlugin0.path == "/path/to/plugin1");
REQUIRE(masterPlugin0.bypass == false);
REQUIRE(masterPlugin0.params.at(0) == Approx(0.0f));
REQUIRE(masterPlugin0.params.at(1) == Approx(0.1f));
REQUIRE(masterPlugin0.params.at(2) == Approx(0.2f));
- Patch::plugin_t masterPlugin1 = patch.masterOutPlugins.at(0);
+ patch::plugin_t masterPlugin1 = patch::masterOutPlugins.at(0);
REQUIRE(masterPlugin1.path == "/another/path/to/plugin2");
REQUIRE(masterPlugin1.bypass == true);
REQUIRE(masterPlugin1.params.at(0) == Approx(0.6f));
#if 0
#include "../src/core/pluginHost.h"
-#include "catch.hpp"
+#include "catch/single_include/catch.hpp"
TEST_CASE("Test PluginHost class")
--- /dev/null
+#include "../src/core/recorder.h"
+#include "../src/core/const.h"
+#include "catch/single_include/catch.hpp"
+
+
+using std::string;
+using namespace giada::m;
+
+
+TEST_CASE("Test Recorder")
+{
+ /* Each SECTION the TEST_CASE is executed from the start. The following
+ code is exectuted before each SECTION. */
+
+ pthread_mutex_t mutex;
+ pthread_mutex_init(&mutex, nullptr);
+
+ recorder::init();
+ REQUIRE(recorder::frames.size() == 0);
+ REQUIRE(recorder::global.size() == 0);
+
+ SECTION("Test record single action")
+ {
+ recorder::rec(0, G_ACTION_KEYPRESS, 50, 1, 0.5f);
+
+ REQUIRE(recorder::frames.size() == 1);
+ REQUIRE(recorder::frames.at(0) == 50);
+ REQUIRE(recorder::global.at(0).size() == 1); // 1 action on frame #0
+ REQUIRE(recorder::global.at(0).at(0)->chan == 0);
+ REQUIRE(recorder::global.at(0).at(0)->type == G_ACTION_KEYPRESS);
+ REQUIRE(recorder::global.at(0).at(0)->frame == 50);
+ REQUIRE(recorder::global.at(0).at(0)->iValue == 1);
+ REQUIRE(recorder::global.at(0).at(0)->fValue == 0.5f);
+ }
+
+ SECTION("Test record, two actions on same frame")
+ {
+ recorder::rec(0, G_ACTION_KEYPRESS, 50, 6, 0.3f);
+ recorder::rec(0, G_ACTION_KEYREL, 50, 1, 0.5f);
+
+ REQUIRE(recorder::frames.size() == 1); // same frame, frames.size must stay 1
+ REQUIRE(recorder::frames.at(0) == 50);
+ REQUIRE(recorder::global.at(0).size() == 2); // 2 actions on frame #0
+
+ REQUIRE(recorder::global.at(0).at(0)->chan == 0);
+ REQUIRE(recorder::global.at(0).at(0)->type == G_ACTION_KEYPRESS);
+ REQUIRE(recorder::global.at(0).at(0)->frame == 50);
+ REQUIRE(recorder::global.at(0).at(0)->iValue == 6);
+ REQUIRE(recorder::global.at(0).at(0)->fValue == 0.3f);
+
+ REQUIRE(recorder::global.at(0).at(1)->chan == 0);
+ REQUIRE(recorder::global.at(0).at(1)->type == G_ACTION_KEYREL);
+ REQUIRE(recorder::global.at(0).at(1)->frame == 50);
+ REQUIRE(recorder::global.at(0).at(1)->iValue == 1);
+ REQUIRE(recorder::global.at(0).at(1)->fValue == 0.5f);
+
+ SECTION("Test record, another action on a different frame")
+ {
+ recorder::rec(0, G_ACTION_KEYPRESS, 70, 1, 0.5f);
+
+ REQUIRE(recorder::frames.size() == 2);
+ REQUIRE(recorder::frames.at(1) == 70);
+ REQUIRE(recorder::global.at(0).size() == 2); // 2 actions on frame #0
+ REQUIRE(recorder::global.at(1).size() == 1); // 1 actions on frame #1
+ REQUIRE(recorder::global.at(1).at(0)->chan == 0);
+ REQUIRE(recorder::global.at(1).at(0)->type == G_ACTION_KEYPRESS);
+ REQUIRE(recorder::global.at(1).at(0)->frame == 70);
+ REQUIRE(recorder::global.at(1).at(0)->iValue == 1);
+ REQUIRE(recorder::global.at(1).at(0)->fValue == 0.5f);
+ }
+ }
+
+ SECTION("Test retrieval")
+ {
+ recorder::rec(0, G_ACTION_KEYPRESS, 50, 6, 0.3f);
+ recorder::rec(0, G_ACTION_KEYREL, 70, 1, 0.5f);
+ recorder::rec(1, G_ACTION_KEYPRESS, 50, 6, 0.3f);
+ recorder::rec(1, G_ACTION_KEYREL, 70, 1, 0.5f);
+ recorder::rec(2, G_ACTION_KEYPRESS, 100, 6, 0.3f);
+ recorder::rec(2, G_ACTION_KEYREL, 120, 1, 0.5f);
+
+ /* Give me action on chan 1, type G_ACTION_KEYREL, frame 70. */
+ recorder::action *action = nullptr;
+ REQUIRE(recorder::getAction(1, G_ACTION_KEYREL, 70, &action) == 1);
+
+ REQUIRE(action != nullptr);
+ REQUIRE(action->chan == 1);
+ REQUIRE(action->type == G_ACTION_KEYREL);
+ REQUIRE(action->frame == 70);
+ REQUIRE(action->iValue == 1);
+ REQUIRE(action->fValue == 0.5f);
+
+ /* Give me *next* action on chan 0, type G_ACTION_KEYREL, starting from frame 20.
+ Must be action #2 */
+
+ REQUIRE(recorder::getNextAction(0, G_ACTION_KEYREL, 20, &action) == 1);
+ REQUIRE(action != nullptr);
+ REQUIRE(action->chan == 0);
+ REQUIRE(action->type == G_ACTION_KEYREL);
+ REQUIRE(action->frame == 70);
+
+ /* Give me *next* action on chan 2, type G_ACTION_KEYPRESS, starting from
+ frame 200. You are requesting frame outside boundaries. */
+
+ REQUIRE(recorder::getNextAction(2, G_ACTION_KEYPRESS, 200, &action) == -1);
+
+ /* Give me *next* action on chan 2, type G_ACTION_KEYPRESS, starting from
+ frame 100. That action does not exist. */
+
+ REQUIRE(recorder::getNextAction(2, G_ACTION_KEYPRESS, 100, &action) == -2);
+ }
+
+ SECTION("Test deletion, single action")
+ {
+ recorder::rec(0, G_ACTION_KEYPRESS, 50, 6, 0.3f);
+ recorder::rec(0, G_ACTION_KEYREL, 60, 1, 0.5f);
+ recorder::rec(1, G_ACTION_KEYPRESS, 70, 6, 0.3f);
+ recorder::rec(1, G_ACTION_KEYREL, 80, 1, 0.5f);
+
+ /* Delete action #0, don't check values. */
+ recorder::deleteAction(0, 50, G_ACTION_KEYPRESS, false, &mutex);
+
+ REQUIRE(recorder::frames.size() == 3);
+ REQUIRE(recorder::global.size() == 3);
+
+ SECTION("Test deletion checked")
+ {
+ /* Delete action #1, check values. */
+ recorder::deleteAction(1, 70, G_ACTION_KEYPRESS, true, &mutex, 6, 0.3f);
+
+ REQUIRE(recorder::frames.size() == 2);
+ REQUIRE(recorder::global.size() == 2);
+ }
+ }
+
+ SECTION("Test deletion, range of actions")
+ {
+ recorder::rec(0, G_ACTION_KEYPRESS, 50, 6, 0.3f);
+ recorder::rec(0, G_ACTION_KEYREL, 60, 1, 0.5f);
+ recorder::rec(0, G_ACTION_KEYPRESS, 70, 6, 0.3f);
+ recorder::rec(0, G_ACTION_KEYREL, 80, 1, 0.5f);
+ recorder::rec(1, G_ACTION_KEYPRESS, 100, 6, 0.3f);
+ recorder::rec(1, G_ACTION_KEYREL, 120, 1, 0.5f);
+
+ /* Delete any action on channel 0 of types KEYPRESS | KEYREL between
+ frames 0 and 200. */
+
+ recorder::deleteActions(0, 0, 200, G_ACTION_KEYPRESS | G_ACTION_KEYREL, &mutex);
+
+ REQUIRE(recorder::frames.size() == 2);
+ REQUIRE(recorder::global.size() == 2);
+ REQUIRE(recorder::global.at(0).size() == 1);
+ REQUIRE(recorder::global.at(1).size() == 1);
+
+ REQUIRE(recorder::global.at(0).at(0)->chan == 1);
+ REQUIRE(recorder::global.at(0).at(0)->type == G_ACTION_KEYPRESS);
+ REQUIRE(recorder::global.at(0).at(0)->frame == 100);
+ REQUIRE(recorder::global.at(0).at(0)->iValue == 6);
+ REQUIRE(recorder::global.at(0).at(0)->fValue == 0.3f);
+
+ REQUIRE(recorder::global.at(1).at(0)->chan == 1);
+ REQUIRE(recorder::global.at(1).at(0)->type == G_ACTION_KEYREL);
+ REQUIRE(recorder::global.at(1).at(0)->frame == 120);
+ REQUIRE(recorder::global.at(1).at(0)->iValue == 1);
+ REQUIRE(recorder::global.at(1).at(0)->fValue == 0.5f);
+ }
+
+ SECTION("Test action presence")
+ {
+ recorder::rec(0, G_ACTION_KEYREL, 80, 1, 0.5f);
+ recorder::rec(1, G_ACTION_KEYPRESS, 100, 6, 0.3f);
+ recorder::rec(1, G_ACTION_KEYREL, 120, 1, 0.5f);
+
+ recorder::deleteAction(0, 80, G_ACTION_KEYREL, false, &mutex);
+
+ REQUIRE(recorder::hasActions(0) == false);
+ REQUIRE(recorder::hasActions(1) == true);
+ }
+
+ SECTION("Test clear actions by channel")
+ {
+ recorder::rec(0, G_ACTION_KEYREL, 80, 1, 0.5f);
+ recorder::rec(1, G_ACTION_KEYPRESS, 100, 6, 0.3f);
+ recorder::rec(1, G_ACTION_KEYREL, 120, 1, 0.5f);
+
+ recorder::clearChan(1);
+
+ REQUIRE(recorder::hasActions(0) == true);
+ REQUIRE(recorder::hasActions(1) == false);
+ REQUIRE(recorder::frames.size() == 1);
+ REQUIRE(recorder::global.size() == 1);
+ REQUIRE(recorder::global.at(0).size() == 1);
+ }
+
+ SECTION("Test clear actions by type")
+ {
+ recorder::rec(1, G_ACTION_KEYREL, 80, 1, 0.5f);
+ recorder::rec(0, G_ACTION_KEYPRESS, 100, 6, 0.3f);
+ recorder::rec(1, G_ACTION_KEYREL, 120, 1, 0.5f);
+
+ /* Clear all actions of type KEYREL from channel 1. */
+
+ recorder::clearAction(1, G_ACTION_KEYREL);
+
+ REQUIRE(recorder::hasActions(0) == true);
+ REQUIRE(recorder::hasActions(1) == false);
+ REQUIRE(recorder::frames.size() == 1);
+ REQUIRE(recorder::global.size() == 1);
+ REQUIRE(recorder::global.at(0).size() == 1);
+ }
+
+ SECTION("Test clear all")
+ {
+ recorder::rec(0, G_ACTION_KEYPRESS, 0, 1, 0.5f);
+ recorder::rec(1, G_ACTION_KEYPRESS, 0, 1, 0.5f);
+ recorder::rec(0, G_ACTION_KEYREL, 80, 1, 0.5f);
+ recorder::rec(1, G_ACTION_KEYREL, 100, 6, 0.3f);
+ recorder::rec(2, G_ACTION_KILL, 120, 1, 0.5f);
+
+ recorder::clearAll();
+ REQUIRE(recorder::frames.size() == 0);
+ REQUIRE(recorder::global.size() == 0);
+ }
+
+ SECTION("Test optimization")
+ {
+ recorder::rec(0, G_ACTION_KEYPRESS, 20, 1, 0.5f);
+ recorder::rec(0, G_ACTION_KEYREL, 80, 1, 0.5f);
+ recorder::rec(1, G_ACTION_KEYPRESS, 20, 1, 0.5f);
+ recorder::rec(1, G_ACTION_KEYREL, 80, 1, 0.5f);
+
+ /* Fake frame 80 without actions.*/
+ recorder::global.at(1).clear();
+
+ recorder::optimize();
+
+ REQUIRE(recorder::frames.size() == 1);
+ REQUIRE(recorder::global.size() == 1);
+ REQUIRE(recorder::global.at(0).size() == 2);
+ }
+
+ SECTION("Test BPM update")
+ {
+ recorder::rec(0, G_ACTION_KEYPRESS, 0, 1, 0.5f);
+ recorder::rec(0, G_ACTION_KEYREL, 80, 1, 0.5f);
+
+ recorder::updateBpm(60.0f, 120.0f, 44100); // scaling up
+
+ REQUIRE(recorder::frames.at(0) == 0);
+ REQUIRE(recorder::frames.at(1) == 40);
+
+ recorder::updateBpm(120.0f, 60.0f, 44100); // scaling down
+
+ REQUIRE(recorder::frames.at(0) == 0);
+ REQUIRE(recorder::frames.at(1) == 80);
+ }
+
+ SECTION("Test samplerate update")
+ {
+ recorder::rec(0, G_ACTION_KEYPRESS, 0, 1, 0.5f);
+ recorder::rec(0, G_ACTION_KEYREL, 80, 1, 0.5f);
+ recorder::rec(0, G_ACTION_KEYPRESS, 120, 1, 0.5f);
+ recorder::rec(0, G_ACTION_KEYREL, 150, 1, 0.5f);
+
+ recorder::updateSamplerate(44100, 22050); // scaling down
+
+ REQUIRE(recorder::frames.at(0) == 0);
+ REQUIRE(recorder::frames.at(1) == 160);
+ REQUIRE(recorder::frames.at(2) == 240);
+ REQUIRE(recorder::frames.at(3) == 300);
+
+ recorder::updateSamplerate(22050, 44100); // scaling up
+
+ REQUIRE(recorder::frames.at(0) == 0);
+ REQUIRE(recorder::frames.at(1) == 80);
+ REQUIRE(recorder::frames.at(2) == 120);
+ REQUIRE(recorder::frames.at(3) == 150);
+ }
+
+ SECTION("Test expand")
+ {
+ recorder::rec(0, G_ACTION_KEYPRESS, 0, 1, 0.5f);
+ recorder::rec(0, G_ACTION_KEYREL, 80, 1, 0.5f);
+ recorder::rec(0, G_ACTION_KILL, 200, 1, 0.5f);
+
+ recorder::expand(300, 600);
+
+ REQUIRE(recorder::frames.size() == 6);
+ REQUIRE(recorder::global.size() == 6);
+ REQUIRE(recorder::frames.at(0) == 0);
+ REQUIRE(recorder::frames.at(1) == 80);
+ REQUIRE(recorder::frames.at(2) == 200);
+ REQUIRE(recorder::frames.at(3) == 300);
+ REQUIRE(recorder::frames.at(4) == 380);
+ REQUIRE(recorder::frames.at(5) == 500);
+ }
+
+ SECTION("Test shrink")
+ {
+ recorder::rec(0, G_ACTION_KEYPRESS, 0, 1, 0.5f);
+ recorder::rec(0, G_ACTION_KEYREL, 80, 1, 0.5f);
+ recorder::rec(0, G_ACTION_KILL, 200, 1, 0.5f);
+
+ recorder::shrink(100);
+
+ REQUIRE(recorder::frames.size() == 2);
+ REQUIRE(recorder::global.size() == 2);
+ REQUIRE(recorder::frames.at(0) == 0);
+ REQUIRE(recorder::frames.at(1) == 80);
+ }
+
+ SECTION("Test overdub, full overwrite")
+ {
+ recorder::rec(0, G_ACTION_MUTEON, 0, 1, 0.5f);
+ recorder::rec(0, G_ACTION_MUTEOFF, 80, 1, 0.5f);
+ recorder::rec(0, G_ACTION_MUTEON, 200, 1, 0.5f);
+ recorder::rec(0, G_ACTION_MUTEOFF, 400, 1, 0.5f);
+
+ /* Should delete all actions in between and keep the first one, plus a
+ new last action on frame 500. */
+ recorder::startOverdub(0, G_ACTION_MUTEON | G_ACTION_MUTEOFF, 0, 1024);
+ recorder::stopOverdub(500, 500, &mutex);
+
+ REQUIRE(recorder::frames.size() == 2);
+ REQUIRE(recorder::global.size() == 2);
+ REQUIRE(recorder::frames.at(0) == 0);
+ REQUIRE(recorder::frames.at(1) == 500);
+ REQUIRE(recorder::global.at(0).at(0)->frame == 0);
+ REQUIRE(recorder::global.at(0).at(0)->type == G_ACTION_MUTEON);
+ REQUIRE(recorder::global.at(1).at(0)->frame == 500);
+ REQUIRE(recorder::global.at(1).at(0)->type == G_ACTION_MUTEOFF);
+ }
+
+ SECTION("Test overdub, left overlap")
+ {
+ recorder::rec(0, G_ACTION_MUTEON, 100, 1, 0.5f);
+ recorder::rec(0, G_ACTION_MUTEOFF, 400, 1, 0.5f);
+
+ /* Overdub part of the leftmost part of a composite action. Expected result:
+ a new composite action.
+ Original: ----|########|
+ Overdub: |#######|-----
+ Result: |#######|----- */
+ recorder::startOverdub(0, G_ACTION_MUTEON | G_ACTION_MUTEOFF, 0, 16);
+ recorder::stopOverdub(300, 500, &mutex);
+
+ REQUIRE(recorder::frames.size() == 2);
+ REQUIRE(recorder::global.size() == 2);
+ REQUIRE(recorder::frames.at(0) == 0);
+ REQUIRE(recorder::frames.at(1) == 300);
+
+ REQUIRE(recorder::global.at(0).at(0)->frame == 0);
+ REQUIRE(recorder::global.at(0).at(0)->type == G_ACTION_MUTEON);
+ REQUIRE(recorder::global.at(1).at(0)->frame == 300);
+ REQUIRE(recorder::global.at(1).at(0)->type == G_ACTION_MUTEOFF);
+ }
+
+ SECTION("Test overdub, right overlap")
+ {
+ recorder::rec(0, G_ACTION_MUTEON, 000, 1, 0.5f);
+ recorder::rec(0, G_ACTION_MUTEOFF, 400, 1, 0.5f);
+
+ /* Overdub part of the rightmost part of a composite action. Expected result:
+ a new composite action.
+ Original: |########|------
+ Overdub: -----|#######|--
+ Result: |###||#######|-- */
+ recorder::startOverdub(0, G_ACTION_MUTEON | G_ACTION_MUTEOFF, 100, 16);
+ recorder::stopOverdub(500, 500, &mutex);
+
+ REQUIRE(recorder::frames.size() == 4);
+ REQUIRE(recorder::global.size() == 4);
+ REQUIRE(recorder::frames.at(0) == 0);
+ REQUIRE(recorder::frames.at(1) == 84); // 100 - bufferSize (16)
+ REQUIRE(recorder::frames.at(2) == 100);
+ REQUIRE(recorder::frames.at(3) == 500);
+
+ REQUIRE(recorder::global.at(0).at(0)->frame == 0);
+ REQUIRE(recorder::global.at(0).at(0)->type == G_ACTION_MUTEON);
+ REQUIRE(recorder::global.at(1).at(0)->frame == 84);
+ REQUIRE(recorder::global.at(1).at(0)->type == G_ACTION_MUTEOFF);
+
+ REQUIRE(recorder::global.at(2).at(0)->frame == 100);
+ REQUIRE(recorder::global.at(2).at(0)->type == G_ACTION_MUTEON);
+ REQUIRE(recorder::global.at(3).at(0)->frame == 500);
+ REQUIRE(recorder::global.at(3).at(0)->type == G_ACTION_MUTEOFF);
+ }
+
+ SECTION("Test overdub, hole diggin'")
+ {
+ recorder::rec(0, G_ACTION_MUTEON, 0, 1, 0.5f);
+ recorder::rec(0, G_ACTION_MUTEOFF, 400, 1, 0.5f);
+
+ /* Overdub in the middle of a long, composite action. Expected result:
+ original action trimmed down plus anther action next to it. Total frames
+ should be 4.
+ Original: |#############|
+ Overdub: ---|#######|---
+ Result: |#||#######|--- */
+ recorder::startOverdub(0, G_ACTION_MUTEON | G_ACTION_MUTEOFF, 100, 16);
+ recorder::stopOverdub(300, 500, &mutex);
+
+ REQUIRE(recorder::frames.size() == 4);
+ REQUIRE(recorder::global.size() == 4);
+ REQUIRE(recorder::frames.at(0) == 0);
+ REQUIRE(recorder::frames.at(1) == 84); // 100 - bufferSize (16)
+ REQUIRE(recorder::frames.at(2) == 100);
+ REQUIRE(recorder::frames.at(3) == 300);
+
+ REQUIRE(recorder::global.at(0).at(0)->frame == 0);
+ REQUIRE(recorder::global.at(0).at(0)->type == G_ACTION_MUTEON);
+ REQUIRE(recorder::global.at(1).at(0)->frame == 84);
+ REQUIRE(recorder::global.at(1).at(0)->type == G_ACTION_MUTEOFF);
+
+ REQUIRE(recorder::global.at(2).at(0)->frame == 100);
+ REQUIRE(recorder::global.at(2).at(0)->type == G_ACTION_MUTEON);
+ REQUIRE(recorder::global.at(3).at(0)->frame == 300);
+ REQUIRE(recorder::global.at(3).at(0)->type == G_ACTION_MUTEOFF);
+ }
+
+ SECTION("Test overdub, cover all")
+ {
+ recorder::rec(0, G_ACTION_MUTEON, 0, 1, 0.5f);
+ recorder::rec(0, G_ACTION_MUTEOFF, 100, 1, 0.5f);
+ recorder::rec(0, G_ACTION_MUTEON, 120, 1, 0.5f);
+ recorder::rec(0, G_ACTION_MUTEOFF, 200, 1, 0.5f);
+ recorder::rec(0, G_ACTION_MUTEON, 220, 1, 0.5f);
+ recorder::rec(0, G_ACTION_MUTEOFF, 300, 1, 0.5f);
+
+ /* Overdub all existing actions. Expected result: a single composite one. */
+ recorder::startOverdub(0, G_ACTION_MUTEON | G_ACTION_MUTEOFF, 0, 16);
+ recorder::stopOverdub(500, 500, &mutex);
+
+ REQUIRE(recorder::frames.size() == 2);
+ REQUIRE(recorder::global.size() == 2);
+ REQUIRE(recorder::frames.at(0) == 0);
+ REQUIRE(recorder::frames.at(1) == 500);
+
+ REQUIRE(recorder::global.at(0).at(0)->frame == 0);
+ REQUIRE(recorder::global.at(0).at(0)->type == G_ACTION_MUTEON);
+ REQUIRE(recorder::global.at(1).at(0)->frame == 500);
+ REQUIRE(recorder::global.at(1).at(0)->type == G_ACTION_MUTEOFF);
+ }
+
+ SECTION("Test overdub, null loop")
+ {
+ recorder::rec(0, G_ACTION_MUTEON, 0, 1, 0.5f);
+ recorder::rec(0, G_ACTION_MUTEOFF, 500, 1, 0.5f);
+
+ /* A null loop is a loop that begins and ends on the very same frame. */
+ recorder::startOverdub(0, G_ACTION_MUTEON | G_ACTION_MUTEOFF, 300, 16);
+ recorder::stopOverdub(300, 700, &mutex);
+
+ REQUIRE(recorder::frames.size() == 2);
+ REQUIRE(recorder::frames.at(0) == 0);
+ REQUIRE(recorder::frames.at(1) == 284); // 300 - bufferSize (16)
+
+ REQUIRE(recorder::global.at(0).at(0)->frame == 0);
+ REQUIRE(recorder::global.at(0).at(0)->type == G_ACTION_MUTEON);
+ REQUIRE(recorder::global.at(1).at(0)->frame == 284);
+ REQUIRE(recorder::global.at(1).at(0)->type == G_ACTION_MUTEOFF);
+ }
+
+ SECTION("Test overdub, ring loop")
+ {
+ /* A ring loop occurs when you record the last action beyond the end of
+ the sequencer.
+ Original: ---|#######|---
+ Overdub: #####|------|##
+ Result: ---|#######||#| */
+
+ recorder::rec(0, G_ACTION_MUTEON, 200, 1, 0.5f);
+ recorder::rec(0, G_ACTION_MUTEOFF, 300, 1, 0.5f);
+
+ recorder::startOverdub(0, G_ACTION_MUTEON | G_ACTION_MUTEOFF, 400, 16);
+ recorder::stopOverdub(250, 700, &mutex);
+
+ REQUIRE(recorder::frames.size() == 4);
+ REQUIRE(recorder::frames.at(0) == 200);
+ REQUIRE(recorder::frames.at(1) == 300);
+ REQUIRE(recorder::frames.at(2) == 400);
+ REQUIRE(recorder::frames.at(3) == 700);
+ }
+}
#include "../src/utils/fs.h"
#include "../src/utils/string.h"
-#include "catch.hpp"
+#include "catch/single_include/catch.hpp"
using std::vector;
#include "../src/core/wave.h"
-#include "catch.hpp"
+#include "catch/single_include/catch.hpp"
using std::string;