New upstream version 0.16.4+ds1
authorDennis Braun <d_braun@kabelmail.de>
Thu, 24 Sep 2020 18:21:08 +0000 (20:21 +0200)
committerDennis Braun <d_braun@kabelmail.de>
Thu, 24 Sep 2020 18:21:08 +0000 (20:21 +0200)
79 files changed:
ChangeLog
Makefile.am
create_source_tarball.sh
extras/giada-logo.svg [new file with mode: 0644]
src/core/audioBuffer.cpp
src/core/audioBuffer.h
src/core/channels/audioReceiver.cpp
src/core/channels/audioReceiver.h
src/core/channels/channel.cpp
src/core/channels/channel.h
src/core/channels/channelManager.cpp
src/core/channels/channelManager.h
src/core/channels/sampleActionRecorder.cpp
src/core/channels/sampleActionRecorder.h
src/core/channels/samplePlayer.cpp
src/core/channels/samplePlayer.h
src/core/channels/state.cpp
src/core/channels/state.h
src/core/channels/waveReader.cpp
src/core/conf.cpp
src/core/conf.h
src/core/const.h
src/core/graphics.cpp
src/core/graphics.h
src/core/kernelAudio.cpp
src/core/midiDispatcher.cpp
src/core/mixer.cpp
src/core/mixer.h
src/core/mixerHandler.cpp
src/core/mixerHandler.h
src/core/model/storage.cpp
src/core/patch.cpp
src/core/patch.h
src/core/recManager.cpp
src/core/wave.cpp
src/core/wave.h
src/core/waveManager.cpp
src/core/waveManager.h
src/glue/channel.cpp
src/glue/channel.h
src/glue/events.cpp
src/glue/events.h
src/glue/main.cpp
src/glue/main.h
src/gui/dialogs/actionEditor/baseActionEditor.h
src/gui/dialogs/config.h
src/gui/dialogs/mainWindow.cpp
src/gui/dialogs/midiIO/midiInputBase.h
src/gui/dialogs/midiIO/midiInputChannel.h
src/gui/dialogs/midiIO/midiInputMaster.h
src/gui/dialogs/pluginChooser.h
src/gui/dialogs/sampleEditor.h
src/gui/dispatcher.cpp
src/gui/elems/actionEditor/gridTool.h
src/gui/elems/basics/baseButton.cpp
src/gui/elems/basics/check.cpp
src/gui/elems/basics/check.h
src/gui/elems/basics/choice.cpp
src/gui/elems/basics/choice.h
src/gui/elems/config/tabAudio.cpp
src/gui/elems/config/tabAudio.h
src/gui/elems/config/tabBehaviors.cpp
src/gui/elems/config/tabBehaviors.h
src/gui/elems/config/tabMidi.h
src/gui/elems/config/tabMisc.h
src/gui/elems/config/tabPlugins.cpp
src/gui/elems/mainWindow/keyboard/sampleChannel.cpp
src/gui/elems/mainWindow/mainIO.cpp
src/gui/elems/mainWindow/mainIO.h
src/gui/elems/mainWindow/mainTimer.cpp
src/gui/elems/mainWindow/mainTimer.h
src/gui/elems/mainWindow/mainTransport.cpp
src/gui/elems/mainWindow/mainTransport.h
src/gui/elems/plugin/pluginElement.h
src/utils/gui.cpp
src/utils/gui.h
tests/audioBuffer.cpp
tests/waveFx.cpp
tests/waveManager.cpp

index 76ea8d940bc8359a07e32771959bb00ec83e7e75..ecdf42e972bac0a3f60c2a6d4408321b5530f513 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
 --------------------------------------------------------------------------------
 
 
