From 816a800f12f4623418cf189d28d2b8f201572526 Mon Sep 17 00:00:00 2001 From: Dennis Braun Date: Thu, 24 Sep 2020 20:21:08 +0200 Subject: [PATCH] New upstream version 0.16.4+ds1 --- ChangeLog | 14 + Makefile.am | 2 +- create_source_tarball.sh | 1 + extras/giada-logo.svg | 17 + src/core/audioBuffer.cpp | 23 +- src/core/audioBuffer.h | 34 +- src/core/channels/audioReceiver.cpp | 4 +- src/core/channels/audioReceiver.h | 6 +- src/core/channels/channel.cpp | 36 +- src/core/channels/channel.h | 8 +- src/core/channels/channelManager.cpp | 11 +- src/core/channels/channelManager.h | 8 +- src/core/channels/sampleActionRecorder.cpp | 20 +- src/core/channels/sampleActionRecorder.h | 1 + src/core/channels/samplePlayer.cpp | 22 +- src/core/channels/samplePlayer.h | 6 +- src/core/channels/state.cpp | 12 +- src/core/channels/state.h | 7 +- src/core/channels/waveReader.cpp | 2 +- src/core/conf.cpp | 310 +++++++++--------- src/core/conf.h | 28 +- src/core/const.h | 189 +++++------ src/core/graphics.cpp | 28 ++ src/core/graphics.h | 1 + src/core/kernelAudio.cpp | 22 +- src/core/midiDispatcher.cpp | 10 +- src/core/mixer.cpp | 14 +- src/core/mixer.h | 1 + src/core/mixerHandler.cpp | 130 +++++--- src/core/mixerHandler.h | 8 +- src/core/model/storage.cpp | 20 +- src/core/patch.cpp | 18 +- src/core/patch.h | 1 + src/core/recManager.cpp | 14 +- src/core/wave.cpp | 11 +- src/core/wave.h | 10 +- src/core/waveManager.cpp | 15 +- src/core/waveManager.h | 13 +- src/glue/channel.cpp | 23 +- src/glue/channel.h | 2 + src/glue/events.cpp | 10 +- src/glue/events.h | 3 +- src/glue/main.cpp | 16 +- src/glue/main.h | 5 + .../dialogs/actionEditor/baseActionEditor.h | 2 +- src/gui/dialogs/config.h | 2 +- src/gui/dialogs/mainWindow.cpp | 2 +- src/gui/dialogs/midiIO/midiInputBase.h | 2 +- src/gui/dialogs/midiIO/midiInputChannel.h | 2 +- src/gui/dialogs/midiIO/midiInputMaster.h | 2 +- src/gui/dialogs/pluginChooser.h | 2 +- src/gui/dialogs/sampleEditor.h | 2 +- src/gui/dispatcher.cpp | 2 +- src/gui/elems/actionEditor/gridTool.h | 2 +- src/gui/elems/basics/baseButton.cpp | 1 + src/gui/elems/basics/check.cpp | 38 ++- src/gui/elems/basics/check.h | 4 + src/gui/elems/basics/choice.cpp | 105 ++++-- src/gui/elems/basics/choice.h | 27 +- src/gui/elems/config/tabAudio.cpp | 36 +- src/gui/elems/config/tabAudio.h | 2 +- src/gui/elems/config/tabBehaviors.cpp | 60 ++-- src/gui/elems/config/tabBehaviors.h | 19 +- src/gui/elems/config/tabMidi.h | 2 +- src/gui/elems/config/tabMisc.h | 4 +- src/gui/elems/config/tabPlugins.cpp | 1 - .../mainWindow/keyboard/sampleChannel.cpp | 24 +- src/gui/elems/mainWindow/mainIO.cpp | 110 +++---- src/gui/elems/mainWindow/mainIO.h | 40 +-- src/gui/elems/mainWindow/mainTimer.cpp | 8 +- src/gui/elems/mainWindow/mainTimer.h | 2 +- src/gui/elems/mainWindow/mainTransport.cpp | 7 +- src/gui/elems/mainWindow/mainTransport.h | 2 +- src/gui/elems/plugin/pluginElement.h | 2 +- src/utils/gui.cpp | 16 + src/utils/gui.h | 6 + tests/audioBuffer.cpp | 8 - tests/waveFx.cpp | 40 --- tests/waveManager.cpp | 7 +- 79 files changed, 1023 insertions(+), 704 deletions(-) create mode 100644 extras/giada-logo.svg diff --git a/ChangeLog b/ChangeLog index 76ea8d9..ecdf42e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -11,6 +11,20 @@ -------------------------------------------------------------------------------- +0.16.4 --- +- Support for mono inputs +- Disable record-on-signal mode when sequencer is running +- Shift + [click on R button] kills action reading when "Treat one-shot channels + with actions as loops" option is on +- Start MIDI channels automatically after action recording session +- Enable overdub mode on sample channel with existing audio data +- Fix wrong sample rate conversion when project rate != system rate +- Fix Wrong begin/end sample markers when loading a project with + samplerate != system.samplerate +- Fix wrong MIDI learn mapping for master parameters +- Fix BPM button disabled after audio recording session + + 0.16.3 --- 2020 . 06. 15 - Non-virtual Channels architecture - Added G_DEBUG macro diff --git a/Makefile.am b/Makefile.am index ee58416..9cc69bc 100644 --- a/Makefile.am +++ b/Makefile.am @@ -430,7 +430,7 @@ cppFlags += -D__MACOSX_CORE__ cxxFlags += -ObjC++ ldAdd += -lsndfile -lfltk -lrtmidi -lsamplerate -lm -lpthread \ - -lFLAC -logg -lvorbis -lvorbisenc + -lFLAC -logg -lvorbis -lvorbisenc -lopus ldFlags += -framework CoreAudio -framework Cocoa -framework Carbon \ -framework CoreMIDI -framework CoreFoundation -framework Accelerate \ diff --git a/create_source_tarball.sh b/create_source_tarball.sh index 7be10d0..aff5570 100755 --- a/create_source_tarball.sh +++ b/create_source_tarball.sh @@ -34,6 +34,7 @@ checkout_project() { --single-branch \ --depth=1 \ --recurse-submodules \ + --shallow-submodules \ "${output_name}" } diff --git a/extras/giada-logo.svg b/extras/giada-logo.svg new file mode 100644 index 0000000..17e6885 --- /dev/null +++ b/extras/giada-logo.svg @@ -0,0 +1,17 @@ + + +image/svg+xml diff --git a/src/core/audioBuffer.cpp b/src/core/audioBuffer.cpp index 01b7f8d..483e3c4 100644 --- a/src/core/audioBuffer.cpp +++ b/src/core/audioBuffer.cpp @@ -119,6 +119,8 @@ float AudioBuffer::getPeak() const void AudioBuffer::alloc(Frame size, int channels) { + assert(channels <= NUM_CHANS); + free(); m_size = size; m_channels = channels; @@ -142,6 +144,8 @@ void AudioBuffer::free() void AudioBuffer::setData(float* data, Frame size, int channels) { + assert(channels <= NUM_CHANS); + m_data = data; m_size = size; m_channels = channels; @@ -153,6 +157,8 @@ void AudioBuffer::setData(float* data, Frame size, int channels) void AudioBuffer::moveData(AudioBuffer& b) { + assert(b.countChannels() <= NUM_CHANS); + free(); m_data = b[0]; m_size = b.countFrames(); @@ -164,17 +170,26 @@ void AudioBuffer::moveData(AudioBuffer& b) /* -------------------------------------------------------------------------- */ -void AudioBuffer::copyData(const float* data, Frame frames, int offset) +void AudioBuffer::copyData(const float* data, Frame frames, int channels, int offset) { assert(m_data != nullptr); assert(frames <= m_size - offset); - std::copy_n(data, frames * m_channels, m_data + (offset * m_channels)); + + if (channels < NUM_CHANS) // i.e. one channel, mono + for (int i = offset, k = 0; i < m_size; i++, k++) + for (int j = 0; j < countChannels(); j++) + (*this)[i][j] = data[k]; + else + if (channels == NUM_CHANS) + std::copy_n(data, frames * channels, m_data + (offset * channels)); + else + assert(false); } void AudioBuffer::copyData(const AudioBuffer& b, float gain) { - copyData(b[0], b.countFrames()); + copyData(b[0], b.countFrames(), b.countChannels()); if (gain != 1.0f) applyGain(gain); } @@ -187,7 +202,7 @@ void AudioBuffer::addData(const AudioBuffer& b, float gain, Pan pan) { assert(m_data != nullptr); assert(countFrames() <= b.countFrames()); - assert(countChannels() == pan.size()); + assert(b.countChannels() <= NUM_CHANS); for (int i = 0; i < countFrames(); i++) for (int j = 0; j < countChannels(); j++) diff --git a/src/core/audioBuffer.h b/src/core/audioBuffer.h index 6c0128b..67247aa 100644 --- a/src/core/audioBuffer.h +++ b/src/core/audioBuffer.h @@ -31,17 +31,23 @@ #include #include "core/types.h" -#include "core/const.h" namespace giada { namespace m { +/* AudioBuffer +A class that holds a buffer filled with audio data. NOTE: currently it only +supports 2 channels (stereo). Give it a mono stream and it will convert it to +stereo. Give it a multichannel stream and it will throw an assertion. */ + class AudioBuffer { public: + + static constexpr int NUM_CHANS = 2; - using Pan = std::array; + using Pan = std::array; /* AudioBuffer (1) Creates an empty (and invalid) audio buffer. */ @@ -82,24 +88,36 @@ public: void alloc(Frame size, int channels); void free(); - /* copyData + /* copyData (1) Copies 'frames' frames from the new 'data' into m_data, and fills m_data - starting from frame 'offset'. The new data MUST contain the same number of - channels than m_channels. */ + starting from frame 'offset'. The new data MUST NOT contain more than + NUM_CHANS channels. If channels < NUM_CHANS, they will be spread over the + stereo buffer. */ + + void copyData(const float* data, Frame frames, int channels=NUM_CHANS, int offset=0); - void copyData(const float* data, Frame frames, int offset=0); + /* copyData (2) + Copies buffer 'b' onto this one. If 'b' has less channels than this one, + they will be spread over the current ones. Buffer 'b' MUST NOT contain more + channels than this one. */ void copyData(const AudioBuffer& b, float gain=1.0f); + + /* addData + Merges audio data from buffer 'b' onto this one. Applies optional gain and + pan if needed. */ + void addData(const AudioBuffer& b, float gain=1.0f, Pan pan={1.0f, 1.0f}); /* setData - Borrow 'data' as new m_data. Makes sure not to delete the data 'data' points + Views 'data' as new m_data. Makes sure not to delete the data 'data' points to while using it. Set it back to nullptr when done. */ void setData(float* data, Frame size, int channels); /* moveData - Moves data held by 'b' into this buffer. Then 'b' becomes an empty buffer. */ + Moves data held by 'b' into this buffer. Then 'b' becomes an empty buffer. + TODO - add move constructor instead! */ void moveData(AudioBuffer& b); diff --git a/src/core/channels/audioReceiver.cpp b/src/core/channels/audioReceiver.cpp index 7d8a118..dd222e6 100644 --- a/src/core/channels/audioReceiver.cpp +++ b/src/core/channels/audioReceiver.cpp @@ -32,8 +32,8 @@ namespace giada { namespace m { -AudioReceiver::AudioReceiver(ChannelState* c) -: state (std::make_unique()) +AudioReceiver::AudioReceiver(ChannelState* c, const conf::Conf& conf) +: state (std::make_unique(conf)) , m_channelState(c) { } diff --git a/src/core/channels/audioReceiver.h b/src/core/channels/audioReceiver.h index a285f92..443bd3c 100644 --- a/src/core/channels/audioReceiver.h +++ b/src/core/channels/audioReceiver.h @@ -35,6 +35,10 @@ namespace giada { namespace m { +namespace conf +{ +struct Conf; +} namespace patch { struct Channel; @@ -50,7 +54,7 @@ class AudioReceiver { public: - AudioReceiver(ChannelState*); + AudioReceiver(ChannelState*, const conf::Conf&); AudioReceiver(const patch::Channel&, ChannelState*); AudioReceiver(const AudioReceiver&, ChannelState* c=nullptr); diff --git a/src/core/channels/channel.cpp b/src/core/channels/channel.cpp index 16b625b..502edc0 100644 --- a/src/core/channels/channel.cpp +++ b/src/core/channels/channel.cpp @@ -35,18 +35,18 @@ namespace giada { namespace m { -Channel::Channel(ChannelType type, ID id, ID columnId, Frame bufferSize) -: id (id) -, state (std::make_unique(id, bufferSize)) -, midiLighter (state.get()) -, m_type (type) -, m_columnId (columnId) +Channel::Channel(ChannelType type, ID id, ID columnId, Frame bufferSize, const conf::Conf& conf) +: id (id) +, state (std::make_unique(id, bufferSize)) +, midiLighter(state.get()) +, m_type (type) +, m_columnId (columnId) { switch (m_type) { case ChannelType::SAMPLE: samplePlayer.emplace(state.get()); - audioReceiver.emplace(state.get()); + audioReceiver.emplace(state.get(), conf); sampleActionRecorder.emplace(state.get(), samplePlayer->state.get()); break; @@ -325,6 +325,26 @@ bool Channel::isMuted() const bool Channel::canInputRec() const { - return samplePlayer && !samplePlayer->hasWave() && state->armed.load() == true; + if (m_type != ChannelType::SAMPLE) + return false; + + bool armed = state->armed.load(); + bool hasWave = samplePlayer->hasWave(); + bool isProtected = audioReceiver->state->overdubProtection.load(); + bool canOverdub = !hasWave || (hasWave && !isProtected); + + return armed && canOverdub; +} + + +bool Channel::canActionRec() const +{ + return hasWave() && !samplePlayer->state->isAnyLoopMode(); +} + + +bool Channel::hasWave() const +{ + return m_type == ChannelType::SAMPLE && samplePlayer->hasWave(); } }} // giada::m:: diff --git a/src/core/channels/channel.h b/src/core/channels/channel.h index 8450011..cb0291a 100644 --- a/src/core/channels/channel.h +++ b/src/core/channels/channel.h @@ -49,11 +49,15 @@ namespace giada { namespace m { +namespace conf +{ +struct Conf; +} class Channel final { public: - Channel(ChannelType t, ID id, ID columnId, Frame bufferSize); + Channel(ChannelType t, ID id, ID columnId, Frame bufferSize, const conf::Conf& c); Channel(const Channel&); Channel(const patch::Channel& p, Frame bufferSize); Channel(Channel&&) = default; @@ -79,6 +83,8 @@ public: bool isInternal() const; bool isMuted() const; bool canInputRec() const; + bool canActionRec() const; + bool hasWave() const; ID getColumnId() const; ChannelType getType() const; diff --git a/src/core/channels/channelManager.cpp b/src/core/channels/channelManager.cpp index 260fd5e..f38dd9b 100644 --- a/src/core/channels/channelManager.cpp +++ b/src/core/channels/channelManager.cpp @@ -68,11 +68,13 @@ void init() /* -------------------------------------------------------------------------- */ -std::unique_ptr create(ChannelType type, int bufferSize, - bool inputMonitorOn, ID columnId) +std::unique_ptr create(ChannelType type, int bufferSize, ID columnId, + const conf::Conf& conf) { - return std::make_unique(type, channelId_.get(), columnId, - kernelAudio::getRealBufSize()); + std::unique_ptr ch = std::make_unique(type, + channelId_.get(), columnId, kernelAudio::getRealBufSize(), conf); + + return ch; } @@ -147,6 +149,7 @@ const patch::Channel serializeChannel(const Channel& c) pc.shift = c.samplePlayer->state->shift.load(); pc.midiInVeloAsVol = c.samplePlayer->state->velocityAsVol.load(); pc.inputMonitor = c.audioReceiver->state->inputMonitor.load(); + pc.overdubProtection = c.audioReceiver->state->overdubProtection.load(); } else diff --git a/src/core/channels/channelManager.h b/src/core/channels/channelManager.h index e7667fe..d15aa6a 100644 --- a/src/core/channels/channelManager.h +++ b/src/core/channels/channelManager.h @@ -36,6 +36,10 @@ namespace giada { namespace m { +namespace conf +{ +struct Conf; +} namespace patch { struct Channel; @@ -55,8 +59,8 @@ void init(); /* create (1) Creates a new Channel from scratch. */ -std::unique_ptr create(ChannelType type, int bufferSize, - bool inputMonitorOn, ID columnId); +std::unique_ptr create(ChannelType type, int bufferSize, ID columnId, + const conf::Conf& conf); /* create (2) Creates a new Channel given an existing one (i.e. clone). */ diff --git a/src/core/channels/sampleActionRecorder.cpp b/src/core/channels/sampleActionRecorder.cpp index 10c61c6..2b37b72 100644 --- a/src/core/channels/sampleActionRecorder.cpp +++ b/src/core/channels/sampleActionRecorder.cpp @@ -85,7 +85,10 @@ void SampleActionRecorder::parse(const mixer::Event& e) const onFirstBeat(); break; case mixer::EventType::CHANNEL_TOGGLE_READ_ACTIONS: - toggleReadActions(); break; + toggleReadActions(); break; + + case mixer::EventType::CHANNEL_KILL_READ_ACTIONS: + killReadActions(); break; default: break; } @@ -171,6 +174,21 @@ void SampleActionRecorder::toggleReadActions() const /* -------------------------------------------------------------------------- */ +void SampleActionRecorder::killReadActions() const +{ + /* Killing Read Actions, i.e. shift + click on 'R' button is meaninful only + when the conf::treatRecsAsLoops is true. */ + + if (!conf::conf.treatRecsAsLoops) + return; + m_channelState->recStatus.store(ChannelStatus::OFF); + m_channelState->readActions.store(false); +} + + +/* -------------------------------------------------------------------------- */ + + void SampleActionRecorder::onKeyPress() const { if (!canRecord()) diff --git a/src/core/channels/sampleActionRecorder.h b/src/core/channels/sampleActionRecorder.h index 0f81b17..b6bf451 100644 --- a/src/core/channels/sampleActionRecorder.h +++ b/src/core/channels/sampleActionRecorder.h @@ -64,6 +64,7 @@ private: void toggleReadActions() const; void startReadActions() const; void stopReadActions(ChannelStatus curRecStatus) const; + void killReadActions() const; bool canRecord() const; diff --git a/src/core/channels/samplePlayer.cpp b/src/core/channels/samplePlayer.cpp index e9b96ac..a9ff0e1 100644 --- a/src/core/channels/samplePlayer.cpp +++ b/src/core/channels/samplePlayer.cpp @@ -103,8 +103,6 @@ void SamplePlayer::render(AudioBuffer& out) const if (m_waveReader.wave == nullptr || !m_channelState->isPlaying()) return; - /* Advance SampleController: this is needed for quantization. */ - Frame begin = state->begin.load(); Frame end = state->end.load(); Frame tracker = state->tracker.load(); @@ -134,12 +132,13 @@ void SamplePlayer::render(AudioBuffer& out) const used = m_waveReader.fill(buffer, tracker, state->offset, pitch); tracker += used; -//G_DEBUG ("block=[" << tracker - used << ", " << tracker << ")" << -// ", used=" << used << ", range=[" << begin << ", " << end << ")" << -// ", offset=" << state->offset << ", globalFrame=" << clock::getCurrentFrame()); +G_DEBUG ("block=[" << tracker - used << ", " << tracker << ")" << + ", used=" << used << ", range=[" << begin << ", " << end << ")" << + ", tracker=" << tracker << + ", offset=" << state->offset << ", globalFrame=" << clock::getCurrentFrame()); if (tracker >= end) { -//G_DEBUG ("last frame tracker=" << tracker); +G_DEBUG ("last frame tracker=" << tracker); tracker = begin; m_sampleController.onLastFrame(); if (shouldLoop()) { @@ -182,10 +181,19 @@ void SamplePlayer::loadWave(const Wave* w) /* -------------------------------------------------------------------------- */ -void SamplePlayer::setWave(const Wave& w) +void SamplePlayer::setWave(const Wave& w, float samplerateRatio) { m_waveReader.wave = &w; m_waveId = w.id; + + if (samplerateRatio != 1.0f) { + Frame begin = state->begin.load(); + Frame end = state->end.load(); + Frame shift = state->shift.load(); + state->begin.store(begin * samplerateRatio); + state->end.store(end * samplerateRatio); + state->shift.store(shift * samplerateRatio); + } } diff --git a/src/core/channels/samplePlayer.h b/src/core/channels/samplePlayer.h index d22af29..2ddfbc5 100644 --- a/src/core/channels/samplePlayer.h +++ b/src/core/channels/samplePlayer.h @@ -66,9 +66,11 @@ public: void loadWave(const Wave* w); /* setWave - Just sets the pointer to a Wave object. Used during de-serialization. */ + Just sets the pointer to a Wave object. Used during de-serialization. The + ratio is used to adjust begin/end points in case of patch vs. conf sample + rate mismatch. */ - void setWave(const Wave& w); + void setWave(const Wave& w, float samplerateRatio); /* setInvalidWave Same as setWave(nullptr) plus the invalid ID (i.e. 0). */ diff --git a/src/core/channels/state.cpp b/src/core/channels/state.cpp index 18f4aa7..415f554 100644 --- a/src/core/channels/state.cpp +++ b/src/core/channels/state.cpp @@ -25,6 +25,7 @@ * -------------------------------------------------------------------------- */ +#include "core/conf.h" #include "core/patch.h" #include "state.h" @@ -232,20 +233,23 @@ bool SamplePlayerState::isAnyLoopMode() const /* -------------------------------------------------------------------------- */ -AudioReceiverState::AudioReceiverState() -: inputMonitor(false) +AudioReceiverState::AudioReceiverState(const conf::Conf& c) +: inputMonitor (c.inputMonitorDefaultOn) +, overdubProtection(c.overdubProtectionDefaultOn) { } AudioReceiverState::AudioReceiverState(const patch::Channel& p) -: inputMonitor(p.inputMonitor) +: inputMonitor (p.inputMonitor) +, overdubProtection(p.overdubProtection) { } AudioReceiverState::AudioReceiverState(const AudioReceiverState& o) -: inputMonitor(o.inputMonitor.load()) +: inputMonitor (o.inputMonitor.load()) +, overdubProtection(o.overdubProtection.load()) { } diff --git a/src/core/channels/state.h b/src/core/channels/state.h index e7e8576..dc04169 100644 --- a/src/core/channels/state.h +++ b/src/core/channels/state.h @@ -42,6 +42,10 @@ namespace giada { namespace m { +namespace conf +{ +struct Conf; +} namespace patch { struct Channel; @@ -179,11 +183,12 @@ struct SamplePlayerState struct AudioReceiverState { - AudioReceiverState(); + AudioReceiverState(const conf::Conf& c); AudioReceiverState(const patch::Channel& p); AudioReceiverState(const AudioReceiverState& o); std::atomic inputMonitor; + std::atomic overdubProtection; }; diff --git a/src/core/channels/waveReader.cpp b/src/core/channels/waveReader.cpp index b96c71d..9f57174 100644 --- a/src/core/channels/waveReader.cpp +++ b/src/core/channels/waveReader.cpp @@ -144,7 +144,7 @@ Frame WaveReader::fillCopy(AudioBuffer& dest, Frame start, Frame offset) const if (used > wave->getSize() - start) used = wave->getSize() - start; - dest.copyData(wave->getFrame(start), used, offset); + dest.copyData(wave->getFrame(start), used, G_MAX_IO_CHANS, offset); return used; } diff --git a/src/core/conf.cpp b/src/core/conf.cpp index f68be90..7c68c44 100644 --- a/src/core/conf.cpp +++ b/src/core/conf.cpp @@ -130,83 +130,85 @@ bool read() nl::json j = nl::json::parse(ifs); - conf.logMode = j.value(CONF_KEY_LOG_MODE, conf.logMode); - conf.soundSystem = j.value(CONF_KEY_SOUND_SYSTEM, conf.soundSystem); - conf.soundDeviceOut = j.value(CONF_KEY_SOUND_DEVICE_OUT, conf.soundDeviceOut); - conf.soundDeviceIn = j.value(CONF_KEY_SOUND_DEVICE_IN, conf.soundDeviceIn); - conf.channelsOut = j.value(CONF_KEY_CHANNELS_OUT, conf.channelsOut); - conf.channelsIn = j.value(CONF_KEY_CHANNELS_IN, conf.channelsIn); - conf.samplerate = j.value(CONF_KEY_SAMPLERATE, conf.samplerate); - conf.buffersize = j.value(CONF_KEY_BUFFER_SIZE, conf.buffersize); - conf.limitOutput = j.value(CONF_KEY_LIMIT_OUTPUT, conf.limitOutput); - conf.rsmpQuality = j.value(CONF_KEY_RESAMPLE_QUALITY, conf.rsmpQuality); - conf.midiSystem = j.value(CONF_KEY_MIDI_SYSTEM, conf.midiSystem); - conf.midiPortOut = j.value(CONF_KEY_MIDI_PORT_OUT, conf.midiPortOut); - conf.midiPortIn = j.value(CONF_KEY_MIDI_PORT_IN, conf.midiPortIn); - conf.midiMapPath = j.value(CONF_KEY_MIDIMAP_PATH, conf.midiMapPath); - conf.lastFileMap = j.value(CONF_KEY_LAST_MIDIMAP, conf.lastFileMap); - conf.midiSync = j.value(CONF_KEY_MIDI_SYNC, conf.midiSync); - conf.midiTCfps = j.value(CONF_KEY_MIDI_TC_FPS, conf.midiTCfps); - conf.chansStopOnSeqHalt = j.value(CONF_KEY_CHANS_STOP_ON_SEQ_HALT, conf.chansStopOnSeqHalt); - conf.treatRecsAsLoops = j.value(CONF_KEY_TREAT_RECS_AS_LOOPS, conf.treatRecsAsLoops); - conf.inputMonitorDefaultOn = j.value(CONF_KEY_INPUT_MONITOR_DEFAULT_ON, conf.inputMonitorDefaultOn); - conf.pluginPath = j.value(CONF_KEY_PLUGINS_PATH, conf.pluginPath); - conf.patchPath = j.value(CONF_KEY_PATCHES_PATH, conf.patchPath); - conf.samplePath = j.value(CONF_KEY_SAMPLES_PATH, conf.samplePath); - conf.mainWindowX = j.value(CONF_KEY_MAIN_WINDOW_X, conf.mainWindowX); - conf.mainWindowY = j.value(CONF_KEY_MAIN_WINDOW_Y, conf.mainWindowY); - conf.mainWindowW = j.value(CONF_KEY_MAIN_WINDOW_W, conf.mainWindowW); - conf.mainWindowH = j.value(CONF_KEY_MAIN_WINDOW_H, conf.mainWindowH); - conf.browserX = j.value(CONF_KEY_BROWSER_X, conf.browserX); - conf.browserY = j.value(CONF_KEY_BROWSER_Y, conf.browserY); - conf.browserW = j.value(CONF_KEY_BROWSER_W, conf.browserW); - conf.browserH = j.value(CONF_KEY_BROWSER_H, conf.browserH); - conf.browserPosition = j.value(CONF_KEY_BROWSER_POSITION, conf.browserPosition); - conf.browserLastPath = j.value(CONF_KEY_BROWSER_LAST_PATH, conf.browserLastPath); - conf.browserLastValue = j.value(CONF_KEY_BROWSER_LAST_VALUE, conf.browserLastValue); - conf.actionEditorX = j.value(CONF_KEY_ACTION_EDITOR_X, conf.actionEditorX); - conf.actionEditorY = j.value(CONF_KEY_ACTION_EDITOR_Y, conf.actionEditorY); - conf.actionEditorW = j.value(CONF_KEY_ACTION_EDITOR_W, conf.actionEditorW); - conf.actionEditorH = j.value(CONF_KEY_ACTION_EDITOR_H, conf.actionEditorH); - conf.actionEditorZoom = j.value(CONF_KEY_ACTION_EDITOR_ZOOM, conf.actionEditorZoom); - conf.actionEditorGridVal = j.value(CONF_KEY_ACTION_EDITOR_GRID_VAL, conf.actionEditorGridVal); - conf.actionEditorGridOn = j.value(CONF_KEY_ACTION_EDITOR_GRID_ON, conf.actionEditorGridOn); - conf.sampleEditorX = j.value(CONF_KEY_SAMPLE_EDITOR_X, conf.sampleEditorX); - conf.sampleEditorY = j.value(CONF_KEY_SAMPLE_EDITOR_Y, conf.sampleEditorY); - conf.sampleEditorW = j.value(CONF_KEY_SAMPLE_EDITOR_W, conf.sampleEditorW); - conf.sampleEditorH = j.value(CONF_KEY_SAMPLE_EDITOR_H, conf.sampleEditorH); - conf.sampleEditorGridVal = j.value(CONF_KEY_SAMPLE_EDITOR_GRID_VAL, conf.sampleEditorGridVal); - conf.sampleEditorGridOn = j.value(CONF_KEY_SAMPLE_EDITOR_GRID_ON, conf.sampleEditorGridOn); - conf.pianoRollY = j.value(CONF_KEY_PIANO_ROLL_Y, conf.pianoRollY); - conf.pianoRollH = j.value(CONF_KEY_PIANO_ROLL_H, conf.pianoRollH); - conf.sampleActionEditorH = j.value(CONF_KEY_SAMPLE_ACTION_EDITOR_H, conf.sampleActionEditorH); - conf.velocityEditorH = j.value(CONF_KEY_VELOCITY_EDITOR_H, conf.velocityEditorH); - conf.envelopeEditorH = j.value(CONF_KEY_ENVELOPE_EDITOR_H, conf.envelopeEditorH); - conf.pluginListX = j.value(CONF_KEY_PLUGIN_LIST_X, conf.pluginListX); - conf.pluginListY = j.value(CONF_KEY_PLUGIN_LIST_Y, conf.pluginListY); - conf.midiInputX = j.value(CONF_KEY_MIDI_INPUT_X, conf.midiInputX); - conf.midiInputY = j.value(CONF_KEY_MIDI_INPUT_Y, conf.midiInputY); - conf.midiInputW = j.value(CONF_KEY_MIDI_INPUT_W, conf.midiInputW); - conf.midiInputH = j.value(CONF_KEY_MIDI_INPUT_H, conf.midiInputH); - conf.recTriggerMode = j.value(CONF_KEY_REC_TRIGGER_MODE, conf.recTriggerMode); - conf.recTriggerLevel = j.value(CONF_KEY_REC_TRIGGER_LEVEL, conf.recTriggerLevel); - conf.midiInEnabled = j.value(CONF_KEY_MIDI_IN, conf.midiInEnabled); - conf.midiInFilter = j.value(CONF_KEY_MIDI_IN_FILTER, conf.midiInFilter); - conf.midiInRewind = j.value(CONF_KEY_MIDI_IN_REWIND, conf.midiInRewind); - conf.midiInStartStop = j.value(CONF_KEY_MIDI_IN_START_STOP, conf.midiInStartStop); - conf.midiInActionRec = j.value(CONF_KEY_MIDI_IN_ACTION_REC, conf.midiInActionRec); - conf.midiInInputRec = j.value(CONF_KEY_MIDI_IN_INPUT_REC, conf.midiInInputRec); - conf.midiInMetronome = j.value(CONF_KEY_MIDI_IN_METRONOME, conf.midiInMetronome); - conf.midiInVolumeIn = j.value(CONF_KEY_MIDI_IN_VOLUME_IN, conf.midiInVolumeIn); - conf.midiInVolumeOut = j.value(CONF_KEY_MIDI_IN_VOLUME_OUT, conf.midiInVolumeOut); - conf.midiInBeatDouble = j.value(CONF_KEY_MIDI_IN_BEAT_DOUBLE, conf.midiInBeatDouble); - conf.midiInBeatHalf = j.value(CONF_KEY_MIDI_IN_BEAT_HALF, conf.midiInBeatHalf); + conf.logMode = j.value(CONF_KEY_LOG_MODE, conf.logMode); + conf.soundSystem = j.value(CONF_KEY_SOUND_SYSTEM, conf.soundSystem); + conf.soundDeviceOut = j.value(CONF_KEY_SOUND_DEVICE_OUT, conf.soundDeviceOut); + conf.soundDeviceIn = j.value(CONF_KEY_SOUND_DEVICE_IN, conf.soundDeviceIn); + conf.channelsOut = j.value(CONF_KEY_CHANNELS_OUT, conf.channelsOut); + conf.channelsInCount = j.value(CONF_KEY_CHANNELS_IN_COUNT, conf.channelsInCount); + conf.channelsInStart = j.value(CONF_KEY_CHANNELS_IN_START, conf.channelsInStart); + conf.samplerate = j.value(CONF_KEY_SAMPLERATE, conf.samplerate); + conf.buffersize = j.value(CONF_KEY_BUFFER_SIZE, conf.buffersize); + conf.limitOutput = j.value(CONF_KEY_LIMIT_OUTPUT, conf.limitOutput); + conf.rsmpQuality = j.value(CONF_KEY_RESAMPLE_QUALITY, conf.rsmpQuality); + conf.midiSystem = j.value(CONF_KEY_MIDI_SYSTEM, conf.midiSystem); + conf.midiPortOut = j.value(CONF_KEY_MIDI_PORT_OUT, conf.midiPortOut); + conf.midiPortIn = j.value(CONF_KEY_MIDI_PORT_IN, conf.midiPortIn); + conf.midiMapPath = j.value(CONF_KEY_MIDIMAP_PATH, conf.midiMapPath); + conf.lastFileMap = j.value(CONF_KEY_LAST_MIDIMAP, conf.lastFileMap); + conf.midiSync = j.value(CONF_KEY_MIDI_SYNC, conf.midiSync); + conf.midiTCfps = j.value(CONF_KEY_MIDI_TC_FPS, conf.midiTCfps); + conf.chansStopOnSeqHalt = j.value(CONF_KEY_CHANS_STOP_ON_SEQ_HALT, conf.chansStopOnSeqHalt); + conf.treatRecsAsLoops = j.value(CONF_KEY_TREAT_RECS_AS_LOOPS, conf.treatRecsAsLoops); + conf.inputMonitorDefaultOn = j.value(CONF_KEY_INPUT_MONITOR_DEFAULT_ON, conf.inputMonitorDefaultOn); + conf.overdubProtectionDefaultOn = j.value(CONF_KEY_OVERDUB_PROTECTION_DEFAULT_ON, conf.overdubProtectionDefaultOn); + conf.pluginPath = j.value(CONF_KEY_PLUGINS_PATH, conf.pluginPath); + conf.patchPath = j.value(CONF_KEY_PATCHES_PATH, conf.patchPath); + conf.samplePath = j.value(CONF_KEY_SAMPLES_PATH, conf.samplePath); + conf.mainWindowX = j.value(CONF_KEY_MAIN_WINDOW_X, conf.mainWindowX); + conf.mainWindowY = j.value(CONF_KEY_MAIN_WINDOW_Y, conf.mainWindowY); + conf.mainWindowW = j.value(CONF_KEY_MAIN_WINDOW_W, conf.mainWindowW); + conf.mainWindowH = j.value(CONF_KEY_MAIN_WINDOW_H, conf.mainWindowH); + conf.browserX = j.value(CONF_KEY_BROWSER_X, conf.browserX); + conf.browserY = j.value(CONF_KEY_BROWSER_Y, conf.browserY); + conf.browserW = j.value(CONF_KEY_BROWSER_W, conf.browserW); + conf.browserH = j.value(CONF_KEY_BROWSER_H, conf.browserH); + conf.browserPosition = j.value(CONF_KEY_BROWSER_POSITION, conf.browserPosition); + conf.browserLastPath = j.value(CONF_KEY_BROWSER_LAST_PATH, conf.browserLastPath); + conf.browserLastValue = j.value(CONF_KEY_BROWSER_LAST_VALUE, conf.browserLastValue); + conf.actionEditorX = j.value(CONF_KEY_ACTION_EDITOR_X, conf.actionEditorX); + conf.actionEditorY = j.value(CONF_KEY_ACTION_EDITOR_Y, conf.actionEditorY); + conf.actionEditorW = j.value(CONF_KEY_ACTION_EDITOR_W, conf.actionEditorW); + conf.actionEditorH = j.value(CONF_KEY_ACTION_EDITOR_H, conf.actionEditorH); + conf.actionEditorZoom = j.value(CONF_KEY_ACTION_EDITOR_ZOOM, conf.actionEditorZoom); + conf.actionEditorGridVal = j.value(CONF_KEY_ACTION_EDITOR_GRID_VAL, conf.actionEditorGridVal); + conf.actionEditorGridOn = j.value(CONF_KEY_ACTION_EDITOR_GRID_ON, conf.actionEditorGridOn); + conf.sampleEditorX = j.value(CONF_KEY_SAMPLE_EDITOR_X, conf.sampleEditorX); + conf.sampleEditorY = j.value(CONF_KEY_SAMPLE_EDITOR_Y, conf.sampleEditorY); + conf.sampleEditorW = j.value(CONF_KEY_SAMPLE_EDITOR_W, conf.sampleEditorW); + conf.sampleEditorH = j.value(CONF_KEY_SAMPLE_EDITOR_H, conf.sampleEditorH); + conf.sampleEditorGridVal = j.value(CONF_KEY_SAMPLE_EDITOR_GRID_VAL, conf.sampleEditorGridVal); + conf.sampleEditorGridOn = j.value(CONF_KEY_SAMPLE_EDITOR_GRID_ON, conf.sampleEditorGridOn); + conf.pianoRollY = j.value(CONF_KEY_PIANO_ROLL_Y, conf.pianoRollY); + conf.pianoRollH = j.value(CONF_KEY_PIANO_ROLL_H, conf.pianoRollH); + conf.sampleActionEditorH = j.value(CONF_KEY_SAMPLE_ACTION_EDITOR_H, conf.sampleActionEditorH); + conf.velocityEditorH = j.value(CONF_KEY_VELOCITY_EDITOR_H, conf.velocityEditorH); + conf.envelopeEditorH = j.value(CONF_KEY_ENVELOPE_EDITOR_H, conf.envelopeEditorH); + conf.pluginListX = j.value(CONF_KEY_PLUGIN_LIST_X, conf.pluginListX); + conf.pluginListY = j.value(CONF_KEY_PLUGIN_LIST_Y, conf.pluginListY); + conf.midiInputX = j.value(CONF_KEY_MIDI_INPUT_X, conf.midiInputX); + conf.midiInputY = j.value(CONF_KEY_MIDI_INPUT_Y, conf.midiInputY); + conf.midiInputW = j.value(CONF_KEY_MIDI_INPUT_W, conf.midiInputW); + conf.midiInputH = j.value(CONF_KEY_MIDI_INPUT_H, conf.midiInputH); + conf.recTriggerMode = j.value(CONF_KEY_REC_TRIGGER_MODE, conf.recTriggerMode); + conf.recTriggerLevel = j.value(CONF_KEY_REC_TRIGGER_LEVEL, conf.recTriggerLevel); + conf.midiInEnabled = j.value(CONF_KEY_MIDI_IN, conf.midiInEnabled); + conf.midiInFilter = j.value(CONF_KEY_MIDI_IN_FILTER, conf.midiInFilter); + conf.midiInRewind = j.value(CONF_KEY_MIDI_IN_REWIND, conf.midiInRewind); + conf.midiInStartStop = j.value(CONF_KEY_MIDI_IN_START_STOP, conf.midiInStartStop); + conf.midiInActionRec = j.value(CONF_KEY_MIDI_IN_ACTION_REC, conf.midiInActionRec); + conf.midiInInputRec = j.value(CONF_KEY_MIDI_IN_INPUT_REC, conf.midiInInputRec); + conf.midiInMetronome = j.value(CONF_KEY_MIDI_IN_METRONOME, conf.midiInMetronome); + conf.midiInVolumeIn = j.value(CONF_KEY_MIDI_IN_VOLUME_IN, conf.midiInVolumeIn); + conf.midiInVolumeOut = j.value(CONF_KEY_MIDI_IN_VOLUME_OUT, conf.midiInVolumeOut); + conf.midiInBeatDouble = j.value(CONF_KEY_MIDI_IN_BEAT_DOUBLE, conf.midiInBeatDouble); + conf.midiInBeatHalf = j.value(CONF_KEY_MIDI_IN_BEAT_HALF, conf.midiInBeatHalf); #ifdef WITH_VST - conf.pluginChooserX = j.value(CONF_KEY_PLUGIN_CHOOSER_X, conf.pluginChooserX); - conf.pluginChooserY = j.value(CONF_KEY_PLUGIN_CHOOSER_Y, conf.pluginChooserY); - conf.pluginChooserW = j.value(CONF_KEY_PLUGIN_CHOOSER_W, conf.pluginChooserW); - conf.pluginChooserH = j.value(CONF_KEY_PLUGIN_CHOOSER_H, conf.pluginChooserH); - conf.pluginSortMethod = j.value(CONF_KEY_PLUGIN_SORT_METHOD, conf.pluginSortMethod); + conf.pluginChooserX = j.value(CONF_KEY_PLUGIN_CHOOSER_X, conf.pluginChooserX); + conf.pluginChooserY = j.value(CONF_KEY_PLUGIN_CHOOSER_Y, conf.pluginChooserY); + conf.pluginChooserW = j.value(CONF_KEY_PLUGIN_CHOOSER_W, conf.pluginChooserW); + conf.pluginChooserH = j.value(CONF_KEY_PLUGIN_CHOOSER_H, conf.pluginChooserH); + conf.pluginSortMethod = j.value(CONF_KEY_PLUGIN_SORT_METHOD, conf.pluginSortMethod); #endif return true; @@ -223,84 +225,86 @@ bool write() nl::json j; - j[CONF_KEY_HEADER] = "GIADACFG"; - j[CONF_KEY_LOG_MODE] = conf.logMode; - j[CONF_KEY_SOUND_SYSTEM] = conf.soundSystem; - j[CONF_KEY_SOUND_DEVICE_OUT] = conf.soundDeviceOut; - j[CONF_KEY_SOUND_DEVICE_IN] = conf.soundDeviceIn; - j[CONF_KEY_CHANNELS_OUT] = conf.channelsOut; - j[CONF_KEY_CHANNELS_IN] = conf.channelsIn; - j[CONF_KEY_SAMPLERATE] = conf.samplerate; - j[CONF_KEY_BUFFER_SIZE] = conf.buffersize; - j[CONF_KEY_LIMIT_OUTPUT] = conf.limitOutput; - j[CONF_KEY_RESAMPLE_QUALITY] = conf.rsmpQuality; - j[CONF_KEY_MIDI_SYSTEM] = conf.midiSystem; - j[CONF_KEY_MIDI_PORT_OUT] = conf.midiPortOut; - j[CONF_KEY_MIDI_PORT_IN] = conf.midiPortIn; - j[CONF_KEY_MIDIMAP_PATH] = conf.midiMapPath; - j[CONF_KEY_LAST_MIDIMAP] = conf.lastFileMap; - j[CONF_KEY_MIDI_SYNC] = conf.midiSync; - j[CONF_KEY_MIDI_TC_FPS] = conf.midiTCfps; - j[CONF_KEY_MIDI_IN] = conf.midiInEnabled; - j[CONF_KEY_MIDI_IN_FILTER] = conf.midiInFilter; - j[CONF_KEY_MIDI_IN_REWIND] = conf.midiInRewind; - j[CONF_KEY_MIDI_IN_START_STOP] = conf.midiInStartStop; - j[CONF_KEY_MIDI_IN_ACTION_REC] = conf.midiInActionRec; - j[CONF_KEY_MIDI_IN_INPUT_REC] = conf.midiInInputRec; - j[CONF_KEY_MIDI_IN_METRONOME] = conf.midiInMetronome; - j[CONF_KEY_MIDI_IN_VOLUME_IN] = conf.midiInVolumeIn; - j[CONF_KEY_MIDI_IN_VOLUME_OUT] = conf.midiInVolumeOut; - j[CONF_KEY_MIDI_IN_BEAT_DOUBLE] = conf.midiInBeatDouble; - j[CONF_KEY_MIDI_IN_BEAT_HALF] = conf.midiInBeatHalf; - j[CONF_KEY_CHANS_STOP_ON_SEQ_HALT] = conf.chansStopOnSeqHalt; - j[CONF_KEY_TREAT_RECS_AS_LOOPS] = conf.treatRecsAsLoops; - j[CONF_KEY_INPUT_MONITOR_DEFAULT_ON] = conf.inputMonitorDefaultOn; - j[CONF_KEY_PLUGINS_PATH] = conf.pluginPath; - j[CONF_KEY_PATCHES_PATH] = conf.patchPath; - j[CONF_KEY_SAMPLES_PATH] = conf.samplePath; - j[CONF_KEY_MAIN_WINDOW_X] = conf.mainWindowX; - j[CONF_KEY_MAIN_WINDOW_Y] = conf.mainWindowY; - j[CONF_KEY_MAIN_WINDOW_W] = conf.mainWindowW; - j[CONF_KEY_MAIN_WINDOW_H] = conf.mainWindowH; - j[CONF_KEY_BROWSER_X] = conf.browserX; - j[CONF_KEY_BROWSER_Y] = conf.browserY; - j[CONF_KEY_BROWSER_W] = conf.browserW; - j[CONF_KEY_BROWSER_H] = conf.browserH; - j[CONF_KEY_BROWSER_POSITION] = conf.browserPosition; - j[CONF_KEY_BROWSER_LAST_PATH] = conf.browserLastPath; - j[CONF_KEY_BROWSER_LAST_VALUE] = conf.browserLastValue; - j[CONF_KEY_ACTION_EDITOR_X] = conf.actionEditorX; - j[CONF_KEY_ACTION_EDITOR_Y] = conf.actionEditorY; - j[CONF_KEY_ACTION_EDITOR_W] = conf.actionEditorW; - j[CONF_KEY_ACTION_EDITOR_H] = conf.actionEditorH; - j[CONF_KEY_ACTION_EDITOR_ZOOM] = conf.actionEditorZoom; - j[CONF_KEY_ACTION_EDITOR_GRID_VAL] = conf.actionEditorGridVal; - j[CONF_KEY_ACTION_EDITOR_GRID_ON] = conf.actionEditorGridOn; - j[CONF_KEY_SAMPLE_EDITOR_X] = conf.sampleEditorX; - j[CONF_KEY_SAMPLE_EDITOR_Y] = conf.sampleEditorY; - j[CONF_KEY_SAMPLE_EDITOR_W] = conf.sampleEditorW; - j[CONF_KEY_SAMPLE_EDITOR_H] = conf.sampleEditorH; - j[CONF_KEY_SAMPLE_EDITOR_GRID_VAL] = conf.sampleEditorGridVal; - j[CONF_KEY_SAMPLE_EDITOR_GRID_ON] = conf.sampleEditorGridOn; - j[CONF_KEY_PIANO_ROLL_Y] = conf.pianoRollY; - j[CONF_KEY_PIANO_ROLL_H] = conf.pianoRollH; - j[CONF_KEY_SAMPLE_ACTION_EDITOR_H] = conf.sampleActionEditorH; - j[CONF_KEY_VELOCITY_EDITOR_H] = conf.velocityEditorH; - j[CONF_KEY_ENVELOPE_EDITOR_H] = conf.envelopeEditorH; - j[CONF_KEY_PLUGIN_LIST_X] = conf.pluginListX; - j[CONF_KEY_PLUGIN_LIST_Y] = conf.pluginListY; - j[CONF_KEY_MIDI_INPUT_X] = conf.midiInputX; - j[CONF_KEY_MIDI_INPUT_Y] = conf.midiInputY; - j[CONF_KEY_MIDI_INPUT_W] = conf.midiInputW; - j[CONF_KEY_MIDI_INPUT_H] = conf.midiInputH; - j[CONF_KEY_REC_TRIGGER_MODE] = static_cast(conf.recTriggerMode); - j[CONF_KEY_REC_TRIGGER_LEVEL] = conf.recTriggerLevel; + j[CONF_KEY_HEADER] = "GIADACFG"; + j[CONF_KEY_LOG_MODE] = conf.logMode; + j[CONF_KEY_SOUND_SYSTEM] = conf.soundSystem; + j[CONF_KEY_SOUND_DEVICE_OUT] = conf.soundDeviceOut; + j[CONF_KEY_SOUND_DEVICE_IN] = conf.soundDeviceIn; + j[CONF_KEY_CHANNELS_OUT] = conf.channelsOut; + j[CONF_KEY_CHANNELS_IN_COUNT] = conf.channelsInCount; + j[CONF_KEY_CHANNELS_IN_START] = conf.channelsInStart; + j[CONF_KEY_SAMPLERATE] = conf.samplerate; + j[CONF_KEY_BUFFER_SIZE] = conf.buffersize; + j[CONF_KEY_LIMIT_OUTPUT] = conf.limitOutput; + j[CONF_KEY_RESAMPLE_QUALITY] = conf.rsmpQuality; + j[CONF_KEY_MIDI_SYSTEM] = conf.midiSystem; + j[CONF_KEY_MIDI_PORT_OUT] = conf.midiPortOut; + j[CONF_KEY_MIDI_PORT_IN] = conf.midiPortIn; + j[CONF_KEY_MIDIMAP_PATH] = conf.midiMapPath; + j[CONF_KEY_LAST_MIDIMAP] = conf.lastFileMap; + j[CONF_KEY_MIDI_SYNC] = conf.midiSync; + j[CONF_KEY_MIDI_TC_FPS] = conf.midiTCfps; + j[CONF_KEY_MIDI_IN] = conf.midiInEnabled; + j[CONF_KEY_MIDI_IN_FILTER] = conf.midiInFilter; + j[CONF_KEY_MIDI_IN_REWIND] = conf.midiInRewind; + j[CONF_KEY_MIDI_IN_START_STOP] = conf.midiInStartStop; + j[CONF_KEY_MIDI_IN_ACTION_REC] = conf.midiInActionRec; + j[CONF_KEY_MIDI_IN_INPUT_REC] = conf.midiInInputRec; + j[CONF_KEY_MIDI_IN_METRONOME] = conf.midiInMetronome; + j[CONF_KEY_MIDI_IN_VOLUME_IN] = conf.midiInVolumeIn; + j[CONF_KEY_MIDI_IN_VOLUME_OUT] = conf.midiInVolumeOut; + j[CONF_KEY_MIDI_IN_BEAT_DOUBLE] = conf.midiInBeatDouble; + j[CONF_KEY_MIDI_IN_BEAT_HALF] = conf.midiInBeatHalf; + j[CONF_KEY_CHANS_STOP_ON_SEQ_HALT] = conf.chansStopOnSeqHalt; + j[CONF_KEY_TREAT_RECS_AS_LOOPS] = conf.treatRecsAsLoops; + j[CONF_KEY_INPUT_MONITOR_DEFAULT_ON] = conf.inputMonitorDefaultOn; + j[CONF_KEY_OVERDUB_PROTECTION_DEFAULT_ON] = conf.overdubProtectionDefaultOn; + j[CONF_KEY_PLUGINS_PATH] = conf.pluginPath; + j[CONF_KEY_PATCHES_PATH] = conf.patchPath; + j[CONF_KEY_SAMPLES_PATH] = conf.samplePath; + j[CONF_KEY_MAIN_WINDOW_X] = conf.mainWindowX; + j[CONF_KEY_MAIN_WINDOW_Y] = conf.mainWindowY; + j[CONF_KEY_MAIN_WINDOW_W] = conf.mainWindowW; + j[CONF_KEY_MAIN_WINDOW_H] = conf.mainWindowH; + j[CONF_KEY_BROWSER_X] = conf.browserX; + j[CONF_KEY_BROWSER_Y] = conf.browserY; + j[CONF_KEY_BROWSER_W] = conf.browserW; + j[CONF_KEY_BROWSER_H] = conf.browserH; + j[CONF_KEY_BROWSER_POSITION] = conf.browserPosition; + j[CONF_KEY_BROWSER_LAST_PATH] = conf.browserLastPath; + j[CONF_KEY_BROWSER_LAST_VALUE] = conf.browserLastValue; + j[CONF_KEY_ACTION_EDITOR_X] = conf.actionEditorX; + j[CONF_KEY_ACTION_EDITOR_Y] = conf.actionEditorY; + j[CONF_KEY_ACTION_EDITOR_W] = conf.actionEditorW; + j[CONF_KEY_ACTION_EDITOR_H] = conf.actionEditorH; + j[CONF_KEY_ACTION_EDITOR_ZOOM] = conf.actionEditorZoom; + j[CONF_KEY_ACTION_EDITOR_GRID_VAL] = conf.actionEditorGridVal; + j[CONF_KEY_ACTION_EDITOR_GRID_ON] = conf.actionEditorGridOn; + j[CONF_KEY_SAMPLE_EDITOR_X] = conf.sampleEditorX; + j[CONF_KEY_SAMPLE_EDITOR_Y] = conf.sampleEditorY; + j[CONF_KEY_SAMPLE_EDITOR_W] = conf.sampleEditorW; + j[CONF_KEY_SAMPLE_EDITOR_H] = conf.sampleEditorH; + j[CONF_KEY_SAMPLE_EDITOR_GRID_VAL] = conf.sampleEditorGridVal; + j[CONF_KEY_SAMPLE_EDITOR_GRID_ON] = conf.sampleEditorGridOn; + j[CONF_KEY_PIANO_ROLL_Y] = conf.pianoRollY; + j[CONF_KEY_PIANO_ROLL_H] = conf.pianoRollH; + j[CONF_KEY_SAMPLE_ACTION_EDITOR_H] = conf.sampleActionEditorH; + j[CONF_KEY_VELOCITY_EDITOR_H] = conf.velocityEditorH; + j[CONF_KEY_ENVELOPE_EDITOR_H] = conf.envelopeEditorH; + j[CONF_KEY_PLUGIN_LIST_X] = conf.pluginListX; + j[CONF_KEY_PLUGIN_LIST_Y] = conf.pluginListY; + j[CONF_KEY_MIDI_INPUT_X] = conf.midiInputX; + j[CONF_KEY_MIDI_INPUT_Y] = conf.midiInputY; + j[CONF_KEY_MIDI_INPUT_W] = conf.midiInputW; + j[CONF_KEY_MIDI_INPUT_H] = conf.midiInputH; + j[CONF_KEY_REC_TRIGGER_MODE] = static_cast(conf.recTriggerMode); + j[CONF_KEY_REC_TRIGGER_LEVEL] = conf.recTriggerLevel; #ifdef WITH_VST - j[CONF_KEY_PLUGIN_CHOOSER_X] = conf.pluginChooserX; - j[CONF_KEY_PLUGIN_CHOOSER_Y] = conf.pluginChooserY; - j[CONF_KEY_PLUGIN_CHOOSER_W] = conf.pluginChooserW; - j[CONF_KEY_PLUGIN_CHOOSER_H] = conf.pluginChooserH; - j[CONF_KEY_PLUGIN_SORT_METHOD] = conf.pluginSortMethod; + j[CONF_KEY_PLUGIN_CHOOSER_X] = conf.pluginChooserX; + j[CONF_KEY_PLUGIN_CHOOSER_Y] = conf.pluginChooserY; + j[CONF_KEY_PLUGIN_CHOOSER_W] = conf.pluginChooserW; + j[CONF_KEY_PLUGIN_CHOOSER_H] = conf.pluginChooserH; + j[CONF_KEY_PLUGIN_SORT_METHOD] = conf.pluginSortMethod; #endif std::ofstream ofs(confFilePath_); diff --git a/src/core/conf.h b/src/core/conf.h index d7dc210..c71ec2d 100644 --- a/src/core/conf.h +++ b/src/core/conf.h @@ -41,16 +41,17 @@ namespace conf { struct Conf { - int logMode = LOG_MODE_MUTE; - int soundSystem = G_DEFAULT_SOUNDSYS; - int soundDeviceOut = G_DEFAULT_SOUNDDEV_OUT; - int soundDeviceIn = G_DEFAULT_SOUNDDEV_IN; - int channelsOut = 0; - int channelsIn = 0; - int samplerate = G_DEFAULT_SAMPLERATE; - int buffersize = G_DEFAULT_BUFSIZE; - bool limitOutput = false; - int rsmpQuality = 0; + int logMode = LOG_MODE_MUTE; + int soundSystem = G_DEFAULT_SOUNDSYS; + int soundDeviceOut = G_DEFAULT_SOUNDDEV_OUT; + int soundDeviceIn = G_DEFAULT_SOUNDDEV_IN; + int channelsOut = 0; + int channelsInCount = 0; + int channelsInStart = 0; + int samplerate = G_DEFAULT_SAMPLERATE; + int buffersize = G_DEFAULT_BUFSIZE; + bool limitOutput = false; + int rsmpQuality = 0; int midiSystem = 0; int midiPortOut = G_DEFAULT_MIDI_PORT_OUT; @@ -60,9 +61,10 @@ struct Conf int midiSync = MIDI_SYNC_NONE; float midiTCfps = 25.0f; - bool chansStopOnSeqHalt = false; - bool treatRecsAsLoops = false; - bool inputMonitorDefaultOn = false; + bool chansStopOnSeqHalt = false; + bool treatRecsAsLoops = false; + bool inputMonitorDefaultOn = false; + bool overdubProtectionDefaultOn = false; std::string pluginPath; std::string patchPath; diff --git a/src/core/const.h b/src/core/const.h index 21999d6..aa0d1d3 100644 --- a/src/core/const.h +++ b/src/core/const.h @@ -60,10 +60,10 @@ /* -- version --------------------------------------------------------------- */ constexpr auto G_APP_NAME = "Giada"; -constexpr auto G_VERSION_STR = "0.16.3"; +constexpr auto G_VERSION_STR = "0.16.4"; constexpr int G_VERSION_MAJOR = 0; constexpr int G_VERSION_MINOR = 16; -constexpr int G_VERSION_PATCH = 3; +constexpr int G_VERSION_PATCH = 4; constexpr auto CONF_FILENAME = "giada.conf"; @@ -281,22 +281,22 @@ constexpr uint32_t G_MIDI_ALL_NOTES_OFF = (G_MIDI_CONTROLLER) | (0x7B << 16); /* system common / real-time messages. Single bytes */ -#define MIDI_SYSEX 0xF0 -#define MIDI_MTC_QUARTER 0xF1 -#define MIDI_POSITION_PTR 0xF2 -#define MIDI_CLOCK 0xF8 -#define MIDI_START 0xFA -#define MIDI_CONTINUE 0xFB -#define MIDI_STOP 0xFC -#define MIDI_EOX 0xF7 // end of sysex +constexpr int MIDI_SYSEX = 0xF0; +constexpr int MIDI_MTC_QUARTER = 0xF1; +constexpr int MIDI_POSITION_PTR = 0xF2; +constexpr int MIDI_CLOCK = 0xF8; +constexpr int MIDI_START = 0xFA; +constexpr int MIDI_CONTINUE = 0xFB; +constexpr int MIDI_STOP = 0xFC; +constexpr int MIDI_EOX = 0xF7; // end of sysex /* midi sync constants */ -#define MIDI_SYNC_NONE 0x00 -#define MIDI_SYNC_CLOCK_M 0x01 // master -#define MIDI_SYNC_CLOCK_S 0x02 // slave -#define MIDI_SYNC_MTC_M 0x04 // master -#define MIDI_SYNC_MTC_S 0x08 // slave +constexpr int MIDI_SYNC_NONE = 0x00; +constexpr int MIDI_SYNC_CLOCK_M = 0x01; // master +constexpr int MIDI_SYNC_CLOCK_S = 0x02; // slave +constexpr int MIDI_SYNC_MTC_M = 0x04; // master +constexpr int MIDI_SYNC_MTC_S = 0x08; // slave /* JSON patch keys */ @@ -352,6 +352,7 @@ constexpr auto PATCH_KEY_CHANNEL_HAS_ACTIONS = "has_actions"; constexpr auto PATCH_KEY_CHANNEL_READ_ACTIONS = "read_actions"; constexpr auto PATCH_KEY_CHANNEL_PITCH = "pitch"; constexpr auto PATCH_KEY_CHANNEL_INPUT_MONITOR = "input_monitor"; +constexpr auto PATCH_KEY_CHANNEL_OVERDUB_PROTECTION = "overdub_protection"; constexpr auto PATCH_KEY_CHANNEL_MIDI_IN_READ_ACTIONS = "midi_in_read_actions"; constexpr auto PATCH_KEY_CHANNEL_MIDI_IN_PITCH = "midi_in_pitch"; constexpr auto PATCH_KEY_CHANNEL_MIDI_OUT = "midi_out"; @@ -384,84 +385,86 @@ constexpr auto G_PATCH_KEY_ACTION_NEXT = "next"; /* JSON config keys */ -constexpr auto CONF_KEY_HEADER = "header"; -constexpr auto CONF_KEY_LOG_MODE = "log_mode"; -constexpr auto CONF_KEY_SOUND_SYSTEM = "sound_system"; -constexpr auto CONF_KEY_SOUND_DEVICE_IN = "sound_device_in"; -constexpr auto CONF_KEY_SOUND_DEVICE_OUT = "sound_device_out"; -constexpr auto CONF_KEY_CHANNELS_IN = "channels_in"; -constexpr auto CONF_KEY_CHANNELS_OUT = "channels_out"; -constexpr auto CONF_KEY_SAMPLERATE = "samplerate"; -constexpr auto CONF_KEY_BUFFER_SIZE = "buffer_size"; -constexpr auto CONF_KEY_DELAY_COMPENSATION = "delay_compensation"; -constexpr auto CONF_KEY_LIMIT_OUTPUT = "limit_output"; -constexpr auto CONF_KEY_RESAMPLE_QUALITY = "resample_quality"; -constexpr auto CONF_KEY_MIDI_SYSTEM = "midi_system"; -constexpr auto CONF_KEY_MIDI_PORT_OUT = "midi_port_out"; -constexpr auto CONF_KEY_MIDI_PORT_IN = "midi_port_in"; -constexpr auto CONF_KEY_MIDIMAP_PATH = "midimap_path"; -constexpr auto CONF_KEY_LAST_MIDIMAP = "last_midimap"; -constexpr auto CONF_KEY_MIDI_SYNC = "midi_sync"; -constexpr auto CONF_KEY_MIDI_TC_FPS = "midi_tc_fps"; -constexpr auto CONF_KEY_MIDI_IN = "midi_in"; -constexpr auto CONF_KEY_MIDI_IN_FILTER = "midi_in_filter"; -constexpr auto CONF_KEY_MIDI_IN_REWIND = "midi_in_rewind"; -constexpr auto CONF_KEY_MIDI_IN_START_STOP = "midi_in_start_stop"; -constexpr auto CONF_KEY_MIDI_IN_ACTION_REC = "midi_in_action_rec"; -constexpr auto CONF_KEY_MIDI_IN_INPUT_REC = "midi_in_input_rec"; -constexpr auto CONF_KEY_MIDI_IN_METRONOME = "midi_in_metronome"; -constexpr auto CONF_KEY_MIDI_IN_VOLUME_IN = "midi_in_volume_in"; -constexpr auto CONF_KEY_MIDI_IN_VOLUME_OUT = "midi_in_volume_out"; -constexpr auto CONF_KEY_MIDI_IN_BEAT_DOUBLE = "midi_in_beat_doble"; -constexpr auto CONF_KEY_MIDI_IN_BEAT_HALF = "midi_in_beat_half"; -constexpr auto CONF_KEY_CHANS_STOP_ON_SEQ_HALT = "chans_stop_on_seq_halt"; -constexpr auto CONF_KEY_TREAT_RECS_AS_LOOPS = "treat_recs_as_loops"; -constexpr auto CONF_KEY_INPUT_MONITOR_DEFAULT_ON = "input_monitor_default_on"; -constexpr auto CONF_KEY_PLUGINS_PATH = "plugins_path"; -constexpr auto CONF_KEY_PATCHES_PATH = "patches_path"; -constexpr auto CONF_KEY_SAMPLES_PATH = "samples_path"; -constexpr auto CONF_KEY_MAIN_WINDOW_X = "main_window_x"; -constexpr auto CONF_KEY_MAIN_WINDOW_Y = "main_window_y"; -constexpr auto CONF_KEY_MAIN_WINDOW_W = "main_window_w"; -constexpr auto CONF_KEY_MAIN_WINDOW_H = "main_window_h"; -constexpr auto CONF_KEY_BROWSER_X = "browser_x"; -constexpr auto CONF_KEY_BROWSER_Y = "browser_y"; -constexpr auto CONF_KEY_BROWSER_W = "browser_w"; -constexpr auto CONF_KEY_BROWSER_H = "browser_h"; -constexpr auto CONF_KEY_BROWSER_POSITION = "browser_position"; -constexpr auto CONF_KEY_BROWSER_LAST_PATH = "browser_last_path"; -constexpr auto CONF_KEY_BROWSER_LAST_VALUE = "browser_last_value"; -constexpr auto CONF_KEY_ACTION_EDITOR_X = "action_editor_x"; -constexpr auto CONF_KEY_ACTION_EDITOR_Y = "action_editor_y"; -constexpr auto CONF_KEY_ACTION_EDITOR_W = "action_editor_w"; -constexpr auto CONF_KEY_ACTION_EDITOR_H = "action_editor_h"; -constexpr auto CONF_KEY_ACTION_EDITOR_ZOOM = "action_editor_zoom"; -constexpr auto CONF_KEY_ACTION_EDITOR_GRID_VAL = "action_editor_grid_val"; -constexpr auto CONF_KEY_ACTION_EDITOR_GRID_ON = "action_editor_grid_on"; -constexpr auto CONF_KEY_SAMPLE_EDITOR_X = "sample_editor_x"; -constexpr auto CONF_KEY_SAMPLE_EDITOR_Y = "sample_editor_y"; -constexpr auto CONF_KEY_SAMPLE_EDITOR_W = "sample_editor_w"; -constexpr auto CONF_KEY_SAMPLE_EDITOR_H = "sample_editor_h"; -constexpr auto CONF_KEY_SAMPLE_EDITOR_GRID_VAL = "sample_editor_grid_val"; -constexpr auto CONF_KEY_SAMPLE_EDITOR_GRID_ON = "sample_editor_grid_on"; -constexpr auto CONF_KEY_PIANO_ROLL_Y = "piano_roll_y"; -constexpr auto CONF_KEY_PIANO_ROLL_H = "piano_roll_h"; -constexpr auto CONF_KEY_SAMPLE_ACTION_EDITOR_H = "sample_action_editor_h"; -constexpr auto CONF_KEY_VELOCITY_EDITOR_H = "velocity_editor_h"; -constexpr auto CONF_KEY_ENVELOPE_EDITOR_H = "envelope_editor_h"; -constexpr auto CONF_KEY_PLUGIN_LIST_X = "plugin_list_x"; -constexpr auto CONF_KEY_PLUGIN_LIST_Y = "plugin_list_y"; -constexpr auto CONF_KEY_PLUGIN_CHOOSER_X = "plugin_chooser_x"; -constexpr auto CONF_KEY_PLUGIN_CHOOSER_Y = "plugin_chooser_y"; -constexpr auto CONF_KEY_PLUGIN_CHOOSER_W = "plugin_chooser_w"; -constexpr auto CONF_KEY_PLUGIN_CHOOSER_H = "plugin_chooser_h"; -constexpr auto CONF_KEY_MIDI_INPUT_X = "midi_input_x"; -constexpr auto CONF_KEY_MIDI_INPUT_Y = "midi_input_y"; -constexpr auto CONF_KEY_MIDI_INPUT_W = "midi_input_w"; -constexpr auto CONF_KEY_MIDI_INPUT_H = "midi_input_h"; -constexpr auto CONF_KEY_PLUGIN_SORT_METHOD = "plugin_sort_method"; -constexpr auto CONF_KEY_REC_TRIGGER_MODE = "rec_trigger_mode"; -constexpr auto CONF_KEY_REC_TRIGGER_LEVEL = "rec_trigger_level"; +constexpr auto CONF_KEY_HEADER = "header"; +constexpr auto CONF_KEY_LOG_MODE = "log_mode"; +constexpr auto CONF_KEY_SOUND_SYSTEM = "sound_system"; +constexpr auto CONF_KEY_SOUND_DEVICE_IN = "sound_device_in"; +constexpr auto CONF_KEY_SOUND_DEVICE_OUT = "sound_device_out"; +constexpr auto CONF_KEY_CHANNELS_OUT = "channels_out"; +constexpr auto CONF_KEY_CHANNELS_IN_COUNT = "channels_in_count"; +constexpr auto CONF_KEY_CHANNELS_IN_START = "channels_in_start"; +constexpr auto CONF_KEY_SAMPLERATE = "samplerate"; +constexpr auto CONF_KEY_BUFFER_SIZE = "buffer_size"; +constexpr auto CONF_KEY_DELAY_COMPENSATION = "delay_compensation"; +constexpr auto CONF_KEY_LIMIT_OUTPUT = "limit_output"; +constexpr auto CONF_KEY_RESAMPLE_QUALITY = "resample_quality"; +constexpr auto CONF_KEY_MIDI_SYSTEM = "midi_system"; +constexpr auto CONF_KEY_MIDI_PORT_OUT = "midi_port_out"; +constexpr auto CONF_KEY_MIDI_PORT_IN = "midi_port_in"; +constexpr auto CONF_KEY_MIDIMAP_PATH = "midimap_path"; +constexpr auto CONF_KEY_LAST_MIDIMAP = "last_midimap"; +constexpr auto CONF_KEY_MIDI_SYNC = "midi_sync"; +constexpr auto CONF_KEY_MIDI_TC_FPS = "midi_tc_fps"; +constexpr auto CONF_KEY_MIDI_IN = "midi_in"; +constexpr auto CONF_KEY_MIDI_IN_FILTER = "midi_in_filter"; +constexpr auto CONF_KEY_MIDI_IN_REWIND = "midi_in_rewind"; +constexpr auto CONF_KEY_MIDI_IN_START_STOP = "midi_in_start_stop"; +constexpr auto CONF_KEY_MIDI_IN_ACTION_REC = "midi_in_action_rec"; +constexpr auto CONF_KEY_MIDI_IN_INPUT_REC = "midi_in_input_rec"; +constexpr auto CONF_KEY_MIDI_IN_METRONOME = "midi_in_metronome"; +constexpr auto CONF_KEY_MIDI_IN_VOLUME_IN = "midi_in_volume_in"; +constexpr auto CONF_KEY_MIDI_IN_VOLUME_OUT = "midi_in_volume_out"; +constexpr auto CONF_KEY_MIDI_IN_BEAT_DOUBLE = "midi_in_beat_doble"; +constexpr auto CONF_KEY_MIDI_IN_BEAT_HALF = "midi_in_beat_half"; +constexpr auto CONF_KEY_CHANS_STOP_ON_SEQ_HALT = "chans_stop_on_seq_halt"; +constexpr auto CONF_KEY_TREAT_RECS_AS_LOOPS = "treat_recs_as_loops"; +constexpr auto CONF_KEY_INPUT_MONITOR_DEFAULT_ON = "input_monitor_default_on"; +constexpr auto CONF_KEY_OVERDUB_PROTECTION_DEFAULT_ON = "overdub_protection_default_on"; +constexpr auto CONF_KEY_PLUGINS_PATH = "plugins_path"; +constexpr auto CONF_KEY_PATCHES_PATH = "patches_path"; +constexpr auto CONF_KEY_SAMPLES_PATH = "samples_path"; +constexpr auto CONF_KEY_MAIN_WINDOW_X = "main_window_x"; +constexpr auto CONF_KEY_MAIN_WINDOW_Y = "main_window_y"; +constexpr auto CONF_KEY_MAIN_WINDOW_W = "main_window_w"; +constexpr auto CONF_KEY_MAIN_WINDOW_H = "main_window_h"; +constexpr auto CONF_KEY_BROWSER_X = "browser_x"; +constexpr auto CONF_KEY_BROWSER_Y = "browser_y"; +constexpr auto CONF_KEY_BROWSER_W = "browser_w"; +constexpr auto CONF_KEY_BROWSER_H = "browser_h"; +constexpr auto CONF_KEY_BROWSER_POSITION = "browser_position"; +constexpr auto CONF_KEY_BROWSER_LAST_PATH = "browser_last_path"; +constexpr auto CONF_KEY_BROWSER_LAST_VALUE = "browser_last_value"; +constexpr auto CONF_KEY_ACTION_EDITOR_X = "action_editor_x"; +constexpr auto CONF_KEY_ACTION_EDITOR_Y = "action_editor_y"; +constexpr auto CONF_KEY_ACTION_EDITOR_W = "action_editor_w"; +constexpr auto CONF_KEY_ACTION_EDITOR_H = "action_editor_h"; +constexpr auto CONF_KEY_ACTION_EDITOR_ZOOM = "action_editor_zoom"; +constexpr auto CONF_KEY_ACTION_EDITOR_GRID_VAL = "action_editor_grid_val"; +constexpr auto CONF_KEY_ACTION_EDITOR_GRID_ON = "action_editor_grid_on"; +constexpr auto CONF_KEY_SAMPLE_EDITOR_X = "sample_editor_x"; +constexpr auto CONF_KEY_SAMPLE_EDITOR_Y = "sample_editor_y"; +constexpr auto CONF_KEY_SAMPLE_EDITOR_W = "sample_editor_w"; +constexpr auto CONF_KEY_SAMPLE_EDITOR_H = "sample_editor_h"; +constexpr auto CONF_KEY_SAMPLE_EDITOR_GRID_VAL = "sample_editor_grid_val"; +constexpr auto CONF_KEY_SAMPLE_EDITOR_GRID_ON = "sample_editor_grid_on"; +constexpr auto CONF_KEY_PIANO_ROLL_Y = "piano_roll_y"; +constexpr auto CONF_KEY_PIANO_ROLL_H = "piano_roll_h"; +constexpr auto CONF_KEY_SAMPLE_ACTION_EDITOR_H = "sample_action_editor_h"; +constexpr auto CONF_KEY_VELOCITY_EDITOR_H = "velocity_editor_h"; +constexpr auto CONF_KEY_ENVELOPE_EDITOR_H = "envelope_editor_h"; +constexpr auto CONF_KEY_PLUGIN_LIST_X = "plugin_list_x"; +constexpr auto CONF_KEY_PLUGIN_LIST_Y = "plugin_list_y"; +constexpr auto CONF_KEY_PLUGIN_CHOOSER_X = "plugin_chooser_x"; +constexpr auto CONF_KEY_PLUGIN_CHOOSER_Y = "plugin_chooser_y"; +constexpr auto CONF_KEY_PLUGIN_CHOOSER_W = "plugin_chooser_w"; +constexpr auto CONF_KEY_PLUGIN_CHOOSER_H = "plugin_chooser_h"; +constexpr auto CONF_KEY_MIDI_INPUT_X = "midi_input_x"; +constexpr auto CONF_KEY_MIDI_INPUT_Y = "midi_input_y"; +constexpr auto CONF_KEY_MIDI_INPUT_W = "midi_input_w"; +constexpr auto CONF_KEY_MIDI_INPUT_H = "midi_input_h"; +constexpr auto CONF_KEY_PLUGIN_SORT_METHOD = "plugin_sort_method"; +constexpr auto CONF_KEY_REC_TRIGGER_MODE = "rec_trigger_mode"; +constexpr auto CONF_KEY_REC_TRIGGER_LEVEL = "rec_trigger_level"; /* JSON midimaps keys */ diff --git a/src/core/graphics.cpp b/src/core/graphics.cpp index 74e09a3..3a9e83a 100644 --- a/src/core/graphics.cpp +++ b/src/core/graphics.cpp @@ -1855,3 +1855,31 @@ const char* armOn_xpm[] = { " ", " ", " "}; + +const char* armDisabled_xpm[] = { +"18 18 7 1", +" c None", +". c #232523", +"+ c #303230", +"@ c #393B38", +"# c #424441", +"$ c #4B4D4A", +"% c #4D4F4C", +"..................", +"..................", +"..................", +"..................", +"......+#$$#+......", +".....@%%%%%%@.....", +"....+%%%%%%%%+....", +"....#%%%%%%%%#....", +"....$%%%%%%%%$....", +"....$%%%%%%%%$....", +"....#%%%%%%%%#....", +"....+%%%%%%%%+....", +".....@%%%%%%@.....", +"......+#$$#+......", +"..................", +"..................", +"..................", +".................."}; \ No newline at end of file diff --git a/src/core/graphics.h b/src/core/graphics.h index 3c2cb1d..7db77c9 100644 --- a/src/core/graphics.h +++ b/src/core/graphics.h @@ -86,6 +86,7 @@ extern const char* soloOn_xpm[]; extern const char* armOff_xpm[]; extern const char* armOn_xpm[]; +extern const char* armDisabled_xpm[]; extern const char* readActionOn_xpm[]; extern const char* readActionOff_xpm[]; diff --git a/src/core/kernelAudio.cpp b/src/core/kernelAudio.cpp index 0068c0c..e0dc4e8 100644 --- a/src/core/kernelAudio.cpp +++ b/src/core/kernelAudio.cpp @@ -138,7 +138,7 @@ int openDevice() return 0; } - u::log::print("[KA] Opening devices %d (out), %d (in), f=%d...\n", + u::log::print("[KA] Opening device out=%d, in=%d, samplerate=%d\n", conf::conf.soundDeviceOut, conf::conf.soundDeviceIn, conf::conf.samplerate); numDevs = rtSystem->getDeviceCount(); @@ -161,12 +161,14 @@ int openDevice() outParams.nChannels = G_MAX_IO_CHANS; outParams.firstChannel = conf::conf.channelsOut * G_MAX_IO_CHANS; // chan 0=0, 1=2, 2=4, ... - /* inDevice can be disabled. */ + /* Input device can be disabled. Unlike the output, here we are using all + channels and let the user choose which one to record from in the configuration + panel. */ if (conf::conf.soundDeviceIn != -1) { inParams.deviceId = conf::conf.soundDeviceIn; - inParams.nChannels = G_MAX_IO_CHANS; - inParams.firstChannel = conf::conf.channelsIn * G_MAX_IO_CHANS; // chan 0=0, 1=2, 2=4, ... + inParams.nChannels = conf::conf.channelsInCount; + inParams.firstChannel = conf::conf.channelsInStart; inputEnabled = true; } else @@ -182,20 +184,20 @@ int openDevice() if (api == G_SYS_API_JACK) { conf::conf.samplerate = getFreq(conf::conf.soundDeviceOut, 0); - u::log::print("[KA] JACK in use, freq = %d\n", conf::conf.samplerate); + u::log::print("[KA] JACK in use, samplerate=%d\n", conf::conf.samplerate); } #endif try { rtSystem->openStream( - &outParams, // output params + &outParams, // output params conf::conf.soundDeviceIn != -1 ? &inParams : nullptr, // input params if inDevice is selected - RTAUDIO_FLOAT32, // audio format + RTAUDIO_FLOAT32, // audio format conf::conf.samplerate, // sample rate - &realBufsize, // buffer size in byte - &mixer::masterPlay, // audio callback - nullptr, // user data (unused) + &realBufsize, // buffer size in byte + &mixer::masterPlay, // audio callback + nullptr, // user data (unused) &options); model::onSwap(model::kernel, [](model::Kernel& k) { diff --git a/src/core/midiDispatcher.cpp b/src/core/midiDispatcher.cpp index 269cb7f..ed37976 100644 --- a/src/core/midiDispatcher.cpp +++ b/src/core/midiDispatcher.cpp @@ -282,11 +282,11 @@ void learnMaster_(MidiEvent e, int param, std::function doneCb) case G_MIDI_IN_START_STOP: m.startStop = raw; break; case G_MIDI_IN_ACTION_REC: m.actionRec = raw; break; case G_MIDI_IN_INPUT_REC: m.inputRec = raw; break; - case G_MIDI_IN_METRONOME: m.volumeIn = raw; break; - case G_MIDI_IN_VOLUME_IN: m.volumeOut = raw; break; - case G_MIDI_IN_VOLUME_OUT: m.beatDouble = raw; break; - case G_MIDI_IN_BEAT_DOUBLE: m.beatHalf = raw; break; - case G_MIDI_IN_BEAT_HALF: m.metronome = raw; break; + case G_MIDI_IN_METRONOME: m.metronome = raw; break; + case G_MIDI_IN_VOLUME_IN: m.volumeIn = raw; break; + case G_MIDI_IN_VOLUME_OUT: m.volumeOut = raw; break; + case G_MIDI_IN_BEAT_DOUBLE: m.beatDouble = raw; break; + case G_MIDI_IN_BEAT_HALF: m.beatHalf = raw; break; } }); diff --git a/src/core/mixer.cpp b/src/core/mixer.cpp index a556b7a..a7cef0f 100644 --- a/src/core/mixer.cpp +++ b/src/core/mixer.cpp @@ -58,7 +58,7 @@ Working buffer for audio recording. */ AudioBuffer recBuffer_; /* inBuffer_ -Working buffer for input channel. */ +Working buffer for input channel. Used for the in->out bridge. */ AudioBuffer inBuffer_; @@ -119,7 +119,8 @@ void lineInRec_(const AudioBuffer& inBuf) /* -------------------------------------------------------------------------- */ /* processLineIn -Computes line in peaks, plus handles the internal working buffer for input. */ +Computes line in peaks and prepares the internal working buffer for input +recording. */ void processLineIn_(const AudioBuffer& inBuf) { @@ -129,12 +130,15 @@ void processLineIn_(const AudioBuffer& inBuf) peakIn.store(inBuf.getPeak()); if (signalCb_ != nullptr && u::math::linearToDB(peakIn) > conf::conf.recTriggerLevel) { +G_DEBUG("Signal > threshold!"); signalCb_(); signalCb_ = nullptr; } /* Prepare the working buffer for input stream, which will be processed - later on by the Master Input Channel with plug-ins. */ + later on by the Master Input Channel with plug-ins. */ + + assert(inBuf.countChannels() <= inBuffer_.countChannels()); model::MixerLock lock(model::mixer); inBuffer_.copyData(inBuf, mh::getInVol()); @@ -342,9 +346,9 @@ int masterPlay(void* outBuf, void* inBuf, unsigned bufferSize, #endif AudioBuffer out, in; - out.setData((float*) outBuf, bufferSize, G_MAX_IO_CHANS); + out.setData(static_cast(outBuf), bufferSize, G_MAX_IO_CHANS); if (kernelAudio::isInputEnabled()) - in.setData((float*) inBuf, bufferSize, G_MAX_IO_CHANS); + in.setData(static_cast(inBuf), bufferSize, conf::conf.channelsInCount); /* Reset peak computation. */ diff --git a/src/core/mixer.h b/src/core/mixer.h index ea830e4..46024c1 100644 --- a/src/core/mixer.h +++ b/src/core/mixer.h @@ -64,6 +64,7 @@ enum class EventType MIDI, ACTION, CHANNEL_TOGGLE_READ_ACTIONS, + CHANNEL_KILL_READ_ACTIONS, CHANNEL_TOGGLE_ARM, CHANNEL_MUTE, CHANNEL_SOLO, diff --git a/src/core/mixerHandler.cpp b/src/core/mixerHandler.cpp index 6d864d7..305527e 100644 --- a/src/core/mixerHandler.cpp +++ b/src/core/mixerHandler.cpp @@ -66,7 +66,7 @@ namespace std::unique_ptr createChannel_(ChannelType type, ID columnId, ID channelId=0) { std::unique_ptr ch = channelManager::create(type, - kernelAudio::getRealBufSize(), conf::conf.inputMonitorDefaultOn, columnId); + kernelAudio::getRealBufSize(), columnId, conf::conf); if (type == ChannelType::MASTER) { assert(channelId != 0); @@ -82,17 +82,8 @@ std::unique_ptr createChannel_(ChannelType type, ID columnId, ID channe waveManager::Result createWave_(const std::string& fname) { - waveManager::Result res = waveManager::createFromFile(fname); - if (res.status != G_RES_OK) - return res; - if (res.wave->getRate() != conf::conf.samplerate) { - u::log::print("[mh::createWave_] input rate (%d) != system rate (%d), conversion needed\n", - res.wave->getRate(), conf::conf.samplerate); - res.status = waveManager::resample(*res.wave.get(), conf::conf.rsmpQuality, conf::conf.samplerate); - if (res.status != G_RES_OK) - return res; - } - return res; + return waveManager::createFromFile(fname, /*ID=*/0, conf::conf.samplerate, + conf::conf.rsmpQuality); } @@ -133,10 +124,13 @@ std::vector getChannelsWithWave_() std::vector getRecordableChannels_() { - return getChannelsIf_([] (const Channel* c) - { - return c->canInputRec(); - }); + return getChannelsIf_([] (const Channel* c) { return c->canInputRec() && !c->hasWave(); }); +} + + +std::vector getOverdubbableChannels_() +{ + return getChannelsIf_([] (const Channel* c) { return c->canInputRec() && c->hasWave(); }); } @@ -155,6 +149,76 @@ void pushWave_(Channel& ch, std::unique_ptr&& w) model::WavesLock l(model::waves); ch.samplePlayer->loadWave(model::waves.back()); } + + +/* -------------------------------------------------------------------------- */ + + +void setupChannelPostRecording_(Channel& c) +{ + /* Start sample channels in loop mode right away. */ + if (c.samplePlayer->state->isAnyLoopMode()) + c.samplePlayer->kickIn(clock::getCurrentFrame()); + /* Disable 'arm' button if overdub protection is on. */ + if (c.audioReceiver->state->overdubProtection.load() == true) + c.state->armed.store(false); +} + + +/* -------------------------------------------------------------------------- */ + + +/* recordChannel_ +Records the current Mixer audio input data into an empty channel. */ + +void recordChannel_(ID channelId) +{ + /* Create a new Wave with audio coming from Mixer's virtual input. */ + + std::string filename = "TAKE-" + std::to_string(patch::patch.lastTakeId++) + ".wav"; + + std::unique_ptr wave = waveManager::createEmpty(clock::getFramesInLoop(), + G_MAX_IO_CHANS, conf::conf.samplerate, filename); + + wave->copyData(mixer::getRecBuffer()); + + /* Update Channel with the new Wave. The function pushWave_ will take + care of pushing it into the Wave stack first. */ + + model::onSwap(model::channels, channelId, [&](Channel& c) + { + pushWave_(c, std::move(wave)); + setupChannelPostRecording_(c); + }); +} + + +/* -------------------------------------------------------------------------- */ + + +/* overdubChannel_ +Records the current Mixer audio input data into a channel with an existing +Wave, overdub mode. */ + +void overdubChannel_(ID channelId) +{ + ID waveId; + model::onGet(model::channels, channelId, [&](Channel& c) + { + waveId = c.samplePlayer->getWaveId(); + }); + + model::onGet(m::model::waves, waveId, [&](Wave& w) + { + w.addData(mixer::getRecBuffer()); + w.setLogical(true); + }); + + model::onGet(model::channels, channelId, [&](Channel& c) + { + setupChannelPostRecording_(c); + }); +} }; // {anonymous} @@ -415,28 +479,10 @@ has to be overwritten somehow). */ void finalizeInputRec() { - for (ID id : getRecordableChannels_()) { - - /* Create a new Wave with audio coming from Mixer's virtual input. */ - - std::string filename = "TAKE-" + std::to_string(patch::patch.lastTakeId++) + ".wav"; - - std::unique_ptr wave = waveManager::createEmpty(clock::getFramesInLoop(), - G_MAX_IO_CHANS, conf::conf.samplerate, filename); - - wave->copyData(mixer::getRecBuffer()); - - /* Update Channel with the new Wave. The function pushWave_ will take - care of pushing it into the stack first. Also start all channels in - LOOP mode. */ - - model::onSwap(model::channels, id, [&](Channel& c) - { - pushWave_(c, std::move(wave)); - if (c.samplePlayer->state->isAnyLoopMode()) - c.samplePlayer->kickIn(clock::getCurrentFrame()); - }); - } + for (ID id : getRecordableChannels_()) + recordChannel_(id); + for (ID id : getOverdubbableChannels_()) + overdubChannel_(id); mixer::clearRecBuffer(); } @@ -445,12 +491,18 @@ void finalizeInputRec() /* -------------------------------------------------------------------------- */ -bool hasRecordableSampleChannels() +bool hasInputRecordableChannels() { return anyChannel_([](const Channel* ch) { return ch->canInputRec(); }); } +bool hasActionRecordableChannels() +{ + return anyChannel_([](const Channel* ch) { return ch->canActionRec(); }); +} + + bool hasLogicalSamples() { return anyChannel_([](const Channel* ch) diff --git a/src/core/mixerHandler.h b/src/core/mixerHandler.h index 6598772..4e1cf05 100644 --- a/src/core/mixerHandler.h +++ b/src/core/mixerHandler.h @@ -111,11 +111,11 @@ True if 1 or more samples was edited via gEditor */ bool hasEditedSamples(); -/* hasRecordableSampleChannels -Tells whether Mixer has one or more recordable Sample Channels, that is: -a) armed; b) empty (no Wave). */ +/* has(Input|Action)RecordableChannels +Tells whether Mixer has one or more input or action recordable channels. */ -bool hasRecordableSampleChannels(); +bool hasInputRecordableChannels(); +bool hasActionRecordableChannels(); /* hasActions True if at least one Channel has actions recorded in it. */ diff --git a/src/core/model/storage.cpp b/src/core/model/storage.cpp index 3fc6d39..9042be1 100644 --- a/src/core/model/storage.cpp +++ b/src/core/model/storage.cpp @@ -42,16 +42,6 @@ namespace giada { namespace m { namespace model { -namespace -{ -} // {anonymous} - - -/* -------------------------------------------------------------------------- */ -/* -------------------------------------------------------------------------- */ -/* -------------------------------------------------------------------------- */ - - void store(patch::Patch& patch) { #ifdef WITH_VST @@ -66,7 +56,8 @@ void store(patch::Patch& patch) patch.beats = clock.get()->beats; patch.bpm = clock.get()->bpm; patch.quantize = clock.get()->quantize; - patch.metronome = sequencer::isMetronomeOn(); // TODO - not here + patch.metronome = sequencer::isMetronomeOn(); + patch.samplerate = conf::conf.samplerate; #ifdef WITH_VST for (const Plugin* p : plugins) @@ -129,7 +120,8 @@ void load(const patch::Patch& patch) #endif for (const patch::Wave& pwave : patch.waves) { - std::unique_ptr w = waveManager::deserializeWave(pwave); + std::unique_ptr w = waveManager::deserializeWave(pwave, conf::conf.samplerate, + conf::conf.rsmpQuality); if (w != nullptr) waves.push(std::move(w)); } @@ -142,12 +134,14 @@ void load(const patch::Patch& patch) ChannelsLock cl(channels); WavesLock wl(waves); + + float samplerateRatio = conf::conf.samplerate / static_cast(patch::patch.samplerate); for (Channel* c : channels) { if (!c->samplePlayer) continue; if (exists(waves, c->samplePlayer->getWaveId())) - c->samplePlayer->setWave(get(waves, c->samplePlayer->getWaveId())); + c->samplePlayer->setWave(get(waves, c->samplePlayer->getWaveId()), samplerateRatio); else c->samplePlayer->setInvalidWave(); } diff --git a/src/core/patch.cpp b/src/core/patch.cpp index 84ad90e..cedbffb 100644 --- a/src/core/patch.cpp +++ b/src/core/patch.cpp @@ -186,6 +186,7 @@ void readChannels_(const nl::json& j) c.readActions = jchannel.value(PATCH_KEY_CHANNEL_READ_ACTIONS, false); c.pitch = jchannel.value(PATCH_KEY_CHANNEL_PITCH, G_DEFAULT_PITCH); c.inputMonitor = jchannel.value(PATCH_KEY_CHANNEL_INPUT_MONITOR, false); + c.overdubProtection = jchannel.value(PATCH_KEY_CHANNEL_OVERDUB_PROTECTION, false); c.midiInVeloAsVol = jchannel.value(PATCH_KEY_CHANNEL_MIDI_IN_VELO_AS_VOL, 0); c.midiInReadActions = jchannel.value(PATCH_KEY_CHANNEL_MIDI_IN_READ_ACTIONS, 0); c.midiInPitch = jchannel.value(PATCH_KEY_CHANNEL_MIDI_IN_PITCH, 0); @@ -351,6 +352,7 @@ void writeChannels_(nl::json& j) jchannel[PATCH_KEY_CHANNEL_READ_ACTIONS] = c.readActions; jchannel[PATCH_KEY_CHANNEL_PITCH] = c.pitch; jchannel[PATCH_KEY_CHANNEL_INPUT_MONITOR] = c.inputMonitor; + jchannel[PATCH_KEY_CHANNEL_OVERDUB_PROTECTION] = c.overdubProtection; jchannel[PATCH_KEY_CHANNEL_MIDI_IN_VELO_AS_VOL] = c.midiInVeloAsVol; jchannel[PATCH_KEY_CHANNEL_MIDI_IN_READ_ACTIONS] = c.midiInReadActions; jchannel[PATCH_KEY_CHANNEL_MIDI_IN_PITCH] = c.midiInPitch; @@ -373,19 +375,23 @@ void writeChannels_(nl::json& j) void modernize_() { - /* 0.16.3 - - Make sure that ChannelType is correct: ID 1, 2 are MASTER channels, - ID 3 is PREVIEW channel; - - set panning to default (0.5) and waveId to 0 for non-Sample Channels. */ - for (Channel& c : patch.channels) { - + /* 0.16.3 + Make sure that ChannelType is correct: ID 1, 2 are MASTER channels, ID 3 + is PREVIEW channel. */ if (c.id == mixer::MASTER_OUT_CHANNEL_ID || c.id == mixer::MASTER_IN_CHANNEL_ID) c.type = ChannelType::MASTER; else if (c.id == mixer::PREVIEW_CHANNEL_ID) c.type = ChannelType::PREVIEW; + /* 0.16.4 + Make sure internal channels are never armed. */ + if (c.type == ChannelType::PREVIEW || c.type == ChannelType::MASTER) + c.armed = false; + + /* 0.16.3 + Set panning to default (0.5) and waveId to 0 for non-Sample Channels. */ if (c.type != ChannelType::SAMPLE) { c.pan = G_DEFAULT_PAN; c.waveId = 0; diff --git a/src/core/patch.h b/src/core/patch.h index 7e0fa13..96daeaf 100644 --- a/src/core/patch.h +++ b/src/core/patch.h @@ -94,6 +94,7 @@ struct Channel bool readActions; float pitch = G_DEFAULT_PITCH; bool inputMonitor; + bool overdubProtection; bool midiInVeloAsVol; uint32_t midiInReadActions; uint32_t midiInPitch; diff --git a/src/core/recManager.cpp b/src/core/recManager.cpp index a66f7cf..f03c50f 100644 --- a/src/core/recManager.cpp +++ b/src/core/recManager.cpp @@ -73,6 +73,7 @@ bool startActionRec_() return false; clock::setStatus(ClockStatus::RUNNING); sequencer::start(); + m::conf::conf.recTriggerMode = RecTriggerMode::NORMAL; return true; } @@ -82,10 +83,11 @@ bool startActionRec_() bool startInputRec_() { - if (!kernelAudio::isReady() || !mh::hasRecordableSampleChannels()) + if (!kernelAudio::isReady() || !mh::hasInputRecordableChannels()) return false; mixer::startInputRec(); sequencer::start(); + m::conf::conf.recTriggerMode = RecTriggerMode::NORMAL; return true; } } // {anonymous} @@ -156,12 +158,14 @@ void stopActionRec() /* Enable reading actions for Channels that have just been filled with actions. Start reading right away, without checking whether - conf::treatRecsAsLoops is enabled or not. */ + conf::treatRecsAsLoops is enabled or not. Same thing for MIDI channels. */ for (ID id : channels) { model::onGet(model::channels, id, [](Channel& c) { c.state->readActions.store(true); + if (c.getType() == ChannelType::MIDI) + c.state->playStatus.store(ChannelStatus::PLAY); }); } } @@ -182,13 +186,15 @@ void toggleActionRec(RecTriggerMode m) bool startInputRec(RecTriggerMode mode) { if (mode == RecTriggerMode::NORMAL) { +G_DEBUG("Start input rec, NORMAL mode"); if (!startInputRec_()) return false; setRecordingInput_(true); return true; } - else { // RecTriggerMode::SIGNAL - if (!mh::hasRecordableSampleChannels()) + else { +G_DEBUG("Start input rec, SIGNAL mode"); + if (!mh::hasInputRecordableChannels()) return false; clock::setStatus(ClockStatus::WAITING); clock::rewind(); diff --git a/src/core/wave.cpp b/src/core/wave.cpp index 95859f7..826d350 100644 --- a/src/core/wave.cpp +++ b/src/core/wave.cpp @@ -155,16 +155,14 @@ void Wave::setPath(const std::string& p, int id) /* -------------------------------------------------------------------------- */ -void Wave::copyData(const float* data, int frames, int offset) +void Wave::copyData(const float* data, int frames, int channels, int offset) { - buffer.copyData(data, frames, offset); + buffer.copyData(data, frames, channels, offset); } -void Wave::copyData(const AudioBuffer& b) -{ - buffer.copyData(b); -} +void Wave::copyData(const AudioBuffer& b) { buffer.copyData(b); } +void Wave::addData(const AudioBuffer& b) { buffer.addData(b); } /* -------------------------------------------------------------------------- */ @@ -174,5 +172,4 @@ void Wave::moveData(AudioBuffer& b) { buffer.moveData(b); } - }}; // giada::m:: diff --git a/src/core/wave.h b/src/core/wave.h index 13c080d..80e7e55 100644 --- a/src/core/wave.h +++ b/src/core/wave.h @@ -79,12 +79,16 @@ public: /* copyData Copies 'frames' frames from the new 'data' into m_data, starting from frame - 'offset'. It takes for granted that the new data contains the same number of - channels than m_channels. */ + 'offset'. */ - void copyData(const float* data, int frames, int offset=0); + void copyData(const float* data, int frames, int channels, int offset=0); void copyData(const AudioBuffer& b); + /* addData + Merges audio data from buffer 'b' onto this one. */ + + void addData(const AudioBuffer& b); + void alloc(int size, int channels, int rate, int bits, const std::string& path); ID id; diff --git a/src/core/waveManager.cpp b/src/core/waveManager.cpp index f820c93..d07789f 100644 --- a/src/core/waveManager.cpp +++ b/src/core/waveManager.cpp @@ -85,7 +85,7 @@ void init() /* -------------------------------------------------------------------------- */ -Result createFromFile(const std::string& path, ID id) +Result createFromFile(const std::string& path, ID id, int samplerate, int quality) { if (path == "" || u::fs::isDir(path)) { u::log::print("[waveManager::create] malformed path (was '%s')\n", path.c_str()); @@ -120,6 +120,13 @@ Result createFromFile(const std::string& path, ID id) if (header.channels == 1 && !wfx::monoToStereo(*wave)) return { G_RES_ERR_PROCESSING }; + + if (wave->getRate() != samplerate) { + u::log::print("[waveManager::create] input rate (%d) != required rate (%d), conversion needed\n", + wave->getRate(), samplerate); + if (resample(*wave.get(), quality, samplerate) != G_RES_OK) + return { G_RES_ERR_PROCESSING }; + } u::log::print("[waveManager::create] new Wave created, %d frames\n", wave->getSize()); @@ -153,7 +160,7 @@ std::unique_ptr createFromWave(const Wave& src, int a, int b) std::unique_ptr wave = std::make_unique(waveId_.get()); wave->alloc(frames, channels, src.getRate(), src.getBits(), src.getPath()); - wave->copyData(src.getFrame(a), frames); + wave->copyData(src.getFrame(a), frames, channels); wave->setLogical(true); u::log::print("[waveManager::createFromWave] new Wave created, %d frames\n", frames); @@ -165,9 +172,9 @@ std::unique_ptr createFromWave(const Wave& src, int a, int b) /* -------------------------------------------------------------------------- */ -std::unique_ptr deserializeWave(const patch::Wave& w) +std::unique_ptr deserializeWave(const patch::Wave& w, int samplerate, int quality) { - return createFromFile(w.path, w.id).wave; + return createFromFile(w.path, w.id, samplerate, quality).wave; } diff --git a/src/core/waveManager.h b/src/core/waveManager.h index f8e7a6a..20b9c8c 100644 --- a/src/core/waveManager.h +++ b/src/core/waveManager.h @@ -55,10 +55,11 @@ Initializes internal data. */ void init(); /* create -Creates a new Wave object with data read from file 'path'. Takes an optional -'id' parameter for patch persistence. */ +Creates a new Wave object with data read from file 'path'. Pass id = 0 to +auto-generate it. The function converts the Wave sample rate if it doesn't match +the desired one as specified in 'samplerate'. */ -Result createFromFile(const std::string& path, ID id=0); +Result createFromFile(const std::string& path, ID id, int samplerate, int quality); /* createEmpty Creates a new silent Wave object. */ @@ -74,9 +75,13 @@ std::unique_ptr createFromWave(const Wave& src, int a, int b); /* (de)serializeWave Creates a new Wave given the patch raw data and vice versa. */ -std::unique_ptr deserializeWave(const patch::Wave& w); +std::unique_ptr deserializeWave(const patch::Wave& w, int samplerate, int quality); const patch::Wave serializeWave(const Wave& w); +/* resample +Change sample rate of 'w' to the desider value. The 'quality' parameter sets the +algorithm to use for the conversion. */ + int resample(Wave& w, int quality, int samplerate); /* save diff --git a/src/glue/channel.cpp b/src/glue/channel.cpp index c3ecb15..860c261 100644 --- a/src/glue/channel.cpp +++ b/src/glue/channel.cpp @@ -103,10 +103,11 @@ SampleData::SampleData(const m::SamplePlayer& s, const m::AudioReceiver& a) } -Frame SampleData::a_getTracker() const { return a_get(m_samplePlayer->state->tracker); } -Frame SampleData::a_getBegin() const { return a_get(m_samplePlayer->state->begin); } -Frame SampleData::a_getEnd() const { return a_get(m_samplePlayer->state->end); } -bool SampleData::a_getInputMonitor() const { return a_get(m_audioReceiver->state->inputMonitor); } +Frame SampleData::a_getTracker() const { return a_get(m_samplePlayer->state->tracker); } +Frame SampleData::a_getBegin() const { return a_get(m_samplePlayer->state->begin); } +Frame SampleData::a_getEnd() const { return a_get(m_samplePlayer->state->end); } +bool SampleData::a_getInputMonitor() const { return a_get(m_audioReceiver->state->inputMonitor); } +bool SampleData::a_getOverdubProtection() const { return a_get(m_audioReceiver->state->overdubProtection); } /* -------------------------------------------------------------------------- */ @@ -279,6 +280,20 @@ void setInputMonitor(ID channelId, bool value) /* -------------------------------------------------------------------------- */ +void setOverdubProtection(ID channelId, bool value) +{ + m::model::onGet(m::model::channels, channelId, [&](m::Channel& c) + { + c.audioReceiver->state->overdubProtection.store(value); + if (value == true && c.state->armed.load() == true) + c.state->armed.store(false); + }); +} + + +/* -------------------------------------------------------------------------- */ + + void cloneChannel(ID channelId) { m::mh::cloneChannel(channelId); diff --git a/src/glue/channel.h b/src/glue/channel.h index 2018778..007e0dd 100644 --- a/src/glue/channel.h +++ b/src/glue/channel.h @@ -55,6 +55,7 @@ struct SampleData Frame a_getBegin() const; Frame a_getEnd() const; bool a_getInputMonitor() const; + bool a_getOverdubProtection() const; ID waveId; SamplePlayerMode mode; @@ -174,6 +175,7 @@ void cloneChannel(ID channelId); Sets several channel properties. */ void setInputMonitor(ID channelId, bool value); +void setOverdubProtection(ID channelId, bool value); void setName(ID channelId, const std::string& name); void setHeight(ID channelId, Pixel p); diff --git a/src/glue/events.cpp b/src/glue/events.cpp index 504088c..cbf4508 100644 --- a/src/glue/events.cpp +++ b/src/glue/events.cpp @@ -181,6 +181,12 @@ void toggleReadActionsChannel(ID channelId, Thread t) } +void killReadActionsChannel(ID channelId, Thread t) +{ + pushEvent_({ m::mixer::EventType::CHANNEL_KILL_READ_ACTIONS, 0, {0, channelId} }, t); +} + + /* -------------------------------------------------------------------------- */ @@ -247,6 +253,7 @@ void divideBeats() void startSequencer(Thread t) { pushEvent_({ m::mixer::EventType::SEQUENCER_START, 0 }, t); + m::conf::conf.recTriggerMode = RecTriggerMode::NORMAL; } @@ -279,8 +286,7 @@ void toggleActionRecording() void toggleInputRecording() { - if (!m::recManager::toggleInputRec(m::conf::conf.recTriggerMode)) - v::gdAlert("No channels armed/available for audio recording."); + m::recManager::toggleInputRec(m::conf::conf.recTriggerMode); } diff --git a/src/glue/events.h b/src/glue/events.h index 0949f90..7c8ab7b 100644 --- a/src/glue/events.h +++ b/src/glue/events.h @@ -53,11 +53,12 @@ void releaseChannel (ID channelId, Thread t); void killChannel (ID channelId, Thread t); void setChannelVolume (ID channelId, float v, Thread t); void setChannelPitch (ID channelId, float v, Thread t); -void sendChannelPan (ID channelId, float v); +void sendChannelPan (ID channelId, float v); // FIXME typo: should be setChannelPan void toggleMuteChannel (ID channelId, Thread t); void toggleSoloChannel (ID channelId, Thread t); void toggleArmChannel (ID channelId, Thread t); void toggleReadActionsChannel(ID channelId, Thread t); +void killReadActionsChannel (ID channelId, Thread t); void sendMidiToChannel (ID channelId, m::MidiEvent e, Thread t); /* Main* diff --git a/src/glue/main.cpp b/src/glue/main.cpp index 0e084b8..7867775 100644 --- a/src/glue/main.cpp +++ b/src/glue/main.cpp @@ -163,7 +163,7 @@ IO getIO() namespace mm = m::model; mm::ChannelsLock cl(mm::channels); - mm::MixerLock ml(mm::mixer); + mm::MixerLock ml(mm::mixer); return IO(mm::get(mm::channels, m::mixer::MASTER_OUT_CHANNEL_ID), mm::get(mm::channels, m::mixer::MASTER_IN_CHANNEL_ID), @@ -281,6 +281,20 @@ void setInToOut(bool v) /* -------------------------------------------------------------------------- */ +void toggleRecOnSignal() +{ + /* Can't set RecTriggerMode::SIGNAL while sequencer is running, in order + to prevent mistakes while live recording. */ + + if (m::conf::conf.recTriggerMode == RecTriggerMode::NORMAL && m::clock::isRunning()) + return; + m::conf::conf.recTriggerMode = m::conf::conf.recTriggerMode == RecTriggerMode::NORMAL ? RecTriggerMode::SIGNAL : RecTriggerMode::NORMAL; +} + + +/* -------------------------------------------------------------------------- */ + + void closeProject(bool createColumns) { if (!v::gdConfirmWin("Warning", "Close project: are you sure?")) diff --git a/src/glue/main.h b/src/glue/main.h index c0e8490..beb21e7 100644 --- a/src/glue/main.h +++ b/src/glue/main.h @@ -29,6 +29,9 @@ #define G_MAIN_H +#include "core/types.h" + + namespace giada { namespace m { @@ -97,6 +100,8 @@ Enables the "hear what you playing" feature. */ void setInToOut(bool v); +void toggleRecOnSignal(); + /* closeProject Resets Giada to init state. If resetGui also refresh all widgets. If createColumns also build initial empty columns. */ diff --git a/src/gui/dialogs/actionEditor/baseActionEditor.h b/src/gui/dialogs/actionEditor/baseActionEditor.h index f40e4b1..48cbc50 100644 --- a/src/gui/dialogs/actionEditor/baseActionEditor.h +++ b/src/gui/dialogs/actionEditor/baseActionEditor.h @@ -34,7 +34,6 @@ #include "gui/dialogs/window.h" -class geChoice; class geButton; @@ -46,6 +45,7 @@ struct Action; } namespace v { +class geChoice; class geGridTool; class geScrollPack; class gdBaseActionEditor : public gdWindow diff --git a/src/gui/dialogs/config.h b/src/gui/dialogs/config.h index a4a94f9..b152d9b 100644 --- a/src/gui/dialogs/config.h +++ b/src/gui/dialogs/config.h @@ -40,7 +40,6 @@ class geTabMisc; class geTabPlugins; #endif class geButton; -class geChoice; class geCheck; class geInput; class geRadio; @@ -50,6 +49,7 @@ class geBox; namespace giada { namespace v { +class geChoice; class gdConfig : public gdWindow { public: diff --git a/src/gui/dialogs/mainWindow.cpp b/src/gui/dialogs/mainWindow.cpp index 49db4ad..931d37c 100644 --- a/src/gui/dialogs/mainWindow.cpp +++ b/src/gui/dialogs/mainWindow.cpp @@ -65,7 +65,7 @@ gdMainWindow::gdMainWindow(int W, int H, const char* title, int argc, char** arg #if defined(WITH_VST) mainIO = new v::geMainIO(412, 8); #else - mainIO = new v::geMainIO(476, 8); + mainIO = new v::geMainIO(460, 8); #endif mainTransport = new v::geMainTransport(8, 39); mainTimer = new v::geMainTimer(598, 44); diff --git a/src/gui/dialogs/midiIO/midiInputBase.h b/src/gui/dialogs/midiIO/midiInputBase.h index 8836d32..74e32c2 100644 --- a/src/gui/dialogs/midiIO/midiInputBase.h +++ b/src/gui/dialogs/midiIO/midiInputBase.h @@ -35,12 +35,12 @@ class geButton; class geCheck; -class geChoice; namespace giada { namespace v { +class geChoice; class gdMidiInputBase : public gdWindow { public: diff --git a/src/gui/dialogs/midiIO/midiInputChannel.h b/src/gui/dialogs/midiIO/midiInputChannel.h index 8fff86c..fb1da23 100644 --- a/src/gui/dialogs/midiIO/midiInputChannel.h +++ b/src/gui/dialogs/midiIO/midiInputChannel.h @@ -37,12 +37,12 @@ class geCheck; -class geChoice; namespace giada { namespace v { +class geChoice; class geScrollPack; class geChannelLearnerPack : public geMidiLearnerPack { diff --git a/src/gui/dialogs/midiIO/midiInputMaster.h b/src/gui/dialogs/midiIO/midiInputMaster.h index b3655ec..254157a 100644 --- a/src/gui/dialogs/midiIO/midiInputMaster.h +++ b/src/gui/dialogs/midiIO/midiInputMaster.h @@ -35,12 +35,12 @@ class geCheck; -class geChoice; namespace giada { namespace v { +class geChoice; class geMasterLearnerPack : public geMidiLearnerPack { public: diff --git a/src/gui/dialogs/pluginChooser.h b/src/gui/dialogs/pluginChooser.h index cd5cb66..8509be7 100644 --- a/src/gui/dialogs/pluginChooser.h +++ b/src/gui/dialogs/pluginChooser.h @@ -36,13 +36,13 @@ #include "window.h" -class geChoice; class geButton; class geButton; namespace giada { namespace v { +class geChoice; class gePluginBrowser; class gdPluginChooser : public gdWindow diff --git a/src/gui/dialogs/sampleEditor.h b/src/gui/dialogs/sampleEditor.h index 12bc59a..5fbf8e0 100644 --- a/src/gui/dialogs/sampleEditor.h +++ b/src/gui/dialogs/sampleEditor.h @@ -35,7 +35,6 @@ class geButton; -class geChoice; class geCheck; class geBox; class geButton; @@ -49,6 +48,7 @@ class Wave; } namespace v { +class geChoice; class geVolumeTool; class geWaveTools; class geBoostTool; diff --git a/src/gui/dispatcher.cpp b/src/gui/dispatcher.cpp index 2a5e994..c84dd8f 100644 --- a/src/gui/dispatcher.cpp +++ b/src/gui/dispatcher.cpp @@ -112,7 +112,7 @@ void triggerSignalCb_() void dispatchKey(int event) { /* These events come from the keyboard, not from a direct interaction on the - UI with the mouse/touch. So the 'gui' parameter is set to false. */ + UI with the mouse/touch. */ if (event == FL_KEYDOWN) { if (Fl::event_key() == FL_BackSpace && !backspace_) { diff --git a/src/gui/elems/actionEditor/gridTool.h b/src/gui/elems/actionEditor/gridTool.h index d96c1af..2c6c405 100644 --- a/src/gui/elems/actionEditor/gridTool.h +++ b/src/gui/elems/actionEditor/gridTool.h @@ -33,13 +33,13 @@ #include "core/types.h" -class geChoice; class geCheck; namespace giada { namespace v { +class geChoice; class geGridTool : public Fl_Group { public: diff --git a/src/gui/elems/basics/baseButton.cpp b/src/gui/elems/basics/baseButton.cpp index 3eb53ba..45f6d5e 100644 --- a/src/gui/elems/basics/baseButton.cpp +++ b/src/gui/elems/basics/baseButton.cpp @@ -45,6 +45,7 @@ void geBaseButton::trimLabel() return; std::string out; + /* TODO --- use u::gui::truncate */ if (w() > 20) { out = initLabel; int len = initLabel.size(); diff --git a/src/gui/elems/basics/check.cpp b/src/gui/elems/basics/check.cpp index 9a47de9..5acd01b 100644 --- a/src/gui/elems/basics/check.cpp +++ b/src/gui/elems/basics/check.cpp @@ -25,6 +25,7 @@ * -------------------------------------------------------------------------- */ +#include #include #include "core/const.h" #include "check.h" @@ -41,19 +42,28 @@ geCheck::geCheck(int x, int y, int w, int h, const char* l) void geCheck::draw() { - int color = !active() ? FL_INACTIVE_COLOR : G_COLOR_GREY_4; - - if (value()) { - fl_rect(x(), y(), 12, h(), (Fl_Color) color); - fl_rectf(x(), y(), 12, h(), (Fl_Color) color); - } - else { - fl_rectf(x(), y(), 12, h(), FL_BACKGROUND_COLOR); - fl_rect(x(), y(), 12, h(), (Fl_Color) color); - } - - fl_rectf(x()+20, y(), w(), h(), FL_BACKGROUND_COLOR); // clearer + fl_rectf(x(), y(), w(), h(), FL_BACKGROUND_COLOR); // clearer + + const int boxColor = !active() ? FL_INACTIVE_COLOR : G_COLOR_GREY_4; + const int textColor = !active() ? FL_INACTIVE_COLOR : G_COLOR_LIGHT_2; + const int textAlign = hasMultilineText() ? FL_ALIGN_LEFT | FL_ALIGN_TOP : FL_ALIGN_LEFT | FL_ALIGN_CENTER; + + if (value()) + fl_rectf(x(), y(), 12, 20, (Fl_Color) boxColor); + else + fl_rect(x(), y(), 12, 20, (Fl_Color) boxColor); + fl_font(FL_HELVETICA, G_GUI_FONT_SIZE_BASE); - fl_color(!active() ? FL_INACTIVE_COLOR : G_COLOR_LIGHT_2); - fl_draw(label(), x()+20, y(), w(), h(), (Fl_Align) (FL_ALIGN_LEFT | FL_ALIGN_CENTER)); + fl_color(textColor); + fl_draw(label(), x()+20, y(), w(), h(), (Fl_Align) textAlign); } + + +/* -------------------------------------------------------------------------- */ + + +bool geCheck::hasMultilineText() const +{ + return label() == nullptr ? false : std::strchr(label(), '\n') != nullptr; +} + diff --git a/src/gui/elems/basics/check.h b/src/gui/elems/basics/check.h index 3f7d806..dbe5340 100644 --- a/src/gui/elems/basics/check.h +++ b/src/gui/elems/basics/check.h @@ -39,6 +39,10 @@ public: geCheck(int x, int y, int w, int h, const char *l=0); void draw() override; + +private: + + bool hasMultilineText() const; }; diff --git a/src/gui/elems/basics/choice.cpp b/src/gui/elems/basics/choice.cpp index 4c3a3f9..9745de0 100644 --- a/src/gui/elems/basics/choice.cpp +++ b/src/gui/elems/basics/choice.cpp @@ -27,19 +27,40 @@ #include #include -#include "../../../core/const.h" +#include "core/const.h" +#include "utils/gui.h" +#include "utils/vector.h" #include "choice.h" -geChoice::geChoice(int x, int y, int w, int h, const char *l, bool ang) - : Fl_Choice(x, y, w, h, l), angle(ang) +namespace giada { +namespace v { - labelsize(G_GUI_FONT_SIZE_BASE); - labelcolor(G_COLOR_LIGHT_2); - box(FL_BORDER_BOX); - textsize(G_GUI_FONT_SIZE_BASE); - textcolor(G_COLOR_LIGHT_2); - color(G_COLOR_GREY_2); +geChoice::geChoice(int x, int y, int w, int h, const char* l, bool ang) +: Fl_Choice(x, y, w, h, l) +, angle (ang) +{ + labelsize(G_GUI_FONT_SIZE_BASE); + labelcolor(G_COLOR_LIGHT_2); + box(FL_BORDER_BOX); + textsize(G_GUI_FONT_SIZE_BASE); + textcolor(G_COLOR_LIGHT_2); + color(G_COLOR_GREY_2); +} + + +/* -------------------------------------------------------------------------- */ + + +void geChoice::cb_onChange(Fl_Widget* w, void* p) { (static_cast(p))->cb_onChange(); } + + +/* -------------------------------------------------------------------------- */ + + +void geChoice::cb_onChange() +{ + if (onChange != nullptr) onChange(getSelectedId()); } @@ -48,38 +69,50 @@ geChoice::geChoice(int x, int y, int w, int h, const char *l, bool ang) void geChoice::draw() { - fl_rectf(x(), y(), w(), h(), G_COLOR_GREY_2); // bg - fl_rect(x(), y(), w(), h(), (Fl_Color) G_COLOR_GREY_4); // border - if (angle) - fl_polygon(x()+w()-8, y()+h()-1, x()+w()-1, y()+h()-8, x()+w()-1, y()+h()-1); - - /* pick up the text() from the selected item (value()) and print it in - * the box and avoid overflows */ - - fl_color(!active() ? G_COLOR_GREY_4 : G_COLOR_LIGHT_2); - if (value() != -1) { - if (fl_width(text(value())) < w()-8) { - fl_draw(text(value()), x(), y(), w(), h(), FL_ALIGN_CENTER); - } - else { - std::string tmp = text(value()); - int size = tmp.size(); - while (fl_width(tmp.c_str()) >= w()-16) { - tmp.resize(size); - size--; - } - tmp += "..."; - fl_draw(tmp.c_str(), x(), y(), w(), h(), FL_ALIGN_CENTER); - } - - } + fl_rectf(x(), y(), w(), h(), G_COLOR_GREY_2); // bg + fl_rect(x(), y(), w(), h(), (Fl_Color) G_COLOR_GREY_4); // border + if (angle) + fl_polygon(x()+w()-8, y()+h()-1, x()+w()-1, y()+h()-8, x()+w()-1, y()+h()-1); + + /* pick up the text() from the selected item (value()) and print it in + * the box and avoid overflows */ + + fl_color(!active() ? G_COLOR_GREY_4 : G_COLOR_LIGHT_2); + if (value() != -1) + fl_draw(u::gui::truncate(text(value()), w()-16).c_str(), x(), y(), w(), h(), FL_ALIGN_CENTER); } /* -------------------------------------------------------------------------- */ -void geChoice::showItem(const char *c) +ID geChoice::getSelectedId() const +{ + return value() == -1 ? 0 : ids.at(value()); +} + + +/* -------------------------------------------------------------------------- */ + + +void geChoice::addItem(const std::string& label, ID id) +{ + Fl_Choice::add(label.c_str(), 0, cb_onChange, static_cast(this)); + ids.push_back(id); +} + + +/* -------------------------------------------------------------------------- */ + + +void geChoice::showItem(const char* c) +{ + value(find_index(c)); +} + + +void geChoice::showItem(ID id) { - value(find_index(c)); + value(u::vector::indexOf(ids, id)); } +}} \ No newline at end of file diff --git a/src/gui/elems/basics/choice.h b/src/gui/elems/basics/choice.h index 8c008c6..98d99e2 100644 --- a/src/gui/elems/basics/choice.h +++ b/src/gui/elems/basics/choice.h @@ -29,21 +29,38 @@ #define GE_CHOICE_H +#include +#include #include +#include "core/types.h" +namespace giada { +namespace v +{ class geChoice : public Fl_Choice { public: - geChoice(int X,int Y,int W,int H,const char *L=0, bool angle=true); - void draw(); + geChoice(int x, int y, int w, int h, const char* l=0, bool angle=true); + void draw() override; + + ID getSelectedId() const; + + void addItem(const std::string& label, ID id); + void showItem(const char* c); + void showItem(ID id); - void showItem(const char *c); + std::function onChange = nullptr; + +private: + + static void cb_onChange(Fl_Widget* w, void* p); + void cb_onChange(); bool angle; - int id; + std::vector ids; }; - +}} #endif diff --git a/src/gui/elems/config/tabAudio.cpp b/src/gui/elems/config/tabAudio.cpp index 88216b0..1e6f4ee 100644 --- a/src/gui/elems/config/tabAudio.cpp +++ b/src/gui/elems/config/tabAudio.cpp @@ -138,7 +138,7 @@ geTabAudio::geTabAudio(int X, int Y, int W, int H) break; } -#elif defined (__APPLE__) +#elif defined(__APPLE__) if (m::kernelAudio::hasAPI(RtAudio::MACOSX_CORE)) soundsys->add("CoreAudio"); @@ -200,7 +200,7 @@ geTabAudio::geTabAudio(int X, int Y, int W, int H) buffersize->add("1024"); buffersize->add("2048"); buffersize->add("4096"); - buffersize->showItem(u::string::iToString(m::conf::conf.buffersize).c_str()); + buffersize->showItem(std::to_string(m::conf::conf.buffersize).c_str()); rsmpQuality->add("Sinc best quality (very slow)"); rsmpQuality->add("Sinc medium quality (slow)"); @@ -339,11 +339,18 @@ void geTabAudio::fetchInChans(int menuItem) channelsIn->value(0); return; } - for (unsigned i=0; iadd(tmp.c_str()); - } - channelsIn->value(m::conf::conf.channelsIn); + + /* Dirty trick for stereo inputs: indexes start from 1000. */ + + for (unsigned i = 0; i < chs; i++) + channelsIn->addItem(std::to_string(i + 1).c_str(), i + 1); + for (unsigned i = 0; i < chs; i += 2) + channelsIn->addItem((std::to_string(i + 1) + "-" + std::to_string(i + 2)).c_str(), i + 1001); + + if (m::conf::conf.channelsInCount == 1) + channelsIn->showItem(m::conf::conf.channelsInStart + 1); + else + channelsIn->showItem(m::conf::conf.channelsInStart + 1001); } @@ -496,7 +503,7 @@ void geTabAudio::save() else if (text == "WASAPI") m::conf::conf.soundSystem = G_SYS_API_WASAPI; -#elif defined (__APPLE__) +#elif defined(__APPLE__) else if (text == "CoreAudio") m::conf::conf.soundSystem = G_SYS_API_CORE; @@ -505,12 +512,13 @@ void geTabAudio::save() /* use the device name to search into the drop down menu's */ - m::conf::conf.soundDeviceOut = m::kernelAudio::getDeviceByName(sounddevOut->text(sounddevOut->value())); - m::conf::conf.soundDeviceIn = m::kernelAudio::getDeviceByName(sounddevIn->text(sounddevIn->value())); - m::conf::conf.channelsOut = channelsOut->value(); - m::conf::conf.channelsIn = channelsIn->value(); - m::conf::conf.limitOutput = limitOutput->value(); - m::conf::conf.rsmpQuality = rsmpQuality->value(); + m::conf::conf.soundDeviceOut = m::kernelAudio::getDeviceByName(sounddevOut->text(sounddevOut->value())); + m::conf::conf.soundDeviceIn = m::kernelAudio::getDeviceByName(sounddevIn->text(sounddevIn->value())); + m::conf::conf.channelsOut = channelsOut->value(); + m::conf::conf.channelsInCount = channelsIn->getSelectedId() < 1000 ? 1 : 2; + m::conf::conf.channelsInStart = channelsIn->getSelectedId() - (m::conf::conf.channelsInCount == 1 ? 1 : 1001); + m::conf::conf.limitOutput = limitOutput->value(); + m::conf::conf.rsmpQuality = rsmpQuality->value(); /* if sounddevOut is disabled (because of system change e.g. alsa -> * jack) its value is equal to -1. Change it! */ diff --git a/src/gui/elems/config/tabAudio.h b/src/gui/elems/config/tabAudio.h index c84b820..d0c28cf 100644 --- a/src/gui/elems/config/tabAudio.h +++ b/src/gui/elems/config/tabAudio.h @@ -32,7 +32,6 @@ #include -class geChoice; class geCheck; class geButton; class geInput; @@ -41,6 +40,7 @@ class geInput; namespace giada { namespace v { +class geChoice; class geTabAudio : public Fl_Group { public: diff --git a/src/gui/elems/config/tabBehaviors.cpp b/src/gui/elems/config/tabBehaviors.cpp index 904c78d..c14437c 100644 --- a/src/gui/elems/config/tabBehaviors.cpp +++ b/src/gui/elems/config/tabBehaviors.cpp @@ -29,7 +29,6 @@ #include "core/const.h" #include "core/conf.h" #include "gui/elems/basics/box.h" -#include "gui/elems/basics/radio.h" #include "gui/elems/basics/check.h" #include "tabBehaviors.h" @@ -37,49 +36,31 @@ namespace giada { namespace v { -geTabBehaviors::geTabBehaviors(int X, int Y, int W, int H) -: Fl_Group(X, Y, W, H, "Behaviors") +geTabBehaviors::geTabBehaviors(int X, int Y, int W, int H) +: Fl_Group (X, Y, W, H) +, m_container (X, Y + G_GUI_OUTER_MARGIN, Direction::VERTICAL, G_GUI_OUTER_MARGIN) +, m_chansStopOnSeqHalt (0, 0, 280, 30, "Dynamic channels stop immediately when the sequencer\nis halted") +, m_treatRecsAsLoops (0, 0, 280, 20, "Treat one shot channels with actions as loops") +, m_inputMonitorDefaultOn (0, 0, 280, 20, "New sample channels have input monitor on by default") +, m_overdubProtectionDefaultOn(0, 0, 280, 30, "New sample channels have overdub protection on\nby default") { - begin(); - - Fl_Group* radioGrp_2 = new Fl_Group(x(), y()+10, w(), 70); // radio group for the mutex - new geBox(x(), radioGrp_2->y(), 70, 25, "When the sequencer is halted:", FL_ALIGN_LEFT); - chansStopOnSeqHalt_1 = new geRadio(x()+25, radioGrp_2->y() + 25, 280, 20, "stop immediately all dynamic channels"); - chansStopOnSeqHalt_0 = new geRadio(x()+25, radioGrp_2->y() + 50, 280, 20, "play all dynamic channels until finished"); - radioGrp_2->end(); - - treatRecsAsLoops = new geCheck(x(), radioGrp_2->y()+radioGrp_2->h() + 15, 280, 20, "Treat one shot channels with actions as loops"); - inputMonitorDefaultOn = new geCheck(x(), treatRecsAsLoops->y()+treatRecsAsLoops->h() + 5, 280, 20, "New sample channels have input monitor on by default"); - end(); + label("Behaviors"); labelsize(G_GUI_FONT_SIZE_BASE); selection_color(G_COLOR_GREY_4); - m::conf::conf.chansStopOnSeqHalt == 1 ? chansStopOnSeqHalt_1->value(1) : chansStopOnSeqHalt_0->value(1); - treatRecsAsLoops->value(m::conf::conf.treatRecsAsLoops); - inputMonitorDefaultOn->value(m::conf::conf.inputMonitorDefaultOn); - - chansStopOnSeqHalt_1->callback(cb_radio_mutex, (void*)this); - chansStopOnSeqHalt_0->callback(cb_radio_mutex, (void*)this); -} - - -/* -------------------------------------------------------------------------- */ + m_container.add(&m_chansStopOnSeqHalt); + m_container.add(&m_treatRecsAsLoops); + m_container.add(&m_inputMonitorDefaultOn); + m_container.add(&m_overdubProtectionDefaultOn); + add(m_container); -void geTabBehaviors::cb_radio_mutex(Fl_Widget* w, void* p) -{ - static_cast(p)->cb_radio_mutex(w); -} - - -/* -------------------------------------------------------------------------- */ - - -void geTabBehaviors::cb_radio_mutex(Fl_Widget* w) -{ - static_cast(w)->type(FL_RADIO_BUTTON); + m_chansStopOnSeqHalt.value(m::conf::conf.chansStopOnSeqHalt); + m_treatRecsAsLoops.value(m::conf::conf.treatRecsAsLoops); + m_inputMonitorDefaultOn.value(m::conf::conf.inputMonitorDefaultOn); + m_overdubProtectionDefaultOn.value(m::conf::conf.overdubProtectionDefaultOn); } @@ -88,8 +69,9 @@ void geTabBehaviors::cb_radio_mutex(Fl_Widget* w) void geTabBehaviors::save() { - m::conf::conf.chansStopOnSeqHalt = chansStopOnSeqHalt_1->value() == 1 ? 1 : 0; - m::conf::conf.treatRecsAsLoops = treatRecsAsLoops->value() == 1 ? 1 : 0; - m::conf::conf.inputMonitorDefaultOn = inputMonitorDefaultOn->value() == 1 ? 1 : 0; + m::conf::conf.chansStopOnSeqHalt = m_chansStopOnSeqHalt.value(); + m::conf::conf.treatRecsAsLoops = m_treatRecsAsLoops.value(); + m::conf::conf.inputMonitorDefaultOn = m_inputMonitorDefaultOn.value(); + m::conf::conf.overdubProtectionDefaultOn = m_overdubProtectionDefaultOn.value(); } }} // giada::v:: \ No newline at end of file diff --git a/src/gui/elems/config/tabBehaviors.h b/src/gui/elems/config/tabBehaviors.h index 59fb16d..84f73cf 100644 --- a/src/gui/elems/config/tabBehaviors.h +++ b/src/gui/elems/config/tabBehaviors.h @@ -30,10 +30,8 @@ #include - - -class geRadio; -class geCheck; +#include "gui/elems/basics/pack.h" +#include "gui/elems/basics/check.h" namespace giada { @@ -47,16 +45,13 @@ public: void save(); - geRadio *chansStopOnSeqHalt_1; - geRadio *chansStopOnSeqHalt_0; - geCheck *treatRecsAsLoops; - geCheck *inputMonitorDefaultOn; - - private: - static void cb_radio_mutex(Fl_Widget* w, void* p); - void cb_radio_mutex(Fl_Widget* w); + gePack m_container; + geCheck m_chansStopOnSeqHalt; + geCheck m_treatRecsAsLoops; + geCheck m_inputMonitorDefaultOn; + geCheck m_overdubProtectionDefaultOn; }; }} // giada::v:: diff --git a/src/gui/elems/config/tabMidi.h b/src/gui/elems/config/tabMidi.h index 6b96757..1e99c5d 100644 --- a/src/gui/elems/config/tabMidi.h +++ b/src/gui/elems/config/tabMidi.h @@ -32,13 +32,13 @@ #include -class geChoice; class geCheck; namespace giada { namespace v { +class geChoice; class geTabMidi : public Fl_Group { public: diff --git a/src/gui/elems/config/tabMisc.h b/src/gui/elems/config/tabMisc.h index c733679..4c119ee 100644 --- a/src/gui/elems/config/tabMisc.h +++ b/src/gui/elems/config/tabMisc.h @@ -32,12 +32,10 @@ #include -class geChoice; - - namespace giada { namespace v { +class geChoice; class geTabMisc : public Fl_Group { public: diff --git a/src/gui/elems/config/tabPlugins.cpp b/src/gui/elems/config/tabPlugins.cpp index c49a1df..144aee1 100644 --- a/src/gui/elems/config/tabPlugins.cpp +++ b/src/gui/elems/config/tabPlugins.cpp @@ -42,7 +42,6 @@ #include "gui/dialogs/mainWindow.h" #include "gui/dialogs/browser/browserDir.h" #include "gui/elems/basics/box.h" -#include "gui/elems/basics/radio.h" #include "gui/elems/basics/check.h" #include "gui/elems/basics/input.h" #include "gui/elems/basics/button.h" diff --git a/src/gui/elems/mainWindow/keyboard/sampleChannel.cpp b/src/gui/elems/mainWindow/keyboard/sampleChannel.cpp index 1ed3cd6..eb312ba 100644 --- a/src/gui/elems/mainWindow/keyboard/sampleChannel.cpp +++ b/src/gui/elems/mainWindow/keyboard/sampleChannel.cpp @@ -76,6 +76,7 @@ namespace enum class Menu { INPUT_MONITOR = 0, + OVERDUB_PROTECTION, LOAD_SAMPLE, EXPORT_SAMPLE, SETUP_KEYBOARD_INPUT, @@ -108,6 +109,10 @@ void menuCallback(Fl_Widget* w, void* v) c::channel::setInputMonitor(data.id, !data.sample->a_getInputMonitor()); break; } + case Menu::OVERDUB_PROTECTION: { + c::channel::setOverdubProtection(data.id, !data.sample->a_getOverdubProtection()); + break; + } case Menu::LOAD_SAMPLE: { gdWindow* w = new gdBrowserLoad("Browse sample", m::conf::conf.samplePath.c_str(), c::storage::loadSample, data.id); @@ -197,7 +202,7 @@ geSampleChannel::geSampleChannel(int X, int Y, int W, int H, c::channel::Data d) #endif playButton = new geStatusButton (x(), y(), G_GUI_UNIT, G_GUI_UNIT, channelStop_xpm, channelPlay_xpm); - arm = new geButton (playButton->x() + playButton->w() + G_GUI_INNER_MARGIN, y(), G_GUI_UNIT, G_GUI_UNIT, "", armOff_xpm, armOn_xpm); + arm = new geButton (playButton->x() + playButton->w() + G_GUI_INNER_MARGIN, y(), G_GUI_UNIT, G_GUI_UNIT, "", armOff_xpm, armOn_xpm, armDisabled_xpm); status = new geChannelStatus (arm->x() + arm->w() + G_GUI_INNER_MARGIN, y(), G_GUI_UNIT, H, m_channel); mainButton = new geSampleChannelButton(status->x() + status->w() + G_GUI_INNER_MARGIN, y(), w() - delta, H, m_channel); readActions = new geStatusButton (mainButton->x() + mainButton->w() + G_GUI_INNER_MARGIN, y(), G_GUI_UNIT, G_GUI_UNIT, readActionOff_xpm, readActionOn_xpm, readActionDisabled_xpm); @@ -277,7 +282,9 @@ void geSampleChannel::cb_openMenu() Fl_Menu_Item rclick_menu[] = { {"Input monitor", 0, menuCallback, (void*) Menu::INPUT_MONITOR, - FL_MENU_TOGGLE | FL_MENU_DIVIDER | (m_channel.sample->a_getInputMonitor() ? FL_MENU_VALUE : 0)}, + FL_MENU_TOGGLE | (m_channel.sample->a_getInputMonitor() ? FL_MENU_VALUE : 0)}, + {"Overdub protection", 0, menuCallback, (void*) Menu::OVERDUB_PROTECTION, + FL_MENU_TOGGLE | FL_MENU_DIVIDER | (m_channel.sample->a_getOverdubProtection() ? 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}, @@ -331,7 +338,10 @@ void geSampleChannel::cb_openMenu() void geSampleChannel::cb_readActions() { - c::events::toggleReadActionsChannel(m_channel.id, Thread::MAIN); + if (Fl::event_shift()) + c::events::killReadActionsChannel(m_channel.id, Thread::MAIN); + else + c::events::toggleReadActionsChannel(m_channel.id, Thread::MAIN); } @@ -342,8 +352,14 @@ void geSampleChannel::refresh() { geChannel::refresh(); - if (m_channel.sample->waveId != 0) + if (m_channel.sample->waveId != 0) { status->redraw(); + if (m_channel.sample->a_getOverdubProtection()) + arm->deactivate(); + else + arm->activate(); + } + if (m_channel.hasActions) { readActions->activate(); readActions->setStatus(m_channel.a_getReadActions()); diff --git a/src/gui/elems/mainWindow/mainIO.cpp b/src/gui/elems/mainWindow/mainIO.cpp index fcd4a54..29437a2 100644 --- a/src/gui/elems/mainWindow/mainIO.cpp +++ b/src/gui/elems/mainWindow/mainIO.cpp @@ -46,50 +46,40 @@ namespace giada { namespace v { geMainIO::geMainIO(int x, int y) -: gePack(x, y, Direction::HORIZONTAL) +: gePack (x, y, Direction::HORIZONTAL) +, outMeter (0, 0, 140, G_GUI_UNIT) +, inMeter (0, 0, 140, G_GUI_UNIT) +, outVol (0, 0, G_GUI_UNIT, G_GUI_UNIT) +, inVol (0, 0, G_GUI_UNIT, G_GUI_UNIT) +, inToOut (0, 0, 12, G_GUI_UNIT, "") +#ifdef WITH_VST +, masterFxOut(0, 0, G_GUI_UNIT, G_GUI_UNIT, fxOff_xpm, fxOn_xpm) +, masterFxIn (0, 0, G_GUI_UNIT, G_GUI_UNIT, fxOff_xpm, fxOn_xpm) +#endif { -#if defined(WITH_VST) - - masterFxIn = new geStatusButton(0, 0, G_GUI_UNIT, G_GUI_UNIT, fxOff_xpm, fxOn_xpm); - inVol = new geDial (0, 0, G_GUI_UNIT, G_GUI_UNIT); - inMeter = new geSoundMeter (0, 0, 140, G_GUI_UNIT); - inToOut = new geButton (0, 0, 12, G_GUI_UNIT, ""); - outMeter = new geSoundMeter (0, 0, 140, G_GUI_UNIT); - outVol = new geDial (0, 0, G_GUI_UNIT, G_GUI_UNIT); - masterFxOut = new geStatusButton(0, 0, G_GUI_UNIT, G_GUI_UNIT, fxOff_xpm, fxOn_xpm); - add(masterFxIn); - add(inVol); - add(inMeter); - add(inToOut); - add(outMeter); - add(outVol); - add(masterFxOut); - -#else - - inVol = new geDial (0, 0, G_GUI_UNIT, G_GUI_UNIT); - inMeter = new geSoundMeter(0, 0, 140, G_GUI_UNIT); - outMeter = new geSoundMeter(0, 0, 140, G_GUI_UNIT); - outVol = new geDial (0, 0, G_GUI_UNIT, G_GUI_UNIT); - add(inVol); - add(inMeter); - add(outMeter); - add(outVol); - +#ifdef WITH_VST + add(&masterFxIn); +#endif + add(&inVol); + add(&inMeter); + add(&inToOut); + add(&outMeter); + add(&outVol); +#ifdef WITH_VST + add(&masterFxOut); #endif resizable(nullptr); // don't resize any widget - outVol->callback(cb_outVol, (void*)this); - inVol->callback(cb_inVol, (void*)this); + outVol.callback(cb_outVol, (void*)this); + inVol.callback(cb_inVol, (void*)this); -#ifdef WITH_VST - - masterFxOut->callback(cb_masterFxOut, (void*)this); - masterFxIn->callback(cb_masterFxIn, (void*)this); - inToOut->callback(cb_inToOut, (void*)this); - inToOut->type(FL_TOGGLE_BUTTON); + inToOut.callback(cb_inToOut, (void*)this); + inToOut.type(FL_TOGGLE_BUTTON); +#ifdef WITH_VST + masterFxOut.callback(cb_masterFxOut, (void*)this); + masterFxIn.callback(cb_masterFxIn, (void*)this); #endif } @@ -99,10 +89,10 @@ geMainIO::geMainIO(int x, int y) void geMainIO::cb_outVol (Fl_Widget* v, void* p) { ((geMainIO*)p)->cb_outVol(); } void geMainIO::cb_inVol (Fl_Widget* v, void* p) { ((geMainIO*)p)->cb_inVol(); } +void geMainIO::cb_inToOut (Fl_Widget* v, void* p) { ((geMainIO*)p)->cb_inToOut(); } #ifdef WITH_VST void geMainIO::cb_masterFxOut(Fl_Widget* v, void* p) { ((geMainIO*)p)->cb_masterFxOut(); } void geMainIO::cb_masterFxIn (Fl_Widget* v, void* p) { ((geMainIO*)p)->cb_masterFxIn(); } -void geMainIO::cb_inToOut (Fl_Widget* v, void* p) { ((geMainIO*)p)->cb_inToOut(); } #endif @@ -111,19 +101,21 @@ void geMainIO::cb_inToOut (Fl_Widget* v, void* p) { ((geMainIO*)p)->cb_inToOu void geMainIO::cb_outVol() { - c::events::setMasterOutVolume(outVol->value(), Thread::MAIN); + c::events::setMasterOutVolume(outVol.value(), Thread::MAIN); } -/* -------------------------------------------------------------------------- */ - - void geMainIO::cb_inVol() { - c::events::setMasterInVolume(inVol->value(), Thread::MAIN); + c::events::setMasterInVolume(inVol.value(), Thread::MAIN); } +void geMainIO::cb_inToOut() +{ + c::main::setInToOut(inToOut.value()); +} + /* -------------------------------------------------------------------------- */ @@ -140,12 +132,6 @@ void geMainIO::cb_masterFxIn() u::gui::openSubWindow(G_MainWin, new v::gdPluginList(m::mixer::MASTER_IN_CHANNEL_ID), WID_FX_LIST); } - -void geMainIO::cb_inToOut() -{ - c::main::setInToOut(inToOut->value()); -} - #endif @@ -154,13 +140,13 @@ void geMainIO::cb_inToOut() void geMainIO::setOutVol(float v) { - outVol->value(v); + outVol.value(v); } void geMainIO::setInVol(float v) { - inVol->value(v); + inVol.value(v); } @@ -171,13 +157,13 @@ void geMainIO::setInVol(float v) void geMainIO::setMasterFxOutFull(bool v) { - masterFxOut->setStatus(v); + masterFxOut.setStatus(v); } void geMainIO::setMasterFxInFull(bool v) { - masterFxIn->setStatus(v); + masterFxIn.setStatus(v); } #endif @@ -188,10 +174,10 @@ void geMainIO::setMasterFxInFull(bool v) void geMainIO::refresh() { - outMeter->mixerPeak = m_io.a_getMasterOutPeak(); - inMeter->mixerPeak = m_io.a_getMasterInPeak(); - outMeter->redraw(); - inMeter->redraw(); + outMeter.mixerPeak = m_io.a_getMasterOutPeak(); + inMeter.mixerPeak = m_io.a_getMasterInPeak(); + outMeter.redraw(); + inMeter.redraw(); } @@ -202,12 +188,12 @@ void geMainIO::rebuild() { m_io = c::main::getIO(); - outVol->value(m_io.masterOutVol); - inVol->value(m_io.masterInVol); + outVol.value(m_io.masterOutVol); + inVol.value(m_io.masterInVol); #ifdef WITH_VST - masterFxOut->setStatus(m_io.masterOutHasPlugins); - masterFxIn->setStatus(m_io.masterInHasPlugins); - inToOut->value(m_io.inToOut); + masterFxOut.setStatus(m_io.masterOutHasPlugins); + masterFxIn.setStatus(m_io.masterInHasPlugins); + inToOut.value(m_io.inToOut); #endif } }} // giada::v:: diff --git a/src/gui/elems/mainWindow/mainIO.h b/src/gui/elems/mainWindow/mainIO.h index 1f9a850..3d6c0dd 100644 --- a/src/gui/elems/mainWindow/mainIO.h +++ b/src/gui/elems/mainWindow/mainIO.h @@ -29,16 +29,14 @@ #define GE_MAIN_IO_H +#include "gui/elems/soundMeter.h" #include "gui/elems/basics/pack.h" -#include "glue/main.h" - - -class geSoundMeter; -class geDial; +#include "gui/elems/basics/dial.h" +#include "gui/elems/basics/button.h" #ifdef WITH_VST -class geStatusButton; -class geButton; +#include "gui/elems/basics/statusButton.h" #endif +#include "glue/main.h" namespace giada { @@ -62,31 +60,29 @@ public: private: - static void cb_outVol (Fl_Widget* v, void* p); - static void cb_inVol (Fl_Widget* v, void* p); -#ifdef WITH_VST - static void cb_masterFxOut(Fl_Widget* v, void* p); - static void cb_masterFxIn (Fl_Widget* v, void* p); - static void cb_inToOut (Fl_Widget* v, void* p); -#endif + static void cb_outVol (Fl_Widget* v, void* p); + static void cb_inVol (Fl_Widget* v, void* p); + static void cb_inToOut(Fl_Widget* v, void* p); void cb_outVol(); void cb_inVol(); + void cb_inToOut(); #ifdef WITH_VST + static void cb_masterFxOut(Fl_Widget* v, void* p); + static void cb_masterFxIn (Fl_Widget* v, void* p); void cb_masterFxOut(); void cb_masterFxIn(); - void cb_inToOut(); #endif c::main::IO m_io; - geSoundMeter* outMeter; - geSoundMeter* inMeter; - geDial* outVol; - geDial* inVol; + geSoundMeter outMeter; + geSoundMeter inMeter; + geDial outVol; + geDial inVol; + geButton inToOut; #ifdef WITH_VST - geStatusButton* masterFxOut; - geStatusButton* masterFxIn; - geButton* inToOut; + geStatusButton masterFxOut; + geStatusButton masterFxIn; #endif }; }} // giada::v:: diff --git a/src/gui/elems/mainWindow/mainTimer.cpp b/src/gui/elems/mainWindow/mainTimer.cpp index eb705ed..7f857ae 100644 --- a/src/gui/elems/mainWindow/mainTimer.cpp +++ b/src/gui/elems/mainWindow/mainTimer.cpp @@ -147,11 +147,12 @@ void geMainTimer::refresh() divider->deactivate(); } else { - /* Don't reactivate bpm when using JACK. It must stay disabled. */ - #if defined(G_OS_LINUX) || defined(G_OS_FREEBSD) + /* Don't reactivate bpm when using JACK. It must stay disabled. */ if (m_timer.isUsingJack) bpm->deactivate(); + else + bpm->activate(); #else bpm->activate(); #endif @@ -174,12 +175,9 @@ void geMainTimer::rebuild() setQuantizer(m_timer.quantize); #if defined(G_OS_LINUX) || defined(G_OS_FREEBSD) - /* Can't change bpm from within Giada when using JACK. */ - if (m_timer.isUsingJack) bpm->deactivate(); - #endif } diff --git a/src/gui/elems/mainWindow/mainTimer.h b/src/gui/elems/mainWindow/mainTimer.h index 676b1fc..e0375ea 100644 --- a/src/gui/elems/mainWindow/mainTimer.h +++ b/src/gui/elems/mainWindow/mainTimer.h @@ -34,12 +34,12 @@ class geButton; -class geChoice; namespace giada { namespace v { +class geChoice; class geMainTimer : public geGroup { public: diff --git a/src/gui/elems/mainWindow/mainTransport.cpp b/src/gui/elems/mainWindow/mainTransport.cpp index cf7c680..305d588 100644 --- a/src/gui/elems/mainWindow/mainTransport.cpp +++ b/src/gui/elems/mainWindow/mainTransport.cpp @@ -51,7 +51,7 @@ geMainTransport::geMainTransport(int x, int y) rewind = new geButton (0, 0, 25, 25, "", rewindOff_xpm, rewindOn_xpm); play = new geStatusButton(0, 0, 25, 25, play_xpm, pause_xpm); spacer1 = new geBox (0, 0, 10, 25); - recTriggerMode = new geButton (0, 0, 15, 25, "", recTriggerModeOff_xpm, recTriggerModeOn_xpm); + recTriggerMode = new geStatusButton(0, 0, 15, 25, recTriggerModeOff_xpm, recTriggerModeOn_xpm); recAction = new geStatusButton(0, 0, 25, 25, recOff_xpm, recOn_xpm); recInput = new geStatusButton(0, 0, 25, 25, inputRecOff_xpm, inputRecOn_xpm); spacer2 = new geBox (0, 0, 10, 25); @@ -81,10 +81,8 @@ geMainTransport::geMainTransport(int x, int y) c::events::toggleInputRecording(); }); - recTriggerMode->value(static_cast(m::conf::conf.recTriggerMode)); - recTriggerMode->type(FL_TOGGLE_BUTTON); recTriggerMode->callback([](Fl_Widget* w, void* v) { - m::conf::conf.recTriggerMode = static_cast(static_cast(w)->value()); + c::main::toggleRecOnSignal(); }); metronome->type(FL_TOGGLE_BUTTON); @@ -103,5 +101,6 @@ void geMainTransport::refresh() recAction->setStatus(m::recManager::isRecordingAction()); recInput->setStatus(m::recManager::isRecordingInput()); metronome->setStatus(m::sequencer::isMetronomeOn()); + recTriggerMode->setStatus(m::conf::conf.recTriggerMode == RecTriggerMode::SIGNAL); } }} // giada::v:: diff --git a/src/gui/elems/mainWindow/mainTransport.h b/src/gui/elems/mainWindow/mainTransport.h index 1338e39..063f206 100644 --- a/src/gui/elems/mainWindow/mainTransport.h +++ b/src/gui/elems/mainWindow/mainTransport.h @@ -53,7 +53,7 @@ private: geButton* rewind; geStatusButton* play; geBox* spacer1; - geButton* recTriggerMode; + geStatusButton* recTriggerMode; geStatusButton* recAction; geStatusButton* recInput; geBox* spacer2; diff --git a/src/gui/elems/plugin/pluginElement.h b/src/gui/elems/plugin/pluginElement.h index c9acb07..5314548 100644 --- a/src/gui/elems/plugin/pluginElement.h +++ b/src/gui/elems/plugin/pluginElement.h @@ -36,13 +36,13 @@ #include "glue/plugin.h" -class geChoice; class geButton; namespace giada { namespace v { +class geChoice; class gdPluginList; class gePluginElement : public Fl_Pack { diff --git a/src/utils/gui.cpp b/src/utils/gui.cpp index a0fa5bd..4d8c8d4 100644 --- a/src/utils/gui.cpp +++ b/src/utils/gui.cpp @@ -268,6 +268,22 @@ std::string removeFltkChars(const std::string& s) /* -------------------------------------------------------------------------- */ +std::string truncate(const std::string& s, Pixel width) +{ + if (getStringWidth(s) <= width) + return s; + + std::string tmp = s; + std::size_t size = tmp.size(); + while (getStringWidth(tmp) > width) + tmp.resize(--size); + + return tmp + "..."; +} + +/* -------------------------------------------------------------------------- */ + + int centerWindowX(int w) { return (Fl::w() / 2) - (w / 2); diff --git a/src/utils/gui.h b/src/utils/gui.h index 9a05df5..323b534 100644 --- a/src/utils/gui.h +++ b/src/utils/gui.h @@ -30,6 +30,7 @@ #include +#include "core/types.h" namespace giada @@ -104,6 +105,11 @@ std::string removeFltkChars(const std::string& s); int getStringWidth(const std::string& s); +/* truncate +Adds ellipsis to a string 's' if it longer than 'width' pixels. */ + +std::string truncate(const std::string& s, Pixel width); + int centerWindowX(int w); int centerWindowY(int h); diff --git a/tests/audioBuffer.cpp b/tests/audioBuffer.cpp index 189f416..31fb1e1 100644 --- a/tests/audioBuffer.cpp +++ b/tests/audioBuffer.cpp @@ -32,14 +32,6 @@ TEST_CASE("AudioBuffer") REQUIRE(buffer.countChannels() == 2); } - SECTION("test odd channels count") - { - buffer.alloc(BUFFER_SIZE, 7); - REQUIRE(buffer.countFrames() == BUFFER_SIZE); - REQUIRE(buffer.countSamples() == BUFFER_SIZE * 7); - REQUIRE(buffer.countChannels() == 7); - } - buffer.free(); REQUIRE(buffer.countFrames() == 0); diff --git a/tests/waveFx.cpp b/tests/waveFx.cpp index ca25d01..52b6400 100644 --- a/tests/waveFx.cpp +++ b/tests/waveFx.cpp @@ -64,15 +64,6 @@ TEST_CASE("waveFx") for (int i=a; i +#include #include "../src/core/waveManager.h" #include "../src/core/wave.h" #include "../src/core/const.h" @@ -21,7 +22,8 @@ TEST_CASE("waveManager") SECTION("test creation") { - waveManager::Result res = waveManager::createFromFile("tests/resources/test.wav"); + waveManager::Result res = waveManager::createFromFile("tests/resources/test.wav", + /*ID=*/0, /*sampleRate=*/G_SAMPLE_RATE, /*quality=*/SRC_LINEAR); REQUIRE(res.status == G_RES_OK); REQUIRE(res.wave->getRate() == G_SAMPLE_RATE); @@ -44,7 +46,8 @@ TEST_CASE("waveManager") SECTION("test resampling") { - waveManager::Result res = waveManager::createFromFile("tests/resources/test.wav"); + waveManager::Result res = waveManager::createFromFile("tests/resources/test.wav", + /*ID=*/0, /*sampleRate=*/G_SAMPLE_RATE, /*quality=*/SRC_LINEAR); int oldSize = res.wave->getSize(); waveManager::resample(*res.wave.get(), 1, G_SAMPLE_RATE * 2); -- 2.30.2