--------------------------------------------------------------------------------
+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
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 \
--single-branch \
--depth=1 \
--recurse-submodules \
+ --shallow-submodules \
"${output_name}"
}
--- /dev/null
+<?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>
void AudioBuffer::alloc(Frame size, int channels)
{
+ assert(channels <= NUM_CHANS);
+
free();
m_size = size;
m_channels = channels;
void AudioBuffer::setData(float* data, Frame size, int channels)
{
+ assert(channels <= NUM_CHANS);
+
m_data = data;
m_size = size;
m_channels = channels;
void AudioBuffer::moveData(AudioBuffer& b)
{
+ assert(b.countChannels() <= NUM_CHANS);
+
free();
m_data = b[0];
m_size = b.countFrames();
/* -------------------------------------------------------------------------- */
-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);
}
{
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++)
#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. */
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);
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)
{
}
namespace giada {
namespace m
{
+namespace conf
+{
+struct Conf;
+}
namespace patch
{
struct Channel;
{
public:
- AudioReceiver(ChannelState*);
+ AudioReceiver(ChannelState*, const conf::Conf&);
AudioReceiver(const patch::Channel&, ChannelState*);
AudioReceiver(const AudioReceiver&, ChannelState* c=nullptr);
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;
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::
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;
bool isInternal() const;
bool isMuted() const;
bool canInputRec() const;
+ bool canActionRec() const;
+ bool hasWave() const;
ID getColumnId() const;
ChannelType getType() const;
/* -------------------------------------------------------------------------- */
-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;
}
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
namespace giada {
namespace m
{
+namespace conf
+{
+struct Conf;
+}
namespace patch
{
struct Channel;
/* 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). */
onFirstBeat(); break;
case mixer::EventType::CHANNEL_TOGGLE_READ_ACTIONS:
- toggleReadActions(); break;
+ toggleReadActions(); break;
+
+ case mixer::EventType::CHANNEL_KILL_READ_ACTIONS:
+ killReadActions(); break;
default: break;
}
/* -------------------------------------------------------------------------- */
+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())
void toggleReadActions() const;
void startReadActions() const;
void stopReadActions(ChannelStatus curRecStatus) const;
+ void killReadActions() const;
bool canRecord() 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();
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()) {
/* -------------------------------------------------------------------------- */
-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);
+ }
}
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). */
* -------------------------------------------------------------------------- */
+#include "core/conf.h"
#include "core/patch.h"
#include "state.h"
/* -------------------------------------------------------------------------- */
-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())
{
}
namespace giada {
namespace m {
+namespace conf
+{
+struct Conf;
+}
namespace patch
{
struct Channel;
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;
};
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;
}
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;
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_);
{
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;
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;
/* -- 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";
/* 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 */
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";
/* 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 */
" ",
" ",
" "};
+
+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
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[];
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();
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
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) {
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;
}
});
AudioBuffer recBuffer_;
/* inBuffer_
-Working buffer for input channel. */
+Working buffer for input channel. Used for the in->out bridge. */
AudioBuffer inBuffer_;
/* -------------------------------------------------------------------------- */
/* 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)
{
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());
#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. */
MIDI,
ACTION,
CHANNEL_TOGGLE_READ_ACTIONS,
+ CHANNEL_KILL_READ_ACTIONS,
CHANNEL_TOGGLE_ARM,
CHANNEL_MUTE,
CHANNEL_SOLO,
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);
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);
}
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(); });
}
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}
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();
}
/* -------------------------------------------------------------------------- */
-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)
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. */
namespace m {
namespace model
{
-namespace
-{
-} // {anonymous}
-
-
-/* -------------------------------------------------------------------------- */
-/* -------------------------------------------------------------------------- */
-/* -------------------------------------------------------------------------- */
-
-
void store(patch::Patch& patch)
{
#ifdef WITH_VST
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)
#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));
}
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();
}
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);
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;
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;
bool readActions;
float pitch = G_DEFAULT_PITCH;
bool inputMonitor;
+ bool overdubProtection;
bool midiInVeloAsVol;
uint32_t midiInReadActions;
uint32_t midiInPitch;
return false;
clock::setStatus(ClockStatus::RUNNING);
sequencer::start();
+ m::conf::conf.recTriggerMode = RecTriggerMode::NORMAL;
return true;
}
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}
/* 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);
});
}
}
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();
/* -------------------------------------------------------------------------- */
-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); }
/* -------------------------------------------------------------------------- */
{
buffer.moveData(b);
}
-
}}; // giada::m::
/* 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;
/* -------------------------------------------------------------------------- */
-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());
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());
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);
/* -------------------------------------------------------------------------- */
-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;
}
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. */
/* (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
}
-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); }
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
+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);
Frame a_getBegin() const;
Frame a_getEnd() const;
bool a_getInputMonitor() const;
+ bool a_getOverdubProtection() const;
ID waveId;
SamplePlayerMode mode;
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);
}
+void killReadActionsChannel(ID channelId, Thread t)
+{
+ pushEvent_({ m::mixer::EventType::CHANNEL_KILL_READ_ACTIONS, 0, {0, channelId} }, t);
+}
+
+
/* -------------------------------------------------------------------------- */
void startSequencer(Thread t)
{
pushEvent_({ m::mixer::EventType::SEQUENCER_START, 0 }, t);
+ m::conf::conf.recTriggerMode = RecTriggerMode::NORMAL;
}
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);
}
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*
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),
/* -------------------------------------------------------------------------- */
+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?"))
#define G_MAIN_H
+#include "core/types.h"
+
+
namespace giada {
namespace m
{
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. */
#include "gui/dialogs/window.h"
-class geChoice;
class geButton;
}
namespace v
{
+class geChoice;
class geGridTool;
class geScrollPack;
class gdBaseActionEditor : public gdWindow
class geTabPlugins;
#endif
class geButton;
-class geChoice;
class geCheck;
class geInput;
class geRadio;
namespace giada {
namespace v
{
+class geChoice;
class gdConfig : public gdWindow
{
public:
#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);
class geButton;
class geCheck;
-class geChoice;
namespace giada {
namespace v
{
+class geChoice;
class gdMidiInputBase : public gdWindow
{
public:
class geCheck;
-class geChoice;
namespace giada {
namespace v
{
+class geChoice;
class geScrollPack;
class geChannelLearnerPack : public geMidiLearnerPack
{
class geCheck;
-class geChoice;
namespace giada {
namespace v
{
+class geChoice;
class geMasterLearnerPack : public geMidiLearnerPack
{
public:
#include "window.h"
-class geChoice;
class geButton;
class geButton;
namespace giada {
namespace v
{
+class geChoice;
class gePluginBrowser;
class gdPluginChooser : public gdWindow
class geButton;
-class geChoice;
class geCheck;
class geBox;
class geButton;
}
namespace v
{
+class geChoice;
class geVolumeTool;
class geWaveTools;
class geBoostTool;
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_) {
#include "core/types.h"
-class geChoice;
class geCheck;
namespace giada {
namespace v
{
+class geChoice;
class geGridTool : public Fl_Group
{
public:
return;
std::string out;
+ /* TODO --- use u::gui::truncate */
if (w() > 20) {
out = initLabel;
int len = initLabel.size();
* -------------------------------------------------------------------------- */
+#include <cstring>
#include <FL/fl_draw.H>
#include "core/const.h"
#include "check.h"
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;
+}
+
geCheck(int x, int y, int w, int h, const char *l=0);
void draw() override;
+
+private:
+
+ bool hasMultilineText() const;
};
#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());
}
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
#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
break;
}
-#elif defined (__APPLE__)
+#elif defined(__APPLE__)
if (m::kernelAudio::hasAPI(RtAudio::MACOSX_CORE))
soundsys->add("CoreAudio");
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)");
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);
}
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;
/* 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! */
#include <FL/Fl_Group.H>
-class geChoice;
class geCheck;
class geButton;
class geInput;
namespace giada {
namespace v
{
+class geChoice;
class geTabAudio : public Fl_Group
{
public:
#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);
}
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
#include <FL/Fl_Group.H>
-
-
-class geRadio;
-class geCheck;
+#include "gui/elems/basics/pack.h"
+#include "gui/elems/basics/check.h"
namespace giada {
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::
#include <FL/Fl_Group.H>
-class geChoice;
class geCheck;
namespace giada {
namespace v
{
+class geChoice;
class geTabMidi : public Fl_Group
{
public:
#include <FL/Fl_Group.H>
-class geChoice;
-
-
namespace giada {
namespace v
{
+class geChoice;
class geTabMisc : public Fl_Group
{
public:
#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"
enum class Menu
{
INPUT_MONITOR = 0,
+ OVERDUB_PROTECTION,
LOAD_SAMPLE,
EXPORT_SAMPLE,
SETUP_KEYBOARD_INPUT,
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);
#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);
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},
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);
}
{
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());
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
}
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
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());
+}
+
/* -------------------------------------------------------------------------- */
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
void geMainIO::setOutVol(float v)
{
- outVol->value(v);
+ outVol.value(v);
}
void geMainIO::setInVol(float v)
{
- inVol->value(v);
+ inVol.value(v);
}
void geMainIO::setMasterFxOutFull(bool v)
{
- masterFxOut->setStatus(v);
+ masterFxOut.setStatus(v);
}
void geMainIO::setMasterFxInFull(bool v)
{
- masterFxIn->setStatus(v);
+ masterFxIn.setStatus(v);
}
#endif
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();
}
{
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::
#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 {
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::
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
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
}
class geButton;
-class geChoice;
namespace giada {
namespace v
{
+class geChoice;
class geMainTimer : public geGroup
{
public:
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);
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);
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::
geButton* rewind;
geStatusButton* play;
geBox* spacer1;
- geButton* recTriggerMode;
+ geStatusButton* recTriggerMode;
geStatusButton* recAction;
geStatusButton* recInput;
geBox* spacer2;
#include "glue/plugin.h"
-class geChoice;
class geButton;
namespace giada {
namespace v
{
+class geChoice;
class gdPluginList;
class gePluginElement : public Fl_Pack
{
/* -------------------------------------------------------------------------- */
+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);
#include <string>
+#include "core/types.h"
namespace giada
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);
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);
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")
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")
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")
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")
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);
- }
}
}
#include <memory>
+#include <samplerate.h>
#include "../src/core/waveManager.h"
#include "../src/core/wave.h"
#include "../src/core/const.h"
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);
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);