+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
index ee5841677e6f79d75f66451590e26cb39ca7f0f4..9cc69bc51abc5b17d0338a9afc727b1bd43db9eb 100644 (file)
@@ -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 \
index 7be10d08a3e25bf80b747687ca99b72079fdf10f..aff5570ca14144a7dfd175592e429d6d5a4eae02 100755 (executable)
@@ -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 (file)
index 0000000..17e6885
--- /dev/null
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Creator: CorelDRAW X7 -->
+<svg xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" style="image-rendering:optimizeQuality;text-rendering:geometricPrecision;shape-rendering:geometricPrecision" xmlns="http://www.w3.org/2000/svg" fill-rule="evenodd" xml:space="preserve" height="1.557in" width="1.557in" version="1.1" clip-rule="evenodd" xmlns:cc="http://creativecommons.org/ns#" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 1557.0001 1556.9888" xmlns:dc="http://purl.org/dc/elements/1.1/"><metadata><rdf:RDF><cc:Work rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/><dc:title/></cc:Work></rdf:RDF></metadata><defs><style type="text/css"><![CDATA[
+    .str0 {stroke:#D2D3D5;stroke-width:6.94488}
+    .fil2 {fill:#FEFEFE}
+    .fil0 {fill:#FEFEFE}
+    .fil11 {fill:#E6E7E8}
+    .fil5 {fill:#4B4B4D}
+    .fil1 {fill:#454545}
+    .fil4 {fill:#E01C26}
+    .fil10 {fill:#E01C26}
+    .fil7 {fill:url(#id0)}
+    .fil8 {fill:url(#id1)}
+    .fil3 {fill:url(#id2)}
+    .fil6 {fill:url(#id3)}
+    .fil9 {fill:url(#id4)}
+   ]]></style><linearGradient id="id0" y2="14445" gradientUnits="userSpaceOnUse" x2="11848" y1="14445" x1="10647"><stop stop-color="#FEFEFE" offset="0"/><stop stop-color="#fff" offset="1"/></linearGradient><linearGradient id="id1" y2="12968" gradientUnits="userSpaceOnUse" x2="2744.5" y1="12032" x1="2042.5"><stop stop-color="#E01C26" offset="0"/><stop stop-color="#822F2F" offset="1"/></linearGradient><linearGradient id="id2" y2="10789" xlink:href="#id1" gradientUnits="userSpaceOnUse" x2="11599" y1="9853.5" x1="10897"/><linearGradient id="id3" y2="14913" xlink:href="#id1" gradientUnits="userSpaceOnUse" x2="11599" y1="13978" x1="10897"/><linearGradient id="id4" y2="22898" gradientUnits="userSpaceOnUse" x2="2500.9" y1="17663" x1="7754.9"><stop stop-color="#E01C26" offset="0"/><stop stop-color="#993132" offset="1"/></linearGradient></defs><g transform="translate(-1615 -3941)"><path d="m2394 3941c430 0 778 348 778 779 0 430-348 778-778 778-431 0-779-348-779-778 0-431 348-779 779-779z" class="fil1" fill="#454545"/><path d="m2394 4362c197 0 357 160 357 358v2h11c91 0 171 43 222 110 7-36 10-74 10-112 0-332-269-601-600-601-332 0-601 269-601 601 0 331 269 600 601 600 89 0 175-20 251-55 27 10 56 15 86 15h17c126-9 227-114 227-243 0-48-14-92-37-130-44-68-120-114-207-114-135 0-244 109-244 244 0 9 0 18 1 27-30 8-62 13-94 13-198 0-358-160-358-357 0-198 160-358 358-358zm151 263c-32-50-88-84-151-84-99 0-179 80-179 179 0 98 80 178 179 178 4 0 8 0 12-1 93-6 166-83 166-177 0-35-10-68-27-95z" class="fil2" fill="#fefefe"/></g></svg>
index 01b7f8d4a0a487fef8686d7ac12307b5e02db965..483e3c45d9e4152c62b9fa8e3ab034bd1d3af8a6 100644 (file)
@@ -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++)
index 6c0128b7916f2947edd1af88b259590dab7c6e03..67247aa56d7c19be453b803dea0d3ac28cf84ca3 100644 (file)
 
 #include <array>
 #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<float, G_MAX_IO_CHANS>;
+       using Pan = std::array<float, NUM_CHANS>;
 
        /* 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);
 
index 7d8a118ce429da6513179870592c4d7f0491698a..dd222e65eec8d90a76b2f29cb244889b8e1261b0 100644 (file)
@@ -32,8 +32,8 @@
 namespace giada {
 namespace m 
 {
-AudioReceiver::AudioReceiver(ChannelState* c)
-: state         (std::make_unique<AudioReceiverState>())
+AudioReceiver::AudioReceiver(ChannelState* c, const conf::Conf& conf)
+: state         (std::make_unique<AudioReceiverState>(conf))
 , m_channelState(c)
 {
 }
index a285f925e7d38930e4d436e9f2f09a1028ce95b1..443bd3c09ec30b0f5000a8bf4f055a74fa073f3d 100644 (file)
 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);
 
index 16b625b0c63a114b7617f8ac376e50ba98e5ca84..502edc0ac34a23245738ae37594c49bb0978698d 100644 (file)
 namespace giada {
 namespace m 
 {
-Channel::Channel(ChannelType type, ID id, ID columnId, Frame bufferSize)
-: id            (id)
-, state         (std::make_unique<ChannelState>(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<ChannelState>(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::
index 84500114c0ab61bb39c9e5bc09c20b4912bb50bf..cb0291af49501d15eade3ca142ccf8813c20a295 100644 (file)
 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;
     
index 260fd5ea3aecca258aad6e0180eb80e5b4b9a472..f38dd9b766914b7434e48567d240c8f542d3f483 100644 (file)
@@ -68,11 +68,13 @@ void init()
 /* -------------------------------------------------------------------------- */
 
 
-std::unique_ptr<Channel> create(ChannelType type, int bufferSize, 
-       bool inputMonitorOn, ID columnId)
+std::unique_ptr<Channel> create(ChannelType type, int bufferSize, ID columnId,
+       const conf::Conf& conf)
 {
-       return std::make_unique<Channel>(type, channelId_.get(), columnId, 
-               kernelAudio::getRealBufSize());
+       std::unique_ptr<Channel> ch = std::make_unique<Channel>(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
index e7667feaac47378d46a306c59ceeb4e09d7964f0..d15aa6a2463d778633de2472138b3be8d2e91639 100644 (file)
 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<Channel> create(ChannelType type, int bufferSize, 
-       bool inputMonitorOn, ID columnId);
+std::unique_ptr<Channel> create(ChannelType type, int bufferSize, ID columnId,
+       const conf::Conf& conf);
 
 /* create (2)
 Creates a new Channel given an existing one (i.e. clone). */
index 10c61c64336b39878526f95c9efdeac4cd6d3a07..2b37b720a763461c17a3313f2d83ff8bde010bd2 100644 (file)
@@ -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()) 
index 0f81b179d15e0297aaa7ac48b85afa913d9e8a8e..b6bf451655ea8cd719ee613d603a59b513a30a96 100644 (file)
@@ -64,6 +64,7 @@ private:
     void toggleReadActions() const;
     void startReadActions() const;
     void stopReadActions(ChannelStatus curRecStatus) const;
+    void killReadActions() const;
 
     bool canRecord() const;
 
index e9b96ac535335b4da94c641951aec0c756e53cf6..a9ff0e158d9df3ff918d1b82ff306eaa99ec3e87 100644 (file)
@@ -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);
+    }
 }
 
 
index d22af295c16a49dff6f95df142e728d34b0d51ff..2ddfbc5e18424f270653d9347981dcee5e41b68f 100644 (file)
@@ -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). */
index 18f4aa7caf8b4de59506946e88a762fee8a4ae71..415f554a7ac0eeb243fc491beaf9e126094002ce 100644 (file)
@@ -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())
 {
 }
 
index e7e85766d033aa7286164ce83b2ee8fc91f5dfd0..dc0416978036bdd46af29ad4d584ef30910668d2 100644 (file)
 
 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<bool> inputMonitor;
+    std::atomic<bool> overdubProtection;
 };
 
 
index b96c71d8d7f02af4e6aff7d30db64c398a9398e8..9f5717401d4660092d7b0e1c0dc22978d24885fb 100644 (file)
@@ -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;
 }
index f68be90c5dde91ba3008c0323f75f3c52ae7eeee..7c68c4490a2eeff947932009c92b47cc34fe84a4 100644 (file)
@@ -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<int>(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<int>(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_);
index d7dc210188156ee5f40353924426ac0746c52d8e..c71ec2dd29fe5e1eacef3553e09cbbcd4ba487a3 100644 (file)
@@ -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;
index 21999d62c48858afd603c35ac309a8019131aacb..aa0d1d38cad21c2898dd743ef12ac53004a82c97 100644 (file)
 
 /* -- 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 */
 
index 74e09a3023bee401635c8ca668905c63b78db668..3a9e83a0d48e13ff88d3cedb054c843f2f29b8d9 100644 (file)
@@ -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
index 3c2cb1d613438538e2ea183e921d5492e0cf8b31..7db77c9976e23cb87dddd8be8a0336f11532fbc0 100644 (file)
@@ -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[];
index 0068c0c1f7521ea8128c795279803c8107c4b7bd..e0dc4e8b8da6ed4529ac4f4c8b861685e91fe7be 100644 (file)
@@ -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) {
index 269cb7fdebeb003bcb4a6a17e57e2c57d97a3e7c..ed37976c05e1db336fac2cfb5f543fd1d0cc7670 100644 (file)
@@ -282,11 +282,11 @@ void learnMaster_(MidiEvent e, int param, std::function<void()> 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;
                }
        });
 
index a556b7a7693adbf972c0ad1bbd284d9c6589ca4c..a7cef0f17e9175d2c219ce45440d1238df9ae38e 100644 (file)
@@ -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<float*>(outBuf), bufferSize, G_MAX_IO_CHANS);
        if (kernelAudio::isInputEnabled())
-               in.setData((float*) inBuf, bufferSize, G_MAX_IO_CHANS);
+               in.setData(static_cast<float*>(inBuf), bufferSize, conf::conf.channelsInCount);
 
        /* Reset peak computation. */
 
index ea830e4f646893f0405c46debe788377a34fe8df..46024c1d995bc7816e7039d91fc0e169dada0890 100644 (file)
@@ -64,6 +64,7 @@ enum class EventType
        MIDI, 
        ACTION, 
        CHANNEL_TOGGLE_READ_ACTIONS,
+       CHANNEL_KILL_READ_ACTIONS,
        CHANNEL_TOGGLE_ARM,
        CHANNEL_MUTE,
        CHANNEL_SOLO,
index 6d864d788a19073e04bc0eead2425b2e468b85ac..305527e676457abb48e345206b3636551b45bcf2 100644 (file)
@@ -66,7 +66,7 @@ namespace
 std::unique_ptr<Channel> createChannel_(ChannelType type, ID columnId, ID channelId=0)
 {
        std::unique_ptr<Channel> 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<Channel> 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<ID> getChannelsWithWave_()
 
 std::vector<ID> getRecordableChannels_()
 {
-       return getChannelsIf_([] (const Channel* c)
-       {
-               return c->canInputRec();
-       });
+       return getChannelsIf_([] (const Channel* c) { return c->canInputRec() && !c->hasWave(); });
+}
+
+
+std::vector<ID> getOverdubbableChannels_()
+{
+       return getChannelsIf_([] (const Channel* c) { return c->canInputRec() && c->hasWave(); });
 }
 
 
@@ -155,6 +149,76 @@ void pushWave_(Channel& ch, std::unique_ptr<Wave>&& 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> 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> 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) 
index 659877252d33580c597a3a11b3d6171ee34b9c0e..4e1cf0536fae0d4d23e5dbd8ab00665db21f4ec5 100644 (file)
@@ -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. */
index 3fc6d39fa258b5245bf78c82206c9b464ecc6019..9042be1f8626299383dde2836e3d0211c7f4efa0 100644 (file)
@@ -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<Wave> w = waveManager::deserializeWave(pwave);
+               std::unique_ptr<Wave> 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<float>(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();
        }
index 84ad90e985f2eca19c2a2b9a5c07776834d3ab03..cedbffb6e5df3b8f58992cdafa32b94465f50cdf 100644 (file)
@@ -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;
index 7e0fa13ce5a76f19df2b271ef1082a7939d71f22..96daeaf5622a47b78b07d849c3beb93ade41105d 100644 (file)
@@ -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;
index a66f7cfe8aa57be75df50228a386f1462e730123..f03c50f1ff985e0fc7db75306564d8e209ce2ca1 100644 (file)
@@ -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();
index 95859f74084b5608882c8d1e952bcc16d36e9f28..826d350bf5059b5343eee6f86e1eb8bf22bdfbb0 100644 (file)
@@ -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::
index 13c080dc14bda092b6c148f022affffe19f44aaf..80e7e55d01e6f3d1ee97dcf45e3d94c3785e1dfe 100644 (file)
@@ -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;
index f820c937a49d0eefc9e89e538510d8079f7fac66..d07789fab1e0007b6e770b212d8880bb4a01ca70 100644 (file)
@@ -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<Wave> createFromWave(const Wave& src, int a, int b)
 
        std::unique_ptr<Wave> wave = std::make_unique<Wave>(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<Wave> createFromWave(const Wave& src, int a, int b)
 /* -------------------------------------------------------------------------- */
 
 
-std::unique_ptr<Wave> deserializeWave(const patch::Wave& w)
+std::unique_ptr<Wave> 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;
 }
 
 
index f8e7a6a77b32619802d723ff7cf92049a3a58e35..20b9c8c0de7de4291b09da58f575e19042351271 100644 (file)
@@ -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<Wave> 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<Wave> deserializeWave(const patch::Wave& w);
+std::unique_ptr<Wave> 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
index c3ecb15339b20bcf777fbcac31911ca25d5babd1..860c261e742433106e3635f828cdf28a51d98edd 100644 (file)
@@ -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);
index 2018778868677bb38d5dec6ba82f6d60eaa5e105..007e0ddd0aa8593d63a002abfe5d578c394626d8 100644 (file)
@@ -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);
 
index 504088ce1a48b52ace266220c26da808edcf5382..cbf450872117f89f99bf0f37c83906c7d9e41217 100644 (file)
@@ -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);
 }
 
 
index 0949f902f80590c88edffe3e49e1d5f8880109e6..7c8ab7b4f5a898369935dd906e1d7c8f9effc672 100644 (file)
@@ -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*
index 0e084b863ae61505f33d893bdb9890609c607ff6..7867775575f47c5b669a63f82286c081f7671c13 100644 (file)
@@ -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?"))
index c0e849018ba0acbb340ec63a6300b21a9783f819..beb21e75423ef33bc0bba9e7058273afd1c9771d 100644 (file)
@@ -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. */
index f40e4b1ff57ee5af813d3bc7d3617f95a30ec9c8..48cbc50ada75e340f0f633a6c467680c6f619195 100644 (file)
@@ -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
index a4a94f92b9923ea1b34fe0d2923bb2ee317a3fb1..b152d9b596825c7fb17950cdf51c27984c5ed6b7 100644 (file)
@@ -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:
index 49db4ad90952f8948062d03da0e7aafa939f154e..931d37cfab16f03aac329b70c64bbdd864eb9ea9 100644 (file)
@@ -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);
index 8836d32b594975b5ec10c197d36a4aba1558dfa7..74e32c2ca876962783257f7d25ee62bdab5d963d 100644 (file)
 
 class geButton;
 class geCheck;
-class geChoice;
 
 
 namespace giada {
 namespace v 
 {
+class geChoice;
 class gdMidiInputBase : public gdWindow
 {
 public:
index 8fff86c98c2a9c44edda15a447de95b25a051a4b..fb1da23b9cf6c724b515ddb16234ab8000cdc2e7 100644 (file)
 
 
 class geCheck;
-class geChoice;
 
 
 namespace giada {
 namespace v 
 {
+class geChoice;
 class geScrollPack;
 class geChannelLearnerPack : public geMidiLearnerPack
 {
index b3655ec1dba69a20d8c92ab6bcfaa6a4bc02ece5..254157a85b7c599f008c05f3c708d5f61b32cae4 100644 (file)
 
 
 class geCheck;
-class geChoice;
 
 
 namespace giada {
 namespace v 
 {
+class geChoice;
 class geMasterLearnerPack : public geMidiLearnerPack
 {
 public:
index cd5cb66d50074e5dfac18c7e65110ce3b739c460..8509be7294ad2b6cccd964755313e52681b6c754 100644 (file)
 #include "window.h"
 
 
-class geChoice;
 class geButton;
 class geButton;
 
 namespace giada {
 namespace v
 {
+class geChoice;
 class gePluginBrowser;
 
 class gdPluginChooser : public gdWindow
index 12bc59a9636addf12f98a5a515e9077cfb92ae68..5fbf8e0cc1d4e4788ad35be3498b660f74f02cb6 100644 (file)
@@ -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;
index 2a5e994032c5c745a875c49e898705127686cb89..c84dd8fac2d5c8b950ebed779349a7ed4693a49d 100644 (file)
@@ -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_) {
index d96c1af6cf007f04dfb57b0bb5442fd050748ca4..2c6c405c5793e3ced69d687429bd2433f1f3095b 100644 (file)
 #include "core/types.h"
 
 
-class geChoice;
 class geCheck;
 
 
 namespace giada {
 namespace v
 {
+class geChoice;
 class geGridTool : public Fl_Group
 {
 public:
index 3eb53ba67333a9c4fdcab8015e85fb8431c53468..45f6d5e77c566e9660c5ac6590836d458f9d2709 100644 (file)
@@ -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();
index 9a47de92198c99b89c4c0336e73b9dec7cc4f04d..5acd01b891c51a502516e74d0ddb2381d1ae3c28 100644 (file)
@@ -25,6 +25,7 @@
  * -------------------------------------------------------------------------- */
 
 
+#include <cstring>
 #include <FL/fl_draw.H>
 #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;
+}
+
index 3f7d8061d5d2d4a2abbb6f5733e440b7b02405d2..dbe53408aca244be6c75dfe5a8473e3039e07ea1 100644 (file)
@@ -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;
 };
 
 
index 4c3a3f940b4d8be79bd5f3527202d3a5a7d5fd21..9745de0f22a3d6977bb0ab8261da33bc4467a725 100644 (file)
 
 #include <string>
 #include <FL/fl_draw.H>
-#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<geChoice*>(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<void*>(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
index 8c008c67863e7b8f7f63c8537e95648c7edfbacb..98d99e26a8f2841f6190116f21f299aa45961996 100644 (file)
 #define GE_CHOICE_H
 
 
+#include <functional>
+#include <vector>
 #include <FL/Fl_Choice.H>
+#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<void(ID)> onChange = nullptr;
+
+private:
+
+       static void cb_onChange(Fl_Widget* w, void* p);
+       void cb_onChange();
 
        bool angle;
-       int  id;
+       std::vector<ID> ids;
 };
-
+}}
 
 #endif
index 88216b02da7ff6008b4e94436eda79b045e619de..1e6f4ee6a908da3f1b4681682cc25c7f9bf17416 100644 (file)
@@ -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; i<chs; i+=2) {
-               std::string tmp = u::string::iToString(i+1) + "-" + u::string::iToString(i+2);
-               channelsIn->add(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! */
index c84b8201f914e9d50319e4f8c411514c93b3b227..d0c28cf5875270cf00e67025b934f75cc8a3afe6 100644 (file)
@@ -32,7 +32,6 @@
 #include <FL/Fl_Group.H>
 
 
-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:
index 904c78d0acc26d0f1987f9e3713820ad298b795e..c14437cdf762f39eeae3d05d3c6cbb8b3a0a6d44 100644 (file)
@@ -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"
 
 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<geTabBehaviors*>(p)->cb_radio_mutex(w); 
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void geTabBehaviors::cb_radio_mutex(Fl_Widget* w)
-{
-       static_cast<Fl_Button*>(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
index 59fb16d8f3db8e30885139d3791435df9f1d0355..84f73cf143980c507dfd088adb63093e8508ea0c 100644 (file)
 
 
 #include <FL/Fl_Group.H>
-
-
-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::
 
index 6b967577c66f70ab4efb8b038359dc10da6405ca..1e99c5d5a8759b0bfff20cb518552699e9a340ae 100644 (file)
 #include <FL/Fl_Group.H>
 
 
-class geChoice;
 class geCheck;
 
 
 namespace giada {
 namespace v
 {
+class geChoice;
 class geTabMidi : public Fl_Group
 {
 public:
index c733679fe3e7e4afcfadc602917118ba43fc897c..4c119ee79ba9afdd255fa50ffb6f591c1d23a504 100644 (file)
 #include <FL/Fl_Group.H>
 
 
-class geChoice;
-
-
 namespace giada {
 namespace v
 {
+class geChoice;
 class geTabMisc : public Fl_Group
 {
 public:
index c49a1df84197180162c0e3d630aa1acb264bc79b..144aee1289930d8b59b978dd2862e251361de156 100644 (file)
@@ -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"
index 1ed3cd6d2e69e811cd394e067a01e52c0559f728..eb312bab878f4c7e61996c19be50d9a550b791f8 100644 (file)
@@ -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());           
index fcd4a543538085780a8fa11fe96a8acf9c508e6e..29437a29403dfa46a71473af14f5e73c73379eff 100644 (file)
@@ -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::
index 1f9a850ff1271218629f21a1e8500aa2bd1d205c..3d6c0dd94eb8c8253a3d326ca1bd2c0c630266c2 100644 (file)
 #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::
index eb705edbaa438717cc77e1ee458ab54c534ba525..7f857aed0de1c06b5856ab3b2f9efbb52f9b19d8 100644 (file)
@@ -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
 }
 
index 676b1fcd7d1ace104ec1fd2b77842d0b1da54d8b..e0375ea7e5c8478cdf5068ac4bc515f1526c1350 100644 (file)
 
 
 class geButton;
-class geChoice;
 
 
 namespace giada {
 namespace v
 {
+class geChoice;
 class geMainTimer : public geGroup
 {
 public:
index cf7c6803264dffb1927a31549dd5f8068ac6a766..305d58856ddd781a01fdb7d621f24ab3c364cdbf 100644 (file)
@@ -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<int>(m::conf::conf.recTriggerMode));
-       recTriggerMode->type(FL_TOGGLE_BUTTON);
        recTriggerMode->callback([](Fl_Widget* w, void* v) { 
-               m::conf::conf.recTriggerMode = static_cast<RecTriggerMode>(static_cast<geButton*>(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::
index 1338e3923fd113c017850668b59f1c7f63628bc4..063f206a839f7a64b7fe5e0b469cbb5bc0d67470 100644 (file)
@@ -53,7 +53,7 @@ private:
        geButton* rewind;
        geStatusButton* play;
        geBox* spacer1; 
-       geButton* recTriggerMode;
+       geStatusButton* recTriggerMode;
        geStatusButton* recAction;
        geStatusButton* recInput;
        geBox* spacer2;
index c9acb07d11df9490da5fb3124b4f8fda48b263a2..5314548f40b0a5afb88251f4c556a307404964f1 100644 (file)
 #include "glue/plugin.h"
 
 
-class geChoice;
 class geButton;
 
 
 namespace giada {
 namespace v
 {
+class geChoice;
 class gdPluginList;
 class gePluginElement : public Fl_Pack
 {
index a0fa5bdfcacb2025e841356f5e79787b54ac5cf1..4d8c8d421e7cb821e0d9ba426b3b0de40a3cb95b 100644 (file)
@@ -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);
index 9a05df539a9e6b9298675e9684418d3a7ad41b18..323b53412ccedb2d52a0ce737ddd172990bfd198 100644 (file)
@@ -30,6 +30,7 @@
 
 
 #include <string>
+#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);
 
index 189f4167342d285f91b2cefb31696d33f12d6e0c..31fb1e1d2a59115dfdc94ef3e2c0da0a951467b0 100644 (file)
@@ -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);
index ca25d016112560a5a56276bba3802815b79a5c58..52b6400ab4cd6d621ff9cc554d2bb4f7a4fc1223 100644 (file)
@@ -64,15 +64,6 @@ TEST_CASE("waveFx")
                for (int i=a; i<b; i++)
                        for (int k=0; k<getWave(WAVE_STEREO_ID).getChannels(); k++)
                                REQUIRE(getWave(WAVE_STEREO_ID)[i][k] == 0.0f);
-
-               SECTION("test silence (mono)")
-               {
-                       wfx::silence(getWave(WAVE_MONO_ID).id, a, b);
-
-                       for (int i=a; i<b; i++)
-                               for (int k=0; k<getWave(WAVE_MONO_ID).getChannels(); k++)
-                                       REQUIRE(getWave(WAVE_MONO_ID)[i][k] == 0.0f);
-               }
        }
 
        SECTION("test cut")
@@ -85,14 +76,6 @@ TEST_CASE("waveFx")
                wfx::cut(getWave(WAVE_STEREO_ID).id, a, b);
 
                REQUIRE(getWave(WAVE_STEREO_ID).getSize() == prevSize - range);
-
-               SECTION("test cut (mono)")
-               {
-                       prevSize = getWave(WAVE_MONO_ID).getSize();
-                       wfx::cut(getWave(WAVE_MONO_ID).id, a, b);
-
-                       REQUIRE(getWave(WAVE_MONO_ID).getSize() == prevSize - range);
-               }
        }
 
        SECTION("test trim")
@@ -104,13 +87,6 @@ TEST_CASE("waveFx")
                wfx::trim(getWave(WAVE_STEREO_ID).id, a, b);
 
                REQUIRE(getWave(WAVE_STEREO_ID).getSize() == area);
-
-               SECTION("test trim (mono)")
-               {
-                       wfx::trim(getWave(WAVE_MONO_ID).id, a, b);
-
-                       REQUIRE(getWave(WAVE_MONO_ID).getSize() == area);
-               }
        }
 
        SECTION("test fade")
@@ -125,15 +101,6 @@ TEST_CASE("waveFx")
                REQUIRE(getWave(WAVE_STEREO_ID).getFrame(a)[1] == 0.0f);
                REQUIRE(getWave(WAVE_STEREO_ID).getFrame(b)[0] == 0.0f);
                REQUIRE(getWave(WAVE_STEREO_ID).getFrame(b)[1] == 0.0f);
-
-               SECTION("test fade (mono)")
-               {
-                       wfx::fade(getWave(WAVE_MONO_ID).id, a, b, wfx::Fade::IN);
-                       wfx::fade(getWave(WAVE_MONO_ID).id, a, b, wfx::Fade::OUT);
-
-                       REQUIRE(getWave(WAVE_MONO_ID).getFrame(a)[0] == 0.0f);
-                       REQUIRE(getWave(WAVE_MONO_ID).getFrame(b)[0] == 0.0f);          
-               }
        }
 
        SECTION("test smooth")
@@ -147,12 +114,5 @@ TEST_CASE("waveFx")
                REQUIRE(getWave(WAVE_STEREO_ID).getFrame(a)[1] == 0.0f);
                REQUIRE(getWave(WAVE_STEREO_ID).getFrame(b)[0] == 0.0f);
                REQUIRE(getWave(WAVE_STEREO_ID).getFrame(b)[1] == 0.0f);
-               
-               SECTION("test smooth (mono)")
-               {
-                       wfx::smooth(getWave(WAVE_MONO_ID).id, a, b);
-                       REQUIRE(getWave(WAVE_MONO_ID).getFrame(a)[0] == 0.0f);
-                       REQUIRE(getWave(WAVE_MONO_ID).getFrame(b)[0] == 0.0f);          
-               }
        }
 }
index 790828b72f72bbc19e3d96c96da424868ee176a4..c561b005b9d9257d845f69987d1e5823d1ef7ba6 100644 (file)
@@ -1,4 +1,5 @@
 #include <memory>
+#include <samplerate.h>
 #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);