From: Jaromír Mikeš Date: Mon, 2 Oct 2017 09:58:07 +0000 (+0200) Subject: New upstream version 0.14.3~dfsg1 X-Git-Tag: archive/raspbian/0.15.4+ds1-1+rpi1^2~12^2~8 X-Git-Url: https://dgit.raspbian.org/?a=commitdiff_plain;h=62252dc7c29a9d0927cf539adc51f10f7654c914;p=giada.git New upstream version 0.14.3~dfsg1 --- diff --git a/ChangeLog b/ChangeLog index f9e8033..01102c8 100644 --- a/ChangeLog +++ b/ChangeLog @@ -12,6 +12,24 @@ -------------------------------------------------------------------------------- +0.14.3 --- 2017 . 09 . 18 +- [Sample Editor] New "reverse selection" function +- [Sample Editor] New "normalize hard" function +- [Sample Editor] New "copy to channel" function +- [Sample Editor] Double click on waveform selects all +- [Sample Editor] Fix garbled characters in window's title +- [Sample Editor] Fix wrong result on "set pitch to song/bar" +- Resizable channels +- Remove calls to malloc/free in Mixer (use new/delete instead) +- Improved UI management of VST plugins +- Fix infinite loop for one shot retrig samples with quantizer > 0 +- Fix wrong geChannel count while saving a patch +- Fix missing greyed-out options in Sample Channel's menu when loading a wrong + sample +- Fix crash while audio recording with BPM set below the default 120 +- Print correct octave numbers in Piano Roll + + 0.14.2 --- 2017 . 08 . 14 - [Sample Editor] Audible preview (with optional loop mode) - [Sample Editor] Frame-precise editing diff --git a/Makefile.am b/Makefile.am index 6bdf82b..2e2a260 100644 --- a/Makefile.am +++ b/Makefile.am @@ -227,6 +227,8 @@ src/gui/elems/basics/radio.h \ src/gui/elems/basics/radio.cpp \ src/utils/log.h \ src/utils/log.cpp \ +src/utils/time.h \ +src/utils/time.cpp \ src/utils/math.h \ src/utils/math.cpp \ src/utils/gui.h \ diff --git a/src/core/channel.cpp b/src/core/channel.cpp index 5677f32..d4bded3 100644 --- a/src/core/channel.cpp +++ b/src/core/channel.cpp @@ -51,37 +51,37 @@ using namespace giada::m; Channel::Channel(int type, int status, int bufferSize) : bufferSize (bufferSize), - midiFilter (-1), - pan (0.5f), - previewMode (G_PREVIEW_NONE), - type (type), - status (status), - key (0), - volume (G_DEFAULT_VOL), - volume_i (1.0f), - volume_d (0.0f), - mute_i (false), - mute_s (false), - mute (false), - solo (false), - hasActions (false), + midiFilter (-1), + pan (0.5f), + previewMode (G_PREVIEW_NONE), + type (type), + status (status), + key (0), + volume (G_DEFAULT_VOL), + volume_i (1.0f), + volume_d (0.0f), + mute_i (false), + mute_s (false), + mute (false), + solo (false), + hasActions (false), readActions (false), - armed (false), - recStatus (REC_STOPPED), - vChan (nullptr), - guiChannel (nullptr), - midiIn (true), - midiInKeyPress (0x0), - midiInKeyRel (0x0), - midiInKill (0x0), - midiInArm (0x0), - midiInVolume (0x0), - midiInMute (0x0), - midiInSolo (0x0), - midiOutL (false), - midiOutLplaying(0x0), - midiOutLmute (0x0), - midiOutLsolo (0x0) + armed (false), + recStatus (REC_STOPPED), + vChan (nullptr), + guiChannel (nullptr), + midiIn (true), + midiInKeyPress (0x0), + midiInKeyRel (0x0), + midiInKill (0x0), + midiInArm (0x0), + midiInVolume (0x0), + midiInMute (0x0), + midiInSolo (0x0), + midiOutL (false), + midiOutLplaying(0x0), + midiOutLmute (0x0), + midiOutLsolo (0x0) { } @@ -117,49 +117,49 @@ bool Channel::allocBuffers() void Channel::copy(const Channel *src, pthread_mutex_t *pluginMutex) { - key = src->key; - volume = src->volume; - volume_i = src->volume_i; - volume_d = src->volume_d; - pan = src->pan; - mute_i = src->mute_i; - mute_s = src->mute_s; - mute = src->mute; - solo = src->solo; - hasActions = src->hasActions; - recStatus = src->recStatus; - midiIn = src->midiIn; - midiInKeyPress = src->midiInKeyPress; - midiInKeyRel = src->midiInKeyRel; - midiInKill = src->midiInKill; - midiInArm = src->midiInArm; - midiInVolume = src->midiInVolume; - midiInMute = src->midiInMute; - midiInSolo = src->midiInSolo; - midiOutL = src->midiOutL; - midiOutLplaying = src->midiOutLplaying; - midiOutLmute = src->midiOutLmute; - midiOutLsolo = src->midiOutLsolo; - - /* clone plugins */ + key = src->key; + volume = src->volume; + volume_i = src->volume_i; + volume_d = src->volume_d; + pan = src->pan; + mute_i = src->mute_i; + mute_s = src->mute_s; + mute = src->mute; + solo = src->solo; + hasActions = src->hasActions; + recStatus = src->recStatus; + midiIn = src->midiIn; + midiInKeyPress = src->midiInKeyPress; + midiInKeyRel = src->midiInKeyRel; + midiInKill = src->midiInKill; + midiInArm = src->midiInArm; + midiInVolume = src->midiInVolume; + midiInMute = src->midiInMute; + midiInSolo = src->midiInSolo; + midiOutL = src->midiOutL; + midiOutLplaying = src->midiOutLplaying; + midiOutLmute = src->midiOutLmute; + midiOutLsolo = src->midiOutLsolo; + + /* clone plugins */ #ifdef WITH_VST - for (unsigned i=0; iplugins.size(); i++) - pluginHost::clonePlugin(src->plugins.at(i), pluginHost::CHANNEL, - pluginMutex, this); + for (unsigned i=0; iplugins.size(); i++) + pluginHost::clonePlugin(src->plugins.at(i), pluginHost::CHANNEL, + pluginMutex, this); #endif - /* clone actions */ - - for (unsigned i=0; ichan == src->index) { - recorder::rec(index, a->type, a->frame, a->iValue, a->fValue); - hasActions = true; - } - } - } + /* clone actions */ + + for (unsigned i=0; ichan == src->index) { + recorder::rec(index, a->type, a->frame, a->iValue, a->fValue); + hasActions = true; + } + } + } } @@ -200,8 +200,9 @@ int Channel::writePatch(int i, bool isProject) { patch::channel_t pch; pch.type = type; - pch.key = key; pch.index = index; + pch.size = guiChannel->getSize(); + pch.key = key; pch.column = guiChannel->getColumnIndex(); pch.mute = mute; pch.mute_s = mute_s; @@ -227,9 +228,9 @@ int Channel::writePatch(int i, bool isProject) if (action->chan == index) { patch::action_t pac; pac.type = action->type; - pac.frame = action->frame; - pac.fValue = action->fValue; - pac.iValue = action->iValue; + pac.frame = action->frame; + pac.fValue = action->fValue; + pac.iValue = action->iValue; pch.actions.push_back(pac); } } @@ -242,7 +243,7 @@ int Channel::writePatch(int i, bool isProject) Plugin *pPlugin = pluginHost::getPluginByIndex(i, pluginHost::CHANNEL, this); patch::plugin_t pp; pp.path = pPlugin->getUniqueId(); - pp.bypass = pPlugin->isBypassed(); + pp.bypass = pPlugin->isBypassed(); for (int k=0; kgetNumParameters(); k++) pp.params.push_back(pPlugin->getParameter(k)); for (unsigned k=0; kmidiInParams.size(); k++) @@ -262,7 +263,7 @@ int Channel::writePatch(int i, bool isProject) int Channel::readPatch(const string &path, int i, pthread_mutex_t *pluginMutex, - int samplerate, int rsmpQuality) + int samplerate, int rsmpQuality) { int ret = 1; patch::channel_t *pch = &patch::channels.at(i); @@ -277,10 +278,10 @@ int Channel::readPatch(const string &path, int i, pthread_mutex_t *pluginMutex, midiIn = pch->midiIn; midiInKeyPress = pch->midiInKeyPress; midiInKeyRel = pch->midiInKeyRel; - midiInKill = pch->midiInKill; - midiInVolume = pch->midiInVolume; - midiInMute = pch->midiInMute; - midiInSolo = pch->midiInSolo; + midiInKill = pch->midiInKill; + midiInVolume = pch->midiInVolume; + midiInMute = pch->midiInMute; + midiInSolo = pch->midiInSolo; midiOutL = pch->midiOutL; midiOutLplaying = pch->midiOutLplaying; midiOutLmute = pch->midiOutLmute; @@ -289,7 +290,7 @@ int Channel::readPatch(const string &path, int i, pthread_mutex_t *pluginMutex, for (unsigned k=0; kactions.size(); k++) { patch::action_t *ac = &pch->actions.at(k); recorder::rec(index, ac->type, ac->frame, ac->iValue, ac->fValue); - hasActions = true; + hasActions = true; } #ifdef WITH_VST @@ -297,17 +298,17 @@ int Channel::readPatch(const string &path, int i, pthread_mutex_t *pluginMutex, for (unsigned k=0; kplugins.size(); k++) { patch::plugin_t *ppl = &pch->plugins.at(k); Plugin *plugin = pluginHost::addPlugin(ppl->path, pluginHost::CHANNEL, - pluginMutex, this); - if (plugin == nullptr) { - ret &= 0; - continue; - } + pluginMutex, this); + if (plugin == nullptr) { + ret &= 0; + continue; + } plugin->setBypass(ppl->bypass); for (unsigned j=0; jparams.size(); j++) plugin->setParameter(j, ppl->params.at(j)); - plugin->midiInParams.clear(); - for (unsigned j=0; jmidiInParams.size(); j++) - plugin->midiInParams.push_back(ppl->midiInParams.at(j)); + plugin->midiInParams.clear(); + for (unsigned j=0; jmidiInParams.size(); j++) + plugin->midiInParams.push_back(ppl->midiInParams.at(j)); ret &= 1; } @@ -380,19 +381,19 @@ void Channel::receiveMidi(uint32_t msg) void Channel::setPan(float v) { - if (v > 1.0f) - pan = 1.0f; - else - if (v < 0.0f) - pan = 0.0f; - else - pan = v; + if (v > 1.0f) + pan = 1.0f; + else + if (v < 0.0f) + pan = 0.0f; + else + pan = v; } float Channel::getPan() { - return pan; + return pan; } @@ -401,12 +402,12 @@ float Channel::getPan() float Channel::calcPanning(int ch) { - if (pan == 0.5f) // center: nothing to do - return 1.0; - if (ch == 0) - return 1.0 - pan; - else // channel 1 - return pan; + if (pan == 0.5f) // center: nothing to do + return 1.0; + if (ch == 0) + return 1.0 - pan; + else // channel 1 + return pan; } @@ -432,7 +433,7 @@ bool Channel::isPreview() juce::MidiBuffer &Channel::getPluginMidiEvents() { - return midiBuffer; + return midiBuffer; } @@ -441,7 +442,7 @@ juce::MidiBuffer &Channel::getPluginMidiEvents() void Channel::clearMidiBuffer() { - midiBuffer.clear(); + midiBuffer.clear(); } diff --git a/src/core/const.h b/src/core/const.h index ad5384a..3d3c223 100644 --- a/src/core/const.h +++ b/src/core/const.h @@ -46,10 +46,10 @@ /* -- version --------------------------------------------------------------- */ #define G_APP_NAME "Giada" -#define G_VERSION_STR "0.14.2" +#define G_VERSION_STR "0.14.3" #define G_VERSION_MAJOR 0 #define G_VERSION_MINOR 14 -#define G_VERSION_PATCH 2 +#define G_VERSION_PATCH 3 #define CONF_FILENAME "giada.conf" @@ -72,18 +72,27 @@ #define G_GUI_HEIGHT 510 #define G_GUI_PLUGIN_RATE 0.05 // refresh rate for plugin GUIs #define G_GUI_FONT_SIZE_BASE 12 +#define G_GUI_INNER_MARGIN 4 +#define G_GUI_OUTER_MARGIN 8 +#define G_GUI_UNIT 20 // base unit for elements +#define G_GUI_CHANNEL_H_1 G_GUI_UNIT +#define G_GUI_CHANNEL_H_2 G_GUI_UNIT * 2 +#define G_GUI_CHANNEL_H_3 G_GUI_UNIT * 4 +#define G_GUI_CHANNEL_H_4 G_GUI_UNIT * 6 + #define G_COLOR_RED fl_rgb_color(28, 32, 80) #define G_COLOR_BLUE fl_rgb_color(113, 31, 31) #define G_COLOR_RED_ALERT fl_rgb_color(239, 75, 53) -#define G_COLOR_LIGHT_2 fl_rgb_color(200, 200, 200) -#define G_COLOR_LIGHT_1 fl_rgb_color(170, 170, 170) -#define G_COLOR_GREY_4 fl_rgb_color(78, 78, 78) -#define G_COLOR_GREY_3 fl_rgb_color(54, 54, 54) -#define G_COLOR_GREY_2 fl_rgb_color(37, 37, 37) -#define G_COLOR_GREY_1 fl_rgb_color(25, 25, 25) -#define G_COLOR_BLACK fl_rgb_color(0, 0, 0) +#define G_COLOR_LIGHT_2 fl_rgb_color(200, 200, 200) +#define G_COLOR_LIGHT_1 fl_rgb_color(170, 170, 170) +#define G_COLOR_GREY_4 fl_rgb_color(78, 78, 78) +#define G_COLOR_GREY_3 fl_rgb_color(54, 54, 54) +#define G_COLOR_GREY_2 fl_rgb_color(37, 37, 37) +#define G_COLOR_GREY_1_5 fl_rgb_color(28, 28, 28) +#define G_COLOR_GREY_1 fl_rgb_color(25, 25, 25) +#define G_COLOR_BLACK fl_rgb_color(0, 0, 0) @@ -93,7 +102,6 @@ #define G_MAX_BEATS 32 #define G_MAX_BARS 32 #define G_MAX_QUANTIZE 8 -#define G_MAX_PATCHNAME_LEN 32 #define G_MIN_DB_SCALE 60.0f #define G_MIN_COLUMN_WIDTH 140 #define G_MAX_BOOST_DB 20.0f @@ -138,8 +146,8 @@ #define G_DEFAULT_MIDI_PORT_IN -1 #define G_DEFAULT_MIDI_PORT_OUT -1 #define G_DEFAULT_SAMPLERATE 44100 -#define G_DEFAULT_BUFSIZE 1024 -#define G_DEFAULT_DELAYCOMP 0 +#define G_DEFAULT_BUFSIZE 1024 +#define G_DEFAULT_DELAYCOMP 0 #define G_DEFAULT_BIT_DEPTH 32 // float #define G_DEFAULT_AUDIO_CHANS 2 // stereo for internal processing #define G_DEFAULT_VOL 1.0f @@ -350,6 +358,7 @@ const int MIDI_CHANS[16] = { #define PATCH_KEY_CHANNELS "channels" #define PATCH_KEY_CHANNEL_TYPE "type" #define PATCH_KEY_CHANNEL_INDEX "index" +#define PATCH_KEY_CHANNEL_SIZE "size" #define PATCH_KEY_CHANNEL_COLUMN "column" #define PATCH_KEY_CHANNEL_MUTE "mute" #define PATCH_KEY_CHANNEL_MUTE_S "mute_s" diff --git a/src/core/mixer.cpp b/src/core/mixer.cpp index 09927dc..e86595d 100644 --- a/src/core/mixer.cpp +++ b/src/core/mixer.cpp @@ -53,24 +53,24 @@ namespace float tock[TICKSIZE] = { - 0.059033, 0.117240, 0.173807, 0.227943, 0.278890, 0.325936, - 0.368423, 0.405755, 0.437413, 0.462951, 0.482013, 0.494333, - 0.499738, 0.498153, 0.489598, 0.474195, 0.452159, 0.423798, - 0.389509, 0.349771, 0.289883, 0.230617, 0.173194, 0.118739, - 0.068260, 0.022631, -0.017423, -0.051339, -0.078721, -0.099345, + 0.059033, 0.117240, 0.173807, 0.227943, 0.278890, 0.325936, + 0.368423, 0.405755, 0.437413, 0.462951, 0.482013, 0.494333, + 0.499738, 0.498153, 0.489598, 0.474195, 0.452159, 0.423798, + 0.389509, 0.349771, 0.289883, 0.230617, 0.173194, 0.118739, + 0.068260, 0.022631, -0.017423, -0.051339, -0.078721, -0.099345, -0.113163, -0.120295, -0.121028, -0.115804, -0.105209, -0.089954, -0.070862, -0.048844 }; float tick[TICKSIZE] = { - 0.175860, 0.341914, 0.488904, 0.608633, 0.694426, 0.741500, - 0.747229, 0.711293, 0.635697, 0.524656, 0.384362, 0.222636, - 0.048496, -0.128348, -0.298035, -0.451105, -0.579021, -0.674653, + 0.175860, 0.341914, 0.488904, 0.608633, 0.694426, 0.741500, + 0.747229, 0.711293, 0.635697, 0.524656, 0.384362, 0.222636, + 0.048496, -0.128348, -0.298035, -0.451105, -0.579021, -0.674653, -0.732667, -0.749830, -0.688924, -0.594091, -0.474481, -0.340160, -0.201360, -0.067752, 0.052194, 0.151746, 0.226280, 0.273493, - 0.293425, 0.288307, 0.262252, 0.220811, 0.170435, 0.117887, - 0.069639, 0.031320 + 0.293425, 0.288307, 0.262252, 0.220811, 0.170435, 0.117887, + 0.069639, 0.031320 }; @@ -83,7 +83,7 @@ Records from line in. */ void lineInRec(float* inBuf, unsigned frame) { if (!mh::hasArmedSampleChannels() || !kernelAudio::isInputEnabled() || !recording) - return; + return; /* Delay comp: wait until waitRec reaches delayComp. WaitRec * returns to 0 in mixerHandler, as soon as the recording ends */ @@ -157,7 +157,7 @@ void readActions(unsigned frame) int index = recorder::global.at(i).at(j)->chan; Channel *ch = mh::getChannelByIndex(index); ch->parseAction(recorder::global.at(i).at(j), frame, - clock::getCurrentFrame(), clock::getQuantize(), clock::isRunning()); + clock::getCurrentFrame(), clock::getQuantize(), clock::isRunning()); } break; } @@ -173,10 +173,10 @@ Computes quantization on 'rewind' button and all channels. */ void doQuantize(unsigned frame) { - /* Nothing to do if quantizer disabled or a quanto has not passed yet. */ + /* Nothing to do if quantizer disabled or a quanto has not passed yet. */ - if (clock::getQuantize() == 0 || !clock::quantoHasPassed()) - return; + if (clock::getQuantize() == 0 || !clock::quantoHasPassed()) + return; if (rewindWait) { rewindWait = false; rewind(); @@ -349,9 +349,9 @@ void testFirstBeat(unsigned frame) void testLastBeat() { - if (clock::isOnBeat()) - if (metronome && !tickPlay) - tockPlay = true; + if (clock::isOnBeat()) + if (metronome && !tickPlay) + tockPlay = true; } }; // {anonymous} @@ -404,11 +404,15 @@ void init(int framesInSeq, int audioBufferSize) /* Allocate virtual input channels. vChanInput has variable size: it depends on how many frames there are in sequencer. */ - allocVirtualInput(framesInSeq); + allocVirtualInput(framesInSeq); - if (vChanInToOut != nullptr) - free(vChanInToOut); - vChanInToOut = (float*) malloc(audioBufferSize * 2 * sizeof(float)); + if (vChanInToOut != nullptr) + delete[] vChanInToOut; + vChanInToOut = new (std::nothrow) float[audioBufferSize * 2]; + if (!vChanInToOut) { + gu_log("[Mixer::init] vChanInToOut alloc error!\n"); + return; + } pthread_mutex_init(&mutex_recs, nullptr); pthread_mutex_init(&mutex_chans, nullptr); @@ -424,10 +428,11 @@ void init(int framesInSeq, int audioBufferSize) void allocVirtualInput(int frames) { if (vChanInput != nullptr) - free(vChanInput); - vChanInput = (float*) malloc(frames * sizeof(float)); + delete[] vChanInput; + vChanInput = new (std::nothrow) float[frames]; if (!vChanInput) - gu_log("[Mixer] vChanInput realloc error!\n"); + gu_log("[Mixer::allocVirtualInput] vChanInput realloc error!\n"); + gu_log("[Mixer::allocVirtualInput] vChanInput ready, %d frames\n", frames); } @@ -441,7 +446,7 @@ int masterPlay(void* _outBuf, void* _inBuf, unsigned bufferSize, return 0; #ifdef __linux__ - clock::recvJackSync(); + clock::recvJackSync(); #endif float* outBuf = (float*) _outBuf; @@ -521,7 +526,7 @@ bool isSilent() void rewind() { - clock::rewind(); + clock::rewind(); if (clock::isRunning()) for (unsigned i=0; irewind(); diff --git a/src/core/mixerHandler.cpp b/src/core/mixerHandler.cpp index 4de62d0..623ed27 100644 --- a/src/core/mixerHandler.cpp +++ b/src/core/mixerHandler.cpp @@ -69,7 +69,7 @@ int readPatchPlugins(vector *list, int type) int ret = 1; for (unsigned i=0; isize(); i++) { patch::plugin_t *ppl = &list->at(i); - // TODO use glue_addPlugin() + // TODO use glue_addPlugin() Plugin *plugin = pluginHost::addPlugin(ppl->path.c_str(), type, &mixer::mutex_plugins, nullptr); if (plugin != nullptr) { @@ -135,7 +135,7 @@ bool uniqueSampleName(SampleChannel* ch, const string& name) Channel* addChannel(int type) { - Channel* ch; + Channel* ch; int bufferSize = kernelAudio::getRealBufSize() * 2; if (type == CHANNEL_SAMPLE) @@ -150,7 +150,7 @@ Channel* addChannel(int type) while (true) { if (pthread_mutex_trylock(&mixer::mutex_chans) != 0) - continue; + continue; mixer::channels.push_back(ch); pthread_mutex_unlock(&mixer::mutex_chans); break; @@ -158,7 +158,7 @@ Channel* addChannel(int type) ch->index = getNewChanIndex(); gu_log("[addChannel] channel index=%d added, type=%d, total=%d\n", - ch->index, ch->type, mixer::channels.size()); + ch->index, ch->type, mixer::channels.size()); return ch; } @@ -182,7 +182,7 @@ int deleteChannel(Channel* ch) while (true) { if (pthread_mutex_trylock(&mixer::mutex_chans) != 0) - continue; + continue; mixer::channels.erase(mixer::channels.begin() + index); delete ch; pthread_mutex_unlock(&mixer::mutex_chans); @@ -194,7 +194,7 @@ int deleteChannel(Channel* ch) /* -------------------------------------------------------------------------- */ -Channel *getChannelByIndex(int index) +Channel* getChannelByIndex(int index) { for (unsigned i=0; iindex == index) @@ -210,12 +210,12 @@ Channel *getChannelByIndex(int index) bool hasLogicalSamples() { for (unsigned i=0; itype != CHANNEL_SAMPLE) - continue; - SampleChannel *ch = static_cast(mixer::channels.at(i)); - if (ch->wave && ch->wave->isLogical()) - return true; - } + if (mixer::channels.at(i)->type != CHANNEL_SAMPLE) + continue; + SampleChannel *ch = static_cast(mixer::channels.at(i)); + if (ch->wave && ch->wave->isLogical()) + return true; + } return false; } @@ -226,13 +226,13 @@ bool hasLogicalSamples() bool hasEditedSamples() { for (unsigned i=0; itype != CHANNEL_SAMPLE) - continue; - SampleChannel *ch = static_cast(mixer::channels.at(i)); - if (ch->wave && ch->wave->isEdited()) - return true; - } + continue; + SampleChannel *ch = static_cast(mixer::channels.at(i)); + if (ch->wave && ch->wave->isEdited()) + return true; + } return false; } @@ -242,7 +242,7 @@ bool hasEditedSamples() void stopSequencer() { - clock::stop(); + clock::stop(); for (unsigned i=0; istopBySeq(conf::chansStopOnSeqHalt); } @@ -251,7 +251,7 @@ void stopSequencer() /* -------------------------------------------------------------------------- */ -bool uniqueSolo(Channel *ch) +bool uniqueSolo(Channel* ch) { int solos = 0; for (unsigned i=0; itype == CHANNEL_SAMPLE && ch->armed) - return true; - } - return false; + for (unsigned i=0; itype == CHANNEL_SAMPLE && ch->armed) + return true; + } + return false; } diff --git a/src/core/patch.cpp b/src/core/patch.cpp index e0daf2e..495134d 100644 --- a/src/core/patch.cpp +++ b/src/core/patch.cpp @@ -58,17 +58,18 @@ void sanitize() samplerate = samplerate <= 0 ? G_DEFAULT_SAMPLERATE : samplerate; for (unsigned i=0; iindex = col->index < 0 ? 0 : col->index; col->width = col->width < G_MIN_COLUMN_WIDTH ? G_MIN_COLUMN_WIDTH : col->width; } for (unsigned i=0; isize = ch->size < G_GUI_CHANNEL_H_1 || ch->size > G_GUI_CHANNEL_H_4 ? G_GUI_CHANNEL_H_1 : ch->size; ch->volume = ch->volume < 0.0f || ch->volume > 1.0f ? G_DEFAULT_VOL : ch->volume; ch->pan = ch->pan < 0.0f || ch->pan > 1.0f ? 1.0f : ch->pan; ch->boost = ch->boost < 1.0f ? G_DEFAULT_BOOST : ch->boost; - ch->pitch = ch->pitch < 0.1f || ch->pitch > 4.0f ? G_DEFAULT_PITCH : ch->pitch; + ch->pitch = ch->pitch < 0.1f || ch->pitch > G_MAX_PITCH ? G_DEFAULT_PITCH : ch->pitch; } } @@ -78,7 +79,7 @@ void sanitize() /* setInvalid Helper function used to return invalid status while reading. */ -int setInvalid(json_t *jRoot) +int setInvalid(json_t* jRoot) { json_decref(jRoot); return PATCH_INVALID; @@ -88,7 +89,7 @@ int setInvalid(json_t *jRoot) /* -------------------------------------------------------------------------- */ -bool readCommons(json_t *jContainer) +bool readCommons(json_t* jContainer) { if (!storager::setString(jContainer, PATCH_KEY_HEADER, header)) return 0; if (!storager::setString(jContainer, PATCH_KEY_VERSION, version)) return 0; @@ -114,14 +115,14 @@ bool readCommons(json_t *jContainer) #ifdef WITH_VST -bool readPlugins(json_t *jContainer, vector *container, const char *key) +bool readPlugins(json_t* jContainer, vector* container, const char* key) { - json_t *jPlugins = json_object_get(jContainer, key); + json_t* jPlugins = json_object_get(jContainer, key); if (!storager::checkArray(jPlugins, key)) return 0; size_t pluginIndex; - json_t *jPlugin; + json_t* jPlugin; json_array_foreach(jPlugins, pluginIndex, jPlugin) { if (!storager::checkObject(jPlugin, "")) // TODO pass pluginIndex as string @@ -133,21 +134,21 @@ bool readPlugins(json_t *jContainer, vector *container, const char *ke /* read plugin params */ - json_t *jParams = json_object_get(jPlugin, PATCH_KEY_PLUGIN_PARAMS); + json_t* jParams = json_object_get(jPlugin, PATCH_KEY_PLUGIN_PARAMS); if (!storager::checkArray(jParams, PATCH_KEY_PLUGIN_PARAMS)) return 0; size_t paramIndex; - json_t *jParam; + json_t* jParam; json_array_foreach(jParams, paramIndex, jParam) plugin.params.push_back(json_real_value(jParam)); /* read midiIn params (midi learning on plugins' parameters) */ - json_t *jMidiInParams = json_object_get(jPlugin, PATCH_KEY_PLUGIN_MIDI_IN_PARAMS); + json_t* jMidiInParams = json_object_get(jPlugin, PATCH_KEY_PLUGIN_MIDI_IN_PARAMS); if (!storager::checkArray(jMidiInParams, PATCH_KEY_PLUGIN_MIDI_IN_PARAMS)) return 0; size_t midiInParamIndex; - json_t *jMidiInParam; + json_t* jMidiInParam; json_array_foreach(jMidiInParams, midiInParamIndex, jMidiInParam) plugin.midiInParams.push_back(json_integer_value(jMidiInParam)); @@ -161,14 +162,14 @@ bool readPlugins(json_t *jContainer, vector *container, const char *ke /* -------------------------------------------------------------------------- */ -bool readActions(json_t *jContainer, channel_t *channel) +bool readActions(json_t* jContainer, channel_t* channel) { - json_t *jActions = json_object_get(jContainer, PATCH_KEY_CHANNEL_ACTIONS); + json_t* jActions = json_object_get(jContainer, PATCH_KEY_CHANNEL_ACTIONS); if (!storager::checkArray(jActions, PATCH_KEY_CHANNEL_ACTIONS)) return 0; size_t actionIndex; - json_t *jAction; + json_t* jAction; json_array_foreach(jActions, actionIndex, jAction) { if (!storager::checkObject(jAction, "")) // TODO pass actionIndex as string @@ -188,14 +189,14 @@ bool readActions(json_t *jContainer, channel_t *channel) /* -------------------------------------------------------------------------- */ -bool readChannels(json_t *jContainer) +bool readChannels(json_t* jContainer) { - json_t *jChannels = json_object_get(jContainer, PATCH_KEY_CHANNELS); + json_t* jChannels = json_object_get(jContainer, PATCH_KEY_CHANNELS); if (!storager::checkArray(jChannels, PATCH_KEY_CHANNELS)) return 0; size_t channelIndex; - json_t *jChannel; + json_t* jChannel; json_array_foreach(jChannels, channelIndex, jChannel) { string channelIndexStr = "channel " + gu_toString(channelIndex); @@ -206,6 +207,7 @@ bool readChannels(json_t *jContainer) if (!storager::setInt (jChannel, PATCH_KEY_CHANNEL_TYPE, channel.type)) return 0; if (!storager::setInt (jChannel, PATCH_KEY_CHANNEL_INDEX, channel.index)) return 0; + if (!storager::setInt (jChannel, PATCH_KEY_CHANNEL_SIZE, channel.size)) return 0; if (!storager::setInt (jChannel, PATCH_KEY_CHANNEL_COLUMN, channel.column)) return 0; if (!storager::setInt (jChannel, PATCH_KEY_CHANNEL_MUTE, channel.mute)) return 0; if (!storager::setInt (jChannel, PATCH_KEY_CHANNEL_MUTE_S, channel.mute_s)) return 0; @@ -252,14 +254,14 @@ bool readChannels(json_t *jContainer) /* -------------------------------------------------------------------------- */ -bool readColumns(json_t *jContainer) +bool readColumns(json_t* jContainer) { - json_t *jColumns = json_object_get(jContainer, PATCH_KEY_COLUMNS); + json_t* jColumns = json_object_get(jContainer, PATCH_KEY_COLUMNS); if (!storager::checkArray(jColumns, PATCH_KEY_COLUMNS)) return 0; size_t columnIndex; - json_t *jColumn; + json_t* jColumn; json_array_foreach(jColumns, columnIndex, jColumn) { string columnIndexStr = "column " + gu_toString(columnIndex); @@ -280,26 +282,26 @@ bool readColumns(json_t *jContainer) #ifdef WITH_VST -void writePlugins(json_t *jContainer, vector *plugins, const char *key) +void writePlugins(json_t* jContainer, vector* plugins, const char* key) { - json_t *jPlugins = json_array(); + json_t* jPlugins = json_array(); for (unsigned j=0; jsize(); j++) { - json_t *jPlugin = json_object(); - plugin_t plugin = plugins->at(j); + json_t* jPlugin = json_object(); + plugin_t plugin = plugins->at(j); json_object_set_new(jPlugin, PATCH_KEY_PLUGIN_PATH, json_string(plugin.path.c_str())); json_object_set_new(jPlugin, PATCH_KEY_PLUGIN_BYPASS, json_boolean(plugin.bypass)); json_array_append_new(jPlugins, jPlugin); /* plugin params */ - json_t *jPluginParams = json_array(); + json_t* jPluginParams = json_array(); for (unsigned z=0; z *plugins, const char *key /* -------------------------------------------------------------------------- */ -void writeColumns(json_t *jContainer, vector *columns) +void writeColumns(json_t* jContainer, vector* columns) { - json_t *jColumns = json_array(); + json_t* jColumns = json_array(); for (unsigned i=0; isize(); i++) { - json_t *jColumn = json_object(); - column_t column = columns->at(i); + json_t* jColumn = json_object(); + column_t column = columns->at(i); json_object_set_new(jColumn, PATCH_KEY_COLUMN_INDEX, json_integer(column.index)); json_object_set_new(jColumn, PATCH_KEY_COLUMN_WIDTH, json_integer(column.width)); json_array_append_new(jColumns, jColumn); @@ -330,12 +332,12 @@ void writeColumns(json_t *jContainer, vector *columns) /* -------------------------------------------------------------------------- */ -void writeActions(json_t *jContainer, vector *actions) +void writeActions(json_t*jContainer, vector*actions) { - json_t *jActions = json_array(); + json_t* jActions = json_array(); for (unsigned k=0; ksize(); k++) { - json_t *jAction = json_object(); - action_t action = actions->at(k); + json_t* jAction = json_object(); + action_t action = actions->at(k); json_object_set_new(jAction, PATCH_KEY_ACTION_TYPE, json_integer(action.type)); json_object_set_new(jAction, PATCH_KEY_ACTION_FRAME, json_integer(action.frame)); json_object_set_new(jAction, PATCH_KEY_ACTION_F_VALUE, json_real(action.fValue)); @@ -349,7 +351,7 @@ void writeActions(json_t *jContainer, vector *actions) /* -------------------------------------------------------------------------- */ -void writeCommons(json_t *jContainer) +void writeCommons(json_t* jContainer) { json_object_set_new(jContainer, PATCH_KEY_HEADER, json_string(header.c_str())); json_object_set_new(jContainer, PATCH_KEY_VERSION, json_string(version.c_str())); @@ -372,14 +374,15 @@ void writeCommons(json_t *jContainer) /* -------------------------------------------------------------------------- */ -void writeChannels(json_t *jContainer, vector *channels) +void writeChannels(json_t* jContainer, vector* channels) { - json_t *jChannels = json_array(); + json_t* jChannels = json_array(); for (unsigned i=0; isize(); i++) { - json_t *jChannel = json_object(); - channel_t channel = channels->at(i); + json_t* jChannel = json_object(); + channel_t channel = channels->at(i); json_object_set_new(jChannel, PATCH_KEY_CHANNEL_TYPE, json_integer(channel.type)); json_object_set_new(jChannel, PATCH_KEY_CHANNEL_INDEX, json_integer(channel.index)); + json_object_set_new(jChannel, PATCH_KEY_CHANNEL_SIZE, json_integer(channel.size)); json_object_set_new(jChannel, PATCH_KEY_CHANNEL_COLUMN, json_integer(channel.column)); json_object_set_new(jChannel, PATCH_KEY_CHANNEL_MUTE, json_integer(channel.mute)); json_object_set_new(jChannel, PATCH_KEY_CHANNEL_MUTE_S, json_integer(channel.mute_s)); @@ -477,9 +480,9 @@ void init() /* -------------------------------------------------------------------------- */ -int write(const string &file) +int write(const string& file) { - json_t *jRoot = json_object(); + json_t* jRoot = json_object(); writeCommons(jRoot); writeColumns(jRoot, &columns); @@ -490,7 +493,7 @@ int write(const string &file) #endif if (json_dump_file(jRoot, file.c_str(), JSON_COMPACT) != 0) { - gu_log("[write] unable to write patch file!\n"); + gu_log("[patch::write] unable to write patch file!\n"); return 0; } return 1; @@ -500,12 +503,13 @@ int write(const string &file) /* -------------------------------------------------------------------------- */ -int read(const string &file) +int read(const string& file) { json_error_t jError; - json_t *jRoot = json_load_file(file.c_str(), 0, &jError); + json_t* jRoot = json_load_file(file.c_str(), 0, &jError); if (!jRoot) { - gu_log("[read] unable to read patch file! Error on line %d: %s\n", jError.line, jError.text); + gu_log("[patch::read] unable to read patch file! Error on line %d: %s\n", + jError.line, jError.text); return PATCH_UNREADABLE; } diff --git a/src/core/patch.h b/src/core/patch.h index 2dd689d..7018dba 100644 --- a/src/core/patch.h +++ b/src/core/patch.h @@ -44,72 +44,73 @@ namespace patch { struct action_t { - int type; - int frame; - float fValue; - uint32_t iValue; + int type; + int frame; + float fValue; + uint32_t iValue; }; #ifdef WITH_VST struct plugin_t { - std::string path; - bool bypass; - std::vector params; - std::vector midiInParams; + std::string path; + bool bypass; + std::vector params; + std::vector midiInParams; }; #endif struct channel_t { - int type; - int index; - int column; - int mute; - int mute_s; - int solo; - float volume; - float pan; - bool midiIn; - uint32_t midiInKeyPress; - uint32_t midiInKeyRel; - uint32_t midiInKill; - uint32_t midiInArm; - uint32_t midiInVolume; - uint32_t midiInMute; - uint32_t midiInSolo; - bool midiOutL; - uint32_t midiOutLplaying; - uint32_t midiOutLmute; - uint32_t midiOutLsolo; - // sample channel - std::string samplePath; - int key; - int mode; - int begin; - int end; - float boost; - int recActive; - float pitch; - bool inputMonitor; - uint32_t midiInReadActions; - uint32_t midiInPitch; - // midi channel - uint32_t midiOut; - uint32_t midiOutChan; - - std::vector actions; + int type; + int index; + int size; + int column; + int mute; + int mute_s; + int solo; + float volume; + float pan; + bool midiIn; + uint32_t midiInKeyPress; + uint32_t midiInKeyRel; + uint32_t midiInKill; + uint32_t midiInArm; + uint32_t midiInVolume; + uint32_t midiInMute; + uint32_t midiInSolo; + bool midiOutL; + uint32_t midiOutLplaying; + uint32_t midiOutLmute; + uint32_t midiOutLsolo; + // sample channel + std::string samplePath; + int key; + int mode; + int begin; + int end; + float boost; + int recActive; + float pitch; + bool inputMonitor; + uint32_t midiInReadActions; + uint32_t midiInPitch; + // midi channel + uint32_t midiOut; + uint32_t midiOutChan; + + std::vector actions; #ifdef WITH_VST - std::vector plugins; + std::vector plugins; #endif }; struct column_t { - int index; - int width; - std::vector channels; + int index; + int width; + std::vector channels; }; extern std::string header; @@ -144,8 +145,8 @@ void init(); /* read/write * Read/write patch to/from file. */ -int write(const std::string &file); -int read (const std::string &file); +int write(const std::string& file); +int read (const std::string& file); }}}; // giada::m::patch:: #endif diff --git a/src/core/plugin.cpp b/src/core/plugin.cpp index 0402818..83361f9 100644 --- a/src/core/plugin.cpp +++ b/src/core/plugin.cpp @@ -28,11 +28,15 @@ #ifdef WITH_VST +#include #include "../utils/log.h" +#include "../utils/time.h" +#include "const.h" #include "plugin.h" using std::string; +using namespace giada::u; int Plugin::idGenerator = 1; @@ -48,21 +52,15 @@ Plugin::Plugin(juce::AudioPluginInstance *plugin, double samplerate, id (idGenerator++), bypass(false) { - /* Fill midiInParams. All values are empty (0x0): they will be filled during + /* Init midiInParams. All values are empty (0x0): they will be filled during midi learning process. */ for (int i=0; igetNumParameters(); i++) midiInParams.push_back(0x0); - - /* Try to enable editor (i.e. plugin's UI) */ - ui = plugin->createEditorIfNeeded(); - if (ui == nullptr) - gu_log("[Plugin] unable to create editor, the plugin might be GUI-less!\n"); - plugin->prepareToPlay(samplerate, buffersize); - gu_log("[Plugin] editor initialized and ready\n"); + gu_log("[Plugin] plugin initialized and ready\n"); } @@ -71,6 +69,7 @@ Plugin::Plugin(juce::AudioPluginInstance *plugin, double samplerate, Plugin::~Plugin() { + closeEditor(); plugin->suspendProcessing(true); plugin->releaseResources(); } @@ -79,12 +78,21 @@ Plugin::~Plugin() /* -------------------------------------------------------------------------- */ -void Plugin::showEditor(void *parent) +void Plugin::showEditor(void* parent) { + ui = plugin->createEditorIfNeeded(); if (ui == nullptr) { - gu_log("[Plugin::showEditor] can't show editor!\n"); + gu_log("[Plugin::showEditor] unable to create editor!\n"); return; } + + /* A silly workaround on X: it seems that calling addToDesktop too fast, i.e. + before the X Window is fully ready screws up the plugin's event dispatcher. */ + +#ifdef G_OS_LINUX + time::sleep(500); +#endif + ui->setOpaque(true); ui->addToDesktop(0, parent); } @@ -95,7 +103,7 @@ void Plugin::showEditor(void *parent) bool Plugin::isEditorOpen() { - return ui->isVisible() && ui->isOnDesktop(); + return ui != nullptr && ui->isVisible() && ui->isOnDesktop(); } @@ -250,8 +258,8 @@ void Plugin::closeEditor() { if (ui == nullptr) return; - if (ui->isOnDesktop()) - ui->removeFromDesktop(); + delete ui; + ui = nullptr; } #endif diff --git a/src/core/sampleChannel.cpp b/src/core/sampleChannel.cpp index 3121015..6ffdd80 100644 --- a/src/core/sampleChannel.cpp +++ b/src/core/sampleChannel.cpp @@ -305,7 +305,7 @@ void SampleChannel::setEnd(int f) if (f >= wave->getSize()) end = (wave->getSize() - 1) * wave->getChannels(); else - if (f <= begin) + if (f * wave->getChannels() <= begin) end = begin + wave->getChannels(); else end = f * wave->getChannels(); @@ -823,6 +823,7 @@ void SampleChannel::reset(int frame) //fadeoutTracker = tracker; // store old frame number for xfade tracker = begin; mute_i = false; + qWait = false; // Was in qWait mode? Reset occured, no more qWait now. if (frame > 0 && status & (STATUS_PLAY | STATUS_ENDING)) tracker = fillChan(vChan, tracker, frame); } diff --git a/src/core/waveFx.cpp b/src/core/waveFx.cpp index 2f0e8ea..a215a7c 100644 --- a/src/core/waveFx.cpp +++ b/src/core/waveFx.cpp @@ -26,6 +26,7 @@ #include +#include #include "../utils/log.h" #include "wave.h" #include "waveFx.h" @@ -43,25 +44,35 @@ void fadeFrame(Wave* w, int i, float val) for (int j=0; jgetChannels(); j++) frame[j] *= val; } -}; // {anonymous} -/* -------------------------------------------------------------------------- */ -/* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ -float normalizeSoft(Wave *w) +float getPeak(Wave* w, int a, int b) { float peak = 0.0f; float abs = 0.0f; - for (int i=0; igetSize(); i++) { + for (int i=a; igetFrame(i); for (int j=0; jgetChannels(); j++) // Find highest value in any channel abs = fabs(frame[j]); if (abs > peak) peak = abs; } + return peak; +} +}; // {anonymous} + + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + + +float normalizeSoft(Wave* w) +{ + float peak = getPeak(w, 0, w->getSize()); /* peak == 0.0f: don't normalize the silence * peak > 1.0f: don't reduce the amplitude, just leave it alone */ @@ -76,6 +87,24 @@ float normalizeSoft(Wave *w) /* -------------------------------------------------------------------------- */ +void normalizeHard(Wave* w, int a, int b) +{ + float peak = getPeak(w, a, b); + if (peak == 0.0f || peak > 1.0f) // as in ::normalizeSoft + return; + + for (int i=a; igetFrame(i); + for (int j=0; jgetChannels(); j++) + frame[j] = frame[j] * (1.0f / peak); + } + w->setEdited(true); +} + + +/* -------------------------------------------------------------------------- */ + + int monoToStereo(Wave* w) { if (w->getChannels() >= G_DEFAULT_AUDIO_CHANS) @@ -117,8 +146,6 @@ void silence(Wave* w, int a, int b) } w->setEdited(true); - - return; } @@ -170,7 +197,7 @@ int trim(Wave* w, int a, int b) int newSize = (b - a) * w->getChannels(); float* newData = new (std::nothrow) float[newSize]; if (newData == nullptr) { - gu_log("[wfx] unable to allocate memory for trimming\n"); + gu_log("[wfx::trim] unable to allocate memory!\n"); return G_RES_ERR_MEMORY; } @@ -193,6 +220,47 @@ int trim(Wave* w, int a, int b) /* -------------------------------------------------------------------------- */ +int paste(Wave* src, Wave* des, int aFrame) +{ + int srcNumSamples = src->getSize() * src->getChannels(); + int desNumSamples = des->getSize() * des->getChannels(); + int aSample = aFrame * des->getChannels(); + + int newSize = srcNumSamples + desNumSamples; + float* newData = new (std::nothrow) float[newSize]; + if (newData == nullptr) { + gu_log("[wfx::paste] unable to allocate memory!\n"); + return G_RES_ERR_MEMORY; + } + + /* |---original data---|///paste data///|---original data---| + chunk 1 chunk 2 chunk 3 + */ + + float* chunk1a = des->getData(); + float* chunk1b = des->getData() + aSample; + + float* chunk2a = src->getData(); + float* chunk2b = src->getData() + srcNumSamples; + + float* chunk3a = chunk1b; + float* chunk3b = des->getData() + desNumSamples; + + std::copy(chunk1a, chunk1b, newData); + std::copy(chunk2a, chunk2b, newData + aSample); + std::copy(chunk3a, chunk3b, newData + aSample + srcNumSamples); + + des->free(); + des->setData(newData, newSize); + des->setEdited(true); + + return G_RES_OK; +} + + +/* -------------------------------------------------------------------------- */ + + void fade(Wave* w, int a, int b, int type) { gu_log("[wfx::fade] fade from %d to %d (range = %d)\n", a, b, b-a); @@ -230,4 +298,20 @@ void smooth(Wave* w, int a, int b) w->setEdited(true); } + +/* -------------------------------------------------------------------------- */ + + +void reverse(Wave* w, int a, int b) +{ + /* https://stackoverflow.com/questions/33201528/reversing-an-array-of-structures-in-c */ + + float* begin = (w->getData() + (a * w->getChannels())); + float* end = (w->getData() + (b * w->getChannels())); + + std::reverse(begin, end); + + w->setEdited(true); +} + }}}; // giada::m::wfx:: \ No newline at end of file diff --git a/src/core/waveFx.h b/src/core/waveFx.h index 4360052..438c7b6 100644 --- a/src/core/waveFx.h +++ b/src/core/waveFx.h @@ -45,20 +45,32 @@ Normalizes the wave by returning the dB value for the boost volume. */ float normalizeSoft(Wave* w); +/* normalizeHard +Normalizes the wave in range a-b by altering values in memory. */ + +void normalizeHard(Wave* w, int a, int b); + int monoToStereo(Wave* w); void silence(Wave* w, int a, int b); int cut(Wave* w, int a, int b); int trim(Wave* w, int a, int b); +int paste(Wave* src, Wave* dest, int a); /* fade - * fade in or fade out selection. Fade In = type 0, Fade Out = type 1 */ +Fades in or fades out selection. Fade In = type 0, Fade Out = type 1 */ void fade(Wave* w, int a, int b, int type); /* smooth - * smooth edges of selection. */ +Smooth edges of selection. */ void smooth(Wave* w, int a, int b); + +/* reverse +Flips Wave's data. */ + +void reverse(Wave* w, int a, int b); + }}}; // giada::m::wfx:: #endif diff --git a/src/core/waveManager.cpp b/src/core/waveManager.cpp index bd102b4..3e430e4 100644 --- a/src/core/waveManager.cpp +++ b/src/core/waveManager.cpp @@ -50,13 +50,13 @@ int getBits(SF_INFO& header) if (header.format & SF_FORMAT_PCM_S8) return 8; else if (header.format & SF_FORMAT_PCM_16) - return 16; + return 16; else if (header.format & SF_FORMAT_PCM_24) - return 24; + return 24; else if (header.format & SF_FORMAT_PCM_32) - return 32; + return 32; else if (header.format & SF_FORMAT_PCM_U8) - return 8; + return 8; else if (header.format & SF_FORMAT_FLOAT) return 32; else if (header.format & SF_FORMAT_DOUBLE) @@ -99,7 +99,7 @@ int create(const string& path, Wave** out) frame1 = [leftChannel, rightChannel] frame2 = [leftChannel, rightChannel] ... */ - + int size = header.frames * header.channels; float* data = new (std::nothrow) float[size]; if (data == nullptr) { @@ -112,7 +112,7 @@ int create(const string& path, Wave** out) sf_close(fileIn); - Wave* wave = new Wave(data, size, header.channels, header.samplerate, + Wave* wave = new Wave(data, size, header.channels, header.samplerate, getBits(header), path); if (header.channels == 1 && !wfx::monoToStereo(wave)) { @@ -139,8 +139,9 @@ int createEmpty(int size, int samplerate, const string& name, Wave** out) return G_RES_ERR_MEMORY; } - Wave *wave = new Wave(data, size, 2, samplerate, G_DEFAULT_BIT_DEPTH, ""); - wave->setLogical(true); + Wave* wave = new Wave(data, size, G_DEFAULT_AUDIO_CHANS, samplerate, + G_DEFAULT_BIT_DEPTH, ""); + wave->setLogical(true); wave->setName(name); wave->setPath(gu_getCurrentPath() + G_SLASH + wave->getName()); @@ -155,6 +156,34 @@ int createEmpty(int size, int samplerate, const string& name, Wave** out) /* -------------------------------------------------------------------------- */ +int createFromWave(const Wave* src, int a, int b, Wave** out) +{ + int numChans = src->getChannels(); + int size = (b - a) * numChans; + float* data = new (std::nothrow) float[size]; + if (data == nullptr) { + gu_log("[waveManager::createFromWave] unable to allocate memory\n"); + return G_RES_ERR_MEMORY; + } + + std::copy(src->getData() + (a*numChans), src->getData() + (b*numChans), data); + + Wave* wave = new Wave(data, size, numChans, src->getRate(), + src->getBits(), src->getPath()); + wave->setLogical(true); + wave->setName(src->getName() + " part"); + + *out = wave; + + gu_log("[waveManager::createFromWave] new Wave created, %d frames\n", size); + + return G_RES_OK; +} + + +/* -------------------------------------------------------------------------- */ + + int resample(Wave* w, int quality, int samplerate) { float ratio = samplerate / (float) w->getRate(); @@ -174,7 +203,7 @@ int resample(Wave* w, int quality, int samplerate) src_data.output_frames = newSizeFrames; src_data.src_ratio = ratio; - gu_log("[waveManager::resample] resampling: new size=%d (%d frames)\n", + gu_log("[waveManager::resample] resampling: new size=%d (%d frames)\n", newSizeSamples, newSizeFrames); int ret = src_simple(&src_data, quality, w->getChannels()); @@ -204,7 +233,7 @@ int save(Wave* w, const string& path) SNDFILE* file = sf_open(path.c_str(), SFM_WRITE, &header); if (file == nullptr) { - gu_log("[waveManager::save] unable to open %s for exporting: %s\n", + gu_log("[waveManager::save] unable to open %s for exporting: %s\n", path.c_str(), sf_strerror(file)); return G_RES_ERR_IO; } @@ -216,7 +245,7 @@ int save(Wave* w, const string& path) w->setLogical(false); w->setEdited(false); - + return G_RES_OK; } }}}; // giada::m::waveManager diff --git a/src/core/waveManager.h b/src/core/waveManager.h index b9e8a05..2772b59 100644 --- a/src/core/waveManager.h +++ b/src/core/waveManager.h @@ -49,6 +49,12 @@ Creates a new silent Wave object. Note: 'size' must take 2 channels into account (stereo). */ int createEmpty(int size, int samplerate, const std::string& name, Wave** out); + +/* createFromWave +Creates a new Wave from an existing one, copying the data in range a - b. */ + +int createFromWave(const Wave* src, int a, int b, Wave** out); + int resample(Wave* w, int quality, int samplerate); int save(Wave* w, const std::string& path); diff --git a/src/glue/channel.cpp b/src/glue/channel.cpp index d9c13d5..12f7582 100644 --- a/src/glue/channel.cpp +++ b/src/glue/channel.cpp @@ -74,13 +74,14 @@ 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. */ + /* 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); + if (ch->status & (STATUS_PLAY | STATUS_ENDING)) + ch->hardStop(0); - /* save the patch and take the last browser's dir in order to re-use it - * the next time */ + /* Save the patch and take the last browser's dir in order to re-use it the + next time. */ conf::samplePath = gu_dirname(fname); @@ -110,10 +111,10 @@ int glue_loadChannel(SampleChannel* ch, const string& fname) /* -------------------------------------------------------------------------- */ -Channel* glue_addChannel(int column, int type) +Channel* glue_addChannel(int column, int type, int size) { Channel* ch = mh::addChannel(type); - geChannel* gch = G_MainWin->keyboard->addChannel(column, ch); + geChannel* gch = G_MainWin->keyboard->addChannel(column, ch, size); ch->guiChannel = gch; return ch; } @@ -124,10 +125,10 @@ Channel* glue_addChannel(int column, int type) void glue_deleteChannel(Channel* ch) { - if (!gdConfirmWin("Warning", "Delete channel: are you sure?")) - return; - recorder::clearChan(ch->index); - ch->hasActions = false; + if (!gdConfirmWin("Warning", "Delete channel: are you sure?")) + return; + recorder::clearChan(ch->index); + ch->hasActions = false; #ifdef WITH_VST pluginHost::freeStack(pluginHost::CHANNEL, &mixer::mutex_plugins, ch); #endif @@ -144,25 +145,25 @@ void glue_deleteChannel(Channel* ch) void glue_freeChannel(Channel *ch) { - 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; + if (ch->status == STATUS_PLAY) { + if (!gdConfirmWin("Warning", "This action will stop the channel: are you sure?")) + return; + } + else + if (!gdConfirmWin("Warning", "Free channel: are you sure?")) + return; G_MainWin->keyboard->freeChannel(ch->guiChannel); recorder::clearChan(ch->index); - ch->hasActions = false; + ch->hasActions = false; ch->empty(); - /* delete any related subwindow */ - /** TODO - use gu_closeAllSubwindows() */ - G_MainWin->delSubWindow(WID_FILE_BROWSER); - G_MainWin->delSubWindow(WID_ACTION_EDITOR); - G_MainWin->delSubWindow(WID_SAMPLE_EDITOR); - G_MainWin->delSubWindow(WID_FX_LIST); + /* delete any related subwindow */ + /** TODO - use gu_closeAllSubwindows() */ + G_MainWin->delSubWindow(WID_FILE_BROWSER); + G_MainWin->delSubWindow(WID_ACTION_EDITOR); + G_MainWin->delSubWindow(WID_SAMPLE_EDITOR); + G_MainWin->delSubWindow(WID_FX_LIST); } @@ -182,8 +183,8 @@ void glue_toggleArm(Channel *ch, bool gui) void glue_toggleInputMonitor(Channel *ch) { - SampleChannel *sch = static_cast(ch); - sch->inputMonitor = !sch->inputMonitor; + SampleChannel *sch = static_cast(ch); + sch->inputMonitor = !sch->inputMonitor; } @@ -193,7 +194,8 @@ void glue_toggleInputMonitor(Channel *ch) int glue_cloneChannel(Channel *src) { Channel *ch = mh::addChannel(src->type); - geChannel *gch = G_MainWin->keyboard->addChannel(src->guiChannel->getColumnIndex(), ch); + geChannel *gch = G_MainWin->keyboard->addChannel(src->guiChannel->getColumnIndex(), + ch, src->guiChannel->getSize()); ch->guiChannel = gch; ch->copy(src, &mixer::mutex_plugins); @@ -212,14 +214,14 @@ void glue_setVolume(Channel *ch, float v, bool gui, bool editor) /* Changing channel volume? Update wave editor (if it's shown). */ - if (!editor) { - gdSampleEditor *gdEditor = (gdSampleEditor*) gu_getSubwindow(G_MainWin, WID_SAMPLE_EDITOR); - if (gdEditor) { - Fl::lock(); - gdEditor->volumeTool->refresh(); - Fl::unlock(); - } - } + if (!editor) { + gdSampleEditor *gdEditor = (gdSampleEditor*) gu_getSubwindow(G_MainWin, WID_SAMPLE_EDITOR); + if (gdEditor) { + Fl::lock(); + gdEditor->volumeTool->refresh(); + Fl::unlock(); + } + } if (!gui) { Fl::lock(); @@ -232,10 +234,10 @@ void glue_setVolume(Channel *ch, float v, bool gui, bool editor) /* -------------------------------------------------------------------------- */ -void glue_setPitch(SampleChannel *ch, float val) +void glue_setPitch(SampleChannel* ch, float val) { ch->setPitch(val); - gdSampleEditor *gdEditor = static_cast(gu_getSubwindow(G_MainWin, WID_SAMPLE_EDITOR)); + gdSampleEditor* gdEditor = static_cast(gu_getSubwindow(G_MainWin, WID_SAMPLE_EDITOR)); if (gdEditor) { Fl::lock(); gdEditor->pitchTool->refresh(); @@ -247,10 +249,10 @@ void glue_setPitch(SampleChannel *ch, float val) /* -------------------------------------------------------------------------- */ -void glue_setPanning(SampleChannel *ch, float val) +void glue_setPanning(SampleChannel* ch, float val) { ch->setPan(val); - gdSampleEditor *gdEditor = static_cast(gu_getSubwindow(G_MainWin, WID_SAMPLE_EDITOR)); + gdSampleEditor* gdEditor = static_cast(gu_getSubwindow(G_MainWin, WID_SAMPLE_EDITOR)); if (gdEditor) { Fl::lock(); gdEditor->panTool->refresh(); @@ -267,12 +269,12 @@ void glue_setMute(Channel *ch, bool gui) if (recorder::active && recorder::canRec(ch, clock::isRunning(), mixer::recording)) { if (!ch->mute) { recorder::startOverdub(ch->index, G_ACTION_MUTES, clock::getCurrentFrame(), - kernelAudio::getRealBufSize()); - ch->readActions = false; // don't read actions while overdubbing - } + kernelAudio::getRealBufSize()); + ch->readActions = false; // don't read actions while overdubbing + } else recorder::stopOverdub(clock::getCurrentFrame(), clock::getTotalFrames(), - &mixer::mutex_recs); + &mixer::mutex_recs); } ch->mute ? ch->unsetMute(false) : ch->setMute(false); diff --git a/src/glue/channel.h b/src/glue/channel.h index b3ac929..65cd77e 100644 --- a/src/glue/channel.h +++ b/src/glue/channel.h @@ -40,7 +40,7 @@ class gdSampleEditor; /* addChannel * add an empty new channel to the stack. Returns the new channel. */ -Channel *glue_addChannel(int column, int type); +Channel *glue_addChannel(int column, int type, int size); /* loadChannel * fill an existing channel with a wave. */ diff --git a/src/glue/main.cpp b/src/glue/main.cpp index efc8c67..2067261 100644 --- a/src/glue/main.cpp +++ b/src/glue/main.cpp @@ -56,10 +56,10 @@ using namespace giada::m; void glue_setBpm(const char *v1, const char *v2) { - /* Never change this stuff while recording audio */ + /* Never change this stuff while recording audio */ - if (mixer::recording) - return; + if (mixer::recording) + return; char bpmS[6]; float bpmF = atof(v1) + (atof(v2)/10); @@ -76,14 +76,15 @@ void glue_setBpm(const char *v1, const char *v2) float oldBpmF = clock::getBpm(); clock::setBpm(bpmF); - recorder::updateBpm(oldBpmF, bpmF, clock::getQuanto()); + recorder::updateBpm(oldBpmF, bpmF, clock::getQuanto()); + mixer::allocVirtualInput(clock::getTotalFrames()); #ifdef __linux__ - kernelAudio::jackSetBpm(clock::getBpm()); + kernelAudio::jackSetBpm(clock::getBpm()); #endif - gu_refreshActionEditor(); - G_MainWin->mainTimer->setBpm(bpmS); + gu_refreshActionEditor(); + G_MainWin->mainTimer->setBpm(bpmS); gu_log("[glue] Bpm changed to %s (real=%f)\n", bpmS, clock::getBpm()); } @@ -94,13 +95,13 @@ void glue_setBpm(const char *v1, const char *v2) void glue_setBpm(float v) { - if (v < G_MIN_BPM || v > G_MAX_BPM) - v = G_DEFAULT_BPM; - double fIpart; - double fPpart = modf(v, &fIpart); - int iIpart = fIpart; - int iPpart = ceilf(fPpart); - glue_setBpm(gu_toString(iIpart).c_str(), gu_toString(iPpart).c_str()); + if (v < G_MIN_BPM || v > G_MAX_BPM) + v = G_DEFAULT_BPM; + double fIpart; + double fPpart = modf(v, &fIpart); + int iIpart = fIpart; + int iPpart = ceilf(fPpart); + glue_setBpm(gu_toString(iIpart).c_str(), gu_toString(iPpart).c_str()); } @@ -109,10 +110,10 @@ void glue_setBpm(float v) void glue_setBeats(int beats, int bars, bool expand) { - /* Never change this stuff while recording audio */ + /* Never change this stuff while recording audio */ - if (mixer::recording) - return; + if (mixer::recording) + return; /* Temp vars to store old data (they are necessary) */ @@ -125,7 +126,7 @@ void glue_setBeats(int beats, int bars, bool expand) mixer::allocVirtualInput(clock::getTotalFrames()); /* Update recorded actions, if 'expand' required and an expansion is taking - place. */ + place. */ if (expand && clock::getBeats() > oldBeats) recorder::expand(oldTotalFrames, clock::getTotalFrames()); @@ -142,13 +143,13 @@ void glue_rewindSeq(bool gui, bool notifyJack) { mh::rewindSequencer(); - /* FIXME - potential desync when Quantizer is enabled from this point on. - Mixer would wait, while the following calls would be made regardless of its - state. */ + /* FIXME - potential desync when Quantizer is enabled from this point on. + Mixer would wait, while the following calls would be made regardless of its + state. */ #ifdef __linux__ - if (notifyJack) - kernelAudio::jackSetPosition(0); + if (notifyJack) + kernelAudio::jackSetPosition(0); #endif if (conf::midiSync == MIDI_SYNC_CLOCK_M) @@ -236,7 +237,7 @@ void glue_resetToInitState(bool resetGui, bool createColumns) if (createColumns) G_MainWin->keyboard->init(); - gu_updateMainWinLabel(G_DEFAULT_PATCH_NAME); + gu_updateMainWinLabel(G_DEFAULT_PATCH_NAME); if (resetGui) gu_updateControls(); diff --git a/src/glue/sampleEditor.cpp b/src/glue/sampleEditor.cpp index ecaade6..0c20347 100644 --- a/src/glue/sampleEditor.cpp +++ b/src/glue/sampleEditor.cpp @@ -38,10 +38,14 @@ #include "../gui/elems/sampleEditor/pitchTool.h" #include "../gui/elems/sampleEditor/rangeTool.h" #include "../gui/elems/sampleEditor/waveform.h" +#include "../gui/elems/mainWindow/keyboard/channel.h" #include "../core/sampleChannel.h" #include "../core/waveFx.h" +#include "../core/wave.h" +#include "../core/waveManager.h" #include "../core/const.h" #include "../utils/gui.h" +#include "../utils/log.h" #include "channel.h" #include "sampleEditor.h" @@ -56,6 +60,18 @@ namespace giada { namespace c { namespace sampleEditor { +namespace +{ + /* m_waveBuffer + A Wave used during cut/copy/paste operations. */ + + Wave* m_waveBuffer = nullptr; +}; // {anonymous} + + +/* -------------------------------------------------------------------------- */ + + gdSampleEditor* getSampleEditorWindow() { gdSampleEditor* se = static_cast(gu_getSubwindow(G_MainWin, WID_SAMPLE_EDITOR)); @@ -67,7 +83,7 @@ gdSampleEditor* getSampleEditorWindow() /* -------------------------------------------------------------------------- */ -void setBeginEndChannel(SampleChannel* ch, int b, int e) +void setBeginEnd(SampleChannel* ch, int b, int e) { ch->setBegin(b); ch->setEnd(e); @@ -75,6 +91,10 @@ void setBeginEndChannel(SampleChannel* ch, int b, int e) Fl::lock(); gdEditor->rangeTool->refresh(); Fl::unlock(); + + gdEditor->waveTools->waveform->recalcPoints(); + gdEditor->waveTools->waveform->clearSel(); + gdEditor->waveTools->waveform->redraw(); } @@ -87,7 +107,7 @@ void cut(SampleChannel* ch, int a, int b) gdAlert("Unable to cut the sample!"); return; } - setBeginEndChannel(ch, ch->getBegin(), ch->getEnd()); + setBeginEnd(ch, ch->getBegin(), ch->getEnd()); gdSampleEditor* gdEditor = getSampleEditorWindow(); gdEditor->waveTools->waveform->clearSel(); gdEditor->waveTools->waveform->refresh(); @@ -98,6 +118,47 @@ void cut(SampleChannel* ch, int a, int b) /* -------------------------------------------------------------------------- */ +void copy(SampleChannel* ch, int a, int b) +{ + if (m_waveBuffer != nullptr) + delete m_waveBuffer; + + int result = waveManager::createFromWave(ch->wave, a, b, &m_waveBuffer); + if (result != G_RES_OK) { + gu_log("[sampleEditor::copy] unable to create wave buffer!\n"); + return; + } +} + + +/* -------------------------------------------------------------------------- */ + + +void paste(SampleChannel* ch, int a) +{ + if (!isWaveBufferFull()) + return; + + wfx::paste(m_waveBuffer, ch->wave, a); + + /* Shift begin/end points to keep the previous position. */ + + int delta = m_waveBuffer->getSize(); + if (a < ch->getBegin() && a < ch->getEnd()) + setBeginEnd(ch, ch->getBegin() + delta, ch->getEnd() + delta); + else + if (a < ch->getEnd()) + setBeginEnd(ch, ch->getBegin(), ch->getEnd() + delta); + + gdSampleEditor* gdEditor = getSampleEditorWindow(); + gdEditor->waveTools->waveform->clearSel(); + gdEditor->waveTools->waveform->refresh(); + gdEditor->updateInfo(); +} + +/* -------------------------------------------------------------------------- */ + + void silence(SampleChannel* ch, int a, int b) { wfx::silence(ch->wave, a, b); @@ -131,13 +192,22 @@ void smoothEdges(SampleChannel* ch, int a, int b) /* -------------------------------------------------------------------------- */ -void setStartEnd(SampleChannel* ch, int a, int b) +void reverse(SampleChannel* ch, int a, int b) { - setBeginEndChannel(ch, a, b); + wfx::reverse(ch->wave, a, b); gdSampleEditor* gdEditor = getSampleEditorWindow(); - gdEditor->waveTools->waveform->recalcPoints(); - gdEditor->waveTools->waveform->clearSel(); - gdEditor->waveTools->waveform->redraw(); + gdEditor->waveTools->waveform->refresh(); +} + + +/* -------------------------------------------------------------------------- */ + + +void normalizeHard(SampleChannel* ch, int a, int b) +{ + wfx::normalizeHard(ch->wave, a, b); + gdSampleEditor* gdEditor = getSampleEditorWindow(); + gdEditor->waveTools->waveform->refresh(); } @@ -150,7 +220,7 @@ void trim(SampleChannel* ch, int a, int b) gdAlert("Unable to trim the sample!"); return; } - setBeginEndChannel(ch, ch->getBegin(), ch->getEnd()); + setBeginEnd(ch, ch->getBegin(), ch->getEnd()); gdSampleEditor* gdEditor = getSampleEditorWindow(); gdEditor->waveTools->waveform->clearSel(); gdEditor->waveTools->waveform->refresh(); @@ -191,4 +261,35 @@ void rewindPreview(SampleChannel* ch) else setPlayHead(ch, 0); } + + +/* -------------------------------------------------------------------------- */ + + +void toNewChannel(SampleChannel* ch, int a, int b) +{ + SampleChannel* newCh = static_cast(glue_addChannel( + ch->guiChannel->getColumnIndex(), CHANNEL_SAMPLE, G_GUI_CHANNEL_H_1)); + + Wave* wave = nullptr; + int result = waveManager::createFromWave(ch->wave, a, b, &wave); + if (result != G_RES_OK) { + gdAlert("Unable to copy to new channel!"); + return; + } + + newCh->pushWave(wave, true); + + newCh->guiChannel->update(); +} + + +/* -------------------------------------------------------------------------- */ + + +bool isWaveBufferFull() +{ + return m_waveBuffer != nullptr; +} + }}}; // giada::c::sampleEditor:: diff --git a/src/glue/sampleEditor.h b/src/glue/sampleEditor.h index e4feeec..3846f9f 100644 --- a/src/glue/sampleEditor.h +++ b/src/glue/sampleEditor.h @@ -39,17 +39,28 @@ namespace sampleEditor { -/* setBeginEndChannel +/* setBeginEnd Sets start/end points in the sample editor. */ -void setBeginEndChannel(SampleChannel* ch, int b, int e); +void setBeginEnd(SampleChannel* ch, int b, int e); void cut(SampleChannel* ch, int a, int b); +void copy(SampleChannel* ch, int a, int b); + +/* paste +Pastes what's defined in m_copyBuffer into channel 'ch' at point 'a'. If +m_copyBuffer is empty, does nothing. */ + +void paste(SampleChannel* ch, int a); + void trim(SampleChannel* ch, int a, int b); +void reverse(SampleChannel* ch, int a, int b); +void normalizeHard(SampleChannel* ch, int a, int b); void silence(SampleChannel* ch, int a, int b); void fade(SampleChannel* ch, int a, int b, int type); void smoothEdges(SampleChannel* ch, int a, int b); -void setStartEnd(SampleChannel* ch, int a, int b); + +bool isWaveBufferFull(); /* setPlayHead Changes playhead's position. Used in preview. */ @@ -58,6 +69,11 @@ void setPlayHead(SampleChannel* ch, int f); void setPreview(SampleChannel* ch, int mode); void rewindPreview(SampleChannel* ch); + +/* toNewChannel +Copies the selected range into a new sample channel. */ + +void toNewChannel(SampleChannel* ch, int a, int b); }}}; // giada::c::sampleEditor:: #endif diff --git a/src/glue/storage.cpp b/src/glue/storage.cpp index c853a9b..da1a80f 100644 --- a/src/glue/storage.cpp +++ b/src/glue/storage.cpp @@ -129,8 +129,8 @@ static void __glue_fillPatchGlobals__(const string &name) patch::beats = clock::getBeats(); patch::quantize = clock::getQuantize(); patch::masterVolIn = mixer::inVol; - patch::masterVolOut = mixer::outVol; - patch::metronome = mixer::metronome; + patch::masterVolOut = mixer::outVol; + patch::metronome = mixer::metronome; #ifdef WITH_VST @@ -194,9 +194,9 @@ void glue_savePatch(void *data) /* -------------------------------------------------------------------------- */ -void glue_loadPatch(void *data) +void glue_loadPatch(void* data) { - gdBrowserLoad *browser = (gdBrowserLoad*) data; + gdBrowserLoad* browser = (gdBrowserLoad*) data; string fullPath = browser->getSelectedItem(); bool isProject = gu_isProject(browser->getSelectedItem()); @@ -239,15 +239,15 @@ void glue_loadPatch(void *data) * by 0.8 / total_channels steps. */ float steps = 0.8 / patch::channels.size(); - for (unsigned i=0; ikeyboard->addColumn(col->width); + + for (const patch::column_t& col : patch::columns) { + G_MainWin->keyboard->addColumn(col.width); for (unsigned k=0; kindex) { - Channel *ch = glue_addChannel(patch::channels.at(k).column, - patch::channels.at(k).type); + if (patch::channels.at(k).column == col.index) { + Channel* ch = glue_addChannel(patch::channels.at(k).column, + patch::channels.at(k).type, patch::channels.at(k).size); ch->readPatch(basePath, k, &mixer::mutex_plugins, conf::samplerate, - conf::rsmpQuality); + conf::rsmpQuality); } browser->setStatusBar(steps); } @@ -348,15 +348,16 @@ void glue_saveProject(void *data) /* -------------------------------------------------------------------------- */ -void glue_loadSample(void *data) +void glue_loadSample(void* data) { - gdBrowserLoad *browser = (gdBrowserLoad*) data; + gdBrowserLoad* browser = (gdBrowserLoad*) data; string fullPath = browser->getSelectedItem(); if (fullPath.empty()) return; - int res = glue_loadChannel((SampleChannel*) browser->getChannel(), fullPath.c_str()); + int res = glue_loadChannel(static_cast(browser->getChannel()), + fullPath); if (res == G_RES_OK) { conf::samplePath = gu_dirname(fullPath); @@ -391,7 +392,7 @@ void glue_saveSample(void *data) return; if (static_cast(browser->getChannel())->save(filePath.c_str())) { - gu_log("[glue_saveSample] sample saved to %s\n", filePath.c_str()); + gu_log("[glue_saveSample] sample saved to %s\n", filePath.c_str()); conf::samplePath = gu_dirname(filePath); browser->do_callback(); } diff --git a/src/gui/dialogs/gd_actionEditor.cpp b/src/gui/dialogs/gd_actionEditor.cpp index 6dba08e..723763f 100644 --- a/src/gui/dialogs/gd_actionEditor.cpp +++ b/src/gui/dialogs/gd_actionEditor.cpp @@ -124,7 +124,8 @@ gdActionEditor::gdActionEditor(Channel *chan) else { pr = new geNoteEditor(scroller->x(), upperArea->y()+upperArea->h()+8, this); scroller->add(pr); - scroller->add(new geResizerBar(pr->x(), pr->y()+pr->h(), scroller->w(), 8)); + /* TODO - avoid magic number 30 for minimum height */ + scroller->add(new geResizerBar(pr->x(), pr->y()+pr->h(), scroller->w(), 30, 8)); } end(); diff --git a/src/gui/dialogs/gd_actionEditor.h b/src/gui/dialogs/gd_actionEditor.h index 2ee9b81..efe3e0e 100644 --- a/src/gui/dialogs/gd_actionEditor.h +++ b/src/gui/dialogs/gd_actionEditor.h @@ -76,7 +76,7 @@ public: inline void __cb_zoomIn(); inline void __cb_zoomOut(); - geChoice *actionType; + geChoice *actionType; geGridTool *gridTool; geButton *zoomIn; geButton *zoomOut; diff --git a/src/gui/dialogs/sampleEditor.cpp b/src/gui/dialogs/sampleEditor.cpp index 0d8b474..6c250f6 100644 --- a/src/gui/dialogs/sampleEditor.cpp +++ b/src/gui/dialogs/sampleEditor.cpp @@ -83,7 +83,7 @@ gdSampleEditor::gdSampleEditor(SampleChannel* ch) gu_setFavicon(this); set_non_modal(); - label(ch->wave->getName().c_str()); + copy_label(ch->wave->getName().c_str()); size_range(720, 480); if (conf::sampleEditorX) @@ -290,6 +290,7 @@ void gdSampleEditor::__cb_rewindPreview() void gdSampleEditor::__cb_reload() { + /* TODO - move to glue::sampleEditor */ if (!gdConfirmWin("Warning", "Reload sample: are you sure?")) return; @@ -306,7 +307,7 @@ void gdSampleEditor::__cb_reload() waveTools->waveform->stretchToWindow(); waveTools->updateWaveform(); - sampleEditor::setBeginEndChannel(ch, 0, ch->wave->getSize()); + sampleEditor::setBeginEnd(ch, 0, ch->wave->getSize()); redraw(); } diff --git a/src/gui/elems/actionEditor/pianoRoll.cpp b/src/gui/elems/actionEditor/pianoRoll.cpp index 36746b2..90b4a10 100644 --- a/src/gui/elems/actionEditor/pianoRoll.cpp +++ b/src/gui/elems/actionEditor/pianoRoll.cpp @@ -163,45 +163,45 @@ void gePianoRoll::drawSurface1() switch (i % KEYS) { case (int) Notes::G: fl_rectf(0, i*CELL_H, CELL_W, CELL_H, G_COLOR_GREY_2); - sprintf(note, "%dG", octave); + sprintf(note, "%d G", octave); break; case (int) Notes::FS: - sprintf(note, "%dF#", octave); + sprintf(note, "%d F#", octave); break; case (int) Notes::F: - sprintf(note, "%dF", octave); + sprintf(note, "%d F", octave); break; case (int) Notes::E: fl_rectf(0, i*CELL_H, CELL_W, CELL_H, G_COLOR_GREY_2); - sprintf(note, "%dE", octave); + sprintf(note, "%d E", octave); break; case (int) Notes::DS: - sprintf(note, "%dD#", octave); + sprintf(note, "%d D#", octave); break; case (int) Notes::D: fl_rectf(0, i*CELL_H, CELL_W, CELL_H, G_COLOR_GREY_2); - sprintf(note, "%dD", octave); + sprintf(note, "%d D", octave); break; case (int) Notes::CS: - sprintf(note, "%dC#", octave); + sprintf(note, "%d C#", octave); break; case (int) Notes::C: - sprintf(note, "%dC", octave); + sprintf(note, "%d C", octave); + octave--; break; case (int) Notes::B: fl_rectf(0, i*CELL_H, CELL_W, CELL_H, G_COLOR_GREY_2); - sprintf(note, "%dB", octave); + sprintf(note, "%d B", octave); break; case (int) Notes::AS: - sprintf(note, "%dA#", octave); + sprintf(note, "%d A#", octave); break; case (int) Notes::A: fl_rectf(0, i*CELL_H, CELL_W, CELL_H, G_COLOR_GREY_2); - sprintf(note, "%dA", octave); + sprintf(note, "%d A", octave); break; case (int) Notes::GS: - sprintf(note, "%dG#", octave); - octave--; + sprintf(note, "%d G#", octave); break; } diff --git a/src/gui/elems/basics/resizerBar.cpp b/src/gui/elems/basics/resizerBar.cpp index da30c69..a433b44 100644 --- a/src/gui/elems/basics/resizerBar.cpp +++ b/src/gui/elems/basics/resizerBar.cpp @@ -9,14 +9,14 @@ * Shows a resize cursor when hovered over. * Assumes: * - Parent is an Fl_Scroll - * - All children of Fl_Scroll are vertically arranged + * - All children of Fl_Scroll are m_vertically arranged * - The widget above us has a bottom edge touching our top edge * ie. (w->y()+w->h() == this->y()) * * When this widget is dragged: * - The widget above us (with a common edge) will be /resized/ - * vertically - * - All children below us will be /moved/ vertically + * m_vertically + * - All children below us will be /moved/ m_vertically * * ----------------------------------------------------------------------------- * @@ -44,23 +44,26 @@ #include #include #include +#include "../../../core/const.h" #include "resizerBar.h" -geResizerBar::geResizerBar(int X,int Y,int W,int H, bool vertical) - : Fl_Box(X,Y,W,H), vertical(vertical) +geResizerBar::geResizerBar(int X, int Y, int W, int H, int minSize, bool type) + : Fl_Box (X, Y, W, H), + m_type (type), + m_minSize(minSize), + m_lastPos(0), + m_hover (false) { - last_y = 0; - min_h = 30; - if (vertical) { - orig_h = H; + if (m_type == VERTICAL) { + m_origSize = H; labelsize(H); } else { - orig_h = W; + m_origSize = W; labelsize(W); } - align(FL_ALIGN_CENTER|FL_ALIGN_INSIDE); + align(FL_ALIGN_CENTER | FL_ALIGN_INSIDE); labelfont(FL_COURIER); visible_focus(0); } @@ -69,12 +72,12 @@ geResizerBar::geResizerBar(int X,int Y,int W,int H, bool vertical) /* -------------------------------------------------------------------------- */ -void geResizerBar::HandleDrag(int diff) +void geResizerBar::handleDrag(int diff) { - Fl_Scroll *grp = static_cast(parent()); + Fl_Scroll* grp = static_cast(parent()); int top; int bot; - if (vertical) { + if (m_type == VERTICAL) { top = y(); bot = y()+h(); } @@ -87,21 +90,21 @@ void geResizerBar::HandleDrag(int diff) // Possibly clamp 'diff' if widget would get too small.. for (int t=0; tchildren(); t++) { - Fl_Widget *wd = grp->child(t); - if (vertical) { - if ((wd->y()+wd->h()) == top) { // found widget directly above? - if ((wd->h()+diff) < min_h) - diff = wd->h() - min_h; // clamp - wd->resize(wd->x(), wd->y(), wd->w(), wd->h()+diff); // change height - break; // done with first pass + Fl_Widget* wd = grp->child(t); + if (m_type == VERTICAL) { + if ((wd->y()+wd->h()) == top) { // found widget directly above? + if ((wd->h()+diff) < m_minSize) + diff = wd->h() - m_minSize; // clamp + wd->resize(wd->x(), wd->y(), wd->w(), wd->h()+diff); // change height + break; // done with first pass } } else { - if ((wd->x()+wd->w()) == top) { // found widget directly above? - if ((wd->w()+diff) < min_h) - diff = wd->w() - min_h; // clamp - wd->resize(wd->x(), wd->y(), wd->w()+diff, wd->h()); // change height - break; // done with first pass + if ((wd->x()+wd->w()) == top) { // found widget directly above? + if ((wd->w()+diff) < m_minSize) + diff = wd->w() - m_minSize; // clamp + wd->resize(wd->x(), wd->y(), wd->w()+diff, wd->h()); // change height + break; // done with first pass } } } @@ -109,10 +112,10 @@ void geResizerBar::HandleDrag(int diff) // Second pass: find widgets below us, move based on clamped diff for (int t=0; tchildren(); t++) { - Fl_Widget *wd = grp->child(t); - if (vertical) { + Fl_Widget* wd = grp->child(t); + if (m_type == VERTICAL) { if (wd->y() >= bot) // found widget below us? - wd->resize(wd->x(), wd->y()+diff, wd->w(), wd->h()); // change position + wd->resize(wd->x(), wd->y()+diff, wd->w(), wd->h()); // change position } else { if (wd->x() >= bot) @@ -122,7 +125,7 @@ void geResizerBar::HandleDrag(int diff) // Change our position last - if (vertical) + if (m_type == VERTICAL) resize(x(), y()+diff, w(), h()); else resize(x()+diff, y(), w(), h()); @@ -135,11 +138,21 @@ void geResizerBar::HandleDrag(int diff) /* -------------------------------------------------------------------------- */ +void geResizerBar::draw() +{ + Fl_Box::draw(); + fl_rectf(x(), y(), w(), h(), m_hover ? G_COLOR_GREY_2 : G_COLOR_GREY_1); +} + + +/* -------------------------------------------------------------------------- */ + + int geResizerBar::handle(int e) { int ret = 0; int this_y; - if (vertical) + if (m_type == VERTICAL) this_y = Fl::event_y_root(); else this_y = Fl::event_x_root(); @@ -149,19 +162,23 @@ int geResizerBar::handle(int e) break; case FL_ENTER: ret = 1; - fl_cursor(vertical ? FL_CURSOR_NS : FL_CURSOR_WE); + fl_cursor(m_type == VERTICAL ? FL_CURSOR_NS : FL_CURSOR_WE); + m_hover = true; + redraw(); break; case FL_LEAVE: ret = 1; fl_cursor(FL_CURSOR_DEFAULT); + m_hover = false; + redraw(); break; case FL_PUSH: ret = 1; - last_y = this_y; + m_lastPos = this_y; break; case FL_DRAG: - HandleDrag(this_y-last_y); - last_y = this_y; + handleDrag(this_y-m_lastPos); + m_lastPos = this_y; ret = 1; break; default: break; @@ -173,10 +190,19 @@ int geResizerBar::handle(int e) /* -------------------------------------------------------------------------- */ -void geResizerBar::resize(int X,int Y,int W,int H) +int geResizerBar::getMinSize() const +{ + return m_minSize; +} + + +/* -------------------------------------------------------------------------- */ + + +void geResizerBar::resize(int x, int y, int w, int h) { - if (vertical) - Fl_Box::resize(X,Y,W,orig_h); // height of resizer stays constant size - else - Fl_Box::resize(X,Y,orig_h,H); + if (m_type == VERTICAL) + Fl_Box::resize(x, y, w, m_origSize); // Height of resizer stays constant size + else + Fl_Box::resize(x, y, m_origSize, h); } diff --git a/src/gui/elems/basics/resizerBar.h b/src/gui/elems/basics/resizerBar.h index 74f5bbf..4308ddb 100644 --- a/src/gui/elems/basics/resizerBar.h +++ b/src/gui/elems/basics/resizerBar.h @@ -52,28 +52,26 @@ class geResizerBar : public Fl_Box { private: - /* TODO - use more general variable names - * (last_y -> last_?, min_h -> min_?, ...) */ + bool m_type; + int m_origSize; + int m_minSize; + int m_lastPos; + bool m_hover; - bool vertical; - int orig_h; - int last_y; - int min_h; // min height for widget above us - - void HandleDrag(int diff); + void handleDrag(int diff); public: - /* 'vertical' defines the bar movement. Vertical=true: the bar moves - * vertically (up and down). */ + static const int HORIZONTAL = 0; + static const int VERTICAL = 1; - geResizerBar(int x, int y, int w, int h, bool vertical=true); + geResizerBar(int x, int y, int w, int h, int minSize, bool type=VERTICAL); - inline void setMinSize(int val) { min_h = val; } - inline int getMinSize() { return min_h; } + int handle(int e) override; + void draw() override; - int handle(int e); - void resize(int x, int y, int w, int h); + int getMinSize() const; + void resize(int x, int y, int w, int h); }; diff --git a/src/gui/elems/mainWindow/keyboard/channel.cpp b/src/gui/elems/mainWindow/keyboard/channel.cpp index 8a59077..d226c1f 100644 --- a/src/gui/elems/mainWindow/keyboard/channel.cpp +++ b/src/gui/elems/mainWindow/keyboard/channel.cpp @@ -36,7 +36,9 @@ #include "../../../dialogs/gd_pluginList.h" #include "../../basics/idButton.h" #include "../../basics/dial.h" +#include "../../basics/statusButton.h" #include "column.h" +#include "channelStatus.h" #include "channelButton.h" #include "channel.h" @@ -49,8 +51,8 @@ using namespace giada::m; geChannel::geChannel(int X, int Y, int W, int H, int type, Channel *ch) : Fl_Group(X, Y, W, H, nullptr), - ch (ch), - type (type) + ch (ch), + type (type) { } @@ -72,7 +74,7 @@ void geChannel::cb_openFxWindow(Fl_Widget *v, void *p) { ((geChannel*)p)->__cb_o void geChannel::__cb_arm() { - glue_toggleArm(ch, true); + glue_toggleArm(ch, true); } @@ -141,7 +143,7 @@ void geChannel::blink() if (gu_getBlinker() > 6) mainButton->setPlayMode(); else - mainButton->setDefaultMode(); + mainButton->setDefaultMode(); } @@ -150,36 +152,36 @@ void geChannel::blink() 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; - } + 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; + } } @@ -188,31 +190,32 @@ void geChannel::setColorsByStatus(int playStatus, int recStatus) void geChannel::packWidgets() { - /* Count visible widgets and resize mainButton according to how many widgets - are visible. */ - - int visibles = 0; - for (int i=0; isize(20, 20); // also normalize widths - if (child(i)->visible()) - visibles++; - } - mainButton->size(w() - ((visibles - 1) * (24)), 20); // -1: exclude itself - - /* Reposition everything else */ - - for (int i=1, p=0; ivisible()) - continue; - for (int k=i-1; k>=0; k--) // Get the first visible item prior to i - if (child(k)->visible()) { - p = k; - break; - } - child(i)->position(child(p)->x() + child(p)->w() + 4, y()); - } - - init_sizes(); // Resets the internal array of widget sizes and positions + /* Count visible widgets and resize mainButton according to how many widgets + are visible. */ + + int visibles = 0; + for (int i=0; isize(MIN_ELEM_W, child(i)->h()); // also normalize widths + if (child(i)->visible()) + visibles++; + } + mainButton->size(w() - ((visibles - 1) * (MIN_ELEM_W + G_GUI_INNER_MARGIN)), // -1: exclude itself + mainButton->h()); + + /* Reposition everything else */ + + for (int i=1, p=0; ivisible()) + continue; + for (int k=i-1; k>=0; k--) // Get the first visible item prior to i + if (child(k)->visible()) { + p = k; + break; + } + child(i)->position(child(p)->x() + child(p)->w() + G_GUI_INNER_MARGIN, child(i)->y()); + } + + init_sizes(); // Resets the internal array of widget sizes and positions } @@ -239,3 +242,33 @@ int geChannel::handleKey(int e, int key) return ret; } + + +/* -------------------------------------------------------------------------- */ + + +void geChannel::changeSize(int H) +{ + size(w(), H); + + int Y = y() + (H / 2 - (G_GUI_UNIT / 2)); + + button->resize(x(), Y, w(), G_GUI_UNIT); + arm->resize(x(), Y, w(), G_GUI_UNIT); + mainButton->resize(x(), y(), w(), H); + mute->resize(x(), Y, w(), G_GUI_UNIT); + solo->resize(x(), Y, w(), G_GUI_UNIT); + vol->resize(x(), Y, w(), G_GUI_UNIT); +#ifdef WITH_VST + fx->resize(x(), Y, w(), G_GUI_UNIT); +#endif +} + + +/* -------------------------------------------------------------------------- */ + + +int geChannel::getSize() +{ + return h(); +} diff --git a/src/gui/elems/mainWindow/keyboard/channel.h b/src/gui/elems/mainWindow/keyboard/channel.h index fd1efd9..04cbfdd 100644 --- a/src/gui/elems/mainWindow/keyboard/channel.h +++ b/src/gui/elems/mainWindow/keyboard/channel.h @@ -61,12 +61,14 @@ protected: 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); + static const int MIN_ELEM_W = 20; + + static void cb_arm (Fl_Widget* v, void* p); + static void cb_mute (Fl_Widget* v, void* p); + static void cb_solo (Fl_Widget* v, void* p); + static void cb_changeVol (Fl_Widget* v, void* p); #ifdef WITH_VST - static void cb_openFxWindow(Fl_Widget *v, void *p); + static void cb_openFxWindow(Fl_Widget* v, void* p); #endif inline void __cb_mute(); @@ -99,7 +101,7 @@ protected: public: - geChannel(int x, int y, int w, int h, int type, Channel *ch); + geChannel(int x, int y, int w, int h, int type, Channel* ch); /* reset * reset channel to initial status. */ @@ -117,6 +119,11 @@ public: virtual void refresh() = 0; + /* changeSize + Changes channel's size according to a template (x1, x2, ...). */ + + virtual void changeSize(int h); + /* keypress * what to do when the corresponding key is pressed. */ @@ -128,17 +135,19 @@ public: int getColumnIndex(); - Channel *ch; - - geIdButton *button; - geChannelStatus *status; - geButton *arm; - geChannelButton *mainButton; - geButton *mute; - geButton *solo; - geDial *vol; + int getSize(); + + Channel* ch; + + geIdButton* button; + geChannelStatus* status; + geButton* arm; + geChannelButton* mainButton; + geButton* mute; + geButton* solo; + geDial* vol; #ifdef WITH_VST - geStatusButton *fx; + geStatusButton* fx; #endif int type; diff --git a/src/gui/elems/mainWindow/keyboard/column.cpp b/src/gui/elems/mainWindow/keyboard/column.cpp index 111ad44..18a88bc 100644 --- a/src/gui/elems/mainWindow/keyboard/column.cpp +++ b/src/gui/elems/mainWindow/keyboard/column.cpp @@ -25,9 +25,11 @@ * -------------------------------------------------------------------------- */ +#include #include #include #include "../../../../core/sampleChannel.h" +#include "../../../../core/midiChannel.h" #include "../../../../glue/channel.h" #include "../../../../utils/log.h" #include "../../../../utils/fs.h" @@ -42,29 +44,32 @@ using std::vector; +using std::string; -geColumn::geColumn(int X, int Y, int W, int H, int index, geKeyboard *parent) - : Fl_Group(X, Y, W, H), parent(parent), index(index) +geColumn::geColumn(int X, int Y, int W, int H, int index, geKeyboard* parent) + : Fl_Group(X, Y, W, H), + m_parent(parent), + m_index (index) { - /* geColumn does a bit of a mess: we pass a pointer to its parent (geKeyboard) and - the geColumn itself deals with the creation of another widget, outside geColumn - and inside geKeyboard, which handles the vertical resize bar (geResizerBar). - The resizer cannot stay inside geColumn: it needs a broader view on the other - side widgets. The view can be obtained from geKeyboard only (the upper level). - Unfortunately, parent() can be nullptr: at this point (i.e the constructor) - geColumn is still detached from any parent. We use a custom geKeyboard *parent - instead. */ + /* geColumn does a bit of a mess: we pass a pointer to its m_parent (geKeyboard) and + the geColumn itself deals with the creation of another widget, outside geColumn + and inside geKeyboard, which handles the vertical resize bar (geResizerBar). + The resizer cannot stay inside geColumn: it needs a broader view on the other + side widgets. The view can be obtained from geKeyboard only (the upper level). + Unfortunately, parent() can be nullptr: at this point (i.e the constructor) + geColumn is still detached from any parent. We use a custom geKeyboard *parent + instead. */ begin(); - addChannelBtn = new geButton(x(), y(), w(), 20, "Add new channel"); + m_addChannelBtn = new geButton(x(), y(), w(), G_GUI_UNIT, "Add new channel"); end(); - resizer = new geResizerBar(x()+w(), y(), 16, h(), false); - resizer->setMinSize(G_MIN_COLUMN_WIDTH); - parent->add(resizer); + m_resizer = new geResizerBar(x()+w(), y(), G_GUI_OUTER_MARGIN * 2, h(), + G_MIN_COLUMN_WIDTH, geResizerBar::HORIZONTAL); + m_parent->add(m_resizer); - addChannelBtn->callback(cb_addChannel, (void*)this); + m_addChannelBtn->callback(cb_addChannel, (void*)this); } @@ -73,10 +78,10 @@ geColumn::geColumn(int X, int Y, int W, int H, int index, geKeyboard *parent) geColumn::~geColumn() { - /* FIXME - this could actually cause a memory leak. resizer is - just removed, not deleted. But we cannot delete it right now. */ + /* FIXME - this could actually cause a memory leak. m_resizer is + just removed, not deleted. But we cannot delete it right now. */ - parent->remove(resizer); + m_parent->remove(m_resizer); } @@ -99,14 +104,15 @@ int geColumn::handle(int e) return 1; } case FL_PASTE: { // handle actual drop (paste) operation - vector paths; + vector paths; gu_split(Fl::event_text(), "\n", &paths); bool fails = false; int result = 0; - for (unsigned i=0; i(glue_addChannel( + m_index, CHANNEL_SAMPLE, G_GUI_CHANNEL_H_1)); + result = glue_loadChannel(c, gu_stripFileUrl(path)); if (result != G_RES_OK) { deleteChannel(c->guiChannel); fails = true; @@ -116,7 +122,7 @@ int geColumn::handle(int e) if (paths.size() > 1) gdAlert("Some files were not loaded successfully."); else - parent->printChannelMessage(result); + m_parent->printChannelMessage(result); } return 1; } @@ -134,21 +140,23 @@ int geColumn::handle(int e) void geColumn::resize(int X, int Y, int W, int H) { - /* resize all children */ + /* Resize all children, including "add channel" button. */ - int ch = children(); - for (int i=0; iresize(X, Y + (i * (c->h() + 4)), W, c->h()); - } + for (int i=0; iresize(X, (wgPrev == nullptr ? Y : wgPrev->y() + wgPrev->h() + G_GUI_INNER_MARGIN), + W, wgCurr->h()); + } - /* resize group itself */ + /* Resize group itself. Must use internal functions, resize() would trigger + infinite recursion. */ - x(X); y(Y); w(W); h(H); + x(X); y(Y); w(W); h(H); - /* resize resizerBar */ + /* Resize resizerBar. */ - resizer->size(16, H); + m_resizer->size(G_GUI_OUTER_MARGIN * 2, H); } @@ -158,7 +166,7 @@ void geColumn::resize(int X, int Y, int W, int H) void geColumn::refreshChannels() { for (int i=1; irefresh(); + static_cast(child(i))->refresh(); } @@ -167,41 +175,57 @@ void geColumn::refreshChannels() void geColumn::draw() { - fl_color(fl_rgb_color(27, 27, 27)); + fl_color(G_COLOR_GREY_1_5); fl_rectf(x(), y(), w(), h()); - /* call draw and then redraw in order to avoid channel corruption when - scrolling horizontally */ + /* call draw and then redraw in order to avoid channel corruption when + scrolling horizontally */ - for (int i=0; idraw(); - child(i)->redraw(); - } + for (int i=0; idraw(); + child(i)->redraw(); + } } /* -------------------------------------------------------------------------- */ -void geColumn::cb_addChannel(Fl_Widget *v, void *p) { ((geColumn*)p)->__cb_addChannel(); } +void geColumn::cb_addChannel(Fl_Widget* v, void* p) { ((geColumn*)p)->__cb_addChannel(); } + + +/* -------------------------------------------------------------------------- */ + + +void geColumn::repositionChannels() +{ + int totalH = 0; + for (int i=0; ih() + G_GUI_INNER_MARGIN; + resize(x(), y(), w(), totalH + 66); // evil space for drag n drop +} /* -------------------------------------------------------------------------- */ -geChannel *geColumn::addChannel(Channel *ch) +geChannel* geColumn::addChannel(Channel* ch, int size) { - int currentY = y() + children() * 24; - geChannel *gch = nullptr; + geChannel* gch = nullptr; + + /* All geChannels are added with y=0. That's not a problem, they will be + repositioned later on during geColumn::resize(). */ + if (ch->type == CHANNEL_SAMPLE) - gch = (geSampleChannel*) new geSampleChannel(x(), currentY, w(), 20, (SampleChannel*) ch); + gch = new geSampleChannel(x(), 0, w(), size, static_cast(ch)); else - gch = (geMidiChannel*) new geMidiChannel(x(), currentY, w(), 20, (MidiChannel*) ch); + gch = new geMidiChannel(x(), 0, w(), size, static_cast(ch)); add(gch); - resize(x(), y(), w(), (children() * 24) + 66); // evil space for drag n drop - gch->redraw(); // avoid corruption - parent->redraw(); // redraw Keyboard + + repositionChannels(); + gch->redraw(); // fix corruption + m_parent->redraw(); // redraw Keyboard return gch; } @@ -209,23 +233,17 @@ geChannel *geColumn::addChannel(Channel *ch) /* -------------------------------------------------------------------------- */ -void geColumn::deleteChannel(geChannel *gch) +void geColumn::deleteChannel(geChannel* gch) { gch->hide(); remove(gch); delete gch; - /* reposition all other channels and resize this group */ /** TODO * reposition is useless when called by geColumn::clear(). Add a new * parameter to skip the operation */ - for (int i=0; iposition(gch->x(), y()+(i*24)); - } - size(w(), children() * 24 + 66); // evil space for drag n drop - redraw(); + repositionChannels(); } @@ -234,10 +252,10 @@ void geColumn::deleteChannel(geChannel *gch) void geColumn::__cb_addChannel() { - gu_log("[geColumn::__cb_addChannel] index = %d\n", index); + gu_log("[geColumn::__cb_addChannel] m_index = %d\n", m_index); int type = openTypeMenu(); if (type) - glue_addChannel(index, type); + glue_addChannel(m_index, type, G_GUI_CHANNEL_H_1); } @@ -252,7 +270,7 @@ int geColumn::openTypeMenu() {0} }; - Fl_Menu_Button *b = new Fl_Menu_Button(0, 0, 100, 50); + Fl_Menu_Button* b = new Fl_Menu_Button(0, 0, 100, 50); b->box(G_CUSTOM_BORDER_BOX); b->textsize(G_GUI_FONT_SIZE_BASE); b->textcolor(G_COLOR_LIGHT_2); @@ -279,7 +297,7 @@ void geColumn::clear(bool full) else { while (children() >= 2) { // skip "add new channel" btn int i = children()-1; - deleteChannel((geChannel*)child(i)); + deleteChannel(static_cast(child(i))); } } } @@ -288,11 +306,16 @@ void geColumn::clear(bool full) /* -------------------------------------------------------------------------- */ -Channel *geColumn::getChannel(int i) +Channel* geColumn::getChannel(int i) { - geChannel *gch = (geChannel*) child(i); - if (gch->type == CHANNEL_SAMPLE) - return ((geSampleChannel*) child(i))->ch; - else - return ((geMidiChannel*) child(i))->ch; + return static_cast(child(i + 1))->ch; // Skip "add channel" } + + +/* -------------------------------------------------------------------------- */ + + +int geColumn::getIndex() { return m_index; } +void geColumn::setIndex(int i) { m_index = i; } +bool geColumn::isEmpty() { return children() == 1; } +int geColumn::countChannels() { return children() - 1; } \ No newline at end of file diff --git a/src/gui/elems/mainWindow/keyboard/column.h b/src/gui/elems/mainWindow/keyboard/column.h index 8c11528..b6ea1d4 100644 --- a/src/gui/elems/mainWindow/keyboard/column.h +++ b/src/gui/elems/mainWindow/keyboard/column.h @@ -43,64 +43,55 @@ class geColumn : public Fl_Group { private: - static void cb_addChannel (Fl_Widget *v, void *p); + static void cb_addChannel (Fl_Widget* v, void* p); inline void __cb_addChannel(); int openTypeMenu(); - geButton *addChannelBtn; - geResizerBar *resizer; - geKeyboard *parent; + geButton* m_addChannelBtn; + geResizerBar* m_resizer; + geKeyboard* m_parent; - int index; + int m_index; public: - geColumn(int x, int y, int w, int h, int index, geKeyboard *parent); + geColumn(int x, int y, int w, int h, int index, geKeyboard* parent); ~geColumn(); /* addChannel - * add a new channel in this column and set the internal pointer - * to channel to 'ch'. */ + Adds a new channel in this column and set the internal pointer to channel + to 'ch'. */ - geChannel *addChannel(Channel *ch); + geChannel* addChannel(Channel* ch, int size); - /* handle */ + int handle(int e) override; + void draw() override; + void resize(int x, int y, int w, int h) override; - int handle(int e); - - /* resize - * custom resize behavior. */ + /* clear + Removes all channels from the column. If full==true, delete also the "add new + channel" button. */ - void resize(int x, int y, int w, int h); + void clear(bool full=false); /* deleteChannel - * remove the channel 'gch' from this column. */ + Removes the channel 'gch' from this column. */ - void deleteChannel(geChannel *gch); + void deleteChannel(geChannel* gch); + + void repositionChannels(); /* refreshChannels - * update channels' graphical statues. Called on each GUI cycle. */ + Updates channels' graphical statues. Called on each GUI cycle. */ void refreshChannels(); - /* getChannel */ - - Channel *getChannel(int i); - - /* clear - * remove all channels from the column. If full==true, delete also the - * "add new channel" button. This method ovverrides the inherited one - * from Fl_Group. */ - - void clear(bool full=false); - - void draw(); - - inline int getIndex() { return index; } - inline void setIndex(int i) { index = i; } - inline bool isEmpty() { return children() == 1; } - inline int countChannels() { return children(); } + Channel* getChannel(int i); + int getIndex(); + void setIndex(int i); + bool isEmpty(); + int countChannels(); }; diff --git a/src/gui/elems/mainWindow/keyboard/keyboard.cpp b/src/gui/elems/mainWindow/keyboard/keyboard.cpp index f76b216..e5e0f3c 100644 --- a/src/gui/elems/mainWindow/keyboard/keyboard.cpp +++ b/src/gui/elems/mainWindow/keyboard/keyboard.cpp @@ -165,7 +165,7 @@ void geKeyboard::cb_addColumn(Fl_Widget* v, void* p) /* -------------------------------------------------------------------------- */ -geChannel* geKeyboard::addChannel(int colIndex, Channel* ch, bool build) +geChannel* geKeyboard::addChannel(int colIndex, Channel* ch, int size, bool build) { geColumn* col = getColumnByIndex(colIndex); @@ -179,8 +179,9 @@ geChannel* geKeyboard::addChannel(int colIndex, Channel* ch, bool build) gu_log("[geKeyboard::addChannel] created new column with index=%d\n", colIndex); } - gu_log("[geKeyboard::addChannel] add to column with index = %d\n", col->getIndex()); - return col->addChannel(ch); + gu_log("[geKeyboard::addChannel] add to column with index=%d, size=%d\n", + col->getIndex(), size); + return col->addChannel(ch, size); } @@ -313,6 +314,8 @@ void geKeyboard::printChannelMessage(int res) gdAlert("Unable to read this sample."); else if (res == G_RES_ERR_PATH_TOO_LONG) gdAlert("File path too long."); + else if (res == G_RES_ERR_NO_DATA) + gdAlert("No file specified."); else gdAlert("Unknown error."); } diff --git a/src/gui/elems/mainWindow/keyboard/keyboard.h b/src/gui/elems/mainWindow/keyboard/keyboard.h index 89c3b4e..432364e 100644 --- a/src/gui/elems/mainWindow/keyboard/keyboard.h +++ b/src/gui/elems/mainWindow/keyboard/keyboard.h @@ -85,12 +85,11 @@ public: void init(); /* addChannel - * 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. */ + Adds 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. */ - geChannel* addChannel(int column, Channel* ch, bool build=false); + geChannel* addChannel(int column, Channel* ch, int size, bool build=false); /* addColumn * add a new column to the top of the stack. */ diff --git a/src/gui/elems/mainWindow/keyboard/midiChannel.cpp b/src/gui/elems/mainWindow/keyboard/midiChannel.cpp index 2261bcd..64e9b9d 100644 --- a/src/gui/elems/mainWindow/keyboard/midiChannel.cpp +++ b/src/gui/elems/mainWindow/keyboard/midiChannel.cpp @@ -4,7 +4,7 @@ * * ----------------------------------------------------------------------------- * - * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual + * Copyright (C) G_GUI_UNIT10-G_GUI_UNIT17 Giovanni A. Zuliani | Monocasual * * This file is part of Giada - Your Hardcore Loopmachine. * @@ -45,6 +45,7 @@ #include "../../basics/idButton.h" #include "../../basics/statusButton.h" #include "../../basics/dial.h" +#include "column.h" #include "midiChannel.h" @@ -58,15 +59,21 @@ namespace { enum class Menu { - EDIT_ACTIONS = 0, - CLEAR_ACTIONS, - CLEAR_ACTIONS_ALL, - __END_SUBMENU__, - SETUP_KEYBOARD_INPUT, - SETUP_MIDI_INPUT, - SETUP_MIDI_OUTPUT, - CLONE_CHANNEL, - DELETE_CHANNEL + EDIT_ACTIONS = 0, + CLEAR_ACTIONS, + CLEAR_ACTIONS_ALL, + __END_CLEAR_ACTION_SUBMENU__, + SETUP_KEYBOARD_INPUT, + SETUP_MIDI_INPUT, + SETUP_MIDI_OUTPUT, + RESIZE, + RESIZE_H1, + RESIZE_H2, + RESIZE_H3, + RESIZE_H4, + __END_RESIZE_SUBMENU__, + CLONE_CHANNEL, + DELETE_CHANNEL }; @@ -75,37 +82,55 @@ enum class Menu void menuCallback(Fl_Widget *w, void *v) { - geMidiChannel *gch = static_cast(w); - Menu selectedItem = (Menu) (intptr_t) v; - - switch (selectedItem) - { - case Menu::CLEAR_ACTIONS: - case Menu::__END_SUBMENU__: - break; - case Menu::EDIT_ACTIONS: - gu_openSubWindow(G_MainWin, new gdActionEditor(gch->ch), WID_ACTION_EDITOR); - break; - case Menu::CLEAR_ACTIONS_ALL: - glue_clearAllActions(gch); - break; - case Menu::SETUP_KEYBOARD_INPUT: - gu_openSubWindow(G_MainWin, new gdKeyGrabber(gch->ch), 0); - break; - case Menu::SETUP_MIDI_INPUT: - gu_openSubWindow(G_MainWin, new gdMidiInputChannel(gch->ch), 0); - break; - case Menu::SETUP_MIDI_OUTPUT: - gu_openSubWindow(G_MainWin, - new gdMidiOutputMidiCh(static_cast(gch->ch)), 0); - break; - case Menu::CLONE_CHANNEL: - glue_cloneChannel(gch->ch); - break; - case Menu::DELETE_CHANNEL: - glue_deleteChannel(gch->ch); - break; - } + geMidiChannel *gch = static_cast(w); + Menu selectedItem = (Menu) (intptr_t) v; + + switch (selectedItem) + { + case Menu::CLEAR_ACTIONS: + case Menu::__END_CLEAR_ACTION_SUBMENU__: + case Menu::RESIZE: + case Menu::__END_RESIZE_SUBMENU__: + break; + case Menu::EDIT_ACTIONS: + gu_openSubWindow(G_MainWin, new gdActionEditor(gch->ch), WID_ACTION_EDITOR); + break; + case Menu::CLEAR_ACTIONS_ALL: + glue_clearAllActions(gch); + break; + case Menu::SETUP_KEYBOARD_INPUT: + gu_openSubWindow(G_MainWin, new gdKeyGrabber(gch->ch), 0); + break; + case Menu::SETUP_MIDI_INPUT: + gu_openSubWindow(G_MainWin, new gdMidiInputChannel(gch->ch), 0); + break; + case Menu::SETUP_MIDI_OUTPUT: + gu_openSubWindow(G_MainWin, + new gdMidiOutputMidiCh(static_cast(gch->ch)), 0); + break; + case Menu::RESIZE_H1: + gch->changeSize(G_GUI_CHANNEL_H_1); + static_cast(gch->parent())->repositionChannels(); + break; + case Menu::RESIZE_H2: + gch->changeSize(G_GUI_CHANNEL_H_2); + static_cast(gch->parent())->repositionChannels(); + break; + case Menu::RESIZE_H3: + gch->changeSize(G_GUI_CHANNEL_H_3); + static_cast(gch->parent())->repositionChannels(); + break; + case Menu::RESIZE_H4: + gch->changeSize(G_GUI_CHANNEL_H_4); + static_cast(gch->parent())->repositionChannels(); + break; + case Menu::CLONE_CHANNEL: + glue_cloneChannel(gch->ch); + break; + case Menu::DELETE_CHANNEL: + glue_deleteChannel(gch->ch); + break; + } } }; // {namespace} @@ -120,26 +145,26 @@ geMidiChannel::geMidiChannel(int X, int Y, int W, int H, MidiChannel *ch) begin(); #if defined(WITH_VST) - int delta = 144; // (6 widgets * 20) + (6 paddings * 4) + int delta = 144; // (6 widgets * G_GUI_UNIT) + (6 paddings * 4) #else - int delta = 120; // (5 widgets * 20) + (5 paddings * 4) + int delta = 120; // (5 widgets * G_GUI_UNIT) + (5 paddings * 4) #endif - button = new geIdButton(x(), y(), 20, 20, "", channelStop_xpm, channelPlay_xpm); - arm = new geButton(button->x()+button->w()+4, y(), 20, 20, "", armOff_xpm, armOn_xpm); - mainButton = new geMidiChannelButton(arm->x()+arm->w()+4, y(), w() - delta, 20, "-- MIDI --"); - mute = new geButton(mainButton->x()+mainButton->w()+4, y(), 20, 20, "", muteOff_xpm, muteOn_xpm); - solo = new geButton(mute->x()+mute->w()+4, y(), 20, 20, "", soloOff_xpm, soloOn_xpm); + button = new geIdButton(x(), y(), G_GUI_UNIT, G_GUI_UNIT, "", channelStop_xpm, channelPlay_xpm); + arm = new geButton(button->x()+button->w()+4, y(), G_GUI_UNIT, G_GUI_UNIT, "", armOff_xpm, armOn_xpm); + mainButton = new geMidiChannelButton(arm->x()+arm->w()+4, y(), w() - delta, H, "-- MIDI --"); + mute = new geButton(mainButton->x()+mainButton->w()+4, y(), G_GUI_UNIT, G_GUI_UNIT, "", muteOff_xpm, muteOn_xpm); + solo = new geButton(mute->x()+mute->w()+4, y(), G_GUI_UNIT, G_GUI_UNIT, "", soloOff_xpm, soloOn_xpm); #if defined(WITH_VST) - fx = new geStatusButton(solo->x()+solo->w()+4, y(), 20, 20, fxOff_xpm, fxOn_xpm); - vol = new geDial(fx->x()+fx->w()+4, y(), 20, 20); + fx = new geStatusButton(solo->x()+solo->w()+4, y(), G_GUI_UNIT, G_GUI_UNIT, fxOff_xpm, fxOn_xpm); + vol = new geDial(fx->x()+fx->w()+4, y(), G_GUI_UNIT, G_GUI_UNIT); #else - vol = new geDial(solo->x()+solo->w()+4, y(), 20, 20); + vol = new geDial(solo->x()+solo->w()+4, y(), G_GUI_UNIT, G_GUI_UNIT); #endif end(); - resizable(mainButton); + resizable(mainButton); update(); @@ -164,6 +189,8 @@ geMidiChannel::geMidiChannel(int X, int Y, int W, int H, MidiChannel *ch) vol->callback(cb_changeVol, (void*)this); ch->guiChannel = this; + + changeSize(H); // Update size dynamically } @@ -197,8 +224,14 @@ void geMidiChannel::__cb_openMenu() {"Setup keyboard input...", 0, menuCallback, (void*) Menu::SETUP_KEYBOARD_INPUT}, {"Setup MIDI input...", 0, menuCallback, (void*) Menu::SETUP_MIDI_INPUT}, {"Setup MIDI output...", 0, menuCallback, (void*) Menu::SETUP_MIDI_OUTPUT}, - {"Clone channel", 0, menuCallback, (void*) Menu::CLONE_CHANNEL}, - {"Delete channel", 0, menuCallback, (void*) Menu::DELETE_CHANNEL}, + {"Resize", 0, menuCallback, (void*) Menu::RESIZE, FL_SUBMENU}, + {"Normal", 0, menuCallback, (void*) Menu::RESIZE_H1}, + {"Medium", 0, menuCallback, (void*) Menu::RESIZE_H2}, + {"Large", 0, menuCallback, (void*) Menu::RESIZE_H3}, + {"X-Large", 0, menuCallback, (void*) Menu::RESIZE_H4}, + {0}, + {"Clone channel", 0, menuCallback, (void*) Menu::CLONE_CHANNEL}, + {"Delete channel", 0, menuCallback, (void*) Menu::DELETE_CHANNEL}, {0} }; @@ -213,10 +246,10 @@ void geMidiChannel::__cb_openMenu() b->textcolor(G_COLOR_LIGHT_2); b->color(G_COLOR_GREY_2); - const Fl_Menu_Item *m = rclick_menu->popup(Fl::event_x(), Fl::event_y(), 0, 0, b); - if (m) - m->do_callback(this, m->user_data()); - return; + const Fl_Menu_Item *m = rclick_menu->popup(Fl::event_x(), Fl::event_y(), 0, 0, b); + if (m) + m->do_callback(this, m->user_data()); + return; } @@ -271,7 +304,7 @@ void geMidiChannel::update() void geMidiChannel::resize(int X, int Y, int W, int H) { - geChannel::resize(X, Y, W, H); + geChannel::resize(X, Y, W, H); arm->hide(); #ifdef WITH_VST diff --git a/src/gui/elems/mainWindow/keyboard/midiChannel.h b/src/gui/elems/mainWindow/keyboard/midiChannel.h index 25de771..8ed63f0 100644 --- a/src/gui/elems/mainWindow/keyboard/midiChannel.h +++ b/src/gui/elems/mainWindow/keyboard/midiChannel.h @@ -51,11 +51,13 @@ public: geMidiChannel(int x, int y, int w, int h, 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); + void resize(int x, int y, int w, int h) override; + + void reset() override; + void update() override; + void refresh() override; + + int keyPress(int event); // TODO - move to base class }; diff --git a/src/gui/elems/mainWindow/keyboard/sampleChannel.cpp b/src/gui/elems/mainWindow/keyboard/sampleChannel.cpp index b22dcc1..1595097 100644 --- a/src/gui/elems/mainWindow/keyboard/sampleChannel.cpp +++ b/src/gui/elems/mainWindow/keyboard/sampleChannel.cpp @@ -53,10 +53,11 @@ #include "channelMode.h" #include "sampleChannelButton.h" #include "keyboard.h" +#include "column.h" #include "sampleChannel.h" -extern gdMainWindow *G_MainWin; +extern gdMainWindow* G_MainWin; using namespace giada::m; @@ -66,105 +67,133 @@ namespace { enum class Menu { - INPUT_MONITOR = 0, - LOAD_SAMPLE, - EXPORT_SAMPLE, - SETUP_KEYBOARD_INPUT, - SETUP_MIDI_INPUT, - SETUP_MIDI_OUTPUT, - EDIT_SAMPLE, - EDIT_ACTIONS, - CLEAR_ACTIONS, - CLEAR_ACTIONS_ALL, - CLEAR_ACTIONS_MUTE, - CLEAR_ACTIONS_VOLUME, - CLEAR_ACTIONS_START_STOP, - __END_SUBMENU__, - CLONE_CHANNEL, - FREE_CHANNEL, - DELETE_CHANNEL + INPUT_MONITOR = 0, + LOAD_SAMPLE, + EXPORT_SAMPLE, + SETUP_KEYBOARD_INPUT, + SETUP_MIDI_INPUT, + SETUP_MIDI_OUTPUT, + EDIT_SAMPLE, + EDIT_ACTIONS, + CLEAR_ACTIONS, + CLEAR_ACTIONS_ALL, + CLEAR_ACTIONS_MUTE, + CLEAR_ACTIONS_VOLUME, + CLEAR_ACTIONS_START_STOP, + __END_CLEAR_ACTIONS_SUBMENU__, + RESIZE, + RESIZE_H1, + RESIZE_H2, + RESIZE_H3, + RESIZE_H4, + __END_RESIZE_SUBMENU__, + CLONE_CHANNEL, + FREE_CHANNEL, + DELETE_CHANNEL }; /* -------------------------------------------------------------------------- */ -void menuCallback(Fl_Widget *w, void *v) +void menuCallback(Fl_Widget* w, void* v) { - geSampleChannel *gch = static_cast(w); - Menu selectedItem = (Menu) (intptr_t) v; - - switch (selectedItem) { - case Menu::INPUT_MONITOR: { - glue_toggleInputMonitor(gch->ch); - break; - } - case Menu::LOAD_SAMPLE: { - gdWindow *w = new gdBrowserLoad(conf::browserX, conf::browserY, - conf::browserW, conf::browserH, "Browse sample", - conf::samplePath.c_str(), glue_loadSample, gch->ch); - gu_openSubWindow(G_MainWin, w, WID_FILE_BROWSER); - break; - } - case Menu::EXPORT_SAMPLE: { - gdWindow *w = new gdBrowserSave(conf::browserX, conf::browserY, - conf::browserW, conf::browserH, "Save sample", - conf::samplePath.c_str(), "", glue_saveSample, gch->ch); - gu_openSubWindow(G_MainWin, w, WID_FILE_BROWSER); - break; - } - case Menu::SETUP_KEYBOARD_INPUT: { - new gdKeyGrabber(gch->ch); // FIXME - use gu_openSubWindow - break; - } - case Menu::SETUP_MIDI_INPUT: { - gu_openSubWindow(G_MainWin, new gdMidiInputChannel(gch->ch), 0); - break; - } - case Menu::SETUP_MIDI_OUTPUT: { - gu_openSubWindow(G_MainWin, new gdMidiOutputSampleCh(static_cast(gch->ch)), 0); - break; - } - case Menu::EDIT_SAMPLE: { - gu_openSubWindow(G_MainWin, new gdSampleEditor(static_cast(gch->ch)), WID_SAMPLE_EDITOR); - break; - } - case Menu::EDIT_ACTIONS: { - gu_openSubWindow(G_MainWin, new gdActionEditor(gch->ch), WID_ACTION_EDITOR); - break; - } - case Menu::CLEAR_ACTIONS: - case Menu::__END_SUBMENU__: - break; - case Menu::CLEAR_ACTIONS_ALL: { - glue_clearAllActions(gch); - break; - } - case Menu::CLEAR_ACTIONS_MUTE: { - glue_clearMuteActions(gch); - break; - } - case Menu::CLEAR_ACTIONS_VOLUME: { - glue_clearVolumeActions(gch); - break; - } - case Menu::CLEAR_ACTIONS_START_STOP: { - glue_clearStartStopActions(gch); - break; - } - case Menu::CLONE_CHANNEL: { - glue_cloneChannel(gch->ch); - break; - } - case Menu::FREE_CHANNEL: { - glue_freeChannel(gch->ch); - break; - } - case Menu::DELETE_CHANNEL: { - glue_deleteChannel(gch->ch); - break; - } - } + geSampleChannel* gch = static_cast(w); + Menu selectedItem = (Menu) (intptr_t) v; + + switch (selectedItem) { + case Menu::INPUT_MONITOR: { + glue_toggleInputMonitor(gch->ch); + break; + } + case Menu::LOAD_SAMPLE: { + gdWindow *w = new gdBrowserLoad(conf::browserX, conf::browserY, + conf::browserW, conf::browserH, "Browse sample", + conf::samplePath.c_str(), glue_loadSample, gch->ch); + gu_openSubWindow(G_MainWin, w, WID_FILE_BROWSER); + break; + } + case Menu::EXPORT_SAMPLE: { + gdWindow *w = new gdBrowserSave(conf::browserX, conf::browserY, + conf::browserW, conf::browserH, "Save sample", + conf::samplePath.c_str(), "", glue_saveSample, gch->ch); + gu_openSubWindow(G_MainWin, w, WID_FILE_BROWSER); + break; + } + case Menu::SETUP_KEYBOARD_INPUT: { + new gdKeyGrabber(gch->ch); // FIXME - use gu_openSubWindow + break; + } + case Menu::SETUP_MIDI_INPUT: { + gu_openSubWindow(G_MainWin, new gdMidiInputChannel(gch->ch), 0); + break; + } + case Menu::SETUP_MIDI_OUTPUT: { + gu_openSubWindow(G_MainWin, new gdMidiOutputSampleCh(static_cast(gch->ch)), 0); + break; + } + case Menu::EDIT_SAMPLE: { + gu_openSubWindow(G_MainWin, new gdSampleEditor(static_cast(gch->ch)), WID_SAMPLE_EDITOR); + break; + } + case Menu::EDIT_ACTIONS: { + gu_openSubWindow(G_MainWin, new gdActionEditor(gch->ch), WID_ACTION_EDITOR); + break; + } + case Menu::CLEAR_ACTIONS: + case Menu::RESIZE: + case Menu::__END_CLEAR_ACTIONS_SUBMENU__: + case Menu::__END_RESIZE_SUBMENU__: + break; + case Menu::CLEAR_ACTIONS_ALL: { + glue_clearAllActions(gch); + break; + } + case Menu::CLEAR_ACTIONS_MUTE: { + glue_clearMuteActions(gch); + break; + } + case Menu::CLEAR_ACTIONS_VOLUME: { + glue_clearVolumeActions(gch); + break; + } + case Menu::CLEAR_ACTIONS_START_STOP: { + glue_clearStartStopActions(gch); + break; + } + case Menu::RESIZE_H1: { + gch->changeSize(G_GUI_CHANNEL_H_1); + static_cast(gch->parent())->repositionChannels(); + break; + } + case Menu::RESIZE_H2: { + gch->changeSize(G_GUI_CHANNEL_H_2); + static_cast(gch->parent())->repositionChannels(); + break; + } + case Menu::RESIZE_H3: { + gch->changeSize(G_GUI_CHANNEL_H_3); + static_cast(gch->parent())->repositionChannels(); + break; + } + case Menu::RESIZE_H4: { + gch->changeSize(G_GUI_CHANNEL_H_4); + static_cast(gch->parent())->repositionChannels(); + break; + } + case Menu::CLONE_CHANNEL: { + glue_cloneChannel(gch->ch); + break; + } + case Menu::FREE_CHANNEL: { + glue_freeChannel(gch->ch); + break; + } + case Menu::DELETE_CHANNEL: { + glue_deleteChannel(gch->ch); + break; + } + } } }; // {namespace} @@ -173,29 +202,29 @@ void menuCallback(Fl_Widget *w, void *v) /* -------------------------------------------------------------------------- */ -geSampleChannel::geSampleChannel(int X, int Y, int W, int H, SampleChannel *ch) - : geChannel(X, Y, W, H, CHANNEL_SAMPLE, (Channel*) ch) +geSampleChannel::geSampleChannel(int X, int Y, int W, int H, SampleChannel* ch) + : geChannel(X, Y, W, H, CHANNEL_SAMPLE, ch) { begin(); - button = new geIdButton(x(), y(), 20, 20, "", channelStop_xpm, channelPlay_xpm); - arm = new geButton(button->x()+button->w()+4, y(), 20, 20, "", armOff_xpm, armOn_xpm); - status = new geChannelStatus(arm->x()+arm->w()+4, y(), 20, 20, ch); - mainButton = new geSampleChannelButton(status->x()+status->w()+4, y(), 20, 20, "-- no sample --"); - readActions = new geButton(mainButton->x()+mainButton->w()+4, y(), 20, 20, "", readActionOff_xpm, readActionOn_xpm); - modeBox = new geChannelMode(readActions->x()+readActions->w()+4, y(), 20, 20, ch); - mute = new geButton(modeBox->x()+modeBox->w()+4, y(), 20, 20, "", muteOff_xpm, muteOn_xpm); - solo = new geButton(mute->x()+mute->w()+4, y(), 20, 20, "", soloOff_xpm, soloOn_xpm); + button = new geIdButton(x(), y(), G_GUI_UNIT, G_GUI_UNIT, "", channelStop_xpm, channelPlay_xpm); + arm = new geButton(button->x()+button->w()+4, y(), G_GUI_UNIT, G_GUI_UNIT, "", armOff_xpm, armOn_xpm); + status = new geChannelStatus(arm->x()+arm->w()+4, y(), G_GUI_UNIT, H, ch); + mainButton = new geSampleChannelButton(status->x()+status->w()+4, y(), G_GUI_UNIT, H, "-- no sample --"); + readActions = new geButton(mainButton->x()+mainButton->w()+4, y(), G_GUI_UNIT, G_GUI_UNIT, "", readActionOff_xpm, readActionOn_xpm); + modeBox = new geChannelMode(readActions->x()+readActions->w()+4, y(), G_GUI_UNIT, G_GUI_UNIT, ch); + mute = new geButton(modeBox->x()+modeBox->w()+4, y(), G_GUI_UNIT, G_GUI_UNIT, "", muteOff_xpm, muteOn_xpm); + solo = new geButton(mute->x()+mute->w()+4, y(), G_GUI_UNIT, G_GUI_UNIT, "", soloOff_xpm, soloOn_xpm); #ifdef WITH_VST - fx = new geStatusButton(solo->x()+solo->w()+4, y(), 20, 20, fxOff_xpm, fxOn_xpm); - vol = new geDial(fx->x()+fx->w()+4, y(), 20, 20); + fx = new geStatusButton(solo->x()+solo->w()+4, y(), G_GUI_UNIT, G_GUI_UNIT, fxOff_xpm, fxOn_xpm); + vol = new geDial(fx->x()+fx->w()+4, y(), G_GUI_UNIT, G_GUI_UNIT); #else - vol = new geDial(solo->x()+solo->w()+4, y(), 20, 20); + vol = new geDial(solo->x()+solo->w()+4, y(), G_GUI_UNIT, G_GUI_UNIT); #endif end(); - resizable(mainButton); + resizable(mainButton); update(); @@ -224,15 +253,17 @@ geSampleChannel::geSampleChannel(int X, int Y, int W, int H, SampleChannel *ch) vol->callback(cb_changeVol, (void*)this); ch->guiChannel = this; + + changeSize(H); // Update size dynamically } /* -------------------------------------------------------------------------- */ -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 (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(); } /* -------------------------------------------------------------------------- */ @@ -260,7 +291,7 @@ void geSampleChannel::__cb_openMenu() Fl_Menu_Item rclick_menu[] = { {"Input monitor", 0, menuCallback, (void*) Menu::INPUT_MONITOR, - FL_MENU_TOGGLE | FL_MENU_DIVIDER | (static_cast(ch)->inputMonitor ? FL_MENU_VALUE : 0)}, + FL_MENU_TOGGLE | FL_MENU_DIVIDER | (static_cast(ch)->inputMonitor ? FL_MENU_VALUE : 0)}, {"Load new sample...", 0, menuCallback, (void*) Menu::LOAD_SAMPLE}, {"Export sample to file...", 0, menuCallback, (void*) Menu::EXPORT_SAMPLE}, {"Setup keyboard input...", 0, menuCallback, (void*) Menu::SETUP_KEYBOARD_INPUT}, @@ -274,6 +305,12 @@ void geSampleChannel::__cb_openMenu() {"Volume", 0, menuCallback, (void*) Menu::CLEAR_ACTIONS_VOLUME}, {"Start/Stop", 0, menuCallback, (void*) Menu::CLEAR_ACTIONS_START_STOP}, {0}, + {"Resize", 0, menuCallback, (void*) Menu::RESIZE, FL_SUBMENU}, + {"Normal", 0, menuCallback, (void*) Menu::RESIZE_H1}, + {"Medium", 0, menuCallback, (void*) Menu::RESIZE_H2}, + {"Large", 0, menuCallback, (void*) Menu::RESIZE_H3}, + {"X-Large", 0, menuCallback, (void*) Menu::RESIZE_H4}, + {0}, {"Clone channel", 0, menuCallback, (void*) Menu::CLONE_CHANNEL}, {"Free channel", 0, menuCallback, (void*) Menu::FREE_CHANNEL}, {"Delete channel", 0, menuCallback, (void*) Menu::DELETE_CHANNEL}, @@ -290,21 +327,21 @@ void geSampleChannel::__cb_openMenu() rclick_menu[(int) Menu::CLEAR_ACTIONS].deactivate(); /* No 'clear start/stop actions' for those channels in loop mode: they cannot - have start/stop actions. */ + have start/stop actions. */ if (static_cast(ch)->mode & LOOP_ANY) rclick_menu[(int) Menu::CLEAR_ACTIONS_START_STOP].deactivate(); - Fl_Menu_Button *b = new Fl_Menu_Button(0, 0, 100, 50); + Fl_Menu_Button* b = new Fl_Menu_Button(0, 0, 100, 50); b->box(G_CUSTOM_BORDER_BOX); b->textsize(G_GUI_FONT_SIZE_BASE); b->textcolor(G_COLOR_LIGHT_2); b->color(G_COLOR_GREY_2); - const Fl_Menu_Item *m = rclick_menu->popup(Fl::event_x(), Fl::event_y(), 0, 0, b); - if (m) - m->do_callback(this, m->user_data()); - return; + const Fl_Menu_Item* m = rclick_menu->popup(Fl::event_x(), Fl::event_y(), 0, 0, b); + if (m) + m->do_callback(this, m->user_data()); + return; } @@ -313,7 +350,7 @@ void geSampleChannel::__cb_openMenu() void geSampleChannel::__cb_readActions() { - glue_startStopReadingRecs((SampleChannel*) ch); + glue_startStopReadingRecs(static_cast(ch)); } @@ -322,12 +359,12 @@ void geSampleChannel::__cb_readActions() void geSampleChannel::refresh() { - if (!mainButton->visible()) // mainButton invisible? status too (see below) - return; + if (!mainButton->visible()) // mainButton invisible? status too (see below) + return; setColorsByStatus(ch->status, ch->recStatus); - if (((SampleChannel*) ch)->wave != nullptr) { + if (static_cast(ch)->wave != nullptr) { if (mixer::recording && ch->armed) mainButton->setInputRecordMode(); if (recorder::active) { @@ -347,7 +384,7 @@ void geSampleChannel::reset() { hideActionButton(); mainButton->setDefaultMode("-- no sample --"); - mainButton->redraw(); + mainButton->redraw(); status->redraw(); } @@ -368,7 +405,7 @@ void geSampleChannel::update() mainButton->label("* file not found! *"); break; default: - mainButton->label(((SampleChannel*) ch)->wave->getName().c_str()); + mainButton->label(static_cast(ch)->wave->getName().c_str()); break; } @@ -383,7 +420,7 @@ void geSampleChannel::update() /* updates modebox */ - modeBox->value(((SampleChannel*) ch)->mode); + modeBox->value(static_cast(ch)->mode); modeBox->redraw(); /* update volumes+mute+solo */ @@ -406,7 +443,7 @@ void geSampleChannel::update() void geSampleChannel::showActionButton() { - readActions->value(((SampleChannel*) ch)->readActions); + readActions->value(static_cast(ch)->readActions); readActions->show(); packWidgets(); redraw(); @@ -429,7 +466,7 @@ void geSampleChannel::hideActionButton() void geSampleChannel::resize(int X, int Y, int W, int H) { - geChannel::resize(X, Y, W, H); + geChannel::resize(X, Y, W, H); arm->hide(); modeBox->hide(); @@ -451,3 +488,15 @@ void geSampleChannel::resize(int X, int Y, int W, int H) packWidgets(); } + + +void geSampleChannel::changeSize(int H) +{ + geChannel::changeSize(H); + + int Y = y() + (H / 2 - (G_GUI_UNIT / 2)); + + status->resize(x(), Y, w(), G_GUI_UNIT); + modeBox->resize(x(), Y, w(), G_GUI_UNIT); + readActions->resize(x(), Y, w(), G_GUI_UNIT); +} \ No newline at end of file diff --git a/src/gui/elems/mainWindow/keyboard/sampleChannel.h b/src/gui/elems/mainWindow/keyboard/sampleChannel.h index 875682d..0e070bc 100644 --- a/src/gui/elems/mainWindow/keyboard/sampleChannel.h +++ b/src/gui/elems/mainWindow/keyboard/sampleChannel.h @@ -41,22 +41,24 @@ 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 (); + 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); + void __cb_button (); + void __cb_openMenu (); + void __cb_readActions(); public: - geSampleChannel(int x, int y, int w, int h, SampleChannel *ch); + geSampleChannel(int x, int y, int w, int h, SampleChannel* ch); + + void resize(int x, int y, int w, int h) override; + + void reset() override; + void update() override; + void refresh() override; - void reset (); - void update (); - void refresh (); - void resize (int x, int y, int w, int h); + void changeSize(int h); /* show/hideActionButton Adds or removes 'R' button when actions are available. */ @@ -64,8 +66,8 @@ public: void showActionButton(); void hideActionButton(); - geChannelMode *modeBox; - geButton *readActions; + geChannelMode* modeBox; + geButton* readActions; }; diff --git a/src/gui/elems/mainWindow/keyboard/sampleChannelButton.cpp b/src/gui/elems/mainWindow/keyboard/sampleChannelButton.cpp index 66f8867..a2a9c3f 100644 --- a/src/gui/elems/mainWindow/keyboard/sampleChannelButton.cpp +++ b/src/gui/elems/mainWindow/keyboard/sampleChannelButton.cpp @@ -41,7 +41,7 @@ extern gdMainWindow *G_MainWin; geSampleChannelButton::geSampleChannelButton(int x, int y, int w, int h, - const char *l) + const char *l) : geChannelButton(x, y, w, h, l) { } @@ -61,9 +61,9 @@ int geSampleChannelButton::handle(int e) break; } case FL_PASTE: { - geSampleChannel *gch = static_cast(parent()); - SampleChannel *ch = static_cast(gch->ch); - int result = glue_loadChannel(ch, gu_trim(gu_stripFileUrl(Fl::event_text())).c_str()); + geSampleChannel *gch = static_cast(parent()); + SampleChannel *ch = static_cast(gch->ch); + int result = glue_loadChannel(ch, gu_trim(gu_stripFileUrl(Fl::event_text())).c_str()); if (result != G_RES_OK) G_MainWin->keyboard->printChannelMessage(result); ret = 1; diff --git a/src/gui/elems/sampleEditor/pitchTool.cpp b/src/gui/elems/sampleEditor/pitchTool.cpp index 84da01e..80e2319 100644 --- a/src/gui/elems/sampleEditor/pitchTool.cpp +++ b/src/gui/elems/sampleEditor/pitchTool.cpp @@ -43,7 +43,7 @@ using namespace giada::m; -gePitchTool::gePitchTool(int x, int y, SampleChannel *ch) +gePitchTool::gePitchTool(int x, int y, SampleChannel* ch) : Fl_Group(x, y, 600, 20), ch (ch) { @@ -91,13 +91,13 @@ void gePitchTool::refresh() /* -------------------------------------------------------------------------- */ -void gePitchTool::cb_setPitch (Fl_Widget *w, void *p) { ((gePitchTool*)p)->__cb_setPitch(); } -void gePitchTool::cb_setPitchToBar (Fl_Widget *w, void *p) { ((gePitchTool*)p)->__cb_setPitchToBar(); } -void gePitchTool::cb_setPitchToSong(Fl_Widget *w, void *p) { ((gePitchTool*)p)->__cb_setPitchToSong(); } -void gePitchTool::cb_setPitchHalf (Fl_Widget *w, void *p) { ((gePitchTool*)p)->__cb_setPitchHalf(); } -void gePitchTool::cb_setPitchDouble(Fl_Widget *w, void *p) { ((gePitchTool*)p)->__cb_setPitchDouble(); } -void gePitchTool::cb_resetPitch (Fl_Widget *w, void *p) { ((gePitchTool*)p)->__cb_resetPitch(); } -void gePitchTool::cb_setPitchNum (Fl_Widget *w, void *p) { ((gePitchTool*)p)->__cb_setPitchNum(); } +void gePitchTool::cb_setPitch (Fl_Widget* w, void* p) { ((gePitchTool*)p)->__cb_setPitch(); } +void gePitchTool::cb_setPitchToBar (Fl_Widget* w, void* p) { ((gePitchTool*)p)->__cb_setPitchToBar(); } +void gePitchTool::cb_setPitchToSong(Fl_Widget* w, void* p) { ((gePitchTool*)p)->__cb_setPitchToSong(); } +void gePitchTool::cb_setPitchHalf (Fl_Widget* w, void* p) { ((gePitchTool*)p)->__cb_setPitchHalf(); } +void gePitchTool::cb_setPitchDouble(Fl_Widget* w, void* p) { ((gePitchTool*)p)->__cb_setPitchDouble(); } +void gePitchTool::cb_resetPitch (Fl_Widget* w, void* p) { ((gePitchTool*)p)->__cb_resetPitch(); } +void gePitchTool::cb_setPitchNum (Fl_Widget* w, void* p) { ((gePitchTool*)p)->__cb_setPitchNum(); } /* -------------------------------------------------------------------------- */ @@ -141,7 +141,8 @@ void gePitchTool::__cb_setPitchDouble() void gePitchTool::__cb_setPitchToBar() { - glue_setPitch(ch, ch->getEnd() / (float) clock::getFramesPerBar()); + // TODO - opaque channel's count + glue_setPitch(ch, (ch->getEnd()*2) / (float) clock::getFramesPerBar()); } @@ -150,7 +151,8 @@ void gePitchTool::__cb_setPitchToBar() void gePitchTool::__cb_setPitchToSong() { - glue_setPitch(ch, ch->getEnd() / (float) clock::getTotalFrames()); + // TODO - opaque channel's count + glue_setPitch(ch, (ch->getEnd()*2) / (float) clock::getTotalFrames()); } diff --git a/src/gui/elems/sampleEditor/rangeTool.cpp b/src/gui/elems/sampleEditor/rangeTool.cpp index 8abd173..9bdd59a 100644 --- a/src/gui/elems/sampleEditor/rangeTool.cpp +++ b/src/gui/elems/sampleEditor/rangeTool.cpp @@ -90,7 +90,7 @@ void geRangeTool::cb_resetStartEnd(Fl_Widget* w, void* p) { ((geRangeTool*)p)->_ void geRangeTool::__cb_setChanPos() { - sampleEditor::setBeginEndChannel(m_ch, atoi(m_begin->value()), atoi(m_end->value())); + sampleEditor::setBeginEnd(m_ch, atoi(m_begin->value()), atoi(m_end->value())); static_cast(window())->waveTools->updateWaveform(); } @@ -100,6 +100,6 @@ void geRangeTool::__cb_setChanPos() void geRangeTool::__cb_resetStartEnd() { - sampleEditor::setBeginEndChannel(m_ch, 0, m_ch->wave->getSize() - 1); + sampleEditor::setBeginEnd(m_ch, 0, m_ch->wave->getSize() - 1); static_cast(window())->waveTools->updateWaveform(); } diff --git a/src/gui/elems/sampleEditor/waveTools.cpp b/src/gui/elems/sampleEditor/waveTools.cpp index 0b7e059..7bfdb18 100644 --- a/src/gui/elems/sampleEditor/waveTools.cpp +++ b/src/gui/elems/sampleEditor/waveTools.cpp @@ -48,13 +48,18 @@ namespace { enum class Menu { - CUT = 0, - TRIM, - SILENCE, - FADE_IN, - FADE_OUT, - SMOOTH_EDGES, - SET_START_END + CUT = 0, + COPY, + PASTE, + TRIM, + SILENCE, + REVERSE, + NORMALIZE, + FADE_IN, + FADE_OUT, + SMOOTH_EDGES, + SET_BEGIN_END, + TO_NEW_CHANNEL }; @@ -63,35 +68,50 @@ enum class Menu void menuCallback(Fl_Widget* w, void* v) { - geWaveTools* wavetools = static_cast(w); - Menu selectedItem = (Menu) (intptr_t) v; - - int a = wavetools->waveform->getSelectionA(); - int b = wavetools->waveform->getSelectionB(); - - switch (selectedItem) { - case Menu::CUT: - c::sampleEditor::cut(wavetools->ch, a, b); - break; - case Menu::TRIM: - c::sampleEditor::trim(wavetools->ch, a, b); - break; - case Menu::SILENCE: - c::sampleEditor::silence(wavetools->ch, a, b); - break; - case Menu::FADE_IN: - c::sampleEditor::fade(wavetools->ch, a, b, m::wfx::FADE_IN); - break; - case Menu::FADE_OUT: - c::sampleEditor::fade(wavetools->ch, a, b, m::wfx::FADE_OUT); - break; - case Menu::SMOOTH_EDGES: - c::sampleEditor::smoothEdges(wavetools->ch, a, b); - break; - case Menu::SET_START_END: - c::sampleEditor::setStartEnd(wavetools->ch, a, b); - break; - } + geWaveTools* wavetools = static_cast(w); + Menu selectedItem = (Menu) (intptr_t) v; + + int a = wavetools->waveform->getSelectionA(); + int b = wavetools->waveform->getSelectionB(); + + switch (selectedItem) { + case Menu::CUT: + c::sampleEditor::cut(wavetools->ch, a, b); + break; + case Menu::COPY: + c::sampleEditor::copy(wavetools->ch, a, b); + break; + case Menu::PASTE: + c::sampleEditor::paste(wavetools->ch, a); + break; + case Menu::TRIM: + c::sampleEditor::trim(wavetools->ch, a, b); + break; + case Menu::SILENCE: + c::sampleEditor::silence(wavetools->ch, a, b); + break; + case Menu::REVERSE: + c::sampleEditor::reverse(wavetools->ch, a, b); + break; + case Menu::NORMALIZE: + c::sampleEditor::normalizeHard(wavetools->ch, a, b); + break; + case Menu::FADE_IN: + c::sampleEditor::fade(wavetools->ch, a, b, m::wfx::FADE_IN); + break; + case Menu::FADE_OUT: + c::sampleEditor::fade(wavetools->ch, a, b, m::wfx::FADE_OUT); + break; + case Menu::SMOOTH_EDGES: + c::sampleEditor::smoothEdges(wavetools->ch, a, b); + break; + case Menu::SET_BEGIN_END: + c::sampleEditor::setBeginEnd(wavetools->ch, a, b); + break; + case Menu::TO_NEW_CHANNEL: + c::sampleEditor::toNewChannel(wavetools->ch, a, b); + break; + } } }; // {anonymous} @@ -101,7 +121,7 @@ void menuCallback(Fl_Widget* w, void* v) geWaveTools::geWaveTools(int x, int y, int w, int h, SampleChannel *ch, const char *l) : Fl_Scroll(x, y, w, h, l), - ch (ch) + ch (ch) { type(Fl_Scroll::HORIZONTAL_ALWAYS); hscrollbar.color(G_COLOR_GREY_2); @@ -185,25 +205,40 @@ int geWaveTools::handle(int e) void geWaveTools::openMenu() { - if (!waveform->isSelected()) - return; - Fl_Menu_Item menu[] = { - {"Cut", 0, menuCallback, (void*) Menu::CUT}, - {"Trim", 0, menuCallback, (void*) Menu::TRIM}, - {"Silence", 0, menuCallback, (void*) Menu::SILENCE}, - {"Fade in", 0, menuCallback, (void*) Menu::FADE_IN}, - {"Fade out", 0, menuCallback, (void*) Menu::FADE_OUT}, - {"Smooth edges", 0, menuCallback, (void*) Menu::SMOOTH_EDGES}, - {"Set start/end here", 0, menuCallback, (void*) Menu::SET_START_END}, - {0} + {"Cut", 0, menuCallback, (void*) Menu::CUT}, + {"Copy", 0, menuCallback, (void*) Menu::COPY}, + {"Paste", 0, menuCallback, (void*) Menu::PASTE}, + {"Trim", 0, menuCallback, (void*) Menu::TRIM}, + {"Silence", 0, menuCallback, (void*) Menu::SILENCE}, + {"Reverse", 0, menuCallback, (void*) Menu::REVERSE}, + {"Normalize", 0, menuCallback, (void*) Menu::NORMALIZE}, + {"Fade in", 0, menuCallback, (void*) Menu::FADE_IN}, + {"Fade out", 0, menuCallback, (void*) Menu::FADE_OUT}, + {"Smooth edges", 0, menuCallback, (void*) Menu::SMOOTH_EDGES}, + {"Set begin/end here", 0, menuCallback, (void*) Menu::SET_BEGIN_END}, + {"Copy to new channel", 0, menuCallback, (void*) Menu::TO_NEW_CHANNEL}, + {0} }; - if (ch->status == STATUS_PLAY) { - menu[(int)Menu::CUT].deactivate(); - menu[(int)Menu::TRIM].deactivate(); - } + if (ch->status == STATUS_PLAY) { + menu[(int)Menu::CUT].deactivate(); + menu[(int)Menu::TRIM].deactivate(); + } + if (!waveform->isSelected()) { + menu[(int)Menu::CUT].deactivate(); + menu[(int)Menu::COPY].deactivate(); + menu[(int)Menu::TRIM].deactivate(); + menu[(int)Menu::SILENCE].deactivate(); + menu[(int)Menu::REVERSE].deactivate(); + menu[(int)Menu::NORMALIZE].deactivate(); + menu[(int)Menu::FADE_IN].deactivate(); + menu[(int)Menu::FADE_OUT].deactivate(); + menu[(int)Menu::SMOOTH_EDGES].deactivate(); + menu[(int)Menu::SET_BEGIN_END].deactivate(); + menu[(int)Menu::TO_NEW_CHANNEL].deactivate(); + } Fl_Menu_Button *b = new Fl_Menu_Button(0, 0, 100, 50); b->box(G_CUSTOM_BORDER_BOX); @@ -212,7 +247,7 @@ void geWaveTools::openMenu() b->color(G_COLOR_GREY_2); const Fl_Menu_Item *m = menu->popup(Fl::event_x(), Fl::event_y(), 0, 0, b); - if (m) - m->do_callback(this, m->user_data()); - return; + if (m) + m->do_callback(this, m->user_data()); + return; } diff --git a/src/gui/elems/sampleEditor/waveform.cpp b/src/gui/elems/sampleEditor/waveform.cpp index d153ad1..d13b369 100644 --- a/src/gui/elems/sampleEditor/waveform.cpp +++ b/src/gui/elems/sampleEditor/waveform.cpp @@ -50,26 +50,26 @@ using namespace giada::c; geWaveform::geWaveform(int x, int y, int w, int h, SampleChannel* ch, const char* l) : Fl_Widget (x, y, w, h, l), - m_selection {}, - m_ch (ch), - m_chanStart (0), - m_chanStartLit(false), - m_chanEnd (0), - m_chanEndLit (false), - m_pushed (false), - m_dragged (false), - m_resizedA (false), - m_resizedB (false), - m_ratio (0.0f) + m_selection {}, + m_ch (ch), + m_chanStart (0), + m_chanStartLit(false), + m_chanEnd (0), + m_chanEndLit (false), + m_pushed (false), + m_dragged (false), + m_resizedA (false), + m_resizedB (false), + m_ratio (0.0f) { - m_data.sup = nullptr; - m_data.inf = nullptr; - m_data.size = 0; + m_data.sup = nullptr; + m_data.inf = nullptr; + m_data.size = 0; - m_grid.snap = conf::sampleEditorGridOn; - m_grid.level = conf::sampleEditorGridVal; + m_grid.snap = conf::sampleEditorGridOn; + m_grid.level = conf::sampleEditorGridVal; - alloc(w); + alloc(w); } @@ -78,7 +78,7 @@ geWaveform::geWaveform(int x, int y, int w, int h, SampleChannel* ch, const char geWaveform::~geWaveform() { - freeData(); + freeData(); } @@ -87,14 +87,14 @@ geWaveform::~geWaveform() void geWaveform::freeData() { - if (m_data.sup) { - delete[] m_data.sup; - delete[] m_data.inf; - m_data.sup = nullptr; - m_data.inf = nullptr; - m_data.size = 0; - } - m_grid.points.clear(); + if (m_data.sup) { + delete[] m_data.sup; + delete[] m_data.inf; + m_data.sup = nullptr; + m_data.inf = nullptr; + m_data.size = 0; + } + m_grid.points.clear(); } @@ -103,60 +103,60 @@ void geWaveform::freeData() int geWaveform::alloc(int datasize, bool force) { - Wave* wave = m_ch->wave; + Wave* wave = m_ch->wave; - m_ratio = wave->getSize() / (float) datasize; + m_ratio = wave->getSize() / (float) datasize; - /* Limit 1:1 drawing (to avoid sub-frame drawing) by keeping m_ratio >= 1. */ + /* Limit 1:1 drawing (to avoid sub-frame drawing) by keeping m_ratio >= 1. */ - if (m_ratio < 1) { - datasize = wave->getSize(); - m_ratio = 1; - } + if (m_ratio < 1) { + datasize = wave->getSize(); + m_ratio = 1; + } - if (datasize == m_data.size && !force) - return 0; + if (datasize == m_data.size && !force) + return 0; - freeData(); + freeData(); - m_data.size = datasize; - m_data.sup = new (std::nothrow) int[m_data.size]; - m_data.inf = new (std::nothrow) int[m_data.size]; + m_data.size = datasize; + m_data.sup = new (std::nothrow) int[m_data.size]; + m_data.inf = new (std::nothrow) int[m_data.size]; - if (!m_data.sup || !m_data.inf) { - gu_log("[geWaveform::alloc] unable to allocate memory for the waveform!\n"); - return 0; - } + if (!m_data.sup || !m_data.inf) { + gu_log("[geWaveform::alloc] unable to allocate memory for the waveform!\n"); + return 0; + } - gu_log("[geWaveform::alloc] %d pixels, %f m_ratio\n", m_data.size, m_ratio); + gu_log("[geWaveform::alloc] %d pixels, %f m_ratio\n", m_data.size, m_ratio); - int offset = h() / 2; - int zero = y() + offset; // center, zero amplitude (-inf dB) + int offset = h() / 2; + int zero = y() + offset; // center, zero amplitude (-inf dB) - /* Frid frequency: store a grid point every 'gridFreq' frame (if grid is - enabled). TODO - this will cause round off errors, since gridFreq is integer. */ + /* Frid frequency: store a grid point every 'gridFreq' frame (if grid is + enabled). TODO - this will cause round off errors, since gridFreq is integer. */ - int gridFreq = m_grid.level != 0 ? wave->getSize() / m_grid.level : 0; + int gridFreq = m_grid.level != 0 ? wave->getSize() / m_grid.level : 0; /* Resampling the waveform, hardcore way. Many thanks to http://fourier.eng.hmc.edu/e161/lectures/resize/node3.html */ - for (int i=0; i= wave->getSize()) - continue; + if (k >= wave->getSize()) + continue; - /* Compute average of stereo signal. */ + /* Compute average of stereo signal. */ float avg = 0.0f; float* frame = wave->getFrame(k); @@ -173,19 +173,19 @@ int geWaveform::alloc(int datasize, bool force) if (gridFreq != 0 && (int) k % gridFreq == 0 && k != 0) m_grid.points.push_back(k); - } + } - m_data.sup[i] = zero - (peaksup * m_ch->getBoost() * offset); - m_data.inf[i] = zero - (peakinf * m_ch->getBoost() * offset); + m_data.sup[i] = zero - (peaksup * m_ch->getBoost() * offset); + m_data.inf[i] = zero - (peakinf * m_ch->getBoost() * offset); - // avoid window overflow + // avoid window overflow - if (m_data.sup[i] < y()) m_data.sup[i] = y(); - if (m_data.inf[i] > y()+h()-1) m_data.inf[i] = y()+h()-1; - } + if (m_data.sup[i] < y()) m_data.sup[i] = y(); + if (m_data.inf[i] > y()+h()-1) m_data.inf[i] = y()+h()-1; + } - recalcPoints(); - return 1; + recalcPoints(); + return 1; } @@ -194,8 +194,8 @@ int geWaveform::alloc(int datasize, bool force) void geWaveform::recalcPoints() { - m_chanStart = m_ch->getBegin(); - m_chanEnd = m_ch->getEnd(); + m_chanStart = m_ch->getBegin(); + m_chanEnd = m_ch->getEnd(); } @@ -204,21 +204,21 @@ void geWaveform::recalcPoints() void geWaveform::drawSelection() { - if (!isSelected()) - return; + if (!isSelected()) + return; - int a = frameToPixel(m_selection.a) + x(); - int b = frameToPixel(m_selection.b) + x(); + int a = frameToPixel(m_selection.a) + x(); + int b = frameToPixel(m_selection.b) + x(); - if (a < 0) - a = 0; - if (b >= w() + BORDER) - b = w() + BORDER; + if (a < 0) + a = 0; + if (b >= w() + BORDER) + b = w() + BORDER; - if (a < b) - fl_rectf(a, y(), b-a, h(), G_COLOR_GREY_4); - else - fl_rectf(b, y(), a-b, h(), G_COLOR_GREY_4); + if (a < b) + fl_rectf(a, y(), b-a, h(), G_COLOR_GREY_4); + else + fl_rectf(b, y(), a-b, h(), G_COLOR_GREY_4); } @@ -227,15 +227,15 @@ void geWaveform::drawSelection() void geWaveform::drawWaveform(int from, int to) { - int zero = y() + (h() / 2); // zero amplitude (-inf dB) - - fl_color(G_COLOR_BLACK); - for (int i=from; i= m_data.size) - break; - fl_line(i+x(), zero, i+x(), m_data.sup[i]); - fl_line(i+x(), zero, i+x(), m_data.inf[i]); - } + int zero = y() + (h() / 2); // zero amplitude (-inf dB) + + fl_color(G_COLOR_BLACK); + for (int i=from; i= m_data.size) + break; + fl_line(i+x(), zero, i+x(), m_data.sup[i]); + fl_line(i+x(), zero, i+x(), m_data.inf[i]); + } } @@ -262,38 +262,38 @@ void geWaveform::drawGrid(int from, int to) void geWaveform::drawStartEndPoints() { - /* print m_chanStart */ + /* print m_chanStart */ - int lineX = frameToPixel(m_chanStart) + x(); + int lineX = frameToPixel(m_chanStart) + x(); - if (m_chanStartLit) fl_color(G_COLOR_LIGHT_2); - else fl_color(G_COLOR_LIGHT_1); + if (m_chanStartLit) fl_color(G_COLOR_LIGHT_2); + else fl_color(G_COLOR_LIGHT_1); - /* vertical line */ + /* vertical line */ - fl_line(lineX, y()+1, lineX, y()+h()-2); + fl_line(lineX, y()+1, lineX, y()+h()-2); - /* print flag and avoid overflow */ + /* print flag and avoid overflow */ - if (lineX+FLAG_WIDTH > w()+x()-2) - fl_rectf(lineX, y()+h()-FLAG_HEIGHT-1, w()-lineX+x()-1, FLAG_HEIGHT); - else - fl_rectf(lineX, y()+h()-FLAG_HEIGHT-1, FLAG_WIDTH, FLAG_HEIGHT); + if (lineX+FLAG_WIDTH > w()+x()-2) + fl_rectf(lineX, y()+h()-FLAG_HEIGHT-1, w()-lineX+x()-1, FLAG_HEIGHT); + else + fl_rectf(lineX, y()+h()-FLAG_HEIGHT-1, FLAG_WIDTH, FLAG_HEIGHT); - /* print m_chanEnd */ + /* print m_chanEnd */ - lineX = frameToPixel(m_chanEnd) + x() - 1; - if (m_chanEndLit) fl_color(G_COLOR_LIGHT_2); - else fl_color(G_COLOR_LIGHT_1); + lineX = frameToPixel(m_chanEnd) + x() - 1; + if (m_chanEndLit) fl_color(G_COLOR_LIGHT_2); + else fl_color(G_COLOR_LIGHT_1); - /* vertical line */ + /* vertical line */ - fl_line(lineX, y()+1, lineX, y()+h()-2); + fl_line(lineX, y()+1, lineX, y()+h()-2); - if (lineX-FLAG_WIDTH < x()) - fl_rectf(x()+1, y()+1, lineX-x(), FLAG_HEIGHT); - else - fl_rectf(lineX-FLAG_WIDTH, y()+1, FLAG_WIDTH, FLAG_HEIGHT); + if (lineX-FLAG_WIDTH < x()) + fl_rectf(x()+1, y()+1, lineX-x(), FLAG_HEIGHT); + else + fl_rectf(lineX-FLAG_WIDTH, y()+1, FLAG_WIDTH, FLAG_HEIGHT); } @@ -302,9 +302,9 @@ void geWaveform::drawStartEndPoints() void geWaveform::drawPlayHead() { - int p = frameToPixel(m_ch->getTrackerPreview()) + x(); - fl_color(G_COLOR_LIGHT_2); - fl_line(p, y() + 1, p, y() + h() - 2); + int p = frameToPixel(m_ch->getTrackerPreview()) + x(); + fl_color(G_COLOR_LIGHT_2); + fl_line(p, y() + 1, p, y() + h() - 2); } @@ -313,27 +313,27 @@ void geWaveform::drawPlayHead() void geWaveform::draw() { - assert(m_data.sup != nullptr); - assert(m_data.inf != nullptr); + assert(m_data.sup != nullptr); + assert(m_data.inf != nullptr); - fl_rectf(x(), y(), w(), h(), G_COLOR_GREY_2); // blank canvas + fl_rectf(x(), y(), w(), h(), G_COLOR_GREY_2); // blank canvas - /* Draw things from 'from' (offset driven by the scrollbar) to 'to' (width of - parent window). We don't draw the entire waveform, only the visibile part. */ + /* Draw things from 'from' (offset driven by the scrollbar) to 'to' (width of + parent window). We don't draw the entire waveform, only the visibile part. */ - int from = abs(x() - parent()->x()); - int to = from + parent()->w(); - if (x() + w() < parent()->w()) - to = x() + w() - BORDER; + int from = abs(x() - parent()->x()); + int to = from + parent()->w(); + if (x() + w() < parent()->w()) + to = x() + w() - BORDER; - drawSelection(); - drawWaveform(from, to); - drawGrid(from, to); - drawPlayHead(); + drawSelection(); + drawWaveform(from, to); + drawGrid(from, to); + drawPlayHead(); - fl_rect(x(), y(), w(), h(), G_COLOR_GREY_4); // border box - - drawStartEndPoints(); + fl_rect(x(), y(), w(), h(), G_COLOR_GREY_4); // border box + + drawStartEndPoints(); } @@ -345,161 +345,166 @@ int geWaveform::handle(int e) m_mouseX = pixelToFrame(Fl::event_x() - x()); m_mouseY = pixelToFrame(Fl::event_y() - y()); - switch (e) { + switch (e) { - case FL_KEYDOWN: { - if (Fl::event_key() == ' ') - static_cast(window())->__cb_togglePreview(); - else - if (Fl::event_key() == FL_BackSpace) + case FL_KEYDOWN: { + if (Fl::event_key() == ' ') + static_cast(window())->__cb_togglePreview(); + else + if (Fl::event_key() == FL_BackSpace) sampleEditor::rewindPreview(m_ch); - return 1; - } - - case FL_PUSH: { - - m_pushed = true; - - if (!mouseOnEnd() && !mouseOnStart()) { - if (Fl::event_button3()) // let the parent (waveTools) handle this - return 0; - if (mouseOnSelectionA()) - m_resizedA = true; - else - if(mouseOnSelectionB()) - m_resizedB = true; - else { - m_dragged = true; - m_selection.a = m_mouseX; - m_selection.b = m_mouseX; - } - } - return 1; - } - - case FL_RELEASE: { - - sampleEditor::setPlayHead(m_ch, m_mouseX); - - /* If selection has been done (m_dragged or resized), make sure that point A - is always lower than B. */ - - if (m_dragged || m_resizedA || m_resizedB) - fixSelection(); - - /* Handle begin/end markers interaction. */ - - if (m_chanStartLit || m_chanEndLit) - sampleEditor::setBeginEndChannel(m_ch, m_chanStart, m_chanEnd); - - m_pushed = false; - m_dragged = false; - m_resizedA = false; - m_resizedB = false; - - redraw(); - return 1; - } - - case FL_ENTER: { // enables FL_DRAG - return 1; - } - - case FL_LEAVE: { - if (m_chanStartLit || m_chanEndLit) { - m_chanStartLit = false; - m_chanEndLit = false; - redraw(); - } - return 1; - } - - case FL_MOVE: { - - if (mouseOnStart()) { - m_chanStartLit = true; - redraw(); - } - else - if (m_chanStartLit) { - m_chanStartLit = false; - redraw(); - } - - if (mouseOnEnd()) { - m_chanEndLit = true; - redraw(); - } - else - if (m_chanEndLit) { - m_chanEndLit = false; - redraw(); - } - - if (mouseOnSelectionA() && isSelected()) - fl_cursor(FL_CURSOR_WE, FL_WHITE, FL_BLACK); - else - if (mouseOnSelectionB() && isSelected()) - fl_cursor(FL_CURSOR_WE, FL_WHITE, FL_BLACK); - else - fl_cursor(FL_CURSOR_DEFAULT, FL_WHITE, FL_BLACK); - - return 1; - } - - case FL_DRAG: { - - /* here the mouse is on the m_chanStart tool */ - - if (m_chanStartLit && m_pushed) { - - m_chanStart = snap(m_mouseX); - - if (m_chanStart < 0) - m_chanStart = 0; - else - if (m_chanStart >= m_chanEnd) - m_chanStart = m_chanEnd - 2; - - redraw(); - } - else - if (m_chanEndLit && m_pushed) { - - m_chanEnd = snap(m_mouseX); - - if (m_chanEnd > m_ch->wave->getSize()) - m_chanEnd = m_ch->wave->getSize(); - else - if (m_chanEnd <= m_chanStart) - m_chanEnd = m_chanStart + 2; - - redraw(); - } - - /* Here the mouse is on the waveform, i.e. a new selection has started. */ - - else - if (m_dragged) { - m_selection.b = snap(m_mouseX); - redraw(); - } - - /* here the mouse is on a selection boundary i.e. resize */ - - else - if (m_resizedA || m_resizedB) { - int pos = snap(m_mouseX); - m_resizedA ? m_selection.a = pos : m_selection.b = pos; - redraw(); - } - - return 1; - } - - default: - return Fl_Widget::handle(e); - } + return 1; + } + + case FL_PUSH: { + + if (Fl::event_clicks() > 0) { + selectAll(); + return 1; + } + + m_pushed = true; + + if (!mouseOnEnd() && !mouseOnStart()) { + if (Fl::event_button3()) // let the parent (waveTools) handle this + return 0; + if (mouseOnSelectionA()) + m_resizedA = true; + else + if(mouseOnSelectionB()) + m_resizedB = true; + else { + m_dragged = true; + m_selection.a = m_mouseX; + m_selection.b = m_mouseX; + } + } + return 1; + } + + case FL_RELEASE: { + + sampleEditor::setPlayHead(m_ch, m_mouseX); + + /* If selection has been done (m_dragged or resized), make sure that point A + is always lower than B. */ + + if (m_dragged || m_resizedA || m_resizedB) + fixSelection(); + + /* Handle begin/end markers interaction. */ + + if (m_chanStartLit || m_chanEndLit) + sampleEditor::setBeginEnd(m_ch, m_chanStart, m_chanEnd); + + m_pushed = false; + m_dragged = false; + m_resizedA = false; + m_resizedB = false; + + redraw(); + return 1; + } + + case FL_ENTER: { // enables FL_DRAG + return 1; + } + + case FL_LEAVE: { + if (m_chanStartLit || m_chanEndLit) { + m_chanStartLit = false; + m_chanEndLit = false; + redraw(); + } + return 1; + } + + case FL_MOVE: { + + if (mouseOnStart()) { + m_chanStartLit = true; + redraw(); + } + else + if (m_chanStartLit) { + m_chanStartLit = false; + redraw(); + } + + if (mouseOnEnd()) { + m_chanEndLit = true; + redraw(); + } + else + if (m_chanEndLit) { + m_chanEndLit = false; + redraw(); + } + + if (mouseOnSelectionA() && isSelected()) + fl_cursor(FL_CURSOR_WE, FL_WHITE, FL_BLACK); + else + if (mouseOnSelectionB() && isSelected()) + fl_cursor(FL_CURSOR_WE, FL_WHITE, FL_BLACK); + else + fl_cursor(FL_CURSOR_DEFAULT, FL_WHITE, FL_BLACK); + + return 1; + } + + case FL_DRAG: { + + /* here the mouse is on the m_chanStart tool */ + + if (m_chanStartLit && m_pushed) { + + m_chanStart = snap(m_mouseX); + + if (m_chanStart < 0) + m_chanStart = 0; + else + if (m_chanStart >= m_chanEnd) + m_chanStart = m_chanEnd - 2; + + redraw(); + } + else + if (m_chanEndLit && m_pushed) { + + m_chanEnd = snap(m_mouseX); + + if (m_chanEnd > m_ch->wave->getSize()) + m_chanEnd = m_ch->wave->getSize(); + else + if (m_chanEnd <= m_chanStart) + m_chanEnd = m_chanStart + 2; + + redraw(); + } + + /* Here the mouse is on the waveform, i.e. a new selection has started. */ + + else + if (m_dragged) { + m_selection.b = snap(m_mouseX); + redraw(); + } + + /* here the mouse is on a selection boundary i.e. resize */ + + else + if (m_resizedA || m_resizedB) { + int pos = snap(m_mouseX); + m_resizedA ? m_selection.a = pos : m_selection.b = pos; + redraw(); + } + + return 1; + } + + default: + return Fl_Widget::handle(e); + } } @@ -512,7 +517,7 @@ int geWaveform::snap(int pos) return pos; for (int pf : m_grid.points) { if (pos >= pf - pixelToFrame(SNAPPING) && - pos <= pf + pixelToFrame(SNAPPING)) + pos <= pf + pixelToFrame(SNAPPING)) { return pf; } @@ -529,9 +534,9 @@ bool geWaveform::mouseOnStart() int mouseXp = frameToPixel(m_mouseX); int mouseYp = frameToPixel(m_mouseY); int chanStartP = frameToPixel(m_chanStart); - return mouseXp - (FLAG_WIDTH / 2) > chanStartP - BORDER && - mouseXp - (FLAG_WIDTH / 2) <= chanStartP - BORDER + FLAG_WIDTH && - mouseYp > h() - FLAG_HEIGHT; + return mouseXp - (FLAG_WIDTH / 2) > chanStartP - BORDER && + mouseXp - (FLAG_WIDTH / 2) <= chanStartP - BORDER + FLAG_WIDTH && + mouseYp > h() - FLAG_HEIGHT; } @@ -543,9 +548,9 @@ bool geWaveform::mouseOnEnd() int mouseXp = frameToPixel(m_mouseX); int mouseYp = frameToPixel(m_mouseY); int chanEndP = frameToPixel(m_chanEnd); - return mouseXp - (FLAG_WIDTH / 2) >= chanEndP - BORDER - FLAG_WIDTH && - mouseXp - (FLAG_WIDTH / 2) <= chanEndP - BORDER && - mouseYp <= FLAG_HEIGHT + 1; + return mouseXp - (FLAG_WIDTH / 2) >= chanEndP - BORDER - FLAG_WIDTH && + mouseXp - (FLAG_WIDTH / 2) <= chanEndP - BORDER && + mouseYp <= FLAG_HEIGHT + 1; } @@ -556,7 +561,7 @@ bool geWaveform::mouseOnSelectionA() { int mouseXp = frameToPixel(m_mouseX); int selAp = frameToPixel(m_selection.a); - return mouseXp >= selAp - (FLAG_WIDTH / 2) && mouseXp <= selAp + (FLAG_WIDTH / 2); + return mouseXp >= selAp - (FLAG_WIDTH / 2) && mouseXp <= selAp + (FLAG_WIDTH / 2); } @@ -564,7 +569,7 @@ bool geWaveform::mouseOnSelectionB() { int mouseXp = frameToPixel(m_mouseX); int selBp = frameToPixel(m_selection.b); - return mouseXp >= selBp - (FLAG_WIDTH / 2) && mouseXp <= selBp + (FLAG_WIDTH / 2); + return mouseXp >= selBp - (FLAG_WIDTH / 2) && mouseXp <= selBp + (FLAG_WIDTH / 2); } @@ -573,11 +578,11 @@ bool geWaveform::mouseOnSelectionB() int geWaveform::pixelToFrame(int p) { - if (p <= 0) - return 0; - if (p > m_data.size) - return m_ch->wave->getSize() - 1; - return p * m_ratio; + if (p <= 0) + return 0; + if (p > m_data.size) + return m_ch->wave->getSize() - 1; + return p * m_ratio; } @@ -586,7 +591,7 @@ int geWaveform::pixelToFrame(int p) int geWaveform::frameToPixel(int p) { - return ceil(p / m_ratio); + return ceil(p / m_ratio); } @@ -595,9 +600,9 @@ int geWaveform::frameToPixel(int p) void geWaveform::fixSelection() { - if (m_selection.a > m_selection.b) // inverted m_selection - std::swap(m_selection.a, m_selection.b); - sampleEditor::setPlayHead(m_ch, m_selection.a); + if (m_selection.a > m_selection.b) // inverted m_selection + std::swap(m_selection.a, m_selection.b); + sampleEditor::setPlayHead(m_ch, m_selection.a); } @@ -606,8 +611,8 @@ void geWaveform::fixSelection() void geWaveform::clearSel() { - m_selection.a = 0; - m_selection.b = 0; + m_selection.a = 0; + m_selection.b = 0; } @@ -616,37 +621,37 @@ void geWaveform::clearSel() void geWaveform::setZoom(int type) { - if (!alloc(type == ZOOM_IN ? m_data.size * 2 : m_data.size / 2)) - return; + if (!alloc(type == ZOOM_IN ? m_data.size * 2 : m_data.size / 2)) + return; - size(m_data.size, h()); + size(m_data.size, h()); - /* Zoom to cursor. */ + /* Zoom to cursor. */ int newX = -frameToPixel(m_mouseX) + Fl::event_x(); if (newX > BORDER) newX = BORDER; position(newX, y()); - /* Avoid overflow when zooming out with scrollbar like that: + /* Avoid overflow when zooming out with scrollbar like that: - |----------[scrollbar]| + |----------[scrollbar]| - Offset vs smaller: - - |[wave------------| offset > 0 smaller = false - |[wave----] | offset < 0, smaller = true - |-------------] | offset < 0, smaller = false */ + Offset vs smaller: + + |[wave------------| offset > 0 smaller = false + |[wave----] | offset < 0, smaller = true + |-------------] | offset < 0, smaller = false */ - int parentW = parent()->w(); - int thisW = x() + w() - BORDER; // visible width, not full width + int parentW = parent()->w(); + int thisW = x() + w() - BORDER; // visible width, not full width - if (thisW < parentW) - position(x() + parentW - thisW, y()); - if (smaller()) - stretchToWindow(); + if (thisW < parentW) + position(x() + parentW - thisW, y()); + if (smaller()) + stretchToWindow(); - redraw(); + redraw(); } @@ -655,10 +660,10 @@ void geWaveform::setZoom(int type) void geWaveform::stretchToWindow() { - int s = parent()->w(); - alloc(s); - position(BORDER, y()); - size(s, h()); + int s = parent()->w(); + alloc(s); + position(BORDER, y()); + size(s, h()); } @@ -667,8 +672,8 @@ void geWaveform::stretchToWindow() void geWaveform::refresh() { - alloc(m_data.size, true); // force - redraw(); + alloc(m_data.size, true); // force + redraw(); } @@ -677,7 +682,7 @@ void geWaveform::refresh() bool geWaveform::smaller() { - return w() < parent()->w(); + return w() < parent()->w(); } @@ -686,10 +691,10 @@ bool geWaveform::smaller() void geWaveform::setGridLevel(int l) { - m_grid.points.clear(); - m_grid.level = l; - alloc(m_data.size, true); // force alloc - redraw(); + m_grid.points.clear(); + m_grid.level = l; + alloc(m_data.size, true); // force alloc + redraw(); } @@ -698,7 +703,7 @@ void geWaveform::setGridLevel(int l) bool geWaveform::isSelected() { - return m_selection.a != m_selection.b; + return m_selection.a != m_selection.b; } @@ -715,12 +720,19 @@ int geWaveform::getSize() { return m_data.size; } int geWaveform::getSelectionA() { - return m_selection.a; + return m_selection.a; } int geWaveform::getSelectionB() { - return m_selection.b; + return m_selection.b; } + +void geWaveform::selectAll() +{ + m_selection.a = 0; + m_selection.b = m_ch->wave->getSize() - 1; + redraw(); +} diff --git a/src/gui/elems/sampleEditor/waveform.h b/src/gui/elems/sampleEditor/waveform.h index a427860..bfddf6e 100644 --- a/src/gui/elems/sampleEditor/waveform.h +++ b/src/gui/elems/sampleEditor/waveform.h @@ -58,14 +58,14 @@ private: Real graphic stuff from the underlying waveform. */ struct - { + { int* sup; // upper part of the waveform int* inf; // lower part of the waveform int size; // width of the waveform to draw (in pixel) } m_data; struct - { + { bool snap; int level; std::vector points; @@ -96,8 +96,8 @@ private: bool mouseOnSelectionA(); bool mouseOnSelectionB(); - int pixelToFrame(int p); - int frameToPixel(int f); + int pixelToFrame(int p); // TODO - move these to utils::, will be needed in actionEditor + int frameToPixel(int f); // TODO - move these to utils::, will be needed in actionEditor /* fixSelection Helper function which flattens the selection if it was made from right to left @@ -116,19 +116,21 @@ private: bool smaller(); - /* snap - Snaps a point at 'pos' pixel. */ + /* snap + Snaps a point at 'pos' pixel. */ + + int snap(int pos); - int snap(int pos); + /* draw* + Drawing functions. */ - /* draw* - Drawing functions. */ + void drawSelection(); + void drawWaveform(int from, int to); + void drawGrid(int from, int to); + void drawStartEndPoints(); + void drawPlayHead(); - void drawSelection(); - void drawWaveform(int from, int to); - void drawGrid(int from, int to); - void drawStartEndPoints(); - void drawPlayHead(); + void selectAll(); public: @@ -172,8 +174,8 @@ public: void setGridLevel(int l); - void setSnap(bool v); - bool getSnap(); + void setSnap(bool v); + bool getSnap(); int getSize(); /* isSelected diff --git a/src/utils/time.cpp b/src/utils/time.cpp new file mode 100644 index 0000000..e28a706 --- /dev/null +++ b/src/utils/time.cpp @@ -0,0 +1,43 @@ +/* ----------------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * utils + * + * ----------------------------------------------------------------------------- + * + * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual + * + * This file is part of Giada - Your Hardcore Loopmachine. + * + * Giada - Your Hardcore Loopmachine is free software: you can + * redistribute it and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Giada - Your Hardcore Loopmachine is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Giada - Your Hardcore Loopmachine. If not, see + * . + * + * -------------------------------------------------------------------------- */ + + +#include +#include +#include "time.h" + + +namespace giada { +namespace u { +namespace time +{ +void sleep(int millisecs) +{ + std::this_thread::sleep_for(std::chrono::milliseconds(millisecs)); +} +}}}; // giada::u::time:: diff --git a/src/utils/time.h b/src/utils/time.h new file mode 100644 index 0000000..5ebd453 --- /dev/null +++ b/src/utils/time.h @@ -0,0 +1,42 @@ +/* ----------------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * utils + * + * ----------------------------------------------------------------------------- + * + * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual + * + * This file is part of Giada - Your Hardcore Loopmachine. + * + * Giada - Your Hardcore Loopmachine is free software: you can + * redistribute it and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * Giada - Your Hardcore Loopmachine is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Giada - Your Hardcore Loopmachine. If not, see + * . + * + * -------------------------------------------------------------------------- */ + + +#ifndef G_UTILS_TIME_H +#define G_UTILS_TIME_H + + +namespace giada { +namespace u { +namespace time +{ +void sleep(int millisecs); +}}}; + + +#endif \ No newline at end of file diff --git a/tests/patch.cpp b/tests/patch.cpp index a84c945..da55162 100644 --- a/tests/patch.cpp +++ b/tests/patch.cpp @@ -10,221 +10,222 @@ using namespace giada::m; TEST_CASE("Test Patch class") { - string filename = "./test-patch.json"; - - SECTION("test write") - { - patch::action_t action1; - patch::action_t action2; - patch::channel_t channel1; - patch::channel_t channel2; - patch::column_t column; + string filename = "./test-patch.json"; + + SECTION("test write") + { + patch::action_t action1; + patch::action_t action2; + patch::channel_t channel1; + patch::column_t column; #ifdef WITH_VST - patch::plugin_t plugin1; - patch::plugin_t plugin2; - patch::plugin_t plugin3; + patch::plugin_t plugin1; + patch::plugin_t plugin2; + patch::plugin_t plugin3; #endif - action1.type = 0; - action1.frame = 50000; - action1.fValue = 0.3f; - action1.iValue = 1000; - action2.type = 2; - action2.frame = 589; - action2.fValue = 1.0f; - action2.iValue = 130; - channel1.actions.push_back(action1); - channel1.actions.push_back(action2); + action1.type = 0; + action1.frame = 50000; + action1.fValue = 0.3f; + action1.iValue = 1000; + action2.type = 2; + action2.frame = 589; + action2.fValue = 1.0f; + action2.iValue = 130; + channel1.actions.push_back(action1); + channel1.actions.push_back(action2); #ifdef WITH_VST - plugin1.path = "/path/to/plugin1"; - plugin1.bypass = false; - plugin1.params.push_back(0.0f); - plugin1.params.push_back(0.1f); - plugin1.params.push_back(0.2f); - channel1.plugins.push_back(plugin1); - - plugin2.path = "/another/path/to/plugin2"; - plugin2.bypass = true; - plugin2.params.push_back(0.6f); - plugin2.params.push_back(0.6f); - plugin2.params.push_back(0.6f); - plugin2.params.push_back(0.0f); - plugin2.params.push_back(1.0f); - plugin2.params.push_back(1.0f); - plugin2.params.push_back(0.333f); - channel1.plugins.push_back(plugin2); + plugin1.path = "/path/to/plugin1"; + plugin1.bypass = false; + plugin1.params.push_back(0.0f); + plugin1.params.push_back(0.1f); + plugin1.params.push_back(0.2f); + channel1.plugins.push_back(plugin1); + + plugin2.path = "/another/path/to/plugin2"; + plugin2.bypass = true; + plugin2.params.push_back(0.6f); + plugin2.params.push_back(0.6f); + plugin2.params.push_back(0.6f); + plugin2.params.push_back(0.0f); + plugin2.params.push_back(1.0f); + plugin2.params.push_back(1.0f); + plugin2.params.push_back(0.333f); + channel1.plugins.push_back(plugin2); #endif - channel1.type = CHANNEL_SAMPLE; - channel1.index = 666; - channel1.column = 0; - channel1.mute = 0; - channel1.mute_s = 0; - channel1.solo = 0; - channel1.volume = 1.0f; - channel1.pan = 0.5f; - channel1.midiIn = true; - 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; - channel1.midiOutL = true; - channel1.midiOutLplaying = 7; - channel1.midiOutLmute = 8; - channel1.midiOutLsolo = 9; - channel1.samplePath = "/tmp/test.wav"; - channel1.key = 666; - channel1.mode = 0; - channel1.begin = 0; - channel1.end = 0; - channel1.boost = 0; - channel1.recActive = 0; - channel1.pitch = 1.2f; - channel1.midiInReadActions = 0; - channel1.midiInPitch = 0; - channel1.midiOut = 0; - channel1.midiOutChan = 5; - patch::channels.push_back(channel1); - - column.index = 0; - column.width = 500; - patch::columns.push_back(column); - - patch::header = "GPTCH"; - patch::version = "1.0"; - patch::versionMajor = 6; - patch::versionMinor = 6; - patch::versionPatch = 6; - patch::name = "test patch"; - patch::bpm = 100.0f; - patch::bars = 4; - patch::beats = 23; - patch::quantize = 1; - patch::masterVolIn = 1.0f; - patch::masterVolOut = 0.7f; - patch::metronome = 0; - patch::lastTakeId = 0; - patch::samplerate = 44100; + channel1.type = CHANNEL_SAMPLE; + channel1.index = 666; + channel1.size = G_GUI_CHANNEL_H_1; + channel1.column = 0; + channel1.mute = 0; + channel1.mute_s = 0; + channel1.solo = 0; + channel1.volume = 1.0f; + channel1.pan = 0.5f; + channel1.midiIn = true; + 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; + channel1.midiOutL = true; + channel1.midiOutLplaying = 7; + channel1.midiOutLmute = 8; + channel1.midiOutLsolo = 9; + channel1.samplePath = "/tmp/test.wav"; + channel1.key = 666; + channel1.mode = 0; + channel1.begin = 0; + channel1.end = 0; + channel1.boost = 0; + channel1.recActive = 0; + channel1.pitch = 1.2f; + channel1.midiInReadActions = 0; + channel1.midiInPitch = 0; + channel1.midiOut = 0; + channel1.midiOutChan = 5; + patch::channels.push_back(channel1); + + column.index = 0; + column.width = 500; + patch::columns.push_back(column); + + patch::header = "GPTCH"; + patch::version = "1.0"; + patch::versionMajor = 6; + patch::versionMinor = 6; + patch::versionPatch = 6; + patch::name = "test patch"; + patch::bpm = 100.0f; + patch::bars = 4; + patch::beats = 23; + patch::quantize = 1; + patch::masterVolIn = 1.0f; + patch::masterVolOut = 0.7f; + patch::metronome = 0; + patch::lastTakeId = 0; + patch::samplerate = 44100; #ifdef WITH_VST - patch::masterInPlugins.push_back(plugin1); - patch::masterOutPlugins.push_back(plugin2); + patch::masterInPlugins.push_back(plugin1); + patch::masterOutPlugins.push_back(plugin2); #endif - REQUIRE(patch::write(filename) == 1); - } - - SECTION("test read") - { - REQUIRE(patch::read(filename) == PATCH_READ_OK); - REQUIRE(patch::header == "GPTCH"); - REQUIRE(patch::version == "1.0"); - REQUIRE(patch::versionMajor == 6); - REQUIRE(patch::versionMinor == 6); - REQUIRE(patch::versionPatch == 6); - REQUIRE(patch::name == "test patch"); - REQUIRE(patch::bpm == Approx(100.0f)); - REQUIRE(patch::bars == 4); - REQUIRE(patch::beats == 23); - REQUIRE(patch::quantize == 1); - REQUIRE(patch::masterVolIn == Approx(1.0f)); - REQUIRE(patch::masterVolOut == Approx(0.7f)); - REQUIRE(patch::metronome == 0); - REQUIRE(patch::lastTakeId == 0); - REQUIRE(patch::samplerate == 44100); - - patch::column_t column0 = patch::columns.at(0); - REQUIRE(column0.index == 0); - REQUIRE(column0.width == 500); - - patch::channel_t channel0 = patch::channels.at(0); - REQUIRE(channel0.type == CHANNEL_SAMPLE); - REQUIRE(channel0.index == 666); - REQUIRE(channel0.column == 0); - REQUIRE(channel0.mute == 0); - REQUIRE(channel0.mute_s == 0); - REQUIRE(channel0.solo == 0); - REQUIRE(channel0.volume == Approx(1.0f)); - REQUIRE(channel0.pan == Approx(0.5f)); - REQUIRE(channel0.midiIn == true); - 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); - REQUIRE(channel0.midiOutL == true); - REQUIRE(channel0.midiOutLplaying == 7); - REQUIRE(channel0.midiOutLmute == 8); - REQUIRE(channel0.midiOutLsolo == 9); - REQUIRE(channel0.samplePath == "/tmp/test.wav"); - REQUIRE(channel0.key == 666); - REQUIRE(channel0.mode == 0); - REQUIRE(channel0.begin == 0); - REQUIRE(channel0.end == 0); - REQUIRE(channel0.boost == 1.0f); - REQUIRE(channel0.recActive == 0); - REQUIRE(channel0.pitch == Approx(1.2f)); - REQUIRE(channel0.midiInReadActions == 0); - REQUIRE(channel0.midiInPitch == 0); - REQUIRE(channel0.midiOut == 0); - REQUIRE(channel0.midiOutChan == 5); - - patch::action_t action0 = channel0.actions.at(0); - REQUIRE(action0.type == 0); - REQUIRE(action0.frame == 50000); - REQUIRE(action0.fValue == Approx(0.3f)); - REQUIRE(action0.iValue == 1000); - - patch::action_t action1 = channel0.actions.at(1); - REQUIRE(action1.type == 2); - REQUIRE(action1.frame == 589); - REQUIRE(action1.fValue == Approx(1.0f)); - REQUIRE(action1.iValue == 130); + REQUIRE(patch::write(filename) == 1); + } + + SECTION("test read") + { + REQUIRE(patch::read(filename) == PATCH_READ_OK); + REQUIRE(patch::header == "GPTCH"); + REQUIRE(patch::version == "1.0"); + REQUIRE(patch::versionMajor == 6); + REQUIRE(patch::versionMinor == 6); + REQUIRE(patch::versionPatch == 6); + REQUIRE(patch::name == "test patch"); + REQUIRE(patch::bpm == Approx(100.0f)); + REQUIRE(patch::bars == 4); + REQUIRE(patch::beats == 23); + REQUIRE(patch::quantize == 1); + REQUIRE(patch::masterVolIn == Approx(1.0f)); + REQUIRE(patch::masterVolOut == Approx(0.7f)); + REQUIRE(patch::metronome == 0); + REQUIRE(patch::lastTakeId == 0); + REQUIRE(patch::samplerate == 44100); + + patch::column_t column0 = patch::columns.at(0); + REQUIRE(column0.index == 0); + REQUIRE(column0.width == 500); + + patch::channel_t channel0 = patch::channels.at(0); + REQUIRE(channel0.type == CHANNEL_SAMPLE); + REQUIRE(channel0.index == 666); + REQUIRE(channel0.size == G_GUI_CHANNEL_H_1); + REQUIRE(channel0.column == 0); + REQUIRE(channel0.mute == 0); + REQUIRE(channel0.mute_s == 0); + REQUIRE(channel0.solo == 0); + REQUIRE(channel0.volume == Approx(1.0f)); + REQUIRE(channel0.pan == Approx(0.5f)); + REQUIRE(channel0.midiIn == true); + 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); + REQUIRE(channel0.midiOutL == true); + REQUIRE(channel0.midiOutLplaying == 7); + REQUIRE(channel0.midiOutLmute == 8); + REQUIRE(channel0.midiOutLsolo == 9); + REQUIRE(channel0.samplePath == "/tmp/test.wav"); + REQUIRE(channel0.key == 666); + REQUIRE(channel0.mode == 0); + REQUIRE(channel0.begin == 0); + REQUIRE(channel0.end == 0); + REQUIRE(channel0.boost == 1.0f); + REQUIRE(channel0.recActive == 0); + REQUIRE(channel0.pitch == Approx(1.2f)); + REQUIRE(channel0.midiInReadActions == 0); + REQUIRE(channel0.midiInPitch == 0); + REQUIRE(channel0.midiOut == 0); + REQUIRE(channel0.midiOutChan == 5); + + patch::action_t action0 = channel0.actions.at(0); + REQUIRE(action0.type == 0); + REQUIRE(action0.frame == 50000); + REQUIRE(action0.fValue == Approx(0.3f)); + REQUIRE(action0.iValue == 1000); + + patch::action_t action1 = channel0.actions.at(1); + REQUIRE(action1.type == 2); + REQUIRE(action1.frame == 589); + REQUIRE(action1.fValue == Approx(1.0f)); + REQUIRE(action1.iValue == 130); #ifdef WITH_VST - patch::plugin_t plugin0 = channel0.plugins.at(0); - REQUIRE(plugin0.path == "/path/to/plugin1"); - REQUIRE(plugin0.bypass == false); - REQUIRE(plugin0.params.at(0) == Approx(0.0f)); - REQUIRE(plugin0.params.at(1) == Approx(0.1f)); - REQUIRE(plugin0.params.at(2) == Approx(0.2f)); - - patch::plugin_t plugin1 = channel0.plugins.at(1); - REQUIRE(plugin1.path == "/another/path/to/plugin2"); - REQUIRE(plugin1.bypass == true); - REQUIRE(plugin1.params.at(0) == Approx(0.6f)); - REQUIRE(plugin1.params.at(1) == Approx(0.6f)); - REQUIRE(plugin1.params.at(2) == Approx(0.6f)); - REQUIRE(plugin1.params.at(3) == Approx(0.0f)); - REQUIRE(plugin1.params.at(4) == Approx(1.0f)); - REQUIRE(plugin1.params.at(5) == Approx(1.0f)); - REQUIRE(plugin1.params.at(6) == Approx(0.333f)); - - patch::plugin_t masterPlugin0 = patch::masterInPlugins.at(0); - REQUIRE(masterPlugin0.path == "/path/to/plugin1"); - REQUIRE(masterPlugin0.bypass == false); - REQUIRE(masterPlugin0.params.at(0) == Approx(0.0f)); - REQUIRE(masterPlugin0.params.at(1) == Approx(0.1f)); - REQUIRE(masterPlugin0.params.at(2) == Approx(0.2f)); - - patch::plugin_t masterPlugin1 = patch::masterOutPlugins.at(0); - REQUIRE(masterPlugin1.path == "/another/path/to/plugin2"); - REQUIRE(masterPlugin1.bypass == true); - REQUIRE(masterPlugin1.params.at(0) == Approx(0.6f)); - REQUIRE(masterPlugin1.params.at(1) == Approx(0.6f)); - REQUIRE(masterPlugin1.params.at(2) == Approx(0.6f)); - REQUIRE(masterPlugin1.params.at(3) == Approx(0.0f)); - REQUIRE(masterPlugin1.params.at(4) == Approx(1.0f)); - REQUIRE(masterPlugin1.params.at(5) == Approx(1.0f)); - REQUIRE(masterPlugin1.params.at(6) == Approx(0.333f)); + patch::plugin_t plugin0 = channel0.plugins.at(0); + REQUIRE(plugin0.path == "/path/to/plugin1"); + REQUIRE(plugin0.bypass == false); + REQUIRE(plugin0.params.at(0) == Approx(0.0f)); + REQUIRE(plugin0.params.at(1) == Approx(0.1f)); + REQUIRE(plugin0.params.at(2) == Approx(0.2f)); + + patch::plugin_t plugin1 = channel0.plugins.at(1); + REQUIRE(plugin1.path == "/another/path/to/plugin2"); + REQUIRE(plugin1.bypass == true); + REQUIRE(plugin1.params.at(0) == Approx(0.6f)); + REQUIRE(plugin1.params.at(1) == Approx(0.6f)); + REQUIRE(plugin1.params.at(2) == Approx(0.6f)); + REQUIRE(plugin1.params.at(3) == Approx(0.0f)); + REQUIRE(plugin1.params.at(4) == Approx(1.0f)); + REQUIRE(plugin1.params.at(5) == Approx(1.0f)); + REQUIRE(plugin1.params.at(6) == Approx(0.333f)); + + patch::plugin_t masterPlugin0 = patch::masterInPlugins.at(0); + REQUIRE(masterPlugin0.path == "/path/to/plugin1"); + REQUIRE(masterPlugin0.bypass == false); + REQUIRE(masterPlugin0.params.at(0) == Approx(0.0f)); + REQUIRE(masterPlugin0.params.at(1) == Approx(0.1f)); + REQUIRE(masterPlugin0.params.at(2) == Approx(0.2f)); + + patch::plugin_t masterPlugin1 = patch::masterOutPlugins.at(0); + REQUIRE(masterPlugin1.path == "/another/path/to/plugin2"); + REQUIRE(masterPlugin1.bypass == true); + REQUIRE(masterPlugin1.params.at(0) == Approx(0.6f)); + REQUIRE(masterPlugin1.params.at(1) == Approx(0.6f)); + REQUIRE(masterPlugin1.params.at(2) == Approx(0.6f)); + REQUIRE(masterPlugin1.params.at(3) == Approx(0.0f)); + REQUIRE(masterPlugin1.params.at(4) == Approx(1.0f)); + REQUIRE(masterPlugin1.params.at(5) == Approx(1.0f)); + REQUIRE(masterPlugin1.params.at(6) == Approx(0.333f)); #endif - } + } }