--------------------------------------------------------------------------------
-0.13.0 ---
+0.13.1 --- 2016 . 11 . 16
+- Input MIDI to MIDI channels/plugins
+- Refinements to show/hide 'R' button's dynamics
+- Increase piano roll items' height
+- Set input volume to max by default
+- Start live-recorded sample channels right away
+- Avoid potential crashes when loading samples on running channels
+- Generate metronome during output post-processing
+- Better widgets' layout in Sample Editor
+- Lots of source code optimizations and cleanups
+- Fix inverted 'R' button's status (GitHub #94)
+- Better handling of 'R' button's status when the sequencer is off (GitHub #95)
+- Fix non-playing samples if live-recorded and 'R' button is on (GitHub #93)
+- Reset button statuses once channels have been freed (GitHub #100)
+- Fix missing ASIO and WASAPI APIs on Windows (GitHub #96)
+- Missing RtMidi libs on Linux (GitHub #102)
+- Fix fade-in/fade-out editing not triggering alert on save (GitHub #101)
+
+
+0.13.0 --- 2016 . 09 . 20
- Deep file browser refactoring
- Save browser's scroll position and last item selected on opening
- Load patches/projects/samples on double click
- Update JUCE to version 4.2.3
- Don't include JUCE on tests without VST support (GitHub #75)
- Fix compilation errors on GCC 6 (GitHub #82)
+- Fix includes on OSX (GitHub #92)
- Fix wrong channel's actions count that prevented "R" button to be toggled
properly
- Fixed a bug that prevented actions on frame 0 to being properly reproduced
+- Make Recorder a proper class
+- Better naming convention for ActionEditor's children classes
+- Source code reorganization
0.12.2 --- 2016 . 06 . 02
src/core/dataStorageIni.cpp \
src/core/dataStorageJson.h \
src/core/dataStorageJson.cpp \
-src/glue/glue.h \
-src/glue/glue.cpp \
+src/glue/main.h \
+src/glue/main.cpp \
+src/glue/io.h \
+src/glue/io.cpp \
src/glue/storage.h \
src/glue/storage.cpp \
src/glue/channel.h \
src/gui/dialogs/gd_pluginChooser.cpp \
src/gui/elems/ge_column.h \
src/gui/elems/ge_column.cpp \
-src/gui/elems/ge_sampleChannel.h \
-src/gui/elems/ge_sampleChannel.cpp \
-src/gui/elems/ge_midiChannel.h \
-src/gui/elems/ge_midiChannel.cpp \
+src/gui/elems/sampleChannel.h \
+src/gui/elems/sampleChannel.cpp \
+src/gui/elems/midiChannel.h \
+src/gui/elems/midiChannel.cpp \
src/gui/elems/ge_midiIoTools.h \
src/gui/elems/ge_midiIoTools.cpp \
src/gui/elems/ge_mixed.h \
src/gui/elems/ge_waveform.cpp \
src/gui/elems/ge_browser.h \
src/gui/elems/ge_browser.cpp \
-src/gui/elems/ge_actionWidget.h \
-src/gui/elems/ge_actionWidget.cpp \
-src/gui/elems/ge_envelopeChannel.h \
-src/gui/elems/ge_envelopeChannel.cpp \
-src/gui/elems/ge_pianoRoll.h \
-src/gui/elems/ge_pianoRoll.cpp \
-src/gui/elems/ge_channel.h \
-src/gui/elems/ge_channel.cpp \
-src/gui/elems/ge_muteChannel.h \
-src/gui/elems/ge_muteChannel.cpp \
-src/gui/elems/ge_actionChannel.h \
-src/gui/elems/ge_actionChannel.cpp \
+src/gui/elems/baseActionEditor.h \
+src/gui/elems/baseActionEditor.cpp \
+src/gui/elems/envelopeEditor.h \
+src/gui/elems/envelopeEditor.cpp \
+src/gui/elems/pianoRoll.h \
+src/gui/elems/pianoRoll.cpp \
+src/gui/elems/noteEditor.h \
+src/gui/elems/noteEditor.cpp \
+src/gui/elems/pianoItem.h \
+src/gui/elems/pianoItem.cpp \
+src/gui/elems/channel.h \
+src/gui/elems/channel.cpp \
+src/gui/elems/muteEditor.h \
+src/gui/elems/muteEditor.cpp \
+src/gui/elems/actionEditor.h \
+src/gui/elems/actionEditor.cpp \
src/gui/elems/ge_window.h \
src/gui/elems/ge_window.cpp \
src/gui/elems/ge_status.h \
src/gui/elems/ge_modeBox.cpp \
src/gui/elems/ge_controller.h \
src/gui/elems/ge_controller.cpp \
-src/gui/elems/ge_channelButton.h \
-src/gui/elems/ge_channelButton.cpp \
+src/gui/elems/channelButton.h \
+src/gui/elems/channelButton.cpp \
src/gui/elems/ge_pluginBrowser.h \
src/gui/elems/ge_pluginBrowser.cpp \
src/utils/log.h \
src/utils/log.cpp \
-src/utils/gui_utils.h \
-src/utils/gui_utils.cpp \
+src/utils/gui.h \
+src/utils/gui.cpp \
src/utils/gvector.h \
-src/utils/utils.h \
-src/utils/utils.cpp \
+src/utils/fs.h \
+src/utils/fs.cpp \
src/utils/string.h \
src/utils/string.cpp
-DJUCE_PLUGINHOST_VST=1 \
-DJUCE_PLUGINHOST_VST3=0 \
-DJUCE_PLUGINHOST_AU=0
-giada_CXXFLAGS += -Wno-error=misleading-indentation
endif
if LINUX
giada_SOURCES += src/deps/rtaudio-mod/RtAudio.h src/deps/rtaudio-mod/RtAudio.cpp
-# mute rtAudio error on variable length array
-giada_CXXFLAGS += -Wno-error=vla
+# -Wno-error=vla: mute rtAudio error on variable length array
+# -Wno-error=misleading-indentation: mute JUCE warnings on GCC6
+giada_CXXFLAGS += -Wno-error=vla -Wno-error=misleading-indentation
giada_CPPFLAGS += -D__LINUX_ALSA__ -D__LINUX_PULSE__ -D__UNIX_JACK__
giada_LDADD = -lsndfile -lfltk -lXext -lX11 -lXft -lXpm -lm -ljack -lasound \
-lpthread -ldl -lpulse-simple -lpulse -lsamplerate -lrtmidi -ljansson \
endif
if WINDOWS
-giada_LDADD = -lrtaudio -ldsound -lwsock32 -lm -lfltk -lwininet -lgdi32 \
+giada_SOURCES += \
+src/deps/rtaudio-mod/RtAudio.h \
+src/deps/rtaudio-mod/RtAudio.cpp \
+src/deps/rtaudio-mod/include/asio.h \
+src/deps/rtaudio-mod/include/asio.cpp \
+src/deps/rtaudio-mod/include/asiolist.h \
+src/deps/rtaudio-mod/include/asiolist.cpp \
+src/deps/rtaudio-mod/include/asiodrivers.h \
+src/deps/rtaudio-mod/include/asiodrivers.cpp \
+src/deps/rtaudio-mod/include/iasiothiscallresolver.h \
+src/deps/rtaudio-mod/include/iasiothiscallresolver.cpp
+# -Wno-error=misleading-indentation: mute JUCE warnings on GCC6
+# -Wno-error=unused-but-set-variable: silence ASIO errors
+giada_CXXFLAGS += \
+-Wno-error=misleading-indentation \
+-Wno-error=unused-but-set-variable
+giada_CPPFLAGS += \
+-I./src/deps/rtaudio-mod/include \
+-D__WINDOWS_ASIO__ \
+-D__WINDOWS_WASAPI__ \
+-D__WINDOWS_DS__
+giada_LDADD = -ldsound -lwsock32 -lm -lfltk -lwininet -lgdi32 \
-lshell32 -lvfw32 -lrpcrt4 -luuid -lcomctl32 -lole32 -lws2_32 -lsndfile \
-lsamplerate -lrtmidi -lwinmm -lsetupapi -lksuser -lpthreadGC2 -ljansson \
-limm32 -lglu32 -lshell32 -lversion -lopengl32 -loleaut32 -lshlwapi -lcomdlg32
# export CXXFLAGS="-m32"
# export LDFLAGS="-m32"
# -ObjC++: Juce requires to build some Objective C code
+# -Wno-unknown-pragmas: shut up Juce even more
giada_SOURCES += src/utils/cocoa.mm src/utils/cocoa.h
-giada_CXXFLAGS += -ObjC++
+giada_CXXFLAGS += -ObjC++ -Wno-unknown-pragmas
giada_LDADD = -lsndfile -lm -lpthread -lfltk -lrtmidi -lrtaudio \
-lsamplerate -ljansson
giada_LDFLAGS = -framework CoreAudio -framework Cocoa -framework Carbon \
src/core/midiMapConf.cpp \
src/core/patch.cpp \
src/core/plugin.cpp \
-src/core/pluginHost.cpp \
src/core/dataStorageIni.cpp \
src/core/dataStorageJson.cpp \
-src/utils/utils.cpp \
+src/utils/fs.cpp \
+src/utils/string.cpp \
src/utils/log.cpp
if WITH_VST
#include "../utils/log.h"
-#include "../gui/elems/ge_channel.h"
+#include "../gui/elems/channel.h"
#include "channel.h"
#include "pluginHost.h"
#include "plugin.h"
#include "midiMapConf.h"
+extern Recorder G_Recorder;
+extern KernelMidi G_KernelMidi;
+
+
Channel::Channel(int type, int status, int bufferSize, MidiMapConf *midiMapConf)
#if defined(WITH_VST)
-: pluginHost(NULL),
+: pluginHost(nullptr),
#else
:
#endif
mute (false),
solo (false),
hasActions (false),
+ armed (false),
recStatus (REC_STOPPED),
- vChan (NULL),
- guiChannel (NULL),
+ vChan (nullptr),
+ guiChannel (nullptr),
midiIn (true),
midiInKeyPress (0x0),
midiInKeyRel (0x0),
midiInKill (0x0),
+ midiInArm (0x0),
midiInVolume (0x0),
midiInMute (0x0),
midiInSolo (0x0),
{
vChan = (float *) malloc(bufferSize * sizeof(float));
if (!vChan)
- gLog("[Channel::allocVchan] unable to alloc memory for vChan\n");
+ gu_log("[Channel::allocVchan] unable to alloc memory for vChan\n");
memset(vChan, 0, bufferSize * sizeof(float));
}
midiInKeyPress = src->midiInKeyPress;
midiInKeyRel = src->midiInKeyRel;
midiInKill = src->midiInKill;
+ midiInArm = src->midiInArm;
midiInVolume = src->midiInVolume;
midiInMute = src->midiInMute;
midiInSolo = src->midiInSolo;
/* clone actions */
- 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);
+ 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);
if (a->chan == src->index)
- recorder::rec(index, a->type, a->frame, a->iValue, a->fValue);
+ G_Recorder.rec(index, a->type, a->frame, a->iValue, a->fValue);
}
}
}
void Channel::sendMidiLmessage(uint32_t learn, const MidiMapConf::message_t &msg)
{
- gLog("[channel::sendMidiLmessage] learn=%#X, chan=%d, msg=%#X, offset=%d\n",
+ gu_log("[channel::sendMidiLmessage] learn=%#X, chan=%d, msg=%#X, offset=%d\n",
learn, msg.channel, msg.value, msg.offset);
/* isolate 'channel' from learnt message and offset it as requested by 'nn'
* send it. */
out |= msg.value | (msg.channel << 24);
- kernelMidi::send(out);
+ G_KernelMidi.send(out);
}
pch.midiInKeyPress = midiInKeyPress;
pch.midiInKeyRel = midiInKeyRel;
pch.midiInKill = midiInKill;
+ pch.midiInArm = midiInArm;
pch.midiInVolume = midiInVolume;
pch.midiInMute = midiInMute;
pch.midiInSolo = midiInSolo;
pch.midiOutLmute = midiOutLmute;
pch.midiOutLsolo = midiOutLsolo;
- 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);
+ 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);
if (action->chan == index) {
Patch::action_t pac;
pac.type = action->type;
for (unsigned k=0; k<pch->actions.size(); k++) {
Patch::action_t *ac = &pch->actions.at(k);
- recorder::rec(index, ac->type, ac->frame, ac->iValue, ac->fValue);
+ G_Recorder.rec(index, ac->type, ac->frame, ac->iValue, ac->fValue);
}
#ifdef WITH_VST
Patch::plugin_t *ppl = &pch->plugins.at(k);
Plugin *plugin = pluginHost->addPlugin(ppl->path, PluginHost::CHANNEL,
pluginMutex, this);
- if (plugin != NULL) {
+ if (plugin != nullptr) {
plugin->setBypass(ppl->bypass);
for (unsigned j=0; j<ppl->params.size(); j++)
plugin->setParameter(j, ppl->params.at(j));
/* -------------------------------------------------------------------------- */
+void Channel::receiveMidi(uint32_t msg)
+{
+}
+
+/* -------------------------------------------------------------------------- */
+
+
#ifdef WITH_VST
+
+juce::MidiBuffer &Channel::getPluginMidiEvents()
+{
+ return midiBuffer;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void Channel::clearMidiBuffer()
+{
+ midiBuffer.clear();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
void Channel::setPluginHost(PluginHost *pluginHost)
{
this->pluginHost = pluginHost;
}
+
#endif
#include <vector>
-#include "../utils/utils.h"
+#include <pthread.h>
#include "midiMapConf.h"
-#include "const.h"
#include "recorder.h"
#ifdef WITH_VST
#include "../deps/juce/config.h"
#endif
+
using std::vector;
using std::string;
virtual void copy(const Channel *src, pthread_mutex_t *pluginMutex) = 0;
- /* writePatch
- * Fill a patch with channel values. Returns the index of the last
- * Patch::channel_t added. */
-
- virtual int writePatch(int i, bool isProject, class Patch *patch);
-
/* readPatch
* Fill channel with data from patch. */
pthread_mutex_t *pluginMutex, int samplerate, int rsmpQuality);
/* process
- * merge vChannels into buffer, plus plugin processing (if any). */
+ Merges vChannels into buffer, plus plugin processing (if any). Warning:
+ inBuffer might be nullptr if no input devices are available for recording. */
- virtual void process(float *buffer) = 0;
+ virtual void process(float *outBuffer, float *inBuffer) = 0;
/* start
- * action to do when channel starts. doQuantize = false (don't
- * quantize) when Mixer is reading actions from Recorder::. */
+ Action to do when channel starts. doQuantize = false (don't quantize)
+ when Mixer is reading actions from Recorder. If isUserGenerated means that
+ the channel has been started by a human key press and not a pre-recorded
+ action. */
virtual void start(int frame, bool doQuantize, int quantize,
- bool mixerIsRunning) = 0;
+ bool mixerIsRunning, bool forceStart, bool isUserGenerated) = 0;
/* stop
* action to do when channel is stopped normally (via key or MIDI). */
* localFrame - frame number of the processed buffer
* globalFrame - actual frame in Mixer */
- virtual void parseAction(recorder::action *a, int localFrame, int globalFrame,
+ // TODO - quantize is useless!
+
+ virtual void parseAction(Recorder::action *a, int localFrame, int globalFrame,
int quantize, bool mixerIsRunning) = 0;
/* rewind
virtual void rewind() = 0;
+ /* clear
+ Clears all memory buffers. This is actually useful to sample channels only. */
+
+ virtual void clear() = 0;
+
+ /* canInputRec
+ Tells whether a channel can accept and handle input audio. Always false for
+ Midi channels, true for Sample channels only if they don't contain a
+ sample yet.*/
+
+ virtual bool canInputRec() = 0;
+
+ /* writePatch
+ * Fill a patch with channel values. Returns the index of the last
+ * Patch::channel_t added. */
+
+ virtual int writePatch(int i, bool isProject, class Patch *patch);
+
+ /* receiveMidi
+ * Receives and processes midi messages from external devices. */
+
+ virtual void receiveMidi(uint32_t msg);
+
/* ------------------------------------------------------------------------ */
- int index; // unique id
- int type; // midi or sample
- int status; // status: see const.h
- int key; // keyboard button
- float volume; // global volume
- float volume_i; // internal volume
- float volume_d; // delta volume (for envelope)
+ int index; // unique id
+ int type; // midi or sample
+ int status; // status: see const.h
+ int key; // keyboard button
+ float volume; // global volume
+ float volume_i; // internal volume
+ float volume_d; // delta volume (for envelope)
float panLeft;
float panRight;
- bool mute_i; // internal mute
- bool mute_s; // previous mute status after being solo'd
- bool mute; // global mute
+ bool mute_i; // internal mute
+ bool mute_s; // previous mute status after being solo'd
+ bool mute; // global mute
bool solo;
- bool hasActions; // has something recorded
- int recStatus; // status of recordings (waiting, ending, ...)
- float *vChan; // virtual channel
- class gChannel *guiChannel; // pointer to a gChannel object, part of the GUI
+ bool hasActions; // has something recorded
+ bool armed; // armed for recording
+ int recStatus; // status of recordings (waiting, ending, ...)
+ float *vChan; // virtual channel
+ class geChannel *guiChannel; // pointer to a gChannel object, part of the GUI
// TODO - midi structs, please
uint32_t midiInKeyPress;
uint32_t midiInKeyRel;
uint32_t midiInKill;
+ uint32_t midiInArm;
uint32_t midiInVolume;
uint32_t midiInMute;
uint32_t midiInSolo;
* Return a reference to midiBuffer stack. This is available for any kind of
* channel, but it makes sense only for MIDI channels. */
- juce::MidiBuffer &getPluginMidiEvents() { return midiBuffer; };
+ juce::MidiBuffer &getPluginMidiEvents();
+
+ void clearMidiBuffer();
#endif
};
#include <string>
#include "conf.h"
#include "const.h"
-#include "../utils/utils.h"
+#include "../utils/fs.h"
#include "../utils/log.h"
#if defined(__linux__) || defined(__APPLE__)
- confFilePath = gGetHomePath() + G_SLASH + CONF_FILENAME;
- confDirPath = gGetHomePath() + G_SLASH;
+ confFilePath = gu_getHomePath() + G_SLASH + CONF_FILENAME;
+ confDirPath = gu_getHomePath() + G_SLASH;
#elif defined(_WIN32)
{
#if defined(__linux__) || defined(__APPLE__)
- if (gDirExists(confDirPath))
+ if (gu_dirExists(confDirPath))
return 1;
- gLog("[Conf::createConfigFolder] .giada folder not present. Updating...\n");
+ gu_log("[Conf::createConfigFolder] .giada folder not present. Updating...\n");
- if (gMkdir(confDirPath)) {
- gLog("[Conf::createConfigFolder] status: ok\n");
+ if (gu_mkdir(confDirPath)) {
+ gu_log("[Conf::createConfigFolder] status: ok\n");
return 1;
}
else {
- gLog("[Conf::createConfigFolder] status: error!\n");
+ gu_log("[Conf::createConfigFolder] status: error!\n");
return 0;
}
jRoot = json_load_file(confFilePath.c_str(), 0, &jError);
if (!jRoot) {
- gLog("[Conf::read] unable to read configuration file! Error on line %d: %s\n", jError.line, jError.text);
+ gu_log("[Conf::read] unable to read configuration file! Error on line %d: %s\n", jError.line, jError.text);
return 0;
}
#endif
if (json_dump_file(jRoot, confFilePath.c_str(), JSON_INDENT(2)) != 0) {
- gLog("[Conf::write] unable to write configuration file!\n");
+ gu_log("[Conf::write] unable to write configuration file!\n");
return 0;
}
return 1;
/* -- version --------------------------------------------------------------- */
-#define G_VERSION_STR "0.13.0"
+#define G_VERSION_STR "0.13.1"
#define G_APP_NAME "Giada"
#define G_VERSION_MAJOR 0
#define G_VERSION_MINOR 13
-#define G_VERSION_PATCH 0
+#define G_VERSION_PATCH 1
#define CONF_FILENAME "giada.conf"
#define COLOR_TEXT_0 fl_rgb_color(200, 200, 200)
#define COLOR_TEXT_1 fl_rgb_color(25, 25, 25) // TODO - duplicate!
#define COLOR_BG_MAIN fl_rgb_color(25, 25, 25) // windows background
+#define COLOR_BG_RICH fl_rgb_color(30, 30, 30) // lighter background
+#define COLOR_BG_LINE fl_rgb_color(54, 54, 54) // lighter, for bg lines
#define COLOR_BG_DARK fl_rgb_color(0, 0, 0) // inputs background
#define DEFAULT_SAMPLERATE 44100
#define DEFAULT_BUFSIZE 1024
#define DEFAULT_DELAYCOMP 0
-#define DEFAULT_VOL 0.0f
+#define DEFAULT_VOL 1.0f
#define DEFAULT_BOOST 0.0f
#define gDEFAULT_PITCH 1.0f // ugly and temporary fix to avoid conflicts with wingdi.h (Windows only).
#define DEFAULT_OUT_VOL 1.0f
-#define DEFAULT_IN_VOL 0.0f
+#define DEFAULT_IN_VOL 1.0f
#define DEFAULT_CHANMODE SINGLE_BASIC
#define DEFAULT_BPM 120.0f
#define DEFAULT_BEATS 4
#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)"
/* -- browser types --------------------------------------------------------- */
+/* TODO - USELESS, remove them */
#define BROWSER_LOAD_PATCH 0x00
#define BROWSER_LOAD_SAMPLE 0x01
#define BROWSER_SAVE_PATCH 0x02
#define MIDI_CONTROLLER 0xB0 << 24
#define MIDI_NOTE_ON 0x90 << 24
#define MIDI_NOTE_OFF 0x80 << 24
+#define MIDI_VELOCITY 0x3F << 8
#define MIDI_ALL_NOTES_OFF (MIDI_CONTROLLER) | (0x7B << 16)
#define MIDI_VOLUME (MIDI_CONTROLLER) | (0x07 << 16)
#define PATCH_KEY_CHANNEL_MIDI_IN_KEYPRESS "midi_in_keypress"
#define PATCH_KEY_CHANNEL_MIDI_IN_KEYREL "midi_in_keyrel"
#define PATCH_KEY_CHANNEL_MIDI_IN_KILL "midi_in_kill"
+#define PATCH_KEY_CHANNEL_MIDI_IN_ARM "midi_in_arm"
#define PATCH_KEY_CHANNEL_MIDI_IN_VOLUME "midi_in_volume"
#define PATCH_KEY_CHANNEL_MIDI_IN_MUTE "midi_in_mute"
#define PATCH_KEY_CHANNEL_MIDI_IN_SOLO "midi_in_solo"
char buffer[MAX_LINE_LEN];
if (fgets(buffer, MAX_LINE_LEN, fp) == NULL) {
- gLog("[DataStorageIni::getValue] key '%s' not found\n", in);
+ gu_log("[DataStorageIni::getValue] key '%s' not found\n", in);
return "";
}
{
json_t *jObject = json_object_get(jRoot, key);
if (!json_is_string(jObject)) {
- gLog("[dataStorageJson::setString] key '%s' is not a string!\n", key);
+ gu_log("[dataStorageJson::setString] key '%s' is not a string!\n", key);
json_decref(jRoot);
return false;
}
{
json_t *jObject = json_object_get(jRoot, key);
if (!jObject) {
- gLog("[dataStorageJson::setFloat] key '%s' not found, using default value\n", key);
+ gu_log("[dataStorageJson::setFloat] key '%s' not found, using default value\n", key);
output = 0.0f;
return true;
}
if (!json_is_real(jObject)) {
- gLog("[dataStorageJson::setFloat] key '%s' is not a float!\n", key);
+ gu_log("[dataStorageJson::setFloat] key '%s' is not a float!\n", key);
json_decref(jRoot);
return false;
}
{
json_t *jObject = json_object_get(jRoot, key);
if (!jObject) {
- gLog("[dataStorageJson::setUint32] key '%s' not found, using default value\n", key);
+ gu_log("[dataStorageJson::setUint32] key '%s' not found, using default value\n", key);
output = 0;
return true;
}
if (!json_is_integer(jObject)) {
- gLog("[dataStorageJson::setUint32] key '%s' is not an integer!\n", key);
+ gu_log("[dataStorageJson::setUint32] key '%s' is not an integer!\n", key);
json_decref(jRoot);
return false;
}
{
json_t *jObject = json_object_get(jRoot, key);
if (!jObject) {
- gLog("[dataStorageJson::setBool] key '%s' not found, using default value\n", key);
+ gu_log("[dataStorageJson::setBool] key '%s' not found, using default value\n", key);
output = false;
return true;
}
if (!json_is_boolean(jObject)) {
- gLog("[dataStorageJson::setBool] key '%s' is not a boolean!\n", key);
+ gu_log("[dataStorageJson::setBool] key '%s' is not a boolean!\n", key);
json_decref(jRoot);
return false;
}
bool DataStorageJson::checkObject(json_t *jRoot, const char *key)
{
if (!json_is_object(jRoot)) {
- gLog("[DataStorageJson::checkObject] malformed json: %s is not an object!\n", key);
+ gu_log("[DataStorageJson::checkObject] malformed json: %s is not an object!\n", key);
json_decref(jRoot);
return false;
}
bool DataStorageJson::checkArray(json_t *jRoot, const char *key)
{
if (!json_is_array(jRoot)) {
- gLog("[DataStorageJson::checkObject] malformed json: %s is not an array!\n", key);
+ gu_log("[DataStorageJson::checkObject] malformed json: %s is not an array!\n", key);
json_decref(jRoot);
return false;
}
" ",
" "};
+const char *inputToOutputOn_xpm[] = {
+"10 10 8 1",
+" c #4D4F4C",
+". c #585A57",
+"+ c #666765",
+"@ c #6F716E",
+"# c #939592",
+"$ c #999B98",
+"% c #AEB0AD",
+"& c #BCBEBB",
+" ",
+" ",
+" .#@ ",
+" #&&#+ ",
+" @$&%. ",
+" @$&%. ",
+" #&&#+ ",
+" .#@ ",
+" ",
+" "};
+
+const char *inputToOutputOff_xpm[] = {
+"10 10 8 1",
+" c #242523",
+". c #2E302D",
+"+ c #3A3B39",
+"@ c #4F514E",
+"# c #828481",
+"$ c #8B8D8A",
+"% c #A7A9A6",
+"& c #B9BBB7",
+" ",
+" ",
+" +$@ ",
+" .#&&#+ ",
+" @$&%. ",
+" @$&%. ",
+" .#&&#+ ",
+" +$@ ",
+" ",
+" "};
+
const char *muteOff_xpm[] = {
"18 18 8 1",
" c #242523",
#endif // #ifdef WITH_VST
-const char *beatsDivideOn_xpm[] = {
+const char *divideOn_xpm[] = {
"18 18 7 1",
" c #5A5A5A",
". c #696969",
" "};
-const char *beatsDivideOff_xpm[] = {
+const char *divideOff_xpm[] = {
"18 18 8 1",
" c #252525",
". c #3B3B3B",
" "};
-const char *beatsMultiplyOn_xpm[] = {
+const char *multiplyOn_xpm[] = {
"18 18 8 1",
" c #595B58",
". c #737572",
" "};
-const char *beatsMultiplyOff_xpm[] = {
+const char *multiplyOff_xpm[] = {
"18 18 8 1",
" c #242523",
". c #4A4C49",
" ",
" ",
" "};
+
+
+const char *armOff_xpm[] = {
+"18 18 8 1",
+" c #242523",
+". c #4F4445",
+"+ c #514647",
+"@ c #6D5C5E",
+"# c #8E7372",
+"$ c #AA8889",
+"% c #AC898A",
+"& c #B18E8F",
+" ",
+" ",
+" ",
+" ",
+" +#%%#. ",
+" @&&&&&&@ ",
+" +&&&&&&&&. ",
+" #&&&&&&&&# ",
+" %&&&&&&&&% ",
+" %&&&&&&&&% ",
+" #&&&&&&&&# ",
+" .&&&&&&&&. ",
+" @&&&&&&@ ",
+" .#%%#. ",
+" ",
+" ",
+" ",
+" "};
+
+
+const char *armOn_xpm[] = {
+"18 18 8 1",
+" c #4D4F4C",
+". c #6B5077",
+"+ c #805191",
+"@ c #9950AD",
+"# c #9751B3",
+"$ c #9553AD",
+"% c #AA52C9",
+"& c #AE52D1",
+" ",
+" ",
+" ",
+" ",
+" .#%%#. ",
+" +&&&&&&+ ",
+" .&&&&&&&&. ",
+" #&&&&&&&&@ ",
+" %&&&&&&&&% ",
+" %&&&&&&&&% ",
+" #&&&&&&&&$ ",
+" .&&&&&&&&. ",
+" +&&&&&&+ ",
+" .@%%$. ",
+" ",
+" ",
+" ",
+" "};
extern const char *inputRecOn_xpm[];
extern const char *inputRecOff_xpm[];
-extern const char *beatsDivideOn_xpm[];
-extern const char *beatsDivideOff_xpm[];
-extern const char *beatsMultiplyOn_xpm[];
-extern const char *beatsMultiplyOff_xpm[];
+extern const char *inputToOutputOn_xpm[];
+extern const char *inputToOutputOff_xpm[];
+
+extern const char *divideOn_xpm[];
+extern const char *divideOff_xpm[];
+extern const char *multiplyOn_xpm[];
+extern const char *multiplyOff_xpm[];
extern const char *muteOff_xpm[];
extern const char *muteOn_xpm[];
extern const char *soloOff_xpm[];
extern const char *soloOn_xpm[];
+extern const char *armOff_xpm[];
+extern const char *armOn_xpm[];
+
extern const char *readActionOn_xpm[];
extern const char *readActionOff_xpm[];
#include <ctime>
#include "../utils/log.h"
-#include "../utils/utils.h"
-#include "../utils/gui_utils.h"
+#include "../utils/fs.h"
+#include "../utils/gui.h"
#include "../gui/dialogs/gd_mainWindow.h"
#include "../gui/dialogs/gd_warnings.h"
#include "init.h"
#include "kernelMidi.h"
+extern KernelAudio G_KernelAudio;
extern Mixer G_Mixer;
+extern Recorder G_Recorder;
+extern KernelMidi G_KernelMidi;
extern bool G_audio_status;
extern bool G_quit;
extern Patch_DEPR_ G_Patch_DEPR_;
extern Patch G_Patch;
extern Conf G_Conf;
extern MidiMapConf G_MidiMap;
-extern gdMainWindow *mainWin;
+extern gdMainWindow *G_MainWin;
#ifdef WITH_VST
extern PluginHost G_PluginHost;
{
time_t t;
time (&t);
- gLog("[init] Giada " G_VERSION_STR " - %s", ctime(&t));
+ gu_log("[init] Giada " G_VERSION_STR " - %s", ctime(&t));
G_Conf.read();
G_Patch_DEPR_.setDefault();
G_Patch.init();
-#ifdef WITH_VST
-
- G_PluginHost.init(G_Conf.buffersize, G_Conf.samplerate);
- G_PluginHost.sortPlugins(G_Conf.pluginSortMethod);
-
-#endif
+ if (!gu_logInit(G_Conf.logMode))
+ gu_log("[init] log init failed! Using default stdout\n");
-
- if (!gLog_init(G_Conf.logMode))
- gLog("[init] log init failed! Using default stdout\n");
-
- gLog("[init] configuration file ready\n");
+ gu_log("[init] configuration file ready\n");
}
void init_prepareKernelAudio()
{
- kernelAudio::openDevice(
- G_Conf.soundSystem,
- G_Conf.soundDeviceOut,
- G_Conf.soundDeviceIn,
- G_Conf.channelsOut,
- G_Conf.channelsIn,
- G_Conf.samplerate,
- G_Conf.buffersize);
+ G_KernelAudio.openDevice(G_Conf.soundSystem, G_Conf.soundDeviceOut,
+ G_Conf.soundDeviceIn, G_Conf.channelsOut, G_Conf.channelsIn,
+ G_Conf.samplerate, G_Conf.buffersize);
G_Mixer.init();
- recorder::init();
+ G_Recorder.init();
+
+#ifdef WITH_VST
+
+ /* If with Jack don't use buffer size stored in Conf. Use real buffersize
+ from the soundcard (G_KernelAudio.realBufsize). */
+
+ if (G_Conf.soundSystem == SYS_API_JACK)
+ G_PluginHost.init(G_KernelAudio.realBufsize, G_Conf.samplerate);
+ else
+ G_PluginHost.init(G_Conf.buffersize, G_Conf.samplerate);
+
+ G_PluginHost.sortPlugins(G_Conf.pluginSortMethod);
+
+#endif
}
void init_prepareKernelMIDI()
{
- kernelMidi::setApi(G_Conf.midiSystem);
- kernelMidi::openOutDevice(G_Conf.midiPortOut);
- kernelMidi::openInDevice(G_Conf.midiPortIn);
+ G_KernelMidi.setApi(G_Conf.midiSystem);
+ G_KernelMidi.openOutDevice(G_Conf.midiPortOut);
+ G_KernelMidi.openInDevice(G_Conf.midiPortIn);
}
// TODO - do the opposite: if json fails, go with deprecated one
if (G_MidiMap.read(G_Conf.midiMapPath) != MIDIMAP_READ_OK) {
- gLog("[init_prepareMidiMap] JSON-based midimap read failed, trying with the deprecated one...\n");
+ gu_log("[init_prepareMidiMap] JSON-based midimap read failed, trying with the deprecated one...\n");
if (G_MidiMap.readMap_DEPR_(G_Conf.midiMapPath) == MIDIMAP_INVALID)
- gLog("[init_prepareMidiMap] unable to read deprecated midimap. Nothing to do\n");
+ gu_log("[init_prepareMidiMap] unable to read deprecated midimap. Nothing to do\n");
}
}
void init_startGUI(int argc, char **argv)
{
- char win_label[32];
- sprintf(win_label, "%s - %s",
- G_APP_NAME,
- !strcmp(G_Patch_DEPR_.name, "") ? "(default patch)" : G_Patch_DEPR_.name);
+ G_MainWin = new gdMainWindow(GUI_WIDTH, GUI_HEIGHT, "", argc, argv);
+ G_MainWin->resize(G_Conf.mainWindowX, G_Conf.mainWindowY, G_Conf.mainWindowW,
+ G_Conf.mainWindowH);
- mainWin = new gdMainWindow(GUI_WIDTH, GUI_HEIGHT, win_label, argc, argv);
- mainWin->resize(G_Conf.mainWindowX, G_Conf.mainWindowY, G_Conf.mainWindowW, G_Conf.mainWindowH);
+ gu_updateMainWinLabel(G_Patch.name == "" ? G_DEFAULT_PATCH_NAME : G_Patch.name);
/* never update the GUI elements if G_audio_status is bad, segfaults
* are around the corner */
if (G_audio_status)
gu_updateControls();
-
- if (!G_audio_status)
- gdAlert(
- "Your soundcard isn't configured correctly.\n"
- "Check the configuration and restart Giada."
- );
+ else
+ gdAlert("Your soundcard isn't configured correctly.\n"
+ "Check the configuration and restart Giada.");
}
/* -------------------------------------------------------------------------- */
void init_startKernelAudio()
{
if (G_audio_status)
- kernelAudio::startStream();
+ G_KernelAudio.startStream();
}
/* store position and size of the main window for the next startup */
- G_Conf.mainWindowX = mainWin->x();
- G_Conf.mainWindowY = mainWin->y();
- G_Conf.mainWindowW = mainWin->w();
- G_Conf.mainWindowH = mainWin->h();
+ G_Conf.mainWindowX = G_MainWin->x();
+ G_Conf.mainWindowY = G_MainWin->y();
+ G_Conf.mainWindowW = G_MainWin->w();
+ G_Conf.mainWindowH = G_MainWin->h();
/* close any open subwindow, especially before cleaning PluginHost_DEPR_ to
* avoid mess */
gu_closeAllSubwindows();
- gLog("[init] all subwindows closed\n");
+ gu_log("[init] all subwindows closed\n");
/* write configuration file */
if (!G_Conf.write())
- gLog("[init] error while saving configuration file!\n");
+ gu_log("[init] error while saving configuration file!\n");
else
- gLog("[init] configuration saved\n");
+ gu_log("[init] configuration saved\n");
/* if G_audio_status we close the kernelAudio FIRST, THEN the mixer.
* The opposite could cause random segfaults (even now with RtAudio?). */
if (G_audio_status) {
- kernelAudio::closeDevice();
+ G_KernelAudio.closeDevice();
G_Mixer.close();
- gLog("[init] Mixer closed\n");
+ gu_log("[init] Mixer closed\n");
}
- recorder::clearAll();
- gLog("[init] Recorder cleaned up\n");
+ G_Recorder.clearAll();
+ gu_log("[init] Recorder cleaned up\n");
#ifdef WITH_VST
G_PluginHost.freeAllStacks(&G_Mixer.channels, &G_Mixer.mutex_plugins);
- gLog("[init] PluginHost cleaned up\n");
+ gu_log("[init] PluginHost cleaned up\n");
#endif
- gLog("[init] Giada " G_VERSION_STR " closed\n\n");
- gLog_close();
+ gu_log("[init] Giada " G_VERSION_STR " closed\n\n");
+ gu_logClose();
}
* -------------------------------------------------------------------------- */
-#include <vector>
-#include <cstring>
#include "../utils/log.h"
-#include "../glue/glue.h"
-#include "kernelAudio.h"
+#include "../glue/main.h"
+#include "conf.h"
#include "mixer.h"
+#include "const.h"
+#include "kernelAudio.h"
-#include "conf.h"
+
+extern KernelAudio G_KernelAudio;
+extern Mixer G_Mixer;
+extern Conf G_Conf;
+extern bool G_audio_status;
-extern Mixer G_Mixer;
-extern Conf G_Conf;
-extern bool G_audio_status;
+using std::string;
+using std::vector;
-namespace kernelAudio
+KernelAudio::KernelAudio()
{
-RtAudio *system = NULL;
-unsigned numDevs = 0;
-bool inputEnabled = 0;
-unsigned realBufsize = 0;
-int api = 0;
-
-int openDevice(
- int _api,
- int outDev,
- int inDev,
- int outChan,
- int inChan,
- int samplerate,
- int buffersize)
+ system = nullptr;
+ numDevs = 0;
+ inputEnabled = 0;
+ realBufsize = 0;
+ api = 0;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+
+int KernelAudio::openDevice(int _api, int outDev, int inDev, int outChan,
+ int inChan, int samplerate, int buffersize)
{
api = _api;
- gLog("[KA] using system 0x%x\n", api);
+ gu_log("[KA] using system 0x%x\n", api);
#if defined(__linux__)
if (api == SYS_API_CORE && hasAPI(RtAudio::MACOSX_CORE))
system = new RtAudio(RtAudio::MACOSX_CORE);
-
+
#endif
else {
return 0;
}
-
-
- //gLog("[KA] %d\n", sizeof(system->rtapi_));
-
- gLog("[KA] Opening devices %d (out), %d (in), f=%d...\n", outDev, inDev, samplerate);
+ gu_log("[KA] Opening devices %d (out), %d (in), f=%d...\n", outDev, inDev, samplerate);
numDevs = system->getDeviceCount();
if (numDevs < 1) {
- gLog("[KA] no devices found with this API\n");
+ gu_log("[KA] no devices found with this API\n");
closeDevice();
G_audio_status = false;
return 0;
}
else {
- gLog("[KA] %d device(s) found\n", numDevs);
+ gu_log("[KA] %d device(s) found\n", numDevs);
for (unsigned i=0; i<numDevs; i++)
- gLog(" %d) %s\n", i, getDeviceName(i).c_str());
+ gu_log(" %d) %s\n", i, getDeviceName(i).c_str());
}
-
RtAudio::StreamParameters outParams;
RtAudio::StreamParameters inParams;
outParams.deviceId = getDefaultOut();
else
outParams.deviceId = outDev;
+
outParams.nChannels = 2;
outParams.firstChannel = outChan*2; // chan 0=0, 1=2, 2=4, ...
else
inputEnabled = false;
-
RtAudio::StreamOptions options;
options.streamName = "Giada";
options.numberOfBuffers = 4;
#if defined(__linux__) || defined(__APPLE__)
if (api == SYS_API_JACK) {
samplerate = getFreq(outDev, 0);
- gLog("[KA] JACK in use, freq = %d\n", samplerate);
+ gu_log("[KA] JACK in use, freq = %d\n", samplerate);
G_Conf.samplerate = samplerate;
}
#endif
try {
system->openStream(
- &outParams, // output params
- inDev != -1 ? &inParams : NULL, // input params if inDevice is selected
- RTAUDIO_FLOAT32, // audio format
- samplerate, // sample rate
- &realBufsize, // buffer size in byte
- &G_Mixer.masterPlay, // audio callback
- NULL, // user data (unused)
+ &outParams, // output params
+ inDev != -1 ? &inParams : nullptr, // input params if inDevice is selected
+ RTAUDIO_FLOAT32, // audio format
+ samplerate, // sample rate
+ &realBufsize, // buffer size in byte
+ &G_Mixer.masterPlay, // audio callback
+ nullptr, // user data (unused)
&options);
G_audio_status = true;
return 1;
}
catch (RtAudioError &e) {
- gLog("[KA] system init error: %s\n", e.getMessage().c_str());
+ gu_log("[KA] system init error: %s\n", e.getMessage().c_str());
closeDevice();
G_audio_status = false;
return 0;
/* -------------------------------------------------------------------------- */
-int startStream()
+int KernelAudio::startStream()
{
try {
system->startStream();
- gLog("[KA] latency = %lu\n", system->getStreamLatency());
+ gu_log("[KA] latency = %lu\n", system->getStreamLatency());
return 1;
}
catch (RtAudioError &e) {
- gLog("[KA] Start stream error: %s\n", e.getMessage().c_str());
+ gu_log("[KA] Start stream error: %s\n", e.getMessage().c_str());
return 0;
}
}
/* -------------------------------------------------------------------------- */
-int stopStream()
+int KernelAudio::stopStream()
{
try {
system->stopStream();
return 1;
}
catch (RtAudioError &e) {
- gLog("[KA] Stop stream error\n");
+ gu_log("[KA] Stop stream error\n");
return 0;
}
}
/* -------------------------------------------------------------------------- */
-string getDeviceName(unsigned dev)
+string KernelAudio::getDeviceName(unsigned dev)
{
try {
return ((RtAudio::DeviceInfo) system->getDeviceInfo(dev)).name;
}
catch (RtAudioError &e) {
- gLog("[KA] invalid device ID = %d\n", dev);
+ gu_log("[KA] invalid device ID = %d\n", dev);
return "";
}
}
/* -------------------------------------------------------------------------- */
-int closeDevice()
+int KernelAudio::closeDevice()
{
if (system->isStreamOpen()) {
#if defined(__linux__) || defined(__APPLE__)
#endif
system->closeStream();
delete system;
- system = NULL;
+ system = nullptr;
}
return 1;
}
/* -------------------------------------------------------------------------- */
-unsigned getMaxInChans(int dev)
+unsigned KernelAudio::getMaxInChans(int dev)
{
if (dev == -1) return 0;
return ((RtAudio::DeviceInfo) system->getDeviceInfo(dev)).inputChannels;
}
catch (RtAudioError &e) {
- gLog("[KA] Unable to get input channels\n");
+ gu_log("[KA] Unable to get input channels\n");
return 0;
}
}
/* -------------------------------------------------------------------------- */
-unsigned getMaxOutChans(unsigned dev)
+unsigned KernelAudio::getMaxOutChans(unsigned dev)
{
try {
return ((RtAudio::DeviceInfo) system->getDeviceInfo(dev)).outputChannels;
}
catch (RtAudioError &e) {
- gLog("[KA] Unable to get output channels\n");
+ gu_log("[KA] Unable to get output channels\n");
return 0;
}
}
/* -------------------------------------------------------------------------- */
-bool isProbed(unsigned dev)
+bool KernelAudio::isProbed(unsigned dev)
{
try {
return ((RtAudio::DeviceInfo) system->getDeviceInfo(dev)).probed;
/* -------------------------------------------------------------------------- */
-unsigned getDuplexChans(unsigned dev)
+unsigned KernelAudio::getDuplexChans(unsigned dev)
{
try {
return ((RtAudio::DeviceInfo) system->getDeviceInfo(dev)).duplexChannels;
/* -------------------------------------------------------------------------- */
-bool isDefaultIn(unsigned dev)
+bool KernelAudio::isDefaultIn(unsigned dev)
{
try {
return ((RtAudio::DeviceInfo) system->getDeviceInfo(dev)).isDefaultInput;
/* -------------------------------------------------------------------------- */
-bool isDefaultOut(unsigned dev)
+bool KernelAudio::isDefaultOut(unsigned dev)
{
try {
return ((RtAudio::DeviceInfo) system->getDeviceInfo(dev)).isDefaultOutput;
/* -------------------------------------------------------------------------- */
-int getTotalFreqs(unsigned dev)
+int KernelAudio::getTotalFreqs(unsigned dev)
{
try {
return ((RtAudio::DeviceInfo) system->getDeviceInfo(dev)).sampleRates.size();
/* -------------------------------------------------------------------------- */
-int getFreq(unsigned dev, int i)
+int KernelAudio::getFreq(unsigned dev, int i)
{
try {
return ((RtAudio::DeviceInfo) system->getDeviceInfo(dev)).sampleRates.at(i);
/* -------------------------------------------------------------------------- */
-int getDefaultIn()
+int KernelAudio::getDefaultIn()
{
return system->getDefaultInputDevice();
}
-int getDefaultOut()
+int KernelAudio::getDefaultOut()
{
return system->getDefaultOutputDevice();
}
/* -------------------------------------------------------------------------- */
-int getDeviceByName(const char *name)
+int KernelAudio::getDeviceByName(const char *name)
{
for (unsigned i=0; i<numDevs; i++)
if (name == getDeviceName(i))
/* -------------------------------------------------------------------------- */
-bool hasAPI(int API)
+bool KernelAudio::hasAPI(int API)
{
- std::vector<RtAudio::Api> APIs;
+ vector<RtAudio::Api> APIs;
RtAudio::getCompiledApi(APIs);
for (unsigned i=0; i<APIs.size(); i++)
if (APIs.at(i) == API)
/* -------------------------------------------------------------------------- */
-std::string getRtAudioVersion()
+string KernelAudio::getRtAudioVersion()
{
return RtAudio::getVersion();
}
#ifdef __linux__
-#include <jack/jack.h>
-#include <jack/intclient.h>
-#include <jack/transport.h>
-jack_client_t *jackGetHandle()
+int KernelAudio::jackSyncCb(jack_transport_state_t state, jack_position_t *pos,
+ void *arg)
+{
+ return G_KernelAudio.__jackSyncCb(state, pos, arg);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+jack_client_t *KernelAudio::jackGetHandle()
{
return (jack_client_t*) system->rtapi_->__HACK__getJackClient();
}
-void jackStart()
+
+/* -------------------------------------------------------------------------- */
+
+
+void KernelAudio::jackStart()
{
if (api == SYS_API_JACK) {
jack_client_t *client = jackGetHandle();
}
-void jackStop()
+/* -------------------------------------------------------------------------- */
+
+
+void KernelAudio::jackStop()
{
if (api == SYS_API_JACK) {
jack_client_t *client = jackGetHandle();
}
-void jackSetSyncCb()
+/* -------------------------------------------------------------------------- */
+
+
+void KernelAudio::jackSetSyncCb()
{
jack_client_t *client = jackGetHandle();
- jack_set_sync_callback(client, jackSyncCb, NULL);
+ jack_set_sync_callback(client, jackSyncCb, nullptr);
//jack_set_sync_timeout(client, 8);
}
-int jackSyncCb(jack_transport_state_t state, jack_position_t *pos,
+/* -------------------------------------------------------------------------- */
+
+
+int KernelAudio::__jackSyncCb(jack_transport_state_t state, jack_position_t *pos,
void *arg)
{
switch (state) {
case JackTransportStopped:
- gLog("[KA] Jack transport stopped, frame=%d\n", pos->frame);
+ gu_log("[KA] Jack transport stopped, frame=%d\n", pos->frame);
glue_stopSeq(false); // false = not from GUI
if (pos->frame == 0)
glue_rewindSeq();
break;
case JackTransportRolling:
- gLog("[KA] Jack transport rolling\n");
+ gu_log("[KA] Jack transport rolling\n");
break;
case JackTransportStarting:
- gLog("[KA] Jack transport starting, frame=%d\n", pos->frame);
+ gu_log("[KA] Jack transport starting, frame=%d\n", pos->frame);
glue_startSeq(false); // false = not from GUI
if (pos->frame == 0)
glue_rewindSeq();
break;
default:
- gLog("[KA] Jack transport [unknown]\n");
+ gu_log("[KA] Jack transport [unknown]\n");
}
return 1;
}
#endif
-
-}
-/* ---------------------------------------------------------------------
+/* -----------------------------------------------------------------------------
*
* Giada - Your Hardcore Loopmachine
*
* KernelAudio
*
- * ---------------------------------------------------------------------
+ * -----------------------------------------------------------------------------
*
* Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
*
* along with Giada - Your Hardcore Loopmachine. If not, see
* <http://www.gnu.org/licenses/>.
*
- * ------------------------------------------------------------------ */
+ * -------------------------------------------------------------------------- */
#ifndef KERNELAUDIO_H
#include "../deps/rtaudio-mod/RtAudio.h"
-#if defined(__linux__)
+#ifdef __linux__
#include <jack/jack.h>
#include <jack/intclient.h>
#include <jack/transport.h>
#endif
-using std::string;
+class KernelAudio
+{
+public:
+ KernelAudio();
-namespace kernelAudio {
+ int openDevice(int api, int outDev, int inDev, int outChan, int inChan,
+ int samplerate, int buffersize);
- int openDevice(
- int api,
- int outDev,
- int inDev,
- int outChan,
- int inChan,
- int samplerate,
- int buffersize);
int closeDevice();
int startStream();
bool isProbed (unsigned dev);
bool isDefaultIn (unsigned dev);
bool isDefaultOut (unsigned dev);
- string getDeviceName (unsigned dev);
+ std::string getDeviceName (unsigned dev);
unsigned getMaxInChans (int dev);
unsigned getMaxOutChans (unsigned dev);
unsigned getDuplexChans (unsigned dev);
int getDefaultOut ();
int getDefaultIn ();
bool hasAPI (int API);
- string getRtAudioVersion();
+ std::string getRtAudioVersion();
#ifdef __linux__
+
jack_client_t *jackGetHandle();
void jackStart();
void jackStop();
void jackSetSyncCb();
- int jackSyncCb(jack_transport_state_t state, jack_position_t *pos, void *arg);
+ static int jackSyncCb(jack_transport_state_t state, jack_position_t *pos, void *arg);
+ int __jackSyncCb(jack_transport_state_t state, jack_position_t *pos, void *arg);
+
#endif
- /* *** how to avoid multiple definition of ***
- * When you declare a variable in a header file, every source file that
- * includes that header, either directly or indirectly, gets its own
- * separate copy of the variable. Then when you go to link all the .o
- * files together, the linker sees that the variable is instantiated
- * in a bunch of .o files. Make it extern in the header file and
- * instantiate it in memory.cpp. */
-
- extern RtAudio *system;
- extern unsigned numDevs;
- extern bool inputEnabled;
- extern unsigned realBufsize; // reale bufsize from the soundcard
- extern int api;
-}
+ unsigned numDevs;
+ bool inputEnabled;
+ unsigned realBufsize; // reale bufsize from the soundcard
+ int api;
+
+private:
+
+ RtAudio *system;
+};
#endif
* -------------------------------------------------------------------------- */
-#include <stdio.h>
+#include <RtMidi.h>
#include "../utils/log.h"
-#include "../glue/glue.h"
-#include "kernelMidi.h"
+#include "../glue/channel.h"
+#include "../glue/main.h"
+#include "../glue/io.h"
#include "mixer.h"
+#include "const.h"
#include "channel.h"
#include "sampleChannel.h"
+#include "midiChannel.h"
#include "conf.h"
#include "midiMapConf.h"
+#include "pluginHost.h"
+#include "kernelMidi.h"
extern bool G_midiStatus;
extern Conf G_Conf;
extern Mixer G_Mixer;
+extern KernelMidi G_KernelMidi;
extern MidiMapConf G_MidiMap;
+extern PluginHost G_PluginHost;
using std::string;
+using std::vector;
+
+
+KernelMidi::KernelMidi()
+ : numOutPorts(0),
+ numInPorts (0),
+ api (0), // one api for both in & out
+ midiOut (nullptr),
+ midiIn (nullptr),
+ cb_learn (nullptr),
+ cb_data (nullptr)
+{
+}
-namespace kernelMidi
+/* -------------------------------------------------------------------------- */
+
+
+void KernelMidi::callback(double t, vector<unsigned char> *msg, void *data)
{
-int api = 0; // one api for both in & out
-RtMidiOut *midiOut = NULL;
-RtMidiIn *midiIn = NULL;
-unsigned numOutPorts = 0;
-unsigned numInPorts = 0;
+ G_KernelMidi.__callback(t, msg, data);
+}
+
-cb_midiLearn *cb_learn = NULL;
-void *cb_data = NULL;
+/* -------------------------------------------------------------------------- */
-void __sendMidiLightningInitMsgs__()
+void KernelMidi::sendMidiLightningInitMsgs()
{
for(unsigned i=0; i<G_MidiMap.initCommands.size(); i++) {
MidiMapConf::message_t msg = G_MidiMap.initCommands.at(i);
if (msg.value != 0x0 && msg.channel != -1) {
- gLog("[KM] MIDI send (init) - Channel %x - Event 0x%X\n", msg.channel, msg.value);
+ gu_log("[KM] MIDI send (init) - Channel %x - Event 0x%X\n", msg.channel, msg.value);
send(msg.value | MIDI_CHANS[msg.channel]);
}
}
/* -------------------------------------------------------------------------- */
-void startMidiLearn(cb_midiLearn *cb, void *data)
+void KernelMidi::startMidiLearn(cb_midiLearn *cb, void *data)
{
cb_learn = cb;
cb_data = data;
/* -------------------------------------------------------------------------- */
-void stopMidiLearn()
+void KernelMidi::stopMidiLearn()
{
- cb_learn = NULL;
- cb_data = NULL;
+ cb_learn = nullptr;
+ cb_data = nullptr;
}
/* -------------------------------------------------------------------------- */
-void setApi(int _api)
+void KernelMidi::setApi(int _api)
{
api = _api;
- gLog("[KM] using system 0x%x\n", api);
+ gu_log("[KM] using system 0x%x\n", api);
}
/* -------------------------------------------------------------------------- */
-int openOutDevice(int port)
+int KernelMidi::openOutDevice(int port)
{
try {
midiOut = new RtMidiOut((RtMidi::Api) api, "Giada MIDI Output");
G_midiStatus = true;
}
catch (RtMidiError &error) {
- gLog("[KM] MIDI out device error: %s\n", error.getMessage().c_str());
+ gu_log("[KM] MIDI out device error: %s\n", error.getMessage().c_str());
G_midiStatus = false;
return 0;
}
/* print output ports */
numOutPorts = midiOut->getPortCount();
- gLog("[KM] %d output MIDI ports found\n", numOutPorts);
+ gu_log("[KM] %d output MIDI ports found\n", numOutPorts);
for (unsigned i=0; i<numOutPorts; i++)
- gLog(" %d) %s\n", i, getOutPortName(i).c_str());
+ gu_log(" %d) %s\n", i, getOutPortName(i).c_str());
/* try to open a port, if enabled */
if (port != -1 && numOutPorts > 0) {
try {
midiOut->openPort(port, getOutPortName(port));
- gLog("[KM] MIDI out port %d open\n", port);
+ gu_log("[KM] MIDI out port %d open\n", port);
/* TODO - it shold send midiLightning message only if there is a map loaded
and available in G_MidiMap. */
- __sendMidiLightningInitMsgs__();
+ sendMidiLightningInitMsgs();
return 1;
}
catch (RtMidiError &error) {
- gLog("[KM] unable to open MIDI out port %d: %s\n", port, error.getMessage().c_str());
+ gu_log("[KM] unable to open MIDI out port %d: %s\n", port, error.getMessage().c_str());
G_midiStatus = false;
return 0;
}
/* -------------------------------------------------------------------------- */
-int openInDevice(int port)
+int KernelMidi::openInDevice(int port)
{
try {
midiIn = new RtMidiIn((RtMidi::Api) api, "Giada MIDI input");
G_midiStatus = true;
}
catch (RtMidiError &error) {
- gLog("[KM] MIDI in device error: %s\n", error.getMessage().c_str());
+ gu_log("[KM] MIDI in device error: %s\n", error.getMessage().c_str());
G_midiStatus = false;
return 0;
}
/* print input ports */
numInPorts = midiIn->getPortCount();
- gLog("[KM] %d input MIDI ports found\n", numInPorts);
+ gu_log("[KM] %d input MIDI ports found\n", numInPorts);
for (unsigned i=0; i<numInPorts; i++)
- gLog(" %d) %s\n", i, getInPortName(i).c_str());
+ gu_log(" %d) %s\n", i, getInPortName(i).c_str());
/* try to open a port, if enabled */
try {
midiIn->openPort(port, getInPortName(port));
midiIn->ignoreTypes(true, false, true); // ignore all system/time msgs, for now
- gLog("[KM] MIDI in port %d open\n", port);
+ gu_log("[KM] MIDI in port %d open\n", port);
midiIn->setCallback(&callback);
return 1;
}
catch (RtMidiError &error) {
- gLog("[KM] unable to open MIDI in port %d: %s\n", port, error.getMessage().c_str());
+ gu_log("[KM] unable to open MIDI in port %d: %s\n", port, error.getMessage().c_str());
G_midiStatus = false;
return 0;
}
/* -------------------------------------------------------------------------- */
-bool hasAPI(int API)
+bool KernelMidi::hasAPI(int API)
{
- std::vector<RtMidi::Api> APIs;
+ vector<RtMidi::Api> APIs;
RtMidi::getCompiledApi(APIs);
for (unsigned i=0; i<APIs.size(); i++)
if (APIs.at(i) == API)
/* -------------------------------------------------------------------------- */
-string getOutPortName(unsigned p)
+string KernelMidi::getOutPortName(unsigned p)
{
try { return midiOut->getPortName(p); }
catch (RtMidiError &error) { return ""; }
}
-string getInPortName(unsigned p)
+string KernelMidi::getInPortName(unsigned p)
{
try { return midiIn->getPortName(p); }
catch (RtMidiError &error) { return ""; }
/* -------------------------------------------------------------------------- */
-void send(uint32_t data)
+void KernelMidi::send(uint32_t data)
{
if (!G_midiStatus)
return;
- std::vector<unsigned char> msg(1, getB1(data));
+ vector<unsigned char> msg(1, getB1(data));
msg.push_back(getB2(data));
msg.push_back(getB3(data));
midiOut->sendMessage(&msg);
- gLog("[KM] send msg=0x%X (%X %X %X)\n", data, msg[0], msg[1], msg[2]);
+ gu_log("[KM] send msg=0x%X (%X %X %X)\n", data, msg[0], msg[1], msg[2]);
}
/* -------------------------------------------------------------------------- */
-void send(int b1, int b2, int b3)
+void KernelMidi::send(int b1, int b2, int b3)
{
if (!G_midiStatus)
return;
- std::vector<unsigned char> msg(1, b1);
+ vector<unsigned char> msg(1, b1);
if (b2 != -1)
msg.push_back(b2);
msg.push_back(b3);
midiOut->sendMessage(&msg);
- //gLog("[KM] send msg=(%X %X %X)\n", b1, b2, b3);
+ //gu_log("[KM] send msg=(%X %X %X)\n", b1, b2, b3);
}
/* -------------------------------------------------------------------------- */
-void callback(double t, std::vector<unsigned char> *msg, void *data)
+void KernelMidi::__callback(double t, vector<unsigned char> *msg, void *data)
{
/* 0.8.0 - for now we handle other midi signals (common and real-time
* messages) as unknown, for debugging purposes */
if (msg->size() < 3) {
- gLog("[KM] MIDI received - unknown signal - size=%d, value=0x", (int) msg->size());
+ gu_log("[KM] MIDI received - unknown signal - size=%d, value=0x", (int) msg->size());
for (unsigned i=0; i<msg->size(); i++)
- gLog("%X", (int) msg->at(i));
- gLog("\n");
+ gu_log("%X", (int) msg->at(i));
+ gu_log("\n");
return;
}
uint32_t value = input & 0x0000FF00;
uint32_t pure = 0x00;
if (!G_Conf.noNoteOff)
- pure = input & 0xFFFF0000; // input without 'value' byte
+ pure = input & 0xFFFF0000; // input without 'value' byte
else
- pure = input & 0xFFFFFF00; // input with 'value' byte
+ pure = input & 0xFFFFFF00; // input with 'value' byte
- gLog("[KM] MIDI received - 0x%X (chan %d)", input, chan >> 24);
+ gu_log("[KM] MIDI received - 0x%X (chan %d)\n", input, chan >> 24);
/* start dispatcher. If midi learn is on don't parse channels, just
* learn incoming midi signal. Otherwise process master events first,
* then each channel in the stack. This way incoming signals don't
* get processed by glue_* when midi learning is on. */
- if (cb_learn) {
- gLog("\n");
+ if (cb_learn)
cb_learn(pure, cb_data);
- }
else {
+ processMaster(pure, value);
+ processChannels(pure, value);
+ }
+}
- /* process master events */
- if (pure == G_Conf.midiInRewind) {
- gLog(" >>> rewind (global) (pure=0x%X)", pure);
- glue_rewindSeq();
- }
- else if (pure == G_Conf.midiInStartStop) {
- gLog(" >>> startStop (global) (pure=0x%X)", pure);
- glue_startStopSeq();
- }
- else if (pure == G_Conf.midiInActionRec) {
- gLog(" >>> actionRec (global) (pure=0x%X)", pure);
- glue_startStopActionRec();
+/* -------------------------------------------------------------------------- */
+
+
+void KernelMidi::processMaster(uint32_t pure, uint32_t value)
+{
+ if (pure == G_Conf.midiInRewind) {
+ gu_log(" >>> rewind (master) (pure=0x%X)\n", pure);
+ glue_rewindSeq();
+ }
+ else if (pure == G_Conf.midiInStartStop) {
+ gu_log(" >>> startStop (master) (pure=0x%X)\n", pure);
+ glue_startStopSeq(false);
+ }
+ else if (pure == G_Conf.midiInActionRec) {
+ gu_log(" >>> actionRec (master) (pure=0x%X)\n", pure);
+ glue_startStopActionRec(false);
+ }
+ else if (pure == G_Conf.midiInInputRec) {
+ gu_log(" >>> inputRec (master) (pure=0x%X)\n", pure);
+ glue_startStopInputRec(false);
+ }
+ else if (pure == G_Conf.midiInMetronome) {
+ gu_log(" >>> metronome (master) (pure=0x%X)\n", pure);
+ glue_startStopMetronome(false);
+ }
+ else if (pure == G_Conf.midiInVolumeIn) {
+ float vf = (value >> 8)/127.0f;
+ gu_log(" >>> input volume (master) (pure=0x%X, value=%d, float=%f)\n",
+ pure, value >> 8, vf);
+ glue_setInVol(vf, false);
+ }
+ else if (pure == G_Conf.midiInVolumeOut) {
+ float vf = (value >> 8)/127.0f;
+ gu_log(" >>> output volume (master) (pure=0x%X, value=%d, float=%f)\n",
+ pure, value >> 8, vf);
+ glue_setOutVol(vf, false);
+ }
+ else if (pure == G_Conf.midiInBeatDouble) {
+ gu_log(" >>> sequencer x2 (master) (pure=0x%X)\n", pure);
+ glue_beatsMultiply();
+ }
+ else if (pure == G_Conf.midiInBeatHalf) {
+ gu_log(" >>> sequencer /2 (master) (pure=0x%X)\n", pure);
+ glue_beatsDivide();
+ }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void KernelMidi::processChannels(uint32_t pure, uint32_t value)
+{
+ for (unsigned i=0; i<G_Mixer.channels.size(); i++) {
+
+ 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 == G_Conf.midiInInputRec) {
- gLog(" >>> inputRec (global) (pure=0x%X)", pure);
- glue_startStopInputRec(false, false); // update gui, no popup messages
+ else if (pure == ch->midiInKeyRel) {
+ gu_log(" >>> keyRel ch=%d (pure=0x%X)\n", ch->index, pure);
+ glue_keyRelease(ch, false, false);
}
- else if (pure == G_Conf.midiInMetronome) {
- gLog(" >>> metronome (global) (pure=0x%X)", pure);
- glue_startStopMetronome(false);
+ else if (pure == ch->midiInMute) {
+ gu_log(" >>> mute ch=%d (pure=0x%X)\n", ch->index, pure);
+ glue_setMute(ch, false);
}
- else if (pure == G_Conf.midiInVolumeIn) {
- float vf = (value >> 8)/127.0f;
- gLog(" >>> input volume (global) (pure=0x%X, value=%d, float=%f)", pure, value >> 8, vf);
- glue_setInVol(vf, false);
+ else if (pure == ch->midiInSolo) {
+ gu_log(" >>> solo ch=%d (pure=0x%X)\n", ch->index, pure);
+ ch->solo ? glue_setSoloOn(ch, false) : glue_setSoloOff(ch, false);
}
- else if (pure == G_Conf.midiInVolumeOut) {
+ else if (pure == ch->midiInVolume) {
float vf = (value >> 8)/127.0f;
- gLog(" >>> output volume (global) (pure=0x%X, value=%d, float=%f)", pure, value >> 8, vf);
- glue_setOutVol(vf, false);
+ gu_log(" >>> volume ch=%d (pure=0x%X, value=%d, float=%f)\n",
+ ch->index, pure, value >> 8, vf);
+ glue_setChanVol(ch, vf, false);
}
- else if (pure == G_Conf.midiInBeatDouble) {
- gLog(" >>> sequencer x2 (global) (pure=0x%X)", pure);
- glue_beatsMultiply();
+ else if (pure == ((SampleChannel*)ch)->midiInPitch) {
+ float vf = (value >> 8)/(127/4.0f); // [0-127] ~> [0.0 4.0]
+ gu_log(" >>> pitch ch=%d (pure=0x%X, value=%d, float=%f)\n",
+ ch->index, pure, value >> 8, vf);
+ glue_setPitch(nullptr, (SampleChannel*)ch, vf, false);
}
- else if (pure == G_Conf.midiInBeatHalf) {
- gLog(" >>> sequencer /2 (global) (pure=0x%X)", pure);
- glue_beatsDivide();
+ else if (pure == ((SampleChannel*)ch)->midiInReadActions) {
+ gu_log(" >>> start/stop read actions ch=%d (pure=0x%X)\n", ch->index, pure);
+ glue_startStopReadingRecs((SampleChannel*)ch, false);
}
- /* process channels */
-
- for (unsigned i=0; i<G_Mixer.channels.size(); i++) {
-
- Channel *ch = (Channel*) G_Mixer.channels.at(i);
-
- if (!ch->midiIn) continue;
-
- if (pure == ch->midiInKeyPress) {
- gLog(" >>> keyPress, ch=%d (pure=0x%X)", ch->index, pure);
- glue_keyPress(ch, false, false);
- }
- else if (pure == ch->midiInKeyRel) {
- gLog(" >>> keyRel ch=%d (pure=0x%X)", ch->index, pure);
- glue_keyRelease(ch, false, false);
- }
- else if (pure == ch->midiInMute) {
- gLog(" >>> mute ch=%d (pure=0x%X)", ch->index, pure);
- glue_setMute(ch, false);
- }
- else if (pure == ch->midiInSolo) {
- gLog(" >>> solo ch=%d (pure=0x%X)", ch->index, pure);
- ch->solo ? glue_setSoloOn(ch, false) : glue_setSoloOff(ch, false);
- }
- else if (pure == ch->midiInVolume) {
- float vf = (value >> 8)/127.0f;
- gLog(" >>> volume ch=%d (pure=0x%X, value=%d, float=%f)", ch->index, pure, value >> 8, vf);
- glue_setChanVol(ch, vf, false);
- }
- else if (pure == ((SampleChannel*)ch)->midiInPitch) {
- float vf = (value >> 8)/(127/4.0f); // [0-127] ~> [0.0 4.0]
- gLog(" >>> pitch ch=%d (pure=0x%X, value=%d, float=%f)", ch->index, pure, value >> 8, vf);
- glue_setPitch(NULL, (SampleChannel*)ch, vf, false);
- }
- else if (pure == ((SampleChannel*)ch)->midiInReadActions) {
- gLog(" >>> start/stop read actions ch=%d (pure=0x%X)", ch->index, pure);
- glue_startStopReadingRecs((SampleChannel*)ch, false);
- }
- }
- gLog("\n");
+ /* redirect full midi message to plugins */
+
+ ch->receiveMidi(pure | value);
}
}
/* -------------------------------------------------------------------------- */
-std::string getRtMidiVersion()
+string KernelMidi::getRtMidiVersion()
{
return midiOut->getVersion();
}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-} // namespace
-/* ---------------------------------------------------------------------
+/* -----------------------------------------------------------------------------
*
* Giada - Your Hardcore Loopmachine
*
* KernelMidi
*
- * ---------------------------------------------------------------------
+ * -----------------------------------------------------------------------------
*
* Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
*
* along with Giada - Your Hardcore Loopmachine. If not, see
* <http://www.gnu.org/licenses/>.
*
- * ------------------------------------------------------------------ */
+ * -------------------------------------------------------------------------- */
#ifndef KERNELMIDI_H
#define KERNELMIDI_H
-#include <stdint.h>
-#include <RtMidi.h>
-#include "channel.h"
-
+#ifdef __APPLE__ // our compiler still doesn't know about cstdint (c++11 stuff)
+ #include <stdint.h>
+#else
+ #include <cstdint>
+#endif
+#include <string>
+#include <vector>
-using std::string;
+class KernelMidi
+{
+public:
-namespace kernelMidi {
+ unsigned numOutPorts;
+ unsigned numInPorts;
- extern int api; // one api for both in & out
- extern unsigned numOutPorts;
- extern unsigned numInPorts;
+ KernelMidi();
typedef void (cb_midiLearn) (uint32_t, void *);
- /* cb_learn
- * callback prepared by the gdMidiGrabber window and called by
- * kernelMidi. It contains things to do once the midi message has been
- * stored. */
-
- extern cb_midiLearn *cb_learn;
- extern void *cb_data;
-
void startMidiLearn(cb_midiLearn *cb, void *data);
void stopMidiLearn();
- inline int getB1(uint32_t iValue) { return (iValue >> 24) & 0xFF; }
- inline int getB2(uint32_t iValue) { return (iValue >> 16) & 0xFF; }
- inline int getB3(uint32_t iValue) { return (iValue >> 8) & 0xFF; }
+ int getB1(uint32_t iValue) { return (iValue >> 24) & 0xFF; }
+ int getB2(uint32_t iValue) { return (iValue >> 16) & 0xFF; }
+ int getB3(uint32_t iValue) { return (iValue >> 8) & 0xFF; }
- inline uint32_t getIValue(int b1, int b2, int b3) {
+ uint32_t getIValue(int b1, int b2, int b3) {
return (b1 << 24) | (b2 << 16) | (b3 << 8) | (0x00);
}
/* getIn/OutPortName
* return the name of the port 'p'. */
- string getInPortName(unsigned p);
- string getOutPortName(unsigned p);
+ std::string getInPortName(unsigned p);
+ std::string getOutPortName(unsigned p);
bool hasAPI(int API);
+ std::string getRtMidiVersion();
+
+private:
+
+ int api;
+ class RtMidiOut *midiOut;
+ class RtMidiIn *midiIn;
+
+ /* cb_learn
+ * callback prepared by the gdMidiGrabber window and called by
+ * kernelMidi. It contains things to do once the midi message has been
+ * stored. */
+
+ cb_midiLearn *cb_learn;
+ void *cb_data;
+
/* callback
* master callback for input events. */
- void callback(double t, std::vector<unsigned char> *msg, void *data);
+ 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();
+
- string getRtMidiVersion();
-}
+ void processMaster(uint32_t pure, uint32_t value);
+ void processChannels(uint32_t pure, uint32_t value);
+};
#endif
#include "patch_DEPR_.h"
#include "patch.h"
#include "conf.h"
+#include "mixer.h"
#include "kernelMidi.h"
+extern Recorder G_Recorder;
+extern KernelMidi G_KernelMidi;
+extern PluginHost G_PluginHost;
+extern Mixer G_Mixer;
+
+
MidiChannel::MidiChannel(int bufferSize, MidiMapConf *midiMapConf)
: Channel (CHANNEL_MIDI, STATUS_OFF, bufferSize, midiMapConf),
midiOut (false),
midiOutChan(MIDI_CHANS[0])
{
-#ifdef WITH_VST // init VstEvents stack
- freeVstMidiEvents(true);
-#endif
}
/* -------------------------------------------------------------------------- */
-#ifdef WITH_VST
-
-void MidiChannel::freeVstMidiEvents(bool init)
-{
- midiBuffer.clear();
-}
-
-#endif
-
-
-/* -------------------------------------------------------------------------- */
-
-
#ifdef WITH_VST
void MidiChannel::addVstMidiEvent(uint32_t msg, int localFrame)
{
juce::MidiMessage message = juce::MidiMessage(
- kernelMidi::getB1(msg),
- kernelMidi::getB2(msg),
- kernelMidi::getB3(msg));
-
+ G_KernelMidi.getB1(msg),
+ G_KernelMidi.getB2(msg),
+ G_KernelMidi.getB3(msg));
midiBuffer.addEvent(message, localFrame);
}
/* -------------------------------------------------------------------------- */
-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)
{
mute = true; // internal mute does not exist for midi (for now)
if (midiOut)
- kernelMidi::send(MIDI_ALL_NOTES_OFF);
+ G_KernelMidi.send(MIDI_ALL_NOTES_OFF);
#ifdef WITH_VST
addVstMidiEvent(MIDI_ALL_NOTES_OFF, 0);
#endif
/* -------------------------------------------------------------------------- */
-void MidiChannel::process(float *buffer)
+void MidiChannel::process(float *outBuffer, float *inBuffer)
{
#ifdef WITH_VST
pluginHost->processStack(vChan, PluginHost::CHANNEL, this);
- freeVstMidiEvents();
#endif
+ /* TODO - isn't this useful only if WITH_VST ? */
for (int j=0; j<bufferSize; j+=2) {
- buffer[j] += vChan[j] * volume; // * panLeft; future?
- buffer[j+1] += vChan[j+1] * volume; // * panRight; future?
+ outBuffer[j] += vChan[j] * volume; // * panLeft; future?
+ outBuffer[j+1] += vChan[j+1] * volume; // * panRight; future?
}
}
void MidiChannel::start(int frame, bool doQuantize, int quantize,
- bool mixerIsRunning)
+ bool mixerIsRunning, bool forceStart, bool isUserGenerated)
{
switch (status) {
case STATUS_PLAY:
{
if (status & (STATUS_PLAY | STATUS_ENDING)) {
if (midiOut)
- kernelMidi::send(MIDI_ALL_NOTES_OFF);
+ G_KernelMidi.send(MIDI_ALL_NOTES_OFF);
#ifdef WITH_VST
addVstMidiEvent(MIDI_ALL_NOTES_OFF, 0);
#endif
/* -------------------------------------------------------------------------- */
-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)
- kernelMidi::send(a->iValue | MIDI_CHANS[midiOutChan]);
+ G_KernelMidi.send(a->iValue | MIDI_CHANS[midiOutChan]);
#ifdef WITH_VST
addVstMidiEvent(a->iValue, localFrame);
{
if (status & (STATUS_PLAY | STATUS_ENDING) && !mute) {
if (midiOut)
- kernelMidi::send(data | MIDI_CHANS[midiOutChan]);
+ G_KernelMidi.send(data | MIDI_CHANS[midiOutChan]);
#ifdef WITH_VST
addVstMidiEvent(data, 0);
#endif
void MidiChannel::rewind()
{
if (midiOut)
- kernelMidi::send(MIDI_ALL_NOTES_OFF);
+ G_KernelMidi.send(MIDI_ALL_NOTES_OFF);
#ifdef WITH_VST
addVstMidiEvent(MIDI_ALL_NOTES_OFF, 0);
#endif
return 0;
}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void MidiChannel::receiveMidi(uint32_t msg)
+{
+ if (!armed)
+ return;
+ while (true) {
+ if (pthread_mutex_trylock(&G_PluginHost.mutex_midi) != 0)
+ continue;
+ gu_log("[Channel::processMidi] msg=%X\n", msg);
+#ifdef WITH_VST
+ addVstMidiEvent(msg, 0);
+#endif
+ pthread_mutex_unlock(&G_PluginHost.mutex_midi);
+ break;
+ }
+
+ if (G_Recorder.canRec(this))
+ G_Recorder.rec(index, ACTION_MIDI, G_Mixer.actualFrame, msg);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+bool MidiChannel::canInputRec()
+{
+ return false; // midi channels don't handle input audio
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void MidiChannel::clear() {}
-/* ---------------------------------------------------------------------
+/* -----------------------------------------------------------------------------
*
* Giada - Your Hardcore Loopmachine
*
* channel
*
- * ---------------------------------------------------------------------
+ * -----------------------------------------------------------------------------
*
* Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
*
* along with Giada - Your Hardcore Loopmachine. If not, see
* <http://www.gnu.org/licenses/>.
*
- * ------------------------------------------------------------------ */
+ * -------------------------------------------------------------------------- */
#ifndef MIDI_CHANNEL_H
bool midiOut; // enable midi output
uint8_t midiOutChan; // midi output channel
- void copy(const Channel *src, pthread_mutex_t *pluginMutex);
-
- void process (float *buffer);
- void start (int frame, bool doQuantize, int quantize, bool mixerIsRunning);
- void kill (int frame);
- void empty ();
- void stopBySeq (bool chansStopOnSeqHalt);
- void stop ();
- void rewind ();
- void setMute (bool internal);
- void unsetMute (bool internal);
- int readPatch_DEPR_ (const char *file, int i, class Patch_DEPR_ *patch,
- int samplerate, int rsmpQuality);
- int readPatch (const string &basePath, int i, class Patch *patch,
- pthread_mutex_t *pluginMutex, int samplerate, int rsmpQuality);
- int writePatch (int i, bool isProject, class Patch *patch);
- void quantize (int index, int localFrame, int globalFrame);
- void onZero (int frame, bool recsStopOnChanHalt);
- void onBar (int frame);
- void parseAction(recorder::action *a, int localFrame, int globalFrame,
- int quantize, bool mixerIsRunning);
-
- /* ---------------------------------------------------------------- */
+ void copy(const Channel *src, pthread_mutex_t *pluginMutex) override;
+ void clear() override;
+ void process(float *outBuffer, float *inBuffer) override;
+ void start(int frame, bool doQuantize, int quantize, bool mixerIsRunning,
+ bool forceStart, bool isUserGenerated) override;
+ void kill(int frame) override;
+ void empty() override;
+ void stopBySeq(bool chansStopOnSeqHalt) override;
+ void stop() override;
+ void rewind() override;
+ void setMute(bool internal) override;
+ void unsetMute(bool internal) override;
+ int readPatch_DEPR_(const char *file, int i, class Patch_DEPR_ *patch,
+ int samplerate, int rsmpQuality) override;
+ int readPatch(const string &basePath, int i, class Patch *patch,
+ pthread_mutex_t *pluginMutex, int samplerate, int rsmpQuality) override;
+ int writePatch(int i, bool isProject, class Patch *patch) override;
+ void quantize(int index, int localFrame, int globalFrame) override;
+ void onZero(int frame, bool recsStopOnChanHalt) override;
+ void onBar(int frame) override;
+ void parseAction(Recorder::action *a, int localFrame, int globalFrame,
+ int quantize, bool mixerIsRunning) override;
+ void receiveMidi(uint32_t msg) override;
+ bool canInputRec() override;
+
+ /* ------------------------------------------------------------------------ */
/* sendMidi
* send Midi event to the outside world. */
- void sendMidi(recorder::action *a, int localFrame);
+ void sendMidi(Recorder::action *a, int localFrame);
void sendMidi(uint32_t data);
-#ifdef WITH_VST
-
- /* freeVstMidiEvents
- * empty vstEvents structure. Init: use the method for channel
- * initialization. */
- void freeVstMidiEvents(bool init=false);
+#ifdef WITH_VST
/* addVstMidiEvent
* Add a new Midi event to the midiEvent stack fom a composite uint32_t raw
#include <dirent.h>
#include "midiMapConf.h"
#include "const.h"
-#include "../utils/utils.h"
+#include "../utils/string.h"
#include "../utils/log.h"
void MidiMapConf::init()
{
- midimapsPath = gGetHomePath() + G_SLASH + "midimaps" + G_SLASH;
+ midimapsPath = gu_getHomePath() + G_SLASH + "midimaps" + G_SLASH;
/* scan dir of midi maps and load the filenames into <>maps. */
- gLog("[MidiMapConf::init] scanning midimaps directory...\n");
+ gu_log("[MidiMapConf::init] scanning midimaps directory...\n");
DIR *dp;
dirent *ep;
dp = opendir(midimapsPath.c_str());
if (!dp) {
- gLog("[MidiMapConf::init] unable to scan midimaps directory!\n");
+ gu_log("[MidiMapConf::init] unable to scan midimaps directory!\n");
return;
}
// TODO - check if is a valid midimap file (verify headers)
- gLog("[MidiMapConf::init] found midimap '%s'\n", ep->d_name);
+ gu_log("[MidiMapConf::init] found midimap '%s'\n", ep->d_name);
maps.push_back(ep->d_name);
}
- gLog("[MidiMapConf::init] total midimaps found: %d\n", maps.size());
+ gu_log("[MidiMapConf::init] total midimaps found: %d\n", maps.size());
closedir(dp);
}
int MidiMapConf::read(const string &file)
{
if (file.empty()) {
- gLog("[MidiMapConf::read] midimap not specified, nothing to do\n");
+ gu_log("[MidiMapConf::read] midimap not specified, nothing to do\n");
return MIDIMAP_NOT_SPECIFIED;
}
- gLog("[MidiMapConf::read] reading midimap file '%s'\n", file.c_str());
+ gu_log("[MidiMapConf::read] reading midimap file '%s'\n", file.c_str());
string path = midimapsPath + file;
jRoot = json_load_file(path.c_str(), 0, &jError);
if (!jRoot) {
- gLog("[MidiMapConf::read] unreadable midimap file. Error on line %d: %s\n", jError.line, jError.text);
+ gu_log("[MidiMapConf::read] unreadable midimap file. Error on line %d: %s\n", jError.line, jError.text);
return MIDIMAP_UNREADABLE;
}
json_t *jInitCommand;
json_array_foreach(jInitCommands, commandIndex, jInitCommand) {
- string indexStr = "init command " + gItoa(commandIndex);
+ string indexStr = "init command " + gu_itoa(commandIndex);
if (!checkObject(jInitCommand, indexStr.c_str()))
return 0;
message->value = strtoul(output.c_str(), NULL, 16);
- gLog("[MidiMapConf::parse] parsed chan=%d valueStr=%s value=%#x, offset=%d\n",
+ gu_log("[MidiMapConf::parse] parsed chan=%d valueStr=%s value=%#x, offset=%d\n",
message->channel, message->valueStr.c_str(), message->value, message->offset);
}
int MidiMapConf::readMap_DEPR_(string file)
{
if (file.empty()) {
- gLog("[MidiMapConf::readMap_DEPR_] midimap not specified, nothing to do\n");
+ gu_log("[MidiMapConf::readMap_DEPR_] midimap not specified, nothing to do\n");
return MIDIMAP_NOT_SPECIFIED;
}
- gLog("[MidiMapConf::readMap_DEPR_] reading midimap file '%s'\n", file.c_str());
+ gu_log("[MidiMapConf::readMap_DEPR_] reading midimap file '%s'\n", file.c_str());
string path = midimapsPath + file;
fp = fopen(path.c_str(), "r");
if (!fp) {
- gLog("[MidiMapConf::readMap_DEPR_] unreadable midimap file\n");
+ gu_log("[MidiMapConf::readMap_DEPR_] unreadable midimap file\n");
return MIDIMAP_UNREADABLE;
}
device = getValue("device");
if (brand.empty() || device.empty()) {
- gLog("[MidiMapConf::readMap_DEPR_] invalid midimap file\n");
+ gu_log("[MidiMapConf::readMap_DEPR_] invalid midimap file\n");
return MIDIMAP_INVALID;
}
- gLog("[MidiMapConf::readMap_DEPR_] reading midimap for %s %s\n",
+ gu_log("[MidiMapConf::readMap_DEPR_] reading midimap for %s %s\n",
brand.c_str(), device.c_str());
/* parse init commands */
vector<string> ic;
- gSplit(getValue("init_commands"), ";", &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]);
- gLog("[MidiMapConf::readMap_DEPR_] init command %d - channel %d - message 0x%X\n",
+ gu_log("[MidiMapConf::readMap_DEPR_] init command %d - channel %d - message 0x%X\n",
i, init_channels[i], init_messages[i]);
/* forward compatibility */
void MidiMapConf::parse_DEPR_(string key, int *chan, uint32_t *msg, int *offset)
{
- gLog("[MidiMapConf::parse_DEPR_] command %s - ", key.c_str());
+ gu_log("[MidiMapConf::parse_DEPR_] command %s - ", key.c_str());
string value = getValue(key.c_str());
/* grab channel part, i.e. [channel]:*/
*msg = strtoul(strmsg, NULL, 16); // from string to uint32_t
- gLog("chan=%d value=%s msg=%#x, offset=%d\n", *chan, midiParts.c_str(), *msg, *offset);
+ gu_log("chan=%d value=%s msg=%#x, offset=%d\n", *chan, midiParts.c_str(), *msg, *offset);
}
#include <vector>
#include "dataStorageIni.h"
#include "dataStorageJson.h"
-#include "../utils/utils.h"
+#include "../utils/fs.h"
#if defined(__APPLE__)
#include <pwd.h>
#endif
* -------------------------------------------------------------------------- */
-#include <math.h>
#include "../utils/log.h"
-#include "../utils/gui_utils.h"
-#include "mixer.h"
-#include "init.h"
#include "wave.h"
#include "recorder.h"
#include "pluginHost.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;
/* -------------------------------------------------------------------------- */
-Mixer::~Mixer() {}
-
-
-/* -------------------------------------------------------------------------- */
-
-
#define TICKSIZE 38
docross = false;
rewindWait = false;
running = false;
+ recording = false;
ready = true;
waitRec = 0;
actualFrame = 0;
inVol = DEFAULT_IN_VOL;
peakOut = 0.0f;
peakIn = 0.0f;
- chanInput = NULL;
inputTracker = 0;
actualBeat = 0;
/** TODO - set kernelAudio::realBufsize * 2 as private member */
vChanInput = NULL;
- vChanInToOut = (float *) malloc(kernelAudio::realBufsize * 2 * sizeof(float));
+ vChanInToOut = (float *) malloc(G_KernelAudio.realBufsize * 2 * sizeof(float));
pthread_mutex_init(&mutex_recs, NULL);
pthread_mutex_init(&mutex_chans, NULL);
Channel *Mixer::addChannel(int type)
{
Channel *ch;
- int bufferSize = kernelAudio::realBufsize*2;
+ int bufferSize = G_KernelAudio.realBufsize * 2;
if (type == CHANNEL_SAMPLE)
ch = new SampleChannel(bufferSize, &G_MidiMap);
}
ch->index = getNewIndex();
- gLog("[mixer] channel index=%d added, type=%d, total=%d\n", ch->index, ch->type, channels.size());
+ gu_log("[mixer] channel index=%d added, type=%d, total=%d\n", ch->index, ch->type, channels.size());
return ch;
}
}
if (index == -1) {
- gLog("[Mixer::deleteChannel] unable to find Channel %d for deletion!\n", ch->index);
+ gu_log("[Mixer::deleteChannel] unable to find Channel %d for deletion!\n", ch->index);
return 0;
}
return 1;
}
//else
- // gLog("[mixer::deleteChannel] waiting for mutex...\n");
+ // gu_log("[mixer::deleteChannel] waiting for mutex...\n");
}
}
for (unsigned i=0; i<channels.size(); i++)
if (channels.at(i)->index == index)
return channels.at(i);
- gLog("[mixer::getChannelByIndex] channel at index %d not found!\n", index);
+ gu_log("[mixer::getChannelByIndex] channel at index %d not found!\n", index);
return NULL;
}
{
if (G_Conf.midiSync == MIDI_SYNC_CLOCK_M) {
if (actualFrame % (framesPerBeat/24) == 0)
- kernelMidi::send(MIDI_CLOCK, -1, -1);
+ G_KernelMidi.send(MIDI_CLOCK, -1, -1);
}
else
if (G_Conf.midiSync == MIDI_SYNC_MTC_M) {
* seconds high nibble */
if (midiTCframes % 2 == 0) {
- kernelMidi::send(MIDI_MTC_QUARTER, (midiTCframes & 0x0F) | 0x00, -1);
- kernelMidi::send(MIDI_MTC_QUARTER, (midiTCframes >> 4) | 0x10, -1);
- kernelMidi::send(MIDI_MTC_QUARTER, (midiTCseconds & 0x0F) | 0x20, -1);
- kernelMidi::send(MIDI_MTC_QUARTER, (midiTCseconds >> 4) | 0x30, -1);
+ G_KernelMidi.send(MIDI_MTC_QUARTER, (midiTCframes & 0x0F) | 0x00, -1);
+ G_KernelMidi.send(MIDI_MTC_QUARTER, (midiTCframes >> 4) | 0x10, -1);
+ G_KernelMidi.send(MIDI_MTC_QUARTER, (midiTCseconds & 0x0F) | 0x20, -1);
+ G_KernelMidi.send(MIDI_MTC_QUARTER, (midiTCseconds >> 4) | 0x30, -1);
}
/* minutes low nibble
* hours high nibble SMPTE frame rate */
else {
- kernelMidi::send(MIDI_MTC_QUARTER, (midiTCminutes & 0x0F) | 0x40, -1);
- kernelMidi::send(MIDI_MTC_QUARTER, (midiTCminutes >> 4) | 0x50, -1);
- kernelMidi::send(MIDI_MTC_QUARTER, (midiTChours & 0x0F) | 0x60, -1);
- kernelMidi::send(MIDI_MTC_QUARTER, (midiTChours >> 4) | 0x70, -1);
+ G_KernelMidi.send(MIDI_MTC_QUARTER, (midiTCminutes & 0x0F) | 0x40, -1);
+ G_KernelMidi.send(MIDI_MTC_QUARTER, (midiTCminutes >> 4) | 0x50, -1);
+ G_KernelMidi.send(MIDI_MTC_QUARTER, (midiTChours & 0x0F) | 0x60, -1);
+ G_KernelMidi.send(MIDI_MTC_QUARTER, (midiTChours >> 4) | 0x70, -1);
}
midiTCframes++;
midiTCminutes = 0;
}
}
- //gLog("%d:%d:%d:%d\n", midiTChours, midiTCminutes, midiTCseconds, midiTCframes);
+ //gu_log("%d:%d:%d:%d\n", midiTChours, midiTCminutes, midiTCseconds, midiTCframes);
}
}
}
* SMPTE time in one message */
if (G_Conf.midiSync == MIDI_SYNC_MTC_M) {
- kernelMidi::send(MIDI_SYSEX, 0x7F, 0x00); // send msg on channel 0
- kernelMidi::send(0x01, 0x01, 0x00); // hours 0
- kernelMidi::send(0x00, 0x00, 0x00); // mins, secs, frames 0
- kernelMidi::send(MIDI_EOX, -1, -1); // end of sysex
+ G_KernelMidi.send(MIDI_SYSEX, 0x7F, 0x00); // send msg on channel 0
+ G_KernelMidi.send(0x01, 0x01, 0x00); // hours 0
+ G_KernelMidi.send(0x00, 0x00, 0x00); // mins, secs, frames 0
+ G_KernelMidi.send(MIDI_EOX, -1, -1); // end of sysex
}
}
/* -------------------------------------------------------------------------- */
-int Mixer::masterPlay(
- void *out_buf, void *in_buf, unsigned n_frames,
+int Mixer::masterPlay(void *outBuf, void *inBuf, unsigned bufferSize,
double streamTime, RtAudioStreamStatus status, void *userData)
{
- return G_Mixer.__masterPlay(out_buf, in_buf, n_frames);
+ return G_Mixer.__masterPlay(outBuf, inBuf, bufferSize);
}
/* -------------------------------------------------------------------------- */
-int Mixer::__masterPlay(void *out_buf, void *in_buf, unsigned bufferFrames)
+int Mixer::__masterPlay(void *_outBuf, void *_inBuf, unsigned bufferSize)
{
if (!ready)
return 0;
- float *outBuf = ((float *) out_buf);
- float *inBuf = ((float *) in_buf);
- bufferFrames *= 2; // stereo
+ float *outBuf = (float *) _outBuf;
+ float *inBuf = G_KernelAudio.inputEnabled ? (float *) _inBuf : nullptr;
+ bufferSize *= 2; // stereo
peakOut = 0.0f; // reset peak calculator
peakIn = 0.0f; // reset peak calculator
- /* always clean each buffer */
-
- memset(outBuf, 0, sizeof(float) * bufferFrames); // out
- memset(vChanInToOut, 0, sizeof(float) * bufferFrames); // inToOut vChan
-
- pthread_mutex_lock(&mutex_chans);
- for (unsigned i=0; i<channels.size(); i++)
- if (channels.at(i)->type == CHANNEL_SAMPLE)
- ((SampleChannel*)channels.at(i))->clear();
- pthread_mutex_unlock(&mutex_chans);
-
- for (unsigned j=0; j<bufferFrames; j+=2) {
-
- if (kernelAudio::inputEnabled) {
-
- /* input peak calculation (left chan only so far). */
-
- if (inBuf[j] * inVol > peakIn)
- peakIn = inBuf[j] * inVol;
-
- /* "hear what you're playing" - process, copy and paste the input buffer
- * onto the output buffer */
-
- if (inToOut) {
- vChanInToOut[j] = inBuf[j] * inVol;
- vChanInToOut[j+1] = inBuf[j+1] * inVol;
- }
- }
-
- /* operations to do if the sequencer is running:
- * - compute quantizer
- * - time check for LOOP_REPEAT
- * - reset loops at beat 0
- * - read recorded actions
- * - reset actualFrame */
+ clearAllBuffers(outBuf, bufferSize);
+ for (unsigned j=0; j<bufferSize; j+=2) {
+ processLineIn(inBuf, j);
if (running) {
-
- /* line in recording */
-
- if (chanInput != NULL && kernelAudio::inputEnabled) {
-
- /* 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;
- else {
- vChanInput[inputTracker] += inBuf[j] * inVol;
- vChanInput[inputTracker+1] += inBuf[j+1] * inVol;
- inputTracker += 2;
- if (inputTracker >= totalFrames)
- inputTracker = 0;
- }
- }
-
- /* quantizer computations: quantize rewind and all channels. */
-
- if (quantize > 0 && quanto > 0) {
- if (actualFrame % (quanto) == 0) { // is quanto!
- if (rewindWait) {
- rewindWait = false;
- rewind();
- }
- pthread_mutex_lock(&mutex_chans);
- for (unsigned k=0; k<channels.size(); k++)
- channels.at(k)->quantize(k, j, actualFrame); // j == localFrame
- pthread_mutex_unlock(&mutex_chans);
- }
- }
-
- /* reset LOOP_REPEAT, if a bar has passed */
-
- if (actualFrame % framesPerBar == 0 && actualFrame != 0) {
- if (metronome)
- tickPlay = true;
-
- pthread_mutex_lock(&mutex_chans);
- for (unsigned k=0; k<channels.size(); k++)
- channels.at(k)->onBar(j);
- pthread_mutex_unlock(&mutex_chans);
- }
-
- /* reset loops on beat 0 */
-
- if (actualFrame == 0) {
- pthread_mutex_lock(&mutex_chans);
- for (unsigned k=0; k<channels.size(); k++)
- channels.at(k)->onZero(j, G_Conf.recsStopOnChanHalt);
- pthread_mutex_unlock(&mutex_chans);
- }
-
- /* reading all actions recorded */
-
- pthread_mutex_lock(&mutex_recs);
- for (unsigned y=0; y<recorder::frames.size(); y++) {
- if (recorder::frames.at(y) == actualFrame) {
- for (unsigned z=0; z<recorder::global.at(y).size(); z++) {
- int index = recorder::global.at(y).at(z)->chan;
- Channel *ch = getChannelByIndex(index);
- ch->parseAction(recorder::global.at(y).at(z), j, actualFrame, quantize, running);
- }
- break;
- }
- }
- pthread_mutex_unlock(&mutex_recs);
-
- /* increase actualFrame */
-
+ lineInRec(inBuf, j);
+ doQuantize(j);
+ testBar(j);
+ testFirstBeat(j);
+ readActions(j);
actualFrame += 2;
-
- /* if actualFrame > totalFrames the sequencer returns to frame 0,
- * beat 0. This must be the last operation. */
-
- if (actualFrame > totalFrames) {
- actualFrame = 0;
- actualBeat = 0;
- }
- else
- if (actualFrame % framesPerBeat == 0 && actualFrame > 0) {
- actualBeat++;
-
- /* avoid tick and tock to overlap when a new bar has passed (which
- * is also a beat) */
-
- if (metronome && !tickPlay)
- tockPlay = true;
- }
-
+ testLastBeat(); // this test must be the last one
sendMIDIsync();
-
- } // if (running)
-
- /* sum channels, CHANNEL_SAMPLE only */
-
- pthread_mutex_lock(&mutex_chans);
- for (unsigned k=0; k<channels.size(); k++) {
- if (channels.at(k)->type == CHANNEL_SAMPLE)
- ((SampleChannel*)channels.at(k))->sum(j, running);
}
- pthread_mutex_unlock(&mutex_chans);
-
- /* metronome play */
- /** FIXME - move this one after the peak meter calculation */
-
- if (tockPlay) {
- outBuf[j] += tock[tockTracker];
- outBuf[j+1] += tock[tockTracker];
- tockTracker++;
- if (tockTracker >= TICKSIZE-1) {
- tockPlay = false;
- tockTracker = 0;
- }
- }
- if (tickPlay) {
- outBuf[j] += tick[tickTracker];
- outBuf[j+1] += tick[tickTracker];
- tickTracker++;
- if (tickTracker >= TICKSIZE-1) {
- tickPlay = false;
- tickTracker = 0;
- }
- }
- } // end loop J
-
-
- /* final loop: sum virtual channels and process plugins. */
-
- pthread_mutex_lock(&mutex_chans);
- for (unsigned k=0; k<channels.size(); k++)
- channels.at(k)->process(outBuf);
- pthread_mutex_unlock(&mutex_chans);
-
- /* processing fxs master in & out, if any. */
-
-#ifdef WITH_VST
- pthread_mutex_lock(&mutex_plugins);
- G_PluginHost.processStack(outBuf, PluginHost::MASTER_OUT);
- G_PluginHost.processStack(vChanInToOut, PluginHost::MASTER_IN);
- pthread_mutex_unlock(&mutex_plugins);
-#endif
-
- /* post processing master fx + peak calculation. */
-
- for (unsigned j=0; j<bufferFrames; j+=2) {
-
- /* merging vChanInToOut, if enabled */
-
- if (inToOut) {
- outBuf[j] += vChanInToOut[j];
- outBuf[j+1] += vChanInToOut[j+1];
- }
-
- outBuf[j] *= outVol;
- outBuf[j+1] *= outVol;
-
- /* computes the peak for the left channel (so far). */
+ sumChannels(j);
+ }
- if (outBuf[j] > peakOut)
- peakOut = outBuf[j];
+ renderIO(outBuf, inBuf);
- if (G_Conf.limitOutput) {
- if (outBuf[j] > 1.0f)
- outBuf[j] = 1.0f;
- else if (outBuf[j] < -1.0f)
- outBuf[j] = -1.0f;
+ /* post processing */
- if (outBuf[j+1] > 1.0f)
- outBuf[j+1] = 1.0f;
- else if (outBuf[j+1] < -1.0f)
- outBuf[j+1] = -1.0f;
- }
+ for (unsigned j=0; j<bufferSize; j+=2) {
+ finalizeOutput(outBuf, j);
+ if (G_Conf.limitOutput)
+ limitOutput(outBuf, j);
+ computePeak(outBuf, j);
+ renderMetronome(outBuf, j);
}
return 0;
free(vChanInput);
vChanInput = (float*) malloc(totalFrames * sizeof(float));
if (!vChanInput)
- gLog("[Mixer] vChanInput realloc error!\n");
+ gu_log("[Mixer] vChanInput realloc error!\n");
}
/* -------------------------------------------------------------------------- */
-bool Mixer::mergeVirtualInput()
+void Mixer::mergeVirtualInput()
{
- if (vChanInput == NULL) {
- gLog("[Mixer] virtual input channel not alloc'd\n");
- return false;
+ 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
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void Mixer::lineInRec(float *inBuf, unsigned frame)
+{
+ if (!mh_hasArmedSampleChannels() || !G_KernelAudio.inputEnabled || !recording)
+ return;
+
+ /* Delay comp: wait until waitRec reaches delayComp. WaitRec
+ * returns to 0 in mixerHandler, as soon as the recording ends */
+
+ if (waitRec < G_Conf.delayComp) {
+ waitRec += 2;
+ return;
+ }
+
+ vChanInput[inputTracker] += inBuf[frame] * inVol;
+ vChanInput[inputTracker+1] += inBuf[frame+1] * inVol;
+ inputTracker += 2;
+ if (inputTracker >= totalFrames)
+ inputTracker = 0;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void Mixer::processLineIn(float *inBuf, unsigned frame)
+{
+ if (!G_KernelAudio.inputEnabled)
+ return;
+
+ /* input peak calculation (left chan only so far). */
+
+ if (inBuf[frame] * inVol > peakIn)
+ peakIn = inBuf[frame] * inVol;
+
+ /* "hear what you're playing" - process, copy and paste the input buffer
+ * onto the output buffer */
+
+ if (inToOut) {
+ vChanInToOut[frame] = inBuf[frame] * inVol;
+ vChanInToOut[frame+1] = inBuf[frame+1] * inVol;
+ }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void Mixer::readActions(unsigned frame)
+{
+ pthread_mutex_lock(&mutex_recs);
+ for (unsigned i=0; i<G_Recorder.frames.size(); i++) {
+ if (G_Recorder.frames.at(i) == actualFrame) {
+ 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, actualFrame, quantize, running);
+ }
+ break;
+ }
}
- else {
+ pthread_mutex_unlock(&mutex_recs);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void Mixer::doQuantize(unsigned frame)
+{
+ if (quantize < 0 || quanto <= 0) // if quantizer disabled
+ return;
+ if (actualFrame % (quanto) != 0) // if a quanto has not passed yet
+ return;
+
+ if (rewindWait) {
+ rewindWait = false;
+ rewind();
+ }
+ pthread_mutex_lock(&mutex_chans);
+ for (unsigned i=0; i<channels.size(); i++)
+ channels.at(i)->quantize(i, frame, actualFrame); // j == localFrame
+ pthread_mutex_unlock(&mutex_chans);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void Mixer::testBar(unsigned frame)
+{
+ if (actualFrame % framesPerBar != 0 || actualFrame == 0)
+ return;
+
+ if (metronome)
+ tickPlay = true;
+
+ pthread_mutex_lock(&mutex_chans);
+ for (unsigned k=0; k<channels.size(); k++)
+ channels.at(k)->onBar(frame);
+ pthread_mutex_unlock(&mutex_chans);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void Mixer::testFirstBeat(unsigned frame)
+{
+ if (actualFrame != 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);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void Mixer::testLastBeat()
+{
+ /* if actualFrame > totalFrames the sequencer returns to frame 0,
+ * beat 0. This must be the last operation. */
+
+ if (actualFrame > totalFrames) {
+ actualFrame = 0;
+ actualBeat = 0;
+ }
+ else
+ if (actualFrame % framesPerBeat == 0 && actualFrame > 0) {
+ actualBeat++;
+
+ /* avoid tick and tock to overlap when a new bar has passed (which
+ * is also a beat) */
+
+ if (metronome && !tickPlay)
+ tockPlay = true;
+ }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void Mixer::sumChannels(unsigned frame)
+{
+ pthread_mutex_lock(&mutex_chans);
+ for (unsigned k=0; k<channels.size(); k++) {
+ if (channels.at(k)->type == CHANNEL_SAMPLE)
+ ((SampleChannel*)channels.at(k))->sum(frame, running);
+ }
+ pthread_mutex_unlock(&mutex_chans);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void Mixer::renderMetronome(float *outBuf, unsigned frame)
+{
+ if (tockPlay) {
+ outBuf[frame] += tock[tockTracker];
+ outBuf[frame+1] += tock[tockTracker];
+ tockTracker++;
+ if (tockTracker >= TICKSIZE-1) {
+ tockPlay = false;
+ tockTracker = 0;
+ }
+ }
+ if (tickPlay) {
+ outBuf[frame] += tick[tickTracker];
+ outBuf[frame+1] += tick[tickTracker];
+ tickTracker++;
+ if (tickTracker >= TICKSIZE-1) {
+ tickPlay = false;
+ tickTracker = 0;
+ }
+ }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void Mixer::renderIO(float *outBuf, float *inBuf)
+{
+ pthread_mutex_lock(&mutex_chans);
+ for (unsigned k=0; k<channels.size(); k++)
+ channels.at(k)->process(outBuf, inBuf);
+ pthread_mutex_unlock(&mutex_chans);
+
#ifdef WITH_VST
- G_PluginHost.processStackOffline(vChanInput, PluginHost::MASTER_IN, 0, totalFrames);
+ pthread_mutex_lock(&mutex_plugins);
+ G_PluginHost.processStack(outBuf, PluginHost::MASTER_OUT);
+ G_PluginHost.processStack(vChanInToOut, PluginHost::MASTER_IN);
+ pthread_mutex_unlock(&mutex_plugins);
#endif
- int numFrames = totalFrames*sizeof(float);
- memcpy(chanInput->wave->data, vChanInput, numFrames);
- memset(vChanInput, 0, numFrames); // clear vchan
- return true;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void Mixer::computePeak(float *outBuf, unsigned frame)
+{
+ /* TODO it takes into account only left channel so far! */
+ if (outBuf[frame] > peakOut)
+ peakOut = outBuf[frame];
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void Mixer::limitOutput(float *outBuf, unsigned frame)
+{
+ if (outBuf[frame] > 1.0f)
+ outBuf[frame] = 1.0f;
+ else
+ if (outBuf[frame] < -1.0f)
+ outBuf[frame] = -1.0f;
+
+ if (outBuf[frame+1] > 1.0f)
+ outBuf[frame+1] = 1.0f;
+ else
+ if (outBuf[frame+1] < -1.0f)
+ outBuf[frame+1] = -1.0f;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void Mixer::finalizeOutput(float *outBuf, unsigned frame)
+{
+ /* merge vChanInToOut, if enabled */
+
+ if (inToOut) {
+ outBuf[frame] += vChanInToOut[frame];
+ outBuf[frame+1] += vChanInToOut[frame+1];
}
+ outBuf[frame] *= outVol;
+ outBuf[frame+1] *= outVol;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void Mixer::clearAllBuffers(float *outBuf, unsigned bufferSize)
+{
+ memset(outBuf, 0, sizeof(float) * bufferSize); // out
+ memset(vChanInToOut, 0, sizeof(float) * bufferSize); // inToOut vChan
+
+ pthread_mutex_lock(&mutex_chans);
+ for (unsigned i=0; i<channels.size(); i++)
+ channels.at(i)->clear();
+ pthread_mutex_unlock(&mutex_chans);
}
#ifndef MIXER_H
#define MIXER_H
-#include <stdlib.h>
+
#include <pthread.h>
#include <vector>
-#include "const.h"
#include "kernelAudio.h"
-#include "../utils/utils.h"
-
-
-using std::vector;
class Mixer
public:
Mixer();
- ~Mixer();
void init();
int close();
/* masterPlay
* core method (callback) */
- static int masterPlay(
- void *out_buf, void *in_buf, unsigned n_frames,
- double streamTime, RtAudioStreamStatus status, void *userData
- );
- int __masterPlay(void *out_buf, void *in_buf, unsigned n_frames);
+ static int masterPlay(void *outBuf, void *inBuf, unsigned bufferSize,
+ double streamTime, RtAudioStreamStatus status, void *userData);
+ int __masterPlay(void *outBuf, void *inBuf, unsigned bufferSize);
/* updateFrameBars
* updates bpm, frames, beats and so on. */
* memcpy the virtual channel input in the channel designed for input
* recording. Called by mixerHandler on stopInputRec() */
- bool mergeVirtualInput();
+ void mergeVirtualInput();
/* getChannelByIndex
* return channel with given index 'i'. */
XFADE = 0x02
};
- vector<class Channel*> channels;
+ 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 tickTracker, tockTracker;
bool tickPlay, tockPlay; // 1 = play, 0 = stop
- /* chanInput
- * the active channel during a recording. NULL = no channels active */
-
- class SampleChannel *chanInput;
-
/* inputTracker
* position of the sample in the input side (recording) */
* the slave */
void sendMIDIrewind();
+
+ /* lineInRec
+ Records from line in. */
+
+ void lineInRec(float *inBuf, unsigned frame);
+
+ /* ProcessLineIn
+ Computes line in peaks, plus handles "hear what you're playin'" thing. */
+
+ void processLineIn(float *inBuf, unsigned frame);
+
+ /* clearAllBuffers
+ Cleans up every buffer, both in Mixer and in channels. */
+
+ void clearAllBuffers(float *outBuf, unsigned bufferSize);
+
+ /* readActions
+ Reads all recorded actions. */
+
+ void readActions(unsigned frame);
+
+ /* doQuantize
+ Computes quantization on 'rewind' button and all channels. */
+
+ void doQuantize(unsigned frame);
+
+ /* sumChannels
+ Sums channels, i.e. lets them add sample frames to their virtual channels.
+ This is required for CHANNEL_SAMPLE only */
+
+ void sumChannels(unsigned frame);
+
+ /* renderMetronome
+ Generates metronome when needed and pastes it to the output buffer. */
+
+ void renderMetronome(float *outBuf, unsigned frame);
+
+ /* renderIO
+ Final processing stage. Take each channel and process it (i.e. copy its
+ content to the output buffer). Process plugins too, if any. */
+
+ void renderIO(float *outBuf, float *inBuf);
+
+ /* limitOutput
+ Applies a very dumb hard limiter. */
+
+ void limitOutput(float *outBuf, unsigned frame);
+
+ /* computePeak */
+
+ void computePeak(float *outBuf, unsigned frame);
+
+ /* finalizeOutput
+ Last touches after the output has been rendered: apply inToOut if any, apply
+ output volume. */
+
+ void finalizeOutput(float *outBuf, unsigned frame);
+
+ /* test*
+ Checks if the sequencer has reached a specific point (bar, first beat or
+ last frame). */
+
+ void testBar(unsigned frame);
+ void testFirstBeat(unsigned frame);
+ void testLastBeat();
};
#endif
#endif
#include <vector>
-#include "../utils/utils.h"
+#include "../utils/fs.h"
+#include "../utils/string.h"
#include "../utils/log.h"
-#include "../glue/glue.h"
+#include "../glue/main.h"
#include "../glue/channel.h"
#include "mixerHandler.h"
#include "kernelMidi.h"
/* -------------------------------------------------------------------------- */
-SampleChannel *mh_startInputRec()
+bool mh_startInputRec()
{
- /* search for the next available channel */
+ int channelsReady = 0;
- SampleChannel *chan = NULL;
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))->canInputRec()) {
- chan = (SampleChannel*) G_Mixer.channels.at(i);
- break;
- }
- }
- /* no chans available? */
+ if (!G_Mixer.channels.at(i)->canInputRec())
+ continue;
- if (chan == NULL)
- return NULL;
+ SampleChannel *ch = (SampleChannel*) G_Mixer.channels.at(i);
- Wave *w = new Wave();
- if (!w->allocEmpty(G_Mixer.totalFrames, G_Conf.samplerate))
- return NULL;
+ /* Allocate empty sample for the current channel. */
+
+ if (!ch->allocEmpty(G_Mixer.totalFrames, G_Conf.samplerate, G_Patch.lastTakeId))
+ {
+ gu_log("[mh_startInputRec] unable to allocate new Wave in chan %d!\n",
+ ch->index);
+ continue;
+ }
+
+ /* Increase lastTakeId until the sample name TAKE-[n] is unique */
+
+ while (!mh_uniqueSampleName(ch, ch->wave->name)) {
+ G_Patch_DEPR_.lastTakeId++;
+ G_Patch.lastTakeId++;
+ ch->wave->name = "TAKE-" + gu_itoa(G_Patch.lastTakeId);
+ }
- /* increase lastTakeId until the sample name TAKE-[n] is unique */
+ gu_log("[mh_startInputRec] start input recs using chan %d with size %d "
+ "frame=%d\n", ch->index, G_Mixer.totalFrames, G_Mixer.inputTracker);
- char name[32];
- sprintf(name, "TAKE-%d", G_Patch_DEPR_.lastTakeId);
- while (!mh_uniqueSamplename(chan, name)) {
- G_Patch_DEPR_.lastTakeId++;
- G_Patch.lastTakeId++;
- sprintf(name, "TAKE-%d", G_Patch_DEPR_.lastTakeId);
+ channelsReady++;
}
- chan->allocEmpty(G_Mixer.totalFrames, G_Conf.samplerate, G_Patch_DEPR_.lastTakeId);
- G_Mixer.chanInput = chan;
+ if (channelsReady > 0) {
+ G_Mixer.recording = true;
+ /* start to write from the actualFrame, not the beginning */
+ /** FIXME: this should be done before wave allocation */
+ G_Mixer.inputTracker = G_Mixer.actualFrame;
+ return true;
+ }
+ return false;
+}
- /* start to write from the actualFrame, not the beginning */
- /** FIXME: move this before wave allocation*/
- G_Mixer.inputTracker = G_Mixer.actualFrame;
+/* -------------------------------------------------------------------------- */
- gLog(
- "[mh] start input recs using chan %d with size %d, frame=%d\n",
- chan->index, G_Mixer.totalFrames, G_Mixer.inputTracker
- );
- return chan;
+void mh_stopInputRec()
+{
+ G_Mixer.mergeVirtualInput();
+ G_Mixer.recording = false;
+ G_Mixer.waitRec = 0; // in case delay compensation is in use
+ gu_log("[mh] stop input recs\n");
}
/* -------------------------------------------------------------------------- */
-SampleChannel *mh_stopInputRec()
+bool mh_hasArmedSampleChannels()
{
- gLog("[mh] stop input recs\n");
- G_Mixer.mergeVirtualInput();
- SampleChannel *ch = G_Mixer.chanInput;
- G_Mixer.chanInput = NULL;
- G_Mixer.waitRec = 0; // if delay compensation is in use
- return ch;
+ for (unsigned i=0; i<G_Mixer.channels.size(); i++) {
+ Channel *ch = G_Mixer.channels.at(i);
+ if (ch->type == CHANNEL_SAMPLE && ch->armed)
+ return true;
+ }
+ return false;
}
/* -------------------------------------------------------------------------- */
-bool mh_uniqueSamplename(SampleChannel *ch, const char *name)
+bool mh_uniqueSampleName(SampleChannel *ch, const string &name)
{
for (unsigned i=0; i<G_Mixer.channels.size(); i++) {
- if (ch != G_Mixer.channels.at(i)) {
- if (G_Mixer.channels.at(i)->type == CHANNEL_SAMPLE) {
- SampleChannel *other = (SampleChannel*) G_Mixer.channels.at(i);
- if (other->wave != NULL)
- if (!strcmp(name, other->wave->name.c_str()))
- return false;
- }
- }
+ if (ch == G_Mixer.channels.at(i)) // skip itself
+ continue;
+ if (G_Mixer.channels.at(i)->type != CHANNEL_SAMPLE)
+ continue;
+ SampleChannel *other = (SampleChannel*) G_Mixer.channels.at(i);
+ if (other->wave != NULL && name == other->wave->name)
+ return false;
}
return true;
}
#define MIXERHANDLER_H
-#include <vector>
-#include "recorder.h"
-#include "patch.h"
-
-
-using std::vector;
+#include <string>
/* stopSequencer
* the chan number chosen, otherwise -1 if there are no more empty
* channels available. */
-SampleChannel *mh_startInputRec();
+bool mh_startInputRec();
-SampleChannel *mh_stopInputRec();
+void mh_stopInputRec();
/* uniqueSamplename
* return true if samplename 'n' is unique. Requires SampleChannel *ch
* in order to skip check against itself. */
-bool mh_uniqueSamplename(class SampleChannel *ch, const char *name);
+bool mh_uniqueSampleName(class SampleChannel *ch, const std::string &s);
+
+/* hasArmedSampleChannels
+Tells whether Mixer has one or more sample channels armed for input
+recording. */
+
+bool mh_hasArmedSampleChannels();
#endif
* -------------------------------------------------------------------------- */
-#include <stdint.h>
#include "../utils/log.h"
-#include "../utils/utils.h"
-#include "../gui/dialogs/gd_mainWindow.h"
-#include "../gui/elems/ge_keyboard.h"
-#include "patch.h"
+#include "../utils/string.h"
#include "const.h"
-#include "init.h"
-#include "recorder.h"
#include "conf.h"
-#include "wave.h"
#include "mixer.h"
-#include "channel.h"
+#include "patch.h"
-extern Mixer G_Mixer;
-extern Conf G_Conf;
-extern gdMainWindow *mainWin;
+extern Mixer G_Mixer;
+extern Conf G_Conf;
void Patch::init()
#endif
if (json_dump_file(jRoot, file.c_str(), JSON_COMPACT) != 0) {
- gLog("[Patch::write] unable to write patch file!\n");
+ gu_log("[Patch::write] unable to write patch file!\n");
return 0;
}
return 1;
{
jRoot = json_load_file(file.c_str(), 0, &jError);
if (!jRoot) {
- gLog("[Patch::read] unable to read patch file! Error on line %d: %s\n", jError.line, jError.text);
+ gu_log("[Patch::read] unable to read patch file! Error on line %d: %s\n", jError.line, jError.text);
return PATCH_UNREADABLE;
}
json_object_set_new(jChannel, PATCH_KEY_CHANNEL_MIDI_IN_KEYPRESS, json_integer(channel.midiInKeyPress));
json_object_set_new(jChannel, PATCH_KEY_CHANNEL_MIDI_IN_KEYREL, json_integer(channel.midiInKeyRel));
json_object_set_new(jChannel, PATCH_KEY_CHANNEL_MIDI_IN_KILL, json_integer(channel.midiInKill));
+ json_object_set_new(jChannel, PATCH_KEY_CHANNEL_MIDI_IN_ARM, json_integer(channel.midiInArm));
json_object_set_new(jChannel, PATCH_KEY_CHANNEL_MIDI_IN_VOLUME, json_integer(channel.midiInVolume));
json_object_set_new(jChannel, PATCH_KEY_CHANNEL_MIDI_IN_MUTE, json_integer(channel.midiInMute));
json_object_set_new(jChannel, PATCH_KEY_CHANNEL_MIDI_IN_SOLO, json_integer(channel.midiInSolo));
json_t *jColumn;
json_array_foreach(jColumns, columnIndex, jColumn) {
- string columnIndexStr = "column " + gItoa(columnIndex);
+ string columnIndexStr = "column " + gu_itoa(columnIndex);
if (!checkObject(jColumn, columnIndexStr.c_str()))
return 0;
json_t *jChannel;
json_array_foreach(jChannels, channelIndex, jChannel) {
- string channelIndexStr = "channel " + gItoa(channelIndex);
+ string channelIndexStr = "channel " + gu_itoa(channelIndex);
if (!checkObject(jChannel, channelIndexStr.c_str()))
return 0;
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;
#include <string>
#include <vector>
#include <stdint.h>
-#include "../utils/utils.h"
#include "dataStorageJson.h"
-#include "const.h"
using std::string;
uint32_t midiInKeyPress;
uint32_t midiInKeyRel;
uint32_t midiInKill;
+ uint32_t midiInArm;
uint32_t midiInVolume;
uint32_t midiInMute;
uint32_t midiInSolo;
#include <stdint.h>
#include "../utils/log.h"
-#include "../utils/utils.h"
+#include "../utils/fs.h"
#include "../gui/dialogs/gd_mainWindow.h"
#include "../gui/elems/ge_keyboard.h"
#include "patch_DEPR_.h"
#include "channel.h"
-extern Mixer G_Mixer;
-extern Conf G_Conf;
+extern Mixer G_Mixer;
+extern Conf G_Conf;
+extern Recorder G_Recorder;
#ifdef WITH_VST
extern PluginHost G_PluginHost;
#endif
return PATCH_INVALID;
version = atof(getValue("versionf").c_str());
- gLog("[patch_DEPR_] open patch version %f\n", version);
+ gu_log("[patch_DEPR_] open patch version %f\n", version);
return PATCH_READ_OK;
}
int Patch_DEPR_::readRecs()
{
- gLog("[patch_DEPR_] Reading recs...\n");
+ gu_log("[patch_DEPR_] Reading recs...\n");
unsigned numrecs = atoi(getValue("numrecs").c_str());
sprintf(tmpbuf, "recframe%d", i);
sscanf(getValue(tmpbuf).c_str(), "%d %d", &frame, &recPerFrame);
-//gLog("processing frame=%d, recPerFrame=%d\n", frame, recPerFrame);
+//gu_log("processing frame=%d, recPerFrame=%d\n", frame, recPerFrame);
for (int k=0; k<recPerFrame; k++) {
int chan = 0;
else
sscanf(getValue(tmpbuf).c_str(), "%d|%d|%f|%u", &chan, &type, &fValue, &iValue);
-//gLog(" loading chan=%d, type=%d, fValue=%f, iValue=%u\n", 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)
- recorder::rec(ch->index, type, frame, iValue_fix, fValue);
+ G_Recorder.rec(ch->index, type, frame, iValue_fix, fValue);
else
- recorder::rec(ch->index, type, frame, iValue, fValue);
+ G_Recorder.rec(ch->index, type, frame, iValue, fValue);
}
}
}
#ifdef WITH_VST
int Patch_DEPR_::readPlugins()
{
- gLog("[patch_DEPR_] Reading plugins...\n");
+ gu_log("[patch_DEPR_] Reading plugins...\n");
int globalOut = 1;
status = 1;
if (getActiveEditor() != NULL) {
- gLog("[Plugin::init] plugin has an already active editor!\n");
+ gu_log("[Plugin::init] plugin has an already active editor!\n");
return;
}
ui = createEditorIfNeeded();
if (ui == NULL) {
- gLog("[Plugin::init] unable to create editor, the plugin might be GUI-less!\n");
+ gu_log("[Plugin::init] unable to create editor, the plugin might be GUI-less!\n");
return;
}
- gLog("[Plugin::init] editor initialized and ready\n");
+ gu_log("[Plugin::init] editor initialized and ready\n");
}
void Plugin::showEditor(void *parent)
{
if (ui == NULL) {
- gLog("[Plugin::showEditor] can't show editor!\n");
+ gu_log("[Plugin::showEditor] can't show editor!\n");
return;
}
ui->setOpaque(true);
#include "../utils/log.h"
-#include "../utils/utils.h"
-#include "mixer.h"
+#include "const.h"
#include "channel.h"
-#include "midiChannel.h"
#include "plugin.h"
#include "pluginHost.h"
void PluginHost::init(int _buffersize, int _samplerate)
{
- gLog("[PluginHost::init] initialize with buffersize=%d, samplerate=%d\n",
+ gu_log("[PluginHost::init] initialize with buffersize=%d, samplerate=%d\n",
_buffersize, _samplerate);
messageManager = juce::MessageManager::getInstance();
buffersize = _buffersize;
missingPlugins = false;
//unknownPluginList.empty();
- loadList(gGetHomePath() + G_SLASH + "plugins.xml");
+ loadList(gu_getHomePath() + G_SLASH + "plugins.xml");
+
+ pthread_mutex_init(&mutex_midi, NULL);
}
int PluginHost::scanDir(const string &dirpath, void (*callback)(float progress, void *p),
void *p)
{
- gLog("[PluginHost::scanDir] requested directory: '%s'\n", dirpath.c_str());
- gLog("[PluginHost::scanDir] current plugins: %d\n", knownPluginList.getNumTypes());
+ gu_log("[PluginHost::scanDir] requested directory: '%s'\n", dirpath.c_str());
+ gu_log("[PluginHost::scanDir] current plugins: %d\n", knownPluginList.getNumTypes());
knownPluginList.clear(); // clear up previous plugins
bool cont = true;
juce::String name;
while (cont) {
- gLog("[PluginHost::scanDir] scanning '%s'\n", name.toRawUTF8());
+ gu_log("[PluginHost::scanDir] scanning '%s'\n", name.toRawUTF8());
cont = scanner.scanNextFile(false, name);
if (callback)
callback(scanner.getProgress(), p);
}
- gLog("[PluginHost::scanDir] %d plugin(s) found\n", knownPluginList.getNumTypes());
+ gu_log("[PluginHost::scanDir] %d plugin(s) found\n", knownPluginList.getNumTypes());
return knownPluginList.getNumTypes();
}
{
int out = knownPluginList.createXml()->writeToFile(juce::File(filepath), "");
if (!out)
- gLog("[PluginHost::saveList] unable to save plugin list to %s\n", filepath.c_str());
+ gu_log("[PluginHost::saveList] unable to save plugin list to %s\n", filepath.c_str());
return out;
}
juce::PluginDescription *pd = knownPluginList.getTypeForFile(fid);
if (!pd) {
- gLog("[PluginHost::addPlugin] no plugin found with fid=%s!\n", fid.c_str());
+ gu_log("[PluginHost::addPlugin] no plugin found with fid=%s!\n", fid.c_str());
missingPlugins = true;
unknownPluginList.push_back(fid);
return NULL;
Plugin *p = (Plugin *) pluginFormat.createInstanceFromDescription(*pd, samplerate, buffersize);
if (!p) {
- gLog("[PluginHost::addPlugin] unable to create instance with fid=%s!\n", fid.c_str());
+ gu_log("[PluginHost::addPlugin] unable to create instance with fid=%s!\n", fid.c_str());
missingPlugins = true;
return NULL;
}
p->init();
p->prepareToPlay(samplerate, buffersize);
- gLog("[PluginHost::addPlugin] plugin instance with fid=%s created\n", fid.c_str());
+ gu_log("[PluginHost::addPlugin] plugin instance with fid=%s created\n", fid.c_str());
/* Try to inject the plugin as soon as possible. */
}
}
- gLog("[PluginHost::addPlugin] plugin id=%s loaded (%s), stack type=%d, stack size=%d\n",
+ gu_log("[PluginHost::addPlugin] plugin id=%s loaded (%s), stack type=%d, stack size=%d\n",
fid.c_str(), p->getName().toStdString().c_str(), stackType, pStack->size());
return p;
{
juce::PluginDescription *pd = knownPluginList.getType(index);
if (pd) {
- gLog("[PluginHost::addPlugin] plugin found, uid=%s, name=%s...\n",
+ gu_log("[PluginHost::addPlugin] plugin found, uid=%s, name=%s...\n",
pd->fileOrIdentifier.toStdString().c_str(), pd->name.toStdString().c_str());
return addPlugin(pd->fileOrIdentifier.toStdString(), stackType, mutex, ch);
}
else {
- gLog("[PluginHost::addPlugin] no plugins found at index=%d!\n", index);
+ gu_log("[PluginHost::addPlugin] no plugins found at index=%d!\n", index);
return NULL;
}
}
pi.isInstrument = pd->isInstrument;
/*
if (!p) {
- gLog("[PluginHost::getAvailablePlugin] unable to create plugin instance!\n");
+ gu_log("[PluginHost::getAvailablePlugin] unable to create plugin instance!\n");
return NULL;
}
*/
break;
}
}
- gLog("[PluginHost::freeStack] stack type=%d freed\n", stackType);
+ gu_log("[PluginHost::freeStack] stack type=%d freed\n", stackType);
}
audioBuffer.setSample(1, i, buffer[(i*2)+1]);
}
- /* hardcore processing. At the end we swap input and output, so that
- * the N-th plugin will process the result of the plugin N-1. */
+ /* Hardcore processing. At the end we swap input and output, so that he N-th
+ plugin will process the result of the plugin N-1. Part of this loop must be
+ guarded by mutexes, i.e. the MIDI process part. You definitely don't want
+ a situation like the following one:
+ processBlock(...)
+ [a new midi event from kernelMidi thread]
+ clearMidiBuffer()
+ The midi event in between would be surely lost, deleted by clearMidiBuffer! */
for (unsigned i=0; i<pStack->size(); i++) {
Plugin *plugin = pStack->at(i);
if (plugin->getStatus() != 1 || plugin->isSuspended() || plugin->isBypassed())
continue;
- juce::MidiBuffer midiBuffer;
- if (ch) // ch might be null if stackType is MASTER_IN or MASTER_OUT
- midiBuffer = ch->getPluginMidiEvents();
- plugin->processBlock(audioBuffer, midiBuffer);
+ if (ch) { // ch might be null if stackType is MASTER_IN/OUT
+ pthread_mutex_lock(&mutex_midi);
+ plugin->processBlock(audioBuffer, ch->getPluginMidiEvents());
+ ch->clearMidiBuffer();
+ pthread_mutex_unlock(&mutex_midi);
+ }
+ else {
+ juce::MidiBuffer midiBuffer; // empty buffer
+ plugin->processBlock(audioBuffer, midiBuffer);
+ }
}
/* converting buffer from Juce to Giada. A note for the future: if we
if (lockStatus == 0) {
std::swap(pStack->at(indexA), pStack->at(indexB));
pthread_mutex_unlock(mutex);
- gLog("[pluginHost::swapPlugin] plugin at index %d and %d swapped\n", indexA, indexB);
+ gu_log("[pluginHost::swapPlugin] plugin at index %d and %d swapped\n", indexA, indexB);
return;
}
//else
- //gLog("[pluginHost] waiting for mutex...\n");
+ //gu_log("[pluginHost] waiting for mutex...\n");
}
}
{
vector <Plugin *> *pStack = getStack(stackType, ch);
- /* try to delete the plugin until succeed. G_Mixer has priority. */
-
for (unsigned i=0; i<pStack->size(); i++) {
Plugin *pPlugin = pStack->at(i);
- if (pPlugin->getId() == id) {
- if (pPlugin->getStatus() == 0) { // no frills if plugin is missing
- delete pPlugin;
- pStack->erase(pStack->begin() + i);
- gLog("[pluginHost::freePlugin] plugin id=%d removed with no frills, since it had status=0\n", id);
- return;
- }
- else {
- int lockStatus;
- while (true) {
- lockStatus = pthread_mutex_trylock(mutex);
- if (lockStatus == 0) {
- pPlugin->suspendProcessing(true);
- pPlugin->releaseResources();
- delete pPlugin;
- pStack->erase(pStack->begin() + i);
- pthread_mutex_unlock(mutex);
- gLog("[pluginHost::freePlugin] plugin id=%d removed\n", id);
- return;
- }
- //else
- //gLog("[pluginHost] waiting for mutex...\n");
- }
- }
+ if (pPlugin->getId() != id)
+ continue;
+ if (pPlugin->getStatus() == 0) { // no frills if plugin is missing
+ delete pPlugin;
+ pStack->erase(pStack->begin() + i);
+ gu_log("[pluginHost::freePlugin] plugin id=%d removed with no frills, since it had status=0\n", id);
+ return;
+ }
+
+ while (true) {
+ if (pthread_mutex_trylock(mutex) != 0)
+ continue;
+ pPlugin->suspendProcessing(true);
+ pPlugin->releaseResources();
+ delete pPlugin;
+ pStack->erase(pStack->begin() + i);
+ pthread_mutex_unlock(mutex);
+ gu_log("[pluginHost::freePlugin] plugin id=%d removed\n", id);
+ return;
}
}
- gLog("[pluginHost::freePlugin] plugin id=%d not found\n", id);
+ gu_log("[pluginHost::freePlugin] plugin id=%d not found\n", id);
}
void PluginHost::runDispatchLoop()
{
messageManager->runDispatchLoopUntil(10);
- //gLog("[PluginHost::runDispatchLoop] %d, hasStopMessageBeenSent=%d\n", r, messageManager->hasStopMessageBeenSent());
+ //gu_log("[PluginHost::runDispatchLoop] %d, hasStopMessageBeenSent=%d\n", r, messageManager->hasStopMessageBeenSent());
}
juce::PluginDescription pd = src->getPluginDescription();
Plugin *p = addPlugin(pd.fileOrIdentifier.toStdString(), stackType, mutex, ch);
if (!p) {
- gLog("[PluginHost::clonePlugin] unable to add new plugin to stack!\n");
+ gu_log("[PluginHost::clonePlugin] unable to add new plugin to stack!\n");
return 0;
}
for (int k=0; k<src->getNumParameters(); k++) {
}
}
-/* -------------------------------------------------------------------------- */
-
-
-void PluginHost::processStackOffline(float *buffer, int stackType, Channel *ch, int size)
-{
-#if 0
- /* call processStack on the entire size of the buffer. How many cycles?
- * size / (kernelAudio::realBufsize*2) (ie. internal bufsize) */
-
- /** FIXME 1 - calling processStack is slow, due to its internal buffer
- * conversions. We should also call processOffline from VST sdk */
-
- int index = 0;
- int step = kernelAudio::realBufsize*2;
-
- while (index <= size) {
- int left = index+step-size;
- if (left < 0)
- processStack(&buffer[index], stackType, ch);
-
- /** FIXME 2 - we left out the last part of buffer, because size % step != 0.
- * we should process the last chunk in a separate buffer, padded with 0 */
-
- //else
- // gLog("chunk of buffer left, size=%d\n", left);
-
- index+=step;
- }
-#endif
-}
-
#endif // #ifdef WITH_VST
void runDispatchLoop();
- /* processStackOffline
- * apply the fx list to a longer chunk of data */
-
- void processStackOffline(float *buffer, int stackType, class Channel *ch, int size);
-
/* freeAllStacks
* Free everything. */
bool hasMissingPlugins() { return missingPlugins; };
void sortPlugins(int sortMethod);
+
+ pthread_mutex_t mutex_midi;
};
#endif
* Giada - Your Hardcore Loopmachine
*
* recorder
- * Action recorder.
*
* -----------------------------------------------------------------------------
*
#include <math.h>
-#include "recorder.h"
+#include "../utils/log.h"
+#include "../utils/fs.h"
#include "const.h"
#include "mixer.h"
#include "mixerHandler.h"
#include "conf.h"
#include "channel.h"
#include "sampleChannel.h"
-#include "../utils/log.h"
-#include "../utils/utils.h"
+#include "recorder.h"
+extern KernelAudio G_KernelAudio;
extern Mixer G_Mixer;
-extern Patch_DEPR_ f_patch;
-extern Conf G_Conf;
-namespace recorder
+Recorder::Recorder()
+ : active (false),
+ sortedActions(false)
{
-vector<int> frames;
-vector< vector<action*> > global;
-vector<action*> actions;
-
-bool active = false;
-bool sortedActions = false;
-
-composite cmp;
-
+}
/* -------------------------------------------------------------------------- */
-void init()
+void Recorder::init()
{
sortedActions = false;
active = false;
/* -------------------------------------------------------------------------- */
-bool canRec(Channel *ch)
+bool Recorder::canRec(Channel *ch)
{
/* NO recording if:
* recorder is inactive
* mixer is not running
- * mixer is recording a take in this channel ch
+ * mixer is recording a take somewhere
* channel is empty */
- if (!active || !G_Mixer.running || G_Mixer.chanInput == ch || (ch->type == CHANNEL_SAMPLE && ((SampleChannel*)ch)->wave == NULL))
- return 0;
- return 1;
+ if (!active ||
+ !G_Mixer.running ||
+ G_Mixer.recording ||
+ (ch->type == CHANNEL_SAMPLE && ((SampleChannel*)ch)->wave == NULL)
+ )
+ return false;
+ return true;
}
/* -------------------------------------------------------------------------- */
-void rec(int index, int type, int frame, uint32_t iValue, float fValue)
+void Recorder::rec(int index, int type, int frame, uint32_t iValue, float fValue)
{
/* make sure frame is even */
/* don't activate the channel (readActions == false), it's up to
* the other layers */
- Channel *ch = G_Mixer.getChannelByIndex(index);
- ch->hasActions = true;
+ G_Mixer.getChannelByIndex(index)->hasActions = true;
sortedActions = false;
- gLog("[REC] action recorded, type=%d frame=%d chan=%d iValue=%d (%X) fValue=%f\n",
+ gu_log("[REC] action recorded, type=%d frame=%d chan=%d iValue=%d (0x%X) fValue=%f\n",
a->type, a->frame, a->chan, a->iValue, a->iValue, a->fValue);
//print();
}
/* -------------------------------------------------------------------------- */
-void clearChan(int index)
+void Recorder::clearChan(int index)
{
- gLog("[REC] clearing chan %d...\n", index);
+ gu_log("[REC] clearing chan %d...\n", index);
for (unsigned i=0; i<global.size(); i++) { // for each frame i
unsigned j=0;
/* -------------------------------------------------------------------------- */
-void clearAction(int index, char act)
+void Recorder::clearAction(int index, char act)
{
- gLog("[REC] clearing action %d from chan %d...\n", act, index);
+ gu_log("[REC] 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 deleteAction(int chan, int frame, char type, bool checkValues, uint32_t iValue, float fValue)
+void Recorder::deleteAction(int chan, int frame, char type, bool checkValues,
+ uint32_t iValue, float fValue)
{
/* make sure frame is even */
doit &= (a->iValue == iValue && a->fValue == fValue);
if (doit) {
+ // TODO - wft? just do: while (true); if (pthread_mutex_trylock(&G_Mixer.mutex_recs))
int lockStatus = 0;
while (lockStatus == 0) {
lockStatus = pthread_mutex_trylock(&G_Mixer.mutex_recs);
break;
}
else
- gLog("[REC] delete action: waiting for mutex...\n");
+ gu_log("[REC] delete action: waiting for mutex...\n");
}
}
}
if (found) {
optimize();
setChanHasActionsStatus(chan);
- gLog("[REC] action deleted, type=%d frame=%d chan=%d iValue=%d (%X) fValue=%f\n",
+ gu_log("[REC] action deleted, type=%d frame=%d chan=%d iValue=%d (%X) fValue=%f\n",
type, frame, chan, iValue, iValue, fValue);
}
else
- gLog("[REC] unable to delete action, not found! type=%d frame=%d chan=%d iValue=%d (%X) fValue=%f\n",
+ gu_log("[REC] unable to delete action, not found! type=%d frame=%d chan=%d iValue=%d (%X) fValue=%f\n",
type, frame, chan, iValue, iValue, fValue);
}
/* -------------------------------------------------------------------------- */
-void deleteActions(int chan, int frame_a, int frame_b, char type)
+void Recorder::deleteActions(int chan, int frame_a, int frame_b, char type)
{
sortActions();
vector<int> dels;
/* -------------------------------------------------------------------------- */
-void clearAll()
+void Recorder::clearAll()
{
while (global.size() > 0) {
for (unsigned i=0; i<global.size(); i++) {
- for (unsigned k=0; k<global.at(i).size(); k++) {
-#ifdef WITH_VST
-#if 0
- if (global.at(i).at(k)->type == ACTION_MIDI)
- free(global.at(i).at(k)->event);
-#endif
-#endif
+ for (unsigned k=0; k<global.at(i).size(); k++)
free(global.at(i).at(k)); // free action
- }
global.at(i).clear(); // free action container
global.erase(global.begin() + i);
}
/* -------------------------------------------------------------------------- */
-void optimize()
+void Recorder::optimize()
{
/* do something until the i frame is empty. */
/* -------------------------------------------------------------------------- */
-void sortActions()
+void Recorder::sortActions()
{
if (sortedActions)
return;
/* -------------------------------------------------------------------------- */
-void updateBpm(float oldval, float newval, int oldquanto)
+void Recorder::updateBpm(float oldval, float newval, int oldquanto)
{
for (unsigned i=0; i<frames.size(); i++) {
/* -------------------------------------------------------------------------- */
-void updateSamplerate(int systemRate, int patchRate)
+void Recorder::updateSamplerate(int systemRate, int patchRate)
{
/* diff ratio: systemRate / patchRate
* e.g. 44100 / 96000 = 0.4... */
if (systemRate == patchRate)
return;
- gLog("[REC] systemRate (%d) != patchRate (%d), converting...\n", systemRate, patchRate);
+ gu_log("[REC] systemRate (%d) != patchRate (%d), converting...\n", systemRate, patchRate);
float ratio = systemRate / (float) patchRate;
for (unsigned i=0; i<frames.size(); i++) {
- gLog("[REC] oldFrame = %d", frames.at(i));
+ gu_log("[REC] oldFrame = %d", frames.at(i));
float newFrame = frames.at(i);
newFrame = floorf(newFrame * ratio);
if (frames.at(i) % 2 != 0)
frames.at(i)++;
- gLog(", newFrame = %d\n", frames.at(i));
+ gu_log(", newFrame = %d\n", frames.at(i));
}
/* update structs */
/* -------------------------------------------------------------------------- */
-void expand(int old_fpb, int new_fpb)
+void Recorder::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,
}
}
}
- gLog("[REC] expanded recs\n");
+ gu_log("[REC] expanded recs\n");
//print();
}
/* -------------------------------------------------------------------------- */
-void shrink(int new_fpb)
+void Recorder::shrink(int new_fpb)
{
/* easier than expand(): here we delete eveything beyond old_framesPerBars. */
i++;
}
optimize();
- gLog("[REC] shrinked recs\n");
+ gu_log("[REC] shrinked recs\n");
//print();
}
/* -------------------------------------------------------------------------- */
-void setChanHasActionsStatus(int index)
+void Recorder::setChanHasActionsStatus(int index)
{
Channel *ch = G_Mixer.getChannelByIndex(index);
if (global.size() == 0) {
/* -------------------------------------------------------------------------- */
-int getNextAction(int chan, char type, int frame, action **out, uint32_t iValue)
+int Recorder::getNextAction(int chan, char type, int frame, action **out,
+ uint32_t iValue)
{
sortActions(); // mandatory
for (unsigned j=0; j<global.at(i).size(); j++) {
action *a = global.at(i).at(j);
if (a->chan == chan && (type & a->type) == a->type) {
- if (iValue == 0 || (iValue != 0 && a->iValue == iValue)) {
+ //if (iValue == 0 || (iValue != 0 && a->iValue == iValue)) {
+ if (iValue == 0 || (iValue != 0 && (iValue & a->iValue) == a->iValue )) {
*out = global.at(i).at(j);
return 1;
}
/* -------------------------------------------------------------------------- */
-int getAction(int chan, char action, int frame, struct action **out)
+int Recorder::getAction(int chan, char action, int frame, struct action **out)
{
for (unsigned i=0; i<global.size(); i++)
for (unsigned j=0; j<global.at(i).size(); j++)
/* -------------------------------------------------------------------------- */
-void startOverdub(int index, char actionMask, int frame)
+void Recorder::startOverdub(int index, char actionMask, int frame)
{
/* prepare the composite struct */
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-kernelAudio::realBufsize;
+ int truncFrame = cmp.a1.frame - G_KernelAudio.realBufsize;
if (truncFrame < 0)
truncFrame = 0;
- gLog("[REC] add truncation at frame %d, type=%d\n", truncFrame, cmp.a2.type);
+ gu_log("[REC] add truncation at frame %d, type=%d\n", truncFrame, cmp.a2.type);
rec(index, cmp.a2.type, truncFrame);
}
}
/* -------------------------------------------------------------------------- */
-void stopOverdub(int frame)
+void Recorder::stopOverdub(int frame)
{
cmp.a2.frame = frame;
bool ringLoop = false;
if (cmp.a2.frame < cmp.a1.frame) {
ringLoop = true;
- gLog("[REC] ring loop! frame1=%d < frame2=%d\n", cmp.a1.frame, cmp.a2.frame);
+ gu_log("[REC] ring loop! frame1=%d < frame2=%d\n", cmp.a1.frame, cmp.a2.frame);
rec(cmp.a2.chan, cmp.a2.type, G_Mixer.totalFrames); // record at the end of the sequencer
}
else
if (cmp.a2.frame == cmp.a1.frame) {
nullLoop = true;
- gLog("[REC] null loop! frame1=%d == frame2=%d\n", cmp.a1.frame, cmp.a2.frame);
+ gu_log("[REC] null loop! frame1=%d == frame2=%d\n", cmp.a1.frame, cmp.a2.frame);
deleteAction(cmp.a1.chan, cmp.a1.frame, cmp.a1.type, false); // false == don't check values
}
int res = getNextAction(cmp.a2.chan, cmp.a1.type | cmp.a2.type, cmp.a2.frame, &act);
if (res == 1) {
if (act->type == cmp.a2.type) {
- gLog("[REC] add truncation at frame %d, type=%d\n", act->frame, act->type);
+ gu_log("[REC] add truncation at frame %d, type=%d\n", act->frame, act->type);
deleteAction(act->chan, act->frame, act->type, false); // false == don't check values
}
}
/* -------------------------------------------------------------------------- */
-void print()
+void Recorder::print()
{
- gLog("[REC] ** print debug **\n");
+ gu_log("[REC] ** print debug **\n");
for (unsigned i=0; i<global.size(); i++) {
- gLog(" frame %d\n", frames.at(i));
+ gu_log(" frame %d\n", frames.at(i));
for (unsigned j=0; j<global.at(i).size(); j++) {
- gLog(" 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);
+ gu_log(" action %d | chan %d | frame %d\n", global.at(i).at(j)->type, global.at(i).at(j)->chan, global.at(i).at(j)->frame);
}
}
}
-
-} // namespace
-/* ---------------------------------------------------------------------
+/* -----------------------------------------------------------------------------
*
* Giada - Your Hardcore Loopmachine
*
* recorder
*
- * ---------------------------------------------------------------------
+ * -----------------------------------------------------------------------------
*
* Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
*
* along with Giada - Your Hardcore Loopmachine. If not, see
* <http://www.gnu.org/licenses/>.
*
- * ------------------------------------------------------------------ */
+ * -------------------------------------------------------------------------- */
+
#ifndef RECORDER_H
#define RECORDER_H
-#include <stdio.h>
-#include <stdlib.h>
+
+#ifdef __APPLE__ // our compiler still doesn't know about cstdint (c++11 stuff)
+ #include <stdint.h>
+#else
+ #include <cstdint>
+#endif
#include <vector>
-#include "../utils/utils.h"
-#include "const.h"
-#include "mixer.h"
using std::vector;
-/*
- * [global0]-->[vector<_action*>0]-->[a0][a1][a2] 0[frames1]
- * [global1]-->[vector<_action*>1]-->[a0][a1][a2] 1[frames2]
- * [global2]-->[vector<_action*>2]-->[a0][a1][a2] 2[frames3]
- * [global3]-->[vector<_action*>3]-->[a0][a1][a2] 3[frames4]
- * */
-
-namespace recorder
+class Recorder
{
-/* action
- * struct containing fields to describe an atomic action. Note from
- * VST sdk: parameter values, like all VST parameters, are declared as
- * floats with an inclusive range of 0.0 to 1.0 (fValue). */
+public:
-struct action
-{
- int chan; // channel index, i.e. Channel->index
- int type;
- int frame; // redundant info, used by helper functions
- float fValue; // used only for envelopes (volumes, vst params).
- uint32_t iValue; // used only for MIDI events
-};
+ Recorder();
-/* composite
- * a group of two actions (keypress+keyrel, muteon+muteoff) used during
- * the overdub process */
+ /* action
+ * struct containing fields to describe an atomic action. Note from
+ * VST sdk: parameter values, like all VST parameters, are declared as
+ * floats with an inclusive range of 0.0 to 1.0 (fValue). */
-struct composite
-{
- action a1;
- action a2;
-};
+ struct action
+ {
+ int chan; // channel index, i.e. Channel->index
+ int type;
+ int frame; // redundant info, used by helper functions
+ float fValue; // used only for envelopes (volumes, vst params).
+ uint32_t iValue; // used only for MIDI events
+ };
+
+ /* [global0]-->[vector<_action*>0]-->[a0][a1][a2] 0[frames1]
+ * [global1]-->[vector<_action*>1]-->[a0][a1][a2] 1[frames2]
+ * [global2]-->[vector<_action*>2]-->[a0][a1][a2] 2[frames3]
+ * [global3]-->[vector<_action*>3]-->[a0][a1][a2] 3[frames4] */
+
+ vector<int> frames; // frame counter (sentinel) frames.size == global.size
+ vector<vector<action*>> global; // container of containers of actions
+ vector<action*> actions; // container of actions
-extern vector<int> frames; // frame counter (sentinel) frames.size == global.size
-extern vector< vector<action*> > global; // container of containers of actions
-extern vector<action*> actions; // container of actions
+ bool active;
+ bool sortedActions; // are actions sorted via sortActions()?
-extern bool active;
-extern bool sortedActions; // are actions sorted via sortActions()?
+ /* init
+ * everything starts from here. */
-/* init
- * everything starts from here. */
+ void init();
-void init();
+ /* setChanHasActionsStatus
+ * Check if the channel has at least one action recorded. If false, sets
+ * ch->hasActions = false. Used after an action deletion. */
-/* setChanHasActionsStatus
- * Check if the channel has at least one action recorded. If false, sets
- * ch->hasActions = false. Used after an action deletion. */
+ void setChanHasActionsStatus(int chan);
-void setChanHasActionsStatus(int chan);
+ /* canRec
+ * can a channel rec an action? Call this one BEFORE rec(). */
-/* canRec
- * can a channel rec an action? Call this one BEFORE rec(). */
+ bool canRec(class Channel *ch);
-bool canRec(Channel *ch);
+ /* rec
+ * record an action. */
-/* rec
- * record an action. */
+ void rec(int chan, int action, int frame, uint32_t iValue=0,
+ float fValue=0.0f);
-void rec(int chan, int action, int frame, uint32_t iValue=0, float fValue=0.0f);
+ /* clearChan
+ * clear all actions from a channel. */
-/* clearChan
- * clear all actions from a channel. */
+ void clearChan(int chan);
-void clearChan(int chan);
+ /* clearAction
+ * clear the 'action' action type from a channel. */
-/* clearAction
- * clear the 'action' action type from a channel. */
+ void clearAction(int chan, char action);
-void clearAction(int chan, char action);
+ /* deleteAction
+ * delete ONE action. Useful in the action editor. 'type' can be a mask. */
-/* deleteAction
- * delete ONE action. Useful in the action editor. 'type' can be a mask. */
+ void deleteAction(int chan, int frame, char type, bool checkValues,
+ uint32_t iValue=0, float fValue=0.0);
-void deleteAction(int chan, int frame, char type, bool checkValues, uint32_t iValue=0, float fValue=0.0);
+ /* deleteActions
+ * delete A RANGE of actions from frame_a to frame_b in channel 'chan'.
+ * 'type' can be a bitmask. Exclusive range (frame_a, frame_b). */
-/* deleteActions
- * delete A RANGE of actions from frame_a to frame_b in channel 'chan'.
- * 'type' can be a bitmask. Exclusive range (frame_a, frame_b). */
+ void deleteActions(int chan, int frame_a, int frame_b, char type);
-void deleteActions(int chan, int frame_a, int frame_b, char type);
+ /* clearAll
+ * delete everything. */
-/* clearAll
- * delete everything. */
+ void clearAll();
-void clearAll();
+ /* optimize
+ * clear frames without actions. */
-/* optimize
- * clear frames without actions. */
+ void optimize();
-void optimize();
+ /* sortActions
+ * sorts actions by frame, asc mode. */
-/* sortActions
- * sorts actions by frame, asc mode. */
+ void sortActions();
-void sortActions();
+ /* updateBpm
+ * reassign frames by calculating the new bpm value. */
-/* updateBpm
- * reassign frames by calculating the new bpm value. */
+ void updateBpm(float oldval, float newval, int oldquanto);
-void updateBpm(float oldval, float newval, int oldquanto);
+ /* updateSamplerate
+ * reassign frames taking in account the samplerate. If f_system ==
+ * f_patch nothing changes, otherwise the conversion is mandatory. */
-/* updateSamplerate
- * reassign frames taking in account the samplerate. If f_system ==
- * f_patch nothing changes, otherwise the conversion is mandatory. */
+ void updateSamplerate(int systemRate, int patchRate);
-void updateSamplerate(int systemRate, int patchRate);
+ void expand(int old_fpb, int new_fpb);
+ void shrink(int new_fpb);
-void expand(int old_fpb, int new_fpb);
-void shrink(int new_fpb);
+ /* getNextAction
+ * Return the nearest action in chan 'chan' of type 'action' starting
+ * from 'frame'. Action can be a bitmask. If iValue != 0 search for
+ * next action with iValue == iValue: useful for MIDI key_release. iValue
+ * can be a bitmask. */
-/* getNextAction
- * return the nearest action in chan 'chan' of type 'action' starting
- * from 'frame'. Action can be a bitmask. If iValue != -1 search for
- * next action with iValue == iValue: useful for MIDI key_release. */
+ int getNextAction(int chan, char action, int frame, struct action **out,
+ uint32_t iValue=0);
-int getNextAction(int chan, char action, int frame, struct action **out, uint32_t iValue=0);
+ /* getAction
+ * return a pointer to action in chan 'chan' of type 'action' at frame
+ * 'frame'. */
-/* getAction
- * return a pointer to action in chan 'chan' of type 'action' at frame
- * 'frame'. */
+ int getAction(int chan, char action, int frame, struct action **out);
-int getAction(int chan, char action, int frame, struct action **out);
+ /* start/endOverdub */
-/* start/endOverdub */
+ void startOverdub(int chan, char action, int frame);
+ void stopOverdub(int frame);
-void startOverdub(int chan, char action, int frame);
-void stopOverdub(int frame);
+private:
-/* print
- * debug of the frame stack. */
+ /* composite
+ * a group of two actions (keypress+keyrel, muteon+muteoff) used during
+ * the overdub process */
-void print();
+ struct composite
+ {
+ action a1;
+ action a2;
+ } cmp;
-} // namespace
+ /* print
+ * debug of the frame stack. */
+
+ void print();
+
+};
#endif
#include <math.h>
#include "../utils/log.h"
+#include "../utils/string.h"
#include "sampleChannel.h"
#include "patch_DEPR_.h"
#include "patch.h"
#include "waveFx.h"
#include "mixerHandler.h"
#include "kernelMidi.h"
+#include "kernelAudio.h"
using std::string;
+extern Recorder G_Recorder;
+extern KernelAudio G_KernelAudio;
+
+
SampleChannel::SampleChannel(int bufferSize, MidiMapConf *midiMapConf)
: Channel (CHANNEL_SAMPLE, STATUS_EMPTY, bufferSize, midiMapConf),
frameRewind (-1),
fadeoutVol (1.0f),
fadeoutTracker (0),
fadeoutStep (DEFAULT_FADEOUT_STEP),
- readActions (true),
+ readActions (false),
midiInReadActions(0x0),
midiInPitch (0x0)
{
rsmp_state = src_new(SRC_LINEAR, 2, NULL);
- pChan = (float *) malloc(kernelAudio::realBufsize * 2 * sizeof(float));
+ pChan = (float *) malloc(G_KernelAudio.realBufsize * 2 * sizeof(float));
}
{
string oldName = wave->name;
int k = 1; // Start from k = 1, zero is too nerdy
- while (!mh_uniqueSamplename(this, wave->name.c_str())) {
- wave->updateName((oldName + "-" + gItoa(k)).c_str());
+ while (!mh_uniqueSampleName(this, wave->name)) {
+ wave->updateName((oldName + "-" + gu_itoa(k)).c_str());
k++;
}
}
if (status & (STATUS_PLAY | STATUS_ENDING)) {
tracker = fillChan(vChan, tracker, 0);
if (fadeoutOn && fadeoutType == XFADE) {
- gLog("[clear] filling pChan fadeoutTracker=%d\n", fadeoutTracker);
+ gu_log("[clear] filling pChan fadeoutTracker=%d\n", fadeoutTracker);
fadeoutTracker = fillChan(pChan, fadeoutTracker, 0);
}
}
{
/* method: check this frame && next frame, then calculate delta */
- recorder::action *a0 = NULL;
- recorder::action *a1 = NULL;
+ Recorder::action *a0 = NULL;
+ Recorder::action *a1 = NULL;
int res;
/* get this action on frame 'frame'. It's unlikely that the action
* is not found. */
- res = recorder::getAction(index, ACTION_VOLUME, frame, &a0);
+ res = G_Recorder.getAction(index, ACTION_VOLUME, frame, &a0);
if (res == 0)
return;
* and use action at frame number 0 (actions[0]).
* res == -2 ACTION_VOLUME not found. This should never happen */
- res = recorder::getNextAction(index, ACTION_VOLUME, frame, &a1);
+ res = G_Recorder.getNextAction(index, ACTION_VOLUME, frame, &a1);
if (res == -1)
- res = recorder::getAction(index, ACTION_VOLUME, 0, &a1);
+ res = G_Recorder.getAction(index, ACTION_VOLUME, 0, &a1);
volume_i = a0->fValue;
volume_d = ((a1->fValue - a0->fValue) / ((a1->frame - a0->frame) / 2)) * 1.003f;
/* -------------------------------------------------------------------------- */
-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)
switch (a->type) {
case ACTION_KEYPRESS:
if (mode & SINGLE_ANY)
- start(localFrame, false, quantize, mixerIsRunning);
+ start(localFrame, false, quantize, mixerIsRunning, false, false);
break;
case ACTION_KEYREL:
if (mode & SINGLE_ANY)
/* this is the moment in which we record the keypress, if the
* quantizer is on. SINGLE_PRESS needs overdub */
- if (recorder::canRec(this)) {
+ if (G_Recorder.canRec(this)) {
if (mode == SINGLE_PRESS)
- recorder::startOverdub(index, ACTION_KEYS, globalFrame);
+ G_Recorder.startOverdub(index, ACTION_KEYS, globalFrame);
else
- recorder::rec(index, ACTION_KEYPRESS, globalFrame);
+ G_Recorder.rec(index, ACTION_KEYPRESS, globalFrame);
}
}
void SampleChannel::setReadActions(bool v, bool recsStopOnChanHalt)
{
- if (v)
- readActions = true;
- else {
- readActions = false;
- if (recsStopOnChanHalt)
- kill(0); /// FIXME - wrong frame value
- }
+ readActions = v;
+ if (!readActions && recsStopOnChanHalt)
+ kill(0); /// FIXME - wrong frame value
}
void SampleChannel::setXFade(int frame)
{
- gLog("[xFade] frame=%d tracker=%d\n", frame, tracker);
+ gu_log("[xFade] frame=%d tracker=%d\n", frame, tracker);
calcFadeoutStep();
fadeoutOn = true;
{
wave = w;
status = STATUS_OFF;
- sendMidiLplay();
+ sendMidiLplay(); // FIXME - why here?!?!
begin = 0;
end = wave->size;
}
if (!w->allocEmpty(frames, samplerate))
return false;
- w->name = "TAKE-" + gItoa(takeId);
- w->pathfile = gGetCurrentPath() + G_SLASH + w->name;
+ w->name = "TAKE-" + gu_itoa(takeId);
+ w->pathfile = gu_getCurrentPath() + G_SLASH + w->name;
wave = w;
status = STATUS_OFF;
begin = 0;
end = wave->size;
- sendMidiLplay();
+ sendMidiLplay(); // FIXME - why here?!?!
return true;
}
/* -------------------------------------------------------------------------- */
-void SampleChannel::process(float *buffer)
+void SampleChannel::process(float *outBuffer, float *inBuffer)
{
+ /* If armed and inbuffer is not null (i.e. input device available), copy input
+ buffer to vChan: this enables the live recording mode. The vChan will be
+ overwritten later by PluginHost::processStack, so that you would record "clean"
+ audio (i.e. not plugin-processed). */
+
+ if (armed && inBuffer)
+ for (int i=0; i<bufferSize; i++)
+ vChan[i] += inBuffer[i]; // add, don't overwrite (no raw memcpy)
+
#ifdef WITH_VST
pluginHost->processStack(vChan, PluginHost::CHANNEL, this);
#endif
for (int j=0; j<bufferSize; j+=2) {
- buffer[j] += vChan[j] * volume * panLeft * boost;
- buffer[j+1] += vChan[j+1] * volume * panRight * boost;
+ outBuffer[j] += vChan[j] * volume * panLeft * boost;
+ outBuffer[j+1] += vChan[j+1] * volume * panRight * boost;
}
}
int SampleChannel::load(const char *file, int samplerate, int rsmpQuality)
{
- if (strcmp(file, "") == 0 || gIsDir(file)) {
- gLog("[SampleChannel] file not specified\n");
+ if (strcmp(file, "") == 0 || gu_isDir(file)) {
+ gu_log("[SampleChannel] file not specified\n");
return SAMPLE_LEFT_EMPTY;
}
Wave *w = new Wave();
if (!w->open(file)) {
- gLog("[SampleChannel] %s: read error\n", file);
+ gu_log("[SampleChannel] %s: read error\n", file);
delete w;
return SAMPLE_READ_ERROR;
}
if (w->channels() > 2) {
- gLog("[SampleChannel] %s: unsupported multichannel wave\n", file);
+ gu_log("[SampleChannel] %s: unsupported multichannel wave\n", file);
delete w;
return SAMPLE_MULTICHANNEL;
}
wfx_monoToStereo(w);
if (w->rate() != samplerate) {
- gLog("[SampleChannel] input rate (%d) != system rate (%d), conversion needed\n",
+ gu_log("[SampleChannel] input rate (%d) != system rate (%d), conversion needed\n",
w->rate(), samplerate);
w->resample(rsmpQuality, samplerate);
}
pushWave(w);
generateUniqueSampleName();
- gLog("[SampleChannel] %s loaded in channel %d\n", file, index);
+ gu_log("[SampleChannel] %s loaded in channel %d\n", file, index);
return SAMPLE_LOADED_OK;
}
bool SampleChannel::canInputRec()
- {
- return wave == NULL;
+{
+ return wave == NULL && armed;
}
void SampleChannel::start(int frame, bool doQuantize, int quantize,
- bool mixerIsRunning)
+ bool mixerIsRunning, bool forceStart, bool isUserGenerated)
{
switch (status) {
case STATUS_EMPTY:
{
return;
}
-
case STATUS_OFF:
{
if (mode & LOOP_ANY) {
- status = STATUS_WAIT;
+ if (forceStart) {
+ status = STATUS_PLAY;
+ tracker = frame;
+ }
+ else
+ status = STATUS_WAIT;
sendMidiLplay();
}
else {
if (quantize > 0 && mixerIsRunning && doQuantize)
qWait = true;
else {
-
- /* fillChan only if frame != 0. If you call fillChan on frame == 0
- a duplicate call to fillChan occurs with loss of data. Yeah, but
- what happens if an action really occurs on frame 0 (and it happens,
- for example when you start the sequencer from the firt beat)? Cheat
- time! Shift a little bit the frame, so that it's no longer zero. */
-
status = STATUS_PLAY;
sendMidiLplay();
- if (frame == 0)
- frame = 2;
- //if (frame != 0)
+
+ /* Do fillChan only if this is not a user-generated event (i.e. is an
+ action read by Mixer). Otherwise clear() will take take of calling
+ fillChan on the next cycle. */
+
+ if (!isUserGenerated)
tracker = fillChan(vChan, tracker, frame);
}
}
break;
}
-
case STATUS_PLAY:
{
if (mode == SINGLE_BASIC)
}
break;
}
-
case STATUS_WAIT:
{
status = STATUS_OFF;
sendMidiLplay();
break;
}
-
case STATUS_ENDING:
{
status = STATUS_PLAY;
int SampleChannel::writePatch(int i, bool isProject, Patch *patch)
{
+ // TODO - this code belongs to an upper level (glue)
+
int pchIndex = Channel::writePatch(i, isProject, patch);
Patch::channel_t *pch = &patch->channels.at(pchIndex);
if (wave != NULL) {
pch->samplePath = wave->pathfile;
if (isProject)
- pch->samplePath = gBasename(wave->pathfile); // make it portable
+ pch->samplePath = gu_basename(wave->pathfile); // make it portable
}
else
pch->samplePath = "";
SampleChannel(int bufferSize, class MidiMapConf *midiMapConf);
~SampleChannel();
- void copy(const Channel *src, pthread_mutex_t *pluginMutex);
-
- void clear ();
- void process (float *buffer);
- void start (int frame, bool doQuantize, int quantize, bool mixerIsRunning);
- void kill (int frame);
- void empty ();
- void stopBySeq (bool chansStopOnSeqHalt);
- void stop ();
- void rewind ();
- void setMute (bool internal);
- void unsetMute (bool internal);
- void reset (int frame);
- int load (const char *file, int samplerate, int rsmpQuality);
- int readPatch_DEPR_ (const char *file, int i, class Patch_DEPR_ *patch,
- int samplerate, int rsmpQuality);
- int readPatch (const string &basePath, int i, class Patch *patch,
- pthread_mutex_t *pluginMutex, int samplerate, int rsmpQuality);
- int writePatch (int i, bool isProject, class Patch *patch);
- void quantize (int index, int localFrame, int globalFrame);
- void onZero (int frame, bool recsStopOnChanHalt);
- void onBar (int frame);
- void parseAction(recorder::action *a, int localFrame, int globalFrame,
- int quantize, bool mixerIsRunning);
+ void copy(const Channel *src, pthread_mutex_t *pluginMutex) override;
+ void clear() override;
+ void process(float *outBuffer, float *inBuffer) override;
+ void start(int frame, bool doQuantize, int quantize, bool mixerIsRunning,
+ bool forceStart, bool isUserGenerated) override;
+ void kill(int frame) override;
+ void empty() override;
+ void stopBySeq(bool chansStopOnSeqHalt) override;
+ void stop() override;
+ void rewind() override;
+ void setMute(bool internal) override;
+ void unsetMute(bool internal) override;
+ int readPatch_DEPR_(const char *file, int i, class Patch_DEPR_ *patch,
+ int samplerate, int rsmpQuality) override;
+ int readPatch(const string &basePath, int i, class Patch *patch,
+ pthread_mutex_t *pluginMutex, int samplerate, int rsmpQuality) override;
+ int writePatch(int i, bool isProject, class Patch *patch) override;
+ void quantize(int index, int localFrame, int globalFrame) override;
+ void onZero(int frame, bool recsStopOnChanHalt) override;
+ void onBar(int frame) override;
+ void parseAction(Recorder::action *a, int localFrame, int globalFrame,
+ int quantize, bool mixerIsRunning) override;
+ bool canInputRec() override;
+
+ int load(const char *file, int samplerate, int rsmpQuality);
+
+ void reset(int frame);
/* fade methods
* prepare channel for fade, mixer will take care of the process
bool allocEmpty(int frames, int samplerate, int takeId);
- /* canInputRec
- * true if channel can host a new wave from input recording. */
-
- bool canInputRec();
-
/* setReadActions
- * if enabled, recorder will read actions from this channel. If
+ * if enabled (v == true), recorder will read actions from this channel. If
* recsStopOnChanHalt == true, stop reading actions right away. */
void setReadActions(bool v, bool recsStopOnChanHalt);
#include <stdlib.h>
#include <string.h> // memcpy
#include <math.h>
-#include "../utils/utils.h"
+#include "../utils/fs.h"
#include "../utils/log.h"
#include "init.h"
#include "const.h"
int Wave::open(const char *f)
{
pathfile = f;
- name = gStripExt(gBasename(f).c_str());
+ name = gu_stripExt(gu_basename(f));
fileIn = sf_open(f, SFM_READ, &inHeader);
if (fileIn == NULL) {
- gLog("[wave] unable to read %s. %s\n", f, sf_strerror(fileIn));
+ gu_log("[wave] unable to read %s. %s\n", f, sf_strerror(fileIn));
pathfile = "";
name = "";
return 0;
size = inHeader.frames * inHeader.channels;
data = (float *) malloc(size * sizeof(float));
if (data == NULL) {
- gLog("[wave] unable to allocate memory\n");
+ gu_log("[wave] unable to allocate memory\n");
return 0;
}
if (sf_read_float(fileIn, data, size) != size)
- gLog("[wave] warning: incomplete read!\n");
+ gu_log("[wave] warning: incomplete read!\n");
sf_close(fileIn);
return 1;
fileOut = sf_open(f, SFM_WRITE, &outHeader);
if (fileOut == NULL) {
- gLog("[wave] unable to open %s for exporting\n", f);
+ gu_log("[wave] unable to open %s for exporting\n", f);
return 0;
}
int out = sf_write_float(fileOut, data, size);
if (out != (int) size) {
- gLog("[wave] error while exporting %s! %s\n", f, sf_strerror(fileOut));
+ gu_log("[wave] error while exporting %s! %s\n", f, sf_strerror(fileOut));
return 0;
}
size = __size;
data = (float *) malloc(size * sizeof(float));
if (data == NULL) {
- gLog("[wave] unable to allocate memory\n");
+ gu_log("[wave] unable to allocate memory\n");
return 0;
}
float *tmp = (float *) malloc(newSize * sizeof(float));
if (!tmp) {
- gLog("[wave] unable to allocate memory for resampling\n");
+ gu_log("[wave] unable to allocate memory for resampling\n");
return -1;
}
src_data.output_frames = newSize/2; // in frames, i.e. /2 (stereo)
src_data.src_ratio = ratio;
- gLog("[wave] resampling: new size=%d (%d frames)\n", newSize, newSize/2);
+ gu_log("[wave] resampling: new size=%d (%d frames)\n", newSize, newSize/2);
int ret = src_simple(&src_data, quality, 2);
if (ret != 0) {
- gLog("[wave] resampling error: %s\n", src_strerror(ret));
+ gu_log("[wave] resampling error: %s\n", src_strerror(ret));
return 0;
}
string Wave::basename(bool ext) const
{
- return ext ? gBasename(pathfile) : gStripExt(gBasename(pathfile));
+ return ext ? gu_basename(pathfile) : gu_stripExt(gu_basename(pathfile));
}
string Wave::extension() const
{
- return gGetExt(pathfile.c_str());
+ return gu_getExt(pathfile);
}
void Wave::updateName(const char *n)
{
- string ext = gGetExt(pathfile.c_str());
- name = gStripExt(gBasename(n).c_str());
- pathfile = gDirname(pathfile.c_str()) + G_SLASH + name + "." + ext;
+ string ext = gu_getExt(pathfile);
+ name = gu_stripExt(gu_basename(n));
+ pathfile = gu_dirname(pathfile) + G_SLASH + name + "." + ext;
isLogical = true;
/* a wave with updated name must become logical, since the underlying
-/* ---------------------------------------------------------------------
+/* -----------------------------------------------------------------------------
*
* Giada - Your Hardcore Loopmachine
*
* waveFx
*
- * ---------------------------------------------------------------------
+ * -----------------------------------------------------------------------------
*
* Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
*
* along with Giada - Your Hardcore Loopmachine. If not, see
* <http://www.gnu.org/licenses/>.
*
- * ------------------------------------------------------------------ */
+ * -------------------------------------------------------------------------- */
-#include <math.h>
+#include <cmath>
#include "../utils/log.h"
-#include "waveFx.h"
-#include "channel.h"
-#include "mixer.h"
#include "wave.h"
+#include "waveFx.h"
-extern Mixer G_Mixer;
-
-
-float wfx_normalizeSoft(Wave *w) {
+float wfx_normalizeSoft(Wave *w)
+{
float peak = 0.0f;
float abs = 0.0f;
for (int i=0; i<w->size; i++) { // i++: both L and R samples
}
-/* ------------------------------------------------------------------ */
-
+/* -------------------------------------------------------------------------- */
-bool wfx_monoToStereo(Wave *w) {
+bool wfx_monoToStereo(Wave *w)
+{
unsigned newSize = w->size * 2;
float *dataNew = (float *) malloc(newSize * sizeof(float));
if (dataNew == NULL) {
- gLog("[wfx] unable to allocate memory for mono>stereo conversion\n");
+ gu_log("[wfx] unable to allocate memory for mono>stereo conversion\n");
return 0;
}
}
-/* ------------------------------------------------------------------ */
+/* -------------------------------------------------------------------------- */
-void wfx_silence(Wave *w, int a, int b) {
-
+void wfx_silence(Wave *w, int a, int b)
+{
/* stereo values */
a = a * 2;
b = b * 2;
- gLog("[wfx] silencing from %d to %d\n", a, b);
+ gu_log("[wfx] silencing from %d to %d\n", a, b);
for (int i=a; i<b; i+=2) {
w->data[i] = 0.0f;
}
-/* ------------------------------------------------------------------ */
+/* -------------------------------------------------------------------------- */
-int wfx_cut(Wave *w, int a, int b) {
+int wfx_cut(Wave *w, int a, int b)
+{
a = a * 2;
b = b * 2;
unsigned newSize = w->size-(b-a);
float *temp = (float *) malloc(newSize * sizeof(float));
if (temp == NULL) {
- gLog("[wfx] unable to allocate memory for cutting\n");
+ gu_log("[wfx] unable to allocate memory for cutting\n");
return 0;
}
- gLog("[wfx] cutting from %d to %d, new size=%d (video=%d)\n", a, b, newSize, newSize/2);
+ gu_log("[wfx] cutting from %d to %d, new size=%d (video=%d)\n", a, b, newSize, newSize/2);
for (int i=0, k=0; i<w->size; i++) {
if (i < a || i >= b) { // left margin always included, in order to keep
w->frames(w->frames() - b - a);
w->isEdited = true;
- gLog("[wfx] cutting done\n");
+ gu_log("[wfx] cutting done\n");
return 1;
}
-/* ------------------------------------------------------------------ */
+/* -------------------------------------------------------------------------- */
-int wfx_trim(Wave *w, int a, int b) {
+int wfx_trim(Wave *w, int a, int b)
+{
a = a * 2;
b = b * 2;
int newSize = b - a;
float *temp = (float *) malloc(newSize * sizeof(float));
if (temp == NULL) {
- gLog("[wfx] unable to allocate memory for trimming\n");
+ gu_log("[wfx] unable to allocate memory for trimming\n");
return 0;
}
- gLog("[wfx] trimming from %d to %d (area = %d)\n", a, b, b-a);
+ gu_log("[wfx] trimming from %d to %d (area = %d)\n", a, b, b-a);
for (int i=a, k=0; i<b; i++, k++)
temp[k] = w->data[i];
}
-/* ------------------------------------------------------------------ */
-
+/* -------------------------------------------------------------------------- */
-void wfx_fade(Wave *w, int a, int b, int type) {
+void wfx_fade(Wave *w, int a, int b, int type)
+{
float m = type == 0 ? 0.0f : 1.0f;
float d = 1.0f/(float)(b-a);
if (type == 1)
w->data[i+1] *= m;
m += d;
}
-}
+ w->isEdited = true;
+}
-/* ------------------------------------------------------------------ */
+/* -------------------------------------------------------------------------- */
-void wfx_smooth(Wave *w, int a, int b) {
+void wfx_smooth(Wave *w, int a, int b)
+{
int d = 32; // 64 if stereo data
/* do nothing if fade edges (both of 32 samples) are > than selected
* values. */
if (d*2 > (b-a)*2) {
- gLog("[WFX] selection is too small, nothing to do\n");
+ gu_log("[WFX] selection is too small, nothing to do\n");
return;
}
-/* ---------------------------------------------------------------------
+/* -----------------------------------------------------------------------------
*
* Giada - Your Hardcore Loopmachine
*
* waveFx
*
- * ---------------------------------------------------------------------
+ * -----------------------------------------------------------------------------
*
* Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
*
* along with Giada - Your Hardcore Loopmachine. If not, see
* <http://www.gnu.org/licenses/>.
*
- * ------------------------------------------------------------------ */
+ * -------------------------------------------------------------------------- */
#ifndef WAVEFX_H
#include "../gui/dialogs/gd_mainWindow.h"
+#include "../gui/dialogs/gd_editor.h"
#include "../gui/elems/ge_keyboard.h"
-#include "../gui/elems/ge_channel.h"
-#include "../utils/gui_utils.h"
+#include "../gui/elems/ge_waveTools.h"
+#include "../gui/elems/ge_waveform.h"
+#include "../gui/elems/channel.h"
+#include "../utils/gui.h"
#include "../core/mixerHandler.h"
#include "../core/mixer.h"
#include "../core/pluginHost.h"
#include "../core/conf.h"
+#include "../core/wave.h"
#include "../core/channel.h"
#include "../core/sampleChannel.h"
#include "../core/midiChannel.h"
-#include "glue.h"
+#include "main.h"
#include "channel.h"
-extern gdMainWindow *mainWin;
+extern gdMainWindow *G_MainWin;
extern Conf G_Conf;
+extern Recorder G_Recorder;
extern Mixer G_Mixer;
#ifdef WITH_VST
extern PluginHost G_PluginHost;
using std::string;
-int glue_loadChannel(SampleChannel *ch, const char *fname)
+static bool __soloSession__ = false;
+
+
+int glue_loadChannel(SampleChannel *ch, const string &fname)
{
+ /* Always stop a channel before loading a new sample in it. This will prevent
+ issues if tracker is outside the boundaries of the new sample -> segfault. */
+
+ ch->hardStop(0);
+
/* save the patch and take the last browser's dir in order to re-use it
* the next time */
- G_Conf.samplePath = gDirname(fname);
+ G_Conf.samplePath = gu_dirname(fname);
- int result = ch->load(fname, G_Conf.samplerate, G_Conf.rsmpQuality);
+ int result = ch->load(fname.c_str(), G_Conf.samplerate, G_Conf.rsmpQuality);
if (result == SAMPLE_LOADED_OK)
- mainWin->keyboard->updateChannel(ch->guiChannel);
+ G_MainWin->keyboard->updateChannel(ch->guiChannel);
return result;
}
Channel *glue_addChannel(int column, int type)
{
- Channel *ch = G_Mixer.addChannel(type);
- gChannel *gch = mainWin->keyboard->addChannel(column, ch);
- ch->guiChannel = gch;
- glue_setChanVol(ch, 1.0, false); // false = not from gui click
+ Channel *ch = G_Mixer.addChannel(type);
+ geChannel *gch = G_MainWin->keyboard->addChannel(column, ch);
+ ch->guiChannel = gch;
return ch;
}
void glue_deleteChannel(Channel *ch)
{
- recorder::clearChan(ch->index);
+ G_Recorder.clearChan(ch->index);
#ifdef WITH_VST
G_PluginHost.freeStack(PluginHost::CHANNEL, &G_Mixer.mutex_plugins, ch);
#endif
Fl::lock();
- mainWin->keyboard->deleteChannel(ch->guiChannel);
+ G_MainWin->keyboard->deleteChannel(ch->guiChannel);
Fl::unlock();
G_Mixer.deleteChannel(ch);
gu_closeAllSubwindows();
void glue_freeChannel(Channel *ch)
{
#ifdef WITH_VST
+
G_PluginHost.freeStack(PluginHost::CHANNEL, &G_Mixer.mutex_plugins, ch);
+ ch->guiChannel->fx->full = false;
+
#endif
- mainWin->keyboard->freeChannel(ch->guiChannel);
- recorder::clearChan(ch->index);
+ G_MainWin->keyboard->freeChannel(ch->guiChannel);
+ G_Recorder.clearChan(ch->index);
ch->empty();
}
/* -------------------------------------------------------------------------- */
+void glue_toggleArm(Channel *ch, bool gui)
+{
+ ch->armed = !ch->armed;
+ if (!gui)
+ ch->guiChannel->arm->value(ch->armed);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
int glue_cloneChannel(Channel *src)
{
Channel *ch = G_Mixer.addChannel(src->type);
- gChannel *gch = mainWin->keyboard->addChannel(src->guiChannel->getColumnIndex(), ch);
+ geChannel *gch = G_MainWin->keyboard->addChannel(src->guiChannel->getColumnIndex(), ch);
ch->guiChannel = gch;
ch->copy(src, &G_Mixer.mutex_plugins);
- mainWin->keyboard->updateChannel(ch->guiChannel);
+ G_MainWin->keyboard->updateChannel(ch->guiChannel);
return true;
}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void glue_setChanVol(Channel *ch, float v, bool gui)
+{
+ ch->volume = v;
+
+ /* also update wave editor if it's shown */
+
+ gdEditor *editor = (gdEditor*) gu_getSubwindow(G_MainWin, WID_SAMPLE_EDITOR);
+ if (editor) {
+ glue_setVolEditor(editor, (SampleChannel*) ch, v, false);
+ Fl::lock();
+ editor->volume->value(v);
+ Fl::unlock();
+ }
+
+ if (!gui) {
+ Fl::lock();
+ ch->guiChannel->vol->value(v);
+ Fl::unlock();
+ }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void glue_setPitch(gdEditor *win, SampleChannel *ch, float val, bool numeric)
+{
+ if (numeric) {
+ if (val <= 0.0f)
+ val = 0.1000f;
+ if (val > 4.0f)
+ val = 4.0000f;
+ if (win)
+ win->pitch->value(val);
+ }
+
+ ch->setPitch(val);
+
+ if (win) {
+ char buf[16];
+ sprintf(buf, "%.4f", val);
+ Fl::lock();
+ win->pitchNum->value(buf);
+ win->pitchNum->redraw();
+ Fl::unlock();
+ }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void glue_setPanning(gdEditor *win, SampleChannel *ch, float val)
+{
+ if (val < 1.0f) {
+ ch->panLeft = 1.0f;
+ ch->panRight= 0.0f + val;
+
+ char buf[8];
+ sprintf(buf, "%d L", (int) std::abs((ch->panRight * 100.0f) - 100));
+ win->panNum->value(buf);
+ }
+ else if (val == 1.0f) {
+ ch->panLeft = 1.0f;
+ ch->panRight= 1.0f;
+ win->panNum->value("C");
+ }
+ else {
+ ch->panLeft = 2.0f - val;
+ ch->panRight= 1.0f;
+
+ char buf[8];
+ sprintf(buf, "%d R", (int) std::abs((ch->panLeft * 100.0f) - 100));
+ win->panNum->value(buf);
+ }
+ win->panNum->redraw();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void glue_setMute(Channel *ch, bool gui)
+{
+ if (G_Recorder.active && G_Recorder.canRec(ch)) {
+ if (!ch->mute)
+ G_Recorder.startOverdub(ch->index, ACTION_MUTES, G_Mixer.actualFrame);
+ else
+ G_Recorder.stopOverdub(G_Mixer.actualFrame);
+ }
+
+ ch->mute ? ch->unsetMute(false) : ch->setMute(false);
+
+ if (!gui) {
+ Fl::lock();
+ ch->guiChannel->mute->value(ch->mute);
+ Fl::unlock();
+ }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void glue_setSoloOn(Channel *ch, bool gui)
+{
+ /* if there's no solo session, store mute configuration of all chans
+ * and start the session */
+
+ if (!__soloSession__) {
+ for (unsigned i=0; i<G_Mixer.channels.size(); i++) {
+ Channel *och = G_Mixer.channels.at(i);
+ och->mute_s = och->mute;
+ }
+ __soloSession__ = true;
+ }
+
+ ch->solo = !ch->solo;
+ ch->sendMidiLsolo();
+
+ /* 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);
+ if (!och->solo && !och->mute) {
+ och->setMute(false);
+ Fl::lock();
+ och->guiChannel->mute->value(true);
+ Fl::unlock();
+ }
+ }
+
+ if (ch->mute) {
+ ch->unsetMute(false);
+ Fl::lock();
+ ch->guiChannel->mute->value(false);
+ Fl::unlock();
+ }
+
+ if (!gui) {
+ Fl::lock();
+ ch->guiChannel->solo->value(1);
+ Fl::unlock();
+ }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void glue_setSoloOff(Channel *ch, bool gui)
+{
+ /* if this is uniqueSolo, stop solo session and restore mute status,
+ * else mute this */
+
+ if (mh_uniqueSolo(ch)) {
+ __soloSession__ = false;
+ for (unsigned i=0; i<G_Mixer.channels.size(); i++) {
+ Channel *och = G_Mixer.channels.at(i);
+ if (och->mute_s) {
+ och->setMute(false);
+ Fl::lock();
+ och->guiChannel->mute->value(true);
+ Fl::unlock();
+ }
+ else {
+ och->unsetMute(false);
+ Fl::lock();
+ och->guiChannel->mute->value(false);
+ Fl::unlock();
+ }
+ och->mute_s = false;
+ }
+ }
+ else {
+ ch->setMute(false);
+ Fl::lock();
+ ch->guiChannel->mute->value(true);
+ Fl::unlock();
+ }
+
+ ch->solo = !ch->solo;
+ ch->sendMidiLsolo();
+
+ if (!gui) {
+ Fl::lock();
+ ch->guiChannel->solo->value(0);
+ Fl::unlock();
+ }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void glue_setBeginEndChannel(gdEditor *win, SampleChannel *ch, int b, int e,
+ bool recalc, bool check)
+{
+ if (check) {
+ if (e > ch->wave->size)
+ e = ch->wave->size;
+ if (b < 0)
+ b = 0;
+ if (b > ch->wave->size)
+ b = ch->wave->size-2;
+ if (b >= ch->end)
+ b = ch->begin;
+ if (e <= ch->begin)
+ e = ch->end;
+ }
+
+ /* continue only if new values != old values */
+
+ if (b == ch->begin && e == ch->end)
+ return;
+
+ /* print mono values */
+
+ char tmp[16];
+ sprintf(tmp, "%d", b/2);
+ win->chanStart->value(tmp);
+
+ tmp[0] = '\0';
+ sprintf(tmp, "%d", e/2);
+ win->chanEnd->value(tmp);
+
+ ch->setBegin(b);
+ ch->setEnd(e);
+
+ /* Recalc is not needed when the user drags the bars directly over the
+ waveform */
+
+ if (recalc) {
+ win->waveTools->waveform->recalcPoints();
+ win->waveTools->waveform->redraw();
+ }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void glue_setBoost(gdEditor *win, SampleChannel *ch, float val, bool numeric)
+{
+ if (numeric) {
+ if (val > 20.0f)
+ val = 20.0f;
+ else if (val < 0.0f)
+ val = 0.0f;
+
+ float linear = pow(10, (val / 20)); // linear = 10^(dB/20)
+
+ ch->boost = linear;
+
+ char buf[10];
+ sprintf(buf, "%.2f", val);
+ win->boostNum->value(buf);
+ win->boostNum->redraw();
+
+ win->boost->value(linear);
+ win->boost->redraw(); /// inutile
+ }
+ else {
+ ch->boost = val;
+ char buf[10];
+ sprintf(buf, "%.2f", 20*log10(val));
+ win->boostNum->value(buf);
+ win->boostNum->redraw();
+ }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void glue_setVolEditor(class gdEditor *win, SampleChannel *ch, float val,
+ bool numeric)
+{
+ if (numeric) {
+ if (val > 0.0f)
+ val = 0.0f;
+ else if (val < -60.0f)
+ val = -INFINITY;
+
+ float linear = pow(10, (val / 20)); // linear = 10^(dB/20)
+
+ ch->volume = linear;
+
+ win->volume->value(linear);
+ win->volume->redraw();
+
+ char buf[10];
+ if (val > -INFINITY)
+ sprintf(buf, "%.2f", val);
+ else
+ sprintf(buf, "-inf");
+ win->volumeNum->value(buf);
+ win->volumeNum->redraw();
+
+ ch->guiChannel->vol->value(linear);
+ ch->guiChannel->vol->redraw();
+ }
+ else {
+ ch->volume = val;
+
+ float dbVal = 20 * log10(val);
+ char buf[10];
+ if (dbVal > -INFINITY)
+ sprintf(buf, "%.2f", dbVal);
+ else
+ sprintf(buf, "-inf");
+
+ win->volumeNum->value(buf);
+ win->volumeNum->redraw();
+
+ ch->guiChannel->vol->value(val);
+ ch->guiChannel->vol->redraw();
+ }
+}
#define GLUE_CHANNEL_H
-#include "../core/patch.h"
-
-
-using std::string;
+#include <string>
/* addChannel
/* loadChannel
* fill an existing channel with a wave. */
-int glue_loadChannel(class SampleChannel *ch, const char *fname);
+int glue_loadChannel(class SampleChannel *ch, const std::string &fname);
/* deleteChannel
* Remove a channel from Mixer. */
/* cloneChannel
* Make an exact copy of Channel *ch. */
-
+
int glue_cloneChannel(class Channel *ch);
+/* toggle/set*
+ * Toggle or set several channel properties. If gui == true the signal comes
+ * from a manual interaction on the GUI, otherwise it's a MIDI/Jack/external
+ * signal. */
+
+void glue_toggleArm(class Channel *ch, bool gui=true);
+void glue_setChanVol(class Channel *ch, float v, bool gui=true);
+void glue_setMute(class Channel *ch, bool gui=true);
+void glue_setSoloOn (class Channel *ch, bool gui=true);
+void glue_setSoloOff(class Channel *ch, bool gui=true);
+
+void glue_setPitch(class gdEditor *win, class SampleChannel *ch, float val,
+ bool numeric);
+
+void glue_setPanning(class gdEditor *win, class SampleChannel *ch, float val);
+
+/* setBeginEndChannel
+ * sets start/end points in the sample editor. Recalc=false: don't recalc
+ * internal position. check=true: check the points' consistency */
+
+void glue_setBeginEndChannel(class gdEditor *win, class SampleChannel *ch,
+ int b, int e, bool recalc=false, bool check=true);
+
+void glue_setBoost(class gdEditor *win, class SampleChannel *ch, float val,
+ bool numeric);
+
+/* setVolEditor
+ * handles the volume inside the SAMPLE EDITOR (not the main gui). The
+ * numeric flag tells if we want to handle the dial or the numeric input
+ * field. */
+
+void glue_setVolEditor(class gdEditor *win, class SampleChannel *ch, float val,
+ bool numeric);
#endif
+++ /dev/null
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * glue
- * Intermediate layer GUI <-> CORE.
- *
- * How to know if you need another glue_ function? Ask yourself if the
- * new action will ever be called via MIDI or keyboard/mouse. If yes,
- * put it here.
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
- *
- * This file is part of Giada - Your Hardcore Loopmachine.
- *
- * Giada - Your Hardcore Loopmachine is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General
- * Public License as published by the Free Software Foundation, either
- * version 3 of the License, or (at your option) any later version.
- *
- * Giada - Your Hardcore Loopmachine is distributed in the hope that it
- * will be useful, but WITHOUT ANY WARRANTY; without even the implied
- * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Giada - Your Hardcore Loopmachine. If not, see
- * <http://www.gnu.org/licenses/>.
- *
- * -------------------------------------------------------------------------- */
-
-
-#include "../gui/elems/ge_waveform.h"
-#include "../gui/elems/ge_mixed.h"
-#include "../gui/elems/ge_channel.h"
-#include "../gui/elems/ge_sampleChannel.h"
-#include "../gui/elems/ge_waveTools.h"
-#include "../gui/elems/ge_keyboard.h"
-#include "../gui/dialogs/gd_mainWindow.h"
-#include "../gui/dialogs/gd_editor.h"
-#include "../gui/dialogs/gd_warnings.h"
-#include "../utils/gui_utils.h"
-#include "../utils/utils.h"
-#include "../utils/log.h"
-#include "../core/mixerHandler.h"
-#include "../core/mixer.h"
-#include "../core/recorder.h"
-#include "../core/wave.h"
-#include "../core/pluginHost.h"
-#include "../core/channel.h"
-#include "../core/sampleChannel.h"
-#include "../core/midiChannel.h"
-#include "../core/kernelMidi.h"
-#include "../core/patch_DEPR_.h"
-#include "../core/conf.h"
-#include "glue.h"
-
-
-extern gdMainWindow *mainWin;
-extern Mixer G_Mixer;
-extern Patch_DEPR_ G_Patch_DEPR_;
-extern Conf G_Conf;
-extern bool G_audio_status;
-#ifdef WITH_VST
-extern PluginHost G_PluginHost;
-#endif
-
-
-static bool __soloSession__ = false;
-
-
-void glue_setBpm(const char *v1, const char *v2)
-{
- char buf[6];
- float value = atof(v1) + (atof(v2)/10);
- if (value < 20.0f) {
- value = 20.0f;
- sprintf(buf, "20.0");
- }
- else
- sprintf(buf, "%s.%s", v1, !strcmp(v2, "") ? "0" : v2);
-
- /* a value such as atof("120.1") will never be 120.1 but 120.0999999,
- * because of the rounding error. So we pass the real "wrong" value to
- * G_Mixer and we show the nice looking (but fake) one to the GUI. */
-
- float old_bpm = G_Mixer.bpm;
- G_Mixer.bpm = value;
- G_Mixer.updateFrameBars();
-
- /* inform recorder and actionEditor of the change */
-
- recorder::updateBpm(old_bpm, value, G_Mixer.quanto);
- gu_refreshActionEditor();
-
- mainWin->timing->setBpm(buf);
- gLog("[glue] Bpm changed to %s (real=%f)\n", buf, G_Mixer.bpm);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void glue_setBeats(int beats, int bars, bool expand)
-{
- /* temp vars to store old data (they are necessary) */
-
- int oldvalue = G_Mixer.beats;
- unsigned oldfpb = G_Mixer.totalFrames;
-
- if (beats > MAX_BEATS)
- G_Mixer.beats = MAX_BEATS;
- else if (beats < 1)
- G_Mixer.beats = 1;
- else
- G_Mixer.beats = beats;
-
- /* update bars - bars cannot be greate than beats and must be a sub
- * multiple of beats. If not, approximation to the nearest (and greater)
- * value available. */
-
- if (bars > G_Mixer.beats)
- G_Mixer.bars = G_Mixer.beats;
- else if (bars <= 0)
- G_Mixer.bars = 1;
- else if (beats % bars != 0) {
- G_Mixer.bars = bars + (beats % bars);
- if (beats % G_Mixer.bars != 0) // it could be an odd value, let's check it (and avoid it)
- G_Mixer.bars = G_Mixer.bars - (beats % G_Mixer.bars);
- }
- else
- G_Mixer.bars = bars;
-
- G_Mixer.updateFrameBars();
-
- /* update recorded actions */
-
- if (expand) {
- if (G_Mixer.beats > oldvalue)
- recorder::expand(oldfpb, G_Mixer.totalFrames);
- //else if (G_Mixer.beats < oldvalue)
- // recorder::shrink(G_Mixer.totalFrames);
- }
-
- mainWin->timing->setMeter(G_Mixer.beats, G_Mixer.bars);
- gu_refreshActionEditor(); // in case the action editor is open
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void glue_startStopSeq(bool gui)
-{
- G_Mixer.running ? glue_stopSeq(gui) : glue_startSeq(gui);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void glue_startSeq(bool gui)
-{
- G_Mixer.running = true;
-
- if (gui) {
-#ifdef __linux__
- kernelAudio::jackStart();
-#endif
- }
-
- if (G_Conf.midiSync == MIDI_SYNC_CLOCK_M) {
- kernelMidi::send(MIDI_START, -1, -1);
- kernelMidi::send(MIDI_POSITION_PTR, 0, 0);
- }
-
- if (gui) Fl::lock();
- mainWin->controller->updatePlay(1);
- if (gui) Fl::unlock();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void glue_stopSeq(bool gui) {
-
- mh_stopSequencer();
-
- if (G_Conf.midiSync == MIDI_SYNC_CLOCK_M)
- kernelMidi::send(MIDI_STOP, -1, -1);
-
-#ifdef __linux__
- if (gui)
- kernelAudio::jackStop();
-#endif
-
- /* what to do if we stop the sequencer and some action recs are active?
- * Deactivate the button and delete any 'rec on' status */
-
- if (recorder::active) {
- recorder::active = false;
- if (gui) Fl::lock();
- mainWin->controller->updateRecAction(0);
- if (gui) Fl::unlock();
- }
-
- /* if input recs are active (who knows why) we must deactivate them.
- * One might stop the sequencer while an input rec is running. */
-
- if (G_Mixer.chanInput != NULL) {
- mh_stopInputRec();
- if (gui) Fl::lock();
- mainWin->controller->updateRecInput(0);
- if (gui) Fl::unlock();
- }
-
- if (gui) Fl::lock();
- mainWin->controller->updatePlay(0);
- if (gui) Fl::unlock();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void glue_rewindSeq() {
- mh_rewindSequencer();
- if (G_Conf.midiSync == MIDI_SYNC_CLOCK_M)
- kernelMidi::send(MIDI_POSITION_PTR, 0, 0);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void glue_startStopActionRec() {
- recorder::active ? glue_stopActionRec() : glue_startActionRec();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void glue_startActionRec() {
- if (G_audio_status == false)
- return;
- if (!G_Mixer.running)
- glue_startSeq(); // start the sequencer for convenience
- recorder::active = true;
-
- Fl::lock();
- mainWin->controller->updateRecAction(1);
- Fl::unlock();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void glue_stopActionRec() {
-
- /* stop the recorder and sort new actions */
-
- recorder::active = false;
- recorder::sortActions();
-
- for (unsigned i=0; i<G_Mixer.channels.size(); i++)
- if (G_Mixer.channels.at(i)->type == CHANNEL_SAMPLE) {
- SampleChannel *ch = (SampleChannel*) G_Mixer.channels.at(i);
- if (ch->hasActions)
- ch->readActions = true;
- else
- ch->readActions = false;
- mainWin->keyboard->setChannelWithActions((gSampleChannel*)ch->guiChannel);
- }
-
- Fl::lock();
- mainWin->controller->updateRecAction(0);
- Fl::unlock();
-
- /* in case acton editor is on, refresh it */
-
- gu_refreshActionEditor();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void glue_startStopReadingRecs(SampleChannel *ch, bool gui) {
- if (ch->readActions)
- glue_stopReadingRecs(ch, gui);
- else
- glue_startReadingRecs(ch, gui);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void glue_startReadingRecs(SampleChannel *ch, bool gui) {
- if (G_Conf.treatRecsAsLoops)
- ch->recStatus = REC_WAITING;
- else
- ch->setReadActions(true, G_Conf.recsStopOnChanHalt);
- if (!gui) {
- gSampleChannel *gch = (gSampleChannel*)ch->guiChannel;
- if (gch->readActions) { // if button exists
- Fl::lock();
- gch->readActions->value(1);
- Fl::unlock();
- }
- }
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void glue_stopReadingRecs(SampleChannel *ch, bool gui) {
-
- /* if "treatRecsAsLoop" wait until the sequencer reaches beat 0, so put
- * the channel in REC_ENDING status */
-
- if (G_Conf.treatRecsAsLoops)
- ch->recStatus = REC_ENDING;
- else
- ch->setReadActions(false, G_Conf.recsStopOnChanHalt);
- if (!gui) {
- gSampleChannel *gch = (gSampleChannel*)ch->guiChannel;
- if (gch->readActions) { // if button exists
- Fl::lock();
- gch->readActions->value(0);
- Fl::unlock();
- }
- }
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void glue_quantize(int val) {
- G_Mixer.quantize = val;
- G_Mixer.updateQuanto();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void glue_setChanVol(Channel *ch, float v, bool gui) {
-
- ch->volume = v;
-
- /* also update wave editor if it's shown */
-
- gdEditor *editor = (gdEditor*) gu_getSubwindow(mainWin, WID_SAMPLE_EDITOR);
- if (editor) {
- glue_setVolEditor(editor, (SampleChannel*) ch, v, false);
- Fl::lock();
- editor->volume->value(v);
- Fl::unlock();
- }
-
- if (!gui) {
- Fl::lock();
- ch->guiChannel->vol->value(v);
- Fl::unlock();
- }
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void glue_setOutVol(float v, bool gui) {
- G_Mixer.outVol = v;
- if (!gui) {
- Fl::lock();
- mainWin->inOut->setOutVol(v);
- Fl::unlock();
- }
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void glue_setInVol(float v, bool gui)
-{
- G_Mixer.inVol = v;
- if (!gui) {
- Fl::lock();
- mainWin->inOut->setInVol(v);
- Fl::unlock();
- }
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void glue_clearAllSamples()
-{
- G_Mixer.running = false;
- for (unsigned i=0; i<G_Mixer.channels.size(); i++) {
- G_Mixer.channels.at(i)->empty();
- G_Mixer.channels.at(i)->guiChannel->reset();
- }
- recorder::init();
- return;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void glue_clearAllRecs()
-{
- recorder::init();
- gu_updateControls();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void glue_resetToInitState(bool resetGui, bool createColumns)
-{
- G_Patch_DEPR_.setDefault();
- G_Mixer.close();
- G_Mixer.init();
- recorder::init();
-#ifdef WITH_VST
- G_PluginHost.freeAllStacks(&G_Mixer.channels, &G_Mixer.mutex_plugins);
-#endif
-
- mainWin->keyboard->clear();
- if (createColumns)
- mainWin->keyboard->init();
-
- if (resetGui)
- gu_updateControls();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void glue_startStopMetronome(bool gui)
-{
- G_Mixer.metronome = !G_Mixer.metronome;
- if (!gui) {
- Fl::lock();
- mainWin->controller->updateMetronome(G_Mixer.metronome);
- Fl::unlock();
- }
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void glue_setBeginEndChannel(gdEditor *win, SampleChannel *ch, int b, int e, bool recalc, bool check)
-{
- if (check) {
- if (e > ch->wave->size)
- e = ch->wave->size;
- if (b < 0)
- b = 0;
- if (b > ch->wave->size)
- b = ch->wave->size-2;
- if (b >= ch->end)
- b = ch->begin;
- if (e <= ch->begin)
- e = ch->end;
- }
-
- /* continue only if new values != old values */
-
- if (b == ch->begin && e == ch->end)
- return;
-
- /* print mono values */
-
- char tmp[16];
- sprintf(tmp, "%d", b/2);
- win->chanStart->value(tmp);
-
- tmp[0] = '\0';
- sprintf(tmp, "%d", e/2);
- win->chanEnd->value(tmp);
-
- ch->setBegin(b);
- ch->setEnd(e);
-
- /* recalc is not needed when the user drags the bars directly over the waveform */
-
- if (recalc) {
- win->waveTools->waveform->recalcPoints(); // importante, altrimenti non si vedono
- win->waveTools->waveform->redraw();
- }
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void glue_setBoost(gdEditor *win, SampleChannel *ch, float val, bool numeric)
-{
- if (numeric) {
- if (val > 20.0f)
- val = 20.0f;
- else if (val < 0.0f)
- val = 0.0f;
-
- float linear = pow(10, (val / 20)); // linear = 10^(dB/20)
-
- ch->boost = linear;
-
- char buf[10];
- sprintf(buf, "%.2f", val);
- win->boostNum->value(buf);
- win->boostNum->redraw();
-
- win->boost->value(linear);
- win->boost->redraw(); /// inutile
- }
- else {
- ch->boost = val;
- char buf[10];
- sprintf(buf, "%.2f", 20*log10(val));
- win->boostNum->value(buf);
- win->boostNum->redraw();
- }
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void glue_setVolEditor(class gdEditor *win, SampleChannel *ch, float val, bool numeric)
-{
- if (numeric) {
- if (val > 0.0f)
- val = 0.0f;
- else if (val < -60.0f)
- val = -INFINITY;
-
- float linear = pow(10, (val / 20)); // linear = 10^(dB/20)
-
- ch->volume = linear;
-
- win->volume->value(linear);
- win->volume->redraw();
-
- char buf[10];
- if (val > -INFINITY)
- sprintf(buf, "%.2f", val);
- else
- sprintf(buf, "-inf");
- win->volumeNum->value(buf);
- win->volumeNum->redraw();
-
- ch->guiChannel->vol->value(linear);
- ch->guiChannel->vol->redraw();
- }
- else {
- ch->volume = val;
-
- float dbVal = 20 * log10(val);
- char buf[10];
- if (dbVal > -INFINITY)
- sprintf(buf, "%.2f", dbVal);
- else
- sprintf(buf, "-inf");
-
- win->volumeNum->value(buf);
- win->volumeNum->redraw();
-
- ch->guiChannel->vol->value(val);
- ch->guiChannel->vol->redraw();
- }
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void glue_setMute(Channel *ch, bool gui)
-{
- if (recorder::active && recorder::canRec(ch)) {
- if (!ch->mute)
- recorder::startOverdub(ch->index, ACTION_MUTES, G_Mixer.actualFrame);
- else
- recorder::stopOverdub(G_Mixer.actualFrame);
- }
-
- ch->mute ? ch->unsetMute(false) : ch->setMute(false);
-
- if (!gui) {
- Fl::lock();
- ch->guiChannel->mute->value(ch->mute);
- Fl::unlock();
- }
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void glue_setSoloOn(Channel *ch, bool gui)
-{
- /* if there's no solo session, store mute configuration of all chans
- * and start the session */
-
- if (!__soloSession__) {
- for (unsigned i=0; i<G_Mixer.channels.size(); i++) {
- Channel *och = G_Mixer.channels.at(i);
- och->mute_s = och->mute;
- }
- __soloSession__ = true;
- }
-
- ch->solo = !ch->solo;
- ch->sendMidiLsolo();
-
- /* 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);
- if (!och->solo && !och->mute) {
- och->setMute(false);
- Fl::lock();
- och->guiChannel->mute->value(true);
- Fl::unlock();
- }
- }
-
- if (ch->mute) {
- ch->unsetMute(false);
- Fl::lock();
- ch->guiChannel->mute->value(false);
- Fl::unlock();
- }
-
- if (!gui) {
- Fl::lock();
- ch->guiChannel->solo->value(1);
- Fl::unlock();
- }
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void glue_setSoloOff(Channel *ch, bool gui)
-{
- /* if this is uniqueSolo, stop solo session and restore mute status,
- * else mute this */
-
- if (mh_uniqueSolo(ch)) {
- __soloSession__ = false;
- for (unsigned i=0; i<G_Mixer.channels.size(); i++) {
- Channel *och = G_Mixer.channels.at(i);
- if (och->mute_s) {
- och->setMute(false);
- Fl::lock();
- och->guiChannel->mute->value(true);
- Fl::unlock();
- }
- else {
- och->unsetMute(false);
- Fl::lock();
- och->guiChannel->mute->value(false);
- Fl::unlock();
- }
- och->mute_s = false;
- }
- }
- else {
- ch->setMute(false);
- Fl::lock();
- ch->guiChannel->mute->value(true);
- Fl::unlock();
- }
-
- ch->solo = !ch->solo;
- ch->sendMidiLsolo();
-
- if (!gui) {
- Fl::lock();
- ch->guiChannel->solo->value(0);
- Fl::unlock();
- }
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void glue_setPanning(class gdEditor *win, SampleChannel *ch, float val)
-{
- if (val < 1.0f) {
- ch->panLeft = 1.0f;
- ch->panRight= 0.0f + val;
-
- char buf[8];
- sprintf(buf, "%d L", (int) abs((ch->panRight * 100.0f) - 100));
- win->panNum->value(buf);
- }
- else if (val == 1.0f) {
- ch->panLeft = 1.0f;
- ch->panRight= 1.0f;
- win->panNum->value("C");
- }
- else {
- ch->panLeft = 2.0f - val;
- ch->panRight= 1.0f;
-
- char buf[8];
- sprintf(buf, "%d R", (int) abs((ch->panLeft * 100.0f) - 100));
- win->panNum->value(buf);
- }
- win->panNum->redraw();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void glue_startStopInputRec(bool gui, bool alert)
-{
- if (G_Mixer.chanInput == NULL) {
- if (!glue_startInputRec(gui)) {
- if (alert) gdAlert("No channels available for recording.");
- else gLog("[glue] no channels available for recording\n");
- }
- }
- else
- glue_stopInputRec(gui);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-int glue_startInputRec(bool gui)
-{
- if (G_audio_status == false)
- return -1;
-
- SampleChannel *ch = mh_startInputRec();
- if (ch == NULL) { // no chans available
- Fl::lock();
- mainWin->controller->updateRecInput(0);
- Fl::unlock();
- return 0;
- }
-
- if (!G_Mixer.running) {
- glue_startSeq();
- Fl::lock();
- mainWin->controller->updatePlay(1);
- Fl::unlock();
- }
-
- glue_setChanVol(ch, 1.0f, false); // false = not from gui click
-
- ch->guiChannel->mainButton->label(ch->wave->name.c_str());
-
- if (!gui) {
- Fl::lock();
- mainWin->controller->updateRecInput(1);
- Fl::unlock();
- }
-
- return 1;
-
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-int glue_stopInputRec(bool gui)
-{
- SampleChannel *ch = mh_stopInputRec();
-
- if (ch->mode & (LOOP_BASIC | LOOP_ONCE | LOOP_REPEAT))
- ch->start(0, true, G_Mixer.quantize, G_Mixer.running); // on frame 0: user-generated event
-
- if (!gui) {
- Fl::lock();
- mainWin->controller->updateRecInput(0);
- Fl::unlock();
- }
-
- return 1;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void glue_keyPress(Channel *ch, bool ctrl, bool shift)
-{
- if (ch->type == CHANNEL_SAMPLE)
- glue_keyPress((SampleChannel*)ch, ctrl, shift);
- else
- glue_keyPress((MidiChannel*)ch, ctrl, shift);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void glue_keyRelease(Channel *ch, bool ctrl, bool shift)
-{
- if (ch->type == CHANNEL_SAMPLE)
- glue_keyRelease((SampleChannel*)ch, ctrl, shift);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void glue_keyPress(MidiChannel *ch, bool ctrl, bool shift)
-{
- if (ctrl)
- glue_setMute(ch);
- else
- if (shift)
- ch->kill(0); // on frame 0: user-generated event
- else
- ch->start(0, true, G_Mixer.quantize, G_Mixer.running); // on frame 0: user-generated event
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void glue_keyPress(SampleChannel *ch, bool ctrl, bool shift)
-{
- /* case CTRL */
-
- if (ctrl)
- glue_setMute(ch);
-
- /* case SHIFT
- *
- * action recording on:
- * if seq is playing, rec a killchan
- * action recording off:
- * if chan has recorded events:
- * | if seq is playing OR channel 'c' is stopped, de/activate recs
- * | else kill chan
- * else kill chan */
-
- else
- if (shift) {
- if (recorder::active) {
- if (G_Mixer.running) {
- ch->kill(0); // on frame 0: user-generated event
- if (recorder::canRec(ch) && !(ch->mode & LOOP_ANY)) // don't record killChan actions for LOOP channels
- recorder::rec(ch->index, ACTION_KILLCHAN, G_Mixer.actualFrame);
- }
- }
- else {
- if (ch->hasActions) {
- if (G_Mixer.running || ch->status == STATUS_OFF)
- ch->readActions ? glue_stopReadingRecs(ch) : glue_startReadingRecs(ch);
- else
- ch->kill(0); // on frame 0: user-generated event
- }
- else
- ch->kill(0); // on frame 0: user-generated event
- }
- }
-
- /* case no modifier */
-
- else {
-
- /* record now if the quantizer is off, otherwise let mixer to handle it
- * when a quantoWait has passed. Moreover, KEYPRESS and KEYREL are
- * meaningless for loop modes */
-
- if (G_Mixer.quantize == 0 &&
- recorder::canRec(ch) &&
- !(ch->mode & LOOP_ANY))
- {
- if (ch->mode == SINGLE_PRESS)
- recorder::startOverdub(ch->index, ACTION_KEYS, G_Mixer.actualFrame);
- else
- recorder::rec(ch->index, ACTION_KEYPRESS, G_Mixer.actualFrame);
- }
-
- ch->start(0, true, G_Mixer.quantize, G_Mixer.running); // on frame 0: user-generated event
- }
-
- /* the GUI update is done by gui_refresh() */
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void glue_keyRelease(SampleChannel *ch, bool ctrl, bool shift)
-{
- if (!ctrl && !shift) {
- ch->stop();
-
- /* record a key release only if channel is single_press. For any
- * other mode the KEY REL is meaningless. */
-
- if (ch->mode == SINGLE_PRESS && recorder::canRec(ch))
- recorder::stopOverdub(G_Mixer.actualFrame);
- }
-
- /* the GUI update is done by gui_refresh() */
-
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void glue_setPitch(class gdEditor *win, SampleChannel *ch, float val, bool numeric)
-{
- if (numeric) {
- if (val <= 0.0f)
- val = 0.1000f;
- if (val > 4.0f)
- val = 4.0000f;
- if (win)
- win->pitch->value(val);
- }
-
- ch->setPitch(val);
-
- if (win) {
- char buf[16];
- sprintf(buf, "%.4f", val);
- Fl::lock();
- win->pitchNum->value(buf);
- win->pitchNum->redraw();
- Fl::unlock();
- }
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-/* never expand or shrink recordings (last param of setBeats = false):
- * this is live manipulation */
-
-void glue_beatsMultiply()
-{
- glue_setBeats(G_Mixer.beats*2, G_Mixer.bars, false);
-}
-
-void glue_beatsDivide()
-{
- glue_setBeats(G_Mixer.beats/2, G_Mixer.bars, false);
-}
+++ /dev/null
-/* ---------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * glue
- * Intermediate layer GUI <-> CORE.
- *
- * How to know if you need another glue_ function? Ask yourself if the
- * new action will ever be called via MIDI or keyboard/mouse. If yes,
- * put it here.
- *
- * ---------------------------------------------------------------------
- *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
- *
- * This file is part of Giada - Your Hardcore Loopmachine.
- *
- * Giada - Your Hardcore Loopmachine is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General
- * Public License as published by the Free Software Foundation, either
- * version 3 of the License, or (at your option) any later version.
- *
- * Giada - Your Hardcore Loopmachine is distributed in the hope that it
- * will be useful, but WITHOUT ANY WARRANTY; without even the implied
- * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Giada - Your Hardcore Loopmachine. If not, see
- * <http://www.gnu.org/licenses/>.
- *
- * ------------------------------------------------------------------ */
-
-
-#ifndef GLUE_H
-#define GLUE_H
-
-
-/* keyPress / keyRelease
- * handle the key pressure, either via mouse/keyboard or MIDI. If gui
- * is true it means that the event comes from the main window (mouse,
- * keyb or MIDI), otherwise the event comes from the action recorder. */
-
-void glue_keyPress (class Channel *ch, bool ctrl=0, bool shift=0);
-void glue_keyPress (class SampleChannel *ch, bool ctrl=0, bool shift=0);
-void glue_keyPress (class MidiChannel *ch, bool ctrl=0, bool shift=0);
-void glue_keyRelease(class Channel *ch, bool ctrl=0, bool shift=0);
-void glue_keyRelease(class SampleChannel *ch, bool ctrl=0, bool shift=0);
-
-void glue_setBpm(const char *v1, const char *v2);
-void glue_setBeats(int beats, int bars, bool expand);
-
-/* start, stop, rewind sequencer
- * if gui == true the signal comes from an internal interaction on the
- * GUI, otherwise it's a MIDI/Jack/external signal. */
-
-void glue_startStopSeq(bool gui=true);
-void glue_startSeq (bool gui=true);
-void glue_stopSeq (bool gui=true);
-void glue_rewindSeq ();
-
-/* start/stopActionRec
- * handle the action recording. */
-
-void glue_startStopActionRec();
-void glue_startActionRec();
-void glue_stopActionRec();
-
-/* start/stopInputRec
- * handle the input recording (take). If gui == true the signal comes
- * from an internal interaction on the GUI, otherwise it's a
- * MIDI/Jack/external signal. Alert displays or not the popup message
- * if there are no available channels. */
-
-void glue_startStopInputRec(bool gui=true, bool alert=true);
-int glue_startInputRec (bool gui=true);
-int glue_stopInputRec (bool gui=true);
-
-/* start/stopReadingRecs
- * handle the 'R' button. If gui == true the signal comes from an
- * internal interaction on the GUI, otherwise it's a MIDI/Jack/external
- * signal. */
-
-void glue_startStopReadingRecs(class SampleChannel *ch, bool gui=true);
-void glue_startReadingRecs (class SampleChannel *ch, bool gui=true);
-void glue_stopReadingRecs (class SampleChannel *ch, bool gui=true);
-
-void glue_quantize(int val);
-
-void glue_setChanVol(class Channel *ch, float v, bool gui=true);
-void glue_setOutVol (float v, bool gui=true);
-void glue_setInVol (float v, bool gui=true);
-
-void glue_setPanning(class gdEditor *win, class SampleChannel *ch, float val);
-
-void glue_clearAllSamples();
-void glue_clearAllRecs();
-
-/* resetToInitState
- * reset Giada to init state. If resetGui also refresh all widgets. If
- * createColumns also build initial empty columns. */
-
-void glue_resetToInitState(bool resetGui=true, bool createColumns=true);
-
-void glue_startStopMetronome(bool gui=true);
-
-/* setBeginEndChannel
- * sets start/end points in the sample editor.
- * Recalc=false: don't recalc internal position
- * check=true: check the points' consistency */
-
-/** FIXME - nobody will call this via MIDI/keyb/mouse! */
-void glue_setBeginEndChannel(class gdEditor *win, class SampleChannel *ch, int b, int e,
- bool recalc=false, bool check=true);
-
-/** FIXME - nobody will call this via MIDI/keyb/mouse! */
-void glue_setBoost(class gdEditor *win, class SampleChannel *ch, float val, bool numeric);
-
-void glue_setPitch(class gdEditor *win, class SampleChannel *ch, float val, bool numeric);
-
-/* setVolEditor
- * handles the volume inside the SAMPLE EDITOR (not the main gui). The
- * numeric flag tells if we want to handle the dial or the numeric input
- * field. */
-
- /** FIXME - nobody will call this via MIDI/keyb/mouse! */
-void glue_setVolEditor(class gdEditor *win, class SampleChannel *ch, float val, bool numeric);
-
-/* mute
- * set mute on or off. If gui == true the signal comes from an internal
- * interaction on the GUI, otherwise it's a MIDI/Jack/external signal. */
-
-void glue_setMute(class Channel *ch, bool gui=true);
-
-/* solo on/off
- * set solo on and off. If gui == true the signal comes from an internal
- * interaction on the GUI, otherwise it's a MIDI/Jack/external signal. */
-
-void glue_setSoloOn (class Channel *ch, bool gui=true);
-void glue_setSoloOff(class Channel *ch, bool gui=true);
-
-/* beatsDivide/Multiply
- * shrinks or enlarges the number of beats by 2. */
-
-void glue_beatsMultiply();
-void glue_beatsDivide();
-
-#endif
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * glue
+ * Intermediate layer GUI <-> CORE.
+ *
+ * How to know if you need another glue_ function? Ask yourself if the
+ * new action will ever be called via MIDI or keyboard/mouse. If yes,
+ * put it here.
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#include <FL/Fl.H>
+#include "../gui/dialogs/gd_mainWindow.h"
+#include "../gui/dialogs/gd_warnings.h"
+#include "../gui/elems/ge_keyboard.h"
+#include "../gui/elems/channel.h"
+#include "../gui/elems/sampleChannel.h"
+#include "../utils/gui.h"
+#include "../utils/log.h"
+#include "../core/recorder.h"
+#include "../core/mixer.h"
+#include "../core/mixerHandler.h"
+#include "../core/wave.h"
+#include "../core/channel.h"
+#include "../core/sampleChannel.h"
+#include "../core/midiChannel.h"
+#include "main.h"
+#include "channel.h"
+#include "io.h"
+
+
+extern Recorder G_Recorder;
+extern bool G_audio_status;
+extern Mixer G_Mixer;
+extern gdMainWindow *G_MainWin;
+
+
+void glue_keyPress(Channel *ch, bool ctrl, bool shift)
+{
+ if (ch->type == CHANNEL_SAMPLE)
+ glue_keyPress((SampleChannel*)ch, ctrl, shift);
+ else
+ glue_keyPress((MidiChannel*)ch, ctrl, shift);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void glue_keyRelease(Channel *ch, bool ctrl, bool shift)
+{
+ if (ch->type == CHANNEL_SAMPLE)
+ glue_keyRelease((SampleChannel*)ch, ctrl, shift);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void glue_keyPress(MidiChannel *ch, bool ctrl, bool shift)
+{
+ if (ctrl)
+ glue_setMute(ch);
+ else
+ if (shift)
+ ch->kill(0); // on frame 0: user-generated event
+ else
+ ch->start(0, true, G_Mixer.quantize, G_Mixer.running, false, true); // on frame 0: user-generated event
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void glue_keyPress(SampleChannel *ch, bool ctrl, bool shift)
+{
+ /* case CTRL */
+
+ if (ctrl)
+ glue_setMute(ch);
+
+ /* case SHIFT
+ *
+ * action recording on:
+ * if seq is playing, rec a killchan
+ * action recording off:
+ * if chan has recorded events:
+ * | if seq is playing OR channel 'c' is stopped, de/activate recs
+ * | else kill chan
+ * else kill chan */
+
+ else
+ if (shift) {
+ if (G_Recorder.active) {
+ if (G_Mixer.running) {
+ ch->kill(0); // on frame 0: user-generated event
+ if (G_Recorder.canRec(ch) && !(ch->mode & LOOP_ANY)) // don't record killChan actions for LOOP channels
+ G_Recorder.rec(ch->index, ACTION_KILLCHAN, G_Mixer.actualFrame);
+ }
+ }
+ else {
+ if (ch->hasActions) {
+ if (G_Mixer.running || ch->status == STATUS_OFF)
+ ch->readActions ? glue_stopReadingRecs(ch) : glue_startReadingRecs(ch);
+ else
+ ch->kill(0); // on frame 0: user-generated event
+ }
+ else
+ ch->kill(0); // on frame 0: user-generated event
+ }
+ }
+ else { /* case no modifier */
+
+ /* record now if the quantizer is off, otherwise let mixer to handle it
+ * when a quantoWait has passed. Moreover, KEYPRESS and KEYREL are
+ * meaningless for loop modes */
+
+ if (G_Mixer.quantize == 0 &&
+ G_Recorder.canRec(ch) &&
+ !(ch->mode & LOOP_ANY))
+ {
+ if (ch->mode == SINGLE_PRESS)
+ G_Recorder.startOverdub(ch->index, ACTION_KEYS, G_Mixer.actualFrame);
+ else {
+ G_Recorder.rec(ch->index, ACTION_KEYPRESS, G_Mixer.actualFrame);
+
+ /* Why return here? You record an action (as done on line 148) and then
+ you call ch->start (line 165): Mixer, which is on another thread, reads
+ your newly recorded action if you have readActions == true, and then
+ ch->start kicks in right after it (as done on line 165).
+ The result: Mixer plays the channel (due to the new action) but ch->start
+ kills it right away (because the sample is playing). Fix: call ch->start
+ only if you are not recording anything, i.e. let Mixer play it. */
+
+ if (ch->readActions)
+ return;
+ }
+ }
+
+ /* This is a user-generated event, so it's on frame 0 */
+
+ ch->start(0, true, G_Mixer.quantize, G_Mixer.running, false, true);
+ }
+
+ /* the GUI update is done by gui_refresh() */
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void glue_keyRelease(SampleChannel *ch, bool ctrl, bool shift)
+{
+ if (ctrl || shift)
+ return;
+
+ ch->stop();
+
+ /* record a key release only if channel is single_press. For any
+ * other mode the KEY REL is meaningless. */
+
+ if (ch->mode == SINGLE_PRESS && G_Recorder.canRec(ch))
+ G_Recorder.stopOverdub(G_Mixer.actualFrame);
+
+ /* the GUI update is done by gui_refresh() */
+
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void glue_startStopActionRec(bool gui)
+{
+ G_Recorder.active ? glue_stopActionRec(gui) : glue_startActionRec(gui);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void glue_startActionRec(bool gui)
+{
+ if (G_audio_status == false)
+ return;
+
+ G_Recorder.active = true;
+
+ if (!G_Mixer.running)
+ glue_startSeq(false); // update gui ayway
+
+ if (!gui) {
+ Fl::lock();
+ G_MainWin->controller->updateRecAction(1);
+ Fl::unlock();
+ }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void glue_stopActionRec(bool gui)
+{
+ /* stop the recorder and sort new actions */
+
+ G_Recorder.active = false;
+ G_Recorder.sortActions();
+
+ for (unsigned i=0; i<G_Mixer.channels.size(); i++)
+ {
+ if (G_Mixer.channels.at(i)->type == CHANNEL_MIDI)
+ continue;
+ SampleChannel *ch = (SampleChannel*) G_Mixer.channels.at(i);
+ G_MainWin->keyboard->setChannelWithActions((geSampleChannel*)ch->guiChannel);
+ if (!ch->readActions && ch->hasActions)
+ glue_startReadingRecs(ch, false);
+ }
+
+ if (!gui) {
+ Fl::lock();
+ G_MainWin->controller->updateRecAction(0);
+ Fl::unlock();
+ }
+
+ gu_refreshActionEditor(); // in case it's open
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void glue_startStopInputRec(bool gui)
+{
+ if (G_Mixer.recording)
+ glue_stopInputRec(gui);
+ else
+ if (!glue_startInputRec(gui))
+ gdAlert("No channels armed/available for audio recording.");
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int glue_startInputRec(bool gui)
+{
+ if (G_audio_status == false)
+ return false;
+
+ if (!mh_startInputRec()) {
+ Fl::lock();
+ G_MainWin->controller->updateRecInput(0); // set it off, anyway
+ Fl::unlock();
+ return false;
+ }
+
+ if (!G_Mixer.running)
+ glue_startSeq(false); // update gui anyway
+
+ if (!gui) {
+ Fl::lock();
+ G_MainWin->controller->updateRecInput(1);
+ Fl::unlock();
+ }
+
+ /* Update sample name inside sample channels' main button. This is useless for
+ midi channel, but let's do it anyway. */
+
+ for (unsigned i=0; i<G_Mixer.channels.size(); i++)
+ G_Mixer.channels.at(i)->guiChannel->update();
+
+ return true;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int glue_stopInputRec(bool gui)
+{
+ mh_stopInputRec();
+
+ /* Start all sample channels in loop mode that were armed, i.e. that were
+ recording stuff and not yet in play. They are also started in force mode, i.e.
+ they must start playing right away at the current frame, not at the next first
+ beat. */
+
+ for (unsigned i=0; i<G_Mixer.channels.size(); i++) {
+ if (G_Mixer.channels.at(i)->type == CHANNEL_MIDI)
+ continue;
+ SampleChannel *ch = (SampleChannel*) G_Mixer.channels.at(i);
+ if (ch->mode & (LOOP_ANY) && ch->status == STATUS_OFF && ch->armed)
+ ch->start(G_Mixer.actualFrame, true, G_Mixer.quantize, G_Mixer.running, true, true);
+ }
+
+ if (!gui) {
+ Fl::lock();
+ G_MainWin->controller->updateRecInput(0);
+ Fl::unlock();
+ }
+
+ return 1;
+}
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * glue
+ * Intermediate layer GUI <-> CORE.
+ *
+ * How to know if you need another glue_ function? Ask yourself if the
+ * new action will ever be called via MIDI or keyboard/mouse. If yes,
+ * put it here.
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#ifndef GLUE_IO_H
+#define GLUE_IO_H
+
+
+/* keyPress / keyRelease
+ * handle the key pressure, either via mouse/keyboard or MIDI. If gui
+ * is true it means that the event comes from the main window (mouse,
+ * keyb or MIDI), otherwise the event comes from the action recorder. */
+
+void glue_keyPress (class Channel *ch, bool ctrl=0, bool shift=0);
+void glue_keyPress (class SampleChannel *ch, bool ctrl=0, bool shift=0);
+void glue_keyPress (class MidiChannel *ch, bool ctrl=0, bool shift=0);
+void glue_keyRelease(class Channel *ch, bool ctrl=0, bool shift=0);
+void glue_keyRelease(class SampleChannel *ch, bool ctrl=0, bool shift=0);
+
+/* start/stopActionRec
+Handles the action recording. If gui == true the signal comes from an user
+interaction, otherwise it's a MIDI/Jack/external signal. */
+
+void glue_startStopActionRec(bool gui=true);
+void glue_startActionRec(bool gui=true);
+void glue_stopActionRec(bool gui=true);
+
+/* start/stopInputRec
+Handles the input recording (take). If gui == true the signal comes from an
+internal interaction on the GUI, otherwise it's a MIDI/Jack/external signal. */
+
+void glue_startStopInputRec(bool gui=true);
+int glue_startInputRec (bool gui=true);
+int glue_stopInputRec (bool gui=true);
+
+
+#endif
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * glue
+ * Intermediate layer GUI <-> CORE.
+ *
+ * How to know if you need another glue_ function? Ask yourself if the
+ * new action will ever be called via MIDI or keyboard/mouse. If yes,
+ * put it here.
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#include <cmath>
+#include "../gui/elems/ge_waveform.h"
+#include "../gui/elems/ge_mixed.h"
+#include "../gui/elems/channel.h"
+#include "../gui/elems/sampleChannel.h"
+#include "../gui/elems/ge_waveTools.h"
+#include "../gui/elems/ge_keyboard.h"
+#include "../gui/dialogs/gd_mainWindow.h"
+#include "../gui/dialogs/gd_editor.h"
+#include "../gui/dialogs/gd_warnings.h"
+#include "../utils/gui.h"
+#include "../utils/fs.h"
+#include "../utils/log.h"
+#include "../core/mixerHandler.h"
+#include "../core/mixer.h"
+#include "../core/recorder.h"
+#include "../core/wave.h"
+#include "../core/pluginHost.h"
+#include "../core/channel.h"
+#include "../core/sampleChannel.h"
+#include "../core/midiChannel.h"
+#include "../core/kernelMidi.h"
+#include "../core/patch_DEPR_.h"
+#include "../core/conf.h"
+#include "main.h"
+
+
+extern gdMainWindow *G_MainWin;
+extern Mixer G_Mixer;
+extern Recorder G_Recorder;
+extern KernelAudio G_KernelAudio;
+extern KernelMidi G_KernelMidi;
+extern Patch_DEPR_ G_Patch_DEPR_;
+extern Conf G_Conf;
+extern bool G_audio_status;
+#ifdef WITH_VST
+extern PluginHost G_PluginHost;
+#endif
+
+
+void glue_setBpm(const char *v1, const char *v2)
+{
+ char buf[6];
+ float value = atof(v1) + (atof(v2)/10);
+ if (value < 20.0f) {
+ value = 20.0f;
+ sprintf(buf, "20.0");
+ }
+ else
+ sprintf(buf, "%s.%s", v1, !strcmp(v2, "") ? "0" : v2);
+
+ /* a value such as atof("120.1") will never be 120.1 but 120.0999999,
+ * because of the rounding error. So we pass the real "wrong" value to
+ * G_Mixer and we show the nice looking (but fake) one to the GUI. */
+
+ float old_bpm = G_Mixer.bpm;
+ G_Mixer.bpm = value;
+ G_Mixer.updateFrameBars();
+
+ /* inform recorder and actionEditor of the change */
+
+ G_Recorder.updateBpm(old_bpm, value, G_Mixer.quanto);
+ gu_refreshActionEditor();
+
+ G_MainWin->timing->setBpm(buf);
+ gu_log("[glue] Bpm changed to %s (real=%f)\n", buf, G_Mixer.bpm);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void glue_setBeats(int beats, int bars, bool expand)
+{
+ /* temp vars to store old data (they are necessary) */
+
+ int oldvalue = G_Mixer.beats;
+ unsigned oldfpb = G_Mixer.totalFrames;
+
+ if (beats > MAX_BEATS)
+ G_Mixer.beats = MAX_BEATS;
+ else if (beats < 1)
+ G_Mixer.beats = 1;
+ else
+ G_Mixer.beats = beats;
+
+ /* update bars - bars cannot be greate than beats and must be a sub
+ * multiple of beats. If not, approximation to the nearest (and greater)
+ * value available. */
+
+ if (bars > G_Mixer.beats)
+ G_Mixer.bars = G_Mixer.beats;
+ else if (bars <= 0)
+ G_Mixer.bars = 1;
+ else if (beats % bars != 0) {
+ G_Mixer.bars = bars + (beats % bars);
+ if (beats % G_Mixer.bars != 0) // it could be an odd value, let's check it (and avoid it)
+ G_Mixer.bars = G_Mixer.bars - (beats % G_Mixer.bars);
+ }
+ else
+ G_Mixer.bars = bars;
+
+ G_Mixer.updateFrameBars();
+
+ /* update recorded actions */
+
+ if (expand) {
+ if (G_Mixer.beats > oldvalue)
+ G_Recorder.expand(oldfpb, G_Mixer.totalFrames);
+ //else if (G_Mixer.beats < oldvalue)
+ // G_Recorder.shrink(G_Mixer.totalFrames);
+ }
+
+ G_MainWin->timing->setMeter(G_Mixer.beats, G_Mixer.bars);
+ gu_refreshActionEditor(); // in case the action editor is open
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void glue_startStopSeq(bool gui)
+{
+ G_Mixer.running ? glue_stopSeq(gui) : glue_startSeq(gui);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void glue_startSeq(bool gui)
+{
+ G_Mixer.running = true;
+
+ if (gui) {
+#ifdef __linux__
+ G_KernelAudio.jackStart();
+#endif
+ }
+
+ if (G_Conf.midiSync == MIDI_SYNC_CLOCK_M) {
+ G_KernelMidi.send(MIDI_START, -1, -1);
+ G_KernelMidi.send(MIDI_POSITION_PTR, 0, 0);
+ }
+
+ if (!gui) {
+ Fl::lock();
+ G_MainWin->controller->updatePlay(1);
+ Fl::unlock();
+ }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void glue_stopSeq(bool gui)
+{
+ mh_stopSequencer();
+
+ if (G_Conf.midiSync == MIDI_SYNC_CLOCK_M)
+ G_KernelMidi.send(MIDI_STOP, -1, -1);
+
+#ifdef __linux__
+ if (gui)
+ G_KernelAudio.jackStop();
+#endif
+
+ /* what to do if we stop the sequencer and some action recs are active?
+ * Deactivate the button and delete any 'rec on' status */
+
+ if (G_Recorder.active) {
+ G_Recorder.active = false;
+ Fl::lock();
+ G_MainWin->controller->updateRecAction(0);
+ Fl::unlock();
+ }
+
+ /* if input recs are active (who knows why) we must deactivate them.
+ * One might stop the sequencer while an input rec is running. */
+
+ if (G_Mixer.recording) {
+ mh_stopInputRec();
+ Fl::lock();
+ G_MainWin->controller->updateRecInput(0);
+ Fl::unlock();
+ }
+
+ if (!gui) {
+ Fl::lock();
+ G_MainWin->controller->updatePlay(0);
+ Fl::unlock();
+ }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void glue_rewindSeq()
+{
+ mh_rewindSequencer();
+ if (G_Conf.midiSync == MIDI_SYNC_CLOCK_M)
+ G_KernelMidi.send(MIDI_POSITION_PTR, 0, 0);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void glue_startStopReadingRecs(SampleChannel *ch, bool gui)
+{
+ /* When you call glue_startReadingRecs with G_Conf.treatRecsAsLoops, the
+ member value ch->readActions actually is not set to true immediately, because
+ the channel is in wait mode (REC_WAITING). ch->readActions will become true on
+ the next first beat. So a 'stop rec' command should occur also when
+ ch->readActions is false but the channel is in wait mode; this check will
+ handle the case of when you press 'R', the channel goes into REC_WAITING and
+ then you press 'R' again to undo the status. */
+
+ if (ch->readActions || (!ch->readActions && ch->recStatus == REC_WAITING))
+ glue_stopReadingRecs(ch, gui);
+ else
+ glue_startReadingRecs(ch, gui);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void glue_startReadingRecs(SampleChannel *ch, bool gui)
+{
+ if (G_Conf.treatRecsAsLoops)
+ ch->recStatus = REC_WAITING;
+ else
+ ch->setReadActions(true, G_Conf.recsStopOnChanHalt);
+ if (!gui) {
+ Fl::lock();
+ ((geSampleChannel*)ch->guiChannel)->readActions->value(1);
+ Fl::unlock();
+ }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void glue_stopReadingRecs(SampleChannel *ch, bool gui)
+{
+ /* First of all, if the mixer is not running just stop and disable everything.
+ Then if "treatRecsAsLoop" wait until the sequencer reaches beat 0, so put the
+ channel in REC_ENDING status. */
+
+ if (!G_Mixer.running) {
+ ch->recStatus = REC_STOPPED;
+ ch->readActions = false;
+ }
+ else
+ if (G_Conf.treatRecsAsLoops)
+ ch->recStatus = REC_ENDING;
+ else
+ ch->setReadActions(false, G_Conf.recsStopOnChanHalt);
+
+ if (!gui) {
+ Fl::lock();
+ ((geSampleChannel*)ch->guiChannel)->readActions->value(0);
+ Fl::unlock();
+ }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void glue_quantize(int val)
+{
+ G_Mixer.quantize = val;
+ G_Mixer.updateQuanto();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void glue_setOutVol(float v, bool gui)
+{
+ G_Mixer.outVol = v;
+ if (!gui) {
+ Fl::lock();
+ G_MainWin->inOut->setOutVol(v);
+ Fl::unlock();
+ }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void glue_setInVol(float v, bool gui)
+{
+ G_Mixer.inVol = v;
+ if (!gui) {
+ Fl::lock();
+ G_MainWin->inOut->setInVol(v);
+ Fl::unlock();
+ }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void glue_clearAllSamples()
+{
+ G_Mixer.running = false;
+ for (unsigned i=0; i<G_Mixer.channels.size(); i++) {
+ G_Mixer.channels.at(i)->empty();
+ G_Mixer.channels.at(i)->guiChannel->reset();
+ }
+ G_Recorder.init();
+ return;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void glue_clearAllRecs()
+{
+ G_Recorder.init();
+ gu_updateControls();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void glue_resetToInitState(bool resetGui, bool createColumns)
+{
+ G_Patch_DEPR_.setDefault();
+ G_Mixer.close();
+ G_Mixer.init();
+ G_Recorder.init();
+#ifdef WITH_VST
+ G_PluginHost.freeAllStacks(&G_Mixer.channels, &G_Mixer.mutex_plugins);
+#endif
+
+ G_MainWin->keyboard->clear();
+ if (createColumns)
+ G_MainWin->keyboard->init();
+
+ gu_updateMainWinLabel(G_DEFAULT_PATCH_NAME);
+
+ if (resetGui)
+ gu_updateControls();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void glue_startStopMetronome(bool gui)
+{
+ G_Mixer.metronome = !G_Mixer.metronome;
+ if (!gui) {
+ Fl::lock();
+ G_MainWin->controller->updateMetronome(G_Mixer.metronome);
+ Fl::unlock();
+ }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+/* never expand or shrink recordings (last param of setBeats = false):
+ * this is live manipulation */
+
+void glue_beatsMultiply()
+{
+ glue_setBeats(G_Mixer.beats*2, G_Mixer.bars, false);
+}
+
+void glue_beatsDivide()
+{
+ glue_setBeats(G_Mixer.beats/2, G_Mixer.bars, false);
+}
--- /dev/null
+/* ---------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * glue
+ * Intermediate layer GUI <-> CORE.
+ *
+ * How to know if you need another glue_ function? Ask yourself if the
+ * new action will ever be called via MIDI or keyboard/mouse. If yes,
+ * put it here.
+ *
+ * ---------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * ------------------------------------------------------------------ */
+
+
+#ifndef GLUE_H
+#define GLUE_H
+
+
+void glue_setBpm(const char *v1, const char *v2);
+void glue_setBeats(int beats, int bars, bool expand);
+
+/* start, stop, rewind sequencer
+If gui == true the signal comes from an user interaction on the GUI,
+otherwise it's a MIDI/Jack/external signal. */
+
+void glue_startStopSeq(bool gui=true);
+void glue_startSeq (bool gui=true);
+void glue_stopSeq (bool gui=true);
+void glue_rewindSeq ();
+
+/* start/stopReadingRecs
+Handles the 'R' button. If gui == true the signal comes from an user interaction
+on the GUI, otherwise it's a MIDI/Jack/external signal. */
+
+void glue_startStopReadingRecs(class SampleChannel *ch, bool gui=true);
+void glue_startReadingRecs (class SampleChannel *ch, bool gui=true);
+void glue_stopReadingRecs (class SampleChannel *ch, bool gui=true);
+
+void glue_quantize(int val);
+
+void glue_setOutVol (float v, bool gui=true);
+void glue_setInVol (float v, bool gui=true);
+
+void glue_clearAllSamples();
+void glue_clearAllRecs();
+
+/* resetToInitState
+ * reset Giada to init state. If resetGui also refresh all widgets. If
+ * createColumns also build initial empty columns. */
+
+void glue_resetToInitState(bool resetGui=true, bool createColumns=true);
+
+void glue_startStopMetronome(bool gui=true);
+
+/* beatsDivide/Multiply
+ * shrinks or enlarges the number of beats by 2. */
+
+void glue_beatsMultiply();
+void glue_beatsDivide();
+
+#endif
#include "../core/sampleChannel.h"
#include "../core/midiChannel.h"
#include "../core/wave.h"
-#include "../utils/gui_utils.h"
-#include "glue.h" // TODO - remove, used only for DEPR calls
+#include "../utils/gui.h"
+#include "../utils/log.h"
+#include "main.h" // TODO - remove, used only for DEPR calls
#include "channel.h"
#include "storage.h"
using std::string;
+using std::vector;
-extern gdMainWindow *mainWin;
+extern gdMainWindow *G_MainWin;
extern Mixer G_Mixer;
+extern Recorder G_Recorder;
extern Patch G_Patch;
extern Conf G_Conf;
extern Patch_DEPR_ G_Patch_DEPR_; // TODO - remove, used only for DEPR calls
static void __glue_fillPatchColumns__()
{
- for (unsigned i=0; i<mainWin->keyboard->getTotalColumns(); i++) {
- gColumn *gCol = mainWin->keyboard->getColumn(i);
+ for (unsigned i=0; i<G_MainWin->keyboard->getTotalColumns(); i++) {
+ gColumn *gCol = G_MainWin->keyboard->getColumn(i);
Patch::column_t pCol;
pCol.index = gCol->getIndex();
pCol.width = gCol->w();
__glue_fillPatchColumns__();
if (G_Patch.write(fullPath)) {
- gu_update_win_label(name.c_str());
- gLog("[glue_savePatch] patch saved as %s\n", fullPath.c_str());
+ gu_updateMainWinLabel(name);
+ gu_log("[glue_savePatch] patch saved as %s\n", fullPath.c_str());
return true;
}
return false;
{
gdSaveBrowser *browser = (gdSaveBrowser*) data;
string name = browser->getName();
- string fullPath = browser->getCurrentPath() + G_SLASH + gStripExt(name) + ".gptc";
+ string fullPath = browser->getCurrentPath() + G_SLASH + gu_stripExt(name) + ".gptc";
if (name == "") {
gdAlert("Please choose a file name.");
return;
}
- if (gFileExists(fullPath.c_str()))
+ if (gu_fileExists(fullPath))
if (!gdConfirmWin("Warning", "File exists: overwrite?"))
return;
if (__glue_savePatch__(fullPath, name, false)) { // false == not a project
- G_Conf.patchPath = gDirname(fullPath);
+ G_Conf.patchPath = gu_dirname(fullPath);
browser->do_callback();
}
else
{
gdLoadBrowser *browser = (gdLoadBrowser*) data;
string fullPath = browser->getSelectedItem();
- bool isProject = gIsProject(browser->getSelectedItem());
+ bool isProject = gu_isProject(browser->getSelectedItem());
browser->showStatusBar();
- gLog("[glue] loading %s...\n", fullPath.c_str());
+ gu_log("[glue] loading %s...\n", fullPath.c_str());
string fileToLoad = fullPath; // patch file to read from
string basePath = ""; // base path, in case of reading from a project
if (isProject) {
- fileToLoad = fullPath + G_SLASH + gStripExt(gBasename(fullPath)) + ".gptc";
+ fileToLoad = fullPath + G_SLASH + gu_stripExt(gu_basename(fullPath)) + ".gptc";
basePath = fullPath + G_SLASH;
}
bool deprecated = false;
if (res == PATCH_UNREADABLE) {
- gLog("[glue] failed reading JSON-based patch. Trying with the deprecated method\n");
+ gu_log("[glue] failed reading JSON-based patch. Trying with the deprecated method\n");
deprecated = true;
- res = glue_loadPatch__DEPR__(gBasename(fileToLoad).c_str(), fileToLoad.c_str(),
+ res = glue_loadPatch__DEPR__(gu_basename(fileToLoad).c_str(), fileToLoad.c_str(),
browser->getStatusBar(), isProject);
}
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);
- mainWin->keyboard->addColumn(col->width);
+ 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,
/* let recorder recompute the actions' positions if the current
* samplerate != patch samplerate */
- recorder::updateSamplerate(G_Conf.samplerate, G_Patch.samplerate);
+ G_Recorder.updateSamplerate(G_Conf.samplerate, G_Patch.samplerate);
/* save patchPath by taking the last dir of the broswer, in order to
* reuse it the next time */
- G_Conf.patchPath = gDirname(fullPath);
+ G_Conf.patchPath = gu_dirname(fullPath);
/* refresh GUI */
gu_updateControls();
- gu_update_win_label(G_Patch.name.c_str());
+ gu_updateMainWinLabel(G_Patch.name);
browser->setStatusBar(0.1f);
//__glue_setProgressBar__(status, 1.0f);
- gLog("[glue] patch loaded successfully\n");
+ gu_log("[glue] patch loaded successfully\n");
#ifdef WITH_VST
/* mixerHandler will update the samples inside Mixer */
- mh_loadPatch_DEPR_(isProject, gDirname(fpath).c_str());
+ mh_loadPatch_DEPR_(isProject, gu_dirname(fpath).c_str());
/* take the patch name and update the main window's title */
G_Patch_DEPR_.getName();
- gu_update_win_label(G_Patch_DEPR_.name);
+ gu_updateMainWinLabel(G_Patch_DEPR_.name);
status->value(0.4f); // progress status: 0.4
//Fl::check();
/* this one is vital: let recorder recompute the actions' positions if
* the current samplerate != patch samplerate */
- recorder::updateSamplerate(G_Conf.samplerate, G_Patch_DEPR_.samplerate);
+ G_Recorder.updateSamplerate(G_Conf.samplerate, G_Patch_DEPR_.samplerate);
/* update gui */
/* save patchPath by taking the last dir of the broswer, in order to
* reuse it the next time */
- G_Conf.patchPath = gDirname(fpath).c_str();
+ G_Conf.patchPath = gu_dirname(fpath).c_str();
- gLog("[glue] patch %s loaded\n", fname);
+ gu_log("[glue] patch %s loaded\n", fname);
#ifdef WITH_VST
if (resPlugins != 1)
gdSaveBrowser *browser = (gdSaveBrowser*) data;
string name = browser->getName();
string folderPath = browser->getCurrentPath(); //browser->getSelectedItem();
- string fullPath = folderPath + G_SLASH + gStripExt(name) + ".gprj";
+ string fullPath = folderPath + G_SLASH + gu_stripExt(name) + ".gprj";
if (name == "") {
gdAlert("Please choose a project name.");
return;
}
- if (gIsProject(fullPath.c_str()) && !gdConfirmWin("Warning", "Project exists: overwrite?"))
+ if (gu_isProject(fullPath) && !gdConfirmWin("Warning", "Project exists: overwrite?"))
return;
- if (!gDirExists(fullPath.c_str()) && !gMkdir(fullPath.c_str())) {
- gLog("[glue_saveProject] unable to make project directory!\n");
+ if (!gu_dirExists(fullPath) && !gu_mkdir(fullPath)) {
+ gu_log("[glue_saveProject] unable to make project directory!\n");
return;
}
- gLog("[glue_saveProject] project dir created: %s\n", fullPath.c_str());
+ gu_log("[glue_saveProject] project dir created: %s\n", fullPath.c_str());
/* copy all samples inside the folder. Takes and logical ones are saved
* via glue_saveSample() */
string samplePath = fullPath + G_SLASH + ch->wave->basename(true);
- if (gFileExists(samplePath.c_str()))
+ if (gu_fileExists(samplePath))
remove(samplePath.c_str());
if (ch->save(samplePath.c_str()))
ch->wave->pathfile = samplePath;
}
- string gptcPath = fullPath + G_SLASH + gStripExt(name.c_str()) + ".gptc";
+ string gptcPath = fullPath + G_SLASH + gu_stripExt(name) + ".gptc";
if (__glue_savePatch__(gptcPath, name, true)) // true == it's a project
browser->do_callback();
else
int res = glue_loadChannel((SampleChannel*) browser->getChannel(), fullPath.c_str());
if (res == SAMPLE_LOADED_OK) {
- G_Conf.samplePath = gDirname(fullPath);
+ G_Conf.samplePath = gu_dirname(fullPath);
browser->do_callback();
- mainWin->delSubWindow(WID_SAMPLE_EDITOR); // if editor is open
+ G_MainWin->delSubWindow(WID_SAMPLE_EDITOR); // if editor is open
}
else
- mainWin->keyboard->printChannelMessage(res);
+ G_MainWin->keyboard->printChannelMessage(res);
}
/* bruteforce check extension. */
- string filePath = folderPath + G_SLASH + gStripExt(name) + ".wav";
+ string filePath = folderPath + G_SLASH + gu_stripExt(name) + ".wav";
- if (gFileExists(filePath))
+ if (gu_fileExists(filePath))
if (!gdConfirmWin("Warning", "File exists: overwrite?"))
return;
if (((SampleChannel*)browser->getChannel())->save(filePath.c_str())) {
- G_Conf.samplePath = gDirname(folderPath);
+ G_Conf.samplePath = gu_dirname(folderPath);
browser->do_callback();
}
else
#define GLUE_STORAGE_H
-#include <vector>
-#include "../core/patch.h"
-
-
-using std::string;
-using std::vector;
-
-
void glue_loadPatch (void *data);
int glue_loadPatch__DEPR__(const char *fname, const char *fpath, class gProgress *status, bool isProject);
void glue_savePatch (void *data);
void glue_saveSample (void *data);
void glue_loadSample (void *data);
+
#endif
#include "../../core/kernelAudio.h"
#include "../../core/kernelMidi.h"
#include "../../core/graphics.h"
-#include "../../utils/gui_utils.h"
+#include "../../deps/juce/config.h"
+#include "../../utils/gui.h"
#include "../elems/ge_mixed.h"
#include "gd_about.h"
-extern Conf G_Conf;
+extern Conf G_Conf;
+extern KernelAudio G_KernelAudio;
+extern KernelMidi G_KernelMidi;
gdAbout::gdAbout()
"News, infos, contacts and documentation:\n"
"www.giadamusic.com",
FL_MAJOR_VERSION, FL_MINOR_VERSION, FL_PATCH_VERSION,
- kernelAudio::getRtAudioVersion().c_str(),
- kernelMidi::getRtMidiVersion().c_str(),
+ G_KernelAudio.getRtAudioVersion().c_str(),
+ G_KernelMidi.getRtMidiVersion().c_str(),
JANSSON_VERSION
#ifdef WITH_VST
, JUCE_MAJOR_VERSION, JUCE_MINOR_VERSION, JUCE_BUILDNUMBER);
-/* ---------------------------------------------------------------------
+/* -----------------------------------------------------------------------------
*
* Giada - Your Hardcore Loopmachine
*
* gd_about
*
- * ---------------------------------------------------------------------
+ * -----------------------------------------------------------------------------
*
* Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
*
* along with Giada - Your Hardcore Loopmachine. If not, see
* <http://www.gnu.org/licenses/>.
*
- * ------------------------------------------------------------------ */
+ * -------------------------------------------------------------------------- */
#ifndef GD_ABOUT_H
#define GD_ABOUT_H
-#include <FL/Fl.H>
-#include <FL/Fl_Window.H>
+
#include "../elems/ge_window.h"
-class gdAbout : public gWindow {
+class gdAbout : public gWindow
+{
private:
+
class gBox *logo;
class gBox *text;
class gClick *close;
#endif
public:
+
gdAbout();
~gdAbout();
static void cb_close(Fl_Widget *w, void *p);
inline void __cb_close();
-
};
#endif
#include <math.h>
-#include "../../utils/gui_utils.h"
+#include "../../utils/gui.h"
#include "../../core/graphics.h"
#include "../../core/mixer.h"
#include "../../core/recorder.h"
#include "../../core/conf.h"
#include "../../core/channel.h"
#include "../../core/sampleChannel.h"
-#include "../elems/ge_actionChannel.h"
-#include "../elems/ge_muteChannel.h"
-#include "../elems/ge_envelopeChannel.h"
-#include "../elems/ge_pianoRoll.h"
+#include "../elems/actionEditor.h"
+#include "../elems/envelopeEditor.h"
+#include "../elems/muteEditor.h"
+#include "../elems/noteEditor.h"
#include "../elems/ge_mixed.h"
#include "gd_actionEditor.h"
SampleChannel *ch = (SampleChannel*) chan;
- ac = new gActionChannel (scroller->x(), upperArea->y()+upperArea->h()+8, this, ch);
- mc = new gMuteChannel (scroller->x(), ac->y()+ac->h()+8, this);
- vc = new gEnvelopeChannel (scroller->x(), mc->y()+mc->h()+8, this, ACTION_VOLUME, RANGE_FLOAT, "volume");
+ ac = new geActionEditor (scroller->x(), upperArea->y()+upperArea->h()+8, this, ch);
+ mc = new geMuteEditor (scroller->x(), ac->y()+ac->h()+8, this);
+ vc = new geEnvelopeEditor(scroller->x(), mc->y()+mc->h()+8, this, ACTION_VOLUME, RANGE_FLOAT, "volume");
scroller->add(ac);
//scroller->add(new gResizerBar(ac->x(), ac->y()+ac->h(), scroller->w(), 8));
scroller->add(mc);
ac->deactivate();
}
else {
- pr = new gPianoRollContainer(scroller->x(), upperArea->y()+upperArea->h()+8, this);
+ pr = new geNoteEditor(scroller->x(), upperArea->y()+upperArea->h()+8, this);
scroller->add(pr);
scroller->add(new gResizerBar(pr->x(), pr->y()+pr->h(), scroller->w(), 8));
}
* This class calculates chan, zoom, frames per beat, and so on. Each
* sub-widget contains a pointer to this window to query those data. */
-class gdActionEditor : public gWindow {
-
+class gdActionEditor : public gWindow
+{
private:
/* update
class gClick *zoomOut;
class gScroll *scroller; // widget container
- class gActionChannel *ac;
- class gMuteChannel *mc;
- class gEnvelopeChannel *vc;
- class gPianoRollContainer *pr;
+ class geActionEditor *ac;
+ class geMuteEditor *mc;
+ class geEnvelopeEditor *vc;
+ class geNoteEditor *pr;
vector <class gActionWidget*> widgets;
* ------------------------------------------------------------------ */
-#include "../../utils/gui_utils.h"
+#include "../../utils/gui.h"
#include "../../core/mixer.h"
#include "../../core/conf.h"
-#include "../../glue/glue.h"
+#include "../../core/const.h"
+#include "../../glue/main.h"
#include "gd_beatsInput.h"
#include "gd_mainWindow.h"
#include "../../core/conf.h"
+#include "../../core/const.h"
#include "../../core/mixer.h"
-#include "../../glue/glue.h"
-#include "../../utils/gui_utils.h"
+#include "../../glue/main.h"
+#include "../../utils/gui.h"
#include "../elems/ge_mixed.h"
#include "gd_bpmInput.h"
#include "gd_mainWindow.h"
#include "../../core/channel.h"
#include "../../core/sampleChannel.h"
#include "../../core/conf.h"
-#include "../../glue/glue.h"
+#include "../../core/const.h"
+#include "../../glue/main.h"
#include "../../glue/channel.h"
#include "../../glue/storage.h"
-#include "../../utils/gui_utils.h"
+#include "../../utils/gui.h"
#include "../elems/ge_browser.h"
-#include "../elems/ge_channel.h"
+#include "../elems/channel.h"
#include "gd_browser.h"
G_Conf.browserW = w();
G_Conf.browserH = h();
G_Conf.browserPosition = browser->position();
- G_Conf.browserLastPath = gDirname(browser->getSelectedItem());
+ G_Conf.browserLastPath = gu_dirname(browser->getSelectedItem());
G_Conf.browserLastValue = browser->value();
}
/* if the selected item is a directory just load its content. If it's a file
* use it as the file name (i.e. fill name->value()). */
- if (gIsDir(path)) {
+ if (gu_isDir(path)) {
browser->loadDir(path);
where->value(browser->getCurrentDir().c_str());
}
{
string path = browser->getSelectedItem();
- if (path.empty() || !gIsDir(path)) // when click on an empty area or not a dir
+ if (path.empty() || !gu_isDir(path)) // when click on an empty area or not a dir
return;
browser->loadDir(path);
* -------------------------------------------------------------------------- */
+#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 "../../utils/gui_utils.h"
+#include "../../utils/gui.h"
#include "../../utils/log.h"
+#include "../../utils/string.h"
#include "../elems/ge_mixed.h"
#include "gd_config.h"
#include "gd_keyGrabber.h"
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;
#if defined(__linux__)
- if (kernelAudio::hasAPI(RtAudio::LINUX_ALSA))
+ if (G_KernelAudio.hasAPI(RtAudio::LINUX_ALSA))
soundsys->add("ALSA");
- if (kernelAudio::hasAPI(RtAudio::UNIX_JACK))
+ if (G_KernelAudio.hasAPI(RtAudio::UNIX_JACK))
soundsys->add("Jack");
- if (kernelAudio::hasAPI(RtAudio::LINUX_PULSE))
+ if (G_KernelAudio.hasAPI(RtAudio::LINUX_PULSE))
soundsys->add("PulseAudio");
switch (G_Conf.soundSystem) {
#elif defined(_WIN32)
- if (kernelAudio::hasAPI(RtAudio::WINDOWS_DS))
+ if (G_KernelAudio.hasAPI(RtAudio::WINDOWS_DS))
soundsys->add("DirectSound");
- if (kernelAudio::hasAPI(RtAudio::WINDOWS_ASIO))
+ if (G_KernelAudio.hasAPI(RtAudio::WINDOWS_ASIO))
soundsys->add("ASIO");
- if (kernelAudio::hasAPI(RtAudio::WINDOWS_WASAPI))
+ if (G_KernelAudio.hasAPI(RtAudio::WINDOWS_WASAPI))
soundsys->add("WASAPI");
switch (G_Conf.soundSystem) {
#elif defined (__APPLE__)
- if (kernelAudio::hasAPI(RtAudio::MACOSX_CORE))
+ if (G_KernelAudio.hasAPI(RtAudio::MACOSX_CORE))
soundsys->add("CoreAudio");
switch (G_Conf.soundSystem) {
/* fill frequency dropdown menu */
/* TODO - add fetchFrequencies() */
- int nfreq = kernelAudio::getTotalFreqs(sounddevOut->value());
+ int nfreq = G_KernelAudio.getTotalFreqs(sounddevOut->value());
for (int i=0; i<nfreq; i++) {
- int freq = kernelAudio::getFreq(sounddevOut->value(), i);
- samplerate->add(gItoa(freq).c_str());
+ int freq = G_KernelAudio.getFreq(sounddevOut->value(), i);
+ samplerate->add(gu_itoa(freq).c_str());
if (freq == G_Conf.samplerate)
samplerate->value(i);
}
buffersize->add("1024");
buffersize->add("2048");
buffersize->add("4096");
- buffersize->showItem(gItoa(G_Conf.buffersize).c_str());
+ buffersize->showItem(gu_itoa(G_Conf.buffersize).c_str());
rsmpQuality->add("Sinc best quality (very slow)");
rsmpQuality->add("Sinc medium quality (slow)");
rsmpQuality->add("Linear (very fast)");
rsmpQuality->value(G_Conf.rsmpQuality);
- delayComp->value(gItoa(G_Conf.delayComp).c_str());
+ delayComp->value(gu_itoa(G_Conf.delayComp).c_str());
delayComp->type(FL_INT_INPUT);
delayComp->maximum_size(5);
void gTabAudio::__cb_showInputInfo()
{
- unsigned dev = kernelAudio::getDeviceByName(sounddevIn->text(sounddevIn->value()));
+ unsigned dev = G_KernelAudio.getDeviceByName(sounddevIn->text(sounddevIn->value()));
new gdDevInfo(dev);
}
void gTabAudio::__cb_showOutputInfo()
{
- unsigned dev = kernelAudio::getDeviceByName(sounddevOut->text(sounddevOut->value()));
+ unsigned dev = G_KernelAudio.getDeviceByName(sounddevOut->text(sounddevOut->value()));
new gdDevInfo(dev);
}
channelsIn->clear();
- unsigned dev = kernelAudio::getDeviceByName(sounddevIn->text(sounddevIn->value()));
- unsigned chs = kernelAudio::getMaxInChans(dev);
+ unsigned dev = G_KernelAudio.getDeviceByName(sounddevIn->text(sounddevIn->value()));
+ unsigned chs = G_KernelAudio.getMaxInChans(dev);
if (chs == 0) {
channelsIn->add("none");
{
channelsOut->clear();
- unsigned dev = kernelAudio::getDeviceByName(sounddevOut->text(sounddevOut->value()));
- unsigned chs = kernelAudio::getMaxOutChans(dev);
+ unsigned dev = G_KernelAudio.getDeviceByName(sounddevOut->text(sounddevOut->value()));
+ unsigned chs = G_KernelAudio.getMaxOutChans(dev);
if (chs == 0) {
channelsOut->add("none");
return 0;
for (int i=0; i<m->size(); i++) {
- if (kernelAudio::getDeviceName(device) == "")
+ if (G_KernelAudio.getDeviceName(device) == "")
continue;
if (m->text(i) == NULL)
continue;
- if (m->text(i) == kernelAudio::getDeviceName(device))
+ if (m->text(i) == G_KernelAudio.getDeviceName(device))
return i;
}
void gTabAudio::fetchSoundDevs()
{
- if (kernelAudio::numDevs == 0) {
+ if (G_KernelAudio.numDevs == 0) {
sounddevOut->add("-- no devices found --");
sounddevOut->value(0);
sounddevIn->add("-- no devices found --");
sounddevIn->add("(disabled)");
- for (unsigned i=0; i<kernelAudio::numDevs; i++) {
+ for (unsigned i=0; i<G_KernelAudio.numDevs; i++) {
/* escaping '/', very dangerous in FLTK (it creates a submenu) */
- string tmp = kernelAudio::getDeviceName(i);
+ 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] = '-';
* way we can filter devices only for input or output, e.g. an input
* devices has 0 output channels. */
- if (kernelAudio::getMaxOutChans(i) > 0)
+ if (G_KernelAudio.getMaxOutChans(i) > 0)
sounddevOut->add(tmp.c_str());
- if (kernelAudio::getMaxInChans(i) > 0)
+ if (G_KernelAudio.getMaxInChans(i) > 0)
sounddevIn->add(tmp.c_str());
}
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__)
/* use the device name to search into the drop down menu's */
- G_Conf.soundDeviceOut = kernelAudio::getDeviceByName(sounddevOut->text(sounddevOut->value()));
- G_Conf.soundDeviceIn = kernelAudio::getDeviceByName(sounddevIn->text(sounddevIn->value()));
+ G_Conf.soundDeviceOut = G_KernelAudio.getDeviceByName(sounddevOut->text(sounddevOut->value()));
+ G_Conf.soundDeviceIn = G_KernelAudio.getDeviceByName(sounddevIn->text(sounddevIn->value()));
G_Conf.channelsOut = channelsOut->value();
G_Conf.channelsIn = channelsIn->value();
G_Conf.limitOutput = limitOutput->value();
void gTabMidi::fetchOutPorts() {
- if (kernelMidi::numOutPorts == 0) {
+ if (G_KernelMidi.numOutPorts == 0) {
portOut->add("-- no ports found --");
portOut->value(0);
portOut->deactivate();
portOut->add("(disabled)");
- for (unsigned i=0; i<kernelMidi::numOutPorts; i++)
- portOut->add(gu_removeFltkChars(kernelMidi::getOutPortName(i)).c_str());
+ 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 (kernelMidi::numInPorts == 0) {
+ if (G_KernelMidi.numInPorts == 0) {
portIn->add("-- no ports found --");
portIn->value(0);
portIn->deactivate();
portIn->add("(disabled)");
- for (unsigned i=0; i<kernelMidi::numInPorts; i++)
- portIn->add(gu_removeFltkChars(kernelMidi::getInPortName(i)).c_str());
+ 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)'
}
}
/* Preselect the 0 midimap if nothing is selected but midimaps exist. */
-
+
if (midiMap->value() == -1 && G_MidiMap.maps.size() > 0)
midiMap->value(0);
}
{
#if defined(__linux__)
- if (kernelMidi::hasAPI(RtMidi::LINUX_ALSA))
+ if (G_KernelMidi.hasAPI(RtMidi::LINUX_ALSA))
system->add("ALSA");
- if (kernelMidi::hasAPI(RtMidi::UNIX_JACK))
+ if (G_KernelMidi.hasAPI(RtMidi::UNIX_JACK))
system->add("Jack");
#elif defined(_WIN32)
- if (kernelMidi::hasAPI(RtMidi::WINDOWS_MM))
+ if (G_KernelMidi.hasAPI(RtMidi::WINDOWS_MM))
system->add("Multimedia MIDI");
#elif defined (__APPLE__)
void gTabPlugins::updateCount()
{
- string scanLabel = "Scan (" + gItoa(G_PluginHost.countAvailablePlugins()) + " found)";
+ string scanLabel = "Scan (" + gu_itoa(G_PluginHost.countAvailablePlugins()) + " found)";
scanButton->label(scanLabel.c_str());
}
void gTabPlugins::cb_onScan(float progress, void *p)
{
- string l = "Scan in progress (" + gItoa((int)(progress*100)) + "%). Please wait...";
+ string l = "Scan in progress (" + gu_itoa((int)(progress*100)) + "%). Please wait...";
((gTabPlugins *)p)->info->label(l.c_str());
Fl::wait();
}
{
info->show();
G_PluginHost.scanDir(folderPath->value(), cb_onScan, (void*) this);
- G_PluginHost.saveList(gGetHomePath() + G_SLASH + "plugins.xml");
+ G_PluginHost.saveList(gu_getHomePath() + G_SLASH + "plugins.xml");
info->hide();
updateCount();
}
-/* ---------------------------------------------------------------------
+/* -----------------------------------------------------------------------------
*
* Giada - Your Hardcore Loopmachine
*
* gd_config
*
- * ---------------------------------------------------------------------
+ * -----------------------------------------------------------------------------
*
* Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
*
* along with Giada - Your Hardcore Loopmachine. If not, see
* <http://www.gnu.org/licenses/>.
*
- * ------------------------------------------------------------------ */
+ * -------------------------------------------------------------------------- */
+
#ifndef GD_CONFIG_H
#define GD_CONFIG_H
-#include <FL/Fl.H>
-#include <FL/Fl_Double_Window.H>
-#include <FL/Fl_Tabs.H>
-#include "../elems/ge_window.h"
-
-using std::string;
+#include "../elems/ge_window.h"
class gdConfig : public gWindow
#include "../../core/kernelAudio.h"
-#include "../../utils/gui_utils.h"
-#include "../../utils/utils.h"
+#include "../../utils/gui.h"
+#include "../../utils/string.h"
#include "../elems/ge_mixed.h"
#include "gd_devInfo.h"
+extern KernelAudio G_KernelAudio;
+
+
using std::string;
gdDevInfo::gdDevInfo(unsigned dev)
-: Fl_Window(340, 300, "Device information") {
+ : Fl_Window(340, 300, "Device information")
+{
set_modal();
text = new gBox(8, 8, 320, 200, "", (Fl_Align) (FL_ALIGN_LEFT | FL_ALIGN_TOP));
string body = "";
int lines = 7;
- body = "Device name: " + kernelAudio::getDeviceName(dev) + "\n";
- body += "Total output(s): " + gItoa(kernelAudio::getMaxOutChans(dev)) + "\n";
- body += "Total intput(s): " + gItoa(kernelAudio::getMaxInChans(dev)) + "\n";
- body += "Duplex channel(s): " + gItoa(kernelAudio::getDuplexChans(dev)) + "\n";
- body += "Default output: " + string(kernelAudio::isDefaultOut(dev) ? "yes" : "no") + "\n";
- body += "Default input: " + string(kernelAudio::isDefaultIn(dev) ? "yes" : "no") + "\n";
+ body = "Device name: " + G_KernelAudio.getDeviceName(dev) + "\n";
+ body += "Total output(s): " + gu_itoa(G_KernelAudio.getMaxOutChans(dev)) + "\n";
+ body += "Total intput(s): " + gu_itoa(G_KernelAudio.getMaxInChans(dev)) + "\n";
+ body += "Duplex channel(s): " + gu_itoa(G_KernelAudio.getDuplexChans(dev)) + "\n";
+ body += "Default output: " + string(G_KernelAudio.isDefaultOut(dev) ? "yes" : "no") + "\n";
+ body += "Default input: " + string(G_KernelAudio.isDefaultIn(dev) ? "yes" : "no") + "\n";
- int totalFreq = kernelAudio::getTotalFreqs(dev);
- body += "Supported frequencies: " + gItoa(totalFreq);
+ int totalFreq = G_KernelAudio.getTotalFreqs(dev);
+ body += "Supported frequencies: " + gu_itoa(totalFreq);
for (int i=0; i<totalFreq; i++) {
if (i % 6 == 0) {
body += "\n "; // add new line each 6 printed freqs AND on the first line (i % 0 != 0)
lines++;
}
- body += gItoa( kernelAudio::getFreq(dev, i)) + " ";
+ body += gu_itoa( G_KernelAudio.getFreq(dev, i)) + " ";
}
text->copy_label(body.c_str());
-/* ---------------------------------------------------------------------
+/* -----------------------------------------------------------------------------
*
* Giada - Your Hardcore Loopmachine
*
* gd_editor
*
- * ---------------------------------------------------------------------
+ * -----------------------------------------------------------------------------
*
* Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
*
* along with Giada - Your Hardcore Loopmachine. If not, see
* <http://www.gnu.org/licenses/>.
*
- * ------------------------------------------------------------------ */
+ * -------------------------------------------------------------------------- */
-#include "../../utils/gui_utils.h"
-#include "../../glue/glue.h"
+#include "../../glue/channel.h"
#include "../../core/waveFx.h"
#include "../../core/conf.h"
+#include "../../core/const.h"
#include "../../core/graphics.h"
-#include "../../core/mixerHandler.h"
-#include "../../core/channel.h"
#include "../../core/sampleChannel.h"
#include "../../core/mixer.h"
#include "../../core/wave.h"
+#include "../elems/channel.h"
#include "../elems/ge_waveform.h"
#include "../elems/ge_mixed.h"
-#include "../elems/ge_channel.h"
#include "../elems/ge_waveTools.h"
-#include "../elems/ge_keyboard.h"
-#include "gd_editor.h"
-#include "gd_mainWindow.h"
#include "gd_warnings.h"
+#include "gd_editor.h"
-extern Mixer G_Mixer;
-extern gdMainWindow *mainWin;
-extern Conf G_Conf;
+extern Mixer G_Mixer;
+extern Conf G_Conf;
gdEditor::gdEditor(SampleChannel *ch)
Fl_Group *tools = new Fl_Group(8, waveTools->y()+waveTools->h()+8, w()-16, 130);
tools->begin();
- volume = new gDial (tools->x()+42, tools->y(), 20, 20, "Volume");
+ volume = new gDial (tools->x()+50, tools->y(), 20, 20, "Volume");
volumeNum = new gInput(volume->x()+volume->w()+4, tools->y(), 46, 20, "dB");
- boost = new gDial (volumeNum->x()+volumeNum->w()+80, tools->y(), 20, 20, "Boost");
- boostNum = new gInput(boost->x()+boost->w()+4, tools->y(), 46, 20, "dB");
+ boost = new gDial (volumeNum->x()+volumeNum->w()+108, tools->y(), 20, 20, "Boost");
+ boostNum = new gInput(boost->x()+boost->w()+4, tools->y(), 44, 20, "dB");
normalize = new gClick(boostNum->x()+boostNum->w()+54, tools->y(), 70, 20, "Normalize");
pan = new gDial (normalize->x()+normalize->w()+40, tools->y(), 20, 20, "Pan");
panNum = new gInput(pan->x()+pan->w()+4, tools->y(), 45, 20, "%");
- pitch = new gDial (tools->x()+42, volume->y()+volume->h()+4, 20, 20, "Pitch");
+ pitch = new gDial (tools->x()+50, volume->y()+volume->h()+4, 20, 20, "Pitch");
pitchNum = new gInput(pitch->x()+pitch->w()+4, volume->y()+volume->h()+4, 46, 20);
- pitchToBar = new gClick(pitchNum->x()+pitchNum->w()+4, volume->y()+volume->h()+4, 46, 20, "To bar");
- pitchToSong = new gClick(pitchToBar->x()+pitchToBar->w()+4, volume->y()+volume->h()+4, 46, 20, "To song");
- pitchHalf = new gClick(pitchToSong->x()+pitchToSong->w()+4, volume->y()+volume->h()+4, 21, 20, "÷");
- pitchDouble = new gClick(pitchHalf->x()+pitchHalf->w()+4, volume->y()+volume->h()+4, 21, 20, "×");
+ pitchToBar = new gClick(pitchNum->x()+pitchNum->w()+4, volume->y()+volume->h()+4, 60, 20, "To bar");
+ pitchToSong = new gClick(pitchToBar->x()+pitchToBar->w()+4, volume->y()+volume->h()+4, 60, 20, "To song");
+ pitchHalf = new gClick(pitchToSong->x()+pitchToSong->w()+4, volume->y()+volume->h()+4, 20, 20, "", divideOff_xpm, divideOn_xpm);
+ pitchDouble = new gClick(pitchHalf->x()+pitchHalf->w()+4, volume->y()+volume->h()+4, 20, 20, "", multiplyOff_xpm, multiplyOn_xpm);
pitchReset = new gClick(pitchDouble->x()+pitchDouble->w()+4, volume->y()+volume->h()+4, 46, 20, "Reset");
reload = new gClick(pitchReset->x()+pitchReset->w()+4, volume->y()+volume->h()+4, 70, 20, "Reload");
- chanStart = new gInput(tools->x()+52, pitch->y()+pitch->h()+4, 60, 20, "Start");
- chanEnd = new gInput(chanStart->x()+chanStart->w()+40, pitch->y()+pitch->h()+4, 60, 20, "End");
- resetStartEnd = new gClick(chanEnd->x()+chanEnd->w()+4, pitch->y()+pitch->h()+4, 46, 20, "Reset");
+ chanStart = new gInput(tools->x()+60, pitch->y()+pitch->h()+4, 60, 20, "Range");
+ chanEnd = new gInput(chanStart->x()+chanStart->w()+4, pitch->y()+pitch->h()+4, 60, 20, "");
+ resetStartEnd = new gClick(chanEnd->x()+chanEnd->w()+4, pitch->y()+pitch->h()+4, 60, 20, "Reset");
tools->end();
tools->resizable(new gBox(panNum->x()+panNum->w()+4, tools->y(), 80, tools->h()));
if (ch->panRight < 1.0f) {
char buf[8];
- sprintf(buf, "%d L", (int) abs((ch->panRight * 100.0f) - 100));
+ sprintf(buf, "%d L", (int) std::abs((ch->panRight * 100.0f) - 100));
pan->value(ch->panRight);
panNum->value(buf);
}
}
else {
char buf[8];
- sprintf(buf, "%d R", (int) abs((ch->panLeft * 100.0f) - 100));
+ sprintf(buf, "%d R", (int) std::abs((ch->panLeft * 100.0f) - 100));
pan->value(2.0f - ch->panLeft);
panNum->value(buf);
}
}
-/* ------------------------------------------------------------------ */
+/* -------------------------------------------------------------------------- */
gdEditor::~gdEditor()
}
-/* ------------------------------------------------------------------ */
+/* -------------------------------------------------------------------------- */
void gdEditor::cb_setChanPos (Fl_Widget *w, void *p) { ((gdEditor*)p)->__cb_setChanPos(); }
void gdEditor::cb_enableSnap (Fl_Widget *w, void *p) { ((gdEditor*)p)->__cb_enableSnap(); }
-/* ------------------------------------------------------------------ */
+/* -------------------------------------------------------------------------- */
void gdEditor::__cb_enableSnap()
}
-/* ------------------------------------------------------------------ */
+/* -------------------------------------------------------------------------- */
void gdEditor::__cb_setPitchToBar()
}
-/* ------------------------------------------------------------------ */
+/* -------------------------------------------------------------------------- */
void gdEditor::__cb_setPitchToSong()
}
-/* ------------------------------------------------------------------ */
+/* -------------------------------------------------------------------------- */
void gdEditor::__cb_resetPitch()
}
-/* ------------------------------------------------------------------ */
+/* -------------------------------------------------------------------------- */
void gdEditor::__cb_setChanPos()
}
-/* ------------------------------------------------------------------ */
+/* -------------------------------------------------------------------------- */
void gdEditor::__cb_resetStartEnd()
}
-/* ------------------------------------------------------------------ */
+/* -------------------------------------------------------------------------- */
void gdEditor::__cb_setVolume()
}
-/* ------------------------------------------------------------------ */
+/* -------------------------------------------------------------------------- */
void gdEditor::__cb_setVolumeNum()
}
-/* ------------------------------------------------------------------ */
+/* -------------------------------------------------------------------------- */
void gdEditor::__cb_setBoost()
}
-/* ------------------------------------------------------------------ */
+/* -------------------------------------------------------------------------- */
void gdEditor::__cb_setBoostNum()
}
-/* ------------------------------------------------------------------ */
+/* -------------------------------------------------------------------------- */
void gdEditor::__cb_normalize()
}
-/* ------------------------------------------------------------------ */
+/* -------------------------------------------------------------------------- */
void gdEditor::__cb_panning()
}
-/* ------------------------------------------------------------------ */
+/* -------------------------------------------------------------------------- */
void gdEditor::__cb_reload()
}
-/* ------------------------------------------------------------------ */
+/* -------------------------------------------------------------------------- */
void gdEditor::__cb_setPitch()
}
-/* ------------------------------------------------------------------ */
+/* -------------------------------------------------------------------------- */
void gdEditor::__cb_setPitchNum()
}
-/* ------------------------------------------------------------------ */
+/* -------------------------------------------------------------------------- */
void gdEditor::__cb_setPitchHalf()
}
-/* ------------------------------------------------------------------ */
+/* -------------------------------------------------------------------------- */
void gdEditor::__cb_setPitchDouble()
}
-/* ------------------------------------------------------------------ */
+/* -------------------------------------------------------------------------- */
void gdEditor::__cb_zoomIn()
}
-/* ------------------------------------------------------------------ */
+/* -------------------------------------------------------------------------- */
void gdEditor::__cb_zoomOut()
}
-/* ------------------------------------------------------------------ */
+/* -------------------------------------------------------------------------- */
void gdEditor::__cb_changeGrid()
-/* ---------------------------------------------------------------------
+/* -----------------------------------------------------------------------------
*
* Giada - Your Hardcore Loopmachine
*
* gd_editor
*
- * ---------------------------------------------------------------------
+ * -----------------------------------------------------------------------------
*
* Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
*
* along with Giada - Your Hardcore Loopmachine. If not, see
* <http://www.gnu.org/licenses/>.
*
- * ------------------------------------------------------------------ */
+ * -------------------------------------------------------------------------- */
+
#ifndef GD_EDITOR_H
#define GD_EDITOR_H
-#include <FL/Fl.H>
-#include <FL/Fl_Double_Window.H>
-#include <math.h>
-#include "../elems/ge_window.h"
+#include "../elems/ge_window.h"
-class gdEditor : public gWindow {
+class gdEditor : public gWindow
+{
private:
static void cb_setChanPos (Fl_Widget *w, void *p);
class SampleChannel *ch;
};
+
#endif
* -------------------------------------------------------------------------- */
-#include "../../utils/gui_utils.h"
+#include "../../utils/gui.h"
#include "../../core/conf.h"
#include "../../core/channel.h"
#include "../../core/sampleChannel.h"
#include "../../utils/log.h"
#include "../elems/ge_keyboard.h"
#include "../elems/ge_mixed.h"
-#include "../elems/ge_channel.h"
-#include "../elems/ge_channelButton.h"
+#include "../elems/channel.h"
+#include "../elems/channelButton.h"
#include "gd_keyGrabber.h"
#include "gd_config.h"
#include "gd_mainWindow.h"
&& x != FL_End
&& x != ' ')
{
- gLog("set key '%c' (%d) for channel %d\n", x, x, ch->index);
+ gu_log("set key '%c' (%d) for channel %d\n", x, x, ch->index);
setButtonLabel(x);
updateText(x);
break;
}
else
- gLog("invalid key\n");
+ gu_log("invalid key\n");
}
}
return(ret);
-/* ---------------------------------------------------------------------
+/* -----------------------------------------------------------------------------
*
* Giada - Your Hardcore Loopmachine
*
* gd_mainWindow
*
- * ---------------------------------------------------------------------
+ * -----------------------------------------------------------------------------
*
* Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
*
* along with Giada - Your Hardcore Loopmachine. If not, see
* <http://www.gnu.org/licenses/>.
*
- * ------------------------------------------------------------------ */
+ * -------------------------------------------------------------------------- */
#ifdef __linux__
#include "../../core/sampleChannel.h"
#include "../../core/init.h"
#include "../../core/patch_DEPR_.h"
+#include "../../core/patch.h"
#include "../../core/conf.h"
#include "../../core/pluginHost.h"
-#include "../../glue/glue.h"
+#include "../../glue/main.h"
#include "../../glue/storage.h"
#include "../elems/ge_keyboard.h"
#include "gd_warnings.h"
extern Patch_DEPR_ G_Patch_DEPR_;
extern Patch G_Patch;
extern Conf G_Conf;
-extern gdMainWindow *mainWin;
+extern gdMainWindow *G_MainWin;
extern bool G_quit;
extern bool G_audio_status;
size_range(GUI_WIDTH, GUI_HEIGHT);
menu = new gMenu(8, -1);
- inOut = new gInOut(414, 8);
+ inOut = new gInOut(412, 8);
controller = new gController(8, 39);
timing = new gTiming(628, 44);
beatMeter = new gBeatMeter(100, 83, 609, 20);
}
-/* ------------------------------------------------------------------ */
+/* -------------------------------------------------------------------------- */
-void gdMainWindow::cb_endprogram(Fl_Widget *v, void *p) { mainWin->__cb_endprogram(); }
+void gdMainWindow::cb_endprogram(Fl_Widget *v, void *p) { G_MainWin->__cb_endprogram(); }
-/* ------------------------------------------------------------------ */
+/* -------------------------------------------------------------------------- */
void gdMainWindow::__cb_endprogram()
}
-/* ------------------------------------------------------------------ */
-/* ------------------------------------------------------------------ */
-/* ------------------------------------------------------------------ */
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
gInOut::gInOut(int x, int y)
- : Fl_Group(x, y, 394, 20)
+ : Fl_Group(x, y, 396, 20)
{
begin();
#if defined(WITH_VST)
masterFxIn = new gFxButton (x, y, 20, 20, fxOff_xpm, fxOn_xpm);
inVol = new gDial (masterFxIn->x()+masterFxIn->w()+4, y, 20, 20);
- inMeter = new gSoundMeter(inVol->x()+inVol->w()+4, y+5, 140, 10);
- inToOut = new gClick (inMeter->x()+inMeter->w()+4, y+5, 10, 10, "");
- outMeter = new gSoundMeter(inToOut->x()+inToOut->w()+4, y+5, 140, 10);
+ inMeter = new gSoundMeter(inVol->x()+inVol->w()+4, y+4, 140, 12);
+ inToOut = new gClick (inMeter->x()+inMeter->w()+4, y+4, 12, 12, "", inputToOutputOff_xpm, inputToOutputOn_xpm);
+ outMeter = new gSoundMeter(inToOut->x()+inToOut->w()+4, y+4, 140, 12);
outVol = new gDial (outMeter->x()+outMeter->w()+4, y, 20, 20);
masterFxOut = new gFxButton (outVol->x()+outVol->w()+4, y, 20, 20, fxOff_xpm, fxOn_xpm);
#else
inVol = new gDial (x+62, y, 20, 20);
- inMeter = new gSoundMeter(inVol->x()+inVol->w()+4, y+5, 140, 10);
- outMeter = new gSoundMeter(inMeter->x()+inMeter->w()+4, y+5, 140, 10);
+ inMeter = new gSoundMeter(inVol->x()+inVol->w()+4, y+5, 140, 12);
+ outMeter = new gSoundMeter(inMeter->x()+inMeter->w()+4, y+5, 140, 12);
outVol = new gDial (outMeter->x()+outMeter->w()+4, y, 20, 20);
#endif
}
-/* ------------------------------------------------------------------ */
+/* -------------------------------------------------------------------------- */
void gInOut::cb_outVol (Fl_Widget *v, void *p) { ((gInOut*)p)->__cb_outVol(); }
#endif
-/* ------------------------------------------------------------------ */
+/* -------------------------------------------------------------------------- */
void gInOut::__cb_outVol()
}
-/* ------------------------------------------------------------------ */
+/* -------------------------------------------------------------------------- */
void gInOut::__cb_inVol()
}
-/* ------------------------------------------------------------------ */
+/* -------------------------------------------------------------------------- */
#ifdef WITH_VST
void gInOut::__cb_masterFxOut()
{
- gu_openSubWindow(mainWin, new gdPluginList(PluginHost::MASTER_OUT), WID_FX_LIST);
+ gu_openSubWindow(G_MainWin, new gdPluginList(PluginHost::MASTER_OUT), WID_FX_LIST);
}
void gInOut::__cb_masterFxIn()
{
- gu_openSubWindow(mainWin, new gdPluginList(PluginHost::MASTER_IN), WID_FX_LIST);
+ gu_openSubWindow(G_MainWin, new gdPluginList(PluginHost::MASTER_IN), WID_FX_LIST);
}
void gInOut::__cb_inToOut()
#endif
-/* ------------------------------------------------------------------ */
+/* -------------------------------------------------------------------------- */
void gInOut::refresh()
}
-/* ------------------------------------------------------------------ */
-/* ------------------------------------------------------------------ */
-/* ------------------------------------------------------------------ */
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
gMenu::gMenu(int x, int y)
}
-/* ------------------------------------------------------------------ */
+/* -------------------------------------------------------------------------- */
void gMenu::cb_about (Fl_Widget *v, void *p) { ((gMenu*)p)->__cb_about(); }
void gMenu::cb_edit (Fl_Widget *v, void *p) { ((gMenu*)p)->__cb_edit(); }
-/* ------------------------------------------------------------------ */
+/* -------------------------------------------------------------------------- */
void gMenu::__cb_about()
{
- gu_openSubWindow(mainWin, new gdAbout(), WID_ABOUT);
+ gu_openSubWindow(G_MainWin, new gdAbout(), WID_ABOUT);
}
-/* ------------------------------------------------------------------ */
+/* -------------------------------------------------------------------------- */
void gMenu::__cb_config()
{
- gu_openSubWindow(mainWin, new gdConfig(380, 370), WID_CONFIG);
+ gu_openSubWindow(G_MainWin, new gdConfig(380, 370), WID_CONFIG);
}
-/* ------------------------------------------------------------------ */
+/* -------------------------------------------------------------------------- */
void gMenu::__cb_file()
if (strcmp(m->label(), "Open patch or project...") == 0) {
//gWindow *childWin = new gdBrowser("Load Patch", G_Conf.patchPath.c_str(), 0, BROWSER_LOAD_PATCH);
- //gu_openSubWindow(mainWin, childWin, WID_FILE_BROWSER);
+ //gu_openSubWindow(G_MainWin, childWin, WID_FILE_BROWSER);
gWindow *childWin = new gdLoadBrowser(G_Conf.browserX, G_Conf.browserY,
G_Conf.browserW, G_Conf.browserH, "Load patch or project",
G_Conf.patchPath, glue_loadPatch, NULL);
- gu_openSubWindow(mainWin, childWin, WID_FILE_BROWSER);
+ gu_openSubWindow(G_MainWin, childWin, WID_FILE_BROWSER);
return;
}
if (strcmp(m->label(), "Save patch...") == 0) {
gWindow *childWin = new gdSaveBrowser(G_Conf.browserX, G_Conf.browserY,
G_Conf.browserW, G_Conf.browserH, "Save patch",
G_Conf.patchPath, G_Patch.name, glue_savePatch, NULL);
- gu_openSubWindow(mainWin, childWin, WID_FILE_BROWSER);
+ gu_openSubWindow(G_MainWin, childWin, WID_FILE_BROWSER);
return;
}
if (strcmp(m->label(), "Save project...") == 0) {
gWindow *childWin = new gdSaveBrowser(G_Conf.browserX, G_Conf.browserY,
G_Conf.browserW, G_Conf.browserH, "Save project",
G_Conf.patchPath, G_Patch.name, glue_saveProject, NULL);
- gu_openSubWindow(mainWin, childWin, WID_FILE_BROWSER);
+ gu_openSubWindow(G_MainWin, childWin, WID_FILE_BROWSER);
return;
}
if (strcmp(m->label(), "Quit Giada") == 0) {
- mainWin->do_callback();
+ G_MainWin->do_callback();
return;
}
}
-/* ------------------------------------------------------------------ */
+/* -------------------------------------------------------------------------- */
void gMenu::__cb_edit()
if (strcmp(m->label(), "Clear all samples") == 0) {
if (!gdConfirmWin("Warning", "Clear all samples: are you sure?"))
return;
- mainWin->delSubWindow(WID_SAMPLE_EDITOR);
+ G_MainWin->delSubWindow(WID_SAMPLE_EDITOR);
glue_clearAllSamples();
return;
}
if (strcmp(m->label(), "Clear all actions") == 0) {
if (!gdConfirmWin("Warning", "Clear all actions: are you sure?"))
return;
- mainWin->delSubWindow(WID_ACTION_EDITOR);
+ G_MainWin->delSubWindow(WID_ACTION_EDITOR);
glue_clearAllRecs();
return;
}
return;
}
if (strcmp(m->label(), "Remove empty columns") == 0) {
- mainWin->keyboard->organizeColumns();
+ G_MainWin->keyboard->organizeColumns();
return;
}
if (strcmp(m->label(), "Setup global MIDI input...") == 0) {
- gu_openSubWindow(mainWin, new gdMidiInputMaster(), 0);
+ gu_openSubWindow(G_MainWin, new gdMidiInputMaster(), 0);
return;
}
}
-/* ------------------------------------------------------------------ */
-/* ------------------------------------------------------------------ */
-/* ------------------------------------------------------------------ */
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
gTiming::gTiming(int x, int y)
quantizer = new gChoice(x, y, 40, 20, "", false);
bpm = new gClick (quantizer->x()+quantizer->w()+4, y, 40, 20);
meter = new gClick (bpm->x()+bpm->w()+8, y, 40, 20, "4/1");
- multiplier = new gClick (meter->x()+meter->w()+4, y, 20, 20, "", beatsMultiplyOff_xpm, beatsMultiplyOn_xpm);
- divider = new gClick (multiplier->x()+multiplier->w()+4, y, 20, 20, "", beatsDivideOff_xpm, beatsDivideOn_xpm);
+ multiplier = new gClick (meter->x()+meter->w()+4, y, 20, 20, "", multiplyOff_xpm, multiplyOn_xpm);
+ divider = new gClick (multiplier->x()+multiplier->w()+4, y, 20, 20, "", divideOff_xpm, divideOn_xpm);
end();
}
-/* ------------------------------------------------------------------ */
+/* -------------------------------------------------------------------------- */
void gTiming::cb_bpm (Fl_Widget *v, void *p) { ((gTiming*)p)->__cb_bpm(); }
void gTiming::cb_divider (Fl_Widget *v, void *p) { ((gTiming*)p)->__cb_divider(); }
-/* ------------------------------------------------------------------ */
+/* -------------------------------------------------------------------------- */
void gTiming::__cb_bpm()
{
- gu_openSubWindow(mainWin, new gdBpmInput(bpm->label()), WID_BPM);
+ gu_openSubWindow(G_MainWin, new gdBpmInput(bpm->label()), WID_BPM);
}
-/* ------------------------------------------------------------------ */
+/* -------------------------------------------------------------------------- */
void gTiming::__cb_meter()
{
- gu_openSubWindow(mainWin, new gdBeatsInput(), WID_BEATS);
+ gu_openSubWindow(G_MainWin, new gdBeatsInput(), WID_BEATS);
}
-/* ------------------------------------------------------------------ */
+/* -------------------------------------------------------------------------- */
void gTiming::__cb_quantizer()
}
-/* ------------------------------------------------------------------ */
+/* -------------------------------------------------------------------------- */
void gTiming::__cb_multiplier()
}
-/* ------------------------------------------------------------------ */
+/* -------------------------------------------------------------------------- */
void gTiming::__cb_divider()
}
-/* ------------------------------------------------------------------ */
+/* -------------------------------------------------------------------------- */
void gTiming::setBpm(const char *v)
}
-/* ------------------------------------------------------------------ */
+/* -------------------------------------------------------------------------- */
void gTiming::setMeter(int beats, int bars)
-/* ---------------------------------------------------------------------
+/* -----------------------------------------------------------------------------
*
* Giada - Your Hardcore Loopmachine
* gd_mainWindow
*
- * ---------------------------------------------------------------------
+ * -----------------------------------------------------------------------------
*
* Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
*
* along with Giada - Your Hardcore Loopmachine. If not, see
* <http://www.gnu.org/licenses/>.
*
- * ------------------------------------------------------------------ */
+ * -------------------------------------------------------------------------- */
#ifndef GD_MAINWINDOW_H
#include "../elems/ge_controller.h"
-/* ------------------------------------------------------------------ */
+/* -------------------------------------------------------------------------- */
class gdMainWindow : public gWindow
};
-/* ------------------------------------------------------------------ */
+/* -------------------------------------------------------------------------- */
class gInOut : public Fl_Group
};
-/* ------------------------------------------------------------------ */
+/* -------------------------------------------------------------------------- */
class gMenu : public Fl_Group
};
-/* ------------------------------------------------------------------ */
+/* -------------------------------------------------------------------------- */
class gTiming : public Fl_Group
* -------------------------------------------------------------------------- */
-#include "../../utils/gui_utils.h"
+#include "../../utils/gui.h"
#include "../../core/kernelMidi.h"
#include "../../core/conf.h"
+#include "../../core/const.h"
#include "../../core/sampleChannel.h"
#include "../../utils/log.h"
+#include "../../utils/string.h"
#include "../elems/ge_mixed.h"
#include "../elems/ge_midiIoTools.h"
#include "gd_midiInput.h"
-extern Conf G_Conf;
+extern Conf G_Conf;
+extern KernelMidi G_KernelMidi;
+
+
+using std::string;
gdMidiInput::gdMidiInput(int w, int h, const char *title)
/* -------------------------------------------------------------------------- */
-gdMidiInput::~gdMidiInput() {
- kernelMidi::stopMidiLearn();
+gdMidiInput::~gdMidiInput()
+{
+ G_KernelMidi.stopMidiLearn();
}
/* -------------------------------------------------------------------------- */
-void gdMidiInput::stopMidiLearn(gLearner *learner) {
- kernelMidi::stopMidiLearn();
+void gdMidiInput::stopMidiLearn(gLearner *learner)
+{
+ G_KernelMidi.stopMidiLearn();
learner->updateValue();
}
/* -------------------------------------------------------------------------- */
-void gdMidiInput::__cb_learn(uint32_t *param, uint32_t msg, gLearner *l) {
+void gdMidiInput::__cb_learn(uint32_t *param, uint32_t msg, gLearner *l)
+{
*param = msg;
stopMidiLearn(l);
- gLog("[gdMidiGrabber] MIDI learn done - message=0x%X\n", msg);
+ gu_log("[gdMidiGrabber] MIDI learn done - message=0x%X\n", msg);
}
/* -------------------------------------------------------------------------- */
-void gdMidiInput::cb_learn(uint32_t msg, void *d) {
+void gdMidiInput::cb_learn(uint32_t msg, void *d)
+{
cbData *data = (cbData*) d;
gdMidiInput *window = (gdMidiInput*) data->window;
gLearner *learner = data->learner;
/* -------------------------------------------------------------------------- */
-void gdMidiInput::__cb_close() {
+void gdMidiInput::__cb_close()
+{
do_callback();
}
gdMidiInputChannel::gdMidiInputChannel(Channel *ch)
- : gdMidiInput(300, 206, "MIDI Input Setup"),
+ : gdMidiInput(300, 230, "MIDI Input Setup"),
ch(ch)
{
- char title[64];
- sprintf(title, "MIDI Input Setup (channel %d)", ch->index+1);
- label(title);
+ string title = "MIDI Input Setup (channel " + gu_itoa(ch->index+1) + ")";
+ label(title.c_str());
set_modal();
new gLearner(8, 30, w()-16, "key press", cb_learn, &ch->midiInKeyPress);
new gLearner(8, 54, w()-16, "key release", cb_learn, &ch->midiInKeyRel);
new gLearner(8, 78, w()-16, "key kill", cb_learn, &ch->midiInKill);
- new gLearner(8, 102, w()-16, "mute", cb_learn, &ch->midiInMute);
- new gLearner(8, 126, w()-16, "solo", cb_learn, &ch->midiInSolo);
- new gLearner(8, 150, w()-16, "volume", cb_learn, &ch->midiInVolume);
- int yy = 178;
+ new gLearner(8, 102, w()-16, "arm", cb_learn, &ch->midiInArm);
+ new gLearner(8, 126, w()-16, "mute", cb_learn, &ch->midiInMute);
+ new gLearner(8, 150, w()-16, "solo", cb_learn, &ch->midiInSolo);
+ new gLearner(8, 174, w()-16, "volume", cb_learn, &ch->midiInVolume);
+ int yy = 202;
if (ch->type == CHANNEL_SAMPLE) {
- size(300, 254);
- new gLearner(8, 174, w()-16, "pitch", cb_learn, &((SampleChannel*)ch)->midiInPitch);
- new gLearner(8, 198, w()-16, "read actions", cb_learn, &((SampleChannel*)ch)->midiInReadActions);
- yy = 226;
+ size(300, 278);
+ new gLearner(8, 198, w()-16, "pitch", cb_learn, &((SampleChannel*)ch)->midiInPitch);
+ new gLearner(8, 222, w()-16, "read actions", cb_learn, &((SampleChannel*)ch)->midiInReadActions);
+ yy = 250;
}
ok = new gButton(w()-88, yy, 80, 20, "Close");
/* -------------------------------------------------------------------------- */
-void gdMidiInputChannel::__cb_enable() {
+void gdMidiInputChannel::__cb_enable()
+{
ch->midiIn = enable->value();
}
#define GD_MIDI_INPUT_H
-#include "../../core/kernelMidi.h"
-#include "../../utils/utils.h"
#include "../elems/ge_window.h"
-#include "../elems/ge_mixed.h"
class gdMidiInput : public gWindow
{
protected:
- gClick *ok;
+ class gClick *ok;
void stopMidiLearn(class gLearner *l);
class Channel *ch;
- gCheck *enable;
-
+ class gCheck *enable;
//gVector <gLearner *> items; for future use, with vst parameters
#include "../../core/sampleChannel.h"
#include "../../core/conf.h"
#include "../../core/midiChannel.h"
-#include "../../utils/gui_utils.h"
+#include "../../utils/gui.h"
+#include "../../utils/log.h"
#include "../elems/ge_mixed.h"
-#include "../elems/ge_channel.h"
+#include "../elems/channel.h"
#include "../elems/ge_midiIoTools.h"
#include "../elems/ge_keyboard.h"
#include "gd_midiOutput.h"
-extern Conf G_Conf;
+extern Conf G_Conf;
+extern KernelMidi G_KernelMidi;
gdMidiOutput::gdMidiOutput(int w, int h)
- //: gWindow(300, 64, "Midi Output Setup")
: gWindow(w, h, "Midi Output Setup")
{
}
/* -------------------------------------------------------------------------- */
-void gdMidiOutput::stopMidiLearn(gLearner *learner) {
- kernelMidi::stopMidiLearn();
+void gdMidiOutput::stopMidiLearn(gLearner *learner)
+{
+ G_KernelMidi.stopMidiLearn();
learner->updateValue();
}
/* -------------------------------------------------------------------------- */
-void gdMidiOutput::__cb_learn(uint32_t *param, uint32_t msg, gLearner *l) {
+void gdMidiOutput::__cb_learn(uint32_t *param, uint32_t msg, gLearner *l)
+{
*param = msg;
stopMidiLearn(l);
- gLog("[gdMidiGrabber] MIDI learn done - message=0x%X\n", msg);
+ gu_log("[gdMidiGrabber] MIDI learn done - message=0x%X\n", msg);
}
/* -------------------------------------------------------------------------- */
-void gdMidiOutput::cb_learn(uint32_t msg, void *d) {
+void gdMidiOutput::cb_learn(uint32_t msg, void *d)
+{
cbData *data = (cbData*) d;
gdMidiOutput *window = (gdMidiOutput*) data->window;
gLearner *learner = data->learner;
/* -------------------------------------------------------------------------- */
-void gdMidiOutput::__cb_close() {
+void gdMidiOutput::__cb_close()
+{
do_callback();
}
/* -------------------------------------------------------------------------- */
-void gdMidiOutput::cb_enableLightning(Fl_Widget *w, void *p) {
+void gdMidiOutput::cb_enableLightning(Fl_Widget *w, void *p)
+{
((gdMidiOutput*)p)->__cb_enableLightning();
}
/* -------------------------------------------------------------------------- */
-void gdMidiOutputMidiCh::__cb_enableChanList() {
+void gdMidiOutputMidiCh::__cb_enableChanList()
+{
enableOut->value() ? chanListOut->activate() : chanListOut->deactivate();
}
/* -------------------------------------------------------------------------- */
-void gdMidiOutputMidiCh::__cb_close() {
+void gdMidiOutputMidiCh::__cb_close()
+{
ch->midiOut = enableOut->value();
ch->midiOutChan = chanListOut->value();
ch->midiOutL = enableLightning->value();
/* -------------------------------------------------------------------------- */
-void gdMidiOutputSampleCh::__cb_close() {
+void gdMidiOutputSampleCh::__cb_close()
+{
ch->midiOutL = enableLightning->value();
do_callback();
}
#ifdef WITH_VST
-#include "../../utils/gui_utils.h"
+#include "../../utils/gui.h"
#include "../../core/channel.h"
#include "../../core/mixer.h"
#include "../../core/conf.h"
#ifdef WITH_VST
-#include "../../utils/gui_utils.h"
-#include "../../utils/utils.h"
+#include "../../utils/gui.h"
+#include "../../utils/fs.h"
#include "../../core/conf.h"
+#include "../../core/const.h"
#include "../../core/graphics.h"
#include "../../core/pluginHost.h"
#include "../../core/plugin.h"
#include "../../core/mixer.h"
#include "../../core/channel.h"
+#include "../../utils/log.h"
+#include "../../utils/string.h"
#include "../elems/ge_mixed.h"
-#include "../elems/ge_channel.h"
+#include "../elems/channel.h"
#include "gd_pluginList.h"
#include "gd_pluginChooser.h"
#include "gd_pluginWindow.h"
extern Conf G_Conf;
extern Mixer G_Mixer;
extern PluginHost G_PluginHost;
-extern gdMainWindow *mainWin;
+extern gdMainWindow *G_MainWin;
using std::string;
if (stackType == PluginHost::MASTER_IN)
label("Master In Plugins");
else {
- string l = "Channel " + gItoa(ch->index+1) + " Plugins";
+ string l = "Channel " + gu_itoa(ch->index+1) + " Plugins";
copy_label(l.c_str());
}
gdPluginListMaster */
if (stackType == PluginHost::MASTER_OUT) {
- mainWin->inOut->setMasterFxOutFull(
+ G_MainWin->inOut->setMasterFxOutFull(
G_PluginHost.countPlugins(stackType, ch) > 0);
}
else
if (stackType == PluginHost::MASTER_IN) {
- mainWin->inOut->setMasterFxInFull(
+ G_MainWin->inOut->setMasterFxInFull(
G_PluginHost.countPlugins(stackType, ch) > 0);
}
else {
gWindow *w;
if (pPlugin->hasEditor()) {
if (pPlugin->isEditorOpen()) {
- gLog("[gdPlugin::__cb_openPluginWindow] plugin has editor but it's already visible\n");
+ gu_log("[gdPlugin::__cb_openPluginWindow] plugin has editor but it's already visible\n");
return;
}
int pwid = pPlugin->getId()+1;
- gLog("[gdPlugin::__cb_openPluginWindow] plugin has editor, open window id=%d\n", pwid);
+ gu_log("[gdPlugin::__cb_openPluginWindow] plugin has editor, open window id=%d\n", pwid);
if (pParent->hasWindow(pwid))
pParent->delSubWindow(pwid);
#include <FL/Fl_Scroll.H>
-#include "../../utils/gui_utils.h"
+#include "../../utils/gui.h"
#include "../../core/plugin.h"
#include "../elems/ge_mixed.h"
#include "gd_pluginWindow.h"
#include "../../utils/log.h"
-#include "../../utils/gui_utils.h"
+#include "../../utils/gui.h"
#include "../../core/pluginHost.h"
#include "../../core/plugin.h"
#include "../../core/const.h"
#endif
- gLog("[gdPluginWindowGUI] opening GUI, this=%p, xid=%p\n",
+ gu_log("[gdPluginWindowGUI] opening GUI, this=%p, xid=%p\n",
(void*) this, (void*) fl_xid(this));
#if defined(__APPLE__)
int pluginW = pPlugin->getEditorW();
int pluginH = pPlugin->getEditorH();
- printf("w=%d h=%d\n", Fl::w(), Fl::h());
-
resize((Fl::w() - pluginW) / 2, (Fl::h() - pluginH) / 2, pluginW, pluginH);
Fl::add_timeout(GUI_PLUGIN_RATE, cb_refresh, (void*) this);
{
Fl::remove_timeout(cb_refresh);
pPlugin->closeEditor();
- gLog("[gdPluginWindowGUI::__cb_close] GUI closed, this=%p\n", (void*) this);
+ gu_log("[gdPluginWindowGUI::__cb_close] GUI closed, this=%p\n", (void*) this);
}
void gdPluginWindowGUI::__cb_refresh()
{
- //gLog("[gdPluginWindowGUI::__cb_refresh] refresh!\n");
+ //gu_log("[gdPluginWindowGUI::__cb_refresh] refresh!\n");
G_PluginHost.runDispatchLoop();
Fl::repeat_timeout(GUI_PLUGIN_RATE, cb_refresh, (void*) this);
}
#include <FL/Fl_Window.H>
#include <FL/Fl_Box.H>
#include "../elems/ge_mixed.h"
-#include "../../utils/gui_utils.h"
+#include "../../utils/gui.h"
void gdAlert(const char *c);
--- /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 "ge_keyboard.h"
+#include "actionEditor.h"
+
+
+extern gdMainWindow *G_MainWin;
+extern Mixer G_Mixer;
+extern Conf G_Conf;
+extern Recorder G_Recorder;
+
+
+/* -------------------------------------------------------------------------- */
+
+
+geActionEditor::geActionEditor(int x, int y, gdActionEditor *pParent, SampleChannel *ch)
+ : geBaseActionEditor(x, y, 200, 40, pParent),
+ ch (ch),
+ selected (NULL)
+{
+ size(pParent->totalWidth, h());
+
+ /* add actions when the window opens. Their position is zoom-based;
+ * each frame is / 2 because we don't care about stereo infos. */
+
+ for (unsigned i=0; 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);
+ }
+
+ G_Recorder.sortActions();
+
+ index++; // important!
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gAction::delAction()
+{
+ /* if SINGLE_PRESS you must delete both the keypress and the keyrelease
+ * actions. */
+
+ if (ch->mode == SINGLE_PRESS) {
+ G_Recorder.deleteAction(parent->chan->index, frame_a, ACTION_KEYPRESS, false);
+ G_Recorder.deleteAction(parent->chan->index, frame_b, ACTION_KEYREL, false);
+ }
+ else
+ G_Recorder.deleteAction(parent->chan->index, frame_a, type, false);
+
+ /* restore the initial cursor shape, in case you delete an action and
+ * the double arrow (for resizing) is displayed */
+
+ fl_cursor(FL_CURSOR_DEFAULT, FL_WHITE, FL_BLACK);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gAction::moveAction(int frame_a)
+{
+ /* easy one: delete previous action and record the new ones. As usual,
+ * SINGLE_PRESS requires two jobs. If frame_a is valid, use that frame
+ * value. */
+
+ delAction();
+
+ if (frame_a != -1)
+ this->frame_a = frame_a;
+ else
+ this->frame_a = xToFrame_a();
+
+
+ /* always check frame parity */
+
+ if (this->frame_a % 2 != 0)
+ this->frame_a++;
+
+ G_Recorder.rec(parent->chan->index, type, this->frame_a);
+
+ if (ch->mode == SINGLE_PRESS) {
+ frame_b = xToFrame_b();
+ G_Recorder.rec(parent->chan->index, ACTION_KEYREL, frame_b);
+ }
+
+ G_Recorder.sortActions();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int gAction::absx()
+{
+ return x() - parent->ac->x();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int gAction::xToFrame_a()
+{
+ return (absx()) * parent->zoom;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int gAction::xToFrame_b()
+{
+ return (absx() + w()) * parent->zoom;
+}
--- /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
+ *
+ * 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
+ *
+ * ge_channel
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#include "../../core/mixer.h"
+#include "../../core/conf.h"
+#include "../../core/channel.h"
+#include "../../core/patch_DEPR_.h"
+#include "../../core/graphics.h"
+#include "../../core/pluginHost.h"
+#include "../../utils/gui.h"
+#include "../../glue/channel.h"
+#include "../../glue/main.h"
+#include "../dialogs/gd_mainWindow.h"
+#include "../dialogs/gd_pluginList.h"
+#include "ge_column.h"
+#include "channelButton.h"
+#include "channel.h"
+
+
+extern Mixer G_Mixer;
+extern Conf G_Conf;
+extern Patch_DEPR_ G_Patch_DEPR_;
+extern gdMainWindow *G_MainWin;
+
+
+geChannel::geChannel(int X, int Y, int W, int H, int type, Channel *ch)
+ : Fl_Group(X, Y, W, H, NULL),
+ ch (ch),
+ type (type)
+{
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geChannel::cb_arm(Fl_Widget *v, void *p) { ((geChannel*)p)->__cb_arm(); }
+void geChannel::cb_mute(Fl_Widget *v, void *p) { ((geChannel*)p)->__cb_mute(); }
+void geChannel::cb_solo(Fl_Widget *v, void *p) { ((geChannel*)p)->__cb_solo(); }
+void geChannel::cb_changeVol(Fl_Widget *v, void *p) { ((geChannel*)p)->__cb_changeVol(); }
+#ifdef WITH_VST
+void geChannel::cb_openFxWindow(Fl_Widget *v, void *p) { ((geChannel*)p)->__cb_openFxWindow(); }
+#endif
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geChannel::__cb_arm()
+{
+ glue_toggleArm(ch, true);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geChannel::__cb_mute()
+{
+ glue_setMute(ch);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geChannel::__cb_solo()
+{
+ solo->value() ? glue_setSoloOn(ch) : glue_setSoloOff(ch);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geChannel::__cb_changeVol()
+{
+ glue_setChanVol(ch, vol->value());
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+#ifdef WITH_VST
+void geChannel::__cb_openFxWindow()
+{
+ gu_openSubWindow(G_MainWin, new gdPluginList(PluginHost::CHANNEL, ch), WID_FX_LIST);
+}
+#endif
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int geChannel::keyPress(int e)
+{
+ return handleKey(e, ch->key);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+
+int geChannel::getColumnIndex()
+{
+ return ((gColumn*)parent())->getIndex();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geChannel::blink()
+{
+ if (gu_getBlinker() > 6)
+ mainButton->setPlayMode();
+ else
+ mainButton->setDefaultMode();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geChannel::setColorsByStatus(int playStatus, int recStatus)
+{
+ switch (playStatus) {
+ case STATUS_OFF:
+ case STATUS_EMPTY:
+ mainButton->setDefaultMode();
+ button->imgOn = channelPlay_xpm;
+ button->imgOff = channelStop_xpm;
+ button->redraw();
+ break;
+ case STATUS_PLAY:
+ mainButton->setPlayMode();
+ button->imgOn = channelStop_xpm;
+ button->imgOff = channelPlay_xpm;
+ button->redraw();
+ break;
+ case STATUS_WAIT:
+ blink();
+ break;
+ case STATUS_ENDING:
+ mainButton->setEndingMode();
+ break;
+ }
+
+ switch (recStatus) {
+ case REC_WAITING:
+ blink();
+ break;
+ case REC_ENDING:
+ mainButton->setEndingMode();
+ break;
+ }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geChannel::packWidgets()
+{
+ /* Count visible widgets and resize mainButton according to how many widgets
+ are visible. */
+
+ int visibles = 0;
+ for (int i=0; i<children(); i++) {
+ child(i)->size(20, 20); // also normalize widths
+ if (child(i)->visible())
+ visibles++;
+ }
+ mainButton->size(w() - ((visibles - 1) * (24)), 20); // -1: exclude itself
+
+ /* Reposition everything else */
+
+ for (int i=1, p=0; i<children(); i++) {
+ if (!child(i)->visible())
+ continue;
+ for (int k=i-1; k>=0; k--) // Get the first visible item prior to i
+ if (child(k)->visible()) {
+ p = k;
+ break;
+ }
+ child(i)->position(child(p)->x() + child(p)->w() + 4, y());
+ }
+
+ init_sizes(); // Resets the internal array of widget sizes and positions
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int geChannel::handleKey(int e, int key)
+{
+ int ret;
+ if (e == FL_KEYDOWN && button->value()) // key already pressed! skip it
+ ret = 1;
+ else
+ if (Fl::event_key() == key && !button->value()) {
+ button->take_focus(); // move focus to this button
+ button->value((e == FL_KEYDOWN || e == FL_SHORTCUT) ? 1 : 0); // change the button's state
+ button->do_callback(); // invoke the button's callback
+ ret = 1;
+ }
+ else
+ ret = 0;
+
+ if (Fl::event_key() == key)
+ button->value((e == FL_KEYDOWN || e == FL_SHORTCUT) ? 1 : 0); // change the button's state
+
+ return ret;
+}
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * ge_channel
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#ifndef GE_CHANNEL_H
+#define GE_CHANNEL_H
+
+
+#include <FL/Fl_Group.H>
+
+
+class geChannel : public Fl_Group
+{
+protected:
+
+ /* Define some breakpoints for dynamic resize. BREAK_DELTA: base amount of
+ pixels to shrink sampleButton. */
+
+#ifdef WITH_VST
+ static const int BREAK_READ_ACTIONS = 240;
+ static const int BREAK_MODE_BOX = 216;
+ static const int BREAK_FX = 192;
+ static const int BREAK_ARM = 168;
+#else
+ static const int BREAK_READ_ACTIONS = 216;
+ static const int BREAK_MODE_BOX = 192;
+ static const int BREAK_ARM = 168;
+#endif
+
+ static void cb_arm (Fl_Widget *v, void *p);
+ static void cb_mute (Fl_Widget *v, void *p);
+ static void cb_solo (Fl_Widget *v, void *p);
+ static void cb_changeVol (Fl_Widget *v, void *p);
+#ifdef WITH_VST
+ static void cb_openFxWindow(Fl_Widget *v, void *p);
+#endif
+
+ inline void __cb_mute();
+ inline void __cb_arm();
+ inline void __cb_solo();
+ inline void __cb_changeVol();
+#ifdef WITH_VST
+ inline void __cb_openFxWindow();
+#endif
+
+ /* blink
+ * blink button when channel is in wait/ending status. */
+
+ void blink();
+
+ /* setColorByStatus
+ * update colors depending on channel status. */
+
+ void setColorsByStatus(int playStatus, int recStatus);
+
+ /* handleKey
+ * method wrapped by virtual handle(int e). */
+
+ int handleKey(int e, int key);
+
+ /* packWidgets
+ Spread widgets across available space. */
+
+ void packWidgets();
+
+public:
+
+ geChannel(int x, int y, int w, int h, int type, class Channel *ch);
+
+ /* reset
+ * reset channel to initial status. */
+
+ virtual void reset() = 0;
+
+ /* update
+ * update the label of sample button and everything else such as 'R'
+ * button, key box and so on, according to global values. */
+
+ virtual void update() = 0;
+
+ /* refresh
+ * update graphics. */
+
+ virtual void refresh() = 0;
+
+ /* keypress
+ * what to do when the corresponding key is pressed. */
+
+ int keyPress(int event);
+
+ /* getColumnIndex
+ * return the numeric index of the column in which this channel is
+ * located. */
+
+ int getColumnIndex();
+
+ class Channel *ch;
+
+ class gButton *button;
+ class gStatus *status;
+ class gClick *arm;
+ class geChannelButton *mainButton;
+ class gClick *mute;
+ class gClick *solo;
+ class gDial *vol;
+#ifdef WITH_VST
+ class gFxButton *fx;
+#endif
+
+ int type;
+};
+
+
+#endif
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * ge_channelButton
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#include "../../core/const.h"
+#include "../../utils/fs.h"
+#include "channelButton.h"
+
+
+using std::string;
+
+
+geChannelButton::geChannelButton(int x, int y, int w, int h, const char *l)
+ : gClick(x, y, w, h, l), key("") {}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geChannelButton::setKey(const string &k)
+{
+ key = k;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geChannelButton::setKey(int k)
+{
+ if (k == 0)
+ key = "";
+ else {
+ // FIXME - this crap won't work with unicode/utf-8
+ char c = (char) k;
+ key = c;
+ }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geChannelButton::draw()
+{
+ gClick::draw();
+
+ if (key == "")
+ return;
+
+ /* draw background */
+
+ fl_rectf(x()+1, y()+1, 18, h()-2, bgColor0);
+
+ /* draw key */
+
+ fl_color(COLOR_TEXT_0);
+ fl_font(FL_HELVETICA, 11);
+ fl_draw(key.c_str(), x(), y(), 18, h(), FL_ALIGN_CENTER);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geChannelButton::setInputRecordMode()
+{
+ bgColor0 = COLOR_BG_3;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geChannelButton::setActionRecordMode()
+{
+ bgColor0 = COLOR_BG_4;
+ txtColor = COLOR_TEXT_0;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geChannelButton::setDefaultMode(const char *l)
+{
+ bgColor0 = COLOR_BG_0;
+ bdColor = COLOR_BD_0;
+ txtColor = COLOR_TEXT_0;
+ if (l)
+ label(l);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geChannelButton::setPlayMode()
+{
+ bgColor0 = COLOR_BG_2;
+ bdColor = COLOR_BD_1;
+ txtColor = COLOR_TEXT_1;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geChannelButton::setEndingMode()
+{
+ bgColor0 = COLOR_BD_0;
+}
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * ge_channelButton
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#ifndef GE_CHANNEL_BUTTON_H
+#define GE_CHANNEL_BUTTON_H
+
+
+#include "ge_mixed.h"
+
+
+using std::string;
+
+
+class geChannelButton : public gClick
+{
+private:
+
+ string key;
+
+public:
+
+ geChannelButton(int x, int y, int w, int h, const char *l=0);
+
+ virtual int handle(int e) = 0;
+
+ void draw();
+ void setKey(const string &k);
+ void setKey(int k);
+ void setPlayMode();
+ void setEndingMode();
+ void setDefaultMode(const char *l=0);
+ void setInputRecordMode();
+ void setActionRecordMode();
+};
+
+
+#endif
--- /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 "ge_keyboard.h"
+#include "envelopeEditor.h"
+
+
+extern Mixer G_Mixer;
+extern Recorder G_Recorder;
+extern gdMainWindow *G_MainWin;
+
+
+geEnvelopeEditor::geEnvelopeEditor(int x, int y, gdActionEditor *pParent,
+ int type, int range, const char *l)
+ : geBaseActionEditor(x, y, 200, 80, pParent),
+ l (l),
+ type (type),
+ range (range),
+ selectedPoint (-1),
+ draggedPoint (-1)
+{
+ size(pParent->totalWidth, h());
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+geEnvelopeEditor::~geEnvelopeEditor() {
+ clearPoints();
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void geEnvelopeEditor::addPoint(int frame, int iValue, float fValue, int px, int py) {
+ point p;
+ p.frame = frame;
+ p.iValue = iValue;
+ p.fValue = fValue;
+ p.x = px;
+ p.y = py;
+ points.push_back(p);
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void geEnvelopeEditor::updateActions() {
+ for (unsigned i=0; 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);
+ }
+
+ /* line between 2 points y = (x-a) / (b-a); a = h() - 8; b = 1 */
+
+ int frame = mx * pParent->zoom;
+ float value = (my - h() + 8) / (float) (1 - h() + 8);
+ addPoint(frame, 0, value, mx, my);
+ G_Recorder.rec(pParent->chan->index, type, frame, 0, value);
+ G_Recorder.sortActions();
+ sortPoints();
+ }
+ else {
+ /// TODO
+ }
+ G_MainWin->keyboard->setChannelWithActions((geSampleChannel*)pParent->chan->guiChannel); // update mainWindow
+ redraw();
+ }
+ }
+ else {
+
+ /* right click on point 0 or point size-1 deletes the entire envelope */
+
+ if (selectedPoint != -1) {
+ if (selectedPoint == 0 || (unsigned) selectedPoint == points.size()-1) {
+ G_Recorder.clearAction(pParent->chan->index, type);
+ points.clear();
+ }
+ else {
+ G_Recorder.deleteAction(pParent->chan->index, points.at(selectedPoint).frame, type, false);
+ G_Recorder.sortActions();
+ points.erase(points.begin() + selectedPoint);
+ }
+ G_MainWin->keyboard->setChannelWithActions((geSampleChannel*)pParent->chan->guiChannel); // update mainWindow
+ redraw();
+ }
+ }
+
+ ret = 1;
+ break;
+ }
+
+ case FL_RELEASE: {
+ if (draggedPoint != -1) {
+
+ if (points.at(draggedPoint).x == previousXPoint) {
+ //gu_log("nothing to do\n");
+ }
+ else {
+ int newFrame = points.at(draggedPoint).x * pParent->zoom;
+
+ /* x edge correction */
+
+ if (newFrame < 0)
+ newFrame = 0;
+ else if (newFrame > G_Mixer.totalFrames)
+ newFrame = G_Mixer.totalFrames;
+
+ /* vertical line check */
+
+ int vp = verticalPoint(points.at(draggedPoint));
+ if (vp == 1) newFrame -= 256;
+ else if (vp == -1) newFrame += 256;
+
+ /* delete previous point and record a new one */
+
+ G_Recorder.deleteAction(pParent->chan->index, points.at(draggedPoint).frame, type, false);
+
+ if (range == RANGE_FLOAT) {
+ float value = (points.at(draggedPoint).y - h() + 8) / (float) (1 - h() + 8);
+ G_Recorder.rec(pParent->chan->index, type, newFrame, 0, value);
+ }
+ else {
+ /// TODO
+ }
+
+ G_Recorder.sortActions();
+ points.at(draggedPoint).frame = newFrame;
+ draggedPoint = -1;
+ selectedPoint = -1;
+ }
+ }
+ ret = 1;
+ break;
+ }
+
+ case FL_DRAG: {
+
+ if (draggedPoint != -1) {
+
+ /* y constraint */
+
+ if (my > h()-8)
+ points.at(draggedPoint).y = h()-8;
+ else
+ if (my < 1)
+ points.at(draggedPoint).y = 1;
+ else
+ points.at(draggedPoint).y = my;
+
+ /* x constraint
+ * constrain the point between two ends (leftBorder-point, point-point,
+ * point-rightBorder). First & last points cannot be shifted on x */
+
+ if (draggedPoint == 0)
+ points.at(draggedPoint).x = x()-8;
+ else
+ if ((unsigned) draggedPoint == points.size()-1)
+ points.at(draggedPoint).x = pParent->coverX;
+ else {
+ int prevPoint = points.at(draggedPoint-1).x;
+ int nextPoint = points.at(draggedPoint+1).x;
+ if (mx <= prevPoint)
+ points.at(draggedPoint).x = prevPoint;
+ else
+ if (mx >= nextPoint)
+ points.at(draggedPoint).x = nextPoint;
+ //else
+ // points.at(draggedPoint).x = mx;
+ else {
+ if (pParent->gridTool->isOn())
+ points.at(draggedPoint).x = pParent->gridTool->getSnapPoint(mx)-1;
+ else
+ points.at(draggedPoint).x = mx;
+ }
+ }
+ redraw();
+ }
+
+ ret = 1;
+ break;
+ }
+ }
+
+ return ret;
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+int geEnvelopeEditor::verticalPoint(const point &p) {
+ for (unsigned i=0; i<points.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_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/glue.h"
-#include "../../utils/log.h"
-#include "../dialogs/gd_mainWindow.h"
-#include "../dialogs/gd_actionEditor.h"
-#include "ge_keyboard.h"
-#include "ge_actionChannel.h"
-
-
-extern gdMainWindow *mainWin;
-extern Mixer G_Mixer;
-extern Conf G_Conf;
-
-
-/* -------------------------------------------------------------------------- */
-
-
-gActionChannel::gActionChannel(int x, int y, gdActionEditor *pParent, SampleChannel *ch)
- : gActionWidget(x, y, 200, 40, pParent),
- ch (ch),
- selected (NULL)
-{
- size(pParent->totalWidth, h());
-
- /* add actions when the window opens. Their position is zoom-based;
- * each frame is / 2 because we don't care about stereo infos. */
-
- for (unsigned i=0; 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 ACTION_KILLCHAN in a SINGLE_PRESS channel. They cannot be
- recorded in such mode, but they can exist if you change from another
- mode to singlepress;
- - of type ACTION_KEYREL in a SINGLE_PRESS channel. It's up to gAction to
- find the other piece (namely frame_b)
- - not of types ACTION_KEYPRESS | ACTION_KEYREL | ACTION_KILLCHAN */
-
- if ((action->chan != pParent->chan->index) ||
- (recorder::frames.at(i) > G_Mixer.totalFrames) ||
- (action->type == ACTION_KILLCHAN && ch->mode == SINGLE_PRESS) ||
- (action->type == ACTION_KEYREL && ch->mode == SINGLE_PRESS) ||
- (action->type & ~(ACTION_KEYPRESS | ACTION_KEYREL | ACTION_KILLCHAN))
- )
- continue;
-
- int ax = x + (action->frame / pParent->zoom);
- gAction *a = new gAction(
- ax, // x
- y + 4, // y
- h() - 8, // h
- action->frame, // frame_a
- i, // n. of recordings
- pParent, // pointer to the pParent window
- ch, // pointer to SampleChannel
- false, // record = false: don't record it, we are just displaying it!
- action->type); // type of action
- add(a);
- }
- }
- end(); // mandatory when you add widgets to a fl_group, otherwise mega malfunctions
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-gAction *gActionChannel::getSelectedAction()
-{
- for (int i=0; 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 gActionChannel::updateActions()
-{
- /* when zooming, don't delete and re-add actions, just MOVE them. This
- * function shifts the action by a zoom factor. Those singlepress are
- * stretched, as well */
-
- gAction *a;
- for (int i=0; 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 gActionChannel::draw()
-{
- /* draw basic boundaries (+ beat bars) and hide the unused area. Then
- * draw the children (the actions) */
-
- baseDraw();
-
- /* print label */
-
- fl_color(COLOR_BG_1);
- fl_font(FL_HELVETICA, 12);
- if (active())
- fl_draw("start/stop", x()+4, y(), w(), h(), (Fl_Align) (FL_ALIGN_LEFT | FL_ALIGN_CENTER)); /// FIXME h() is too much!
- else
- fl_draw("start/stop (disabled)", x()+4, y(), w(), h(), (Fl_Align) (FL_ALIGN_LEFT | FL_ALIGN_CENTER)); /// FIXME h() is too much!
-
- draw_children();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-int gActionChannel::handle(int e)
-{
- int ret = Fl_Group::handle(e);
-
- /* do nothing if the widget is deactivated. It could happen for loopmode
- * channels */
-
- if (!active())
- return 1;
-
- switch (e) {
-
- case FL_DRAG: {
-
- if (selected == NULL) { // if you drag an empty area
- ret = 1;
- break;
- }
-
- /* if onLeftEdge o onRightEdge are true it means that you're resizing
- * an action. Otherwise move the widget. */
-
- if (selected->onLeftEdge || selected->onRightEdge) {
-
- /* some checks: a) cannot resize an action < N pixels, b) no beyond zero,
- * c) no beyond bar maxwidth. Checks for overlap are done in FL_RELEASE */
-
- if (selected->onRightEdge) {
-
- int aw = Fl::event_x()-selected->x();
- int ah = selected->h();
-
- if (Fl::event_x() < selected->x()+gAction::MIN_WIDTH)
- aw = gAction::MIN_WIDTH;
- else
- if (Fl::event_x() > pParent->coverX)
- aw = pParent->coverX-selected->x();
-
- selected->size(aw, ah);
- }
- else {
-
- int ax = Fl::event_x();
- int ay = selected->y();
- int aw = selected->x()-Fl::event_x()+selected->w();
- int ah = selected->h();
-
- if (Fl::event_x() < x()) {
- ax = x();
- aw = selected->w()+selected->x()-x();
- }
- else
- if (Fl::event_x() > selected->x()+selected->w()-gAction::MIN_WIDTH) {
- ax = selected->x()+selected->w()-gAction::MIN_WIDTH;
- aw = gAction::MIN_WIDTH;
- }
- selected->resize(ax, ay, aw, ah);
- }
- }
-
- /* move the widget around */
-
- else {
- int real_x = Fl::event_x() - actionPickPoint;
- if (real_x < x()) // don't go beyond the left border
- selected->position(x(), selected->y());
- else
- if (real_x+selected->w() > pParent->coverX+x()) // don't go beyond the right border
- selected->position(pParent->coverX+x()-selected->w(), selected->y());
- else {
- if (pParent->gridTool->isOn()) {
- int snpx = pParent->gridTool->getSnapPoint(real_x-x()) + x() -1;
- selected->position(snpx, selected->y());
- }
- else
- selected->position(real_x, selected->y());
- }
- }
- redraw();
- ret = 1;
- break;
- }
-
- case FL_PUSH: {
-
- if (Fl::event_button1()) {
-
- /* avoid at all costs two overlapping actions. We use 'selected' because
- * the selected action can be reused in FL_DRAG, in case you want to move
- * it. */
-
- selected = getSelectedAction();
-
- if (selected == NULL) {
-
- /* avoid click on grey area */
-
- if (Fl::event_x() >= pParent->coverX) {
- ret = 1;
- break;
- }
-
- /* snap function, if enabled */
-
- int ax = Fl::event_x();
- int fx = (ax - x()) * pParent->zoom;
- if (pParent->gridTool->isOn()) {
- ax = pParent->gridTool->getSnapPoint(ax-x()) + x() -1;
- fx = pParent->gridTool->getSnapFrame(ax-x());
-
- /* with snap=on an action can fall onto another */
-
- if (actionCollides(fx)) {
- ret = 1;
- break;
- }
- }
-
- gAction *a = new gAction(
- ax, // x
- y()+4, // y
- h()-8, // h
- fx, // frame_a
- recorder::frames.size()-1, // n. of actions recorded
- pParent, // pParent window pointer
- ch, // pointer to SampleChannel
- true, // record = true: record it!
- pParent->getActionType()); // type of action
- add(a);
- mainWin->keyboard->setChannelWithActions((gSampleChannel*)ch->guiChannel); // mainWindow update
- redraw();
- ret = 1;
- }
- else {
- actionOriginalX = selected->x();
- actionOriginalW = selected->w();
- actionPickPoint = Fl::event_x() - selected->x();
- ret = 1; // for dragging
- }
- }
- else
- if (Fl::event_button3()) {
- gAction *a = getSelectedAction();
- if (a != NULL) {
- a->delAction();
- remove(a);
- delete a;
- mainWin->keyboard->setChannelWithActions((gSampleChannel*)pParent->chan->guiChannel);
- redraw();
- ret = 1;
- }
- }
- break;
- }
- case FL_RELEASE: {
-
- if (selected == NULL) {
- ret = 1;
- break;
- }
-
- /* noChanges = true when you click on an action without doing anything
- * (dragging or moving it). */
-
- bool noChanges = false;
- if (actionOriginalX == selected->x())
- noChanges = true;
- if (ch->mode == SINGLE_PRESS &&
- actionOriginalX+actionOriginalW != selected->x()+selected->w())
- noChanges = false;
-
- if (noChanges) {
- ret = 1;
- selected = NULL;
- break;
- }
-
- /* step 1: check if the action doesn't overlap with another one.
- * In case of overlap the moved action returns to the original X
- * value ("actionOriginalX"). */
-
- bool overlap = false;
- for (int i=0; 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 gActionChannel::actionCollides(int frame)
-{
- /* if SINGLE_PRESS we check that the tail (frame_b) of the action doesn't
- * overlap the head (frame) of the new one. First the general case, yet. */
-
- bool collision = false;
-
- for (int i=0; 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;
- recorder::getNextAction(ch->index, ACTION_KEYREL, frame_a, &a2);
- if (a2) {
- frame_b = a2->frame;
- w((frame_b - frame_a)/parent->zoom);
- }
- else
- gLog("[gActionChannel] frame_b not found! [%d:???]\n", frame_a);
-
- /* a singlepress action narrower than 8 pixel is useless. So check it.
- * Warning: if an action is 8 px narrow, it has no body space to drag
- * it. It's up to the user to zoom in and drag it. */
-
- if (w() < MIN_WIDTH)
- size(MIN_WIDTH, h());
- }
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gAction::draw()
-{
- int color;
- if (selected) /// && gActionChannel !disabled
- color = COLOR_BD_1;
- else
- color = COLOR_BG_2;
-
- if (ch->mode == SINGLE_PRESS) {
- fl_rectf(x(), y(), w(), h(), (Fl_Color) color);
- }
- else {
- if (type == ACTION_KILLCHAN)
- fl_rect(x(), y(), MIN_WIDTH, h(), (Fl_Color) color);
- else {
- fl_rectf(x(), y(), MIN_WIDTH, h(), (Fl_Color) color);
- if (type == ACTION_KEYPRESS)
- fl_rectf(x()+3, y()+h()-11, 2, 8, COLOR_BD_0);
- else
- if (type == ACTION_KEYREL)
- fl_rectf(x()+3, y()+3, 2, 8, COLOR_BD_0);
- }
- }
-
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-int gAction::handle(int e)
-{
- /* ret = 0 sends the event to the parent window. */
-
- int ret = 0;
-
- switch (e) {
-
- case FL_ENTER: {
- selected = true;
- ret = 1;
- redraw();
- break;
- }
- case FL_LEAVE: {
- fl_cursor(FL_CURSOR_DEFAULT, FL_WHITE, FL_BLACK);
- selected = false;
- ret = 1;
- redraw();
- break;
- }
- case FL_MOVE: {
-
- /* handling of the two margins, left & right. 4 pixels are good enough */
-
- if (ch->mode == SINGLE_PRESS) {
- onLeftEdge = false;
- onRightEdge = false;
- if (Fl::event_x() >= x() && Fl::event_x() < x()+4) {
- onLeftEdge = true;
- fl_cursor(FL_CURSOR_WE, FL_WHITE, FL_BLACK);
- }
- else
- if (Fl::event_x() >= x()+w()-4 && Fl::event_x() <= x()+w()) {
- onRightEdge = true;
- fl_cursor(FL_CURSOR_WE, FL_WHITE, FL_BLACK);
- }
- else
- fl_cursor(FL_CURSOR_DEFAULT, FL_WHITE, FL_BLACK);
- }
- }
- }
-
- return ret;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gAction::addAction()
-{
- /* always check frame parity */
-
- if (frame_a % 2 != 0)
- frame_a++;
-
- /* anatomy of an action
- * ____[#######]_____ (a) is the left margin, ACTION_KEYPRESS. (b) is
- * a b the right margin, the ACTION_KEYREL. This is the
- * theory behind the singleshot.press actions; for any other kind the
- * (b) is just a graphical and meaningless point. */
-
- if (ch->mode == SINGLE_PRESS) {
- recorder::rec(parent->chan->index, ACTION_KEYPRESS, frame_a);
- recorder::rec(parent->chan->index, ACTION_KEYREL, frame_a+4096);
- //gLog("action added, [%d, %d]\n", frame_a, frame_a+4096);
- }
- else {
- recorder::rec(parent->chan->index, parent->getActionType(), frame_a);
- //gLog("action added, [%d]\n", frame_a);
- }
-
- recorder::sortActions();
-
- index++; // important!
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gAction::delAction()
-{
- /* if SINGLE_PRESS you must delete both the keypress and the keyrelease
- * actions. */
-
- if (ch->mode == SINGLE_PRESS) {
- recorder::deleteAction(parent->chan->index, frame_a, ACTION_KEYPRESS, false);
- recorder::deleteAction(parent->chan->index, frame_b, ACTION_KEYREL, false);
- }
- else
- recorder::deleteAction(parent->chan->index, frame_a, type, false);
-
- /* restore the initial cursor shape, in case you delete an action and
- * the double arrow (for resizing) is displayed */
-
- fl_cursor(FL_CURSOR_DEFAULT, FL_WHITE, FL_BLACK);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gAction::moveAction(int frame_a)
-{
- /* easy one: delete previous action and record the new ones. As usual,
- * SINGLE_PRESS requires two jobs. If frame_a is valid, use that frame
- * value. */
-
- delAction();
-
- if (frame_a != -1)
- this->frame_a = frame_a;
- else
- this->frame_a = xToFrame_a();
-
-
- /* always check frame parity */
-
- if (this->frame_a % 2 != 0)
- this->frame_a++;
-
- recorder::rec(parent->chan->index, type, this->frame_a);
-
- if (ch->mode == SINGLE_PRESS) {
- frame_b = xToFrame_b();
- recorder::rec(parent->chan->index, ACTION_KEYREL, frame_b);
- }
-
- recorder::sortActions();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-int gAction::absx()
-{
- return x() - parent->ac->x();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-int gAction::xToFrame_a()
-{
- return (absx()) * parent->zoom;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-int gAction::xToFrame_b()
-{
- return (absx() + w()) * parent->zoom;
-}
+++ /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_utils.h"
-#include "../../core/mixer.h"
-#include "../../core/recorder.h"
-#include "ge_actionWidget.h"
-
-
-class gAction : public Fl_Box
-{
-private:
-
- bool selected;
- unsigned index;
- class gdActionEditor *parent; // pointer to parent (gActionEditor)
- class SampleChannel *ch;
- char type; // type of action
-
-public:
-
- gAction(int x, int y, int h, int frame_a, unsigned index,
- gdActionEditor *parent, class SampleChannel *ch, bool record,
- char type);
- void draw();
- int handle(int e);
- void addAction();
- void delAction();
-
- /* moveAction
- * shift the action on the x-axis and update Recorder. If frame_a != -1
- * use the new frame in input (used while snapping) */
-
- void moveAction(int frame_a=-1);
-
- /* absx
- * x() is relative to scrolling position. absx() returns the absolute
- * x value of the action, from the leftmost edge. */
-
- int absx();
-
- /* xToFrame_a,b
- * return the real frames of x() position */
-
- int xToFrame_a();
- int xToFrame_b();
-
- int frame_a; // initial frame (KEYPRESS for singlemode.press)
- int frame_b; // terminal frame (KEYREL for singlemode.press, null for others)
-
- bool onRightEdge;
- bool onLeftEdge;
-
- static const int MIN_WIDTH = 8;
-};
-
-
-/* -------------------------------------------------------------------------- */
-
-
-class gActionChannel : public gActionWidget
-{
-
-private:
-
- class SampleChannel *ch;
-
- /* getSelectedAction
- * get the action under the mouse. NULL if nothing found. */
-
- gAction *getSelectedAction();
-
- /* selected
- * pointer to the selected action. Useful when dragging around. */
-
- gAction *selected;
-
- /* actionOriginalX, actionOriginalW
- * x and w of the action, when moved. Useful for checking if the action
- * overlaps another one: in that case the moved action returns to
- * actionOriginalX (and to actionOriginalW if resized). */
-
- int actionOriginalX;
- int actionOriginalW;
-
- /* actionPickPoint
- * the precise x point in which the action has been picked with the mouse,
- * before a dragging action. */
-
- int actionPickPoint;
-
-
- /* actionCollides
- * true if an action collides with another. Used while adding new points
- * with snap active.*/
-
- bool actionCollides(int frame);
-
-public:
-
- gActionChannel(int x, int y, gdActionEditor *pParent, class SampleChannel *ch);
- void draw();
- int handle(int e);
- void updateActions();
-};
-
-
-#endif
+++ /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 "ge_actionWidget.h"
-#include "ge_mixed.h"
-
-
-extern Mixer G_Mixer;
-
-
-gActionWidget::gActionWidget(int x, int y, int w, int h, gdActionEditor *pParent)
- : Fl_Group(x, y, w, h), pParent(pParent) {}
-
-
-/* ------------------------------------------------------------------ */
-
-
-gActionWidget::~gActionWidget() {}
-
-
-/* ------------------------------------------------------------------ */
-
-
-void gActionWidget::baseDraw(bool clear) {
-
- /* clear the screen */
-
- if (clear)
- fl_rectf(x(), y(), w(), h(), COLOR_BG_MAIN);
-
- /* draw the container */
-
- fl_color(COLOR_BD_0);
- fl_rect(x(), y(), w(), h());
-
- /* grid drawing, if > 1 */
-
- if (pParent->gridTool->getValue() > 1) {
-
- fl_color(fl_rgb_color(54, 54, 54));
- fl_line_style(FL_DASH, 0, NULL);
-
- for (int i=0; i<(int) pParent->gridTool->points.size(); i++) {
- int px = pParent->gridTool->points.at(i)+x()-1;
- fl_line(px, y()+1, px, y()+h()-2);
- }
- fl_line_style(0);
- }
-
- /* bars and beats drawing */
-
- fl_color(COLOR_BD_0);
- for (int i=0; i<(int) pParent->gridTool->beats.size(); i++) {
- int px = pParent->gridTool->beats.at(i)+x()-1;
- fl_line(px, y()+1, px, y()+h()-2);
- }
-
- fl_color(COLOR_BG_2);
- for (int i=0; i<(int) pParent->gridTool->bars.size(); i++) {
- int px = pParent->gridTool->bars.at(i)+x()-1;
- fl_line(px, y()+1, px, y()+h()-2);
- }
-
- /* cover unused area. Avoid drawing cover if width == 0 (i.e. beats
- * are 32) */
-
- int coverWidth = pParent->totalWidth-pParent->coverX;
- if (coverWidth != 0)
- fl_rectf(pParent->coverX+x(), y()+1, coverWidth, h()-2, COLOR_BG_1);
-}
+++ /dev/null
-/* ---------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * ge_actionWidget
- *
- * parent class of any widget inside the action editor.
- *
- * ---------------------------------------------------------------------
- *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
- *
- * This file is part of Giada - Your Hardcore Loopmachine.
- *
- * Giada - Your Hardcore Loopmachine is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General
- * Public License as published by the Free Software Foundation, either
- * version 3 of the License, or (at your option) any later version.
- *
- * Giada - Your Hardcore Loopmachine is distributed in the hope that it
- * will be useful, but WITHOUT ANY WARRANTY; without even the implied
- * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Giada - Your Hardcore Loopmachine. If not, see
- * <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 gActionWidget : public Fl_Group {
-
-protected:
- class gdActionEditor *pParent;
- void baseDraw(bool clear=true);
-
-public:
- virtual void updateActions() = 0;
-
- gActionWidget(int x, int y, int w, int h, gdActionEditor *pParent);
- ~gActionWidget();
-};
-
-#endif
#include <limits.h>
#include "../../core/const.h"
-#include "../../utils/utils.h"
+#include "../../utils/fs.h"
#include "../../utils/string.h"
#include "../../utils/log.h"
#include "../dialogs/gd_browser.h"
string gBrowser::getCurrentDir()
{
- return normalize(gGetRealPath(currentDir));
+ return normalize(gu_getRealPath(currentDir));
}
if (value() == 0) // no rows selected? return current directory
return normalize(currentDir);
else
- return normalize(gGetRealPath(currentDir + G_SLASH + normalize(text(value()))));
+ return normalize(gu_getRealPath(currentDir + G_SLASH + normalize(text(value()))));
}
{
string out = s;
- /* our crappy version of Clang doesn't seem to support std::string::back() */
+ /* If string ends with G_SLASH, remove it. Don't do it if has length > 1, it
+ means that the string is just '/'. Note: our crappy version of Clang doesn't
+ seem to support std::string::back() */
#ifdef __APPLE__
- if (out[out.length() - 1] == G_SLASH)
+ if (out[out.length() - 1] == G_SLASH && out.length() > 1)
#else
- if (out.back() == G_SLASH)
+ if (out.back() == G_SLASH && out.length() > 1)
#endif
out = out.substr(0, out.size()-1);
+++ /dev/null
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * ge_channel
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
- *
- * This file is part of Giada - Your Hardcore Loopmachine.
- *
- * Giada - Your Hardcore Loopmachine is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General
- * Public License as published by the Free Software Foundation, either
- * version 3 of the License, or (at your option) any later version.
- *
- * Giada - Your Hardcore Loopmachine is distributed in the hope that it
- * will be useful, but WITHOUT ANY WARRANTY; without even the implied
- * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Giada - Your Hardcore Loopmachine. If not, see
- * <http://www.gnu.org/licenses/>.
- *
- * -------------------------------------------------------------------------- */
-
-
-#include "../../core/mixer.h"
-#include "../../core/conf.h"
-#include "../../core/patch_DEPR_.h"
-#include "../../core/graphics.h"
-#include "../../core/channel.h"
-#include "../../core/wave.h"
-#include "../../core/sampleChannel.h"
-#include "../../core/midiChannel.h"
-#include "../../glue/glue.h"
-#include "../../utils/gui_utils.h"
-#include "../dialogs/gd_mainWindow.h"
-#include "../dialogs/gd_keyGrabber.h"
-#include "../dialogs/gd_midiInput.h"
-#include "../dialogs/gd_editor.h"
-#include "../dialogs/gd_actionEditor.h"
-#include "../dialogs/gd_warnings.h"
-#include "../dialogs/gd_browser.h"
-#include "../dialogs/gd_midiOutput.h"
-#include "ge_keyboard.h"
-#include "ge_channel.h"
-#include "ge_sampleChannel.h"
-
-
-extern Mixer G_Mixer;
-extern Conf G_Conf;
-extern Patch_DEPR_ G_Patch_DEPR_;
-extern gdMainWindow *mainWin;
-
-
-gChannel::gChannel(int X, int Y, int W, int H, int type)
- : Fl_Group(X, Y, W, H, NULL), type(type) {}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-int gChannel::getColumnIndex()
-{
- return ((gColumn*)parent())->getIndex();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gChannel::blink()
-{
- if (gu_getBlinker() > 6)
- mainButton->setPlayMode();
- else
- mainButton->setDefaultMode();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gChannel::setColorsByStatus(int playStatus, int recStatus)
-{
- switch (playStatus) {
- case STATUS_OFF:
- mainButton->setDefaultMode();
- button->imgOn = channelPlay_xpm;
- button->imgOff = channelStop_xpm;
- button->redraw();
- break;
- case STATUS_PLAY:
- mainButton->setPlayMode();
- button->imgOn = channelStop_xpm;
- button->imgOff = channelPlay_xpm;
- button->redraw();
- break;
- case STATUS_WAIT:
- blink();
- break;
- case STATUS_ENDING:
- mainButton->setEndingMode();
- break;
- }
-
- switch (recStatus) {
- case REC_WAITING:
- blink();
- break;
- case REC_ENDING:
- mainButton->setEndingMode();
- break;
- }
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-int gChannel::handleKey(int e, int key)
-{
- int ret;
- if (e == FL_KEYDOWN && button->value()) // key already pressed! skip it
- ret = 1;
- else
- if (Fl::event_key() == key && !button->value()) {
- button->take_focus(); // move focus to this button
- button->value((e == FL_KEYDOWN || e == FL_SHORTCUT) ? 1 : 0); // change the button's state
- button->do_callback(); // invoke the button's callback
- ret = 1;
- }
- else
- ret = 0;
-
- if (Fl::event_key() == key)
- button->value((e == FL_KEYDOWN || e == FL_SHORTCUT) ? 1 : 0); // change the button's state
-
- return ret;
-}
+++ /dev/null
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * ge_channel
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
- *
- * This file is part of Giada - Your Hardcore Loopmachine.
- *
- * Giada - Your Hardcore Loopmachine is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General
- * Public License as published by the Free Software Foundation, either
- * version 3 of the License, or (at your option) any later version.
- *
- * Giada - Your Hardcore Loopmachine is distributed in the hope that it
- * will be useful, but WITHOUT ANY WARRANTY; without even the implied
- * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Giada - Your Hardcore Loopmachine. If not, see
- * <http://www.gnu.org/licenses/>.
- *
- * -------------------------------------------------------------------------- */
-
-
-#ifndef GE_CHANNEL_H
-#define GE_CHANNEL_H
-
-
-#include <FL/Fl_Scroll.H>
-#include <FL/Fl_Box.H>
-#include <FL/Fl_Menu_Button.H>
-#include "ge_mixed.h"
-
-
-class gChannel : public Fl_Group
-{
-protected:
-
- /* define some breakpoints for dynamic resize */
-
-#ifdef WITH_VST
- static const int BREAK_READ_ACTIONS = 212;
- static const int BREAK_MODE_BOX = 188;
- static const int BREAK_FX = 164;
- static const int BREAK_DELTA = 120;
-#else
- static const int BREAK_READ_ACTIONS = 188;
- static const int BREAK_MODE_BOX = 164;
- static const int BREAK_FX = 140;
- static const int BREAK_DELTA = 96;
-#endif
- static const int BREAK_UNIT = 24;
-
- /* blink
- * blink button when channel is in wait/ending status. */
-
- void blink();
-
- /* setColorByStatus
- * update colors depending on channel status. */
-
- void setColorsByStatus(int playStatus, int recStatus);
-
- /* handleKey
- * method wrapped by virtual handle(int e). */
-
- int handleKey(int e, int key);
-
-public:
-
- gChannel(int x, int y, int w, int h, int type);
-
- /* reset
- * reset channel to initial status. */
-
- virtual void reset() = 0;
-
- /* update
- * update the label of sample button and everything else such as 'R'
- * button, key box and so on, according to global values. */
-
- virtual void update() = 0;
-
- /* refresh
- * update graphics. */
-
- virtual void refresh() = 0;
-
- /* keypress
- * what to do when the corresponding key is pressed. */
-
- virtual int keyPress(int event) = 0;
-
- /* getColumnIndex
- * return the numeric index of the column in which this channel is
- * located. */
-
- int getColumnIndex();
-
- class gButton *button;
- class gStatus *status;
- class gChannelButton *mainButton;
- class gDial *vol;
- class gClick *mute;
- class gClick *solo;
-#ifdef WITH_VST
- class gFxButton *fx;
-#endif
-
- int type;
-};
-
-
-#endif
+++ /dev/null
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * ge_channelButton
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
- *
- * This file is part of Giada - Your Hardcore Loopmachine.
- *
- * Giada - Your Hardcore Loopmachine is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General
- * Public License as published by the Free Software Foundation, either
- * version 3 of the License, or (at your option) any later version.
- *
- * Giada - Your Hardcore Loopmachine is distributed in the hope that it
- * will be useful, but WITHOUT ANY WARRANTY; without even the implied
- * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Giada - Your Hardcore Loopmachine. If not, see
- * <http://www.gnu.org/licenses/>.
- *
- * -------------------------------------------------------------------------- */
-
-
-#include "../../core/const.h"
-#include "../../utils/utils.h"
-#include "ge_channelButton.h"
-
-
-using std::string;
-
-
-gChannelButton::gChannelButton(int x, int y, int w, int h, const char *l)
- : gClick(x, y, w, h, l), key("") {}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gChannelButton::setKey(const string &k)
-{
- key = k;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gChannelButton::setKey(int k)
-{
- if (k == 0)
- key = "";
- else {
- // FIXME - this crap won't work with unicode/utf-8
- char c = (char) k;
- key = c;
- }
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gChannelButton::draw()
-{
- gClick::draw();
-
- if (key == "")
- return;
-
- /* draw background */
-
- fl_rectf(x()+1, y()+1, 18, h()-2, bgColor0);
-
- /* draw key */
-
- fl_color(COLOR_TEXT_0);
- fl_font(FL_HELVETICA, 11);
- fl_draw(key.c_str(), x(), y(), 18, h(), FL_ALIGN_CENTER);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gChannelButton::setInputRecordMode()
-{
- bgColor0 = COLOR_BG_3;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gChannelButton::setActionRecordMode()
-{
- bgColor0 = COLOR_BG_4;
- txtColor = COLOR_TEXT_0;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gChannelButton::setDefaultMode(const char *l)
-{
- bgColor0 = COLOR_BG_0;
- bdColor = COLOR_BD_0;
- txtColor = COLOR_TEXT_0;
- if (l)
- label(l);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gChannelButton::setPlayMode()
-{
- bgColor0 = COLOR_BG_2;
- bdColor = COLOR_BD_1;
- txtColor = COLOR_TEXT_1;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gChannelButton::setEndingMode()
-{
- bgColor0 = COLOR_BD_0;
-}
+++ /dev/null
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * ge_channelButton
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
- *
- * This file is part of Giada - Your Hardcore Loopmachine.
- *
- * Giada - Your Hardcore Loopmachine is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General
- * Public License as published by the Free Software Foundation, either
- * version 3 of the License, or (at your option) any later version.
- *
- * Giada - Your Hardcore Loopmachine is distributed in the hope that it
- * will be useful, but WITHOUT ANY WARRANTY; without even the implied
- * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Giada - Your Hardcore Loopmachine. If not, see
- * <http://www.gnu.org/licenses/>.
- *
- * -------------------------------------------------------------------------- */
-
-
-#ifndef GE_CHANNEL_BUTTON_H
-#define GE_CHANNEL_BUTTON_H
-
-
-#include "ge_mixed.h"
-
-
-using std::string;
-
-
-class gChannelButton : public gClick
-{
-private:
-
- string key;
-
-public:
-
- gChannelButton(int x, int y, int w, int h, const char *l=0);
-
- virtual int handle(int e) = 0;
-
- void draw();
- void setKey(const string &k);
- void setKey(int k);
- void setPlayMode();
- void setEndingMode();
- void setDefaultMode(const char *l=0);
- void setInputRecordMode();
- void setActionRecordMode();
-};
-
-
-#endif
#include "../../core/channel.h"
#include "../../core/sampleChannel.h"
#include "../../core/midiChannel.h"
-#include "../../glue/glue.h"
+#include "../../glue/main.h"
#include "../../glue/channel.h"
#include "../../utils/log.h"
+#include "../../utils/string.h"
#include "../dialogs/gd_mainWindow.h"
#include "../dialogs/gd_warnings.h"
#include "../elems/ge_keyboard.h"
#include "ge_column.h"
-#include "ge_channel.h"
-#include "ge_sampleChannel.h"
-#include "ge_midiChannel.h"
+#include "channel.h"
+#include "sampleChannel.h"
+#include "midiChannel.h"
extern Mixer G_Mixer;
}
case FL_PASTE: { // handle actual drop (paste) operation
vector<std::string> paths;
- gSplit(Fl::event_text(), "\n", &paths);
+ gu_split(Fl::event_text(), "\n", &paths);
bool fails = false;
int result = 0;
for (unsigned i=0; i<paths.size(); i++) {
- gLog("[gColumn::handle] loading %s...\n", paths.at(i).c_str());
+ gu_log("[gColumn::handle] loading %s...\n", paths.at(i).c_str());
SampleChannel *c = (SampleChannel*) glue_addChannel(index, CHANNEL_SAMPLE);
- result = glue_loadChannel(c, gStripFileUrl(paths.at(i).c_str()).c_str());
+ result = glue_loadChannel(c, gu_stripFileUrl(paths.at(i)).c_str());
if (result != SAMPLE_LOADED_OK) {
deleteChannel(c->guiChannel);
fails = true;
void gColumn::refreshChannels()
{
for (int i=1; i<children(); i++)
- ((gChannel*) child(i))->refresh();
+ ((geChannel*) child(i))->refresh();
}
/* -------------------------------------------------------------------------- */
-gChannel *gColumn::addChannel(class Channel *ch)
+geChannel *gColumn::addChannel(Channel *ch)
{
int currentY = y() + children() * 24;
- gChannel *gch = NULL;
+ geChannel *gch = NULL;
if (ch->type == CHANNEL_SAMPLE)
- gch = (gSampleChannel*) new gSampleChannel(x(), currentY, w(), 20, (SampleChannel*) ch);
+ gch = (geSampleChannel*) new geSampleChannel(x(), currentY, w(), 20, (SampleChannel*) ch);
else
- gch = (gMidiChannel*) new gMidiChannel(x(), currentY, w(), 20, (MidiChannel*) ch);
+ gch = (geMidiChannel*) new geMidiChannel(x(), currentY, w(), 20, (MidiChannel*) ch);
add(gch);
resize(x(), y(), w(), (children() * 24) + 66); // evil space for drag n drop
/* -------------------------------------------------------------------------- */
-void gColumn::deleteChannel(gChannel *gch)
+void gColumn::deleteChannel(geChannel *gch)
{
gch->hide();
remove(gch);
* parameter to skip the operation */
for (int i=0; i<children(); i++) {
- gch = (gChannel*) child(i);
+ gch = (geChannel*) child(i);
gch->position(gch->x(), y()+(i*24));
}
size(w(), children() * 24 + 66); // evil space for drag n drop
void gColumn::__cb_addChannel()
{
- gLog("[gColumn::__cb_addChannel] index = %d\n", index);
+ gu_log("[gColumn::__cb_addChannel] index = %d\n", index);
int type = openTypeMenu();
if (type)
glue_addChannel(index, type);
else {
while (children() >= 2) { // skip "add new channel" btn
int i = children()-1;
- deleteChannel((gChannel*)child(i));
+ deleteChannel((geChannel*)child(i));
}
}
}
Channel *gColumn::getChannel(int i)
{
- gChannel *gch = (gChannel*) child(i);
+ geChannel *gch = (geChannel*) child(i);
if (gch->type == CHANNEL_SAMPLE)
- return ((gSampleChannel*) child(i))->ch;
+ return ((geSampleChannel*) child(i))->ch;
else
- return ((gMidiChannel*) child(i))->ch;
+ return ((geMidiChannel*) child(i))->ch;
}
* add a new channel in this column and set the internal pointer
* to channel to 'ch'. */
- class gChannel *addChannel(class Channel *ch);
+ class geChannel *addChannel(class Channel *ch);
/* handle */
/* deleteChannel
* remove the channel 'gch' from this column. */
- void deleteChannel(gChannel *gch);
+ void deleteChannel(geChannel *gch);
/* refreshChannels
* update channels' graphical statues. Called on each GUI cycle. */
#include "../../core/graphics.h"
-#include "../../glue/glue.h"
+#include "../../glue/main.h"
+#include "../../glue/io.h"
#include "ge_mixed.h"
#include "ge_controller.h"
void gController::__cb_play()
{
- glue_startStopSeq();
+ glue_startStopSeq(true);
}
void gController::__cb_recAction()
{
- glue_startStopActionRec();
+ glue_startStopActionRec(true);
}
void gController::__cb_recInput()
{
- glue_startStopInputRec();
+ glue_startStopInputRec(true);
}
void gController::__cb_metronome()
{
- glue_startStopMetronome();
+ glue_startStopMetronome(true);
}
+++ /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 "ge_keyboard.h"
-#include "ge_envelopeChannel.h"
-
-
-extern Mixer G_Mixer;
-extern gdMainWindow *mainWin;
-
-
-gEnvelopeChannel::gEnvelopeChannel(int x, int y, gdActionEditor *pParent, int type, int range, const char *l)
- : gActionWidget(x, y, 200, 80, pParent), l(l), type(type), range(range),
- selectedPoint(-1), draggedPoint(-1)
-{
- size(pParent->totalWidth, h());
-}
-
-
-/* ------------------------------------------------------------------ */
-
-
-gEnvelopeChannel::~gEnvelopeChannel() {
- clearPoints();
-}
-
-
-/* ------------------------------------------------------------------ */
-
-
-void gEnvelopeChannel::addPoint(int frame, int iValue, float fValue, int px, int py) {
- point p;
- p.frame = frame;
- p.iValue = iValue;
- p.fValue = fValue;
- p.x = px;
- p.y = py;
- points.push_back(p);
-}
-
-
-/* ------------------------------------------------------------------ */
-
-
-void gEnvelopeChannel::updateActions() {
- for (unsigned i=0; i<points.size(); i++)
- points.at(i).x = points.at(i).frame / pParent->zoom;
-}
-
-
-/* ------------------------------------------------------------------ */
-
-
-void gEnvelopeChannel::draw() {
-
- baseDraw();
-
- /* print label */
-
- fl_color(COLOR_BG_1);
- fl_font(FL_HELVETICA, 12);
- fl_draw(l, x()+4, y(), 80, h(), (Fl_Align) (FL_ALIGN_LEFT));
-
- int pxOld = x()-3;
- int pyOld = y()+1;
- int pxNew = 0;
- int pyNew = 0;
-
- fl_color(COLOR_BG_2);
-
- for (unsigned i=0; i<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 gEnvelopeChannel::handle(int e) {
-
- /* Adding an action: no further checks required, just record it on frame
- * mx*pParent->zoom. Deleting action is trickier: find the active
- * point and derive from it the corresponding frame. */
-
- int ret = 0;
- int mx = Fl::event_x()-x(); // mouse x
- int my = Fl::event_y()-y(); // mouse y
-
- switch (e) {
-
- case FL_ENTER: {
- ret = 1;
- break;
- }
-
- case FL_MOVE: {
- selectedPoint = getSelectedPoint();
- redraw();
- ret = 1;
- break;
- }
-
- case FL_LEAVE: {
- draggedPoint = -1;
- selectedPoint = -1;
- redraw();
- ret = 1;
- break;
- }
-
- case FL_PUSH: {
-
- /* left click on point: drag
- * right click on point: delete
- * left click on void: add */
-
- if (Fl::event_button1()) {
-
- if (selectedPoint != -1) {
- draggedPoint = selectedPoint;
- }
- else {
-
- /* top & border fix */
-
- if (my > h()-8) my = h()-8;
- if (mx > pParent->coverX-x()) mx = pParent->coverX-x();
-
- if (range == RANGE_FLOAT) {
-
- /* if this is the first point ever, add other two points at the beginning
- * and the end of the range */
-
- if (points.size() == 0) {
- addPoint(0, 0, 1.0f, 0, 1);
- recorder::rec(pParent->chan->index, type, 0, 0, 1.0f);
- addPoint(G_Mixer.totalFrames, 0, 1.0f, pParent->coverX, 1);
- recorder::rec(pParent->chan->index, type, G_Mixer.totalFrames, 0, 1.0f);
- }
-
- /* line between 2 points y = (x-a) / (b-a); a = h() - 8; b = 1 */
-
- int frame = mx * pParent->zoom;
- float value = (my - h() + 8) / (float) (1 - h() + 8);
- addPoint(frame, 0, value, mx, my);
- recorder::rec(pParent->chan->index, type, frame, 0, value);
- recorder::sortActions();
- sortPoints();
- }
- else {
- /// TODO
- }
- mainWin->keyboard->setChannelWithActions((gSampleChannel*)pParent->chan->guiChannel); // update mainWindow
- redraw();
- }
- }
- else {
-
- /* right click on point 0 or point size-1 deletes the entire envelope */
-
- if (selectedPoint != -1) {
- if (selectedPoint == 0 || (unsigned) selectedPoint == points.size()-1) {
- recorder::clearAction(pParent->chan->index, type);
- points.clear();
- }
- else {
- recorder::deleteAction(pParent->chan->index, points.at(selectedPoint).frame, type, false);
- recorder::sortActions();
- points.erase(points.begin() + selectedPoint);
- }
- mainWin->keyboard->setChannelWithActions((gSampleChannel*)pParent->chan->guiChannel); // update mainWindow
- redraw();
- }
- }
-
- ret = 1;
- break;
- }
-
- case FL_RELEASE: {
- if (draggedPoint != -1) {
-
- if (points.at(draggedPoint).x == previousXPoint) {
- //gLog("nothing to do\n");
- }
- else {
- int newFrame = points.at(draggedPoint).x * pParent->zoom;
-
- /* x edge correction */
-
- if (newFrame < 0)
- newFrame = 0;
- else if (newFrame > G_Mixer.totalFrames)
- newFrame = G_Mixer.totalFrames;
-
- /* vertical line check */
-
- int vp = verticalPoint(points.at(draggedPoint));
- if (vp == 1) newFrame -= 256;
- else if (vp == -1) newFrame += 256;
-
- /* delete previous point and record a new one */
-
- recorder::deleteAction(pParent->chan->index, points.at(draggedPoint).frame, type, false);
-
- if (range == RANGE_FLOAT) {
- float value = (points.at(draggedPoint).y - h() + 8) / (float) (1 - h() + 8);
- recorder::rec(pParent->chan->index, type, newFrame, 0, value);
- }
- else {
- /// TODO
- }
-
- recorder::sortActions();
- points.at(draggedPoint).frame = newFrame;
- draggedPoint = -1;
- selectedPoint = -1;
- }
- }
- ret = 1;
- break;
- }
-
- case FL_DRAG: {
-
- if (draggedPoint != -1) {
-
- /* y constraint */
-
- if (my > h()-8)
- points.at(draggedPoint).y = h()-8;
- else
- if (my < 1)
- points.at(draggedPoint).y = 1;
- else
- points.at(draggedPoint).y = my;
-
- /* x constraint
- * constrain the point between two ends (leftBorder-point, point-point,
- * point-rightBorder). First & last points cannot be shifted on x */
-
- if (draggedPoint == 0)
- points.at(draggedPoint).x = x()-8;
- else
- if ((unsigned) draggedPoint == points.size()-1)
- points.at(draggedPoint).x = pParent->coverX;
- else {
- int prevPoint = points.at(draggedPoint-1).x;
- int nextPoint = points.at(draggedPoint+1).x;
- if (mx <= prevPoint)
- points.at(draggedPoint).x = prevPoint;
- else
- if (mx >= nextPoint)
- points.at(draggedPoint).x = nextPoint;
- //else
- // points.at(draggedPoint).x = mx;
- else {
- if (pParent->gridTool->isOn())
- points.at(draggedPoint).x = pParent->gridTool->getSnapPoint(mx)-1;
- else
- points.at(draggedPoint).x = mx;
- }
- }
- redraw();
- }
-
- ret = 1;
- break;
- }
- }
-
- return ret;
-}
-
-
-/* ------------------------------------------------------------------ */
-
-
-int gEnvelopeChannel::verticalPoint(const point &p) {
- for (unsigned i=0; i<points.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 gEnvelopeChannel::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 gEnvelopeChannel::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 gEnvelopeChannel::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 == 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/utils.h"
-#include "ge_actionWidget.h"
-
-
-using std::vector;
-
-
-class gEnvelopeChannel : public gActionWidget
-{
- const char *l; // internal label
- int type; // type of action
- int range;
-
- /* point
- * a single dot in the graph. x = relative frame, y = relative value */
-
- struct point {
- int frame;
- int iValue;
- float fValue;
- int x;
- int y;
- };
-
- /* points
- * array of points, filled by fillPoints() */
-
- vector<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:
- gEnvelopeChannel(int x, int y, gdActionEditor *pParent, int type, int range, const char *l);
- ~gEnvelopeChannel();
-
- /* addPoint
- * add a point made of frame+value to internal points[]. */
-
- void addPoint(int frame, int iValue=0, float fValue=0.0f, int x=-1, int y=-1);
-
- void updateActions();
-
- /* fill
- * parse recorder's stack and fill the widget with points. It's up to
- * the caller to call this method as initialization. */
-
- void fill();
-
- inline void clearPoints() { points.clear(); }
-};
-
-#endif
#include "../../core/patch_DEPR_.h"
#include "../../core/channel.h"
#include "../../core/sampleChannel.h"
-#include "../../glue/glue.h"
+#include "../../glue/main.h"
+#include "../../glue/io.h"
#include "../../utils/log.h"
#include "../dialogs/gd_browser.h"
#include "../dialogs/gd_mainWindow.h"
#include "../dialogs/gd_editor.h"
#include "../dialogs/gd_warnings.h"
-#include "ge_channel.h"
-#include "ge_sampleChannel.h"
+#include "channel.h"
+#include "sampleChannel.h"
#include "ge_keyboard.h"
/* -------------------------------------------------------------------------- */
-void gKeyboard::freeChannel(gChannel *gch)
+void gKeyboard::freeChannel(geChannel *gch)
{
gch->reset();
}
/* -------------------------------------------------------------------------- */
-void gKeyboard::deleteChannel(gChannel *gch)
+void gKeyboard::deleteChannel(geChannel *gch)
{
for (unsigned i=0; i<columns.size(); i++) {
int k = columns.at(i)->find(gch);
/* -------------------------------------------------------------------------- */
-void gKeyboard::updateChannel(gChannel *gch)
+void gKeyboard::updateChannel(geChannel *gch)
{
gch->update();
}
/* -------------------------------------------------------------------------- */
-gChannel *gKeyboard::addChannel(int colIndex, Channel *ch, bool build)
+geChannel *gKeyboard::addChannel(int colIndex, Channel *ch, bool build)
{
gColumn *col = getColumnByIndex(colIndex);
__cb_addColumn();
col = columns.back();
col->setIndex(colIndex);
- gLog("[gKeyboard::addChannel] created new column with index=%d\n", colIndex);
+ gu_log("[gKeyboard::addChannel] created new column with index=%d\n", colIndex);
}
- gLog("[gKeyboard::addChannel] add to column with index = %d\n", col->getIndex());
+ gu_log("[gKeyboard::addChannel] add to column with index = %d\n", col->getIndex());
return col->addChannel(ch);
}
/* -------------------------------------------------------------------------- */
+/* TODO - the following event handling for play, stop, rewind, start rec and
+so on should be moved to the proper widget: gdMainWindow or (better) geController. */
int gKeyboard::handle(int e)
{
}
else if (Fl::event_key() == FL_Enter && !enterPressed) {
enterPressed = true;
- glue_startStopActionRec();
+ glue_startStopActionRec(false); // update gui
ret = 1;
break;
}
else if (Fl::event_key() == ' ' && !spacePressed) {
spacePressed = true;
- G_Mixer.running ? glue_stopSeq() : glue_startSeq(); // TODO - glue_startStopSeq, no core logic here
+ glue_startStopSeq(false); // update gui
ret = 1;
break;
}
for (unsigned i=0; i<columns.size(); i++)
for (int k=1; k<columns.at(i)->children(); k++)
- ret &= ((gChannel*)columns.at(i)->child(k))->keyPress(e);
+ ret &= ((geChannel*)columns.at(i)->child(k))->keyPress(e);
break;
}
}
/* -------------------------------------------------------------------------- */
-void gKeyboard::setChannelWithActions(gSampleChannel *gch)
+void gKeyboard::setChannelWithActions(geSampleChannel *gch)
{
if (gch->ch->hasActions)
- gch->addActionButton();
+ gch->showActionButton();
else
- gch->delActionButton();
+ gch->hideActionButton();
}
addColumnBtn->position(colxw + gap, y());
redraw();
- gLog("[gKeyboard::__cb_addColumn] new column added (index=%d, w=%d), total count=%d, addColumn(x)=%d\n",
+ gu_log("[gKeyboard::__cb_addColumn] new column added (index=%d, w=%d), total count=%d, addColumn(x)=%d\n",
gc->getIndex(), width, columns.size(), addColumnBtn->x());
/* recompute col indexes */
#include <vector>
#include "../elems/ge_column.h"
#include "../../core/const.h"
-#include "../../utils/utils.h"
+#include "../../utils/fs.h"
using std::vector;
void init();
/* addChannel
- * add a new channel to gChannels. Used by callbacks and during
- * patch loading. Requires Channel (and not gChannel). If build is
+ * add a new channel to geChannels. Used by callbacks and during
+ * patch loading. Requires Channel (and not geChannel). If build is
* set to true, also generate the corresponding column if column (index) does
* not exist yet. */
- gChannel *addChannel(int column, class Channel *ch, bool build=false);
+ class geChannel *addChannel(int column, class Channel *ch, bool build=false);
/* addColumn
* add a new column to the top of the stack. */
void addColumn(int width=380);
/* deleteChannel
- * delete a channel from gChannels<> where gChannel->ch == ch and remove
+ * delete a channel from geChannels<> where geChannel->ch == ch and remove
* it from the stack. */
- void deleteChannel(gChannel *gch);
+ void deleteChannel(geChannel *gch);
/* freeChannel
- * free a channel from gChannels<> where gChannel->ch == ch. No channels
+ * free a channel from geChannels<> where geChannel->ch == ch. No channels
* are deleted */
- void freeChannel(gChannel *gch);
+ void freeChannel(geChannel *gch);
/* updateChannel
* wrapper function to call gch->update(). */
- void updateChannel(gChannel *gch);
+ void updateChannel(geChannel *gch);
/* organizeColumns
* reorganize columns layout by removing empty gaps. */
/* setChannelWithActions
* add 'R' button if channel has actions, and set recorder to active. */
- void setChannelWithActions(class gSampleChannel *gch);
+ void setChannelWithActions(class geSampleChannel *gch);
/* printChannelMessage
* given any output by glue_loadChannel, print the message on screen
+++ /dev/null
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * ge_midiChannel
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
- *
- * This file is part of Giada - Your Hardcore Loopmachine.
- *
- * Giada - Your Hardcore Loopmachine is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General
- * Public License as published by the Free Software Foundation, either
- * version 3 of the License, or (at your option) any later version.
- *
- * Giada - Your Hardcore Loopmachine is distributed in the hope that it
- * will be useful, but WITHOUT ANY WARRANTY; without even the implied
- * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Giada - Your Hardcore Loopmachine. If not, see
- * <http://www.gnu.org/licenses/>.
- *
- * -------------------------------------------------------------------------- */
-
-
-#include "../../core/pluginHost.h"
-#include "../../core/mixer.h"
-#include "../../core/conf.h"
-#include "../../core/patch_DEPR_.h"
-#include "../../core/graphics.h"
-#include "../../core/channel.h"
-#include "../../core/wave.h"
-#include "../../core/sampleChannel.h"
-#include "../../core/midiChannel.h"
-#include "../../glue/channel.h"
-#include "../../glue/glue.h"
-#include "../../utils/gui_utils.h"
-#include "../dialogs/gd_mainWindow.h"
-#include "../dialogs/gd_keyGrabber.h"
-#include "../dialogs/gd_midiInput.h"
-#include "../dialogs/gd_editor.h"
-#include "../dialogs/gd_actionEditor.h"
-#include "../dialogs/gd_warnings.h"
-#include "../dialogs/gd_browser.h"
-#include "../dialogs/gd_keyGrabber.h"
-#include "../dialogs/gd_midiOutput.h"
-#include "ge_keyboard.h"
-#include "ge_midiChannel.h"
-#include "ge_channel.h"
-#include "ge_sampleChannel.h"
-#include "../dialogs/gd_pluginList.h"
-
-
-extern Mixer G_Mixer;
-extern Conf G_Conf;
-extern Patch_DEPR_ G_Patch_DEPR_;
-extern gdMainWindow *mainWin;
-
-
-gMidiChannel::gMidiChannel(int X, int Y, int W, int H, class MidiChannel *ch)
- : gChannel(X, Y, W, H, CHANNEL_MIDI), ch(ch)
-{
- begin();
-
-#if defined(WITH_VST)
- int delta = 120; // (5 widgets * 20) + (5 paddings * 4)
-#else
- int delta = 96; // (4 widgets * 20) + (4 paddings * 4)
-#endif
-
- button = new gButton(x(), y(), 20, 20, "", channelStop_xpm, channelPlay_xpm);
- mainButton = new gMidiChannelButton(button->x()+button->w()+4, y(), w() - delta, 20, "-- MIDI --");
- mute = new gClick(mainButton->x()+mainButton->w()+4, y(), 20, 20, "", muteOff_xpm, muteOn_xpm);
- solo = new gClick(mute->x()+mute->w()+4, y(), 20, 20, "", soloOff_xpm, soloOn_xpm);
-#if defined(WITH_VST)
- fx = new gFxButton(solo->x()+solo->w()+4, y(), 20, 20, fxOff_xpm, fxOn_xpm);
- vol = new gDial(fx->x()+fx->w()+4, y(), 20, 20);
-#else
- vol = new gDial(solo->x()+solo->w()+4, y(), 20, 20);
-#endif
-
- end();
-
- resizable(mainButton);
-
- update();
-
- button->callback(cb_button, (void*)this);
- button->when(FL_WHEN_CHANGED); // do callback on keypress && on keyrelease
-
-#ifdef WITH_VST
- fx->callback(cb_openFxWindow, (void*)this);
-#endif
-
- mute->type(FL_TOGGLE_BUTTON);
- mute->callback(cb_mute, (void*)this);
-
- solo->type(FL_TOGGLE_BUTTON);
- solo->callback(cb_solo, (void*)this);
-
- mainButton->callback(cb_openMenu, (void*)this);
-
- vol->callback(cb_changeVol, (void*)this);
-
- ch->guiChannel = this;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gMidiChannel::cb_button (Fl_Widget *v, void *p) { ((gMidiChannel*)p)->__cb_button(); }
-void gMidiChannel::cb_mute (Fl_Widget *v, void *p) { ((gMidiChannel*)p)->__cb_mute(); }
-void gMidiChannel::cb_solo (Fl_Widget *v, void *p) { ((gMidiChannel*)p)->__cb_solo(); }
-void gMidiChannel::cb_openMenu (Fl_Widget *v, void *p) { ((gMidiChannel*)p)->__cb_openMenu(); }
-void gMidiChannel::cb_changeVol (Fl_Widget *v, void *p) { ((gMidiChannel*)p)->__cb_changeVol(); }
-#ifdef WITH_VST
-void gMidiChannel::cb_openFxWindow(Fl_Widget *v, void *p) { ((gMidiChannel*)p)->__cb_openFxWindow(); }
-#endif
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gMidiChannel::__cb_mute()
-{
- glue_setMute(ch);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gMidiChannel::__cb_solo()
-{
- solo->value() ? glue_setSoloOn(ch) : glue_setSoloOff(ch);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gMidiChannel::__cb_changeVol()
-{
- glue_setChanVol(ch, vol->value());
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-#ifdef WITH_VST
-void gMidiChannel::__cb_openFxWindow()
-{
- gu_openSubWindow(mainWin, new gdPluginList(PluginHost::CHANNEL, ch), WID_FX_LIST);
-}
-#endif
-
-/* -------------------------------------------------------------------------- */
-
-
-void gMidiChannel::__cb_button()
-{
- if (button->value())
- glue_keyPress(ch, Fl::event_ctrl(), Fl::event_shift());
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gMidiChannel::__cb_openMenu()
-{
- Fl_Menu_Item rclick_menu[] = {
- {"Edit actions..."}, // 0
- {"Clear actions", 0, 0, 0, FL_SUBMENU}, // 1
- {"All"}, // 2
- {0}, // 3
- {"Setup keyboard input..."}, // 5
- {"Setup MIDI input..."}, // 6
- {"Setup MIDI output..."}, // 7
- {"Clone channel"}, // 8
- {"Delete channel"}, // 9
- {0}
- };
-
- /* no 'clear actions' if there are no actions */
-
- if (!ch->hasActions)
- rclick_menu[1].deactivate();
-
- Fl_Menu_Button *b = new Fl_Menu_Button(0, 0, 100, 50);
- b->box(G_BOX);
- b->textsize(GUI_FONT_SIZE_BASE);
- b->textcolor(COLOR_TEXT_0);
- b->color(COLOR_BG_0);
-
- const Fl_Menu_Item *m = rclick_menu->popup(Fl::event_x(), Fl::event_y(), 0, 0, b);
- if (!m) return;
-
- if (strcmp(m->label(), "Delete channel") == 0) {
- if (!gdConfirmWin("Warning", "Delete channel: are you sure?"))
- return;
- glue_deleteChannel(ch);
- return;
- }
-
- if (strcmp(m->label(), "Clone channel") == 0) {
- glue_cloneChannel(ch);
- return;
- }
-
- if (strcmp(m->label(), "Setup keyboard input...") == 0) {
- gu_openSubWindow(mainWin, new gdKeyGrabber(ch), 0);
- //new gdKeyGrabber(ch);
- return;
- }
-
- if (strcmp(m->label(), "All") == 0) {
- if (!gdConfirmWin("Warning", "Clear all actions: are you sure?"))
- return;
- recorder::clearChan(ch->index);
- gu_refreshActionEditor(); // refresh a.editor window, it could be open
- return;
- }
-
- if (strcmp(m->label(), "Edit actions...") == 0) {
- gu_openSubWindow(mainWin, new gdActionEditor(ch), WID_ACTION_EDITOR);
- return;
- }
-
- if (strcmp(m->label(), "Setup MIDI input...") == 0) {
- gu_openSubWindow(mainWin, new gdMidiInputChannel(ch), 0);
- return;
- }
-
- if (strcmp(m->label(), "Setup MIDI output...") == 0) {
- //gu_openSubWindow(mainWin, new gdMidiGrabberChannel(ch, GrabForOutput), 0);
- gu_openSubWindow(mainWin, new gdMidiOutputMidiCh(ch), 0);
- return;
- }
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gMidiChannel::refresh()
-{
- setColorsByStatus(ch->status, ch->recStatus);
- mainButton->redraw();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gMidiChannel::reset()
-{
- mainButton->setDefaultMode("-- MIDI --");
- mainButton->redraw();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gMidiChannel::update()
-{
- if (ch->midiOut) {
- char tmp[32];
- sprintf(tmp, "-- MIDI (channel %d) --", ch->midiOutChan+1);
- mainButton->copy_label(tmp);
- }
- else
- mainButton->label("-- MIDI --");
-
- vol->value(ch->volume);
- mute->value(ch->mute);
- solo->value(ch->solo);
-
- mainButton->setKey(ch->key);
-
-#ifdef WITH_VST
- fx->full = ch->plugins.size() > 0;
- fx->redraw();
-#endif
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gMidiChannel::resize(int X, int Y, int W, int H)
-{
- gChannel::resize(X, Y, W, H);
-
- /* this stuff makes sense only with FX button available. Do nothing
- * otherwise */
-
-#ifdef WITH_VST
- if (w() < BREAK_FX) {
- fx->hide();
-
- mainButton->size(w() - (BREAK_DELTA - BREAK_UNIT), mainButton->h());
- }
- else {
- fx->show();
- mainButton->size(w() - BREAK_DELTA, mainButton->h());
- }
- mute->resize(mainButton->x()+mainButton->w()+4, y(), 20, 20);
- solo->resize(mute->x()+mute->w()+4, y(), 20, 20);
-
- gChannel::init_sizes();
-#endif
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-int gMidiChannel::keyPress(int e)
-{
- return handleKey(e, ch->key);
-}
-
-
-/* -------------------------------------------------------------------------- */
-/* -------------------------------------------------------------------------- */
-/* -------------------------------------------------------------------------- */
-
-
-gMidiChannelButton::gMidiChannelButton(int x, int y, int w, int h, const char *l)
- : gChannelButton(x, y, w, h, l) {}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-int gMidiChannelButton::handle(int e)
-{
- // MIDI drag-n-drop does nothing so far.
- return gClick::handle(e);
-}
+++ /dev/null
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * ge_midiChannel
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
- *
- * This file is part of Giada - Your Hardcore Loopmachine.
- *
- * Giada - Your Hardcore Loopmachine is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General
- * Public License as published by the Free Software Foundation, either
- * version 3 of the License, or (at your option) any later version.
- *
- * Giada - Your Hardcore Loopmachine is distributed in the hope that it
- * will be useful, but WITHOUT ANY WARRANTY; without even the implied
- * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Giada - Your Hardcore Loopmachine. If not, see
- * <http://www.gnu.org/licenses/>.
- *
- * -------------------------------------------------------------------------- */
-
-
-#ifndef GE_MIDI_CHANNEL_H
-#define GE_MIDI_CHANNEL_H
-
-
-#include <FL/Fl_Scroll.H>
-#include <FL/Fl_Box.H>
-#include <FL/Fl_Menu_Button.H>
-#include "ge_channel.h"
-#include "ge_channelButton.h"
-#include "ge_mixed.h"
-
-
-class gMidiChannel : public gChannel
-{
-private:
-
- static void cb_button (Fl_Widget *v, void *p);
- static void cb_mute (Fl_Widget *v, void *p);
- static void cb_solo (Fl_Widget *v, void *p);
- static void cb_openMenu (Fl_Widget *v, void *p);
- static void cb_changeVol (Fl_Widget *v, void *p);
-#ifdef WITH_VST
- static void cb_openFxWindow (Fl_Widget *v, void *p);
-#endif
-
- inline void __cb_mute ();
- inline void __cb_solo ();
- inline void __cb_changeVol ();
- inline void __cb_button ();
- inline void __cb_openMenu ();
- inline void __cb_readActions ();
-#ifdef WITH_VST
- inline void __cb_openFxWindow();
-#endif
-
-public:
-
- gMidiChannel(int x, int y, int w, int h, class MidiChannel *ch);
-
- void reset ();
- void update ();
- void refresh ();
- int keyPress(int event);
- void resize (int x, int y, int w, int h);
-
- class MidiChannel *ch;
-};
-
-
-/* -------------------------------------------------------------------------- */
-
-
-class gMidiChannelButton : public gChannelButton
-{
-public:
- gMidiChannelButton(int x, int y, int w, int h, const char *l=0);
- int handle(int e);
-};
-
-
-#endif
* -------------------------------------------------------------------------- */
-#include "ge_midiIoTools.h"
#include "ge_mixed.h"
+#include "ge_midiIoTools.h"
+
+
+extern KernelMidi G_KernelMidi;
-gLearner::gLearner(int X, int Y, int W, const char *l, kernelMidi::cb_midiLearn *cb, uint32_t *param)
+gLearner::gLearner(int X, int Y, int W, const char *l, KernelMidi::cb_midiLearn *cb,
+ uint32_t *param)
: Fl_Group(X, Y, W, 20),
callback(cb),
param (param)
cbData *data = (cbData*) malloc(sizeof(cbData));
data->window = (gdMidiInput*) parent(); // parent = gdMidiGrabberChannel
data->learner = this;
- kernelMidi::startMidiLearn(callback, (void*)data);
+ G_KernelMidi.startMidiLearn(callback, (void*)data);
}
else
- kernelMidi::stopMidiLearn();
+ G_KernelMidi.stopMidiLearn();
}
#define GE_LEARNER_H
-#include <FL/Fl.H>
#include <FL/Fl_Group.H>
#include "../../core/kernelMidi.h"
#include "../dialogs/gd_midiInput.h"
+extern KernelMidi G_KernelMidi;
+
+
class gLearner : public Fl_Group
{
private:
* uint32_t msg - MIDI message
* void *data - extra data */
- kernelMidi::cb_midiLearn *callback;
+ KernelMidi::cb_midiLearn *callback;
class gBox *text;
class gClick *value;
uint32_t *param;
- gLearner(int x, int y, int w, const char *l, kernelMidi::cb_midiLearn *cb, uint32_t *param);
+ gLearner(int x, int y, int w, const char *l, KernelMidi::cb_midiLearn *cb, uint32_t *param);
void updateValue();
};
#include "../../core/recorder.h"
#include "../../core/channel.h"
#include "../../core/sampleChannel.h"
-#include "../../utils/gui_utils.h"
+#include "../../utils/gui.h"
#include "../dialogs/gd_mainWindow.h"
#include "ge_mixed.h"
/*
gResizerBar::~gResizerBar()
{
- gLog("------ resizerbar %p destroyed\n", (void*)this);
+ gu_log("------ resizerbar %p destroyed\n", (void*)this);
}
*/
/* 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
{
* -------------------------------------------------------------------------- */
-#include "../../utils/gui_utils.h"
+#include "../../utils/gui.h"
#include "../../core/graphics.h"
#include "../../core/sampleChannel.h"
#include "../../core/const.h"
+++ /dev/null
-/* ---------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * ge_muteChannel
- * a widget that represents mute actions inside the action editor.
- *
- * ---------------------------------------------------------------------
- *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
- *
- * This file is part of Giada - Your Hardcore Loopmachine.
- *
- * Giada - Your Hardcore Loopmachine is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General
- * Public License as published by the Free Software Foundation, either
- * version 3 of the License, or (at your option) any later version.
- *
- * Giada - Your Hardcore Loopmachine is distributed in the hope that it
- * will be useful, but WITHOUT ANY WARRANTY; without even the implied
- * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Giada - Your Hardcore Loopmachine. If not, see
- * <http://www.gnu.org/licenses/>.
- *
- * ------------------------------------------------------------------ */
-
-
-#include "../../core/recorder.h"
-#include "../../core/mixer.h"
-#include "../../core/channel.h"
-#include "../../glue/glue.h"
-#include "../../utils/log.h"
-#include "../dialogs/gd_actionEditor.h"
-#include "../dialogs/gd_mainWindow.h"
-#include "ge_keyboard.h"
-#include "ge_actionWidget.h"
-#include "ge_muteChannel.h"
-
-
-extern gdMainWindow *mainWin;
-extern Mixer G_Mixer;
-
-
-gMuteChannel::gMuteChannel(int x, int y, gdActionEditor *pParent)
- : gActionWidget(x, y, 200, 80, pParent), draggedPoint(-1), selectedPoint(-1)
-{
- size(pParent->totalWidth, h());
- extractPoints();
-}
-
-
-/* ------------------------------------------------------------------ */
-
-
-void gMuteChannel::draw() {
-
- baseDraw();
-
- /* print label */
-
- fl_color(COLOR_BG_1);
- fl_font(FL_HELVETICA, 12);
- fl_draw("mute", x()+4, y(), w(), h(), (Fl_Align) (FL_ALIGN_LEFT | FL_ALIGN_CENTER));
-
- /* draw "on" and "off" labels. Must stay in background */
-
- fl_color(COLOR_BG_1);
- fl_font(FL_HELVETICA, 9);
- fl_draw("on", x()+4, y(), w(), h(), (Fl_Align) (FL_ALIGN_LEFT | FL_ALIGN_TOP));
- fl_draw("off", x()+4, y()+h()-14, w(), h(), (Fl_Align) (FL_ALIGN_LEFT | FL_ALIGN_TOP));
-
- /* draw on-off points. On = higher rect, off = lower rect. It always
- * starts with a note_off */
-
- fl_color(COLOR_BG_2);
-
- int pxOld = x()+1;
- int pxNew = 0;
- int py = y()+h()-5;
- int pyDot = py-6;
-
- for (unsigned i=0; 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 gMuteChannel::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 & (ACTION_MUTEON | ACTION_MUTEOFF)) {
- point p;
- p.frame = recorder::frames.at(i);
- p.type = recorder::global.at(i).at(j)->type;
- p.x = p.frame / pParent->zoom;
- points.push_back(p);
- //gLog("[gMuteChannel::extractPoints] point found, type=%d, frame=%d\n", p.type, p.frame);
- }
- }
- }
- }
-}
-
-
-/* ------------------------------------------------------------------ */
-
-
-void gMuteChannel::updateActions() {
- for (unsigned i=0; i<points.size(); i++)
- points.at(i).x = points.at(i).frame / pParent->zoom;
-}
-
-
-/* ------------------------------------------------------------------ */
-
-
-int gMuteChannel::handle(int e) {
-
- int ret = 0;
- int mouseX = Fl::event_x()-x();
-
- switch (e) {
-
- case FL_ENTER: {
- ret = 1;
- break;
- }
-
- case FL_MOVE: {
- selectedPoint = getSelectedPoint();
- redraw();
- ret = 1;
- break;
- }
-
- case FL_LEAVE: {
- draggedPoint = -1;
- selectedPoint = -1;
- redraw();
- ret = 1;
- break;
- }
-
- case FL_PUSH: {
-
- /* left click on point: drag
- * right click on point: delete
- * left click on void: add */
-
- if (Fl::event_button1()) {
-
- if (selectedPoint != -1) {
- draggedPoint = selectedPoint;
- previousXPoint = points.at(selectedPoint).x;
- }
- else {
-
- /* click on the grey area leads to nowhere */
-
- if (mouseX > pParent->coverX) {
- ret = 1;
- break;
- }
-
- /* click in the middle of a long mute_on (between two points): new actions
- * must be added in reverse: first mute_off then mute_on. Let's find the
- * next point from here. */
-
- unsigned nextPoint = points.size();
- for (unsigned i=0; 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) {
- recorder::rec(pParent->chan->index, ACTION_MUTEOFF, frame_a);
- recorder::rec(pParent->chan->index, ACTION_MUTEON, frame_b);
- }
- else {
- recorder::rec(pParent->chan->index, ACTION_MUTEON, frame_a);
- recorder::rec(pParent->chan->index, ACTION_MUTEOFF, frame_b);
- }
- recorder::sortActions();
-
- mainWin->keyboard->setChannelWithActions((gSampleChannel*)pParent->chan->guiChannel); // update mainWindow
- extractPoints();
- redraw();
- }
- }
- else {
-
- /* delete points pair */
-
- if (selectedPoint != -1) {
-
- unsigned a;
- unsigned b;
-
- if (points.at(selectedPoint).type == ACTION_MUTEOFF) {
- a = selectedPoint-1;
- b = selectedPoint;
- }
- else {
- a = selectedPoint;
- b = selectedPoint+1;
- }
-
- //gLog("selected: a=%d, b=%d >>> frame_a=%d, frame_b=%d\n",
- // a, b, points.at(a).frame, points.at(b).frame);
-
- recorder::deleteAction(pParent->chan->index, points.at(a).frame, points.at(a).type, false); // false = don't check vals
- recorder::deleteAction(pParent->chan->index, points.at(b).frame, points.at(b).type, false); // false = don't check vals
- recorder::sortActions();
-
- mainWin->keyboard->setChannelWithActions((gSampleChannel*)pParent->chan->guiChannel); // update mainWindow
- extractPoints();
- redraw();
- }
- }
- ret = 1;
- break;
- }
-
- case FL_RELEASE: {
-
- if (draggedPoint != -1) {
-
- if (points.at(draggedPoint).x == previousXPoint) {
- //gLog("nothing to do\n");
- }
- else {
-
- int newFrame = points.at(draggedPoint).x * pParent->zoom;
-
- recorder::deleteAction(
- pParent->chan->index,
- points.at(draggedPoint).frame,
- points.at(draggedPoint).type,
- false); // don't check values
-
- recorder::rec(
- pParent->chan->index,
- points.at(draggedPoint).type,
- newFrame);
-
- recorder::sortActions();
-
- points.at(draggedPoint).frame = newFrame;
- }
- }
- draggedPoint = -1;
- selectedPoint = -1;
-
- ret = 1;
- break;
- }
-
- case FL_DRAG: {
-
- if (draggedPoint != -1) {
-
- /* constrain the point between two ends (leftBorder-point,
- * point-point, point-rightBorder) */
-
- int prevPoint;
- int nextPoint;
-
- if (draggedPoint == 0) {
- prevPoint = 0;
- nextPoint = points.at(draggedPoint+1).x - 1;
- if (pParent->gridTool->isOn())
- nextPoint -= pParent->gridTool->getCellSize();
- }
- else
- if ((unsigned) draggedPoint == points.size()-1) {
- prevPoint = points.at(draggedPoint-1).x + 1;
- nextPoint = pParent->coverX-x();
- if (pParent->gridTool->isOn())
- prevPoint += pParent->gridTool->getCellSize();
- }
- else {
- prevPoint = points.at(draggedPoint-1).x + 1;
- nextPoint = points.at(draggedPoint+1).x - 1;
- if (pParent->gridTool->isOn()) {
- prevPoint += pParent->gridTool->getCellSize();
- nextPoint -= pParent->gridTool->getCellSize();
- }
- }
-
- if (mouseX <= prevPoint)
- points.at(draggedPoint).x = prevPoint;
- else
- if (mouseX >= nextPoint)
- points.at(draggedPoint).x = nextPoint;
- else
- if (pParent->gridTool->isOn())
- points.at(draggedPoint).x = pParent->gridTool->getSnapPoint(mouseX)-1;
- else
- points.at(draggedPoint).x = mouseX;
-
- redraw();
- }
- ret = 1;
- break;
- }
- }
-
-
- return ret;
-}
-
-
-/* ------------------------------------------------------------------ */
-
-
-bool gMuteChannel::pointCollides(int frame) {
- for (unsigned i=0; i<points.size(); i++)
- if (frame == points.at(i).frame)
- return true;
- return false;
-}
-
-
-/* ------------------------------------------------------------------ */
-
-
-int gMuteChannel::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
- *
- * ge_muteChannel
- * a widget representing mute actions inside the action editor.
- *
- * ---------------------------------------------------------------------
- *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
- *
- * This file is part of Giada - Your Hardcore Loopmachine.
- *
- * Giada - Your Hardcore Loopmachine is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General
- * Public License as published by the Free Software Foundation, either
- * version 3 of the License, or (at your option) any later version.
- *
- * Giada - Your Hardcore Loopmachine is distributed in the hope that it
- * will be useful, but WITHOUT ANY WARRANTY; without even the implied
- * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Giada - Your Hardcore Loopmachine. If not, see
- * <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/utils.h"
-#include "ge_actionWidget.h"
-
-
-using std::vector;
-
-
-class gMuteChannel : public gActionWidget
-{
-private:
-
- /* point
- * a single dot in the graph. */
-
- struct point {
- int frame;
- char type;
- int x;
- };
-
- /* points
- * array of on/off points, in frames */
-
- vector<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:
-
- gMuteChannel(int x, int y, class gdActionEditor *pParent);
- void draw();
- int handle(int e);
-
- /* updateActions
- * calculates new points affected by the zoom. Call this one after
- * each zoom update. */
-
- void updateActions();
-};
-
-#endif
+++ /dev/null
-/* ---------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * ge_pianoRoll
- *
- * ---------------------------------------------------------------------
- *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
- *
- * This file is part of Giada - Your Hardcore Loopmachine.
- *
- * Giada - Your Hardcore Loopmachine is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General
- * Public License as published by the Free Software Foundation, either
- * version 3 of the License, or (at your option) any later version.
- *
- * Giada - Your Hardcore Loopmachine is distributed in the hope that it
- * will be useful, but WITHOUT ANY WARRANTY; without even the implied
- * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Giada - Your Hardcore Loopmachine. If not, see
- * <http://www.gnu.org/licenses/>.
- *
- * ------------------------------------------------------------------ */
-
-
-#include <FL/fl_draw.H>
-#include "../../core/channel.h"
-#include "../../core/midiChannel.h"
-#include "../../core/const.h"
-#include "../../core/kernelMidi.h"
-#include "../../core/conf.h"
-#include "../../utils/log.h"
-#include "../dialogs/gd_mainWindow.h"
-#include "../dialogs/gd_actionEditor.h"
-#include "ge_pianoRoll.h"
-
-
-extern gdMainWindow *mainWin;
-extern Mixer G_Mixer;
-extern Conf G_Conf;
-
-
-gPianoRollContainer::gPianoRollContainer(int x, int y, class gdActionEditor *pParent)
- : Fl_Scroll(x, y, 200, 422), pParent(pParent)
-{
- size(pParent->totalWidth, G_Conf.pianoRollH);
- pianoRoll = new gPianoRoll(x, y, pParent->totalWidth, pParent);
-}
-
-
-/* ------------------------------------------------------------------ */
-
-
-gPianoRollContainer::~gPianoRollContainer() {
- clear();
- G_Conf.pianoRollH = h();
- G_Conf.pianoRollY = pianoRoll->y();
-}
-
-
-/* ------------------------------------------------------------------ */
-
-
-void gPianoRollContainer::updateActions() {
- pianoRoll->updateActions();
-}
-
-
-/* ------------------------------------------------------------------ */
-
-
-void gPianoRollContainer::draw() {
-
- pianoRoll->size(this->w(), pianoRoll->h()); /// <--- not optimal
-
- /* clear background */
-
- fl_rectf(x(), y(), w(), h(), COLOR_BG_MAIN);
-
- /* clip pianoRoll to pianoRollContainer size */
-
- fl_push_clip(x(), y(), w(), h());
- draw_child(*pianoRoll);
- fl_pop_clip();
-
- fl_color(COLOR_BD_0);
- fl_line_style(0);
- fl_rect(x(), y(), pParent->totalWidth, h());
-}
-
-
-/* ------------------------------------------------------------------ */
-/* ------------------------------------------------------------------ */
-/* ------------------------------------------------------------------ */
-
-
-gPianoRoll::gPianoRoll(int X, int Y, int W, class gdActionEditor *pParent)
- : gActionWidget(X, Y, W, 40, pParent)
-{
- resizable(NULL); // don't resize children (i.e. pianoItem)
- size(W, (MAX_NOTES+1) * CELL_H); // 128 MIDI channels * 15 px height
-
- if (G_Conf.pianoRollY == -1)
- position(x(), y()-(h()/2)); // center
- else
- position(x(), G_Conf.pianoRollY);
-
- drawSurface1();
- drawSurface2();
-
- /* add actions when the window is opened. Position is zoom-based. MIDI
- * actions come always in pair: start + end. */
-
- recorder::sortActions();
-
- recorder::action *a2 = NULL;
- recorder::action *prev = NULL;
-
- for (unsigned i=0; i<recorder::frames.size(); i++) {
- for (unsigned j=0; j<recorder::global.at(i).size(); j++) {
-
- /* don't show actions > than the grey area */
- /** FIXME - can we move this to the outer cycle? */
-
- if (recorder::frames.at(i) > G_Mixer.totalFrames)
- continue;
-
- recorder::action *a1 = recorder::global.at(i).at(j);
-
- if (a1->chan != pParent->chan->index)
- continue;
-
- if (a1->type == ACTION_MIDI) {
-
- /* if this action is == to previous one: skip it, we have already
- * checked it */
-
- if (a1 == prev) {
- //gLog("[gPianoRoll] ACTION_MIDI found, but skipping - was previous\n");
- continue;
- }
-
- /* extract MIDI infos from a1: if is note off skip it, we are looking
- * for note on only */
-
- int a1_type = kernelMidi::getB1(a1->iValue);
- int a1_note = kernelMidi::getB2(a1->iValue);
- int a1_velo = kernelMidi::getB3(a1->iValue);
-
- if (a1_type == 0x80) {
- //gLog("[gPianoRoll] ACTION_MIDI found, but skipping - was note off\n");
- continue;
- }
-
- /* search for the next action. Must have: same channel, ACTION_MIDI, greater
- * than a1->frame and with MIDI properties of note_off (0x80), same note
- * of a1, same velocity of a1 */
-
- recorder::getNextAction(
- a1->chan,
- ACTION_MIDI,
- a1->frame,
- &a2,
- kernelMidi::getIValue(0x80, a1_note, a1_velo));
-
- /* next action note off found: add a new gPianoItem to piano roll */
-
- if (a2) {
- //gLog("[gPianoRoll] ACTION_MIDI pair found, frame_a=%d frame_b=%d, note_a=%d, note_b=%d, type_a=%d, type_b=%d\n",
- // a1->frame, a2->frame, kernelMidi::getNoteValue(a1->iValue), kernelMidi::getNoteValue(a2->iValue),
- // kernelMidi::getNoteOnOff(a1->iValue), kernelMidi::getNoteOnOff(a2->iValue));
- new gPianoItem(0, 0, x(), y()+3, a1, a2, pParent);
- prev = a2;
- a2 = NULL;
- }
- else
- gLog("[gPianoRoll] recorder didn't find action!\n");
-
- }
- }
- }
-
- end();
-}
-
-
-/* ------------------------------------------------------------------ */
-
-
-void gPianoRoll::drawSurface1() {
-
- surface1 = fl_create_offscreen(40, h());
- fl_begin_offscreen(surface1);
-
- /* warning: only w() and h() come from this widget, x and y coordinates
- * are absolute, since we are writing in a memory chunk */
-
- fl_rectf(0, 0, 40, h(), COLOR_BG_MAIN);
-
- fl_line_style(FL_DASH, 0, NULL);
- fl_font(FL_HELVETICA, 11);
-
- int octave = 9;
-
- for (int i=1; i<=MAX_NOTES+1; i++) {
-
- /* print key note label. C C# D D# E F F# G G# A A# B */
-
- char note[6];
- int step = i % 12;
-
- switch (step) {
- case 1:
- fl_rectf(0, i*CELL_H, 40, CELL_H, 30, 30, 30);
- sprintf(note, "%dG", octave);
- break;
- case 2:
- sprintf(note, "%dF#", octave);
- break;
- case 3:
- sprintf(note, "%dF", octave);
- break;
- case 4:
- fl_rectf(0, i*CELL_H, 40, CELL_H, 30, 30, 30);
- sprintf(note, "%dE", octave);
- break;
- case 5:
- sprintf(note, "%dD#", octave);
- break;
- case 6:
- fl_rectf(0, i*CELL_H, 40, CELL_H, 30, 30, 30);
- sprintf(note, "%dD", octave);
- break;
- case 7:
- sprintf(note, "%dC#", octave);
- break;
- case 8:
- sprintf(note, "%dC", octave);
- break;
- case 9:
- fl_rectf(0, i*CELL_H, 40, CELL_H, 30, 30, 30);
- sprintf(note, "%dB", octave);
- break;
- case 10:
- sprintf(note, "%dA#", octave);
- break;
- case 11:
- fl_rectf(0, i*CELL_H, 40, CELL_H, 30, 30, 30);
- sprintf(note, "%dA", octave);
- break;
- case 0:
- sprintf(note, "%dG#", octave);
- octave--;
- break;
- }
-
- fl_color(fl_rgb_color(54, 54, 54));
- fl_draw(note, 4, ((i-1)*CELL_H)+1, 30, CELL_H, (Fl_Align) (FL_ALIGN_LEFT | FL_ALIGN_CENTER));
-
- /* print horizontal line */
-
- if (i < 128)
- fl_line(0, i*CELL_H, 40, +i*CELL_H);
- }
-
- fl_line_style(0);
- fl_end_offscreen();
-}
-
-
-/* ------------------------------------------------------------------ */
-
-
-void gPianoRoll::drawSurface2() {
- surface2 = fl_create_offscreen(40, h());
- fl_begin_offscreen(surface2);
- fl_rectf(0, 0, 40, h(), COLOR_BG_MAIN);
- fl_color(fl_rgb_color(54, 54, 54));
- fl_line_style(FL_DASH, 0, NULL);
- for (int i=1; i<=MAX_NOTES+1; i++) {
- int step = i % 12;
- switch (step) {
- case 1:
- case 4:
- case 6:
- case 9:
- case 11:
- fl_rectf(0, i*CELL_H, 40, CELL_H, 30, 30, 30);
- break;
- }
- if (i < 128) {
- fl_color(fl_rgb_color(54, 54, 54));
- fl_line(0, i*CELL_H, 40, +i*CELL_H);
- }
- }
- fl_line_style(0);
- fl_end_offscreen();
-}
-
-
-/* ------------------------------------------------------------------ */
-
-
-void gPianoRoll::draw() {
-
- fl_copy_offscreen(x(), y(), 40, h(), surface1, 0, 0);
-
-#if defined(__APPLE__)
- for (int i=36; i<pParent->totalWidth; i+=36) /// TODO: i < pParent->coverX is faster
- fl_copy_offscreen(x()+i, y(), 40, h(), surface2, 1, 0);
-#else
- for (int i=40; i<pParent->totalWidth; i+=40) /// TODO: i < pParent->coverX is faster
- fl_copy_offscreen(x()+i, y(), 40, h(), surface2, 0, 0);
-#endif
-
- baseDraw(false);
- draw_children();
-}
-
-
-/* ------------------------------------------------------------------ */
-
-
-int gPianoRoll::handle(int e) {
-
- int ret = Fl_Group::handle(e);
-
- switch (e) {
- case FL_PUSH: {
-
- /* avoid click on grey area */
-
- if (Fl::event_x() >= pParent->coverX) {
- ret = 1;
- break;
- }
-
-
- push_y = Fl::event_y() - y();
-
- if (Fl::event_button1()) {
-
- /* ax is driven by grid, ay by the height in px of each note */
-
- int ax = Fl::event_x();
- int ay = Fl::event_y();
-
- /* vertical snap */
-
- int edge = (ay-y()-3) % 15;
- if (edge != 0) ay -= edge;
-
- /* if no overlap, add new piano item. Also check that it doesn't
- * overflow on the grey area, by shifting it to the left if
- * necessary. */
-
- if (!onItem(ax, ay-y()-3)) {
- int greyover = ax+20 - pParent->coverX-x();
- if (greyover > 0)
- ax -= greyover;
- add(new gPianoItem(ax, ay, ax-x(), ay-y()-3, NULL, NULL, pParent));
- redraw();
- }
- }
- ret = 1;
- break;
- }
- case FL_DRAG: {
-
- if (Fl::event_button3()) {
-
- gPianoRollContainer *prc = (gPianoRollContainer*) parent();
- position(x(), Fl::event_y() - push_y);
-
- if (y() > prc->y())
- position(x(), prc->y());
- else
- if (y() < prc->y()+prc->h()-h())
- position(x(), prc->y()+prc->h()-h());
-
- prc->redraw();
- }
- ret = 1;
- break;
- }
- case FL_MOUSEWHEEL: { // nothing to do, just avoid small internal scroll
- ret = 1;
- break;
- }
- }
- return ret;
-}
-
-
-/* ------------------------------------------------------------------ */
-
-
-void gPianoRoll::updateActions() {
-
- /* when zooming, don't delete and re-add actions, just MOVE them. This
- * function shifts the action by a zoom factor. Those singlepress are
- * stretched, as well */
-
- gPianoItem *i;
- for (int k=0; k<children(); k++) {
- i = (gPianoItem*) child(k);
-
- //gLog("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();
-
- //gLog("update point %p, frame_a=%d frame_b=%d, x()=%d\n", (void*) i, i->getFrame_a(), i->getFrame_b(), i->x());
- }
-}
-
-
-/* ------------------------------------------------------------------ */
-
-
-bool gPianoRoll::onItem(int rel_x, int rel_y) {
-
- if (!pParent->chan->hasActions)
- return false;
-
- int note = MAX_NOTES - (rel_y / CELL_H);
-
- int n = children();
- for (int i=0; i<n; i++) { // no scrollbars to skip
-
- gPianoItem *p = (gPianoItem*) child(i);
- if (p->getNote() != note)
- continue;
-
- /* when 2 segments overlap?
- * start = the highest value between the two starting points
- * end = the lowest value between the two ending points
- * if start < end then there's an overlap of end-start pixels. We
- * also add 1 px to the edges in order to gain some space:
- * [ ][ ] ---> no
- * [ ] [ ] ---> yes! */
-
- int start = p->x() > rel_x ? p->x() : rel_x-1;
- int end = p->x()+p->w() < rel_x + 20 ? p->x()+p->w() : rel_x + 21;
- if (start < end)
- return true;
- }
- return false;
-}
-
-/* ------------------------------------------------------------------ */
-/* ------------------------------------------------------------------ */
-/* ------------------------------------------------------------------ */
-
-
-gPianoItem::gPianoItem(int X, int Y, int rel_x, int rel_y, recorder::action *_a, recorder::action *_b, gdActionEditor *pParent)
- : Fl_Box (X, Y, 20, gPianoRoll::CELL_H-5),
- a (_a),
- b (_b),
- pParent (pParent),
- selected(false),
- event_a (0x00),
- event_b (0x00),
- changed (false)
-{
-
- /* a is a pointer: action exists, needs to be displayed */
-
- if (a) {
- note = kernelMidi::getB2(a->iValue);
- frame_a = a->frame;
- frame_b = b->frame;
- event_a = a->iValue;
- event_b = b->iValue;
- int newX = rel_x + (frame_a / pParent->zoom);
- int newY = rel_y + getY(note);
- int newW = (frame_b - frame_a) / pParent->zoom;
- resize(newX, newY, newW, h());
- }
-
- /* a is null: action needs to be recorded from scratch */
-
- else {
- note = getNote(rel_y);
- frame_a = rel_x * pParent->zoom;
- frame_b = (rel_x + 20) * pParent->zoom;
- record();
- size((frame_b - frame_a) / pParent->zoom, h());
- }
-}
-
-
-/* ------------------------------------------------------------------ */
-
-
-bool gPianoItem::overlap() {
-
- /* when 2 segments overlap?
- * start = the highest value between the two starting points
- * end = the lowest value between the two ending points
- * if start < end then there's an overlap of end-start pixels. */
-
- gPianoRoll *pPiano = (gPianoRoll*) parent();
-
- for (int i=0; i<pPiano->children(); i++) {
-
- gPianoItem *pItem = (gPianoItem*) pPiano->child(i);
-
- /* don't check against itself and with different y positions */
-
- if (pItem == this || pItem->y() != y())
- continue;
-
- int start = pItem->x() >= x() ? pItem->x() : x();
- int end = pItem->x()+pItem->w() < x()+w() ? pItem->x()+pItem->w() : x()+w();
- if (start < end)
- return true;
- }
-
- return false;
-}
-
-
-/* ------------------------------------------------------------------ */
-
-
-void gPianoItem::draw() {
- int _w = w() > 4 ? w() : 4;
- //gLog("[gPianoItem] draw me (%p) at x=%d\n", (void*)this, x());
- fl_rectf(x(), y(), _w, h(), (Fl_Color) selected ? COLOR_BD_1 : COLOR_BG_2);
-}
-
-
-/* ------------------------------------------------------------------ */
-
-
-void gPianoItem::record() {
-
- /* avoid frame overflow */
-
- int overflow = frame_b - G_Mixer.totalFrames;
- if (overflow > 0) {
- frame_b -= overflow;
- frame_a -= overflow;
- }
-
- /* note off */
- /** FIXME - use constants */
- event_a |= (0x90 << 24); // note on
- event_a |= (note << 16); // note value
- event_a |= (0x3F << 8); // velocity
- event_a |= (0x00);
-
- event_b |= (0x80 << 24); // note off
- event_b |= (note << 16); // note value
- event_b |= (0x3F << 8); // velocity
- event_b |= (0x00);
-
- recorder::rec(pParent->chan->index, ACTION_MIDI, frame_a, event_a);
- recorder::rec(pParent->chan->index, ACTION_MIDI, frame_b, event_b);
-}
-
-
-/* ------------------------------------------------------------------ */
-
-
-void gPianoItem::remove() {
- recorder::deleteAction(pParent->chan->index, frame_a, ACTION_MIDI, true, event_a, 0.0);
- recorder::deleteAction(pParent->chan->index, frame_b, ACTION_MIDI, true, event_b, 0.0);
-
- /* send a note-off in case we are deleting it in a middle of a key_on
- * key_off sequence. */
-
- ((MidiChannel*) pParent->chan)->sendMidi(event_b);
-}
-
-
-/* ------------------------------------------------------------------ */
-
-
-int gPianoItem::handle(int e) {
-
- int ret = 0;
-
- switch (e) {
-
- case FL_ENTER: {
- selected = true;
- ret = 1;
- redraw();
- break;
- }
-
- case FL_LEAVE: {
- fl_cursor(FL_CURSOR_DEFAULT, FL_WHITE, FL_BLACK);
- selected = false;
- ret = 1;
- redraw();
- break;
- }
-
- case FL_MOVE: {
- onLeftEdge = false;
- onRightEdge = false;
-
- if (Fl::event_x() >= x() && Fl::event_x() < x()+4) {
- onLeftEdge = true;
- fl_cursor(FL_CURSOR_WE, FL_WHITE, FL_BLACK);
- }
- else
- if (Fl::event_x() >= x()+w()-4 && Fl::event_x() <= x()+w()) {
- onRightEdge = true;
- fl_cursor(FL_CURSOR_WE, FL_WHITE, FL_BLACK);
- }
- else
- fl_cursor(FL_CURSOR_DEFAULT, FL_WHITE, FL_BLACK);
-
- ret = 1;
- break;
- }
-
- case FL_PUSH: {
-
- push_x = Fl::event_x() - x();
- old_x = x();
- old_w = w();
-
- if (Fl::event_button3()) {
- fl_cursor(FL_CURSOR_DEFAULT, FL_WHITE, FL_BLACK);
- remove();
- hide(); // for Windows
- Fl::delete_widget(this);
- ((gPianoRoll*)parent())->redraw();
- }
- ret = 1;
- break;
- }
-
- case FL_DRAG: {
-
- changed = true;
-
- gPianoRoll *pr = (gPianoRoll*) parent();
- int coverX = pParent->coverX + pr->x(); // relative coverX
- int nx, ny, nw;
-
- if (onLeftEdge) {
- nx = Fl::event_x();
- ny = y();
- nw = x()-Fl::event_x()+w();
- if (nx < pr->x()) {
- nx = pr->x();
- nw = w()+x()-pr->x();
- }
- else
- if (nx > x()+w()-8) {
- nx = x()+w()-8;
- nw = 8;
- }
- resize(nx, ny, nw, h());
- }
- else
- if (onRightEdge) {
- nw = Fl::event_x()-x();
- if (Fl::event_x() < x()+8)
- nw = 8;
- else
- if (Fl::event_x() > coverX)
- nw = coverX-x();
- size(nw, h());
- }
- else {
- nx = Fl::event_x() - push_x;
- if (nx < pr->x()+1)
- nx = pr->x()+1;
- else
- if (nx+w() > coverX)
- nx = coverX-w();
-
- /* snapping */
-
- if (pParent->gridTool->isOn())
- nx = pParent->gridTool->getSnapPoint(nx-pr->x()) + pr->x() - 1;
-
- position(nx, y());
- }
-
- /* update screen */
-
- redraw();
- ((gPianoRoll*)parent())->redraw();
- ret = 1;
- break;
- }
-
- case FL_RELEASE: {
-
- /* delete & record the action, only if it doesn't overlap with
- * another one */
-
- if (overlap()) {
- resize(old_x, y(), old_w, h());
- redraw();
- }
- else
- if (changed) {
- remove();
- note = getNote(getRelY());
- frame_a = getRelX() * pParent->zoom;
- frame_b = (getRelX()+w()) * pParent->zoom;
- record();
- changed = false;
- }
-
- ((gPianoRoll*)parent())->redraw();
-
- ret = 1;
- break;
- }
- }
- return ret;
-}
+++ /dev/null
-/* ---------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * ge_pianoRoll
- *
- * ---------------------------------------------------------------------
- *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
- *
- * This file is part of Giada - Your Hardcore Loopmachine.
- *
- * Giada - Your Hardcore Loopmachine is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General
- * Public License as published by the Free Software Foundation, either
- * version 3 of the License, or (at your option) any later version.
- *
- * Giada - Your Hardcore Loopmachine is distributed in the hope that it
- * will be useful, but WITHOUT ANY WARRANTY; without even the implied
- * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Giada - Your Hardcore Loopmachine. If not, see
- * <http://www.gnu.org/licenses/>.
- *
- * ------------------------------------------------------------------ */
-
-
-#ifndef GE_PIANOROLL_H
-#define GE_PIANOROLL_H
-
-#include <FL/Fl.H>
-#include <FL/Fl_Scroll.H>
-#include <FL/Fl_Box.H>
-#include "../../core/recorder.h"
-#include "ge_actionWidget.h"
-
-
-class gPianoRollContainer : public Fl_Scroll {
-
-private:
- class gdActionEditor *pParent;
- class gPianoRoll *pianoRoll;
-
-public:
- gPianoRollContainer(int x, int y, class gdActionEditor *parent);
- ~gPianoRollContainer();
- void draw();
- void updateActions();
-};
-
-
-/* ------------------------------------------------------------------ */
-
-
-class gPianoRoll : public gActionWidget {
-
-private:
-
- /* onItem
- * is curson on a gPianoItem? */
-
- bool onItem(int rel_x, int rel_y);
-
- /* drawSurface*
- * generate a complex drawing in memory first and copy it to the
- * screen at a later point in time. Fl_Offscreen surface holds the
- * necessary data. */
-
- /* drawSurface1
- * draw first tile of note values. */
-
- void drawSurface1();
-
- /* drawSurface2
- * draw the rest of the piano roll. */
-
- void drawSurface2();
-
- int push_y;
- Fl_Offscreen surface1; // notes, no repeat
- Fl_Offscreen surface2; // lines, x-repeat
-
-
-public:
- gPianoRoll(int x, int y, int w, class gdActionEditor *pParent);
-
- void draw();
- int handle(int e);
- void updateActions();
-
- enum { MAX_NOTES = 127, CELL_H = 15 };
-};
-
-
-/* ------------------------------------------------------------------ */
-
-
-class gPianoItem : public Fl_Box {
-
-private:
-
- /* getRelX/Y
- * return x/y point of this item, relative to piano roll (and not to
- * entire screen) */
-
- inline int getRelY() { return y() - parent()->y() - 3; };
- inline int getRelX() { return x() - parent()->x(); };
-
- /* getNote
- * from a relative_y return the real MIDI note, range 0-127. 15 is
- * the hardcoded value for note height in pixels */
-
- inline int getNote(int rel_y) {
- return gPianoRoll::MAX_NOTES - (rel_y / gPianoRoll::CELL_H);
- };
-
- /* getY
- * from a note, return the y position on piano roll */
-
- inline int getY(int note) {
- return (gPianoRoll::MAX_NOTES * gPianoRoll::CELL_H) - (note * gPianoRoll::CELL_H);
- };
-
- /* overlap
- * check if this item don't overlap with another one. */
-
- bool overlap();
-
- recorder::action *a;
- recorder::action *b;
- class gdActionEditor *pParent;
-
- bool selected;
- int push_x;
-
- /* MIDI note, start frame, end frame - Used only if it's a newly added
- * action */ /** FIXME - is it true? */
-
- int note;
- int frame_a;
- int frame_b;
-
- /* event - bitmasked MIDI events, generated by record() or by ctor if
- * not newly added action */
-
- int event_a;
- int event_b;
-
- /* changed - if Item has been moved or resized: re-recording needed */
-
- bool changed;
-
- /* onLeft,RightEdge - if cursor is on a widget's edge */
-
- bool onLeftEdge;
- bool onRightEdge;
-
- /* old_x, old_w - store previous width and position while dragging
- * and moving, in order to restore it if overlap */
-
- int old_x, old_w;
-
-public:
-
- /* pianoItem ctor
- * if action *a == NULL, record a new action */
-
- gPianoItem(int x, int y, int rel_x, int rel_y, recorder::action *a, recorder::action *b, class gdActionEditor *pParent);
-
- void draw();
- int handle(int e);
- void record();
- void remove();
-
- inline int getFrame_a() { return frame_a; }
- inline int getFrame_b() { return frame_b; }
- inline int getNote() { return note; }
-
-};
-
-#endif
+++ /dev/null
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * ge_sampleChannel
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
- *
- * This file is part of Giada - Your Hardcore Loopmachine.
- *
- * Giada - Your Hardcore Loopmachine is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General
- * Public License as published by the Free Software Foundation, either
- * version 3 of the License, or (at your option) any later version.
- *
- * Giada - Your Hardcore Loopmachine is distributed in the hope that it
- * will be useful, but WITHOUT ANY WARRANTY; without even the implied
- * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Giada - Your Hardcore Loopmachine. If not, see
- * <http://www.gnu.org/licenses/>.
- *
- * -------------------------------------------------------------------------- */
-
-
-#include "../../core/pluginHost.h"
-#include "../../core/mixer.h"
-#include "../../core/conf.h"
-#include "../../core/patch_DEPR_.h"
-#include "../../core/graphics.h"
-#include "../../core/channel.h"
-#include "../../core/wave.h"
-#include "../../core/sampleChannel.h"
-#include "../../core/midiChannel.h"
-#include "../../glue/glue.h"
-#include "../../glue/channel.h"
-#include "../../glue/storage.h"
-#include "../../utils/gui_utils.h"
-#include "../dialogs/gd_mainWindow.h"
-#include "../dialogs/gd_keyGrabber.h"
-#include "../dialogs/gd_midiInput.h"
-#include "../dialogs/gd_editor.h"
-#include "../dialogs/gd_actionEditor.h"
-#include "../dialogs/gd_warnings.h"
-#include "../dialogs/gd_browser.h"
-#include "../dialogs/gd_midiOutput.h"
-#include "../dialogs/gd_pluginList.h"
-#include "../dialogs/gd_pluginChooser.h"
-#include "ge_keyboard.h"
-#include "ge_sampleChannel.h"
-#include "ge_status.h"
-#include "ge_modeBox.h"
-
-
-extern Mixer G_Mixer;
-extern Conf G_Conf;
-extern Patch_DEPR_ G_Patch_DEPR_;
-extern gdMainWindow *mainWin;
-
-
-gSampleChannel::gSampleChannel(int X, int Y, int W, int H, class SampleChannel *ch)
- : gChannel(X, Y, W, H, CHANNEL_SAMPLE), ch(ch)
-{
- begin();
-
-#if defined(WITH_VST)
- int delta = 168; // (7 widgets * 20) + (7 paddings * 4)
-#else
- int delta = 144; // (6 widgets * 20) + (6 paddings * 4)
-#endif
-
- button = new gButton(x(), y(), 20, 20, "", channelStop_xpm, channelPlay_xpm);
- status = new gStatus(button->x()+button->w()+4, y(), 20, 20, ch);
- mainButton = new gSampleChannelButton(status->x()+status->w()+4, y(), w() - delta, 20, "-- no sample --");
- modeBox = new gModeBox(mainButton->x()+mainButton->w()+4, y(), 20, 20, ch);
- mute = new gClick(modeBox->x()+modeBox->w()+4, y(), 20, 20, "", muteOff_xpm, muteOn_xpm);
- solo = new gClick(mute->x()+mute->w()+4, y(), 20, 20, "", soloOff_xpm, soloOn_xpm);
- readActions = NULL; // no 'R' button
-
-#if defined(WITH_VST)
- fx = new gFxButton(solo->x()+solo->w()+4, y(), 20, 20, fxOff_xpm, fxOn_xpm);
- vol = new gDial(fx->x()+fx->w()+4, y(), 20, 20);
-#else
- vol = new gDial(solo->x()+solo->w()+4, y(), 20, 20);
-#endif
-
- end();
-
- resizable(mainButton);
-
- update();
-
- button->callback(cb_button, (void*)this);
- button->when(FL_WHEN_CHANGED); // do callback on keypress && on keyrelease
-
-#ifdef WITH_VST
- fx->callback(cb_openFxWindow, (void*)this);
-#endif
-
- mute->type(FL_TOGGLE_BUTTON);
- mute->callback(cb_mute, (void*)this);
-
- solo->type(FL_TOGGLE_BUTTON);
- solo->callback(cb_solo, (void*)this);
-
- mainButton->callback(cb_openMenu, (void*)this);
-
- vol->callback(cb_changeVol, (void*)this);
-
- ch->guiChannel = this;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gSampleChannel::cb_button (Fl_Widget *v, void *p) { ((gSampleChannel*)p)->__cb_button(); }
-void gSampleChannel::cb_mute (Fl_Widget *v, void *p) { ((gSampleChannel*)p)->__cb_mute(); }
-void gSampleChannel::cb_solo (Fl_Widget *v, void *p) { ((gSampleChannel*)p)->__cb_solo(); }
-void gSampleChannel::cb_openMenu (Fl_Widget *v, void *p) { ((gSampleChannel*)p)->__cb_openMenu(); }
-void gSampleChannel::cb_changeVol (Fl_Widget *v, void *p) { ((gSampleChannel*)p)->__cb_changeVol(); }
-void gSampleChannel::cb_readActions (Fl_Widget *v, void *p) { ((gSampleChannel*)p)->__cb_readActions(); }
-#ifdef WITH_VST
-void gSampleChannel::cb_openFxWindow(Fl_Widget *v, void *p) { ((gSampleChannel*)p)->__cb_openFxWindow(); }
-#endif
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gSampleChannel::__cb_mute()
-{
- glue_setMute(ch);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gSampleChannel::__cb_solo()
-{
- solo->value() ? glue_setSoloOn(ch) : glue_setSoloOff(ch);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gSampleChannel::__cb_changeVol()
-{
- glue_setChanVol(ch, vol->value());
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-#ifdef WITH_VST
-void gSampleChannel::__cb_openFxWindow()
-{
- gu_openSubWindow(mainWin, new gdPluginList(PluginHost::CHANNEL, ch), WID_FX_LIST);
-}
-#endif
-
-
-/* -------------------------------------------------------------------------- */
-
-
-
-void gSampleChannel::__cb_button()
-{
- if (button->value()) // pushed
- glue_keyPress(ch, Fl::event_ctrl(), Fl::event_shift());
- else // released
- glue_keyRelease(ch, Fl::event_ctrl(), Fl::event_shift());
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gSampleChannel::__cb_openMenu()
-{
- /* if you're recording (actions or input) no menu is allowed; you can't
- * do anything, especially deallocate the channel */
-
- if (G_Mixer.chanInput == ch || recorder::active)
- return;
-
- /* the following is a trash workaround for a FLTK menu. We need a gMenu
- * widget asap */
-
- Fl_Menu_Item rclick_menu[] = {
- {"Load new sample..."}, // 0
- {"Export sample to file..."}, // 1
- {"Setup keyboard input..."}, // 2
- {"Setup MIDI input..."}, // 3
- {"Setup MIDI output..."}, // 4
- {"Edit sample..."}, // 5
- {"Edit actions..."}, // 6
- {"Clear actions", 0, 0, 0, FL_SUBMENU}, // 7
- {"All"}, // 8
- {"Mute"}, // 9
- {"Volume"}, // 10
- {"Start/Stop"}, // 11
- {0}, // 12
- {"Clone channel"}, // 13
- {"Free channel"}, // 14
- {"Delete channel"}, // 15
- {0}
- };
-
- if (ch->status & (STATUS_EMPTY | STATUS_MISSING)) {
- rclick_menu[1].deactivate();
- rclick_menu[5].deactivate();
- rclick_menu[14].deactivate();
- }
-
- /* no 'clear actions' if there are no actions */
-
- if (!ch->hasActions)
- rclick_menu[7].deactivate();
-
- /* no 'clear start/stop actions' for those channels in loop mode:
- * they cannot have start/stop actions. */
-
- if (ch->mode & LOOP_ANY)
- rclick_menu[11].deactivate();
-
- Fl_Menu_Button *b = new Fl_Menu_Button(0, 0, 100, 50);
- b->box(G_BOX);
- b->textsize(GUI_FONT_SIZE_BASE);
- b->textcolor(COLOR_TEXT_0);
- b->color(COLOR_BG_0);
-
- const Fl_Menu_Item *m = rclick_menu->popup(Fl::event_x(), Fl::event_y(), 0, 0, b);
- if (!m) return;
-
- if (strcmp(m->label(), "Load new sample...") == 0) {
- openBrowser(BROWSER_LOAD_SAMPLE);
- return;
- }
-
- if (strcmp(m->label(), "Setup keyboard input...") == 0) {
- new gdKeyGrabber(ch); /// FIXME - use gu_openSubWindow
- return;
- }
-
- if (strcmp(m->label(), "Setup MIDI input...") == 0) {
- gu_openSubWindow(mainWin, new gdMidiInputChannel(ch), 0);
- return;
- }
-
- if (strcmp(m->label(), "Setup MIDI output...") == 0) {
- gu_openSubWindow(mainWin, new gdMidiOutputSampleCh(ch), 0);
- return;
- }
-
- if (strcmp(m->label(), "Edit sample...") == 0) {
- gu_openSubWindow(mainWin, new gdEditor(ch), WID_SAMPLE_EDITOR); /// FIXME title it's up to gdEditor
- return;
- }
-
- if (strcmp(m->label(), "Export sample to file...") == 0) {
- openBrowser(BROWSER_SAVE_SAMPLE);
- return;
- }
-
- if (strcmp(m->label(), "Delete channel") == 0) {
- if (!gdConfirmWin("Warning", "Delete channel: are you sure?"))
- return;
- glue_deleteChannel(ch);
- return;
- }
-
- if (strcmp(m->label(), "Free channel") == 0) {
- if (ch->status == STATUS_PLAY) {
- if (!gdConfirmWin("Warning", "This action will stop the channel: are you sure?"))
- return;
- }
- else if (!gdConfirmWin("Warning", "Free channel: are you sure?"))
- return;
-
- glue_freeChannel(ch);
-
- /* delete any related subwindow */
-
- /** FIXME - use gu_closeAllSubwindows() */
-
- mainWin->delSubWindow(WID_FILE_BROWSER);
- mainWin->delSubWindow(WID_ACTION_EDITOR);
- mainWin->delSubWindow(WID_SAMPLE_EDITOR);
- mainWin->delSubWindow(WID_FX_LIST);
-
- return;
- }
-
- if (strcmp(m->label(), "Clone channel") == 0) {
- glue_cloneChannel(ch);
- return;
- }
-
- if (strcmp(m->label(), "Mute") == 0) {
- if (!gdConfirmWin("Warning", "Clear all mute actions: are you sure?"))
- return;
- recorder::clearAction(ch->index, ACTION_MUTEON | ACTION_MUTEOFF);
- if (!ch->hasActions)
- delActionButton();
-
- /* TODO - set mute=false */
-
- gu_refreshActionEditor(); // refresh a.editor window, it could be open
- return;
- }
-
- if (strcmp(m->label(), "Start/Stop") == 0) {
- if (!gdConfirmWin("Warning", "Clear all start/stop actions: are you sure?"))
- return;
- recorder::clearAction(ch->index, ACTION_KEYPRESS | ACTION_KEYREL | ACTION_KILLCHAN);
- if (!ch->hasActions)
- delActionButton();
- gu_refreshActionEditor(); // refresh a.editor window, it could be open
- return;
- }
-
- if (strcmp(m->label(), "Volume") == 0) {
- if (!gdConfirmWin("Warning", "Clear all volume actions: are you sure?"))
- return;
- recorder::clearAction(ch->index, ACTION_VOLUME);
- if (!ch->hasActions)
- delActionButton();
- gu_refreshActionEditor(); // refresh a.editor window, it could be open
- return;
- }
-
- if (strcmp(m->label(), "All") == 0) {
- if (!gdConfirmWin("Warning", "Clear all actions: are you sure?"))
- return;
- recorder::clearChan(ch->index);
- delActionButton();
- gu_refreshActionEditor(); // refresh a.editor window, it could be open
- return;
- }
-
- if (strcmp(m->label(), "Edit actions...") == 0) {
- gu_openSubWindow(mainWin, new gdActionEditor(ch), WID_ACTION_EDITOR);
- return;
- }
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gSampleChannel::__cb_readActions()
-{
- ch->readActions ? glue_stopReadingRecs(ch) : glue_startReadingRecs(ch);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gSampleChannel::openBrowser(int type)
-{
- gWindow *childWin = NULL;
- switch (type) {
- case BROWSER_LOAD_SAMPLE:
- childWin = new gdLoadBrowser(G_Conf.browserX, G_Conf.browserY,
- G_Conf.browserW, G_Conf.browserH, "Browse sample",
- G_Conf.samplePath.c_str(), glue_loadSample, ch);
- break;
- case BROWSER_SAVE_SAMPLE:
- childWin = new gdSaveBrowser(G_Conf.browserX, G_Conf.browserY,
- G_Conf.browserW, G_Conf.browserH, "Save sample", \
- G_Conf.samplePath.c_str(), "", glue_saveSample, ch);
- break;
- }
- if (childWin)
- gu_openSubWindow(mainWin, childWin, WID_FILE_BROWSER);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gSampleChannel::refresh()
-{
- if (!mainButton->visible()) // mainButton invisible? status too (see below)
- return;
-
- setColorsByStatus(ch->status, ch->recStatus);
-
- if (ch->wave != NULL) {
- if (G_Mixer.chanInput == ch)
- mainButton->setInputRecordMode();
- if (recorder::active) {
- if (recorder::canRec(ch))
- mainButton->setActionRecordMode();
- }
- status->redraw(); // status invisible? sampleButton too (see below)
- }
- mainButton->redraw();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gSampleChannel::reset()
-{
- delActionButton(true); // force==true, don't check, just remove it
- mainButton->setDefaultMode("-- no sample --");
- mainButton->redraw();
- status->redraw();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gSampleChannel::update()
-{
- /* update sample button's label */
-
- switch (ch->status) {
- case STATUS_EMPTY:
- mainButton->label("-- no sample --");
- break;
- case STATUS_MISSING:
- case STATUS_WRONG:
- mainButton->label("* file not found! *");
- break;
- default:
- mainButton->label(ch->wave->name.c_str());
- break;
- }
-
- /* update channels. If you load a patch with recorded actions, the 'R'
- * button must be shown. Moreover if the actions are active, the 'R'
- * button must be activated accordingly. */
-
- if (ch->hasActions)
- addActionButton();
- else
- delActionButton();
-
- /* updates modebox */
-
- modeBox->value(ch->mode);
- modeBox->redraw();
-
- /* update volumes+mute+solo */
-
- vol->value(ch->volume);
- mute->value(ch->mute);
- solo->value(ch->solo);
-
- mainButton->setKey(ch->key);
-
-#ifdef WITH_VST
- fx->full = ch->plugins.size() > 0;
- fx->redraw();
-#endif
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-int gSampleChannel::keyPress(int e)
-{
- return handleKey(e, ch->key);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gSampleChannel::addActionButton()
-{
- /* quit if 'R' exists yet. */
-
- if (readActions != NULL)
- return;
-
- mainButton->size(mainButton->w()-24, mainButton->h());
-
- redraw();
-
- readActions = new gClick(mainButton->x() + mainButton->w() + 4,
- mainButton->y(), 20, 20, "", readActionOff_xpm,
- readActionOn_xpm);
- readActions->type(FL_TOGGLE_BUTTON);
- readActions->value(ch->readActions);
- readActions->callback(cb_readActions, (void*)this);
- add(readActions);
-
- /* hard redraw: there's no other way to avoid glitches when moving
- * the 'R' button */
-
- mainWin->keyboard->redraw();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gSampleChannel::delActionButton(bool force)
-{
- if (readActions == NULL)
- return;
-
- /* TODO - readActions check is useless here */
-
- if (!force && (readActions == NULL || ch->hasActions))
- return;
-
- remove(readActions); // delete from Keyboard group (FLTK)
- delete readActions; // delete (C++)
- readActions = NULL;
-
- mainButton->size(mainButton->w()+24, mainButton->h());
- mainButton->redraw();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gSampleChannel::resize(int X, int Y, int W, int H)
-{
- gChannel::resize(X, Y, W, H);
-
- if (w() < BREAK_FX) {
-#ifdef WITH_VST
- fx->hide();
-#endif
- mainButton->size(w() - BREAK_DELTA, mainButton->h());
- mute->resize(mainButton->x()+mainButton->w()+4, y(), 20, 20);
- solo->resize(mute->x()+mute->w()+4, y(), 20, 20);
-
- /* The followings are useless on manual resizing, but useful when a channel
- * is added from a patch with a small width. */
-
- modeBox->hide();
- if (readActions)
- readActions->hide();
- }
- else
- if (w() < BREAK_MODE_BOX) {
-#ifdef WITH_VST
- fx->show();
-#endif
- mainButton->size(w() - (BREAK_DELTA + BREAK_UNIT), mainButton->h());
- mute->resize(mainButton->x()+mainButton->w()+4, y(), 20, 20);
- solo->resize(mute->x()+mute->w()+4, y(), 20, 20);
- modeBox->hide();
- }
- else
- if (w() < BREAK_READ_ACTIONS) {
- modeBox->show();
- mainButton->size(w() - (BREAK_DELTA + (BREAK_UNIT * 2)), mainButton->h());
- modeBox->resize(mainButton->x()+mainButton->w()+4, y(), 20, 20);
- if (readActions)
- readActions->hide();
- }
- else {
- if (readActions) {
- mainButton->size(w() - (BREAK_DELTA + (BREAK_UNIT * 3)), mainButton->h());
- readActions->resize(mainButton->x()+mainButton->w()+4, y(), 20, 20);
- readActions->show();
- }
- }
-
- gChannel::init_sizes();
-}
-
-
-/* -------------------------------------------------------------------------- */
-/* -------------------------------------------------------------------------- */
-/* -------------------------------------------------------------------------- */
-
-
-gSampleChannelButton::gSampleChannelButton(int x, int y, int w, int h, const char *l)
- : gChannelButton(x, y, w, h, l) {}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-int gSampleChannelButton::handle(int e)
-{
- int ret = gClick::handle(e);
- switch (e) {
- case FL_DND_ENTER:
- case FL_DND_DRAG:
- case FL_DND_RELEASE: {
- ret = 1;
- break;
- }
- case FL_PASTE: {
- gSampleChannel *gch = (gSampleChannel*) parent(); // parent is gSampleChannel
- SampleChannel *ch = gch->ch;
- int result = glue_loadChannel(ch, gTrim(gStripFileUrl(Fl::event_text())).c_str());
- if (result != SAMPLE_LOADED_OK)
- mainWin->keyboard->printChannelMessage(result);
- ret = 1;
- break;
- }
- }
- return ret;
-}
+++ /dev/null
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * ge_sampleChannel
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
- *
- * This file is part of Giada - Your Hardcore Loopmachine.
- *
- * Giada - Your Hardcore Loopmachine is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General
- * Public License as published by the Free Software Foundation, either
- * version 3 of the License, or (at your option) any later version.
- *
- * Giada - Your Hardcore Loopmachine is distributed in the hope that it
- * will be useful, but WITHOUT ANY WARRANTY; without even the implied
- * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Giada - Your Hardcore Loopmachine. If not, see
- * <http://www.gnu.org/licenses/>.
- *
- * -------------------------------------------------------------------------- */
-
-
-#ifndef GE_SAMPLE_CHANNEL_H
-#define GE_SAMPLE_CHANNEL_H
-
-
-#include <FL/Fl_Scroll.H>
-#include <FL/Fl_Box.H>
-#include <FL/Fl_Menu_Button.H>
-#include "ge_channel.h"
-#include "ge_channelButton.h"
-#include "ge_mixed.h"
-
-
-class gSampleChannel : public gChannel
-{
-private:
-
- static void cb_button (Fl_Widget *v, void *p);
- static void cb_mute (Fl_Widget *v, void *p);
- static void cb_solo (Fl_Widget *v, void *p);
- static void cb_openMenu (Fl_Widget *v, void *p);
- static void cb_changeVol (Fl_Widget *v, void *p);
- static void cb_readActions (Fl_Widget *v, void *p);
-#ifdef WITH_VST
- static void cb_openFxWindow (Fl_Widget *v, void *p);
-#endif
-
- inline void __cb_mute ();
- inline void __cb_solo ();
- inline void __cb_changeVol ();
- inline void __cb_button ();
- inline void __cb_openMenu ();
- inline void __cb_readActions ();
-#ifdef WITH_VST
- inline void __cb_openFxWindow();
-#endif
-
- void openBrowser(int type);
-
-public:
-
- gSampleChannel(int x, int y, int w, int h, class SampleChannel *ch);
-
- void reset ();
- void update ();
- void refresh ();
- int keyPress(int event);
- void resize (int x, int y, int w, int h);
-
- /* add/delActionButton
- * add or remove 'R' button when actions are available. 'Status' is
- * the initial status of the button: on or off.
- * If force==true remove the button with no further checks. */
-
- void addActionButton();
- void delActionButton(bool force=false);
-
- class gModeBox *modeBox;
- class gClick *readActions;
-
- class SampleChannel *ch;
-};
-
-
-/* -------------------------------------------------------------------------- */
-
-
-class gSampleChannelButton : public gChannelButton
-{
-public:
- gSampleChannelButton(int x, int y, int w, int h, const char *l=0);
- int handle(int e);
-};
-
-
-#endif
#include "../../core/mixer.h"
+#include "../../core/const.h"
#include "ge_status.h"
-extern Mixer G_Mixer;
+extern Mixer G_Mixer;
+extern Recorder G_Recorder;
gStatus::gStatus(int x, int y, int w, int h, SampleChannel *ch, const char *L)
fl_rectf(x()+1, y()+1, w()-2, h()-2, COLOR_BG_0); // status empty
- if (G_Mixer.chanInput == ch)
+ if (G_Mixer.recording && ch->armed)
fl_rectf(x()+1, y()+1, w()-2, h()-2, COLOR_BG_3); // take in progress
else
- if (recorder::active && recorder::canRec(ch))
+ if (G_Recorder.active && G_Recorder.canRec(ch))
fl_rectf(x()+1, y()+1, w()-2, h()-2, COLOR_BG_4); // action record
/* equation for the progress bar:
#include "../../core/graphics.h"
#include "../../core/mixer.h"
+#include "../../core/const.h"
#include "../elems/ge_mixed.h"
#include "../elems/ge_waveform.h"
#include "ge_waveTools.h"
/* ------------------------------------------------------------------ */
-void gWaveTools::updateWaveform()
+void gWaveTools::updateWaveform()
{
waveform->alloc(w());
waveform->redraw();
/* ------------------------------------------------------------------ */
-void gWaveTools::resize(int x, int y, int w, int h)
+void gWaveTools::resize(int x, int y, int w, int h)
{
if (this->w() == w || (this->w() != w && this->h() != h)) { // vertical or both resize
Fl_Widget::resize(x, y, w, h);
/* ------------------------------------------------------------------ */
-int gWaveTools::handle(int e)
+int gWaveTools::handle(int e)
{
int ret = Fl_Group::handle(e);
switch (e) {
}
return ret;
}
-
#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/glue.h"
+#include "../../glue/channel.h"
#include "../dialogs/gd_editor.h"
#include "ge_waveTools.h"
#include "ge_mixed.h"
for (unsigned k=0; k<grid.points.size(); k++) {
if (grid.points.at(k) == i) {
- //gLog("draw grid line at %d\n", 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());
#include <FL/Fl_Widget.H>
#include <FL/fl_draw.H>
#include <math.h>
-#include "../../utils/utils.h"
+#include "../../utils/fs.h"
using std::vector;
/** TODO - useless: delete ---------------------------------------- */
for (unsigned i=0; i<subWindows.size(); i++)
if (w->getId() == subWindows.at(i)->getId()) {
- //gLog("[gWindow] window %p (id=%d) exists, not added (and deleted)\n", (void*)w, w->getId());
+ //gu_log("[gWindow] window %p (id=%d) exists, not added (and deleted)\n", (void*)w, w->getId());
delete w;
return;
}
void gWindow::debug() {
- gLog("---- window stack (id=%d): ----\n", getId());
+ gu_log("---- window stack (id=%d): ----\n", getId());
for (unsigned i=0; i<subWindows.size(); i++)
- gLog("[gWindow] %p (id=%d)\n", (void*)subWindows.at(i), subWindows.at(i)->getId());
- gLog("----\n");
+ gu_log("[gWindow] %p (id=%d)\n", (void*)subWindows.at(i), subWindows.at(i)->getId());
+ gu_log("----\n");
}
#include <vector>
#include <FL/Fl_Double_Window.H>
-#include "../../utils/utils.h"
+#include "../../utils/fs.h"
using std::vector;
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * ge_midiChannel
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#include "../../core/mixer.h"
+#include "../../core/conf.h"
+#include "../../core/patch_DEPR_.h"
+#include "../../core/graphics.h"
+#include "../../core/channel.h"
+#include "../../core/midiChannel.h"
+#include "../../glue/channel.h"
+#include "../../glue/main.h"
+#include "../../glue/io.h"
+#include "../../utils/gui.h"
+#include "../dialogs/gd_mainWindow.h"
+#include "../dialogs/gd_keyGrabber.h"
+#include "../dialogs/gd_midiInput.h"
+#include "../dialogs/gd_editor.h"
+#include "../dialogs/gd_actionEditor.h"
+#include "../dialogs/gd_warnings.h"
+#include "../dialogs/gd_browser.h"
+#include "../dialogs/gd_keyGrabber.h"
+#include "../dialogs/gd_midiOutput.h"
+#include "../dialogs/gd_pluginList.h"
+#include "midiChannel.h"
+
+
+extern Mixer G_Mixer;
+extern Conf G_Conf;
+extern Recorder G_Recorder;
+extern Patch_DEPR_ G_Patch_DEPR_;
+extern gdMainWindow *G_MainWin;
+
+
+geMidiChannel::geMidiChannel(int X, int Y, int W, int H, MidiChannel *ch)
+ : geChannel(X, Y, W, H, CHANNEL_MIDI, ch)
+{
+ begin();
+
+#if defined(WITH_VST)
+ int delta = 144; // (6 widgets * 20) + (6 paddings * 4)
+#else
+ int delta = 120; // (5 widgets * 20) + (5 paddings * 4)
+#endif
+
+ button = new gButton(x(), y(), 20, 20, "", channelStop_xpm, channelPlay_xpm);
+ arm = new gClick(button->x()+button->w()+4, y(), 20, 20, "", armOff_xpm, armOn_xpm);
+ mainButton = new geMidiChannelButton(arm->x()+arm->w()+4, y(), w() - delta, 20, "-- MIDI --");
+ mute = new gClick(mainButton->x()+mainButton->w()+4, y(), 20, 20, "", muteOff_xpm, muteOn_xpm);
+ solo = new gClick(mute->x()+mute->w()+4, y(), 20, 20, "", soloOff_xpm, soloOn_xpm);
+#if defined(WITH_VST)
+ fx = new gFxButton(solo->x()+solo->w()+4, y(), 20, 20, fxOff_xpm, fxOn_xpm);
+ vol = new gDial(fx->x()+fx->w()+4, y(), 20, 20);
+#else
+ vol = new gDial(solo->x()+solo->w()+4, y(), 20, 20);
+#endif
+
+ end();
+
+ resizable(mainButton);
+
+ update();
+
+ button->callback(cb_button, (void*)this);
+ button->when(FL_WHEN_CHANGED); // do callback on keypress && on keyrelease
+
+ arm->type(FL_TOGGLE_BUTTON);
+ arm->callback(cb_arm, (void*)this);
+
+#ifdef WITH_VST
+ fx->callback(cb_openFxWindow, (void*)this);
+#endif
+
+ mute->type(FL_TOGGLE_BUTTON);
+ mute->callback(cb_mute, (void*)this);
+
+ solo->type(FL_TOGGLE_BUTTON);
+ solo->callback(cb_solo, (void*)this);
+
+ mainButton->callback(cb_openMenu, (void*)this);
+
+ vol->callback(cb_changeVol, (void*)this);
+
+ ch->guiChannel = this;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geMidiChannel::cb_button (Fl_Widget *v, void *p) { ((geMidiChannel*)p)->__cb_button(); }
+void geMidiChannel::cb_openMenu (Fl_Widget *v, void *p) { ((geMidiChannel*)p)->__cb_openMenu(); }
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geMidiChannel::__cb_button()
+{
+ if (button->value())
+ glue_keyPress(ch, Fl::event_ctrl(), Fl::event_shift());
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geMidiChannel::__cb_openMenu()
+{
+ Fl_Menu_Item rclick_menu[] = {
+ {"Edit actions..."}, // 0
+ {"Clear actions", 0, 0, 0, FL_SUBMENU}, // 1
+ {"All"}, // 2
+ {0}, // 3
+ {"Setup keyboard input..."}, // 5
+ {"Setup MIDI input..."}, // 6
+ {"Setup MIDI output..."}, // 7
+ {"Clone channel"}, // 8
+ {"Delete channel"}, // 9
+ {0}
+ };
+
+ /* no 'clear actions' if there are no actions */
+
+ if (!ch->hasActions)
+ rclick_menu[1].deactivate();
+
+ Fl_Menu_Button *b = new Fl_Menu_Button(0, 0, 100, 50);
+ b->box(G_BOX);
+ b->textsize(GUI_FONT_SIZE_BASE);
+ b->textcolor(COLOR_TEXT_0);
+ b->color(COLOR_BG_0);
+
+ const Fl_Menu_Item *m = rclick_menu->popup(Fl::event_x(), Fl::event_y(), 0, 0, b);
+ if (!m) return;
+
+ if (strcmp(m->label(), "Delete channel") == 0) {
+ if (!gdConfirmWin("Warning", "Delete channel: are you sure?"))
+ return;
+ glue_deleteChannel(ch);
+ return;
+ }
+
+ if (strcmp(m->label(), "Clone channel") == 0) {
+ glue_cloneChannel(ch);
+ return;
+ }
+
+ if (strcmp(m->label(), "Setup keyboard input...") == 0) {
+ gu_openSubWindow(G_MainWin, new gdKeyGrabber(ch), 0);
+ //new gdKeyGrabber(ch);
+ return;
+ }
+
+ if (strcmp(m->label(), "All") == 0) {
+ if (!gdConfirmWin("Warning", "Clear all actions: are you sure?"))
+ return;
+ G_Recorder.clearChan(ch->index);
+ gu_refreshActionEditor(); // refresh a.editor window, it could be open
+ return;
+ }
+
+ if (strcmp(m->label(), "Edit actions...") == 0) {
+ gu_openSubWindow(G_MainWin, new gdActionEditor(ch), WID_ACTION_EDITOR);
+ return;
+ }
+
+ if (strcmp(m->label(), "Setup MIDI input...") == 0) {
+ gu_openSubWindow(G_MainWin, new gdMidiInputChannel(ch), 0);
+ return;
+ }
+
+ if (strcmp(m->label(), "Setup MIDI output...") == 0) {
+ //gu_openSubWindow(G_MainWin, new gdMidiGrabberChannel(ch, GrabForOutput), 0);
+ gu_openSubWindow(G_MainWin, new gdMidiOutputMidiCh((MidiChannel*) ch), 0);
+ return;
+ }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geMidiChannel::refresh()
+{
+ setColorsByStatus(ch->status, ch->recStatus);
+ mainButton->redraw();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geMidiChannel::reset()
+{
+ mainButton->setDefaultMode("-- MIDI --");
+ mainButton->redraw();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geMidiChannel::update()
+{
+ if (((MidiChannel*) ch)->midiOut) {
+ char tmp[32];
+ sprintf(tmp, "-- MIDI (channel %d) --", ((MidiChannel*) ch)->midiOutChan+1);
+ mainButton->copy_label(tmp);
+ }
+ else
+ mainButton->label("-- MIDI --");
+
+ vol->value(ch->volume);
+ mute->value(ch->mute);
+ solo->value(ch->solo);
+
+ mainButton->setKey(ch->key);
+
+#ifdef WITH_VST
+ fx->full = ch->plugins.size() > 0;
+ fx->redraw();
+#endif
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geMidiChannel::resize(int X, int Y, int W, int H)
+{
+ geChannel::resize(X, Y, W, H);
+
+ arm->hide();
+#ifdef WITH_VST
+ fx->hide();
+#endif
+
+ if (w() > BREAK_ARM)
+ arm->show();
+#ifdef WITH_VST
+ if (w() > BREAK_FX)
+ fx->show();
+#endif
+
+ packWidgets();
+}
+
+
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+
+
+geMidiChannelButton::geMidiChannelButton(int x, int y, int w, int h, const char *l)
+ : geChannelButton(x, y, w, h, l) {}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int geMidiChannelButton::handle(int e)
+{
+ // MIDI drag-n-drop does nothing so far.
+ return gClick::handle(e);
+}
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * ge_midiChannel
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#ifndef GE_MIDI_CHANNEL_H
+#define GE_MIDI_CHANNEL_H
+
+
+#include "channel.h"
+#include "channelButton.h"
+
+
+class geMidiChannel : public geChannel
+{
+private:
+
+ static void cb_button (Fl_Widget *v, void *p);
+ static void cb_openMenu (Fl_Widget *v, void *p);
+
+ inline void __cb_button ();
+ inline void __cb_openMenu ();
+ inline void __cb_readActions ();
+
+public:
+
+ geMidiChannel(int x, int y, int w, int h, class MidiChannel *ch);
+
+ void reset ();
+ void update ();
+ void refresh ();
+ int keyPress(int event); // TODO - move to base class
+ void resize (int x, int y, int w, int h);
+};
+
+
+/* -------------------------------------------------------------------------- */
+
+
+class geMidiChannelButton : public geChannelButton
+{
+public:
+ geMidiChannelButton(int x, int y, int w, int h, const char *l=0);
+ int handle(int e);
+};
+
+
+#endif
--- /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 "ge_keyboard.h"
+#include "muteEditor.h"
+
+
+extern gdMainWindow *G_MainWin;
+extern Mixer G_Mixer;
+extern Recorder G_Recorder;
+
+
+geMuteEditor::geMuteEditor(int x, int y, gdActionEditor *pParent)
+ : geBaseActionEditor(x, y, 200, 80, pParent),
+ draggedPoint (-1),
+ selectedPoint (-1)
+{
+ size(pParent->totalWidth, h());
+ extractPoints();
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void geMuteEditor::draw()
+{
+ baseDraw();
+
+ /* print label */
+
+ fl_color(COLOR_BG_1);
+ fl_font(FL_HELVETICA, 12);
+ fl_draw("mute", x()+4, y(), w(), h(), (Fl_Align) (FL_ALIGN_LEFT | FL_ALIGN_CENTER));
+
+ /* draw "on" and "off" labels. Must stay in background */
+
+ fl_color(COLOR_BG_1);
+ fl_font(FL_HELVETICA, 9);
+ fl_draw("on", x()+4, y(), w(), h(), (Fl_Align) (FL_ALIGN_LEFT | FL_ALIGN_TOP));
+ fl_draw("off", x()+4, y()+h()-14, w(), h(), (Fl_Align) (FL_ALIGN_LEFT | FL_ALIGN_TOP));
+
+ /* draw on-off points. On = higher rect, off = lower rect. It always
+ * starts with a note_off */
+
+ fl_color(COLOR_BG_2);
+
+ int pxOld = x()+1;
+ int pxNew = 0;
+ int py = y()+h()-5;
+ int pyDot = py-6;
+
+ for (unsigned i=0; 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);
+ }
+ G_Recorder.sortActions();
+
+ G_MainWin->keyboard->setChannelWithActions((geSampleChannel*)pParent->chan->guiChannel); // update mainWindow
+ extractPoints();
+ redraw();
+ }
+ }
+ else {
+
+ /* delete points pair */
+
+ if (selectedPoint != -1) {
+
+ unsigned a;
+ unsigned b;
+
+ if (points.at(selectedPoint).type == ACTION_MUTEOFF) {
+ a = selectedPoint-1;
+ b = selectedPoint;
+ }
+ else {
+ a = selectedPoint;
+ b = selectedPoint+1;
+ }
+
+ //gu_log("selected: a=%d, b=%d >>> frame_a=%d, frame_b=%d\n",
+ // a, b, points.at(a).frame, points.at(b).frame);
+
+ G_Recorder.deleteAction(pParent->chan->index, points.at(a).frame, points.at(a).type, false); // false = don't check vals
+ G_Recorder.deleteAction(pParent->chan->index, points.at(b).frame, points.at(b).type, false); // false = don't check vals
+ G_Recorder.sortActions();
+
+ G_MainWin->keyboard->setChannelWithActions((geSampleChannel*)pParent->chan->guiChannel); // update mainWindow
+ extractPoints();
+ redraw();
+ }
+ }
+ ret = 1;
+ break;
+ }
+
+ case FL_RELEASE: {
+
+ if (draggedPoint != -1) {
+
+ if (points.at(draggedPoint).x == previousXPoint) {
+ //gu_log("nothing to do\n");
+ }
+ else {
+
+ int newFrame = points.at(draggedPoint).x * pParent->zoom;
+
+ G_Recorder.deleteAction(
+ pParent->chan->index,
+ points.at(draggedPoint).frame,
+ points.at(draggedPoint).type,
+ false); // don't check values
+
+ G_Recorder.rec(
+ pParent->chan->index,
+ points.at(draggedPoint).type,
+ newFrame);
+
+ G_Recorder.sortActions();
+
+ points.at(draggedPoint).frame = newFrame;
+ }
+ }
+ draggedPoint = -1;
+ selectedPoint = -1;
+
+ ret = 1;
+ break;
+ }
+
+ case FL_DRAG: {
+
+ if (draggedPoint != -1) {
+
+ /* constrain the point between two ends (leftBorder-point,
+ * point-point, point-rightBorder) */
+
+ int prevPoint;
+ int nextPoint;
+
+ if (draggedPoint == 0) {
+ prevPoint = 0;
+ nextPoint = points.at(draggedPoint+1).x - 1;
+ if (pParent->gridTool->isOn())
+ nextPoint -= pParent->gridTool->getCellSize();
+ }
+ else
+ if ((unsigned) draggedPoint == points.size()-1) {
+ prevPoint = points.at(draggedPoint-1).x + 1;
+ nextPoint = pParent->coverX-x();
+ if (pParent->gridTool->isOn())
+ prevPoint += pParent->gridTool->getCellSize();
+ }
+ else {
+ prevPoint = points.at(draggedPoint-1).x + 1;
+ nextPoint = points.at(draggedPoint+1).x - 1;
+ if (pParent->gridTool->isOn()) {
+ prevPoint += pParent->gridTool->getCellSize();
+ nextPoint -= pParent->gridTool->getCellSize();
+ }
+ }
+
+ if (mouseX <= prevPoint)
+ points.at(draggedPoint).x = prevPoint;
+ else
+ if (mouseX >= nextPoint)
+ points.at(draggedPoint).x = nextPoint;
+ else
+ if (pParent->gridTool->isOn())
+ points.at(draggedPoint).x = pParent->gridTool->getSnapPoint(mouseX)-1;
+ else
+ points.at(draggedPoint).x = mouseX;
+
+ redraw();
+ }
+ ret = 1;
+ break;
+ }
+ }
+
+
+ return ret;
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+bool geMuteEditor::pointCollides(int frame) {
+ for (unsigned i=0; i<points.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);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gePianoItem::remove()
+{
+ G_Recorder.deleteAction(pParent->chan->index, frame_a, ACTION_MIDI, true, event_a, 0.0);
+ G_Recorder.deleteAction(pParent->chan->index, frame_b, ACTION_MIDI, true, event_b, 0.0);
+
+ /* send a note-off in case we are deleting it in a middle of a key_on
+ * key_off sequence. */
+
+ ((MidiChannel*) pParent->chan)->sendMidi(event_b);
+
+ ((gePianoRoll*) parent())->cursorOnItem = false;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int gePianoItem::handle(int e)
+{
+ int ret = 0;
+
+ switch (e) {
+
+ case FL_ENTER: {
+ ((gePianoRoll*) parent())->cursorOnItem = true;
+ selected = true;
+ ret = 1;
+ redraw();
+ break;
+ }
+
+ case FL_LEAVE: {
+ fl_cursor(FL_CURSOR_DEFAULT, FL_WHITE, FL_BLACK);
+ ((gePianoRoll*) parent())->cursorOnItem = false;
+ selected = false;
+ ret = 1;
+ redraw();
+ break;
+ }
+
+ case FL_MOVE: {
+ onLeftEdge = false;
+ onRightEdge = false;
+
+ if (Fl::event_x() >= x() && Fl::event_x() < x()+HANDLE_WIDTH) {
+ onLeftEdge = true;
+ fl_cursor(FL_CURSOR_WE, FL_WHITE, FL_BLACK);
+ }
+ else
+ if (Fl::event_x() >= x()+w()-HANDLE_WIDTH && Fl::event_x() <= x()+w()) {
+ onRightEdge = true;
+ fl_cursor(FL_CURSOR_WE, FL_WHITE, FL_BLACK);
+ }
+ else
+ fl_cursor(FL_CURSOR_DEFAULT, FL_WHITE, FL_BLACK);
+
+ ret = 1;
+ break;
+ }
+
+ case FL_PUSH: {
+
+ push_x = Fl::event_x() - x();
+ old_x = x();
+ old_w = w();
+
+ if (Fl::event_button3()) {
+ fl_cursor(FL_CURSOR_DEFAULT, FL_WHITE, FL_BLACK);
+ remove();
+ hide(); // for Windows
+ Fl::delete_widget(this);
+ ((geNoteEditor*)parent())->redraw();
+ }
+ ret = 1;
+ break;
+ }
+
+ case FL_DRAG: {
+
+ changed = true;
+
+ geNoteEditor *pr = (geNoteEditor*) parent();
+ int coverX = pParent->coverX + pr->x(); // relative coverX
+ int nx, ny, nw;
+
+ if (onLeftEdge) {
+ nx = Fl::event_x();
+ ny = y();
+ nw = x()-Fl::event_x()+w();
+ if (nx < pr->x()) {
+ nx = pr->x();
+ nw = w()+x()-pr->x();
+ }
+ else
+ if (nx > x()+w()-MIN_WIDTH) {
+ nx = x()+w()-MIN_WIDTH;
+ nw = MIN_WIDTH;
+ }
+ resize(nx, ny, nw, h());
+ }
+ else
+ if (onRightEdge) {
+ nw = Fl::event_x()-x();
+ if (Fl::event_x() < x()+MIN_WIDTH)
+ nw = MIN_WIDTH;
+ else
+ if (Fl::event_x() > coverX)
+ nw = coverX-x();
+ size(nw, h());
+ }
+ else {
+ nx = Fl::event_x() - push_x;
+ if (nx < pr->x()+1)
+ nx = pr->x()+1;
+ else
+ if (nx+w() > coverX)
+ nx = coverX-w();
+
+ /* snapping */
+
+ if (pParent->gridTool->isOn())
+ nx = pParent->gridTool->getSnapPoint(nx-pr->x()) + pr->x() - 1;
+
+ position(nx, y());
+ }
+
+ /* update screen */
+
+ redraw();
+ ((geNoteEditor*)parent())->redraw();
+ ret = 1;
+ break;
+ }
+
+ case FL_RELEASE: {
+
+ /* delete & record the action, only if it doesn't overlap with
+ * another one */
+
+ if (overlap()) {
+ resize(old_x, y(), old_w, h());
+ redraw();
+ }
+ else
+ if (changed) {
+ remove();
+ note = getNote(getRelY());
+ frame_a = getRelX() * pParent->zoom;
+ frame_b = (getRelX()+w()) * pParent->zoom;
+ record();
+ changed = false;
+ }
+
+ ((geNoteEditor*)parent())->redraw();
+
+ ret = 1;
+ break;
+ }
+ }
+ return ret;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int gePianoItem::getNote(int rel_y)
+{
+ return gePianoRoll::MAX_KEYS - (rel_y / gePianoRoll::CELL_H);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int gePianoItem::getRelY()
+{
+ return y() - parent()->y();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int gePianoItem::getRelX()
+{
+ return x() - parent()->x();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int gePianoItem::getY(int note)
+{
+ return (gePianoRoll::MAX_KEYS * gePianoRoll::CELL_H) - (note * gePianoRoll::CELL_H);
+}
--- /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
+ *
+ * ge_sampleChannel
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#include "../../core/pluginHost.h"
+#include "../../core/mixer.h"
+#include "../../core/conf.h"
+#include "../../core/patch_DEPR_.h"
+#include "../../core/recorder.h"
+#include "../../core/graphics.h"
+#include "../../core/wave.h"
+#include "../../glue/main.h"
+#include "../../glue/io.h"
+#include "../../glue/channel.h"
+#include "../../glue/storage.h"
+#include "../../utils/gui.h"
+#include "../../utils/string.h"
+#include "../dialogs/gd_mainWindow.h"
+#include "../dialogs/gd_keyGrabber.h"
+#include "../dialogs/gd_midiOutput.h"
+#include "../dialogs/gd_midiInput.h"
+#include "../dialogs/gd_editor.h"
+#include "../dialogs/gd_actionEditor.h"
+#include "../dialogs/gd_warnings.h"
+#include "../dialogs/gd_browser.h"
+#include "ge_status.h"
+#include "ge_modeBox.h"
+#include "ge_keyboard.h"
+#include "sampleChannel.h"
+
+
+extern Mixer G_Mixer;
+extern Conf G_Conf;
+extern Recorder G_Recorder;
+extern Patch_DEPR_ G_Patch_DEPR_;
+extern gdMainWindow *G_MainWin;
+
+
+geSampleChannel::geSampleChannel(int X, int Y, int W, int H, SampleChannel *ch)
+ : geChannel(X, Y, W, H, CHANNEL_SAMPLE, ch)
+{
+ begin();
+
+ button = new gButton(x(), y(), 20, 20, "", channelStop_xpm, channelPlay_xpm);
+ arm = new gClick(button->x()+button->w()+4, y(), 20, 20, "", armOff_xpm, armOn_xpm);
+ status = new gStatus(arm->x()+arm->w()+4, y(), 20, 20, ch);
+ mainButton = new geSampleChannelButton(status->x()+status->w()+4, y(), 20, 20, "-- no sample --");
+ readActions = new gClick(mainButton->x()+mainButton->w()+4, y(), 20, 20, "", readActionOff_xpm, readActionOn_xpm);
+ modeBox = new gModeBox(readActions->x()+readActions->w()+4, y(), 20, 20, ch);
+ mute = new gClick(modeBox->x()+modeBox->w()+4, y(), 20, 20, "", muteOff_xpm, muteOn_xpm);
+ solo = new gClick(mute->x()+mute->w()+4, y(), 20, 20, "", soloOff_xpm, soloOn_xpm);
+#ifdef WITH_VST
+ fx = new gFxButton(solo->x()+solo->w()+4, y(), 20, 20, fxOff_xpm, fxOn_xpm);
+ vol = new gDial(fx->x()+fx->w()+4, y(), 20, 20);
+#else
+ vol = new gDial(solo->x()+solo->w()+4, y(), 20, 20);
+#endif
+
+ end();
+
+ resizable(mainButton);
+
+ update();
+
+ button->callback(cb_button, (void*)this);
+ button->when(FL_WHEN_CHANGED); // do callback on keypress && on keyrelease
+
+ arm->type(FL_TOGGLE_BUTTON);
+ arm->callback(cb_arm, (void*)this);
+
+#ifdef WITH_VST
+ fx->callback(cb_openFxWindow, (void*)this);
+#endif
+
+ mute->type(FL_TOGGLE_BUTTON);
+ mute->callback(cb_mute, (void*)this);
+
+ solo->type(FL_TOGGLE_BUTTON);
+ solo->callback(cb_solo, (void*)this);
+
+ mainButton->callback(cb_openMenu, (void*)this);
+
+ readActions->type(FL_TOGGLE_BUTTON);
+ readActions->value(ch->readActions);
+ readActions->callback(cb_readActions, (void*)this);
+
+ vol->callback(cb_changeVol, (void*)this);
+
+ ch->guiChannel = this;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geSampleChannel::cb_button (Fl_Widget *v, void *p) { ((geSampleChannel*)p)->__cb_button(); }
+void geSampleChannel::cb_openMenu (Fl_Widget *v, void *p) { ((geSampleChannel*)p)->__cb_openMenu(); }
+void geSampleChannel::cb_readActions (Fl_Widget *v, void *p) { ((geSampleChannel*)p)->__cb_readActions(); }
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geSampleChannel::__cb_button()
+{
+ if (button->value()) // pushed
+ glue_keyPress(ch, Fl::event_ctrl(), Fl::event_shift());
+ else // released
+ glue_keyRelease(ch, Fl::event_ctrl(), Fl::event_shift());
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geSampleChannel::__cb_openMenu()
+{
+ /* If you're recording (input or actions) no menu is allowed; you can't do
+ anything, especially deallocate the channel */
+
+ if (G_Mixer.recording || G_Recorder.active)
+ return;
+
+ /* the following is a trash workaround for a FLTK menu. We need a gMenu
+ * widget asap */
+
+ Fl_Menu_Item rclick_menu[] = {
+ {"Load new sample..."}, // 0
+ {"Export sample to file..."}, // 1
+ {"Setup keyboard input..."}, // 2
+ {"Setup MIDI input..."}, // 3
+ {"Setup MIDI output..."}, // 4
+ {"Edit sample..."}, // 5
+ {"Edit actions..."}, // 6
+ {"Clear actions", 0, 0, 0, FL_SUBMENU}, // 7
+ {"All"}, // 8
+ {"Mute"}, // 9
+ {"Volume"}, // 10
+ {"Start/Stop"}, // 11
+ {0}, // 12
+ {"Clone channel"}, // 13
+ {"Free channel"}, // 14
+ {"Delete channel"}, // 15
+ {0}
+ };
+
+ if (ch->status & (STATUS_EMPTY | STATUS_MISSING)) {
+ rclick_menu[1].deactivate();
+ rclick_menu[5].deactivate();
+ rclick_menu[14].deactivate();
+ }
+
+ /* no 'clear actions' if there are no actions */
+
+ if (!ch->hasActions)
+ rclick_menu[7].deactivate();
+
+ /* no 'clear start/stop actions' for those channels in loop mode:
+ * they cannot have start/stop actions. */
+
+ if (((SampleChannel*)ch)->mode & LOOP_ANY)
+ rclick_menu[11].deactivate();
+
+ Fl_Menu_Button *b = new Fl_Menu_Button(0, 0, 100, 50);
+ b->box(G_BOX);
+ b->textsize(GUI_FONT_SIZE_BASE);
+ b->textcolor(COLOR_TEXT_0);
+ b->color(COLOR_BG_0);
+
+ const Fl_Menu_Item *m = rclick_menu->popup(Fl::event_x(), Fl::event_y(), 0, 0, b);
+ if (!m) return;
+
+ if (strcmp(m->label(), "Load new sample...") == 0) {
+ openBrowser(BROWSER_LOAD_SAMPLE);
+ return;
+ }
+
+ if (strcmp(m->label(), "Setup keyboard input...") == 0) {
+ new gdKeyGrabber(ch); /// FIXME - use gu_openSubWindow
+ return;
+ }
+
+ if (strcmp(m->label(), "Setup MIDI input...") == 0) {
+ gu_openSubWindow(G_MainWin, new gdMidiInputChannel(ch), 0);
+ return;
+ }
+
+ if (strcmp(m->label(), "Setup MIDI output...") == 0) {
+ gu_openSubWindow(G_MainWin, new gdMidiOutputSampleCh((SampleChannel*) ch), 0);
+ return;
+ }
+
+ if (strcmp(m->label(), "Edit sample...") == 0) {
+ gu_openSubWindow(G_MainWin, new gdEditor((SampleChannel*) ch), WID_SAMPLE_EDITOR); /// FIXME title it's up to gdEditor
+ return;
+ }
+
+ if (strcmp(m->label(), "Export sample to file...") == 0) {
+ openBrowser(BROWSER_SAVE_SAMPLE);
+ return;
+ }
+
+ if (strcmp(m->label(), "Delete channel") == 0) {
+ if (!gdConfirmWin("Warning", "Delete channel: are you sure?"))
+ return;
+ glue_deleteChannel(ch);
+ return;
+ }
+
+ if (strcmp(m->label(), "Free channel") == 0) {
+ if (ch->status == STATUS_PLAY) {
+ if (!gdConfirmWin("Warning", "This action will stop the channel: are you sure?"))
+ return;
+ }
+ else if (!gdConfirmWin("Warning", "Free channel: are you sure?"))
+ return;
+
+ glue_freeChannel(ch);
+
+ /* delete any related subwindow */
+
+ /** FIXME - use gu_closeAllSubwindows() */
+
+ G_MainWin->delSubWindow(WID_FILE_BROWSER);
+ G_MainWin->delSubWindow(WID_ACTION_EDITOR);
+ G_MainWin->delSubWindow(WID_SAMPLE_EDITOR);
+ G_MainWin->delSubWindow(WID_FX_LIST);
+
+ return;
+ }
+
+ if (strcmp(m->label(), "Clone channel") == 0) {
+ glue_cloneChannel(ch);
+ return;
+ }
+
+ if (strcmp(m->label(), "Mute") == 0) {
+ if (!gdConfirmWin("Warning", "Clear all mute actions: are you sure?"))
+ return;
+ G_Recorder.clearAction(ch->index, ACTION_MUTEON | ACTION_MUTEOFF);
+ if (!ch->hasActions)
+ hideActionButton();
+
+ /* TODO - set mute=false */
+
+ gu_refreshActionEditor(); // refresh a.editor window, it could be open
+ return;
+ }
+
+ if (strcmp(m->label(), "Start/Stop") == 0) {
+ if (!gdConfirmWin("Warning", "Clear all start/stop actions: are you sure?"))
+ return;
+ G_Recorder.clearAction(ch->index, ACTION_KEYPRESS | ACTION_KEYREL | ACTION_KILLCHAN);
+ if (!ch->hasActions)
+ hideActionButton();
+ gu_refreshActionEditor(); // refresh a.editor window, it could be open
+ return;
+ }
+
+ if (strcmp(m->label(), "Volume") == 0) {
+ if (!gdConfirmWin("Warning", "Clear all volume actions: are you sure?"))
+ return;
+ G_Recorder.clearAction(ch->index, ACTION_VOLUME);
+ if (!ch->hasActions)
+ hideActionButton();
+ gu_refreshActionEditor(); // refresh a.editor window, it could be open
+ return;
+ }
+
+ if (strcmp(m->label(), "All") == 0) {
+ if (!gdConfirmWin("Warning", "Clear all actions: are you sure?"))
+ return;
+ G_Recorder.clearChan(ch->index);
+ hideActionButton();
+ gu_refreshActionEditor(); // refresh a.editor window, it could be open
+ return;
+ }
+
+ if (strcmp(m->label(), "Edit actions...") == 0) {
+ gu_openSubWindow(G_MainWin, new gdActionEditor(ch), WID_ACTION_EDITOR);
+ return;
+ }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geSampleChannel::__cb_readActions()
+{
+ glue_startStopReadingRecs((SampleChannel*) ch);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geSampleChannel::openBrowser(int type)
+{
+ gWindow *childWin = NULL;
+ switch (type) {
+ case BROWSER_LOAD_SAMPLE:
+ childWin = new gdLoadBrowser(G_Conf.browserX, G_Conf.browserY,
+ G_Conf.browserW, G_Conf.browserH, "Browse sample",
+ G_Conf.samplePath.c_str(), glue_loadSample, ch);
+ break;
+ case BROWSER_SAVE_SAMPLE:
+ childWin = new gdSaveBrowser(G_Conf.browserX, G_Conf.browserY,
+ G_Conf.browserW, G_Conf.browserH, "Save sample", \
+ G_Conf.samplePath.c_str(), "", glue_saveSample, ch);
+ break;
+ }
+ if (childWin)
+ gu_openSubWindow(G_MainWin, childWin, WID_FILE_BROWSER);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geSampleChannel::refresh()
+{
+ if (!mainButton->visible()) // mainButton invisible? status too (see below)
+ return;
+
+ setColorsByStatus(ch->status, ch->recStatus);
+
+ if (((SampleChannel*) ch)->wave != NULL) {
+ if (G_Mixer.recording && ch->armed)
+ mainButton->setInputRecordMode();
+ if (G_Recorder.active) {
+ if (G_Recorder.canRec(ch))
+ mainButton->setActionRecordMode();
+ }
+ status->redraw(); // status invisible? sampleButton too (see below)
+ }
+ mainButton->redraw();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geSampleChannel::reset()
+{
+ hideActionButton();
+ mainButton->setDefaultMode("-- no sample --");
+ mainButton->redraw();
+ status->redraw();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geSampleChannel::update()
+{
+ /* update sample button's label */
+
+ switch (ch->status) {
+ case STATUS_EMPTY:
+ mainButton->label("-- no sample --");
+ break;
+ case STATUS_MISSING:
+ case STATUS_WRONG:
+ mainButton->label("* file not found! *");
+ break;
+ default:
+ mainButton->label(((SampleChannel*) ch)->wave->name.c_str());
+ break;
+ }
+
+ /* update channels. If you load a patch with recorded actions, the 'R'
+ * button must be shown. Moreover if the actions are active, the 'R'
+ * button must be activated accordingly. */
+
+ if (ch->hasActions)
+ showActionButton();
+ else
+ hideActionButton();
+
+ /* updates modebox */
+
+ modeBox->value(((SampleChannel*) ch)->mode);
+ modeBox->redraw();
+
+ /* update volumes+mute+solo */
+
+ vol->value(ch->volume);
+ mute->value(ch->mute);
+ solo->value(ch->solo);
+
+ mainButton->setKey(ch->key);
+
+#ifdef WITH_VST
+ fx->full = ch->plugins.size() > 0;
+ fx->redraw();
+#endif
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geSampleChannel::showActionButton()
+{
+ readActions->value(((SampleChannel*) ch)->readActions);
+ readActions->show();
+ packWidgets();
+ redraw();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geSampleChannel::hideActionButton()
+{
+ readActions->hide();
+ packWidgets();
+ redraw();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geSampleChannel::resize(int X, int Y, int W, int H)
+{
+ geChannel::resize(X, Y, W, H);
+
+ arm->hide();
+ modeBox->hide();
+ readActions->hide();
+#ifdef WITH_VST
+ fx->hide();
+#endif
+
+ if (w() > BREAK_ARM)
+ arm->show();
+#ifdef WITH_VST
+ if (w() > BREAK_FX)
+ fx->show();
+#endif
+ if (w() > BREAK_MODE_BOX)
+ modeBox->show();
+ if (w() > BREAK_READ_ACTIONS && ch->hasActions)
+ readActions->show();
+
+ packWidgets();
+}
+
+
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+
+
+geSampleChannelButton::geSampleChannelButton(int x, int y, int w, int h, const char *l)
+ : geChannelButton(x, y, w, h, l) {}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int geSampleChannelButton::handle(int e)
+{
+ int ret = gClick::handle(e);
+ switch (e) {
+ case FL_DND_ENTER:
+ case FL_DND_DRAG:
+ case FL_DND_RELEASE: {
+ ret = 1;
+ break;
+ }
+ case FL_PASTE: {
+ geSampleChannel *gch = (geSampleChannel*) parent(); // parent is geSampleChannel
+ SampleChannel *ch = (SampleChannel*) gch->ch;
+ int result = glue_loadChannel(ch, gu_trim(gu_stripFileUrl(Fl::event_text())).c_str());
+ if (result != SAMPLE_LOADED_OK)
+ G_MainWin->keyboard->printChannelMessage(result);
+ ret = 1;
+ break;
+ }
+ }
+ return ret;
+}
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * ge_sampleChannel
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#ifndef GE_SAMPLE_CHANNEL_H
+#define GE_SAMPLE_CHANNEL_H
+
+
+#include "channel.h"
+#include "channelButton.h"
+
+
+class geSampleChannel : public geChannel
+{
+private:
+
+ static void cb_button (Fl_Widget *v, void *p);
+ static void cb_openMenu (Fl_Widget *v, void *p);
+ static void cb_readActions (Fl_Widget *v, void *p);
+
+ inline void __cb_button ();
+ inline void __cb_openMenu ();
+ inline void __cb_readActions ();
+
+ void openBrowser(int type);
+
+public:
+
+ geSampleChannel(int x, int y, int w, int h, class SampleChannel *ch);
+
+ void reset ();
+ void update ();
+ void refresh ();
+ void resize (int x, int y, int w, int h);
+
+ /* show/hideActionButton
+ Adds or removes 'R' button when actions are available. */
+
+ void showActionButton();
+ void hideActionButton();
+
+ class gModeBox *modeBox;
+ class gClick *readActions;
+};
+
+
+/* -------------------------------------------------------------------------- */
+
+
+class geSampleChannelButton : public geChannelButton
+{
+public:
+ geSampleChannelButton(int x, int y, int w, int h, const char *l=0);
+ int handle(int e);
+};
+
+
+#endif
#include "core/mixer.h"
#include "core/mixerHandler.h"
#include "core/kernelAudio.h"
+#include "core/kernelMidi.h"
#include "core/recorder.h"
-#include "utils/gui_utils.h"
+#include "utils/gui.h"
#include "gui/dialogs/gd_mainWindow.h"
#include "core/pluginHost.h"
/* global variables. Yeah, we are nasty */
-pthread_t t_video;
+pthread_t G_videoThread;
+KernelAudio G_KernelAudio;
Mixer G_Mixer;
+Recorder G_Recorder;
+KernelMidi G_KernelMidi;
bool G_quit;
bool G_audio_status;
bool G_midiStatus;
Patch G_Patch;
Conf G_Conf;
MidiMapConf G_MidiMap;
-gdMainWindow *mainWin;
+gdMainWindow *G_MainWin;
#ifdef WITH_VST
PluginHost G_PluginHost;
#endif
-void *thread_video(void *arg);
+void *videoThreadCb(void *arg);
-int main(int argc, char **argv) {
-
+int main(int argc, char **argv)
+{
G_quit = false;
init_prepareParser();
init_prepareKernelMIDI();
init_startGUI(argc, argv);
Fl::lock();
- pthread_create(&t_video, NULL, thread_video, NULL);
+ pthread_create(&G_videoThread, NULL, videoThreadCb, NULL);
init_startKernelAudio();
#ifdef WITH_VST
juce::shutdownJuce_GUI();
#endif
- pthread_join(t_video, NULL);
+ pthread_join(G_videoThread, NULL);
return ret;
}
-
-void *thread_video(void *arg) {
+void *videoThreadCb(void *arg)
+{
if (G_audio_status)
while (!G_quit) {
- gu_refresh();
+ gu_refreshUI();
#ifdef _WIN32
Sleep(GUI_SLEEP);
#else
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * utils
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#if defined(_WIN32) // getcwd (unix) or __getcwd (win)
+ #include <direct.h>
+ #include <windows.h>
+#else
+ #include <unistd.h>
+#endif
+
+#include <cstdarg>
+#include <sys/stat.h> // stat (gu_dirExists)
+#include <errno.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string>
+#include <string.h>
+#include <limits.h>
+#if defined(__APPLE__)
+ #include <libgen.h> // basename unix
+ #include <pwd.h> // getpwuid
+#endif
+#include "../core/const.h"
+#include "string.h"
+#include "log.h"
+#include "fs.h"
+
+
+using std::string;
+using std::vector;
+
+
+bool gu_fileExists(const string &filename)
+{
+ FILE *fh = fopen(filename.c_str(), "rb");
+ if (!fh) {
+ return 0;
+ }
+ else {
+ fclose(fh);
+ return 1;
+ }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+bool gu_isDir(const string &path)
+{
+ bool ret;
+
+#if defined(__linux__)
+
+ struct stat s1;
+ stat(path.c_str(), &s1);
+ ret = S_ISDIR(s1.st_mode);
+
+#elif defined(__APPLE__)
+
+ if (strcmp(path.c_str(), "") == 0)
+ ret = false;
+ else {
+ struct stat s1;
+ stat(path.c_str(), &s1);
+ ret = S_ISDIR(s1.st_mode);
+
+ /* check if ret is a bundle, a special OS X folder which must be
+ * shown as a regular file (VST).
+ * FIXME - consider native functions CFBundle... */
+
+ if (ret) {
+ if (gu_fileExists(path + "/Contents/Info.plist"))
+ ret = false;
+ }
+ }
+
+#elif defined(__WIN32)
+
+ unsigned dwAttrib = GetFileAttributes(path.c_str());
+ ret = (dwAttrib != INVALID_FILE_ATTRIBUTES &&
+ (dwAttrib & FILE_ATTRIBUTE_DIRECTORY));
+#endif
+
+ return ret & !gu_isProject(path);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+bool gu_dirExists(const string &path)
+{
+ struct stat st;
+ if (stat(path.c_str(), &st) != 0 && errno == ENOENT)
+ return false;
+ return true;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+bool gu_mkdir(const string &path)
+{
+#if defined(__linux__) || defined(__APPLE__)
+ if (mkdir(path.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) == 0)
+#else
+ if (_mkdir(path.c_str()) == 0)
+#endif
+ return true;
+ return false;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+string gu_basename(const string &s)
+{
+ string out = s;
+ out.erase(0, out.find_last_of(G_SLASH_STR) + 1);
+ return out;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+string gu_dirname(const string &path)
+{
+ if (path.empty())
+ return "";
+ string out = path;
+ out.erase(out.find_last_of(G_SLASH_STR));
+ return out;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+string gu_getCurrentPath()
+{
+ char buf[PATH_MAX];
+#if defined(__WIN32)
+ if (_getcwd(buf, PATH_MAX) != NULL)
+#else
+ if (getcwd(buf, PATH_MAX) != NULL)
+#endif
+ return buf;
+ else
+ return "";
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+string gu_getExt(const string &file)
+{
+ // TODO - use std functions
+ int len = strlen(file.c_str());
+ int pos = len;
+ while (pos>0) {
+ if (file[pos] == '.')
+ break;
+ pos--;
+ }
+ if (pos==0)
+ return "";
+ string out = file;
+ return out.substr(pos+1, len);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+string gu_stripExt(const string &s)
+{
+ return s.substr(0, s.find_last_of("."));
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+bool gu_isProject(const string &path)
+{
+ /** FIXME - checks too weak */
+
+ if (gu_getExt(path.c_str()) == "gprj" && gu_dirExists(path))
+ return 1;
+ return 0;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+string gu_stripFileUrl(const string &f)
+{
+ string out = f;
+ out = gu_replace(out, "file://", "");
+ out = gu_replace(out, "%20", " ");
+ return out;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+string gu_getHomePath()
+{
+ char path[PATH_MAX];
+
+#if defined(__linux__)
+
+ snprintf(path, PATH_MAX, "%s/.giada", getenv("HOME"));
+
+#elif defined(_WIN32)
+
+ snprintf(path, PATH_MAX, ".");
+
+#elif defined(__APPLE__)
+
+ struct passwd *p = getpwuid(getuid());
+ if (p == NULL) {
+ gu_log("[gu_getHomePath] unable to fetch user infos\n");
+ return "";
+ }
+ else {
+ const char *home = p->pw_dir;
+ snprintf(path, PATH_MAX, "%s/Library/Application Support/Giada", home);
+ }
+
+#endif
+
+ return string(path);
+}
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * utils
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#ifndef UTILS_H
+#define UTILS_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);
+
+
+#endif
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * gui_utils
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#include "../core/mixer.h"
+#include "../core/patch_DEPR_.h"
+#include "../core/recorder.h"
+#include "../core/wave.h"
+#include "../core/pluginHost.h"
+#include "../core/channel.h"
+#include "../core/conf.h"
+#include "../core/graphics.h"
+#include "../gui/dialogs/gd_warnings.h"
+#include "../gui/dialogs/gd_mainWindow.h"
+#include "../gui/dialogs/gd_actionEditor.h"
+#include "../gui/elems/ge_keyboard.h"
+#include "../gui/elems/ge_window.h"
+#include "../gui/elems/channel.h"
+#include "log.h"
+#include "string.h"
+#include "gui.h"
+
+
+extern Mixer G_Mixer;
+extern unsigned G_beats;
+extern bool G_audio_status;
+extern Patch_DEPR_ G_patch;
+extern Conf G_conf;
+extern uint32_t G_time;
+extern gdMainWindow *G_MainWin;
+#ifdef WITH_VST
+extern PluginHost G_PluginHost;
+#endif
+
+
+static int blinker = 0;
+
+
+void gu_refreshUI()
+{
+ Fl::lock();
+
+ /* update dynamic elements: in and out meters, beat meter and
+ * each channel */
+
+ G_MainWin->inOut->refresh();
+ G_MainWin->beatMeter->redraw();
+ G_MainWin->keyboard->refreshColumns();
+
+ /* compute timer for blinker */
+
+ blinker++;
+ if (blinker > 12)
+ blinker = 0;
+
+ /* redraw GUI */
+
+ Fl::unlock();
+ Fl::awake();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int gu_getBlinker()
+{
+ return blinker;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gu_updateControls()
+{
+ for (unsigned i=0; i<G_Mixer.channels.size(); i++)
+ G_Mixer.channels.at(i)->guiChannel->update();
+
+ G_MainWin->inOut->setOutVol(G_Mixer.outVol);
+ G_MainWin->inOut->setInVol(G_Mixer.inVol);
+#ifdef WITH_VST
+ G_MainWin->inOut->setMasterFxOutFull(G_PluginHost.getStack(PluginHost::MASTER_OUT)->size() > 0);
+ G_MainWin->inOut->setMasterFxInFull(G_PluginHost.getStack(PluginHost::MASTER_IN)->size() > 0);
+#endif
+
+ G_MainWin->timing->setMeter(G_Mixer.beats, G_Mixer.bars);
+ G_MainWin->timing->setBpm(G_Mixer.bpm);
+
+ G_MainWin->controller->updatePlay(G_Mixer.running);
+ G_MainWin->controller->updateMetronome(G_Mixer.metronome);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gu_updateMainWinLabel(const string &s)
+{
+ std::string out = std::string(G_APP_NAME) + " - " + s;
+ G_MainWin->copy_label(out.c_str());
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gu_setFavicon(Fl_Window *w)
+{
+#if defined(__linux__)
+
+ fl_open_display();
+ Pixmap p, mask;
+ XpmCreatePixmapFromData(fl_display, DefaultRootWindow(fl_display),
+ (char **) giada_icon, &p, &mask, NULL);
+ w->icon((char *)p);
+
+#elif defined(_WIN32)
+
+ w->icon((char *)LoadIcon(fl_display, MAKEINTRESOURCE(IDI_ICON1)));
+
+#endif
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gu_openSubWindow(gWindow *parent, gWindow *child, int id)
+{
+ if (parent->hasWindow(id)) {
+ gu_log("[GU] parent has subwindow with id=%d, deleting\n", id);
+ parent->delSubWindow(id);
+ }
+ child->setId(id);
+ parent->addSubWindow(child);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gu_refreshActionEditor()
+{
+ /** TODO - why don't we simply call WID_ACTION_EDITOR->redraw()? */
+
+ gdActionEditor *aeditor = (gdActionEditor*) G_MainWin->getChild(WID_ACTION_EDITOR);
+ if (aeditor) {
+ Channel *chan = aeditor->chan;
+ G_MainWin->delSubWindow(WID_ACTION_EDITOR);
+ gu_openSubWindow(G_MainWin, new gdActionEditor(chan), WID_ACTION_EDITOR);
+ }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+gWindow *gu_getSubwindow(gWindow *parent, int id)
+{
+ if (parent->hasWindow(id))
+ return parent->getChild(id);
+ else
+ return NULL;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gu_closeAllSubwindows()
+{
+ /* don't close WID_FILE_BROWSER, because it's the caller of this
+ * function */
+
+ G_MainWin->delSubWindow(WID_ACTION_EDITOR);
+ G_MainWin->delSubWindow(WID_SAMPLE_EDITOR);
+ G_MainWin->delSubWindow(WID_FX_LIST);
+ G_MainWin->delSubWindow(WID_FX);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+string gu_removeFltkChars(const string &s)
+{
+ string out = gu_replace(s, "/", "-");
+ out = gu_replace(out, "|", "-");
+ out = gu_replace(out, "&", "-");
+ out = gu_replace(out, "_", "-");
+ return out;
+}
--- /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 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
+
+/* including stuff for the favicon */
+
+#if defined(_WIN32)
+ #include "../ext/resource.h"
+#elif defined(__linux__)
+ #include <X11/xpm.h>
+#endif
+
+
+using std::string;
+
+
+/* refresh
+ * refresh all GUI elements. */
+
+void gu_refreshUI();
+
+/* getBlinker
+* return blinker value, used to make widgets blink. */
+
+int gu_getBlinker();
+
+/* updateControls
+ * update attributes of control elements (sample names, volumes, ...).
+ * Useful when loading a new patch. */
+
+void gu_updateControls();
+
+/* update_win_label
+ * update the name of the main window */
+
+void gu_updateMainWinLabel(const string &s);
+
+void gu_setFavicon(Fl_Window *w);
+
+void gu_openSubWindow(class gWindow *parent, gWindow *child, int id);
+
+/* refreshActionEditor
+ * reload the action editor window by closing and reopening it. It's used
+ * when you delete some actions from the mainWindow and the action editor
+ * window is open. */
+
+void gu_refreshActionEditor();
+
+
+/* closeAllSubwindows
+ * close all subwindows attached to mainWin. */
+
+void gu_closeAllSubwindows();
+
+
+/* getSubwindow
+ * return a pointer to an open subwindow, otherwise NULL. */
+
+gWindow *gu_getSubwindow(class gWindow *parent, int id);
+
+/* removeFltkChars
+ * Strip special chars used by FLTK to split menus into sub-menus. */
+
+string gu_removeFltkChars(const string &s);
+
+#endif
+++ /dev/null
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * gui_utils
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
- *
- * This file is part of Giada - Your Hardcore Loopmachine.
- *
- * Giada - Your Hardcore Loopmachine is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General
- * Public License as published by the Free Software Foundation, either
- * version 3 of the License, or (at your option) any later version.
- *
- * Giada - Your Hardcore Loopmachine is distributed in the hope that it
- * will be useful, but WITHOUT ANY WARRANTY; without even the implied
- * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Giada - Your Hardcore Loopmachine. If not, see
- * <http://www.gnu.org/licenses/>.
- *
- * -------------------------------------------------------------------------- */
-
-
-#include "../core/mixer.h"
-#include "../core/patch_DEPR_.h"
-#include "../core/recorder.h"
-#include "../core/wave.h"
-#include "../core/pluginHost.h"
-#include "../core/channel.h"
-#include "../core/conf.h"
-#include "../core/graphics.h"
-#include "../gui/dialogs/gd_warnings.h"
-#include "../gui/dialogs/gd_mainWindow.h"
-#include "../gui/dialogs/gd_actionEditor.h"
-#include "../gui/elems/ge_keyboard.h"
-#include "../gui/elems/ge_window.h"
-#include "../gui/elems/ge_channel.h"
-#include "gui_utils.h"
-#include "log.h"
-
-
-extern Mixer G_Mixer;
-extern unsigned G_beats;
-extern bool G_audio_status;
-extern Patch_DEPR_ G_patch;
-extern Conf G_conf;
-extern uint32_t G_time;
-extern gdMainWindow *mainWin;
-#ifdef WITH_VST
-extern PluginHost G_PluginHost;
-#endif
-
-
-static int blinker = 0;
-
-
-void gu_refresh()
-{
- Fl::lock();
-
- /* update dynamic elements: in and out meters, beat meter and
- * each channel */
-
- mainWin->inOut->refresh();
- mainWin->beatMeter->redraw();
- mainWin->keyboard->refreshColumns();
-
- /* compute timer for blinker */
-
- blinker++;
- if (blinker > 12)
- blinker = 0;
-
- /* redraw GUI */
-
- Fl::unlock();
- Fl::awake();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-int gu_getBlinker()
-{
- return blinker;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gu_updateControls()
-{
- for (unsigned i=0; i<G_Mixer.channels.size(); i++) {
- G_Mixer.channels.at(i)->guiChannel->update();
- }
-
- mainWin->inOut->setOutVol(G_Mixer.outVol);
- mainWin->inOut->setInVol(G_Mixer.inVol);
-#ifdef WITH_VST
- mainWin->inOut->setMasterFxOutFull(G_PluginHost.getStack(PluginHost::MASTER_OUT)->size() > 0);
- mainWin->inOut->setMasterFxInFull(G_PluginHost.getStack(PluginHost::MASTER_IN)->size() > 0);
-#endif
-
- mainWin->timing->setMeter(G_Mixer.beats, G_Mixer.bars);
- mainWin->timing->setBpm(G_Mixer.bpm);
-
- /* if you reset to init state while the seq is in play: it's better to
- * update the button status */
-
- mainWin->controller->updatePlay(G_Mixer.running);
- mainWin->controller->updateMetronome(0);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gu_update_win_label(const char *c)
-{
- std::string out = G_APP_NAME;
- out += " - ";
- out += c;
- mainWin->copy_label(out.c_str());
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gu_setFavicon(Fl_Window *w)
-{
-#if defined(__linux__)
- fl_open_display();
- Pixmap p, mask;
- XpmCreatePixmapFromData(
- fl_display,
- DefaultRootWindow(fl_display),
- (char **)giada_icon,
- &p,
- &mask,
- NULL);
- w->icon((char *)p);
-#elif defined(_WIN32)
- w->icon((char *)LoadIcon(fl_display, MAKEINTRESOURCE(IDI_ICON1)));
-#endif
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gu_openSubWindow(gWindow *parent, gWindow *child, int id)
-{
- if (parent->hasWindow(id)) {
- gLog("[GU] parent has subwindow with id=%d, deleting\n", id);
- parent->delSubWindow(id);
- }
- child->setId(id);
- parent->addSubWindow(child);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gu_refreshActionEditor()
-{
- /** TODO - why don't we simply call WID_ACTION_EDITOR->redraw()? */
-
- gdActionEditor *aeditor = (gdActionEditor*) mainWin->getChild(WID_ACTION_EDITOR);
- if (aeditor) {
- Channel *chan = aeditor->chan;
- mainWin->delSubWindow(WID_ACTION_EDITOR);
- gu_openSubWindow(mainWin, new gdActionEditor(chan), WID_ACTION_EDITOR);
- }
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-gWindow *gu_getSubwindow(gWindow *parent, int id)
-{
- if (parent->hasWindow(id))
- return parent->getChild(id);
- else
- return NULL;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gu_closeAllSubwindows()
-{
- /* don't close WID_FILE_BROWSER, because it's the caller of this
- * function */
-
- mainWin->delSubWindow(WID_ACTION_EDITOR);
- mainWin->delSubWindow(WID_SAMPLE_EDITOR);
- mainWin->delSubWindow(WID_FX_LIST);
- mainWin->delSubWindow(WID_FX);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-string gu_removeFltkChars(const string &s)
-{
- string out = gReplace(s, "/", "-");
- out = gReplace(out, "|", "-");
- out = gReplace(out, "&", "-");
- out = gReplace(out, "_", "-");
- return out;
-}
+++ /dev/null
-/* ---------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * gui_utils
- *
- * ---------------------------------------------------------------------
- *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
- *
- * This file is part of Giada - Your Hardcore Loopmachine.
- *
- * Giada - Your Hardcore Loopmachine is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General
- * Public License as published by the Free Software Foundation, either
- * version 3 of the License, or (at your option) any later version.
- *
- * Giada - Your Hardcore Loopmachine is distributed in the hope that it
- * will be useful, but WITHOUT ANY WARRANTY; without even the implied
- * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Giada - Your Hardcore Loopmachine. If not, see
- * <http://www.gnu.org/licenses/>.
- *
- * ------------------------------------------------------------------ */
-
-#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
-
-/* including stuff for the favicon */
-
-#if defined(_WIN32)
- #include "../ext/resource.h"
-#elif defined(__linux__)
- #include <X11/xpm.h>
-#endif
-
-
-using std::string;
-
-
-/* refresh
- * refresh all GUI elements. */
-
-void gu_refresh();
-
-/* getBlinker
-* return blinker value, used to make widgets blink. */
-
-int gu_getBlinker();
-
-/* updateControls
- * update attributes of control elements (sample names, volumes, ...).
- * Useful when loading a new patch. */
-
-void gu_updateControls();
-
-/* update_win_label
- * update the name of the main window */
-
-void gu_update_win_label(const char *c);
-
-void gu_setFavicon(Fl_Window *w);
-
-void gu_openSubWindow(class gWindow *parent, gWindow *child, int id);
-
-/* refreshActionEditor
- * reload the action editor window by closing and reopening it. It's used
- * when you delete some actions from the mainWindow and the action editor
- * window is open. */
-
-void gu_refreshActionEditor();
-
-
-/* closeAllSubwindows
- * close all subwindows attached to mainWin. */
-
-void gu_closeAllSubwindows();
-
-
-/* getSubwindow
- * return a pointer to an open subwindow, otherwise NULL. */
-
-gWindow *gu_getSubwindow(class gWindow *parent, int id);
-
-/* removeFltkChars
- * Strip special chars used by FLTK to split menus into sub-menus. */
-
-string gu_removeFltkChars(const string &s);
-
-#endif
-/* ---------------------------------------------------------------------
+/* -----------------------------------------------------------------------------
*
* Giada - Your Hardcore Loopmachine
*
* log
*
- * ---------------------------------------------------------------------
+ * -----------------------------------------------------------------------------
*
* Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
*
* along with Giada - Your Hardcore Loopmachine. If not, see
* <http://www.gnu.org/licenses/>.
*
- * ------------------------------------------------------------------ */
+ * -------------------------------------------------------------------------- */
#include <cstdio>
#include <cstdarg>
#include <string>
-#include "../utils/utils.h"
+#include "../utils/fs.h"
#include "../core/const.h"
#include "log.h"
+using std::string;
+
+
static FILE *f;
static int mode;
static bool stat;
-int gLog_init(int m) {
+int gu_logInit(int m)
+{
mode = m;
stat = true;
if (mode == LOG_MODE_FILE) {
- std::string fpath = gGetHomePath() + "/giada.log";
+ string fpath = gu_getHomePath() + G_SLASH + "giada.log";
f = fopen(fpath.c_str(), "a");
if (!f) {
stat = false;
}
-/* ------------------------------------------------------------------ */
+/* -------------------------------------------------------------------------- */
-void gLog_close() {
+void gu_logClose()
+{
if (mode == LOG_MODE_FILE)
fclose(f);
}
-/* ------------------------------------------------------------------ */
+/* -------------------------------------------------------------------------- */
-void gLog(const char *format, ...) {
+void gu_log(const char *format, ...)
+{
if (mode == LOG_MODE_MUTE)
return;
va_list args;
-/* ---------------------------------------------------------------------
+/* -----------------------------------------------------------------------------
*
* Giada - Your Hardcore Loopmachine
*
* log
*
- * ---------------------------------------------------------------------
+ * -----------------------------------------------------------------------------
*
* Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
*
* along with Giada - Your Hardcore Loopmachine. If not, see
* <http://www.gnu.org/licenses/>.
*
- * ------------------------------------------------------------------ */
+ * -------------------------------------------------------------------------- */
#ifndef __LOG_H__
/* init
* init logger. Mode defines where to write the output: LOG_MODE_STDOUT,
* LOG_MODE_FILE and LOG_MODE_MUTE. */
-
-int gLog_init (int mode);
-void gLog_close();
+int gu_logInit(int mode);
-void gLog(const char *format, ...);
+void gu_logClose();
+
+void gu_log(const char *format, ...);
#endif
* -------------------------------------------------------------------------- */
-#include "string.h"
#include <limits.h>
+#include <sstream>
+#include "string.h"
using std::string;
-string gGetRealPath(const string &path)
+string gu_getRealPath(const string &path)
{
string out = "";
}
return out;
}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+string gu_itoa(int i)
+{
+ std::stringstream out;
+ out << i;
+ return out.str();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+string gu_trim(const string &s)
+{
+ std::size_t first = s.find_first_not_of(" \n\t");
+ std::size_t last = s.find_last_not_of(" \n\t");
+ return s.substr(first, last-first+1);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+string gu_replace(string in, const string &search, const string &replace)
+{
+ size_t pos = 0;
+ while ((pos = in.find(search, pos)) != string::npos) {
+ in.replace(pos, search.length(), replace);
+ pos += replace.length();
+ }
+ return in;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gu_split(string in, string sep, vector<string> *v)
+{
+ string full = in;
+ string token = "";
+ size_t curr = 0;
+ size_t next = -1;
+ do {
+ curr = next + 1;
+ next = full.find_first_of(sep, curr);
+ token = full.substr(curr, next - curr);
+ if (token != "")
+ v->push_back(token);
+ }
+ while (next != string::npos);
+}
#include <string>
-#include <cstdio>
#include <vector>
-#include "log.h"
using std::string;
+using std::vector;
-string gGetRealPath(const string &path);
+string gu_getRealPath(const string &path);
+
+string gu_replace(string in, const string &search, const string &replace);
+
+string gu_trim(const string &s);
+
+string gu_itoa(int i);
+
+void gu_split(string in, string sep, vector<string> *v);
+
#endif
+++ /dev/null
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * utils
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
- *
- * This file is part of Giada - Your Hardcore Loopmachine.
- *
- * Giada - Your Hardcore Loopmachine is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General
- * Public License as published by the Free Software Foundation, either
- * version 3 of the License, or (at your option) any later version.
- *
- * Giada - Your Hardcore Loopmachine is distributed in the hope that it
- * will be useful, but WITHOUT ANY WARRANTY; without even the implied
- * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Giada - Your Hardcore Loopmachine. If not, see
- * <http://www.gnu.org/licenses/>.
- *
- * -------------------------------------------------------------------------- */
-
-
-#if defined(_WIN32) // getcwd (unix) or __getcwd (win)
- #include <direct.h>
- #include <windows.h>
-#else
- #include <unistd.h>
-#endif
-
-#include <cstdarg>
-#include <sys/stat.h> // stat (gDirExists)
-#include <errno.h>
-#include <stdlib.h>
-#include <stdint.h>
-#include <string>
-#include <string.h>
-#include <sstream>
-#include <limits.h>
-#if defined(__APPLE__)
- #include <libgen.h> // basename unix
- #include <pwd.h> // getpwuid
-#endif
-#include "../core/const.h"
-#include "utils.h"
-
-
-using std::string;
-using std::vector;
-
-
-bool gFileExists(const char *filename)
-{
- FILE *fh = fopen(filename, "rb");
- if (!fh) {
- return 0;
- }
- else {
- fclose(fh);
- return 1;
- }
-}
-
-
-bool gFileExists(const string &filename)
-{
- return gFileExists(filename.c_str());
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-bool gIsDir(const char *path)
-{
- bool ret;
-
-#if defined(__linux__)
-
- struct stat s1;
- stat(path, &s1);
- ret = S_ISDIR(s1.st_mode);
-
-#elif defined(__APPLE__)
-
- if (strcmp(path, "")==0)
- ret = false;
- else {
- struct stat s1;
- stat(path, &s1);
- ret = S_ISDIR(s1.st_mode);
-
- /* check if ret is a bundle, a special OS X folder which must be
- * shown as a regular file (VST).
- * FIXME - consider native functions CFBundle... */
-
- if (ret) {
- string tmp = path;
- tmp += "/Contents/Info.plist";
- if (gFileExists(tmp.c_str()))
- ret = false;
- }
- }
-
-#elif defined(__WIN32)
-
- unsigned dwAttrib = GetFileAttributes(path);
- ret = (dwAttrib != INVALID_FILE_ATTRIBUTES &&
- (dwAttrib & FILE_ATTRIBUTE_DIRECTORY));
-#endif
-
- return ret & !gIsProject(path);
-}
-
-
-bool gIsDir(const string &path)
-{
- return gIsDir(path.c_str());
-}
-
-/* -------------------------------------------------------------------------- */
-
-
-bool gDirExists(const char *path)
-{
- struct stat st;
- if (stat(path, &st) != 0 && errno == ENOENT)
- return false;
- return true;
-}
-
-
-bool gDirExists(const string &path)
-{
- return gDirExists(path.c_str());
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-bool gMkdir(const char *path)
-{
-#if defined(__linux__) || defined(__APPLE__)
- if (mkdir(path, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) == 0)
-#else
- if (_mkdir(path) == 0)
-#endif
- return true;
- return false;
-}
-
-
-bool gMkdir(const string &path)
-{
- return gMkdir(path.c_str());
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-string gBasename(const string &s)
-{
- string out = s;
- out.erase(0, out.find_last_of(G_SLASH_STR) + 1);
- return out;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-string gDirname(const string &path)
-{
- if (path.empty())
- return "";
- string out = path;
- out.erase(out.find_last_of(G_SLASH_STR));
- return out;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-string gGetCurrentPath()
-{
- char buf[PATH_MAX];
-#if defined(__WIN32)
- if (_getcwd(buf, PATH_MAX) != NULL)
-#else
- if (getcwd(buf, PATH_MAX) != NULL)
-#endif
- return buf;
- else
- return "";
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-string gGetExt(const char *file)
-{
- int len = strlen(file);
- int pos = len;
- while (pos>0) {
- if (file[pos] == '.')
- break;
- pos--;
- }
- if (pos==0)
- return "";
- string out = file;
- return out.substr(pos+1, len);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-string gStripExt(const char *file)
-{
- int len = strlen(file);
- int pos = -1;
- for (int i=0; i<len; i++)
- if (file[i] == '.') {
- pos = i;
- break;
- }
- string out = file;
- return pos == -1 ? out : out.substr(0, pos);
-}
-
-
-string gStripExt(const string &s)
-{
- return s.substr(0, s.find_last_of("."));
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-bool gIsProject(const string &path)
-{
- /** FIXME - checks too weak */
-
- if (gGetExt(path.c_str()) == "gprj" && gDirExists(path))
- return 1;
- return 0;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-bool gIsPatch(const char *path)
-{
- if (gGetExt(path) == "gptc")
- return 1;
- return 0;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-string gGetProjectName(const char *path)
-{
- string out;
- out = gStripExt(path);
-
- int i = out.size();
- while (i>=0) { /// TODO - use gGetSlash()
-#if defined(__linux__) || defined(__APPLE__)
- if (out[i] == '/')
-#elif defined(_WIN32)
- if (out[i] == '\\')
-#endif
- break;
- i--;
- }
-
- out.erase(0, i+1); // includes the '/' (or '\' on windows)
- return out;
-}
-
-
-
-/* -------------------------------------------------------------------------- */
-
-
-string gItoa(int i)
-{
- std::stringstream out;
- out << i;
- return out.str();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-string gTrim(const char *f)
-{
- string out = f;
- return gTrim(out);
-}
-
-
-string gTrim(const string &s)
-{
- std::size_t first = s.find_first_not_of(" \n\t");
- std::size_t last = s.find_last_not_of(" \n\t");
- return s.substr(first, last-first+1);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-string gReplace(string in, const string& search, const string& replace)
-{
- size_t pos = 0;
- while ((pos = in.find(search, pos)) != string::npos) {
- in.replace(pos, search.length(), replace);
- pos += replace.length();
- }
- return in;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-string gStripFileUrl(const char *f)
-{
- string out = f;
- out = gReplace(out, "file://", "");
- out = gReplace(out, "%20", " ");
- return out;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-string gGetHomePath()
-{
- char path[PATH_MAX];
-
-#if defined(__linux__)
-
- snprintf(path, PATH_MAX, "%s/.giada", getenv("HOME"));
-
-#elif defined(_WIN32)
-
- snprintf(path, PATH_MAX, ".");
-
-#elif defined(__APPLE__)
-
- struct passwd *p = getpwuid(getuid());
- if (p == NULL) {
- gLog("[gGetHomePath] unable to fetch user infos\n");
- return "";
- }
- else {
- const char *home = p->pw_dir;
- snprintf(path, PATH_MAX, "%s/Library/Application Support/Giada", home);
- }
-
-#endif
-
- return string(path);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gSplit(string in, string sep, vector<string> *v)
-{
- string full = in;
- string token = "";
- size_t curr = 0;
- size_t next = -1;
- do {
- curr = next + 1;
- next = full.find_first_of(sep, curr);
- token = full.substr(curr, next - curr);
- if (token != "")
- v->push_back(token);
- }
- while (next != string::npos);
-}
+++ /dev/null
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * utils
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2016 Giovanni A. Zuliani | Monocasual
- *
- * This file is part of Giada - Your Hardcore Loopmachine.
- *
- * Giada - Your Hardcore Loopmachine is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General
- * Public License as published by the Free Software Foundation, either
- * version 3 of the License, or (at your option) any later version.
- *
- * Giada - Your Hardcore Loopmachine is distributed in the hope that it
- * will be useful, but WITHOUT ANY WARRANTY; without even the implied
- * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Giada - Your Hardcore Loopmachine. If not, see
- * <http://www.gnu.org/licenses/>.
- *
- * -------------------------------------------------------------------------- */
-
-
-#ifndef UTILS_H
-#define UTILS_H
-
-
-#include <string>
-#include <cstdio>
-#include <vector>
-#include "log.h"
-
-
-using std::string;
-using std::vector;
-
-
-bool gFileExists(const char *path);
-bool gFileExists(const string &path);
-
-bool gDirExists(const char *path);
-bool gDirExists(const string &path);
-
-bool gIsDir(const char *path);
-bool gIsDir(const string &path);
-
-bool gIsProject(const string &path);
-
-bool gIsPatch(const char *path);
-bool gIsPatch(const string &path);
-
-bool gMkdir(const char *path);
-bool gMkdir(const string &path);
-
-string gBasename(const string &s);
-
-string gReplace(string in, const string& search, const string& replace);
-
-string gDirname(const string &s);
-
-string gTrim(const char *path);
-string gTrim(const string &s);
-
-string gGetCurrentPath();
-
-string gGetHomePath();
-
-string gStripFileUrl(const char *path);
-
-string gGetExt(const char *path);
-
-string gStripExt(const char *path);
-string gStripExt(const string &s);
-
-string gGetProjectName(const char *path); // TODO - useless!
-
-string gItoa(int i);
-
-void gSplit(string in, string sep, vector<string> *v);
-
-#endif
channel1.midiInKeyPress = UINT32_MAX; // check maximum value
channel1.midiInKeyRel = 1;
channel1.midiInKill = 2;
+ channel1.midiInArm = 11;
channel1.midiInVolume = 3;
channel1.midiInMute = 4;
channel1.midiInSolo = 5;
REQUIRE(channel0.midiInKeyPress == UINT32_MAX);
REQUIRE(channel0.midiInKeyRel == 1);
REQUIRE(channel0.midiInKill == 2);
+ REQUIRE(channel0.midiInArm == 11);
REQUIRE(channel0.midiInVolume == 3);
REQUIRE(channel0.midiInMute == 4);
REQUIRE(channel0.midiInSolo == 5);
#ifdef WITH_VST
#ifdef RUN_TESTS_WITH_LOCAL_FILES
+// temporarily disabled due to entangled deps (WIP)
+#if 0
#include "../src/core/pluginHost.h"
#include "catch.hpp"
}
}
+#endif
+
#endif
#endif
-#include "../src/utils/utils.h"
+#include "../src/utils/fs.h"
+#include "../src/utils/string.h"
#include "catch.hpp"
TEST_CASE("Test filesystem utils")
{
- REQUIRE(gFileExists("giada_tests") == true);
- REQUIRE(gFileExists("ghost_file") == false);
- REQUIRE(gDirExists("src/") == true);
- REQUIRE(gDirExists("ghost_dir/") == false);
- REQUIRE(gIsDir("src/") == true);
- REQUIRE(gIsDir("giada_tests") == false);
- REQUIRE(gBasename("tests/utils.cpp") == "utils.cpp");
- REQUIRE(gDirname("tests/utils.cpp") == "tests");
- REQUIRE(gGetExt("tests/utils.cpp") == "cpp");
- REQUIRE(gStripExt("tests/utils.cpp") == "tests/utils");
+ REQUIRE(gu_fileExists("giada_tests") == true);
+ REQUIRE(gu_fileExists("ghost_file") == false);
+ REQUIRE(gu_dirExists("src/") == true);
+ REQUIRE(gu_dirExists("ghost_dir/") == false);
+ REQUIRE(gu_isDir("src/") == true);
+ REQUIRE(gu_isDir("giada_tests") == false);
+ REQUIRE(gu_basename("tests/utils.cpp") == "utils.cpp");
+ REQUIRE(gu_dirname("tests/utils.cpp") == "tests");
+ REQUIRE(gu_getExt("tests/utils.cpp") == "cpp");
+ REQUIRE(gu_stripExt("tests/utils.cpp") == "tests/utils");
}
TEST_CASE("Test string utils")
{
- REQUIRE(gReplace("Giada is cool", "cool", "hot") == "Giada is hot");
- REQUIRE(gTrim(" Giada is cool ") == "Giada is cool");
- REQUIRE(gItoa(666) == "666");
+ REQUIRE(gu_replace("Giada is cool", "cool", "hot") == "Giada is hot");
+ REQUIRE(gu_trim(" Giada is cool ") == "Giada is cool");
+ REQUIRE(gu_itoa(666) == "666");
vector<std::string> v;
- gSplit("Giada is cool", " ", &v);
+ gu_split("Giada is cool", " ", &v);
REQUIRE(v.size() == 3);
REQUIRE(v.at(0) == "Giada");
REQUIRE(v.at(1) == "is");