+++ /dev/null
-#!/usr/bin/env bash
-
-mkdir build
-
-if [[ $TRAVIS_OS_NAME == 'osx' ]]; then
-
- cp giada_osx ./build
- upx --best ./build/giada_osx
-
-elif [[ $TRAVIS_OS_NAME == 'linux' ]]; then
-
- : # null command - nothing to do
-
- # TODO
- # cp giada_lin ./build
-
-fi
\ No newline at end of file
+++ /dev/null
-#!/usr/bin/env bash
-
-if [[ $TRAVIS_OS_NAME == 'osx' ]]; then
-
- echo ""
-
-elif [[ $TRAVIS_OS_NAME == 'linux' ]]; then
-
- sudo add-apt-repository ppa:ubuntu-toolchain-r/test -y # for gcc 6
- sudo apt-get update -qq
-
-fi
\ No newline at end of file
+++ /dev/null
-#!/usr/bin/env bash
-
-if [[ $TRAVIS_OS_NAME == 'osx' ]]; then
-
- ./autogen.sh
- ./configure --target=osx --enable-vst
-
-elif [[ $TRAVIS_OS_NAME == 'linux' ]]; then
-
- ./autogen.sh
- ./configure --target=linux #--enable-vst
-
-fi
\ No newline at end of file
+++ /dev/null
-#!/usr/bin/env bash
-
-if [[ $TRAVIS_OS_NAME == 'osx' ]]; then
-
- brew update
- brew install rtmidi
- brew install jansson
- brew install libsamplerate
- brew install fltk
- brew install libsndfile
- brew install upx
-
- #ls Remove dynamic libraries to force static linking.
-
- rm -rf /usr/local/lib/librtmidi.dylib
- rm -rf /usr/local/lib/librtmidi.4.dylib
- rm -rf /usr/local/lib/libjansson.dylib
- rm -rf /usr/local/lib/libjansson.4.dylib
- rm -rf /usr/local/lib/libsamplerate.dylib
- rm -rf /usr/local/lib/libsamplerate.0.dylib
- rm -rf /usr/local/lib/libfltk.1.3.dylib
- rm -rf /usr/local/lib/libfltk.dylib
- rm -rf /usr/local/lib/libfltk_forms.1.3.dylib
- rm -rf /usr/local/lib/libfltk_forms.dylib
- rm -rf /usr/local/lib/libfltk_forms.dylib
- rm -rf /usr/local/lib/libfltk_gl.1.3.dylib
- rm -rf /usr/local/lib/libfltk_gl.dylib
- rm -rf /usr/local/lib/libfltk_images.1.3.dylib
- rm -rf /usr/local/lib/libfltk_images.dylib
- rm -rf /usr/local/lib/libsndfile.1.dylib
- rm -rf /usr/local/lib/libsndfile.dylib
- rm -rf /usr/local/lib/libFLAC++.6.dylib
- rm -rf /usr/local/lib/libFLAC++.dylib
- rm -rf /usr/local/lib/libFLAC.8.dylib
- rm -rf /usr/local/lib/libFLAC.dylib
- rm -rf /usr/local/lib/libogg.0.dylib
- rm -rf /usr/local/lib/libogg.dylib
- rm -rf /usr/local/lib/libvorbis.0.dylib
- rm -rf /usr/local/lib/libvorbis.dylib
- rm -rf /usr/local/lib/libvorbisenc.2.dylib
- rm -rf /usr/local/lib/libvorbisenc.dylib
-
- # TODO - what about midimaps?
-
-elif [[ $TRAVIS_OS_NAME == 'linux' ]]; then
-
- sudo apt-get install -y gcc-6 g++-6 libsndfile1-dev libsamplerate0-dev \
- libfltk1.3-dev libasound2-dev libxpm-dev libpulse-dev libjack-dev \
- libxrandr-dev libx11-dev libxinerama-dev libxcursor-dev
-
- # Symlink gcc in order to use the latest version
-
- sudo ln -f -s /usr/bin/g++-6 /usr/bin/g++
-
- # Download and build latest version of RtMidi
-
- wget https://github.com/thestk/rtmidi/archive/master.zip
- unzip master.zip
- cd rtmidi-master && ./autogen.sh && ./configure --with-jack --with-alsa && make && sudo make install || true
- cd ..
-
- #wget http://www.music.mcgill.ca/~gary/rtmidi/release/rtmidi-2.1.1.tar.gz
- #tar -xvf rtmidi-2.1.1.tar.gz
- #cd rtmidi-2.1.1 && ./configure --with-jack --with-alsa && make && sudo make install || true
- #cd ..
-
- # Download and install latest version of Jansson
- # TODO - no longer needed! Use apt instead
-
- wget http://www.digip.org/jansson/releases/jansson-2.7.tar.gz
- tar -xvf jansson-2.7.tar.gz
- cd jansson-2.7 && ./configure && make && sudo make install || true
- sudo ldconfig
- cd ..
-
- # Download midimaps package for testing purposes
-
- wget https://github.com/monocasual/giada-midimaps/archive/master.zip -O giada-midimaps-master.zip
- unzip giada-midimaps-master.zip
- mkdir -p $HOME/.giada/midimaps
- cp giada-midimaps-master/midimaps/* $HOME/.giada/midimaps
-
- # Download vst plugin for testing purposes
-
- #- wget http://www.discodsp.com/download/?id=18 -O bliss-linux.zip
- #- unzip bliss-linux.zip -d bliss-linux
- #- cp bliss-linux/64-bit/Bliss64Demo.so .
-
-fi
\ No newline at end of file
+++ /dev/null
-#!/usr/bin/env bash
-
-make -j 2
-make rename
-
-if [[ $TRAVIS_OS_NAME == 'linux' ]]; then
-
- xvfb-run make check -j 2
-
-else
-
- make check -j 2
-
-fi
\ No newline at end of file
--------------------------------------------------------------------------------
+0.15.0 --- 2018 . 04 . 18
+- Refactor audio engine into frame-based processing
+- Refactor channels readers/writers into channelManager namespace
+- Smarter Solo algorithm
+- Fix missing .wav extension on recorded audio takes
+- Fix wrong Channel status update after 'Clear all actions'
+
+
0.14.6 --- 2018 . 03 . 15
- MIDI velocity drives volume for one-shot sample channels
- FLAC and Ogg support
src/core/midiMapConf.cpp \
src/core/midiEvent.h \
src/core/midiEvent.cpp \
+src/core/audioBuffer.h \
+src/core/audioBuffer.cpp \
src/core/conf.h \
src/core/conf.cpp \
src/core/kernelAudio.h \
src/core/clock.cpp \
src/core/waveManager.h \
src/core/waveManager.cpp \
+src/core/channelManager.h \
+src/core/channelManager.cpp \
src/glue/main.h \
src/glue/main.cpp \
src/glue/io.h \
tests/utils.cpp \
tests/recorder.cpp \
tests/waveFx.cpp \
+tests/audioBuffer.cpp \
src/core/conf.cpp \
src/core/wave.cpp \
src/core/waveManager.cpp \
src/core/plugin.cpp \
src/core/storager.cpp \
src/core/recorder.cpp \
+src/core/audioBuffer.cpp \
src/utils/fs.cpp \
src/utils/string.cpp \
src/utils/time.cpp \
# prereq & init
AC_PREREQ(2.60)
-AC_INIT([giada], [0.14], [giadaloopmachine@gmail.com])
+AC_INIT([giada], [0.15], [giadaloopmachine@gmail.com])
AC_CONFIG_SRCDIR([src/main.cpp])
AM_INIT_AUTOMAKE([subdir-objects])
--- /dev/null
+#include <new>
+#include <cassert>
+#include <cstring>
+#include "audioBuffer.h"
+
+
+namespace giada {
+namespace m
+{
+AudioBuffer::AudioBuffer()
+ : m_data (nullptr),
+ m_size (0),
+ m_channels(0)
+{
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+AudioBuffer::~AudioBuffer()
+{
+ free();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+float* AudioBuffer::operator [](int offset) const
+{
+ assert(m_data != nullptr);
+ assert(offset < m_size);
+ return m_data + (offset * m_channels);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void AudioBuffer::clear(int a, int b)
+{
+ if (m_data == nullptr)
+ return;
+ if (b == -1) b = m_size;
+ memset(m_data + (a * m_channels), 0, (b - a) * m_channels * sizeof(float));
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int AudioBuffer::countFrames() const { return m_size; }
+int AudioBuffer::countSamples() const { return m_size * m_channels; }
+int AudioBuffer::countChannels() const { return m_channels; }
+bool AudioBuffer::isAllocd() const { return m_data != nullptr; }
+
+
+
+/* -------------------------------------------------------------------------- */
+
+
+bool AudioBuffer::alloc(int size, int channels) noexcept
+{
+ free();
+ m_size = size;
+ m_channels = channels;
+ m_data = new (std::nothrow) float[m_size * m_channels];
+ clear(); // does nothing if m_data == nullptr
+ return m_data != nullptr;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void AudioBuffer::free()
+{
+ delete[] m_data; // No check required, delete nullptr does nothing
+ setData(nullptr, 0, 0);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void AudioBuffer::setData(float* data, int size, int channels)
+{
+ m_data = data;
+ m_size = size;
+ m_channels = channels;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void AudioBuffer::moveData(AudioBuffer& b)
+{
+ free();
+ m_data = b[0];
+ m_size = b.countFrames();
+ m_channels = b.countChannels();
+ b.setData(nullptr, 0, 0);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void AudioBuffer::copyFrame(int frame, float* values)
+{
+ assert(m_data != nullptr);
+ memcpy(m_data + (frame * m_channels), values, m_channels * sizeof(float));
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+void AudioBuffer::copyData(float* data, int frames, int offset)
+{
+ assert(m_data != nullptr);
+ assert(frames <= m_size - offset);
+ memcpy(m_data + (offset * m_channels), data, frames * m_channels * sizeof(float));
+}
+
+}} // giada::m::
\ No newline at end of file
--- /dev/null
+#ifndef G_AUDIO_BUFFER_H
+#define G_AUDIO_BUFFER_H
+
+
+namespace giada {
+namespace m
+{
+class AudioBuffer
+{
+public:
+
+ AudioBuffer();
+ ~AudioBuffer();
+
+ /* operator []
+ Given a frame 'offset', returns a pointer to it. This is useful for digging
+ inside a frame, i.e. parsing each channel. How to use it:
+
+ for (int k=0; k<buffer->countFrames(), k++)
+ for (int i=0; i<buffer->countChannels(); i++)
+ ... buffer[k][i] ...
+
+ Also note that buffer[0] will give you a pointer to the whole internal data
+ array. */
+
+ float* operator [](int offset) const;
+
+ int countFrames() const;
+ int countSamples() const;
+ int countChannels() const;
+ bool isAllocd() const;
+
+ bool alloc(int size, int channels) noexcept;
+ void free();
+
+ /* copyData
+ Copies 'frames' frames from the new 'data' into m_data, and fills m_data
+ starting from frame 'offset'. It takes for granted that the new data contains
+ the same number of channels than m_channels. */
+
+ void copyData(float* data, int frames, int offset=0);
+
+ /* copyFrame
+ Copies data pointed by 'values' into m_data[frame]. It takes for granted that
+ 'values' contains the same number of channels than m_channels. */
+
+ void copyFrame(int frame, float* values);
+
+ /* setData
+ Borrow '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, int size, int channels);
+
+ /* moveData
+ Moves data held by 'b' into this buffer. Then 'b' becomes an empty buffer. */
+
+ void moveData(AudioBuffer& b);
+
+ /* clear
+ Clears the internal data by setting all bytes to 0.0f. Optional parameters
+ 'a' and 'b' set the range. */
+
+ void clear(int a=0, int b=-1);
+
+private:
+
+ float* m_data;
+ int m_size; // in frames
+ int m_channels;
+};
+
+}} // giada::m::
+
+#endif
\ No newline at end of file
#include "../utils/log.h"
#include "../gui/elems/mainWindow/keyboard/channel.h"
#include "const.h"
-#include "channel.h"
+#include "channelManager.h"
#include "pluginHost.h"
#include "plugin.h"
#include "kernelMidi.h"
#include "patch.h"
#include "waveFx.h"
#include "midiMapConf.h"
+#include "channel.h"
using std::string;
Channel::Channel(int type, int status, int bufferSize)
: bufferSize (bufferSize),
- midiInFilter (-1),
+ volume_i (1.0f),
+ volume_d (0.0f),
+ mute_i (false),
+ guiChannel (nullptr),
previewMode (G_PREVIEW_NONE),
pan (0.5f),
volume (G_DEFAULT_VOL),
- volume_i (1.0f),
- volume_d (0.0f),
armed (false),
type (type),
status (status),
key (0),
- mute_i (false),
- mute_s (false),
mute (false),
+ mute_s (false),
solo (false),
hasActions (false),
readActions (false),
recStatus (REC_STOPPED),
- vChan (nullptr),
- guiChannel (nullptr),
midiIn (true),
midiInKeyPress (0x0),
midiInKeyRel (0x0),
midiInVolume (0x0),
midiInMute (0x0),
midiInSolo (0x0),
+ midiInFilter (-1),
midiOutL (false),
midiOutLplaying(0x0),
midiOutLmute (0x0),
Channel::~Channel()
{
status = STATUS_OFF;
- if (vChan != nullptr)
- delete[] vChan;
}
bool Channel::allocBuffers()
{
- vChan = new (std::nothrow) float[bufferSize];
- if (vChan == nullptr) {
+ if (!vChan.alloc(bufferSize, G_MAX_IO_CHANS)) {
gu_log("[Channel::allocBuffers] unable to alloc memory for vChan!\n");
return false;
}
- std::memset(vChan, 0, bufferSize * sizeof(float));
return true;
}
/* -------------------------------------------------------------------------- */
-void Channel::copy(const Channel *src, pthread_mutex_t *pluginMutex)
+void Channel::copy(const Channel* src, pthread_mutex_t* pluginMutex)
{
key = src->key;
volume = src->volume;
for (unsigned i=0; i<recorder::global.size(); i++) {
for (unsigned k=0; k<recorder::global.at(i).size(); k++) {
- recorder::action *a = recorder::global.at(i).at(k);
+ recorder::action* a = recorder::global.at(i).at(k);
if (a->chan == src->index) {
recorder::rec(index, a->type, a->frame, a->iValue, a->fValue);
hasActions = true;
/* -------------------------------------------------------------------------- */
-int Channel::writePatch(int i, bool isProject)
+void Channel::writePatch(int i, bool isProject)
{
- patch::channel_t pch;
- pch.type = type;
- pch.index = index;
- pch.size = guiChannel->getSize();
- pch.name = name;
- pch.key = key;
- pch.armed = armed;
- pch.column = guiChannel->getColumnIndex();
- pch.mute = mute;
- pch.mute_s = mute_s;
- pch.solo = solo;
- pch.volume = volume;
- pch.pan = pan;
- pch.midiIn = midiIn;
- pch.midiInKeyPress = midiInKeyPress;
- pch.midiInKeyRel = midiInKeyRel;
- pch.midiInKill = midiInKill;
- pch.midiInArm = midiInArm;
- pch.midiInVolume = midiInVolume;
- pch.midiInMute = midiInMute;
- pch.midiInFilter = midiInFilter;
- pch.midiInSolo = midiInSolo;
- pch.midiOutL = midiOutL;
- pch.midiOutLplaying = midiOutLplaying;
- pch.midiOutLmute = midiOutLmute;
- pch.midiOutLsolo = midiOutLsolo;
-
- for (unsigned i=0; i<recorder::global.size(); i++) {
- for (unsigned k=0; k<recorder::global.at(i).size(); k++) {
- recorder::action* action = recorder::global.at(i).at(k);
- if (action->chan == index) {
- patch::action_t pac;
- pac.type = action->type;
- pac.frame = action->frame;
- pac.fValue = action->fValue;
- pac.iValue = action->iValue;
- pch.actions.push_back(pac);
- }
- }
- }
-
-#ifdef WITH_VST
-
- unsigned numPlugs = pluginHost::countPlugins(pluginHost::CHANNEL, this);
- for (unsigned i=0; i<numPlugs; i++) {
- Plugin* pPlugin = pluginHost::getPluginByIndex(i, pluginHost::CHANNEL, this);
- patch::plugin_t pp;
- pp.path = pPlugin->getUniqueId();
- pp.bypass = pPlugin->isBypassed();
- for (int k=0; k<pPlugin->getNumParameters(); k++)
- pp.params.push_back(pPlugin->getParameter(k));
- for (unsigned k=0; k<pPlugin->midiInParams.size(); k++)
- pp.midiInParams.push_back(pPlugin->midiInParams.at(k));
- pch.plugins.push_back(pp);
- }
-
-#endif
-
- patch::channels.push_back(pch);
-
- return patch::channels.size() - 1;
+ channelManager::writePatch(this, isProject);
}
/* -------------------------------------------------------------------------- */
-int Channel::readPatch(const string& path, int i, pthread_mutex_t* pluginMutex,
- int samplerate, int rsmpQuality)
+void Channel::readPatch(const string& path, int i)
{
- int ret = 1;
- patch::channel_t* pch = &patch::channels.at(i);
- key = pch->key;
- armed = pch->armed;
- type = pch->type;
- name = pch->name;
- index = pch->index;
- mute = pch->mute;
- mute_s = pch->mute_s;
- solo = pch->solo;
- volume = pch->volume;
- pan = pch->pan;
- midiIn = pch->midiIn;
- midiInKeyPress = pch->midiInKeyPress;
- midiInKeyRel = pch->midiInKeyRel;
- midiInKill = pch->midiInKill;
- midiInVolume = pch->midiInVolume;
- midiInMute = pch->midiInMute;
- midiInFilter = pch->midiInFilter;
- midiInSolo = pch->midiInSolo;
- midiOutL = pch->midiOutL;
- midiOutLplaying = pch->midiOutLplaying;
- midiOutLmute = pch->midiOutLmute;
- midiOutLsolo = pch->midiOutLsolo;
-
- for (const patch::action_t& ac : pch->actions) {
- recorder::rec(index, ac.type, ac.frame, ac.iValue, ac.fValue);
- hasActions = true;
- }
-
-#ifdef WITH_VST
-
- for (const patch::plugin_t& ppl : pch->plugins) {
-
- Plugin* plugin = pluginHost::addPlugin(ppl.path, pluginHost::CHANNEL,
- pluginMutex, this);
-
- if (plugin == nullptr) {
- ret &= 0;
- continue;
- }
-
- plugin->setBypass(ppl.bypass);
-
- for (unsigned j=0; j<ppl.params.size(); j++)
- plugin->setParameter(j, ppl.params.at(j));
-
- /* Don't fill Channel::midiInParam if Patch::midiInParams are 0: it would
- wipe out the current default 0x0 values. */
-
- if (!ppl.midiInParams.empty()) {
- plugin->midiInParams.clear();
- for (uint32_t midiInParam : ppl.midiInParams)
- plugin->midiInParams.push_back(midiInParam);
- }
-
- ret &= 1;
- }
-
-#endif
-
- return ret;
+ channelManager::readPatch(this, i);
}
/* -------------------------------------------------------------------------- */
-void Channel::setMidiInFilter(int c)
-{
- midiInFilter = c;
-}
-
-
-int Channel::getMidiInFilter() const
-{
- return midiInFilter;
-}
-
-
bool Channel::isMidiInAllowed(int c) const
{
return midiInFilter == -1 || midiInFilter == c;
/* -------------------------------------------------------------------------- */
-void Channel::setVolume(float v)
-{
- volume = v;
-}
-
-
void Channel::setVolumeI(float v)
{
volume_i = v;
}
-float Channel::getVolume() const
-{
- return volume;
-}
-
-
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
-void Channel::setArmed(bool b)
-{
- armed = b;
-}
-
-
-bool Channel::isArmed() const
-{
- return armed;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-std::string Channel::getName() const
-{
- return name;
-}
-
-
-void Channel::setName(const std::string& s)
-{
- name = s;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
#ifdef WITH_VST
juce::MidiBuffer &Channel::getPluginMidiEvents()
#include "midiMapConf.h"
#include "midiEvent.h"
#include "recorder.h"
+#include "audioBuffer.h"
#ifdef WITH_VST
#include "../deps/juce-config.h"
{
protected:
+ Channel(int type, int status, int bufferSize);
+
/* sendMidiLMessage
Composes a MIDI message by merging bytes from MidiMap conf class, and sends it
to KernelMidi. */
float calcPanning(int ch);
+ /* vChan
+ Virtual channel for internal processing. */
+
+ giada::m::AudioBuffer vChan;
+
#ifdef WITH_VST
/* MidiBuffer contains MIDI events. When ready, events are sent to each plugin
int bufferSize;
- /* midiInFilter
- Which MIDI channel should be filtered out when receiving MIDI messages. -1
- means 'all'. */
-
- int midiInFilter;
-
- /* previewMode
- Whether the channel is in audio preview mode or not. */
-
- int previewMode;
-
- float pan;
- float volume; // global volume
- float volume_i; // internal volume
- float volume_d; // delta volume (for envelope)
- bool armed;
- std::string name;
+ /* volume_*
+ Internal volume variables: volume_i for envelopes, volume_d keeps track of
+ the delta during volume changes. */
+
+ float volume_i;
+ float volume_d;
+ bool mute_i; // internal mute
+
public:
- Channel(int type, int status, int bufferSize);
-
virtual ~Channel();
/* copy
virtual void copy(const Channel* src, pthread_mutex_t* pluginMutex) = 0;
- /* readPatch
- Fills channel with data from patch. */
-
- virtual int readPatch(const std::string& basePath, int i,
- pthread_mutex_t* pluginMutex, int samplerate, int rsmpQuality);
-
/* process
Merges vChannels into buffer, plus plugin processing (if any). Warning:
inBuffer might be nullptr if no input devices are available for recording. */
- virtual void process(float* outBuffer, float* inBuffer) = 0;
+ virtual void process(giada::m::AudioBuffer& out, const giada::m::AudioBuffer& in) = 0;
/* Preview
Makes itself audibile for audio preview, such as Sample Editor or other
tools. */
- virtual void preview(float* outBuffer) = 0;
+ virtual void preview(giada::m::AudioBuffer& in) = 0;
/* start
Action to do when channel starts. doQuantize = false (don't quantize)
virtual void stopBySeq(bool chansStopOnSeqHalt) = 0;
/* quantize
- Starts channel according to quantizer. Index = array index of mixer::channels,
- used by recorder. LocalFrame = frame within the current buffer. */
+ Starts channel according to quantizer. Index = array index of mixer::channels
+ used by recorder, localFrame = frame within the current buffer,
+ globalFrame = frame within the whole sequencer loop. */
- virtual void quantize(int index, int localFrame) = 0;
+ virtual void quantize(int index, int localFrame, int globalFrame) = 0;
/* onZero
What to do when frame goes to zero, i.e. sequencer restart. */
virtual bool canInputRec() = 0;
+ /* readPatch
+ Fills channel with data from patch. */
+
+ virtual void readPatch(const std::string& basePath, int i);
+
/* writePatch
Fills a patch with channel values. Returns the index of the last
Patch::channel_t added. */
- virtual int writePatch(int i, bool isProject);
+ virtual void writePatch(int i, bool isProject);
/* receiveMidi
Receives and processes midi messages from external devices. */
bool isPlaying() const;
float getPan() const;
- float getVolume() const;
- bool isArmed() const;
- std::string getName() const;
bool isPreview() const;
- int getMidiInFilter() const;
/* isMidiAllowed
Given a MIDI channel 'c' tells whether this channel should be allowed to receive
void sendMidiLplay();
void setPan(float v);
- void setVolume(float v);
void setVolumeI(float v);
- void setArmed(bool b);
- void setName(const std::string& s);
void setPreviewMode(int m);
- void setMidiInFilter(int c);
#ifdef WITH_VST
#endif
- int index; // unique id
- int type; // midi or sample
- int status; // status: see const.h
- int key; // keyboard button
- bool mute_i; // internal mute
- bool mute_s; // previous mute status after being solo'd
- bool mute; // global mute
- bool solo;
- bool hasActions; // has something recorded
- bool readActions; // read what's recorded
- int recStatus; // status of recordings (waiting, ending, ...)
- float* vChan; // virtual channel
geChannel* guiChannel; // pointer to a gChannel object, part of the GUI
+
+ /* previewMode
+ Whether the channel is in audio preview mode or not. */
- // TODO - midi structs, please
+ int previewMode;
+
+ float pan;
+ float volume; // global volume
+ bool armed;
+ std::string name;
+ int index; // unique id
+ int type; // midi or sample
+ int status; // status: see const.h
+ int key; // keyboard button
+ bool mute; // global mute
+ bool mute_s; // previous mute status after being solo'd TODO - remove it with mute refactoring
+ bool solo;
+
+ bool hasActions; // has something recorded
+ bool readActions; // read what's recorded
+ int recStatus; // status of recordings (waiting, ending, ...)
+
+ bool midiIn; // enable midi input
+ uint32_t midiInKeyPress;
+ uint32_t midiInKeyRel;
+ uint32_t midiInKill;
+ uint32_t midiInArm;
+ uint32_t midiInVolume;
+ uint32_t midiInMute;
+ uint32_t midiInSolo;
+
+ /* midiInFilter
+ Which MIDI channel should be filtered out when receiving MIDI messages. -1
+ means 'all'. */
- bool midiIn; // enable midi input
- uint32_t midiInKeyPress;
- uint32_t midiInKeyRel;
- uint32_t midiInKill;
- uint32_t midiInArm;
- uint32_t midiInVolume;
- uint32_t midiInMute;
- uint32_t midiInSolo;
+ int midiInFilter;
/* midiOutL*
* Enable MIDI lightning output, plus a set of midi lighting event to be sent
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#include "../gui/elems/mainWindow/keyboard/channel.h"
+#include "../utils/fs.h"
+#include "const.h"
+#include "channel.h"
+#include "patch.h"
+#include "mixer.h"
+#include "wave.h"
+#include "waveManager.h"
+#include "sampleChannel.h"
+#include "midiChannel.h"
+#include "pluginHost.h"
+#include "plugin.h"
+#include "channelManager.h"
+
+
+using std::string;
+
+
+namespace giada {
+namespace m {
+namespace channelManager
+{
+namespace
+{
+void writeActions_(int chanIndex, patch::channel_t& pch)
+{
+ recorder::forEachAction([&] (const recorder::action* a) {
+ if (a->chan != chanIndex)
+ return;
+ pch.actions.push_back(patch::action_t {
+ a->type, a->frame, a->fValue, a->iValue
+ });
+ });
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void writePlugins_(const Channel* ch, patch::channel_t& pch)
+{
+#ifdef WITH_VST
+
+ pluginHost::forEachPlugin(pluginHost::CHANNEL, ch, [&] (const Plugin* p) {
+ patch::plugin_t pp;
+ pp.path = p->getUniqueId();
+ pp.bypass = p->isBypassed();
+ for (int k=0; k<p->getNumParameters(); k++)
+ pp.params.push_back(p->getParameter(k));
+ for (uint32_t param : p->midiInParams)
+ pp.midiInParams.push_back(param);
+ pch.plugins.push_back(pp);
+ });
+
+#endif
+}
+} // {anonymous}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void readActions_(Channel* ch, const patch::channel_t& pch)
+{
+ for (const patch::action_t& ac : pch.actions) {
+ recorder::rec(ch->index, ac.type, ac.frame, ac.iValue, ac.fValue);
+ ch->hasActions = true;
+ }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void readPlugins_(Channel* ch, const patch::channel_t& pch)
+{
+#ifdef WITH_VST
+
+ for (const patch::plugin_t& ppl : pch.plugins) {
+ Plugin* plugin = pluginHost::addPlugin(ppl.path, pluginHost::CHANNEL,
+ &mixer::mutex_plugins, ch);
+ if (plugin == nullptr)
+ continue;
+
+ plugin->setBypass(ppl.bypass);
+ for (unsigned j=0; j<ppl.params.size(); j++)
+ plugin->setParameter(j, ppl.params.at(j));
+
+ /* Don't fill Channel::midiInParam if Patch::midiInParams are 0: it would
+ wipe out the current default 0x0 values. */
+
+ if (!ppl.midiInParams.empty()) {
+ plugin->midiInParams.clear();
+ for (uint32_t midiInParam : ppl.midiInParams)
+ plugin->midiInParams.push_back(midiInParam);
+ }
+ }
+
+#endif
+}
+
+
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+
+
+int create(int type, int bufferSize, bool inputMonitorOn, Channel** out)
+{
+ Channel* ch;
+ if (type == G_CHANNEL_SAMPLE)
+ ch = new SampleChannel(bufferSize, inputMonitorOn);
+ else
+ ch = new MidiChannel(bufferSize);
+
+ if (!ch->allocBuffers()) {
+ delete ch;
+ return G_RES_ERR_MEMORY;
+ }
+
+ *out = ch;
+
+ return G_RES_OK;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int writePatch(const Channel* ch, bool isProject)
+{
+ patch::channel_t pch;
+ pch.type = ch->type;
+ pch.index = ch->index;
+ pch.size = ch->guiChannel->getSize();
+ pch.name = ch->name;
+ pch.key = ch->key;
+ pch.armed = ch->armed;
+ pch.column = ch->guiChannel->getColumnIndex();
+ pch.mute = ch->mute;
+ // pch.mute_s = ch->mute_s; TODO remove it with mute refactoring
+ pch.solo = ch->solo;
+ pch.volume = ch->volume;
+ pch.pan = ch->pan;
+ pch.midiIn = ch->midiIn;
+ pch.midiInKeyPress = ch->midiInKeyPress;
+ pch.midiInKeyRel = ch->midiInKeyRel;
+ pch.midiInKill = ch->midiInKill;
+ pch.midiInArm = ch->midiInArm;
+ pch.midiInVolume = ch->midiInVolume;
+ pch.midiInMute = ch->midiInMute;
+ pch.midiInFilter = ch->midiInFilter;
+ pch.midiInSolo = ch->midiInSolo;
+ pch.midiOutL = ch->midiOutL;
+ pch.midiOutLplaying = ch->midiOutLplaying;
+ pch.midiOutLmute = ch->midiOutLmute;
+ pch.midiOutLsolo = ch->midiOutLsolo;
+
+ writeActions_(ch->index, pch);
+ writePlugins_(ch, pch);
+
+ patch::channels.push_back(pch);
+
+ return patch::channels.size() - 1;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void writePatch(const MidiChannel* ch, bool isProject, int index)
+{
+ patch::channel_t& pch = patch::channels.at(index);
+ pch.midiOut = ch->midiOut;
+ pch.midiOutChan = ch->midiOutChan;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void writePatch(const SampleChannel* ch, bool isProject, int index)
+{
+ patch::channel_t& pch = patch::channels.at(index);
+
+ if (ch->wave != nullptr) {
+ pch.samplePath = ch->wave->getPath();
+ if (isProject)
+ pch.samplePath = gu_basename(ch->wave->getPath()); // make it portable
+ }
+ else
+ pch.samplePath = "";
+
+ pch.mode = ch->mode;
+ pch.begin = ch->getBegin();
+ pch.end = ch->getEnd();
+ pch.boost = ch->getBoost();
+ pch.recActive = ch->readActions;
+ pch.pitch = ch->getPitch();
+ pch.inputMonitor = ch->inputMonitor;
+ pch.midiInReadActions = ch->midiInReadActions;
+ pch.midiInPitch = ch->midiInPitch;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void readPatch(Channel* ch, int i)
+{
+ const patch::channel_t& pch = patch::channels.at(i);
+
+ ch->key = pch.key;
+ ch->armed = pch.armed;
+ ch->type = pch.type;
+ ch->name = pch.name;
+ ch->index = pch.index;
+ ch->mute = pch.mute;
+ //ch->mute_s = pch.mute_s;
+ ch->solo = pch.solo;
+ ch->volume = pch.volume;
+ ch->pan = pch.pan;
+ ch->midiIn = pch.midiIn;
+ ch->midiInKeyPress = pch.midiInKeyPress;
+ ch->midiInKeyRel = pch.midiInKeyRel;
+ ch->midiInKill = pch.midiInKill;
+ ch->midiInVolume = pch.midiInVolume;
+ ch->midiInMute = pch.midiInMute;
+ ch->midiInFilter = pch.midiInFilter;
+ ch->midiInSolo = pch.midiInSolo;
+ ch->midiOutL = pch.midiOutL;
+ ch->midiOutLplaying = pch.midiOutLplaying;
+ ch->midiOutLmute = pch.midiOutLmute;
+ ch->midiOutLsolo = pch.midiOutLsolo;
+
+ readActions_(ch, pch);
+ readPlugins_(ch, pch);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void readPatch(SampleChannel* ch, const string& basePath, int i)
+{
+ const patch::channel_t& pch = patch::channels.at(i);
+
+ ch->mode = pch.mode;
+ ch->readActions = pch.recActive;
+ ch->recStatus = pch.recActive ? REC_READING : REC_STOPPED;
+ ch->midiInVeloAsVol = pch.midiInVeloAsVol;
+ ch->midiInReadActions = pch.midiInReadActions;
+ ch->midiInPitch = pch.midiInPitch;
+ ch->inputMonitor = pch.inputMonitor;
+ ch->setBoost(pch.boost);
+
+ Wave* w = nullptr;
+ int res = waveManager::create(basePath + pch.samplePath, &w);
+
+ if (res == G_RES_OK) {
+ ch->pushWave(w);
+ ch->setBegin(pch.begin);
+ ch->setEnd(pch.end);
+ ch->setPitch(pch.pitch);
+ }
+ else {
+ if (res == G_RES_ERR_NO_DATA)
+ ch->status = STATUS_EMPTY;
+ else
+ if (res == G_RES_ERR_IO)
+ ch->status = STATUS_MISSING;
+ ch->sendMidiLplay(); // FIXME - why sending MIDI lightning if sample status is wrong?
+ }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void readPatch(MidiChannel* ch, int i)
+{
+ const patch::channel_t& pch = patch::channels.at(i);
+
+ ch->midiOut = pch.midiOut;
+ ch->midiOutChan = pch.midiOutChan;
+}
+}}}; // giada::m::channelManager
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#ifndef G_CHANNEL_MANAGER_H
+#define G_CHANNEL_MANAGER_H
+
+
+#include <string>
+
+
+class Channel;
+class SampleChannel;
+class MidiChannel;
+
+
+namespace giada {
+namespace m {
+namespace channelManager
+{
+int create(int type, int bufferSize, bool inputMonitorOn, Channel** out);
+
+int writePatch(const Channel* ch, bool isProject);
+void writePatch(const SampleChannel* ch, bool isProject, int index);
+void writePatch(const MidiChannel* ch, bool isProject, int index);
+
+void readPatch(Channel* ch, int index);
+void readPatch(SampleChannel* ch, const std::string& basePath, int index);
+void readPatch(MidiChannel* ch, int index);
+}}}; // giada::m::channelManager
+
+
+#endif
\ No newline at end of file
int bars = G_DEFAULT_BARS;
int beats = G_DEFAULT_BEATS;
int quantize = G_DEFAULT_QUANTIZE;
-int quanto = 1; // quantizer step
-int framesPerBar = 0; // frames in one bar
-int framesPerBeat = 0; // frames in one beat
-int framesInSequencer = 0; // frames in the whole sequencer
-int totalFrames = 0; // frames in the selected range (e.g. 4/4)
-int currentFrame = 0;
-int currentBeat = 0;
+int quanto = 1; // quantizer step
+
+int framesInLoop = 0;
+int framesInBar = 0;
+int framesInBeat = 0;
+int framesInSeq = 0;
+int currentFrame = 0;
+int currentBeat = 0;
int midiTCrate = 0; // send MTC data every midiTCrate frames
int midiTCframes = 0;
void updateQuanto()
{
- if (quantize != 0)
- quanto = framesPerBeat / quantize;
- if (quanto % 2 != 0)
- quanto++;
+ if (quantize != 0)
+ quanto = framesInBeat / quantize;
}
}; // {anonymous}
void init(int sampleRate, float midiTCfps)
{
- midiTCrate = (sampleRate / midiTCfps) * 2; // stereo values
- running = false;
- bpm = G_DEFAULT_BPM;
- bars = G_DEFAULT_BARS;
- beats = G_DEFAULT_BEATS;
- quantize = G_DEFAULT_QUANTIZE;
- updateFrameBars();
+ midiTCrate = (sampleRate / midiTCfps) * G_MAX_IO_CHANS; // stereo values
+ running = false;
+ bpm = G_DEFAULT_BPM;
+ bars = G_DEFAULT_BARS;
+ beats = G_DEFAULT_BEATS;
+ quantize = G_DEFAULT_QUANTIZE;
+ updateFrameBars();
}
bool isRunning()
{
- return running;
+ return running;
}
bool quantoHasPassed()
{
- return currentFrame % (quanto) == 0;
+ return currentFrame % (quanto) == 0;
}
bool isOnBar()
{
- /* A bar cannot occur at frame 0. That's the first beat. */
- return currentFrame % framesPerBar == 0 && currentFrame != 0;
+ /* A bar cannot occur at frame 0. That's the first beat. */
+ return currentFrame % framesInBar == 0 && currentFrame != 0;
}
bool isOnBeat()
{
- /* Skip frame 0: it is intended as 'first beat'. */
- /* TODO - this is wrong! */
- return currentFrame % framesPerBeat == 0 && currentFrame > 0;
+ /* Skip frame 0: it is intended as 'first beat'. */
+ /* TODO - this is wrong! */
+ return currentFrame % framesInBeat == 0 && currentFrame > 0;
}
bool isOnFirstBeat()
{
- return currentFrame == 0;
+ return currentFrame == 0;
}
void start()
{
- running = true;
+ running = true;
if (conf::midiSync == MIDI_SYNC_CLOCK_M) {
kernelMidi::send(MIDI_START, -1, -1);
kernelMidi::send(MIDI_POSITION_PTR, 0, 0);
void stop()
{
- running = false;
- if (conf::midiSync == MIDI_SYNC_CLOCK_M)
- kernelMidi::send(MIDI_STOP, -1, -1);
+ running = false;
+ if (conf::midiSync == MIDI_SYNC_CLOCK_M)
+ kernelMidi::send(MIDI_STOP, -1, -1);
}
void setBpm(float b)
{
- if (b < G_MIN_BPM)
- b = G_MIN_BPM;
- bpm = b;
+ if (b < G_MIN_BPM)
+ b = G_MIN_BPM;
+ bpm = b;
updateFrameBars();
}
void setBars(int newBars)
{
- /* Bars cannot be greater than beats and must be a sub multiple of beats. If
- not, approximate to the nearest (and greater) value available. */
-
- if (newBars > beats)
- bars = beats;
- else if (newBars <= 0)
- bars = 1;
- else if (beats % newBars != 0) {
- bars = newBars + (beats % newBars);
- if (beats % bars != 0) // it could be an odd value, let's check it (and avoid it)
- bars = bars - (beats % bars);
- }
- else
- bars = newBars;
+ /* Bars cannot be greater than beats and must be a sub multiple of beats. If
+ not, approximate to the nearest (and greater) value available. */
+
+ if (newBars > beats)
+ bars = beats;
+ else if (newBars <= 0)
+ bars = 1;
+ else if (beats % newBars != 0) {
+ bars = newBars + (beats % newBars);
+ if (beats % bars != 0) // it could be an odd value, let's check it (and avoid it)
+ bars = bars - (beats % bars);
+ }
+ else
+ bars = newBars;
}
void setBeats(int b)
{
- if (b > G_MAX_BEATS)
+ if (b > G_MAX_BEATS)
beats = G_MAX_BEATS;
else if (b < 1)
- beats = 1;
+ beats = 1;
else
- beats = b;
+ beats = b;
}
void setQuantize(int q)
{
- quantize = q;
- updateQuanto();
+ quantize = q;
+ updateQuanto();
}
/* -------------------------------------------------------------------------- */
-void incrCurrentFrame()
-{
- currentFrame += 2;
- if (currentFrame > totalFrames) {
+void incrCurrentFrame() {
+ currentFrame++;
+ if (currentFrame > framesInLoop) {
currentFrame = 0;
currentBeat = 0;
}
- else
- if (isOnBeat())
- currentBeat++;
+ else
+ if (isOnBeat())
+ currentBeat++;
}
void rewind()
{
- currentFrame = 0;
- currentBeat = 0;
- sendMIDIrewind();
+ currentFrame = 0;
+ currentBeat = 0;
+ sendMIDIrewind();
}
void updateFrameBars()
{
- /* seconds ....... total time of play (in seconds) of the whole
- * sequencer. 60 / bpm == how many seconds lasts one bpm
- * totalFrames ... loop length in frames, x2 because it's stereo
- * framesPerBar .. n. of frames within a bar
- * framesPerBeat . n. of frames within a beat
- * framesInSeq ... number of frames in the whole sequencer */
-
- float seconds = (60.0f / bpm) * beats;
- totalFrames = conf::samplerate * seconds * 2;
- framesPerBar = totalFrames / bars;
- framesPerBeat = totalFrames / beats;
- framesInSequencer = framesPerBeat * G_MAX_BEATS;
-
- /* big troubles if frames are odd. */
-
- if (totalFrames % 2 != 0)
- totalFrames--;
- if (framesPerBar % 2 != 0)
- framesPerBar--;
- if (framesPerBeat % 2 != 0)
- framesPerBeat--;
-
- updateQuanto();
+ /* framesInLoop ... loop length in frames, or samplerate * # frames per
+ * current bpm * beats;
+ * framesInBar .... n. of frames within a bar;
+ * framesInBeat ... n. of frames within a beat;
+ * framesInSeq .... number of frames in the whole sequencer. */
+
+ framesInLoop = (conf::samplerate * (60.0f / bpm)) * beats;
+ framesInBar = framesInLoop / bars;
+ framesInBeat = framesInLoop / beats;
+ framesInSeq = framesInBeat * G_MAX_BEATS;
+
+ updateQuanto();
}
void sendMIDIsync()
{
- /* TODO - only Master (_M) is implemented so far. */
+ /* TODO - only Master (_M) is implemented so far. */
if (conf::midiSync == MIDI_SYNC_CLOCK_M) {
- if (currentFrame % (framesPerBeat/24) == 0)
+ if (currentFrame % (framesInBeat/24) == 0)
kernelMidi::send(MIDI_CLOCK, -1, -1);
- return;
- }
+ return;
+ }
if (conf::midiSync == MIDI_SYNC_MTC_M) {
* range 1-4, if odd send 5-8. */
if (currentFrame % midiTCrate != 0) // no timecode frame passed
- return;
+ return;
/* frame low nibble
* frame high nibble
void recvJackSync()
{
- kernelAudio::JackState jackState = kernelAudio::jackTransportQuery();
-
- if (jackState.running != jackStatePrev.running) {
- if (jackState.running) {
- if (!isRunning())
- glue_startSeq(false); // not from UI
- }
- else {
- if (isRunning())
- glue_stopSeq(false); // not from UI
- }
- }
- if (jackState.bpm != jackStatePrev.bpm)
- if (jackState.bpm > 1.0f) // 0 bpm if Jack does not send that info
- glue_setBpm(jackState.bpm);
-
- if (jackState.frame == 0 && jackState.frame != jackStatePrev.frame)
- glue_rewindSeq(false, false); // not from UI, don't notify jack (avoid loop)
-
- jackStatePrev = jackState;
+ kernelAudio::JackState jackState = kernelAudio::jackTransportQuery();
+
+ if (jackState.running != jackStatePrev.running) {
+ if (jackState.running) {
+ if (!isRunning())
+ glue_startSeq(false); // not from UI
+ }
+ else {
+ if (isRunning())
+ glue_stopSeq(false); // not from UI
+ }
+ }
+ if (jackState.bpm != jackStatePrev.bpm)
+ if (jackState.bpm > 1.0f) // 0 bpm if Jack does not send that info
+ glue_setBpm(jackState.bpm);
+
+ if (jackState.frame == 0 && jackState.frame != jackStatePrev.frame)
+ glue_rewindSeq(false, false); // not from UI, don't notify jack (avoid loop)
+
+ jackStatePrev = jackState;
}
#endif
int getCurrentFrame()
{
- return currentFrame;
+ return currentFrame;
}
-int getTotalFrames()
+int getFramesInLoop()
{
- return totalFrames;
+ return framesInLoop;
}
int getCurrentBeat()
{
- return currentBeat;
+ return currentBeat;
}
int getQuantize()
{
- return quantize;
+ return quantize;
}
float getBpm()
{
- return bpm;
+ return bpm;
}
int getBeats()
{
- return beats;
+ return beats;
}
int getBars()
{
- return bars;
+ return bars;
}
int getQuanto()
{
- return quanto;
+ return quanto;
}
-int getFramesPerBar()
+int getFramesInBar()
{
- return framesPerBar;
+ return framesInBar;
}
-int getFramesPerBeat()
+int getFramesInBeat()
{
- return framesPerBeat;
+ return framesInBeat;
}
-int getFramesInSequencer()
+int getFramesInSeq()
{
- return framesInSequencer;
+ return framesInSeq;
}
#define G_CLOCK_H
-class Conf;
-class KernelMidi;
-class KernelAudio;
-
-
namespace giada {
namespace m {
namespace clock
float getBpm();
int getBeats();
int getBars();
-int getCurrentFrame();
int getCurrentBeat();
-int getFramesPerBar();
-int getFramesPerBeat();
-int getTotalFrames();
-int getFramesInSequencer();
+int getCurrentFrame();
+int getFramesInBar();
+int getFramesInBeat();
+int getFramesInLoop();
+int getFramesInSeq();
int getQuantize();
int getQuanto();
/* incrCurrentFrame
-Increases current frame of a stereo step (+2). */
+Increases current frame of a single step (+1). */
void incrCurrentFrame();
/* -- version --------------------------------------------------------------- */
#define G_APP_NAME "Giada"
-#define G_VERSION_STR "0.14.6"
+#define G_VERSION_STR "0.15.0"
#define G_VERSION_MAJOR 0
-#define G_VERSION_MINOR 14
-#define G_VERSION_PATCH 6
+#define G_VERSION_MINOR 15
+#define G_VERSION_PATCH 0
#define CONF_FILENAME "giada.conf"
#define G_GUI_CHANNEL_H_2 G_GUI_UNIT * 2
#define G_GUI_CHANNEL_H_3 G_GUI_UNIT * 4
#define G_GUI_CHANNEL_H_4 G_GUI_UNIT * 6
+#define G_GUI_ZOOM_FACTOR 2
#define G_COLOR_RED fl_rgb_color(28, 32, 80)
#define G_MAX_BUF_SIZE 4096
#define G_MIN_GUI_WIDTH 816
#define G_MIN_GUI_HEIGHT 510
+#define G_MAX_IO_CHANS 2
#define G_DEFAULT_BUFSIZE 1024
#define G_DEFAULT_DELAYCOMP 0
#define G_DEFAULT_BIT_DEPTH 32 // float
-#define G_DEFAULT_AUDIO_CHANS 2 // stereo for internal processing
#define G_DEFAULT_VOL 1.0f
#define G_DEFAULT_PITCH 1.0f
#define G_DEFAULT_BOOST 1.0f
/* -- channel types --------------------------------------------------------- */
-#define CHANNEL_SAMPLE 0x01
-#define CHANNEL_MIDI 0x02
+#define G_CHANNEL_SAMPLE 0x01
+#define G_CHANNEL_MIDI 0x02
{
kernelAudio::openDevice();
clock::init(conf::samplerate, conf::midiTCfps);
- mixer::init(clock::getTotalFrames(), kernelAudio::getRealBufSize());
+ mixer::init(clock::getFramesInLoop(), kernelAudio::getRealBufSize());
recorder::init();
#ifdef WITH_VST
}
recorder::clearAll();
- for (unsigned i=0; i<mixer::channels.size(); i++) {
- mixer::channels.at(i)->hasActions = false;
- mixer::channels.at(i)->readActions = false;
- //if (mixer::channels.at(i)->type == CHANNEL_SAMPLE)
- // ((SampleChannel*)mixer::channels.at(i))->readActions = false;
- }
gu_log("[init] Recorder cleaned up\n");
#ifdef WITH_VST
void init_prepareParser();
-void init_startGUI(int argc, char **argv);
+void init_startGUI(int argc, char** argv);
void init_prepareKernelAudio();
void init_prepareKernelMIDI();
void init_prepareMidiMap();
RtAudio::StreamParameters outParams;
RtAudio::StreamParameters inParams;
- if (conf::soundDeviceOut == G_DEFAULT_SOUNDDEV_OUT)
- outParams.deviceId = getDefaultOut();
- else
- outParams.deviceId = conf::soundDeviceOut;
-
- outParams.nChannels = 2;
- outParams.firstChannel = conf::channelsOut * 2; // chan 0=0, 1=2, 2=4, ...
+ outParams.deviceId = conf::soundDeviceOut == G_DEFAULT_SOUNDDEV_OUT ? getDefaultOut() : conf::soundDeviceOut;
+ outParams.nChannels = G_MAX_IO_CHANS;
+ outParams.firstChannel = conf::channelsOut * G_MAX_IO_CHANS; // chan 0=0, 1=2, 2=4, ...
- /* inDevice can be disabled */
+ /* inDevice can be disabled. */
if (conf::soundDeviceIn != -1) {
inParams.deviceId = conf::soundDeviceIn;
- inParams.nChannels = 2;
- inParams.firstChannel = conf::channelsIn * 2; // chan 0=0, 1=2, 2=4, ...
+ inParams.nChannels = G_MAX_IO_CHANS;
+ inParams.firstChannel = conf::channelsIn * G_MAX_IO_CHANS; // chan 0=0, 1=2, 2=4, ...
inputEnabled = true;
}
else
/* -------------------------------------------------------------------------- */
-int getDeviceByName(const char *name)
+int getDeviceByName(const char* name)
{
for (unsigned i=0; i<numDevs; i++)
if (name == getDeviceName(i))
unsigned countDevices();
int getTotalFreqs(unsigned dev);
int getFreq(unsigned dev, int i);
-int getDeviceByName(const char *name);
+int getDeviceByName(const char* name);
int getDefaultOut();
int getDefaultIn();
bool hasAPI(int API);
#include "../utils/log.h"
#include "midiChannel.h"
+#include "channelManager.h"
#include "channel.h"
#include "patch.h"
#include "const.h"
#include "clock.h"
#include "conf.h"
#include "mixer.h"
-#ifdef WITH_VST
- #include "pluginHost.h"
-#endif
+#include "pluginHost.h"
#include "kernelMidi.h"
MidiChannel::MidiChannel(int bufferSize)
- : Channel (CHANNEL_MIDI, STATUS_OFF, bufferSize),
+ : Channel (G_CHANNEL_MIDI, STATUS_OFF, bufferSize),
midiOut (false),
midiOutChan(MIDI_CHANS[0])
{
/* -------------------------------------------------------------------------- */
-void MidiChannel::copy(const Channel *_src, pthread_mutex_t *pluginMutex)
+void MidiChannel::copy(const Channel* src_, pthread_mutex_t* pluginMutex)
{
- Channel::copy(_src, pluginMutex);
- MidiChannel *src = (MidiChannel *) _src;
+ Channel::copy(src_, pluginMutex);
+ const MidiChannel* src = static_cast<const MidiChannel*>(src_);
midiOut = src->midiOut;
midiOutChan = src->midiOutChan;
}
/* -------------------------------------------------------------------------- */
-void MidiChannel::quantize(int index, int localFrame) {}
+void MidiChannel::quantize(int index, int localFrame, int globalFrame) {}
/* -------------------------------------------------------------------------- */
-void MidiChannel::parseAction(recorder::action *a, int localFrame,
+void MidiChannel::parseAction(recorder::action* a, int localFrame,
int globalFrame, int quantize, bool mixerIsRunning)
{
if (a->type == G_ACTION_MIDI)
- sendMidi(a, localFrame/2);
+ sendMidi(a, localFrame);
}
/* -------------------------------------------------------------------------- */
-void MidiChannel::process(float *outBuffer, float *inBuffer)
+void MidiChannel::process(giada::m::AudioBuffer& out, const giada::m::AudioBuffer& in)
{
#ifdef WITH_VST
pluginHost::processStack(vChan, pluginHost::CHANNEL, this);
#endif
/* TODO - isn't this useful only if WITH_VST ? */
- for (int j=0; j<bufferSize; j+=2) {
- outBuffer[j] += vChan[j] * volume; // * panLeft; future?
- outBuffer[j+1] += vChan[j+1] * volume; // * panRight; future?
- }
+ for (int i=0; i<out.countFrames(); i++)
+ for (int j=0; j<out.countChannels(); j++)
+ out[i][j] += vChan[i][j] * volume;
}
/* -------------------------------------------------------------------------- */
-void MidiChannel::preview(float *outBuffer)
+void MidiChannel::preview(giada::m::AudioBuffer& out)
{
// No preview for MIDI channels (for now).
}
/* -------------------------------------------------------------------------- */
-int MidiChannel::readPatch(const string &basePath, int i,
- pthread_mutex_t *pluginMutex, int samplerate, int rsmpQuality)
+void MidiChannel::readPatch(const string& basePath, int i)
{
- Channel::readPatch("", i, pluginMutex, samplerate, rsmpQuality);
-
- patch::channel_t *pch = &patch::channels.at(i);
-
- midiOut = pch->midiOut;
- midiOutChan = pch->midiOutChan;
-
- return G_RES_OK;
+ Channel::readPatch("", i);
+ channelManager::readPatch(this, i);
}
/* -------------------------------------------------------------------------- */
-void MidiChannel::sendMidi(recorder::action *a, int localFrame)
+void MidiChannel::sendMidi(recorder::action* a, int localFrame)
{
if (status & (STATUS_PLAY | STATUS_ENDING) && !mute) {
if (midiOut)
/* -------------------------------------------------------------------------- */
-int MidiChannel::writePatch(int i, bool isProject)
+void MidiChannel::writePatch(int i, bool isProject)
{
- int pchIndex = Channel::writePatch(i, isProject);
- patch::channel_t *pch = &patch::channels.at(pchIndex);
-
- pch->midiOut = midiOut;
- pch->midiOutChan = midiOutChan;
-
- return 0;
+ Channel::writePatch(i, isProject);
+ channelManager::writePatch(this, isProject, i);
}
MidiChannel(int bufferSize);
~MidiChannel();
- bool midiOut; // enable midi output
- uint8_t midiOutChan; // midi output channel
-
void copy(const Channel* src, pthread_mutex_t* pluginMutex) override;
void clear() override;
- void process(float* outBuffer, float *inBuffer) override;
- void preview(float* outBuffer) override;
+ void process(giada::m::AudioBuffer& out, const giada::m::AudioBuffer& in) override;
+ void preview(giada::m::AudioBuffer& out) override;
void start(int frame, bool doQuantize, int quantize, bool mixerIsRunning,
bool forceStart, bool isUserGenerated) override;
void kill(int frame) override;
void rewind() override;
void setMute(bool internal) override;
void unsetMute(bool internal) override;
- int readPatch(const std::string& basePath, int i, pthread_mutex_t* pluginMutex,
- int samplerate, int rsmpQuality) override;
- int writePatch(int i, bool isProject) override;
- void quantize(int index, int localFrame) override;
+ void readPatch(const std::string& basePath, int i) override;
+ void writePatch(int i, bool isProject) override;
+ void quantize(int index, int localFrame, int globalFrame) override;
void onZero(int frame, bool recsStopOnChanHalt) override;
void onBar(int frame) override;
void parseAction(giada::m::recorder::action* a, int localFrame, int globalFrame,
void addVstMidiEvent(uint32_t msg, int localFrame);
#endif
+
+ bool midiOut; // enable midi output
+ uint8_t midiOutChan; // midi output channel
};
#include "channel.h"
#include "sampleChannel.h"
#include "midiChannel.h"
+#include "audioBuffer.h"
#include "mixer.h"
};
+AudioBuffer vChanInput; // virtual channel for recording
+AudioBuffer vChanInToOut; // virtual channel in->out bridge (hear what you're playin)
+
+int tickTracker, tockTracker = 0;
+bool tickPlay, tockPlay = false; // 1 = play, 0 = stop
+
+/* inputTracker
+Sample position while recording. */
+
+int inputTracker = 0;
+
+
+/* -------------------------------------------------------------------------- */
+
+
+bool isChannelAudible(Channel* ch)
+{
+ return !hasSolos || (hasSolos && ch->solo);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+/* computePeak */
+
+void computePeak(const AudioBuffer& buf, float& peak, unsigned frame)
+{
+ for (int i=0; i<buf.countChannels(); i++)
+ if (buf[frame][i] > peak)
+ peak = buf[frame][i];
+}
+
+
/* -------------------------------------------------------------------------- */
/* lineInRec
Records from line in. */
-void lineInRec(float* inBuf, unsigned frame)
+void lineInRec(const AudioBuffer& inBuf, unsigned frame)
{
if (!mh::hasArmedSampleChannels() || !kernelAudio::isInputEnabled() || !recording)
return;
- /* Delay comp: wait until waitRec reaches delayComp. WaitRec
- * returns to 0 in mixerHandler, as soon as the recording ends */
+ /* Delay comp: wait until waitRec reaches delayComp. WaitRec returns to 0 in
+ mixerHandler, as soon as the recording ends. */
if (waitRec < conf::delayComp) {
- waitRec += 2;
+ waitRec++;
return;
}
- vChanInput[inputTracker] += inBuf[frame] * inVol;
- vChanInput[inputTracker+1] += inBuf[frame+1] * inVol;
- inputTracker += 2;
- if (inputTracker >= clock::getTotalFrames())
+ for (int i=0; i<vChanInput.countChannels(); i++)
+ vChanInput[inputTracker][i] += inBuf[frame][i] * inVol; // adding: overdub!
+
+ inputTracker++;
+ if (inputTracker >= clock::getFramesInLoop())
inputTracker = 0;
}
/* ProcessLineIn
Computes line in peaks, plus handles "hear what you're playin'" thing. */
-void processLineIn(float* inBuf, unsigned frame)
+void processLineIn(const AudioBuffer& inBuf, unsigned frame)
{
if (!kernelAudio::isInputEnabled())
return;
- /* input peak calculation (left chan only so far). */
+ computePeak(inBuf, peakIn, frame);
- if (inBuf[frame] * inVol > peakIn)
- peakIn = inBuf[frame] * inVol;
+ /* "hear what you're playing" - process, copy and paste the input buffer onto
+ the output buffer. */
- /* "hear what you're playing" - process, copy and paste the input buffer
- * onto the output buffer */
-
- if (inToOut) {
- vChanInToOut[frame] = inBuf[frame] * inVol;
- vChanInToOut[frame+1] = inBuf[frame+1] * inVol;
- }
+ if (inToOut)
+ for (int i=0; i<vChanInToOut.countChannels(); i++)
+ vChanInToOut[frame][i] = inBuf[frame][i] * inVol;
}
/* clearAllBuffers
Cleans up every buffer, both in Mixer and in channels. */
-void clearAllBuffers(float* outBuf, unsigned bufferSize)
+void clearAllBuffers(AudioBuffer& outBuf)
{
- memset(outBuf, 0, sizeof(float) * bufferSize); // out
- memset(vChanInToOut, 0, sizeof(float) * bufferSize); // inToOut vChan
+ outBuf.clear();
+ vChanInToOut.clear();
pthread_mutex_lock(&mutex_chans);
for (Channel* channel : channels)
{
pthread_mutex_lock(&mutex_recs);
for (unsigned i=0; i<recorder::frames.size(); i++) {
- if (recorder::frames.at(i) == clock::getCurrentFrame()) {
- for (unsigned j=0; j<recorder::global.at(i).size(); j++) {
- int index = recorder::global.at(i).at(j)->chan;
- Channel *ch = mh::getChannelByIndex(index);
- ch->parseAction(recorder::global.at(i).at(j), frame,
- clock::getCurrentFrame(), clock::getQuantize(), clock::isRunning());
- }
- break;
+ if (recorder::frames.at(i) != clock::getCurrentFrame())
+ continue;
+ for (recorder::action* action : recorder::global.at(i)) {
+ Channel* ch = mh::getChannelByIndex(action->chan);
+ ch->parseAction(action, frame, clock::getCurrentFrame(),
+ clock::getQuantize(), clock::isRunning());
}
+ break;
}
pthread_mutex_unlock(&mutex_recs);
}
if (clock::getQuantize() == 0 || !clock::quantoHasPassed())
return;
+
if (rewindWait) {
rewindWait = false;
rewind();
}
+
pthread_mutex_lock(&mutex_chans);
for (unsigned i=0; i<channels.size(); i++)
- channels.at(i)->quantize(i, frame);
+ channels.at(i)->quantize(i, frame, clock::getCurrentFrame());
pthread_mutex_unlock(&mutex_chans);
}
/* sumChannels
Sums channels, i.e. lets them add sample frames to their virtual channels.
-This is required for CHANNEL_SAMPLE only */
+This is required for G_CHANNEL_SAMPLE only */
void sumChannels(unsigned frame)
{
pthread_mutex_lock(&mutex_chans);
- for (unsigned k=0; k<channels.size(); k++) {
- if (channels.at(k)->type == CHANNEL_SAMPLE)
- static_cast<SampleChannel*>(channels.at(k))->sum(frame, clock::isRunning());
- }
+ for (Channel* ch : channels)
+ if (ch->type == G_CHANNEL_SAMPLE)
+ static_cast<SampleChannel*>(ch)->sum(frame, clock::isRunning());
pthread_mutex_unlock(&mutex_chans);
}
/* renderMetronome
Generates metronome when needed and pastes it to the output buffer. */
-void renderMetronome(float* outBuf, unsigned frame)
+void renderMetronome(AudioBuffer& outBuf, unsigned frame)
{
if (tockPlay) {
- outBuf[frame] += tock[tockTracker];
- outBuf[frame+1] += tock[tockTracker];
+ for (int i=0; i<outBuf.countChannels(); i++)
+ outBuf[frame][i] += tock[tockTracker];
tockTracker++;
if (tockTracker >= TICKSIZE-1) {
tockPlay = false;
}
}
if (tickPlay) {
- outBuf[frame] += tick[tickTracker];
- outBuf[frame+1] += tick[tickTracker];
+ for (int i=0; i<outBuf.countChannels(); i++)
+ outBuf[frame][i] += tick[tickTracker];
tickTracker++;
if (tickTracker >= TICKSIZE-1) {
tickPlay = false;
Final processing stage. Take each channel and process it (i.e. copy its
content to the output buffer). Process plugins too, if any. */
-void renderIO(float* outBuf, float* inBuf)
+void renderIO(AudioBuffer& outBuf, const AudioBuffer& inBuf)
{
pthread_mutex_lock(&mutex_chans);
- for (Channel* channel : channels) {
- channel->process(outBuf, inBuf);
- channel->preview(outBuf);
+ for (Channel* ch : channels) {
+ if (isChannelAudible(ch))
+ ch->process(outBuf, inBuf);
+ ch->preview(outBuf);
}
pthread_mutex_unlock(&mutex_chans);
/* limitOutput
Applies a very dumb hard limiter. */
-void limitOutput(float* outBuf, unsigned frame)
+void limitOutput(AudioBuffer& outBuf, unsigned frame)
{
- if (outBuf[frame] > 1.0f)
- outBuf[frame] = 1.0f;
- else
- if (outBuf[frame] < -1.0f)
- outBuf[frame] = -1.0f;
-
- if (outBuf[frame+1] > 1.0f)
- outBuf[frame+1] = 1.0f;
- else
- if (outBuf[frame+1] < -1.0f)
- outBuf[frame+1] = -1.0f;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-/* computePeak */
-
-void computePeak(float* outBuf, unsigned frame)
-{
- /* TODO it takes into account only left channel so far! */
- if (outBuf[frame] > peakOut)
- peakOut = outBuf[frame];
+ for (int i=0; i<outBuf.countChannels(); i++)
+ if (outBuf[frame][i] > 1.0f)
+ outBuf[frame][i] = 1.0f;
+ else if (outBuf[frame][i] < -1.0f)
+ outBuf[frame][i] = -1.0f;
}
Last touches after the output has been rendered: apply inToOut if any, apply
output volume. */
-void finalizeOutput(float* outBuf, unsigned frame)
+void finalizeOutput(AudioBuffer& outBuf, unsigned frame)
{
- /* merge vChanInToOut, if enabled */
+ /* Merge vChanInToOut, if enabled. */
- if (inToOut) {
- outBuf[frame] += vChanInToOut[frame];
- outBuf[frame+1] += vChanInToOut[frame+1];
- }
- outBuf[frame] *= outVol;
- outBuf[frame+1] *= outVol;
+ if (inToOut)
+ outBuf.copyFrame(frame, vChanInToOut[frame]);
+
+ for (int i=0; i<outBuf.countChannels(); i++)
+ outBuf[frame][i] *= outVol;
}
tickPlay = true;
pthread_mutex_lock(&mutex_chans);
- for (unsigned k=0; k<channels.size(); k++)
- channels.at(k)->onBar(frame);
+ for (Channel* ch : channels)
+ ch->onBar(frame);
pthread_mutex_unlock(&mutex_chans);
}
if (!clock::isOnFirstBeat())
return;
pthread_mutex_lock(&mutex_chans);
- for (unsigned k=0; k<channels.size(); k++)
- channels.at(k)->onZero(frame, conf::recsStopOnChanHalt);
+ for (Channel* ch : channels)
+ ch->onZero(frame, conf::recsStopOnChanHalt);
pthread_mutex_unlock(&mutex_chans);
}
std::vector<Channel*> channels;
-bool recording = false; // is recording something?
+bool recording = false;
bool ready = true;
-float *vChanInput = nullptr; // virtual channel for recording
-float *vChanInToOut = nullptr; // virtual channel in->out bridge (hear what you're playin)
float outVol = G_DEFAULT_OUT_VOL;
float inVol = G_DEFAULT_IN_VOL;
float peakOut = 0.0f;
float peakIn = 0.0f;
bool metronome = false;
-int waitRec = 0; // delayComp guard
-bool docross = false; // crossfade guard
-bool rewindWait = false; // rewind guard, if quantized
-
-int tickTracker, tockTracker = 0;
-bool tickPlay, tockPlay = false; // 1 = play, 0 = stop
-
-/* inputTracker
- * position of the sample in the input side (recording) */
-
-int inputTracker = 0;
-
-/* inToOut
- * copy, process and paste the input into the output, in order to
- * obtain a "hear what you're playing" feature. */
-
-bool inToOut = false;
+int waitRec = 0;
+bool rewindWait = false;
+bool hasSolos = false;
+bool inToOut = false;
pthread_mutex_t mutex_recs;
pthread_mutex_t mutex_chans;
/* -------------------------------------------------------------------------- */
-void init(int framesInSeq, int audioBufferSize)
+void init(int framesInSeq, int framesInBuffer)
{
/* Allocate virtual input channels. vChanInput has variable size: it depends
on how many frames there are in sequencer. */
-
- allocVirtualInput(framesInSeq);
-
- if (vChanInToOut != nullptr)
- delete[] vChanInToOut;
- vChanInToOut = new (std::nothrow) float[audioBufferSize * 2];
- if (!vChanInToOut) {
+ if (!allocVirtualInput(framesInSeq)) {
+ gu_log("[Mixer::init] vChanInput alloc error!\n");
+ return;
+ }
+ if (!vChanInToOut.alloc(framesInBuffer, G_MAX_IO_CHANS)) {
gu_log("[Mixer::init] vChanInToOut alloc error!\n");
return;
}
+ gu_log("[Mixer::init] buffers ready - framesInSeq=%d, framesInBuffer=%d\n",
+ framesInSeq, framesInBuffer);
+
+ hasSolos = false;
+
pthread_mutex_init(&mutex_recs, nullptr);
pthread_mutex_init(&mutex_chans, nullptr);
pthread_mutex_init(&mutex_plugins, nullptr);
/* -------------------------------------------------------------------------- */
-void allocVirtualInput(int frames)
+bool allocVirtualInput(int frames)
{
- if (vChanInput != nullptr)
- delete[] vChanInput;
- vChanInput = new (std::nothrow) float[frames];
- if (!vChanInput)
- gu_log("[Mixer::allocVirtualInput] vChanInput realloc error!\n");
- gu_log("[Mixer::allocVirtualInput] vChanInput ready, %d frames\n", frames);
+ return vChanInput.alloc(frames, G_MAX_IO_CHANS);
}
/* -------------------------------------------------------------------------- */
-int masterPlay(void* _outBuf, void* _inBuf, unsigned bufferSize,
+int masterPlay(void* outBuf, void* inBuf, unsigned bufferSize,
double streamTime, RtAudioStreamStatus status, void* userData)
{
if (!ready)
clock::recvJackSync();
#endif
- float* outBuf = (float*) _outBuf;
- float* inBuf = kernelAudio::isInputEnabled() ? (float*) _inBuf : nullptr;
- bufferSize *= 2; // stereo
- peakOut = 0.0f; // reset peak calculator
- peakIn = 0.0f; // reset peak calculator
+ AudioBuffer out, in;
+ out.setData((float*) outBuf, bufferSize, G_MAX_IO_CHANS);
+ if (kernelAudio::isInputEnabled())
+ in.setData((float*) inBuf, bufferSize, G_MAX_IO_CHANS);
- clearAllBuffers(outBuf, bufferSize);
+ peakOut = 0.0f; // reset peak calculator
+ peakIn = 0.0f; // reset peak calculator
- for (unsigned j=0; j<bufferSize; j+=2) {
- processLineIn(inBuf, j);
+ clearAllBuffers(out);
+
+ for (unsigned j=0; j<bufferSize; j++) {
+ processLineIn(in, j);
if (clock::isRunning()) {
- lineInRec(inBuf, j);
+ lineInRec(in, j);
doQuantize(j);
testBar(j);
testFirstBeat(j);
sumChannels(j);
}
- renderIO(outBuf, inBuf);
-
- /* post processing */
+ renderIO(out, in);
- for (unsigned j=0; j<bufferSize; j+=2) {
- finalizeOutput(outBuf, j);
+ /* Post processing. */
+ for (unsigned j=0; j<bufferSize; j++) {
+ finalizeOutput(out, j);
if (conf::limitOutput)
- limitOutput(outBuf, j);
- computePeak(outBuf, j);
- renderMetronome(outBuf, j);
+ limitOutput(out, j);
+ computePeak(out, peakOut, j);
+ renderMetronome(out, j);
}
+ /* Unset data in buffers. If you don't do this, buffers go out of scope and
+ destroy memory allocated by RtAudio ---> havoc. */
+ out.setData(nullptr, 0, 0);
+ in.setData(nullptr, 0, 0);
+
return 0;
}
/* -------------------------------------------------------------------------- */
-int close()
+void close()
{
clock::stop();
while (channels.size() > 0)
mh::deleteChannel(channels.at(0));
-
- if (vChanInput != nullptr) {
- delete[] vChanInput;
- vChanInput = nullptr;
- }
- if (vChanInToOut != nullptr) {
- delete[] vChanInToOut;
- vChanInToOut = nullptr;
- }
- return 1;
}
bool isSilent()
{
- for (unsigned i=0; i<channels.size(); i++)
- if (channels.at(i)->status == STATUS_PLAY)
+ for (const Channel* ch : channels)
+ if (ch->status == STATUS_PLAY)
return false;
return true;
}
/* -------------------------------------------------------------------------- */
-void mergeVirtualInput()
+void startInputRec()
{
- assert(vChanInput != nullptr);
+ /* Start inputTracker from the current frame, not the beginning. */
+ recording = true;
+ inputTracker = clock::getCurrentFrame();
+}
+
+/* -------------------------------------------------------------------------- */
+
+void mergeVirtualInput()
+{
for (Channel* ch : channels) {
- if (ch->type == CHANNEL_MIDI)
+ if (ch->type == G_CHANNEL_MIDI)
continue;
SampleChannel* sch = static_cast<SampleChannel*>(ch);
- if (sch->isArmed())
- memcpy(sch->wave->getData(), vChanInput, clock::getTotalFrames() * sizeof(float));
+ if (sch->armed)
+ sch->wave->copyData(vChanInput[0], vChanInput.countFrames());
}
- memset(vChanInput, 0, clock::getTotalFrames() * sizeof(float)); // clear vchan
+ vChanInput.clear();
}
namespace m {
namespace mixer
{
-void init(int framesInSeq, int audioBufferSize);
+void init(int framesInSeq, int framesInBuffer);
/* allocVirtualInput
Allocates new memory for the virtual input channel. Call this whenever you
shrink or resize the sequencer. */
-void allocVirtualInput(int frames);
+bool allocVirtualInput(int frames);
-int close();
+void close();
/* masterPlay
Core method (callback) */
-int masterPlay(void *outBuf, void *inBuf, unsigned bufferSize, double streamTime,
- RtAudioStreamStatus status, void *userData);
+int masterPlay(void* outBuf, void* inBuf, unsigned bufferSize, double streamTime,
+ RtAudioStreamStatus status, void* userData);
/* isSilent
Is mixer silent? */
void rewind();
+/* startInputRec
+Starts input recording on frame clock::getCurrentFrame(). */
+
+void startInputRec();
+
/* mergeVirtualInput
Copies the virtual channel input in the channels designed for input recording.
Called by mixerHandler on stopInputRec(). */
extern bool recording; // is recording something?
extern bool ready;
-extern float *vChanInput; // virtual channel for recording
-extern float *vChanInToOut; // virtual channel in->out bridge (hear what you're playin)
-extern int frameSize;
extern float outVol;
extern float inVol;
extern float peakOut;
extern float peakIn;
-extern bool metronome;
-extern int waitRec; // delayComp guard
-extern bool docross; // crossfade guard
-extern bool rewindWait; // rewind guard, if quantized
-
-extern int tickTracker, tockTracker;
-extern bool tickPlay, tockPlay; // 1 = play, 0 = stop
-
-/* inputTracker
- * position of the sample in the input side (recording) */
-
-extern int inputTracker;
+extern bool metronome;
+extern int waitRec; // delayComp guard
+extern bool rewindWait; // rewind guard, if quantized
+extern bool hasSolos; // more than 0 channels soloed
/* inToOut
- * copy, process and paste the input into the output, in order to
- * obtain a "hear what you're playing" feature. */
+Copy, process and paste the input into the output, in order to obtain a "hear
+what you're playing" feature. */
extern bool inToOut;
#include <vector>
+#include <algorithm>
#include "../utils/fs.h"
#include "../utils/string.h"
#include "../utils/log.h"
#include "midiChannel.h"
#include "wave.h"
#include "waveManager.h"
+#include "channelManager.h"
#include "mixerHandler.h"
#endif
-/* ------------------------------------------------------------------------ */
+/* -------------------------------------------------------------------------- */
int getNewChanIndex()
bool uniqueSamplePath(const SampleChannel* skip, const string& path)
{
for (const Channel* ch : mixer::channels) {
- if (skip == ch || ch->type != CHANNEL_SAMPLE) // skip itself and MIDI channels
+ if (skip == ch || ch->type != G_CHANNEL_SAMPLE) // skip itself and MIDI channels
continue;
const SampleChannel* sch = static_cast<const SampleChannel*>(ch);
if (sch->wave != nullptr && path == sch->wave->getPath())
Channel* addChannel(int type)
{
- Channel* ch;
- int bufferSize = kernelAudio::getRealBufSize() * 2;
-
- if (type == CHANNEL_SAMPLE)
- ch = new SampleChannel(bufferSize, conf::inputMonitorDefaultOn);
- else
- ch = new MidiChannel(bufferSize);
-
- if (!ch->allocBuffers()) {
- delete ch;
+ Channel* ch = nullptr;
+ channelManager::create(type, kernelAudio::getRealBufSize(),
+ conf::inputMonitorDefaultOn, &ch);
+ if (ch == nullptr)
return nullptr;
- }
while (true) {
if (pthread_mutex_trylock(&mixer::mutex_chans) != 0)
/* -------------------------------------------------------------------------- */
-int deleteChannel(Channel* ch)
+void deleteChannel(Channel* target)
{
- int index = -1;
- for (unsigned i=0; i<mixer::channels.size(); i++) {
- if (mixer::channels.at(i) == ch) {
- index = i;
- break;
- }
- }
- if (index == -1) {
- gu_log("[deleteChannel] unable to find channel %d for deletion!\n", ch->index);
- return 0;
- }
-
while (true) {
if (pthread_mutex_trylock(&mixer::mutex_chans) != 0)
continue;
- mixer::channels.erase(mixer::channels.begin() + index);
- delete ch;
+ auto it = std::find(mixer::channels.begin(), mixer::channels.end(), target);
+ if (it != mixer::channels.end())
+ mixer::channels.erase(it);
pthread_mutex_unlock(&mixer::mutex_chans);
- return 1;
+ return;
}
}
Channel* getChannelByIndex(int index)
{
- for (unsigned i=0; i<mixer::channels.size(); i++)
- if (mixer::channels.at(i)->index == index)
- return mixer::channels.at(i);
+ for (Channel* ch : mixer::channels)
+ if (ch->index == index)
+ return ch;
gu_log("[getChannelByIndex] channel at index %d not found!\n", index);
return nullptr;
}
bool hasLogicalSamples()
{
- for (unsigned i=0; i<mixer::channels.size(); i++) {
- if (mixer::channels.at(i)->type != CHANNEL_SAMPLE)
+ for (const Channel* ch : mixer::channels) {
+ if (ch->type != G_CHANNEL_SAMPLE)
continue;
- SampleChannel *ch = static_cast<SampleChannel*>(mixer::channels.at(i));
- if (ch->wave && ch->wave->isLogical())
+ const SampleChannel* sch = static_cast<const SampleChannel*>(ch);
+ if (sch->wave != nullptr && sch->wave->isLogical())
return true;
}
return false;
bool hasEditedSamples()
{
- for (unsigned i=0; i<mixer::channels.size(); i++)
- {
- if (mixer::channels.at(i)->type != CHANNEL_SAMPLE)
+ for (const Channel* ch : mixer::channels) {
+ if (ch->type != G_CHANNEL_SAMPLE)
continue;
- SampleChannel *ch = static_cast<SampleChannel*>(mixer::channels.at(i));
- if (ch->wave && ch->wave->isEdited())
+ const SampleChannel* sch = static_cast<const SampleChannel*>(ch);
+ if (sch->wave != nullptr && sch->wave->isEdited())
return true;
}
return false;
void stopSequencer()
{
clock::stop();
- for (unsigned i=0; i<mixer::channels.size(); i++)
- mixer::channels.at(i)->stopBySeq(conf::chansStopOnSeqHalt);
+ for (Channel* ch : mixer::channels)
+ ch->stopBySeq(conf::chansStopOnSeqHalt);
}
/* -------------------------------------------------------------------------- */
-bool uniqueSolo(Channel* ch)
+void updateSoloCount()
{
- int solos = 0;
- for (unsigned i=0; i<mixer::channels.size(); i++) {
- Channel *ch = mixer::channels.at(i);
- if (ch->solo) solos++;
- if (solos > 1) return false;
- }
- return true;
+ for (Channel* ch : mixer::channels)
+ if (ch->solo) {
+ mixer::hasSolos = true;
+ return;
+ }
+ mixer::hasSolos = false;
}
very likely). */
mixer::rewind();
- mixer::allocVirtualInput(clock::getTotalFrames());
+ mixer::allocVirtualInput(clock::getFramesInLoop());
mixer::ready = true;
}
{
int channelsReady = 0;
- for (Channel* channel : mixer::channels) {
+ for (Channel* ch : mixer::channels) {
- if (!channel->canInputRec())
+ if (!ch->canInputRec())
continue;
- SampleChannel* ch = static_cast<SampleChannel*>(channel);
+ SampleChannel* sch = static_cast<SampleChannel*>(ch);
/* Allocate empty sample for the current channel. */
- Wave* wave = nullptr;
- int result = waveManager::createEmpty(clock::getTotalFrames(),
- conf::samplerate, string("TAKE-" + gu_iToString(patch::lastTakeId)), &wave);
- if (result != G_RES_OK) {
- gu_log("[startInputRec] unable to allocate new Wave in chan %d!\n",
- ch->index);
+ Wave* wave = nullptr;
+ string name = string("TAKE-" + gu_iToString(patch::lastTakeId++)); // Increase lastTakeId
+
+ int result = waveManager::createEmpty(clock::getFramesInLoop(), G_MAX_IO_CHANS,
+ conf::samplerate, name + ".wav", &wave);
+ if (result != G_RES_OK)
continue;
- }
- ch->pushWave(wave);
- ch->setName("TAKE-" + gu_iToString(patch::lastTakeId++)); // Increase lastTakeId
+ sch->pushWave(wave);
+ sch->name = name;
channelsReady++;
gu_log("[startInputRec] start input recs using chan %d with size %d "
- "frame=%d\n", ch->index, clock::getTotalFrames(), mixer::inputTracker);
+ "on frame=%d\n", sch->index, clock::getFramesInLoop(), clock::getCurrentFrame());
}
- if (channelsReady > 0) {
- mixer::recording = true;
- /* start to write from the currentFrame, not the beginning */
- /** FIXME: this should be done before wave allocation */
- mixer::inputTracker = clock::getCurrentFrame();
- return true;
- }
- return false;
+ /** FIXME: mixer::startInputRec() should be called before wave allocation */
+ /** FIXME: mixer::startInputRec() should be called before wave allocation */
+ /** FIXME: mixer::startInputRec() should be called before wave allocation */
+ if (channelsReady == 0)
+ return false;
+ mixer::startInputRec();
+ return true;
}
bool hasArmedSampleChannels()
{
- for (unsigned i=0; i<mixer::channels.size(); i++) {
- Channel *ch = mixer::channels.at(i);
- if (ch->type == CHANNEL_SAMPLE && ch->isArmed())
+ for (const Channel* ch : mixer::channels)
+ if (ch->type == G_CHANNEL_SAMPLE && ch->armed)
return true;
- }
return false;
}
/* deleteChannel
Completely removes a channel from the stack. */
-int deleteChannel(Channel* ch);
+void deleteChannel(Channel* ch);
/* getChannelByIndex
Returns channel with given index 'i'. */
void rewindSequencer();
-/* uniqueSolo
- * true if ch is the only solo'd channel in mixer. */
+/* updateSoloCount
+Updates the number of solo-ed channels in mixer. */
-bool uniqueSolo(Channel* ch);
+void updateSoloCount();
/* loadPatch
Loads a path or a project (if isProject) into Mixer. If isProject, path must
void sanitize()
{
- bpm = bpm < G_MIN_BPM || bpm > G_MAX_BPM ? G_DEFAULT_BPM : bpm;
- bars = bars <= 0 || bars > G_MAX_BARS ? G_DEFAULT_BARS : bars;
- beats = beats <= 0 || beats > G_MAX_BEATS ? G_DEFAULT_BEATS : beats;
- quantize = quantize < 0 || quantize > G_MAX_QUANTIZE ? G_DEFAULT_QUANTIZE : quantize;
- masterVolIn = masterVolIn < 0.0f || masterVolIn > 1.0f ? G_DEFAULT_VOL : masterVolIn;
- masterVolOut = masterVolOut < 0.0f || masterVolOut > 1.0f ? G_DEFAULT_VOL : masterVolOut;
- samplerate = samplerate <= 0 ? G_DEFAULT_SAMPLERATE : samplerate;
-
- for (unsigned i=0; i<columns.size(); i++) {
- column_t* col = &columns.at(i);
- col->index = col->index < 0 ? 0 : col->index;
- col->width = col->width < G_MIN_COLUMN_WIDTH ? G_MIN_COLUMN_WIDTH : col->width;
- }
-
- for (unsigned i=0; i<channels.size(); i++) {
- channel_t* ch = &channels.at(i);
- ch->size = ch->size < G_GUI_CHANNEL_H_1 || ch->size > G_GUI_CHANNEL_H_4 ? G_GUI_CHANNEL_H_1 : ch->size;
- ch->volume = ch->volume < 0.0f || ch->volume > 1.0f ? G_DEFAULT_VOL : ch->volume;
- ch->pan = ch->pan < 0.0f || ch->pan > 1.0f ? 1.0f : ch->pan;
- ch->boost = ch->boost < 1.0f ? G_DEFAULT_BOOST : ch->boost;
- ch->pitch = ch->pitch < 0.1f || ch->pitch > G_MAX_PITCH ? G_DEFAULT_PITCH : ch->pitch;
- }
+ bpm = bpm < G_MIN_BPM || bpm > G_MAX_BPM ? G_DEFAULT_BPM : bpm;
+ bars = bars <= 0 || bars > G_MAX_BARS ? G_DEFAULT_BARS : bars;
+ beats = beats <= 0 || beats > G_MAX_BEATS ? G_DEFAULT_BEATS : beats;
+ quantize = quantize < 0 || quantize > G_MAX_QUANTIZE ? G_DEFAULT_QUANTIZE : quantize;
+ masterVolIn = masterVolIn < 0.0f || masterVolIn > 1.0f ? G_DEFAULT_VOL : masterVolIn;
+ masterVolOut = masterVolOut < 0.0f || masterVolOut > 1.0f ? G_DEFAULT_VOL : masterVolOut;
+ samplerate = samplerate <= 0 ? G_DEFAULT_SAMPLERATE : samplerate;
+
+ for (unsigned i=0; i<columns.size(); i++) {
+ column_t* col = &columns.at(i);
+ col->index = col->index < 0 ? 0 : col->index;
+ col->width = col->width < G_MIN_COLUMN_WIDTH ? G_MIN_COLUMN_WIDTH : col->width;
+ }
+
+ for (unsigned i=0; i<channels.size(); i++) {
+ channel_t* ch = &channels.at(i);
+ ch->size = ch->size < G_GUI_CHANNEL_H_1 || ch->size > G_GUI_CHANNEL_H_4 ? G_GUI_CHANNEL_H_1 : ch->size;
+ ch->volume = ch->volume < 0.0f || ch->volume > 1.0f ? G_DEFAULT_VOL : ch->volume;
+ ch->pan = ch->pan < 0.0f || ch->pan > 1.0f ? 1.0f : ch->pan;
+ ch->boost = ch->boost < 1.0f ? G_DEFAULT_BOOST : ch->boost;
+ ch->pitch = ch->pitch < 0.1f || ch->pitch > G_MAX_PITCH ? G_DEFAULT_PITCH : ch->pitch;
+ }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+/* modernize
+Makes sure an older patch is compatible with the current version. */
+
+void modernize()
+{
+ /* Starting from 0.15.0 actions are recorded on frames, not samples. */
+ if (versionMajor <= 0 && versionMinor < 15) {
+ for (channel_t& ch : channels)
+ for (action_t& a : ch.actions)
+ a.frame /= 2;
+ }
}
int setInvalid(json_t* jRoot)
{
- json_decref(jRoot);
- return PATCH_INVALID;
+ json_decref(jRoot);
+ return PATCH_INVALID;
}
bool readCommons(json_t* jContainer)
{
- if (!storager::setString(jContainer, PATCH_KEY_HEADER, header)) return 0;
- if (!storager::setString(jContainer, PATCH_KEY_VERSION, version)) return 0;
- if (!storager::setInt (jContainer, PATCH_KEY_VERSION_MAJOR, versionMajor)) return 0;
- if (!storager::setInt (jContainer, PATCH_KEY_VERSION_MINOR, versionMinor)) return 0;
- if (!storager::setInt (jContainer, PATCH_KEY_VERSION_PATCH, versionPatch)) return 0;
- if (!storager::setString(jContainer, PATCH_KEY_NAME, name)) return 0;
- if (!storager::setFloat (jContainer, PATCH_KEY_BPM, bpm)) return 0;
- if (!storager::setInt (jContainer, PATCH_KEY_BARS, bars)) return 0;
- if (!storager::setInt (jContainer, PATCH_KEY_BEATS, beats)) return 0;
- if (!storager::setInt (jContainer, PATCH_KEY_QUANTIZE, quantize)) return 0;
- if (!storager::setFloat (jContainer, PATCH_KEY_MASTER_VOL_IN, masterVolIn)) return 0;
- if (!storager::setFloat (jContainer, PATCH_KEY_MASTER_VOL_OUT, masterVolOut)) return 0;
- if (!storager::setInt (jContainer, PATCH_KEY_METRONOME, metronome)) return 0;
- if (!storager::setInt (jContainer, PATCH_KEY_LAST_TAKE_ID, lastTakeId)) return 0;
- if (!storager::setInt (jContainer, PATCH_KEY_SAMPLERATE, samplerate)) return 0;
- return 1;
+ if (!storager::setString(jContainer, PATCH_KEY_HEADER, header)) return 0;
+ if (!storager::setString(jContainer, PATCH_KEY_VERSION, version)) return 0;
+ if (!storager::setInt (jContainer, PATCH_KEY_VERSION_MAJOR, versionMajor)) return 0;
+ if (!storager::setInt (jContainer, PATCH_KEY_VERSION_MINOR, versionMinor)) return 0;
+ if (!storager::setInt (jContainer, PATCH_KEY_VERSION_PATCH, versionPatch)) return 0;
+ if (!storager::setString(jContainer, PATCH_KEY_NAME, name)) return 0;
+ if (!storager::setFloat (jContainer, PATCH_KEY_BPM, bpm)) return 0;
+ if (!storager::setInt (jContainer, PATCH_KEY_BARS, bars)) return 0;
+ if (!storager::setInt (jContainer, PATCH_KEY_BEATS, beats)) return 0;
+ if (!storager::setInt (jContainer, PATCH_KEY_QUANTIZE, quantize)) return 0;
+ if (!storager::setFloat (jContainer, PATCH_KEY_MASTER_VOL_IN, masterVolIn)) return 0;
+ if (!storager::setFloat (jContainer, PATCH_KEY_MASTER_VOL_OUT, masterVolOut)) return 0;
+ if (!storager::setInt (jContainer, PATCH_KEY_METRONOME, metronome)) return 0;
+ if (!storager::setInt (jContainer, PATCH_KEY_LAST_TAKE_ID, lastTakeId)) return 0;
+ if (!storager::setInt (jContainer, PATCH_KEY_SAMPLERATE, samplerate)) return 0;
+ return 1;
}
bool readPlugins(json_t* jContainer, vector<plugin_t>* container, const char* key)
{
- json_t* jPlugins = json_object_get(jContainer, key);
- if (!storager::checkArray(jPlugins, key))
- return 0;
+ json_t* jPlugins = json_object_get(jContainer, key);
+ if (!storager::checkArray(jPlugins, key))
+ return 0;
- size_t pluginIndex;
- json_t* jPlugin;
- json_array_foreach(jPlugins, pluginIndex, jPlugin) {
+ size_t pluginIndex;
+ json_t* jPlugin;
+ json_array_foreach(jPlugins, pluginIndex, jPlugin) {
- if (!storager::checkObject(jPlugin, "")) // TODO pass pluginIndex as string
- return 0;
+ if (!storager::checkObject(jPlugin, "")) // TODO pass pluginIndex as string
+ return 0;
- plugin_t plugin;
- if (!storager::setString(jPlugin, PATCH_KEY_PLUGIN_PATH, plugin.path)) return 0;
- if (!storager::setBool (jPlugin, PATCH_KEY_PLUGIN_BYPASS, plugin.bypass)) return 0;
+ plugin_t plugin;
+ if (!storager::setString(jPlugin, PATCH_KEY_PLUGIN_PATH, plugin.path)) return 0;
+ if (!storager::setBool (jPlugin, PATCH_KEY_PLUGIN_BYPASS, plugin.bypass)) return 0;
- /* read plugin params */
+ /* read plugin params */
- json_t* jParams = json_object_get(jPlugin, PATCH_KEY_PLUGIN_PARAMS);
- if (!storager::checkArray(jParams, PATCH_KEY_PLUGIN_PARAMS)) return 0;
+ json_t* jParams = json_object_get(jPlugin, PATCH_KEY_PLUGIN_PARAMS);
+ if (!storager::checkArray(jParams, PATCH_KEY_PLUGIN_PARAMS)) return 0;
- size_t paramIndex;
- json_t* jParam;
- json_array_foreach(jParams, paramIndex, jParam)
- plugin.params.push_back(json_real_value(jParam));
+ size_t paramIndex;
+ json_t* jParam;
+ json_array_foreach(jParams, paramIndex, jParam)
+ plugin.params.push_back(json_real_value(jParam));
- /* read midiIn params (midi learning on plugins' parameters) */
+ /* read midiIn params (midi learning on plugins' parameters) */
- json_t* jMidiInParams = json_object_get(jPlugin, PATCH_KEY_PLUGIN_MIDI_IN_PARAMS);
- if (!storager::checkArray(jMidiInParams, PATCH_KEY_PLUGIN_MIDI_IN_PARAMS)) return 0;
+ json_t* jMidiInParams = json_object_get(jPlugin, PATCH_KEY_PLUGIN_MIDI_IN_PARAMS);
+ if (!storager::checkArray(jMidiInParams, PATCH_KEY_PLUGIN_MIDI_IN_PARAMS)) return 0;
- size_t midiInParamIndex;
- json_t* jMidiInParam;
- json_array_foreach(jMidiInParams, midiInParamIndex, jMidiInParam)
- plugin.midiInParams.push_back(json_integer_value(jMidiInParam));
+ size_t midiInParamIndex;
+ json_t* jMidiInParam;
+ json_array_foreach(jMidiInParams, midiInParamIndex, jMidiInParam)
+ plugin.midiInParams.push_back(json_integer_value(jMidiInParam));
- container->push_back(plugin);
- }
- return 1;
+ container->push_back(plugin);
+ }
+ return 1;
}
#endif
bool readActions(json_t* jContainer, channel_t* channel)
{
- json_t* jActions = json_object_get(jContainer, PATCH_KEY_CHANNEL_ACTIONS);
- if (!storager::checkArray(jActions, PATCH_KEY_CHANNEL_ACTIONS))
- return 0;
-
- size_t actionIndex;
- json_t* jAction;
- json_array_foreach(jActions, actionIndex, jAction) {
-
- if (!storager::checkObject(jAction, "")) // TODO pass actionIndex as string
- return 0;
-
- action_t action;
- if (!storager::setInt (jAction, PATCH_KEY_ACTION_TYPE, action.type)) return 0;
- if (!storager::setInt (jAction, PATCH_KEY_ACTION_FRAME, action.frame)) return 0;
- if (!storager::setFloat (jAction, PATCH_KEY_ACTION_F_VALUE, action.fValue)) return 0;
- if (!storager::setUint32(jAction, PATCH_KEY_ACTION_I_VALUE, action.iValue)) return 0;
- channel->actions.push_back(action);
- }
- return 1;
+ json_t* jActions = json_object_get(jContainer, PATCH_KEY_CHANNEL_ACTIONS);
+ if (!storager::checkArray(jActions, PATCH_KEY_CHANNEL_ACTIONS))
+ return 0;
+
+ size_t actionIndex;
+ json_t* jAction;
+ json_array_foreach(jActions, actionIndex, jAction) {
+
+ if (!storager::checkObject(jAction, "")) // TODO pass actionIndex as string
+ return 0;
+
+ action_t action;
+ if (!storager::setInt (jAction, PATCH_KEY_ACTION_TYPE, action.type)) return 0;
+ if (!storager::setInt (jAction, PATCH_KEY_ACTION_FRAME, action.frame)) return 0;
+ if (!storager::setFloat (jAction, PATCH_KEY_ACTION_F_VALUE, action.fValue)) return 0;
+ if (!storager::setUint32(jAction, PATCH_KEY_ACTION_I_VALUE, action.iValue)) return 0;
+ channel->actions.push_back(action);
+ }
+ return 1;
}
bool readChannels(json_t* jContainer)
{
- json_t* jChannels = json_object_get(jContainer, PATCH_KEY_CHANNELS);
- if (!storager::checkArray(jChannels, PATCH_KEY_CHANNELS))
- return 0;
-
- size_t channelIndex;
- json_t* jChannel;
- json_array_foreach(jChannels, channelIndex, jChannel) {
-
- string channelIndexStr = "channel " + gu_iToString(channelIndex);
- if (!storager::checkObject(jChannel, channelIndexStr.c_str()))
- return 0;
-
- channel_t channel;
-
- if (!storager::setInt (jChannel, PATCH_KEY_CHANNEL_TYPE, channel.type)) return 0;
- if (!storager::setInt (jChannel, PATCH_KEY_CHANNEL_INDEX, channel.index)) return 0;
- if (!storager::setInt (jChannel, PATCH_KEY_CHANNEL_SIZE, channel.size)) return 0;
- if (!storager::setString(jChannel, PATCH_KEY_CHANNEL_NAME, channel.name)) return 0;
- if (!storager::setInt (jChannel, PATCH_KEY_CHANNEL_COLUMN, channel.column)) return 0;
- if (!storager::setInt (jChannel, PATCH_KEY_CHANNEL_MUTE, channel.mute)) return 0;
- if (!storager::setInt (jChannel, PATCH_KEY_CHANNEL_MUTE_S, channel.mute_s)) return 0;
- if (!storager::setInt (jChannel, PATCH_KEY_CHANNEL_SOLO, channel.solo)) return 0;
- if (!storager::setFloat (jChannel, PATCH_KEY_CHANNEL_VOLUME, channel.volume)) return 0;
- if (!storager::setFloat (jChannel, PATCH_KEY_CHANNEL_PAN, channel.pan)) return 0;
- if (!storager::setBool (jChannel, PATCH_KEY_CHANNEL_MIDI_IN, channel.midiIn)) return 0;
- if (!storager::setBool (jChannel, PATCH_KEY_CHANNEL_MIDI_IN_VELO_AS_VOL, channel.midiInVeloAsVol)) return 0;
- if (!storager::setUint32(jChannel, PATCH_KEY_CHANNEL_MIDI_IN_KEYPRESS, channel.midiInKeyPress)) return 0;
- if (!storager::setUint32(jChannel, PATCH_KEY_CHANNEL_MIDI_IN_KEYREL, channel.midiInKeyRel)) return 0;
- if (!storager::setUint32(jChannel, PATCH_KEY_CHANNEL_MIDI_IN_KILL, channel.midiInKill)) return 0;
- if (!storager::setUint32(jChannel, PATCH_KEY_CHANNEL_MIDI_IN_ARM, channel.midiInArm)) return 0;
- if (!storager::setUint32(jChannel, PATCH_KEY_CHANNEL_MIDI_IN_VOLUME, channel.midiInVolume)) return 0;
- if (!storager::setUint32(jChannel, PATCH_KEY_CHANNEL_MIDI_IN_MUTE, channel.midiInMute)) return 0;
- if (!storager::setUint32(jChannel, PATCH_KEY_CHANNEL_MIDI_IN_SOLO, channel.midiInSolo)) return 0;
- if (!storager::setInt (jChannel, PATCH_KEY_CHANNEL_MIDI_IN_FILTER, channel.midiInFilter)) return 0;
- if (!storager::setBool (jChannel, PATCH_KEY_CHANNEL_MIDI_OUT_L, channel.midiOutL)) return 0;
- if (!storager::setUint32(jChannel, PATCH_KEY_CHANNEL_MIDI_OUT_L_PLAYING, channel.midiOutLplaying)) return 0;
- if (!storager::setUint32(jChannel, PATCH_KEY_CHANNEL_MIDI_OUT_L_MUTE, channel.midiOutLmute)) return 0;
- if (!storager::setUint32(jChannel, PATCH_KEY_CHANNEL_MIDI_OUT_L_SOLO, channel.midiOutLsolo)) return 0;
- if (!storager::setString(jChannel, PATCH_KEY_CHANNEL_SAMPLE_PATH, channel.samplePath)) return 0;
- if (!storager::setInt (jChannel, PATCH_KEY_CHANNEL_KEY, channel.key)) return 0;
- if (!storager::setInt (jChannel, PATCH_KEY_CHANNEL_MODE, channel.mode)) return 0;
- if (!storager::setInt (jChannel, PATCH_KEY_CHANNEL_BEGIN, channel.begin)) return 0;
- if (!storager::setInt (jChannel, PATCH_KEY_CHANNEL_END, channel.end)) return 0;
- if (!storager::setFloat (jChannel, PATCH_KEY_CHANNEL_BOOST, channel.boost)) return 0;
- if (!storager::setInt (jChannel, PATCH_KEY_CHANNEL_REC_ACTIVE, channel.recActive)) return 0;
- if (!storager::setFloat (jChannel, PATCH_KEY_CHANNEL_PITCH, channel.pitch)) return 0;
- if (!storager::setBool (jChannel, PATCH_KEY_CHANNEL_INPUT_MONITOR, channel.inputMonitor)) return 0;
- if (!storager::setUint32(jChannel, PATCH_KEY_CHANNEL_MIDI_IN_READ_ACTIONS, channel.midiInReadActions)) return 0;
- if (!storager::setUint32(jChannel, PATCH_KEY_CHANNEL_MIDI_IN_PITCH, channel.midiInPitch)) return 0;
- if (!storager::setUint32(jChannel, PATCH_KEY_CHANNEL_MIDI_OUT, channel.midiOut)) return 0;
- if (!storager::setUint32(jChannel, PATCH_KEY_CHANNEL_MIDI_OUT_CHAN, channel.midiOutChan)) return 0;
- if (!storager::setBool (jChannel, PATCH_KEY_CHANNEL_ARMED, channel.armed)) return 0;
-
- readActions(jChannel, &channel);
+ json_t* jChannels = json_object_get(jContainer, PATCH_KEY_CHANNELS);
+ if (!storager::checkArray(jChannels, PATCH_KEY_CHANNELS))
+ return 0;
+
+ size_t channelIndex;
+ json_t* jChannel;
+ json_array_foreach(jChannels, channelIndex, jChannel) {
+
+ string channelIndexStr = "channel " + gu_iToString(channelIndex);
+ if (!storager::checkObject(jChannel, channelIndexStr.c_str()))
+ return 0;
+
+ channel_t channel;
+
+ if (!storager::setInt (jChannel, PATCH_KEY_CHANNEL_TYPE, channel.type)) return 0;
+ if (!storager::setInt (jChannel, PATCH_KEY_CHANNEL_INDEX, channel.index)) return 0;
+ if (!storager::setInt (jChannel, PATCH_KEY_CHANNEL_SIZE, channel.size)) return 0;
+ if (!storager::setString(jChannel, PATCH_KEY_CHANNEL_NAME, channel.name)) return 0;
+ if (!storager::setInt (jChannel, PATCH_KEY_CHANNEL_COLUMN, channel.column)) return 0;
+ if (!storager::setInt (jChannel, PATCH_KEY_CHANNEL_MUTE, channel.mute)) return 0;
+ if (!storager::setInt (jChannel, PATCH_KEY_CHANNEL_MUTE_S, channel.mute_s)) return 0;
+ if (!storager::setInt (jChannel, PATCH_KEY_CHANNEL_SOLO, channel.solo)) return 0;
+ if (!storager::setFloat (jChannel, PATCH_KEY_CHANNEL_VOLUME, channel.volume)) return 0;
+ if (!storager::setFloat (jChannel, PATCH_KEY_CHANNEL_PAN, channel.pan)) return 0;
+ if (!storager::setBool (jChannel, PATCH_KEY_CHANNEL_MIDI_IN, channel.midiIn)) return 0;
+ if (!storager::setBool (jChannel, PATCH_KEY_CHANNEL_MIDI_IN_VELO_AS_VOL, channel.midiInVeloAsVol)) return 0;
+ if (!storager::setUint32(jChannel, PATCH_KEY_CHANNEL_MIDI_IN_KEYPRESS, channel.midiInKeyPress)) return 0;
+ if (!storager::setUint32(jChannel, PATCH_KEY_CHANNEL_MIDI_IN_KEYREL, channel.midiInKeyRel)) return 0;
+ if (!storager::setUint32(jChannel, PATCH_KEY_CHANNEL_MIDI_IN_KILL, channel.midiInKill)) return 0;
+ if (!storager::setUint32(jChannel, PATCH_KEY_CHANNEL_MIDI_IN_ARM, channel.midiInArm)) return 0;
+ if (!storager::setUint32(jChannel, PATCH_KEY_CHANNEL_MIDI_IN_VOLUME, channel.midiInVolume)) return 0;
+ if (!storager::setUint32(jChannel, PATCH_KEY_CHANNEL_MIDI_IN_MUTE, channel.midiInMute)) return 0;
+ if (!storager::setUint32(jChannel, PATCH_KEY_CHANNEL_MIDI_IN_SOLO, channel.midiInSolo)) return 0;
+ if (!storager::setInt (jChannel, PATCH_KEY_CHANNEL_MIDI_IN_FILTER, channel.midiInFilter)) return 0;
+ if (!storager::setBool (jChannel, PATCH_KEY_CHANNEL_MIDI_OUT_L, channel.midiOutL)) return 0;
+ if (!storager::setUint32(jChannel, PATCH_KEY_CHANNEL_MIDI_OUT_L_PLAYING, channel.midiOutLplaying)) return 0;
+ if (!storager::setUint32(jChannel, PATCH_KEY_CHANNEL_MIDI_OUT_L_MUTE, channel.midiOutLmute)) return 0;
+ if (!storager::setUint32(jChannel, PATCH_KEY_CHANNEL_MIDI_OUT_L_SOLO, channel.midiOutLsolo)) return 0;
+ if (!storager::setString(jChannel, PATCH_KEY_CHANNEL_SAMPLE_PATH, channel.samplePath)) return 0;
+ if (!storager::setInt (jChannel, PATCH_KEY_CHANNEL_KEY, channel.key)) return 0;
+ if (!storager::setInt (jChannel, PATCH_KEY_CHANNEL_MODE, channel.mode)) return 0;
+ if (!storager::setInt (jChannel, PATCH_KEY_CHANNEL_BEGIN, channel.begin)) return 0;
+ if (!storager::setInt (jChannel, PATCH_KEY_CHANNEL_END, channel.end)) return 0;
+ if (!storager::setFloat (jChannel, PATCH_KEY_CHANNEL_BOOST, channel.boost)) return 0;
+ if (!storager::setInt (jChannel, PATCH_KEY_CHANNEL_REC_ACTIVE, channel.recActive)) return 0;
+ if (!storager::setFloat (jChannel, PATCH_KEY_CHANNEL_PITCH, channel.pitch)) return 0;
+ if (!storager::setBool (jChannel, PATCH_KEY_CHANNEL_INPUT_MONITOR, channel.inputMonitor)) return 0;
+ if (!storager::setUint32(jChannel, PATCH_KEY_CHANNEL_MIDI_IN_READ_ACTIONS, channel.midiInReadActions)) return 0;
+ if (!storager::setUint32(jChannel, PATCH_KEY_CHANNEL_MIDI_IN_PITCH, channel.midiInPitch)) return 0;
+ if (!storager::setUint32(jChannel, PATCH_KEY_CHANNEL_MIDI_OUT, channel.midiOut)) return 0;
+ if (!storager::setUint32(jChannel, PATCH_KEY_CHANNEL_MIDI_OUT_CHAN, channel.midiOutChan)) return 0;
+ if (!storager::setBool (jChannel, PATCH_KEY_CHANNEL_ARMED, channel.armed)) return 0;
+
+ readActions(jChannel, &channel);
#ifdef WITH_VST
- readPlugins(jChannel, &channel.plugins, PATCH_KEY_CHANNEL_PLUGINS);
+ readPlugins(jChannel, &channel.plugins, PATCH_KEY_CHANNEL_PLUGINS);
#endif
- channels.push_back(channel);
- }
- return 1;
+ channels.push_back(channel);
+ }
+ return 1;
}
bool readColumns(json_t* jContainer)
{
- json_t* jColumns = json_object_get(jContainer, PATCH_KEY_COLUMNS);
- if (!storager::checkArray(jColumns, PATCH_KEY_COLUMNS))
- return 0;
+ json_t* jColumns = json_object_get(jContainer, PATCH_KEY_COLUMNS);
+ if (!storager::checkArray(jColumns, PATCH_KEY_COLUMNS))
+ return 0;
- size_t columnIndex;
- json_t* jColumn;
- json_array_foreach(jColumns, columnIndex, jColumn) {
+ size_t columnIndex;
+ json_t* jColumn;
+ json_array_foreach(jColumns, columnIndex, jColumn) {
- string columnIndexStr = "column " + gu_iToString(columnIndex);
- if (!storager::checkObject(jColumn, columnIndexStr.c_str()))
- return 0;
+ string columnIndexStr = "column " + gu_iToString(columnIndex);
+ if (!storager::checkObject(jColumn, columnIndexStr.c_str()))
+ return 0;
- column_t column;
- if (!storager::setInt(jColumn, PATCH_KEY_COLUMN_INDEX, column.index)) return 0;
- if (!storager::setInt(jColumn, PATCH_KEY_COLUMN_WIDTH, column.width)) return 0;
+ column_t column;
+ if (!storager::setInt(jColumn, PATCH_KEY_COLUMN_INDEX, column.index)) return 0;
+ if (!storager::setInt(jColumn, PATCH_KEY_COLUMN_WIDTH, column.width)) return 0;
- columns.push_back(column);
- }
- return 1;
+ columns.push_back(column);
+ }
+ return 1;
}
void writePlugins(json_t* jContainer, vector<plugin_t>* plugins, const char* key)
{
- json_t* jPlugins = json_array();
- for (unsigned j=0; j<plugins->size(); j++) {
- json_t* jPlugin = json_object();
- plugin_t plugin = plugins->at(j);
- json_object_set_new(jPlugin, PATCH_KEY_PLUGIN_PATH, json_string(plugin.path.c_str()));
- json_object_set_new(jPlugin, PATCH_KEY_PLUGIN_BYPASS, json_boolean(plugin.bypass));
- json_array_append_new(jPlugins, jPlugin);
-
- /* plugin params */
-
- json_t* jPluginParams = json_array();
- for (unsigned z=0; z<plugin.params.size(); z++)
- json_array_append_new(jPluginParams, json_real(plugin.params.at(z)));
- json_object_set_new(jPlugin, PATCH_KEY_PLUGIN_PARAMS, jPluginParams);
-
- /* midiIn params (midi learning on plugins' parameters) */
-
- json_t* jPluginMidiInParams = json_array();
- for (unsigned z=0; z<plugin.midiInParams.size(); z++)
- json_array_append_new(jPluginMidiInParams, json_integer(plugin.midiInParams.at(z)));
- json_object_set_new(jPlugin, PATCH_KEY_PLUGIN_MIDI_IN_PARAMS, jPluginMidiInParams);
- }
- json_object_set_new(jContainer, key, jPlugins);
+ json_t* jPlugins = json_array();
+ for (unsigned j=0; j<plugins->size(); j++) {
+ json_t* jPlugin = json_object();
+ plugin_t plugin = plugins->at(j);
+ json_object_set_new(jPlugin, PATCH_KEY_PLUGIN_PATH, json_string(plugin.path.c_str()));
+ json_object_set_new(jPlugin, PATCH_KEY_PLUGIN_BYPASS, json_boolean(plugin.bypass));
+ json_array_append_new(jPlugins, jPlugin);
+
+ /* plugin params */
+
+ json_t* jPluginParams = json_array();
+ for (unsigned z=0; z<plugin.params.size(); z++)
+ json_array_append_new(jPluginParams, json_real(plugin.params.at(z)));
+ json_object_set_new(jPlugin, PATCH_KEY_PLUGIN_PARAMS, jPluginParams);
+
+ /* midiIn params (midi learning on plugins' parameters) */
+
+ json_t* jPluginMidiInParams = json_array();
+ for (unsigned z=0; z<plugin.midiInParams.size(); z++)
+ json_array_append_new(jPluginMidiInParams, json_integer(plugin.midiInParams.at(z)));
+ json_object_set_new(jPlugin, PATCH_KEY_PLUGIN_MIDI_IN_PARAMS, jPluginMidiInParams);
+ }
+ json_object_set_new(jContainer, key, jPlugins);
}
#endif
void writeColumns(json_t* jContainer, vector<column_t>* columns)
{
- json_t* jColumns = json_array();
- for (unsigned i=0; i<columns->size(); i++) {
- json_t* jColumn = json_object();
- column_t column = columns->at(i);
- json_object_set_new(jColumn, PATCH_KEY_COLUMN_INDEX, json_integer(column.index));
- json_object_set_new(jColumn, PATCH_KEY_COLUMN_WIDTH, json_integer(column.width));
- json_array_append_new(jColumns, jColumn);
- }
- json_object_set_new(jContainer, PATCH_KEY_COLUMNS, jColumns);
+ json_t* jColumns = json_array();
+ for (unsigned i=0; i<columns->size(); i++) {
+ json_t* jColumn = json_object();
+ column_t column = columns->at(i);
+ json_object_set_new(jColumn, PATCH_KEY_COLUMN_INDEX, json_integer(column.index));
+ json_object_set_new(jColumn, PATCH_KEY_COLUMN_WIDTH, json_integer(column.width));
+ json_array_append_new(jColumns, jColumn);
+ }
+ json_object_set_new(jContainer, PATCH_KEY_COLUMNS, jColumns);
}
void writeActions(json_t*jContainer, vector<action_t>*actions)
{
- json_t* jActions = json_array();
- for (unsigned k=0; k<actions->size(); k++) {
- json_t* jAction = json_object();
- action_t action = actions->at(k);
- json_object_set_new(jAction, PATCH_KEY_ACTION_TYPE, json_integer(action.type));
- json_object_set_new(jAction, PATCH_KEY_ACTION_FRAME, json_integer(action.frame));
- json_object_set_new(jAction, PATCH_KEY_ACTION_F_VALUE, json_real(action.fValue));
- json_object_set_new(jAction, PATCH_KEY_ACTION_I_VALUE, json_integer(action.iValue));
- json_array_append_new(jActions, jAction);
- }
- json_object_set_new(jContainer, PATCH_KEY_CHANNEL_ACTIONS, jActions);
+ json_t* jActions = json_array();
+ for (unsigned k=0; k<actions->size(); k++) {
+ json_t* jAction = json_object();
+ action_t action = actions->at(k);
+ json_object_set_new(jAction, PATCH_KEY_ACTION_TYPE, json_integer(action.type));
+ json_object_set_new(jAction, PATCH_KEY_ACTION_FRAME, json_integer(action.frame));
+ json_object_set_new(jAction, PATCH_KEY_ACTION_F_VALUE, json_real(action.fValue));
+ json_object_set_new(jAction, PATCH_KEY_ACTION_I_VALUE, json_integer(action.iValue));
+ json_array_append_new(jActions, jAction);
+ }
+ json_object_set_new(jContainer, PATCH_KEY_CHANNEL_ACTIONS, jActions);
}
void writeCommons(json_t* jContainer)
{
- json_object_set_new(jContainer, PATCH_KEY_HEADER, json_string(header.c_str()));
- json_object_set_new(jContainer, PATCH_KEY_VERSION, json_string(version.c_str()));
- json_object_set_new(jContainer, PATCH_KEY_VERSION_MAJOR, json_integer(versionMajor));
- json_object_set_new(jContainer, PATCH_KEY_VERSION_MINOR, json_integer(versionMinor));
- json_object_set_new(jContainer, PATCH_KEY_VERSION_PATCH, json_integer(versionPatch));
- json_object_set_new(jContainer, PATCH_KEY_NAME, json_string(name.c_str()));
- json_object_set_new(jContainer, PATCH_KEY_BPM, json_real(bpm));
- json_object_set_new(jContainer, PATCH_KEY_BARS, json_integer(bars));
- json_object_set_new(jContainer, PATCH_KEY_BEATS, json_integer(beats));
- json_object_set_new(jContainer, PATCH_KEY_QUANTIZE, json_integer(quantize));
- json_object_set_new(jContainer, PATCH_KEY_MASTER_VOL_IN, json_real(masterVolIn));
- json_object_set_new(jContainer, PATCH_KEY_MASTER_VOL_OUT, json_real(masterVolOut));
+ json_object_set_new(jContainer, PATCH_KEY_HEADER, json_string(header.c_str()));
+ json_object_set_new(jContainer, PATCH_KEY_VERSION, json_string(version.c_str()));
+ json_object_set_new(jContainer, PATCH_KEY_VERSION_MAJOR, json_integer(versionMajor));
+ json_object_set_new(jContainer, PATCH_KEY_VERSION_MINOR, json_integer(versionMinor));
+ json_object_set_new(jContainer, PATCH_KEY_VERSION_PATCH, json_integer(versionPatch));
+ json_object_set_new(jContainer, PATCH_KEY_NAME, json_string(name.c_str()));
+ json_object_set_new(jContainer, PATCH_KEY_BPM, json_real(bpm));
+ json_object_set_new(jContainer, PATCH_KEY_BARS, json_integer(bars));
+ json_object_set_new(jContainer, PATCH_KEY_BEATS, json_integer(beats));
+ json_object_set_new(jContainer, PATCH_KEY_QUANTIZE, json_integer(quantize));
+ json_object_set_new(jContainer, PATCH_KEY_MASTER_VOL_IN, json_real(masterVolIn));
+ json_object_set_new(jContainer, PATCH_KEY_MASTER_VOL_OUT, json_real(masterVolOut));
json_object_set_new(jContainer, PATCH_KEY_METRONOME, json_integer(metronome));
json_object_set_new(jContainer, PATCH_KEY_LAST_TAKE_ID, json_integer(lastTakeId));
json_object_set_new(jContainer, PATCH_KEY_SAMPLERATE, json_integer(samplerate));
void writeChannels(json_t* jContainer, vector<channel_t>* channels)
{
- json_t* jChannels = json_array();
- for (unsigned i=0; i<channels->size(); i++) {
- json_t* jChannel = json_object();
- channel_t channel = channels->at(i);
- json_object_set_new(jChannel, PATCH_KEY_CHANNEL_TYPE, json_integer(channel.type));
- json_object_set_new(jChannel, PATCH_KEY_CHANNEL_INDEX, json_integer(channel.index));
- json_object_set_new(jChannel, PATCH_KEY_CHANNEL_SIZE, json_integer(channel.size));
- json_object_set_new(jChannel, PATCH_KEY_CHANNEL_NAME, json_string(channel.name.c_str()));
- json_object_set_new(jChannel, PATCH_KEY_CHANNEL_COLUMN, json_integer(channel.column));
- json_object_set_new(jChannel, PATCH_KEY_CHANNEL_MUTE, json_integer(channel.mute));
- json_object_set_new(jChannel, PATCH_KEY_CHANNEL_MUTE_S, json_integer(channel.mute_s));
- json_object_set_new(jChannel, PATCH_KEY_CHANNEL_SOLO, json_integer(channel.solo));
- json_object_set_new(jChannel, PATCH_KEY_CHANNEL_VOLUME, json_real(channel.volume));
- json_object_set_new(jChannel, PATCH_KEY_CHANNEL_PAN, json_real(channel.pan));
- json_object_set_new(jChannel, PATCH_KEY_CHANNEL_MIDI_IN, json_boolean(channel.midiIn));
- json_object_set_new(jChannel, PATCH_KEY_CHANNEL_MIDI_IN_VELO_AS_VOL, json_boolean(channel.midiInVeloAsVol));
- json_object_set_new(jChannel, PATCH_KEY_CHANNEL_MIDI_IN_KEYPRESS, json_integer(channel.midiInKeyPress));
- json_object_set_new(jChannel, PATCH_KEY_CHANNEL_MIDI_IN_KEYREL, json_integer(channel.midiInKeyRel));
- json_object_set_new(jChannel, PATCH_KEY_CHANNEL_MIDI_IN_KILL, json_integer(channel.midiInKill));
- json_object_set_new(jChannel, PATCH_KEY_CHANNEL_MIDI_IN_ARM, json_integer(channel.midiInArm));
- json_object_set_new(jChannel, PATCH_KEY_CHANNEL_MIDI_IN_VOLUME, json_integer(channel.midiInVolume));
- json_object_set_new(jChannel, PATCH_KEY_CHANNEL_MIDI_IN_MUTE, json_integer(channel.midiInMute));
- json_object_set_new(jChannel, PATCH_KEY_CHANNEL_MIDI_IN_FILTER, json_integer(channel.midiInFilter));
- json_object_set_new(jChannel, PATCH_KEY_CHANNEL_MIDI_IN_SOLO, json_integer(channel.midiInSolo));
- json_object_set_new(jChannel, PATCH_KEY_CHANNEL_MIDI_OUT_L, json_boolean(channel.midiOutL));
- json_object_set_new(jChannel, PATCH_KEY_CHANNEL_MIDI_OUT_L_PLAYING, json_integer(channel.midiOutLplaying));
- json_object_set_new(jChannel, PATCH_KEY_CHANNEL_MIDI_OUT_L_MUTE, json_integer(channel.midiOutLmute));
- json_object_set_new(jChannel, PATCH_KEY_CHANNEL_MIDI_OUT_L_SOLO, json_integer(channel.midiOutLsolo));
- json_object_set_new(jChannel, PATCH_KEY_CHANNEL_SAMPLE_PATH, json_string(channel.samplePath.c_str()));
- json_object_set_new(jChannel, PATCH_KEY_CHANNEL_KEY, json_integer(channel.key));
- json_object_set_new(jChannel, PATCH_KEY_CHANNEL_MODE, json_integer(channel.mode));
- json_object_set_new(jChannel, PATCH_KEY_CHANNEL_BEGIN, json_integer(channel.begin));
- json_object_set_new(jChannel, PATCH_KEY_CHANNEL_END, json_integer(channel.end));
- json_object_set_new(jChannel, PATCH_KEY_CHANNEL_BOOST, json_real(channel.boost));
- json_object_set_new(jChannel, PATCH_KEY_CHANNEL_REC_ACTIVE, json_integer(channel.recActive));
- json_object_set_new(jChannel, PATCH_KEY_CHANNEL_PITCH, json_real(channel.pitch));
- json_object_set_new(jChannel, PATCH_KEY_CHANNEL_INPUT_MONITOR, json_boolean(channel.inputMonitor));
- json_object_set_new(jChannel, PATCH_KEY_CHANNEL_MIDI_IN_READ_ACTIONS, json_integer(channel.midiInReadActions));
- json_object_set_new(jChannel, PATCH_KEY_CHANNEL_MIDI_IN_PITCH, json_integer(channel.midiInPitch));
- json_object_set_new(jChannel, PATCH_KEY_CHANNEL_MIDI_OUT, json_integer(channel.midiOut));
- json_object_set_new(jChannel, PATCH_KEY_CHANNEL_MIDI_OUT_CHAN, json_integer(channel.midiOutChan));
- json_object_set_new(jChannel, PATCH_KEY_CHANNEL_ARMED, json_boolean(channel.armed));
- json_array_append_new(jChannels, jChannel);
-
- writeActions(jChannel, &channel.actions);
+ json_t* jChannels = json_array();
+ for (unsigned i=0; i<channels->size(); i++) {
+ json_t* jChannel = json_object();
+ channel_t channel = channels->at(i);
+ json_object_set_new(jChannel, PATCH_KEY_CHANNEL_TYPE, json_integer(channel.type));
+ json_object_set_new(jChannel, PATCH_KEY_CHANNEL_INDEX, json_integer(channel.index));
+ json_object_set_new(jChannel, PATCH_KEY_CHANNEL_SIZE, json_integer(channel.size));
+ json_object_set_new(jChannel, PATCH_KEY_CHANNEL_NAME, json_string(channel.name.c_str()));
+ json_object_set_new(jChannel, PATCH_KEY_CHANNEL_COLUMN, json_integer(channel.column));
+ json_object_set_new(jChannel, PATCH_KEY_CHANNEL_MUTE, json_integer(channel.mute));
+ json_object_set_new(jChannel, PATCH_KEY_CHANNEL_MUTE_S, json_integer(channel.mute_s));
+ json_object_set_new(jChannel, PATCH_KEY_CHANNEL_SOLO, json_integer(channel.solo));
+ json_object_set_new(jChannel, PATCH_KEY_CHANNEL_VOLUME, json_real(channel.volume));
+ json_object_set_new(jChannel, PATCH_KEY_CHANNEL_PAN, json_real(channel.pan));
+ json_object_set_new(jChannel, PATCH_KEY_CHANNEL_MIDI_IN, json_boolean(channel.midiIn));
+ json_object_set_new(jChannel, PATCH_KEY_CHANNEL_MIDI_IN_VELO_AS_VOL, json_boolean(channel.midiInVeloAsVol));
+ json_object_set_new(jChannel, PATCH_KEY_CHANNEL_MIDI_IN_KEYPRESS, json_integer(channel.midiInKeyPress));
+ json_object_set_new(jChannel, PATCH_KEY_CHANNEL_MIDI_IN_KEYREL, json_integer(channel.midiInKeyRel));
+ json_object_set_new(jChannel, PATCH_KEY_CHANNEL_MIDI_IN_KILL, json_integer(channel.midiInKill));
+ json_object_set_new(jChannel, PATCH_KEY_CHANNEL_MIDI_IN_ARM, json_integer(channel.midiInArm));
+ json_object_set_new(jChannel, PATCH_KEY_CHANNEL_MIDI_IN_VOLUME, json_integer(channel.midiInVolume));
+ json_object_set_new(jChannel, PATCH_KEY_CHANNEL_MIDI_IN_MUTE, json_integer(channel.midiInMute));
+ json_object_set_new(jChannel, PATCH_KEY_CHANNEL_MIDI_IN_FILTER, json_integer(channel.midiInFilter));
+ json_object_set_new(jChannel, PATCH_KEY_CHANNEL_MIDI_IN_SOLO, json_integer(channel.midiInSolo));
+ json_object_set_new(jChannel, PATCH_KEY_CHANNEL_MIDI_OUT_L, json_boolean(channel.midiOutL));
+ json_object_set_new(jChannel, PATCH_KEY_CHANNEL_MIDI_OUT_L_PLAYING, json_integer(channel.midiOutLplaying));
+ json_object_set_new(jChannel, PATCH_KEY_CHANNEL_MIDI_OUT_L_MUTE, json_integer(channel.midiOutLmute));
+ json_object_set_new(jChannel, PATCH_KEY_CHANNEL_MIDI_OUT_L_SOLO, json_integer(channel.midiOutLsolo));
+ json_object_set_new(jChannel, PATCH_KEY_CHANNEL_SAMPLE_PATH, json_string(channel.samplePath.c_str()));
+ json_object_set_new(jChannel, PATCH_KEY_CHANNEL_KEY, json_integer(channel.key));
+ json_object_set_new(jChannel, PATCH_KEY_CHANNEL_MODE, json_integer(channel.mode));
+ json_object_set_new(jChannel, PATCH_KEY_CHANNEL_BEGIN, json_integer(channel.begin));
+ json_object_set_new(jChannel, PATCH_KEY_CHANNEL_END, json_integer(channel.end));
+ json_object_set_new(jChannel, PATCH_KEY_CHANNEL_BOOST, json_real(channel.boost));
+ json_object_set_new(jChannel, PATCH_KEY_CHANNEL_REC_ACTIVE, json_integer(channel.recActive));
+ json_object_set_new(jChannel, PATCH_KEY_CHANNEL_PITCH, json_real(channel.pitch));
+ json_object_set_new(jChannel, PATCH_KEY_CHANNEL_INPUT_MONITOR, json_boolean(channel.inputMonitor));
+ json_object_set_new(jChannel, PATCH_KEY_CHANNEL_MIDI_IN_READ_ACTIONS, json_integer(channel.midiInReadActions));
+ json_object_set_new(jChannel, PATCH_KEY_CHANNEL_MIDI_IN_PITCH, json_integer(channel.midiInPitch));
+ json_object_set_new(jChannel, PATCH_KEY_CHANNEL_MIDI_OUT, json_integer(channel.midiOut));
+ json_object_set_new(jChannel, PATCH_KEY_CHANNEL_MIDI_OUT_CHAN, json_integer(channel.midiOutChan));
+ json_object_set_new(jChannel, PATCH_KEY_CHANNEL_ARMED, json_boolean(channel.armed));
+ json_array_append_new(jChannels, jChannel);
+
+ writeActions(jChannel, &channel.actions);
#ifdef WITH_VST
- writePlugins(jChannel, &channel.plugins, PATCH_KEY_CHANNEL_PLUGINS);
+ writePlugins(jChannel, &channel.plugins, PATCH_KEY_CHANNEL_PLUGINS);
#endif
- }
- json_object_set_new(jContainer, PATCH_KEY_CHANNELS, jChannels);
+ }
+ json_object_set_new(jContainer, PATCH_KEY_CHANNELS, jChannels);
}
}; // {anonymous}
void init()
{
- columns.clear();
- channels.clear();
+ columns.clear();
+ channels.clear();
#ifdef WITH_VST
- masterInPlugins.clear();
- masterOutPlugins.clear();
+ masterInPlugins.clear();
+ masterOutPlugins.clear();
#endif
- header = "GIADAPTC";
- lastTakeId = 0;
- samplerate = G_DEFAULT_SAMPLERATE;
+ header = "GIADAPTC";
+ lastTakeId = 0;
+ samplerate = G_DEFAULT_SAMPLERATE;
}
int write(const string& file)
{
- json_t* jRoot = json_object();
+ json_t* jRoot = json_object();
- writeCommons(jRoot);
- writeColumns(jRoot, &columns);
- writeChannels(jRoot, &channels);
+ writeCommons(jRoot);
+ writeColumns(jRoot, &columns);
+ writeChannels(jRoot, &channels);
#ifdef WITH_VST
- writePlugins(jRoot, &masterInPlugins, PATCH_KEY_MASTER_IN_PLUGINS);
- writePlugins(jRoot, &masterOutPlugins, PATCH_KEY_MASTER_OUT_PLUGINS);
+ writePlugins(jRoot, &masterInPlugins, PATCH_KEY_MASTER_IN_PLUGINS);
+ writePlugins(jRoot, &masterOutPlugins, PATCH_KEY_MASTER_OUT_PLUGINS);
#endif
- if (json_dump_file(jRoot, file.c_str(), JSON_COMPACT) != 0) {
- gu_log("[patch::write] unable to write patch file!\n");
- return 0;
- }
- return 1;
+ if (json_dump_file(jRoot, file.c_str(), JSON_COMPACT) != 0) {
+ gu_log("[patch::write] unable to write patch file!\n");
+ return 0;
+ }
+ return 1;
}
int read(const string& file)
{
- json_error_t jError;
- json_t* jRoot = json_load_file(file.c_str(), 0, &jError);
- if (!jRoot) {
- gu_log("[patch::read] unable to read patch file! Error on line %d: %s\n",
- jError.line, jError.text);
- return PATCH_UNREADABLE;
- }
+ json_error_t jError;
+ json_t* jRoot = json_load_file(file.c_str(), 0, &jError);
+ if (!jRoot) {
+ gu_log("[patch::read] unable to read patch file! Error on line %d: %s\n",
+ jError.line, jError.text);
+ return PATCH_UNREADABLE;
+ }
- if (!storager::checkObject(jRoot, "root element"))
- return PATCH_INVALID;
+ if (!storager::checkObject(jRoot, "root element"))
+ return PATCH_INVALID;
- init();
+ init();
- /* TODO json_decref also when PATCH_INVALID */
+ /* TODO json_decref also when PATCH_INVALID */
- if (!readCommons(jRoot)) return setInvalid(jRoot);
- if (!readColumns(jRoot)) return setInvalid(jRoot);
- if (!readChannels(jRoot)) return setInvalid(jRoot);
+ if (!readCommons(jRoot)) return setInvalid(jRoot);
+ if (!readColumns(jRoot)) return setInvalid(jRoot);
+ if (!readChannels(jRoot)) return setInvalid(jRoot);
#ifdef WITH_VST
- if (!readPlugins(jRoot, &masterInPlugins, PATCH_KEY_MASTER_IN_PLUGINS)) return setInvalid(jRoot);
- if (!readPlugins(jRoot, &masterOutPlugins, PATCH_KEY_MASTER_OUT_PLUGINS)) return setInvalid(jRoot);
+ if (!readPlugins(jRoot, &masterInPlugins, PATCH_KEY_MASTER_IN_PLUGINS)) return setInvalid(jRoot);
+ if (!readPlugins(jRoot, &masterOutPlugins, PATCH_KEY_MASTER_OUT_PLUGINS)) return setInvalid(jRoot);
#endif
- json_decref(jRoot);
+ json_decref(jRoot);
- sanitize();
+ sanitize();
+ modernize();
- return PATCH_READ_OK;
+ return PATCH_READ_OK;
}
#ifdef WITH_VST
+#include <cassert>
#include "../utils/log.h"
#include "../utils/fs.h"
#include "../utils/string.h"
/* -------------------------------------------------------------------------- */
-void init(int _buffersize, int _samplerate)
+void init(int buffersize_, int samplerate_)
{
- gu_log("[pluginHost::init] initialize with buffersize=%d, samplerate=%d\n",
- _buffersize, _samplerate);
-
messageManager = juce::MessageManager::getInstance();
- audioBuffer.setSize(2, _buffersize);
- samplerate = _samplerate;
- buffersize = _buffersize;
+ audioBuffer.setSize(G_MAX_IO_CHANS, buffersize_);
+ samplerate = samplerate_;
+ buffersize = buffersize_;
missingPlugins = false;
//unknownPluginList.empty();
loadList(gu_getHomePath() + G_SLASH + "plugins.xml");
pthread_mutex_init(&mutex_midi, nullptr);
+
+ gu_log("[pluginHost::init] initialized with buffersize=%d, samplerate=%d\n",
+ buffersize, samplerate);
}
{
juce::PluginDescription* pd = knownPluginList.getType(i);
PluginInfo pi;
- pi.uid = pd->fileOrIdentifier.toStdString();
- pi.name = pd->name.toStdString();
- pi.category = pd->category.toStdString();
+ pi.uid = pd->fileOrIdentifier.toStdString();
+ pi.name = pd->name.toStdString();
+ pi.category = pd->category.toStdString();
pi.manufacturerName = pd->manufacturerName.toStdString();
- pi.format = pd->pluginFormatName.toStdString();
- pi.isInstrument = pd->isInstrument;
+ pi.format = pd->pluginFormatName.toStdString();
+ pi.isInstrument = pd->isInstrument;
return pi;
}
/* -------------------------------------------------------------------------- */
-void processStack(float* buffer, int stackType, Channel* ch)
+void processStack(AudioBuffer& outBuf, int stackType, Channel* ch)
{
vector<Plugin*>* pStack = getStack(stackType, ch);
if (pStack == nullptr || pStack->size() == 0)
return;
+ assert(outBuf.countFrames() == audioBuffer.getNumSamples());
+
/* MIDI channels must not process the current buffer: give them an empty one.
Sample channels and Master in/out want audio data instead: let's convert the
internal buffer from Giada to Juce. */
- if (ch != nullptr && ch->type == CHANNEL_MIDI)
+ if (ch != nullptr && ch->type == G_CHANNEL_MIDI)
audioBuffer.clear();
else
- for (int i=0; i<buffersize; i++) {
- audioBuffer.setSample(0, i, buffer[i*2]);
- audioBuffer.setSample(1, i, buffer[(i*2)+1]);
- }
+ for (int i=0; i<outBuf.countFrames(); i++)
+ for (int j=0; j<outBuf.countChannels(); j++)
+ audioBuffer.setSample(j, i, outBuf[i][j]);
/* Hardcore processing. At the end we swap input and output, so that he N-th
plugin will process the result of the plugin N-1. Part of this loop must be
set of MIDI events. */
if (ch != nullptr && plugin->acceptsMidi()) {
- juce::AudioBuffer<float> tmp(2, buffersize);
+ juce::AudioBuffer<float> tmp(audioBuffer.getNumChannels(), buffersize);
plugin->process(tmp, ch->getPluginMidiEvents());
- for (int i=0; i<buffersize; i++) {
- audioBuffer.addSample(0, i, tmp.getSample(0, i));
- audioBuffer.addSample(1, i, tmp.getSample(1, i));
- }
+ for (int i=0; i<audioBuffer.getNumSamples(); i++)
+ for (int j=0; j<audioBuffer.getNumChannels(); j++)
+ audioBuffer.addSample(j, i, tmp.getSample(j, i));
}
else
plugin->process(audioBuffer, juce::MidiBuffer()); // Empty MIDI buffer
/* Converting buffer from Juce to Giada. A note for the future: if we
overwrite (=) (as we do now) it's SEND, if we add (+) it's INSERT. */
- for (int i=0; i<buffersize; i++) {
- buffer[i*2] = audioBuffer.getSample(0, i);
- buffer[(i*2)+1] = audioBuffer.getSample(1, i);
- }
+ for (int i=0; i<outBuf.countFrames(); i++)
+ for (int j=0; j<outBuf.countChannels(); j++)
+ outBuf[i][j] = audioBuffer.getSample(j, i);
}
}
}
+
+/* -------------------------------------------------------------------------- */
+
+
+void forEachPlugin(int stackType, const Channel* ch, std::function<void(const Plugin* p)> f)
+{
+ /* TODO - Remove const is ugly. This is a temporary workaround until all
+ PluginHost functions params will be const-correct. */
+ vector<Plugin*>* stack = getStack(stackType, const_cast<Channel*>(ch));
+ for (const Plugin* p : *stack)
+ f(p);
+}
+
+
}}}; // giada::m::pluginHost::
#include <functional>
#include <pthread.h>
#include "../deps/juce-config.h"
+#include "audioBuffer.h"
class Plugin;
bool isInstrument;
};
-void init(int bufSize, int frequency);
+extern pthread_mutex_t mutex_midi;
+
+void init(int bufSize, int samplerate);
void close();
/* scanDirs
void freeStack(int stackType, pthread_mutex_t* mutex, Channel* ch=nullptr);
/* processStack
- * apply the fx list to the buffer. */
+Applies the fx list to the buffer. */
-void processStack(float* buffer, int stackType, Channel* ch=nullptr);
+void processStack(AudioBuffer& outBuf, int stackType, Channel* ch=nullptr);
/* getStack
* Return a std::vector <Plugin *> given the stackType. If stackType == CHANNEL
void sortPlugins(int sortMethod);
-extern pthread_mutex_t mutex_midi;
+void forEachPlugin(int stackType, const Channel* ch, std::function<void(const Plugin* p)> f);
}}}; // giada::m::pluginHost::
if (!active ||
!clockRunning ||
mixerRecording ||
- (ch->type == CHANNEL_SAMPLE && ((SampleChannel*)ch)->wave == nullptr)
+ (ch->type == G_CHANNEL_SAMPLE && static_cast<SampleChannel*>(ch)->wave == nullptr)
)
return false;
return true;
void rec(int index, int type, int frame, uint32_t iValue, float fValue)
{
- /* make sure frame is even */
-
- if (frame % 2 != 0)
- frame++;
-
/* allocating the action */
- action *a = (action*) malloc(sizeof(action));
+ action* a = (action*) malloc(sizeof(action));
a->chan = index;
a->type = type;
a->frame = frame;
/* no duplicates, please */
for (unsigned t=0; t<global.at(frameToExpand).size(); t++) {
- action *ac = global.at(frameToExpand).at(t);
+ action* ac = global.at(frameToExpand).at(t);
if (ac->chan == index &&
ac->type == type &&
ac->frame == frame &&
unsigned j=0;
while (true) {
if (j == global.at(i).size()) break; // for each action j of frame i
- action *a = global.at(i).at(j);
+ action* a = global.at(i).at(j);
if (a->chan == index) {
free(a);
global.at(i).erase(global.at(i).begin() + j);
while (true) { // for each action j of frame i
if (j == global.at(i).size())
break;
- action *a = global.at(i).at(j);
+ action* a = global.at(i).at(j);
if (a->chan == index && (act & a->type) == a->type) { // bitmask
free(a);
global.at(i).erase(global.at(i).begin() + j);
void deleteAction(int chan, int frame, char type, bool checkValues,
pthread_mutex_t* mixerMutex, uint32_t iValue, float fValue)
{
- /* make sure frame is even */
-
- if (frame % 2 != 0)
- frame++;
-
/* find the frame 'frame' */
bool found = false;
/* find the action in frame i */
for (unsigned j=0; j<global.at(i).size(); j++) {
- action *a = global.at(i).at(j);
+ action* a = global.at(i).at(j);
/* action comparison logic */
if (scarto > 0 && scarto <= 6)
frames.at(i) = frames.at(i) + scarto;
}
-
- /* never ever have odd frames. */
-
- if (frames.at(i) % 2 != 0)
- frames.at(i)++;
}
/* update structs */
for (unsigned i=0; i<frames.size(); i++) {
for (unsigned j=0; j<global.at(i).size(); j++) {
- action *a = global.at(i).at(j);
+ action* a = global.at(i).at(j);
a->frame = frames.at(i);
}
}
frames.at(i) = (int) newFrame;
- if (frames.at(i) % 2 != 0)
- frames.at(i)++;
-
gu_log(", newFrame = %d\n", frames.at(i));
}
for (unsigned i=0; i<frames.size(); i++) {
for (unsigned j=0; j<global.at(i).size(); j++) {
- action *a = global.at(i).at(j);
+ action* a = global.at(i).at(j);
a->frame = frames.at(i);
}
}
frames.push_back(newframe);
global.push_back(actions);
for (unsigned k=0; k<global.at(i).size(); k++) {
- action *a = global.at(i).at(k);
+ action* a = global.at(i).at(k);
rec(a->chan, a->type, newframe, a->iValue, a->fValue);
}
}
rec(index, cmp.a1.type, frame);
- action *act = nullptr;
+ action* act = nullptr;
int res = getNextAction(index, cmp.a1.type | cmp.a2.type, cmp.a1.frame, &act);
if (res == 1) {
if (act->type == cmp.a2.type) {
rec(cmp.a2.chan, cmp.a2.type, cmp.a2.frame);
fixOverdubTruncation(cmp, mixerMutex);
}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void forEachAction(std::function<void(const action*)> f)
+{
+ for (const vector<action*> actions : recorder::global)
+ for (const action* action : actions)
+ f(action);
+}
}}}; // giada::m::recorder::
#define G_RECORDER_H
-#ifdef __APPLE__ // our Clang still doesn't know about cstdint (c++11 stuff)
- #include <stdint.h>
-#else
- #include <cstdint>
-#endif
+#include <cstdint>
#include <vector>
+#include <functional>
#include <pthread.h>
/* canRec
* can a channel rec an action? Call this one BEFORE rec(). */
-bool canRec(Channel *ch, bool clockRunning, bool mixerRecording);
+bool canRec(Channel* ch, bool clockRunning, bool mixerRecording);
/* rec
* record an action. */
* delete ONE action. Useful in the action editor. 'type' can be a mask. */
void deleteAction(int chan, int frame, char type, bool checkValues,
- pthread_mutex_t *mixerMutex, uint32_t iValue=0, float fValue=0.0);
+ pthread_mutex_t* mixerMutex, uint32_t iValue=0, float fValue=0.0);
/* deleteActions
Deletes A RANGE of actions from frame_a to frame_b in channel 'chan' of type
'type' (can be a bitmask). Exclusive range (frame_a, frame_b). */
void deleteActions(int chan, int frame_a, int frame_b, char type,
- pthread_mutex_t *mixerMutex);
+ pthread_mutex_t* mixerMutex);
/* clearAll
* delete everything. */
void startOverdub(int chan, char action, int frame, unsigned bufferSize);
void stopOverdub(int currentFrame, int totalFrames, pthread_mutex_t *mixerMutex);
+
+/* forEachAction
+Applies a read-only callback on each action recorded. */
+
+void forEachAction(std::function<void(const action*)> f);
}}}; // giada::m::recorder::
#endif
#include "../utils/fs.h"
#include "../utils/string.h"
#include "patch.h"
+#include "channelManager.h"
#include "const.h"
#include "conf.h"
#include "clock.h"
SampleChannel::SampleChannel(int bufferSize, bool inputMonitor)
- : Channel (CHANNEL_SAMPLE, STATUS_EMPTY, bufferSize),
+ : Channel (G_CHANNEL_SAMPLE, STATUS_EMPTY, bufferSize),
rsmp_state (nullptr),
- pChan (nullptr),
- vChanPreview (nullptr),
frameRewind (-1),
begin (0),
end (0),
- boost (G_DEFAULT_BOOST),
pitch (G_DEFAULT_PITCH),
- trackerPreview (0),
- shift (0),
- wave (nullptr),
- tracker (0),
- mode (G_DEFAULT_CHANMODE),
- qWait (false),
+ boost (G_DEFAULT_BOOST),
fadeinOn (false),
fadeinVol (1.0f),
fadeoutOn (false),
fadeoutVol (1.0f),
fadeoutTracker (0),
fadeoutStep (G_DEFAULT_FADEOUT_STEP),
+ wave (nullptr),
+ tracker (0),
+ trackerPreview (0),
+ shift (0),
+ mode (G_DEFAULT_CHANMODE),
+ qWait (false),
inputMonitor (inputMonitor),
midiInReadActions(0x0),
midiInPitch (0x0)
delete wave;
if (rsmp_state != nullptr)
src_delete(rsmp_state);
- if (pChan != nullptr)
- delete[] pChan;
- if (vChanPreview != nullptr)
- delete[] vChanPreview;
}
if (!Channel::allocBuffers())
return false;
- rsmp_state = src_new(SRC_LINEAR, 2, nullptr);
+ rsmp_state = src_new(SRC_LINEAR, G_MAX_IO_CHANS, nullptr);
if (rsmp_state == nullptr) {
gu_log("[SampleChannel::allocBuffers] unable to alloc memory for SRC_STATE!\n");
return false;
}
- pChan = new (std::nothrow) float[bufferSize];
- if (pChan == nullptr) {
+ if (!pChan.alloc(bufferSize, G_MAX_IO_CHANS)) {
gu_log("[SampleChannel::allocBuffers] unable to alloc memory for pChan!\n");
return false;
}
- vChanPreview = new (std::nothrow) float[bufferSize];
- if (vChanPreview == nullptr) {
+ if (!vChanPreview.alloc(bufferSize, G_MAX_IO_CHANS)) {
gu_log("[SampleChannel::allocBuffers] unable to alloc memory for vChanPreview!\n");
return false;
}
/* -------------------------------------------------------------------------- */
-void SampleChannel::copy(const Channel* _src, pthread_mutex_t* pluginMutex)
+void SampleChannel::copy(const Channel* src_, pthread_mutex_t* pluginMutex)
{
- Channel::copy(_src, pluginMutex);
- const SampleChannel* src = static_cast<const SampleChannel*>(_src);
+ Channel::copy(src_, pluginMutex);
+ const SampleChannel* src = static_cast<const SampleChannel*>(src_);
tracker = src->tracker;
begin = src->begin;
end = src->end;
void SampleChannel::clear()
{
- /** TODO - these memsets may be done only if status PLAY (if below),
+ /** TODO - these clear() may be done only if status PLAY | ENDING (if below),
* but it would require extra clearPChan calls when samples stop */
- std::memset(vChan, 0, sizeof(float) * bufferSize);
- std::memset(pChan, 0, sizeof(float) * bufferSize);
+ vChan.clear();
+ pChan.clear();
if (status & (STATUS_PLAY | STATUS_ENDING)) {
tracker = fillChan(vChan, tracker, 0);
{
/* method: check this frame && next frame, then calculate delta */
- recorder::action *a0 = nullptr;
- recorder::action *a1 = nullptr;
+ recorder::action* a0 = nullptr;
+ recorder::action* a1 = nullptr;
int res;
/* get this action on frame 'frame'. It's unlikely that the action
res = recorder::getAction(index, G_ACTION_VOLUME, 0, &a1);
volume_i = a0->fValue;
- volume_d = ((a1->fValue - a0->fValue) / ((a1->frame - a0->frame) / 2)) * 1.003f;
+ volume_d = ((a1->fValue - a0->fValue) / (a1->frame - a0->frame)) * 1.003f;
}
void SampleChannel::hardStop(int frame)
{
- if (frame != 0) // clear data in range [frame, bufferSize-1]
- clearChan(vChan, frame);
+ if (frame != 0)
+ vChan.clear(frame); // clear data in range [frame, [end]]
status = STATUS_OFF;
sendMidiLplay();
reset(frame);
void SampleChannel::onBar(int frame)
{
///if (mode == LOOP_REPEAT && status == STATUS_PLAY)
- /// //setXFade(frame);
- /// reset(frame);
+ /// //setXFade(frame * 2);
+ /// reset(frame * 2);
if (mode == LOOP_REPEAT) {
if (status == STATUS_PLAY)
- //setXFade(frame);
+ //setXFade(frame * 2);
reset(frame);
}
else
void SampleChannel::setBegin(int f)
{
- /* TODO - Opaque channel's count - everything in SampleChannel should be
- frame-based, not sample-based. I.e. Remove * wave->getChannels() stuff. */
-
if (f < 0)
begin = 0;
else
if (f > wave->getSize())
- begin = wave->getSize() * wave->getChannels();
+ begin = wave->getSize();
else
if (f >= end)
- begin = end - wave->getChannels();
+ begin = end - 1;
else
- begin = f * wave->getChannels();
+ begin = f;
tracker = begin;
trackerPreview = begin;
void SampleChannel::setEnd(int f)
{
- /* TODO - Opaque channel's count - everything in SampleChannel should be
- frame-based, not sample-based. I.e. Remove * wave->getChannels() stuff. */
-
if (f < 0)
end = begin + wave->getChannels();
else
if (f >= wave->getSize())
- end = (wave->getSize() - 1) * wave->getChannels();
+ end = wave->getSize() - 1;
else
- if (f * wave->getChannels() <= begin)
- end = begin + wave->getChannels();
+ if (f <= begin)
+ end = begin + 1;
else
- end = f * wave->getChannels();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-int SampleChannel::getBegin()
-{
- /* TODO - Opaque channel's count - everything in SampleChannel should be
- frame-based, not sample-based. I.e. Remove / wave->getChannels() stuff. */
-
- return begin / wave->getChannels();
-}
-
-
-int SampleChannel::getEnd()
-{
- /* TODO - Opaque channel's count - everything in SampleChannel should be
- frame-based, not sample-based. I.e. Remove / wave->getChannels() stuff. */
-
- return end / wave->getChannels();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void SampleChannel::setTrackerPreview(int f)
-{
- /* TODO - Opaque channel's count - everything in SampleChannel should be
- frame-based, not sample-based. I.e. Remove * wave->getChannels() stuff. */
-
- trackerPreview = f * wave->getChannels();
-}
-
-
-int SampleChannel::getTrackerPreview() const
-{
- /* TODO - Opaque channel's count - everything in SampleChannel should be
- frame-based, not sample-based. I.e. Remove / wave->getChannels() stuff. */
-
- return trackerPreview / wave->getChannels();
+ end = f;
}
/* -------------------------------------------------------------------------- */
-int SampleChannel::getShift() const { return shift; }
-void SampleChannel::setShift(int s) { shift = s; }
+int SampleChannel::getBegin() const { return begin; }
+int SampleChannel::getEnd() const { return end; }
/* -------------------------------------------------------------------------- */
}
-float SampleChannel::getPitch()
-{
- return pitch;
-}
+float SampleChannel::getPitch() const { return pitch; }
/* -------------------------------------------------------------------------- */
void SampleChannel::sum(int frame, bool running)
{
- // TODO - Opaque channels' processing
- // TODO - Opaque channels' processing
- // TODO - Opaque channels' processing
-
if (wave == nullptr || status & ~(STATUS_PLAY | STATUS_ENDING))
return;
* volume envelope. */
if (mute || mute_i) {
- vChan[frame] = 0.0f;
- vChan[frame+1] = 0.0f;
+ for (int i=0; i<vChan.countChannels(); i++)
+ vChan[frame][i] = 0.0f;
}
else
if (fadeinOn) {
if (fadeinVol < 1.0f) {
- vChan[frame] *= fadeinVol * volume_i;
- vChan[frame+1] *= fadeinVol * volume_i;
+ for (int i=0; i<vChan.countChannels(); i++)
+ vChan[frame][i] *= fadeinVol * volume_i;
fadeinVol += 0.01f;
}
else {
if (fadeoutOn) {
if (fadeoutVol > 0.0f) { // fadeout ongoing
if (fadeoutType == XFADE) {
- vChan[frame] *= volume_i;
- vChan[frame+1] *= volume_i;
- vChan[frame] = pChan[frame] * fadeoutVol * volume_i;
- vChan[frame+1] = pChan[frame+1] * fadeoutVol * volume_i;
+ for (int i=0; i<vChan.countChannels(); i++)
+ vChan[frame][i] = pChan[frame][i] * fadeoutVol * volume_i;
}
else {
- vChan[frame] *= fadeoutVol * volume_i;
- vChan[frame+1] *= fadeoutVol * volume_i;
+ for (int i=0; i<vChan.countChannels(); i++)
+ vChan[frame][i] *= fadeoutVol * volume_i;
}
fadeoutVol -= fadeoutStep;
}
}
}
else {
- vChan[frame] *= volume_i;
- vChan[frame+1] *= volume_i;
+ for (int i=0; i<vChan.countChannels(); i++)
+ vChan[frame][i] *= volume_i;
}
}
else { // at this point the sample has reached the end */
status = STATUS_WAIT;
}
- /* check for end of samples. SINGLE_ENDLESS runs forever unless
- * it's in ENDING mode. */
+ /* Check for end of samples. SINGLE_ENDLESS runs forever unless it's in
+ ENDING mode. */
reset(frame);
}
if (status == STATUS_PLAY) {
/*
if (mute || mute_i)
- reset(frame);
+ reset(frame * 2);
else
- setXFade(frame);
+ setXFade(frame * 2);
*/
reset(frame);
}
/* -------------------------------------------------------------------------- */
-void SampleChannel::quantize(int index, int localFrame)
+void SampleChannel::quantize(int index, int localFrame, int globalFrame)
{
/* skip if LOOP_ANY or not in quantizer-wait mode */
tracker = fillChan(vChan, tracker, localFrame); /// FIXME: ???
}
else
- //setXFade(localFrame);
+ //setXFade(localFrame * 2);
reset(localFrame);
/* this is the moment in which we record the keypress, if the
if (recorder::canRec(this, clock::isRunning(), mixer::recording)) {
if (mode == SINGLE_PRESS) {
- recorder::startOverdub(index, G_ACTION_KEYS, clock::getCurrentFrame(),
- kernelAudio::getRealBufSize());
+ recorder::startOverdub(index, G_ACTION_KEYS, globalFrame,
+ kernelAudio::getRealBufSize());
readActions = false; // don't read actions while overdubbing
}
else
- recorder::rec(index, G_ACTION_KEYPRESS, clock::getCurrentFrame());
+ recorder::rec(index, G_ACTION_KEYPRESS, globalFrame);
hasActions = true;
}
}
int SampleChannel::getPosition()
{
- /* TODO - Opaque channel's count - everything in SampleChannel should be
- frame-based, not sample-based. I.e. Remove / wave->getChannels() stuff. */
-
if (status & ~(STATUS_EMPTY | STATUS_MISSING | STATUS_OFF)) // if is not (...)
- return (tracker - begin) / wave->getChannels();
+ return tracker - begin;
else
return -1;
}
void SampleChannel::calcFadeoutStep()
{
- if (end - tracker < (1 / G_DEFAULT_FADEOUT_STEP) * 2)
- fadeoutStep = ceil((end - tracker) / volume) * 2; /// or volume_i ???
+ if (end - tracker < (1 / G_DEFAULT_FADEOUT_STEP))
+ fadeoutStep = ceil((end - tracker) / volume); /// or volume_i ???
else
fadeoutStep = G_DEFAULT_FADEOUT_STEP;
}
/* -------------------------------------------------------------------------- */
-void SampleChannel::setOnEndPreviewCb(std::function<void()> f)
-{
- onPreviewEnd = f;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
void SampleChannel::setFadeIn(bool internal)
{
if (internal) mute_i = false; // remove mute before fading in
/* -------------------------------------------------------------------------- */
-/* On reset, if frame > 0 and in play, fill again pChan to create something like
-this:
-
- |abcdefabcdefab*abcdefabcde|
- [old data-----]*[new data--]
-
-*/
void SampleChannel::reset(int frame)
{
tracker = begin;
mute_i = false;
qWait = false; // Was in qWait mode? Reset occured, no more qWait now.
+
+ /* On reset, if frame > 0 and in play, fill again pChan to create something
+ like this:
+
+ |abcdefabcdefab*abcdefabcde|
+ [old data-----]*[new data--] */
+
if (frame > 0 && status & (STATUS_PLAY | STATUS_ENDING))
tracker = fillChan(vChan, tracker, frame);
}
wave = w;
status = STATUS_OFF;
begin = 0;
- end = (wave->getSize() - 1) * wave->getChannels(); // TODO - Opaque channels' count
+ end = wave->getSize() - 1;
name = wave->getBasename();
}
/* -------------------------------------------------------------------------- */
-void SampleChannel::process(float* outBuffer, float* inBuffer)
+void SampleChannel::process(giada::m::AudioBuffer& out, const giada::m::AudioBuffer& in)
{
+ assert(out.countSamples() == vChan.countSamples());
+ assert(in.countSamples() == vChan.countSamples());
+
/* If armed and inbuffer is not nullptr (i.e. input device available) and
input monitor is on, copy input buffer to vChan: this enables the input
monitoring. The vChan will be overwritten later by pluginHost::processStack,
so that you would record "clean" audio (i.e. not plugin-processed). */
- if (armed && inBuffer && inputMonitor)
- for (int i=0; i<bufferSize; i++)
- vChan[i] += inBuffer[i]; // add, don't overwrite (no raw memcpy)
+ if (armed && in.isAllocd() && inputMonitor)
+ for (int i=0; i<vChan.countFrames(); i++)
+ for (int j=0; j<vChan.countChannels(); j++)
+ vChan[i][j] += in[i][j]; // add, don't overwrite
#ifdef WITH_VST
pluginHost::processStack(vChan, pluginHost::CHANNEL, this);
#endif
- // TODO - Opaque channels' processing
- for (int j=0; j<bufferSize; j+=2) {
- outBuffer[j] += vChan[j] * volume * calcPanning(0) * boost;
- outBuffer[j+1] += vChan[j+1] * volume * calcPanning(1) * boost;
- }
+ for (int i=0; i<out.countFrames(); i++)
+ for (int j=0; j<out.countChannels(); j++)
+ out[i][j] += vChan[i][j] * volume * calcPanning(j) * boost;
}
/* -------------------------------------------------------------------------- */
-void SampleChannel::preview(float* outBuffer)
+void SampleChannel::preview(giada::m::AudioBuffer& out)
{
if (previewMode == G_PREVIEW_NONE)
return;
- std::memset(vChanPreview, 0, sizeof(float) * bufferSize);
+ vChanPreview.clear();
/* If the tracker exceedes the end point and preview is looped, split the
rendering as in SampleChannel::reset(). */
else
trackerPreview = fillChan(vChanPreview, trackerPreview, 0, false);
- // TODO - Opaque channels' processing
- for (int j=0; j<bufferSize; j+=2) {
- outBuffer[j] += vChanPreview[j] * volume * calcPanning(0) * boost;
- outBuffer[j+1] += vChanPreview[j+1] * volume * calcPanning(1) * boost;
- }
+ for (int i=0; i<out.countFrames(); i++)
+ for (int j=0; j<out.countChannels(); j++)
+ out[i][j] += vChanPreview[i][j] * volume * calcPanning(j) * boost;
}
/* -------------------------------------------------------------------------- */
-int SampleChannel::readPatch(const string& basePath, int i,
- pthread_mutex_t* pluginMutex, int samplerate, int rsmpQuality)
+void SampleChannel::readPatch(const string& basePath, int i)
{
- /* load channel's data first: if the sample is missing or wrong, the channel
- * is not completely blank. */
-
- Channel::readPatch("", i, pluginMutex, samplerate, rsmpQuality);
-
- const patch::channel_t* pch = &patch::channels.at(i);
-
- mode = pch->mode;
- boost = pch->boost;
- readActions = pch->recActive;
- recStatus = readActions ? REC_READING : REC_STOPPED;
- midiInVeloAsVol = pch->midiInVeloAsVol;
- midiInReadActions = pch->midiInReadActions;
- midiInPitch = pch->midiInPitch;
- inputMonitor = pch->inputMonitor;
-
- Wave* w = nullptr;
- int res = waveManager::create(basePath + pch->samplePath, &w);
-
- if (res == G_RES_OK) {
- pushWave(w);
- setName(pch->name);
- setBegin(pch->begin);
- setEnd(pch->end);
- setPitch(pch->pitch);
- }
- else {
- if (res == G_RES_ERR_NO_DATA)
- status = STATUS_EMPTY;
- else
- if (res == G_RES_ERR_IO)
- status = STATUS_MISSING;
- sendMidiLplay(); // FIXME - why sending MIDI lightning if sample status is wrong?
- }
-
- return res;
+ Channel::readPatch("", i);
+ channelManager::readPatch(this, basePath, i);
}
/* -------------------------------------------------------------------------- */
-int SampleChannel::writePatch(int i, bool isProject)
+void SampleChannel::writePatch(int i, bool isProject)
{
- // TODO - this code belongs to an upper level (glue)
-
- int pchIndex = Channel::writePatch(i, isProject);
- patch::channel_t *pch = &patch::channels.at(pchIndex);
-
- if (wave != nullptr) {
- pch->samplePath = wave->getPath();
- if (isProject)
- pch->samplePath = gu_basename(wave->getPath()); // make it portable
- }
- else
- pch->samplePath = "";
-
- pch->mode = mode;
- pch->begin = begin;
- pch->end = end;
- pch->boost = boost;
- pch->recActive = readActions;
- pch->pitch = pitch;
- pch->inputMonitor = inputMonitor;
- pch->midiInReadActions = midiInReadActions;
- pch->midiInPitch = midiInPitch;
-
- return 0;
+ Channel::writePatch(i, isProject);
+ channelManager::writePatch(this, isProject, i);
}
/* -------------------------------------------------------------------------- */
-void SampleChannel::clearChan(float *dest, int start)
-{
- memset(dest+start, 0, sizeof(float)*(bufferSize-start));
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-int SampleChannel::fillChan(float *dest, int start, int offset, bool rewind)
+int SampleChannel::fillChan(giada::m::AudioBuffer& dest, int start, int offset, bool rewind)
{
int position; // return value: the new position
if (pitch == 1.0f) {
- /* case 1: 'dest' lies within the original sample boundaries (start-
- * end) */
+ /* Fill 'dest' buffer with a chunk of data of size 'buffersize - offset'.
+ Case 1: there is enough data in Wave. Case2: not enough data or Wave is
+ smaller than the buffer. */
+
+ int chunkSize = bufferSize - offset;
- if (start+bufferSize-offset <= end) {
- memcpy(dest+offset, wave->getData()+start, (bufferSize-offset)*sizeof(float));
- position = start+bufferSize-offset;
+ if (start + chunkSize <= end) {
+ position = start + chunkSize;
if (rewind)
frameRewind = -1;
}
-
- /* case2: 'dest' lies outside the end of the sample, OR the sample
- * is smaller than 'dest' */
-
else {
- memcpy(dest+offset, wave->getData()+start, (end-start)*sizeof(float));
- position = end;
+ chunkSize = end - start;
+ position = end;
if (rewind)
- frameRewind = end-start+offset;
+ frameRewind = chunkSize + offset;
}
+ dest.copyData(wave->getFrame(start), chunkSize, offset);
}
else {
- // TODO - Opaque channels count
- rsmp_data.data_in = wave->getData()+start; // source data
- rsmp_data.input_frames = (end-start)/2; // how many readable bytes
- rsmp_data.data_out = dest+offset; // destination (processed data)
- rsmp_data.output_frames = (bufferSize-offset)/2; // how many bytes to process
+ rsmp_data.data_in = wave->getFrame(start); // source data
+ rsmp_data.input_frames = end - start; // how many readable bytes
+ rsmp_data.data_out = dest[offset]; // destination (processed data)
+ rsmp_data.output_frames = bufferSize - offset; // how many bytes to process
rsmp_data.end_of_input = false;
src_process(rsmp_state, &rsmp_data);
- int gen = rsmp_data.output_frames_gen*2; // frames generated by this call
- position = start + rsmp_data.input_frames_used*2; // position goes forward of frames_used (i.e. read from wave)
+ position = start + rsmp_data.input_frames_used; // position goes forward of frames_used (i.e. read from wave)
if (rewind) {
- if (gen == bufferSize-offset)
+ int gen = rsmp_data.output_frames_gen; // frames generated by this call
+ if (gen == bufferSize - offset)
frameRewind = -1;
else
- frameRewind = gen+offset;
+ frameRewind = gen + offset;
}
}
return position;
rewind=false don't rewind internal tracker. Returns new sample position,
in frames. It resamples data if pitch != 1.0f. */
- int fillChan(float* dest, int start, int offset, bool rewind=true);
-
- /* clearChan
- * set data to zero from start to bufferSize-1. */
-
- void clearChan(float* dest, int start);
+ int fillChan(giada::m::AudioBuffer& dest, int start, int offset, bool rewind=true);
/* calcFadeoutStep
- * how many frames are left before the end of the sample? Is there
- * enough room for a complete fadeout? Should we shorten it? */
+ How many frames are left before the end of the sample? Is there enough room
+ for a complete fadeout? Should we shorten it? */
void calcFadeoutStep();
/* calcVolumeEnv
- * compute any changes in volume done via envelope tool */
+ Computes any changes in volume done via envelope tool. */
void calcVolumeEnv(int frame);
+ /* reset
+ Rewinds tracker to the beginning of the sample. */
+
+ void reset(int frame);
+
+ /* fade methods
+ Prepare channel for fade, mixer will take care of the process during master
+ play. */
+
+ void setFadeIn(bool internal);
+ void setFadeOut(int actionPostFadeout);
+ void setXFade(int frame);
+
/* rsmp_state, rsmp_data
- * structs from libsamplerate */
+ Structs from libsamplerate. */
SRC_STATE* rsmp_state;
SRC_DATA rsmp_data;
- /* pChan
- Extra virtual channel for processing resampled data. */
+ /* pChan, vChanPreview
+ Extra virtual channel for processing resampled data and for audio preview. */
- float* pChan;
-
- /* pChan
- Extra virtual channel for audio preview. */
-
- float* vChanPreview;
+ giada::m::AudioBuffer pChan;
+ giada::m::AudioBuffer vChanPreview;
/* frameRewind
Exact frame in which a rewind occurs. */
int frameRewind;
- int begin;
- int end;
- float boost;
- float pitch;
- int trackerPreview; // chan position for audio preview
- int shift;
+ /* begin, end
+ Begin/end point to read wave data from/to. */
- /* onPreviewEnd
- A callback fired when audio preview ends. */
+ int begin;
+ int end;
+
+ float pitch;
+ float boost;
- std::function<void()> onPreviewEnd;
+ bool fadeinOn;
+ float fadeinVol;
+ bool fadeoutOn;
+ float fadeoutVol; // fadeout volume
+ int fadeoutTracker; // tracker fadeout, xfade only
+ float fadeoutStep; // fadeout decrease
+ int fadeoutType; // xfade or fadeout
+ int fadeoutEnd; // what to do when fadeout ends
public:
void copy(const Channel* src, pthread_mutex_t* pluginMutex) override;
void clear() override;
- void process(float* outBuffer, float* inBuffer) override;
- void preview(float* outBuffer) override;
+ void process(giada::m::AudioBuffer& out, const giada::m::AudioBuffer& in) override;
+ void preview(giada::m::AudioBuffer& out) override;
void start(int frame, bool doQuantize, int quantize, bool mixerIsRunning,
bool forceStart, bool isUserGenerated) override;
void kill(int frame) override;
void rewind() override;
void setMute(bool internal) override;
void unsetMute(bool internal) override;
- int readPatch(const std::string& basePath, int i, pthread_mutex_t* pluginMutex,
- int samplerate, int rsmpQuality) override;
- int writePatch(int i, bool isProject) override;
- void quantize(int index, int localFrame) override;
+ void readPatch(const std::string& basePath, int i) override;
+ void writePatch(int i, bool isProject) override;
+ void quantize(int index, int localFrame, int globalFrame) override;
void onZero(int frame, bool recsStopOnChanHalt) override;
void onBar(int frame) override;
void parseAction(giada::m::recorder::action* a, int localFrame, int globalFrame,
bool canInputRec() override;
bool allocBuffers() override;
- int getTrackerPreview() const;
- int getShift() const;
float getBoost() const;
-
- void setShift(int s);
-
- void reset(int frame);
-
- /* fade methods
- * prepare channel for fade, mixer will take care of the process
- * during master play. */
-
- void setFadeIn(bool internal);
- void setFadeOut(int actionPostFadeout);
- void setXFade(int frame);
+ int getBegin() const;
+ int getEnd() const;
+ float getPitch() const;
/* pushWave
Adds a new wave to an existing channel. */
void pushWave(Wave* w);
/* getPosition
- * returns the position of an active sample. If EMPTY o MISSING
- * returns -1. */
+ Returns the position of an active sample. If EMPTY o MISSING returns -1. */
int getPosition();
/* sum
- * add sample frames to virtual channel. Frame = processed frame in
- * Mixer. Running = is Mixer in play? */
+ Adds sample frames to virtual channel. Frame = processed frame in Mixer.
+ Running == is Mixer in play? */
void sum(int frame, bool running);
-
void setPitch(float v);
- float getPitch();
void setBegin(int f);
- int getBegin();
void setEnd(int f);
- int getEnd();
- void setTrackerPreview(int f);
+ void setBoost(float v);
/* hardStop
- * stop the channel immediately, no further checks. */
+ Stops the channel immediately, no further checks. */
void hardStop(int frame);
void setReadActions(bool v, bool killOnFalse);
- void setBoost(float v);
+ /* onPreviewEnd
+ A callback fired when audio preview ends. */
- void setOnEndPreviewCb(std::function<void()> f);
+ std::function<void()> onPreviewEnd;
Wave* wave;
int tracker; // chan position
+ int trackerPreview; // chan position for audio preview
+ int shift;
int mode; // mode: see const.h
bool qWait; // quantizer wait
- bool fadeinOn;
- float fadeinVol;
- bool fadeoutOn;
- float fadeoutVol; // fadeout volume
- int fadeoutTracker; // tracker fadeout, xfade only
- float fadeoutStep; // fadeout decrease
- int fadeoutType; // xfade or fadeout
- int fadeoutEnd; // what to do when fadeout ends
bool inputMonitor;
/* midi stuff */
Wave::Wave()
-: m_data (nullptr),
- m_size (0),
- m_logical(0),
- m_edited (0) {}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-Wave::Wave(float* data, int size, int channels, int rate, int bits,
- const std::string& path)
-: m_data (data),
- m_size (size),
- m_channels(channels),
- m_rate (rate),
- m_bits (bits),
- m_logical (false),
- m_edited (false),
- m_path (path)
+: m_rate (0),
+ m_bits (0),
+ m_logical(false),
+ m_edited (false)
{
-}
+}
/* -------------------------------------------------------------------------- */
-Wave::~Wave()
+float* Wave::operator [](int offset) const
{
- clear();
+ return buffer[offset];
}
Wave::Wave(const Wave& other)
-: m_data (nullptr),
- m_size (other.m_size),
- m_channels(other.m_channels),
- m_rate (other.m_rate),
- m_bits (other.m_bits),
+: m_rate (other.m_rate),
+ m_bits (other.m_bits),
m_logical (true), // a cloned wave does not exist on disk
m_edited (false),
m_path (other.m_path)
{
- m_data = new float[m_size];
- memcpy(m_data, other.m_data, m_size * sizeof(float));
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void Wave::clear()
-{
- free();
- m_path = "";
- m_size = 0;
+ buffer.alloc(other.getSize(), other.getChannels());
+ buffer.copyData(other.getFrame(0), other.getSize(), other.getChannels());
}
/* -------------------------------------------------------------------------- */
-void Wave::free()
+bool Wave::alloc(int size, int channels, int rate, int bits, const std::string& path)
{
- if (m_data == nullptr)
- return;
- delete[] m_data;
- m_data = nullptr;
+ if (!buffer.alloc(size, channels))
+ return false;
+ m_rate = rate;
+ m_bits = bits;
+ m_path = path;
+ return true;
}
int Wave::getRate() const { return m_rate; }
-int Wave::getChannels() const { return m_channels; }
+int Wave::getChannels() const { return buffer.countChannels(); }
std::string Wave::getPath() const { return m_path; }
-float* Wave::getData() const { return m_data; }
-int Wave::getSize() const { return m_size / m_channels; }
+int Wave::getSize() const { return buffer.countFrames(); }
int Wave::getBits() const { return m_bits; }
bool Wave::isLogical() const { return m_logical; }
bool Wave::isEdited() const { return m_edited; }
int Wave::getDuration() const
{
- return m_size / m_channels / m_rate;
+ return buffer.countFrames() / m_rate;
}
float* Wave::getFrame(int f) const
{
- assert(f >= 0);
- assert(f < getSize());
-
- f *= m_channels; // convert frame to sample
- return m_data + f; // i.e. a pointer to m_data[f]
+ return buffer[f];
}
/* -------------------------------------------------------------------------- */
-void Wave::setRate(int v) { m_rate = v; }
-void Wave::setChannels(int v) { m_channels = v; }
+void Wave::setRate(int v) { m_rate = v; }
void Wave::setLogical(bool l) { m_logical = l; }
-void Wave::setEdited(bool e) { m_edited = e; }
+void Wave::setEdited(bool e) { m_edited = e; }
/* -------------------------------------------------------------------------- */
m_path = p;
else
m_path = gu_stripExt(p) + "-" + gu_iToString(id) + "." + gu_getExt(p);
+}
+
+/* -------------------------------------------------------------------------- */
+
+void Wave::copyData(float* data, int frames, int offset)
+{
+ buffer.copyData(data, frames, offset);
}
/* -------------------------------------------------------------------------- */
-void Wave::setData(float* d, int size)
-{
- m_data = d;
- m_size = size;
-}
+void Wave::moveData(giada::m::AudioBuffer& b)
+{
+ buffer.moveData(b);
+}
\ No newline at end of file
#include <sndfile.h>
#include <string>
#include "const.h"
+#include "audioBuffer.h"
class Wave
{
-private:
-
- float* m_data;
- int m_size; // Wave size in bytes (size in frames: m_size / m_channels)
- int m_channels;
- int m_rate;
- int m_bits;
- bool m_logical; // memory only (a take)
- bool m_edited; // edited via editor
-
- std::string m_path; // E.g. /path/to/my/sample.wav
-
public:
Wave();
- Wave(float* data, int size, int channels, int rate, int bits, const std::string& path);
- ~Wave();
Wave(const Wave& other);
- /* setPath
- Sets new path 'p'. If 'id' != -1 inserts a numeric id next to the file
- extension, e.g. : /path/to/sample-[id].wav */
-
- void setPath(const std::string& p, int id=-1);
+ float* operator [](int offset) const;
+ /* getFrame
+ Works like operator []. See AudioBuffer for reference. */
+
+ float* getFrame(int f) const;
+
std::string getBasename(bool ext=false) const;
std::string getExtension() const;
int getRate() const;
int getChannels() const;
std::string getPath() const;
int getBits() const;
- float* getData() const;
int getSize() const; // in frames
int getDuration() const;
bool isLogical() const;
bool isEdited() const;
+ /* setPath
+ Sets new path 'p'. If 'id' != -1 inserts a numeric id next to the file
+ extension, e.g. : /path/to/sample-[id].wav */
+
+ void setPath(const std::string& p, int id=-1);
+
void setRate(int v);
- void setChannels(int v);
- void setData(float* data, int size);
void setLogical(bool l);
void setEdited(bool e);
-
- /* clear
- Resets Wave to init state. */
-
- void clear();
- /* free
- Frees memory, leaving everything else untouched. */
+ /* moveData
+ Moves data held by 'b' into this buffer. Then 'b' becomes an empty buffer. */
- void free();
+ void moveData(giada::m::AudioBuffer& b);
+
+ /* 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. */
- /* getFrame
- Given a frame 'f', returns a pointer to it. This is useful for digging inside
- a frame, i.e. parsing each channel. How to use it:
+ void copyData(float* data, int frames, int offset=0);
- float* frame = w->getFrame(40);
- for (int i=0; i<w->getChannels(); i++)
- ... frame[i] ...
- */
+ bool alloc(int size, int channels, int rate, int bits, const std::string& path);
- float* getFrame(int f) const;
+private:
+ giada::m::AudioBuffer buffer;
+ int m_rate;
+ int m_bits;
+ bool m_logical; // memory only (a take)
+ bool m_edited; // edited via editor
+ std::string m_path; // E.g. /path/to/my/sample.wav
};
#endif
#include <cmath>
+#include <cassert>
#include <algorithm>
#include "../utils/log.h"
#include "wave.h"
{
namespace
{
-void fadeFrame(Wave* w, int i, float val)
+void fadeFrame(Wave& w, int i, float val)
{
- float* frame = w->getFrame(i);
- for (int j=0; j<w->getChannels(); j++)
- frame[j] *= val;
+ for (int j=0; j<w.getChannels(); j++)
+ w[i][j] *= val;
}
/* -------------------------------------------------------------------------- */
-float getPeak(Wave* w, int a, int b)
+float getPeak(const Wave& w, int a, int b)
{
float peak = 0.0f;
float abs = 0.0f;
for (int i=a; i<b; i++) {
- float* frame = w->getFrame(i);
- for (int j=0; j<w->getChannels(); j++) // Find highest value in any channel
- abs = fabs(frame[j]);
+ for (int j=0; j<w.getChannels(); j++) // Find highest value in any channel
+ abs = fabs(w[i][j]);
if (abs > peak)
peak = abs;
}
/* -------------------------------------------------------------------------- */
-float normalizeSoft(Wave* w)
+float normalizeSoft(const Wave& w)
{
- float peak = getPeak(w, 0, w->getSize());
+ float peak = getPeak(w, 0, w.getSize());
/* peak == 0.0f: don't normalize the silence
* peak > 1.0f: don't reduce the amplitude, just leave it alone */
/* -------------------------------------------------------------------------- */
-void normalizeHard(Wave* w, int a, int b)
+void normalizeHard(Wave& w, int a, int b)
{
float peak = getPeak(w, a, b);
if (peak == 0.0f || peak > 1.0f) // as in ::normalizeSoft
return;
for (int i=a; i<b; i++) {
- float* frame = w->getFrame(i);
- for (int j=0; j<w->getChannels(); j++)
- frame[j] = frame[j] * (1.0f / peak);
+ for (int j=0; j<w.getChannels(); j++)
+ w[i][j] = w[i][j] * (1.0f / peak);
}
- w->setEdited(true);
+ w.setEdited(true);
}
/* -------------------------------------------------------------------------- */
-int monoToStereo(Wave* w)
+int monoToStereo(Wave& w)
{
- if (w->getChannels() >= G_DEFAULT_AUDIO_CHANS)
+ if (w.getChannels() >= G_MAX_IO_CHANS)
return G_RES_OK;
- unsigned newSize = w->getSize() * G_DEFAULT_AUDIO_CHANS;
-
- float* newData = new (std::nothrow) float[newSize];
- if (newData == nullptr) {
+ AudioBuffer newData;
+ if (!newData.alloc(w.getSize(), G_MAX_IO_CHANS)) {
gu_log("[wfx::monoToStereo] unable to allocate memory!\n");
return G_RES_ERR_MEMORY;
}
- for (int i=0, k=0; i<w->getSize(); i++, k+=G_DEFAULT_AUDIO_CHANS) {
- float* frame = w->getFrame(i);
- for (int j=0; j<G_DEFAULT_AUDIO_CHANS; j++)
- newData[k+j] = frame[j];
- }
+ for (int i=0; i<newData.countFrames(); i++)
+ for (int j=0; j<newData.countChannels(); j++)
+ newData[i][j] = w[i][0];
- w->free();
- w->setData(newData, newSize);
- w->setChannels(G_DEFAULT_AUDIO_CHANS);
+ w.moveData(newData);
return G_RES_OK;
}
/* -------------------------------------------------------------------------- */
-void silence(Wave* w, int a, int b)
+void silence(Wave& w, int a, int b)
{
gu_log("[wfx::silence] silencing from %d to %d\n", a, b);
for (int i=a; i<b; i++) {
- float* frame = w->getFrame(i);
- for (int j=0; j<w->getChannels(); j++)
- frame[j] = 0.0f;
+ for (int j=0; j<w.getChannels(); j++)
+ w[i][j] = 0.0f;
}
- w->setEdited(true);
+ w.setEdited(true);
}
/* -------------------------------------------------------------------------- */
-int cut(Wave* w, int a, int b)
+int cut(Wave& w, int a, int b)
{
if (a < 0) a = 0;
- if (b > w->getSize()) b = w->getSize();
+ if (b > w.getSize()) b = w.getSize();
/* Create a new temp wave and copy there the original one, skipping the a-b
range. */
- unsigned newSize = (w->getSize() - (b - a)) * w->getChannels();
- float* newData = new (std::nothrow) float[newSize];
- if (newData == nullptr) {
+ int newSize = w.getSize() - (b - a);
+
+ AudioBuffer newData;
+ if (!newData.alloc(newSize, w.getChannels())) {
gu_log("[wfx::cut] unable to allocate memory!\n");
return G_RES_ERR_MEMORY;
}
gu_log("[wfx::cut] cutting from %d to %d\n", a, b);
- for (int i=0, k=0; i<w->getSize(); i++) {
+ for (int i=0, k=0; i<w.getSize(); i++) {
if (i < a || i >= b) {
- float* frame = w->getFrame(i);
- for (int j=0; j<w->getChannels(); j++)
- newData[k+j] = frame[j];
- k += w->getChannels();
+ for (int j=0; j<w.getChannels(); j++)
+ newData[k][j] = w[i][j];
+ k++;
}
}
- w->free();
- w->setData(newData, newSize);
- w->setEdited(true);
+ w.moveData(newData);
+ w.setEdited(true);
return G_RES_OK;
}
/* -------------------------------------------------------------------------- */
-int trim(Wave* w, int a, int b)
+int trim(Wave& w, int a, int b)
{
if (a < 0) a = 0;
- if (b > w->getSize()) b = w->getSize();
+ if (b > w.getSize()) b = w.getSize();
- int newSize = (b - a) * w->getChannels();
- float* newData = new (std::nothrow) float[newSize];
- if (newData == nullptr) {
+ int newSize = b - a;
+
+ AudioBuffer newData;
+ if (!newData.alloc(newSize, w.getChannels())) {
gu_log("[wfx::trim] unable to allocate memory!\n");
return G_RES_ERR_MEMORY;
}
gu_log("[wfx::trim] trimming from %d to %d (area = %d)\n", a, b, b-a);
- for (int i=a, k=0; i<b; i++, k+=w->getChannels()) {
- float* frame = w->getFrame(i);
- for (int j=0; j<w->getChannels(); j++)
- newData[k+j] = frame[j];
- }
+ for (int i=0; i<newData.countFrames(); i++)
+ for (int j=0; j<newData.countChannels(); j++)
+ newData[i][j] = w[i+a][j];
- w->free();
- w->setData(newData, newSize);
- w->setEdited(true);
+ w.moveData(newData);
+ w.setEdited(true);
return G_RES_OK;
}
/* -------------------------------------------------------------------------- */
-int paste(Wave* src, Wave* des, int aFrame)
+int paste(const Wave& src, Wave& des, int a)
{
- int srcNumSamples = src->getSize() * src->getChannels();
- int desNumSamples = des->getSize() * des->getChannels();
- int aSample = aFrame * des->getChannels();
+ assert(src.getChannels() == des.getChannels());
- int newSize = srcNumSamples + desNumSamples;
- float* newData = new (std::nothrow) float[newSize];
- if (newData == nullptr) {
+ AudioBuffer newData;
+ if (!newData.alloc(src.getSize() + des.getSize(), des.getChannels())) {
gu_log("[wfx::paste] unable to allocate memory!\n");
return G_RES_ERR_MEMORY;
}
/* |---original data---|///paste data///|---original data---|
- chunk 1 chunk 2 chunk 3
- */
-
- float* chunk1a = des->getData();
- float* chunk1b = des->getData() + aSample;
-
- float* chunk2a = src->getData();
- float* chunk2b = src->getData() + srcNumSamples;
-
- float* chunk3a = chunk1b;
- float* chunk3b = des->getData() + desNumSamples;
+ des[0, a) src[0, src.size) des[a, des.size) */
- std::copy(chunk1a, chunk1b, newData);
- std::copy(chunk2a, chunk2b, newData + aSample);
- std::copy(chunk3a, chunk3b, newData + aSample + srcNumSamples);
+ newData.copyData(des[0], a, 0);
+ newData.copyData(src[0], src.getSize(), a);
+ newData.copyData(des[a], des.getSize() - a, src.getSize() + a);
- des->free();
- des->setData(newData, newSize);
- des->setEdited(true);
+ des.moveData(newData);
+ des.setEdited(true);
return G_RES_OK;
}
/* -------------------------------------------------------------------------- */
-void fade(Wave* w, int a, int b, int type)
+void fade(Wave& w, int a, int b, int type)
{
gu_log("[wfx::fade] fade from %d to %d (range = %d)\n", a, b, b-a);
for (int i=b; i>=a; i--, m+=d)
fadeFrame(w, i, m);
- w->setEdited(true);
+ w.setEdited(true);
}
/* -------------------------------------------------------------------------- */
-void smooth(Wave* w, int a, int b)
+void smooth(Wave& w, int a, int b)
{
/* Do nothing if fade edges (both of SMOOTH_SIZE samples) are > than selected
portion of wave. SMOOTH_SIZE*2 to count both edges. */
fade(w, a, a+SMOOTH_SIZE, FADE_IN);
fade(w, b-SMOOTH_SIZE, b, FADE_OUT);
- w->setEdited(true);
+ w.setEdited(true);
}
/* -------------------------------------------------------------------------- */
-void shift(Wave* w, int offset)
+void shift(Wave& w, int offset)
{
if (offset < 0)
- offset = (w->getSize() + w->getChannels()) + offset;
+ offset = (w.getSize() + w.getChannels()) + offset;
- float* begin = w->getData();
- float* end = w->getData() + (w->getSize() * w->getChannels());
+ float* begin = w.getFrame(0);
+ float* end = w.getFrame(0) + (w.getSize() * w.getChannels());
- std::rotate(begin, end - (offset * w->getChannels()), end);
- w->setEdited(true);
+ std::rotate(begin, end - (offset * w.getChannels()), end);
+ w.setEdited(true);
}
/* -------------------------------------------------------------------------- */
-void reverse(Wave* w, int a, int b)
+void reverse(Wave& w, int a, int b)
{
/* https://stackoverflow.com/questions/33201528/reversing-an-array-of-structures-in-c */
- float* begin = w->getData() + (a * w->getChannels());
- float* end = w->getData() + (b * w->getChannels());
+ float* begin = w.getFrame(0) + (a * w.getChannels());
+ float* end = w.getFrame(0) + (b * w.getChannels());
std::reverse(begin, end);
- w->setEdited(true);
+ w.setEdited(true);
}
}}}; // giada::m::wfx::
\ No newline at end of file
/* normalizeSoft
Normalizes the wave by returning the dB value for the boost volume. */
-float normalizeSoft(Wave* w);
+float normalizeSoft(const Wave& w);
/* normalizeHard
Normalizes the wave in range a-b by altering values in memory. */
-void normalizeHard(Wave* w, int a, int b);
+void normalizeHard(Wave& w, int a, int b);
-int monoToStereo(Wave* w);
-void silence(Wave* w, int a, int b);
-int cut(Wave* w, int a, int b);
-int trim(Wave* w, int a, int b);
-int paste(Wave* src, Wave* dest, int a);
+int monoToStereo(Wave& w);
+void silence(Wave& w, int a, int b);
+int cut(Wave& w, int a, int b);
+int trim(Wave& w, int a, int b);
+
+/* paste
+Pastes Wave 'src' into Wave 'dest', starting from frame 'a'. */
+
+int paste(const Wave& src, Wave& dest, int a);
/* fade
Fades in or fades out selection. Fade In = type 0, Fade Out = type 1 */
-void fade(Wave* w, int a, int b, int type);
+void fade(Wave& w, int a, int b, int type);
/* smooth
Smooth edges of selection. */
-void smooth(Wave* w, int a, int b);
+void smooth(Wave& w, int a, int b);
/* reverse
Flips Wave's data. */
-void reverse(Wave* w, int a, int b);
+void reverse(Wave& w, int a, int b);
-void shift(Wave* w, int offset);
+void shift(Wave& w, int offset);
}}}; // giada::m::wfx::
return G_RES_ERR_IO;
}
- if (header.channels > 2) {
+ if (header.channels > G_MAX_IO_CHANS) {
gu_log("[waveManager::create] unsupported multi-channel sample\n");
return G_RES_ERR_WRONG_DATA;
}
- /* Libsndfile's frame structure:
-
- frame1 = [leftChannel, rightChannel]
- frame2 = [leftChannel, rightChannel]
- ... */
-
- int size = header.frames * header.channels;
- float* data = new (std::nothrow) float[size];
- if (data == nullptr) {
+ Wave* wave = new Wave();
+ if (!wave->alloc(header.frames, header.channels, header.samplerate, getBits(header), path)) {
gu_log("[waveManager::create] unable to allocate memory\n");
+ delete wave;
return G_RES_ERR_MEMORY;
}
- if (sf_read_float(fileIn, data, size) != size)
+ if (sf_readf_float(fileIn, wave->getFrame(0), header.frames) != header.frames)
gu_log("[waveManager::create] warning: incomplete read!\n");
sf_close(fileIn);
- Wave* wave = new Wave(data, size, header.channels, header.samplerate,
- getBits(header), path);
-
- if (header.channels == 1 && !wfx::monoToStereo(wave)) {
+ if (header.channels == 1 && !wfx::monoToStereo(*wave)) {
delete wave;
return G_RES_ERR_PROCESSING;
}
/* -------------------------------------------------------------------------- */
-int createEmpty(int size, int samplerate, const string& name, Wave** out)
+int createEmpty(int frames, int channels, int samplerate, const string& name,
+ Wave** out)
{
- float* data = new (std::nothrow) float[size];
- if (data == nullptr) {
+ Wave* wave = new Wave();
+ if (!wave->alloc(frames, channels, samplerate, G_DEFAULT_BIT_DEPTH, name)) {
gu_log("[waveManager::createEmpty] unable to allocate memory\n");
+ delete wave;
return G_RES_ERR_MEMORY;
}
- Wave* wave = new Wave(data, size, G_DEFAULT_AUDIO_CHANS, samplerate,
- G_DEFAULT_BIT_DEPTH, "");
wave->setLogical(true);
*out = wave;
- gu_log("[waveManager::createEmpty] new empty Wave created, %d frames\n", size);
+ gu_log("[waveManager::createEmpty] new empty Wave created, %d frames\n",
+ wave->getSize());
return G_RES_OK;
}
int createFromWave(const Wave* src, int a, int b, Wave** out)
{
- int numChans = src->getChannels();
- int size = (b - a) * numChans;
- float* data = new (std::nothrow) float[size];
- if (data == nullptr) {
+ int channels = src->getChannels();
+ int frames = b - a;
+
+ Wave* wave = new Wave();
+ if (!wave->alloc(frames, channels, src->getRate(), src->getBits(), src->getPath())) {
gu_log("[waveManager::createFromWave] unable to allocate memory\n");
+ delete wave;
return G_RES_ERR_MEMORY;
}
- std::copy(src->getData() + (a*numChans), src->getData() + (b*numChans), data);
-
- Wave* wave = new Wave(data, size, numChans, src->getRate(),
- src->getBits(), src->getPath());
+ wave->copyData(src->getFrame(a), frames);
wave->setLogical(true);
*out = wave;
- gu_log("[waveManager::createFromWave] new Wave created, %d frames\n", size);
+ gu_log("[waveManager::createFromWave] new Wave created, %d frames\n", frames);
return G_RES_OK;
}
int resample(Wave* w, int quality, int samplerate)
{
float ratio = samplerate / (float) w->getRate();
- int newSizeFrames = ceil(w->getSize() * ratio);
- int newSizeSamples = newSizeFrames * w->getChannels();
+ int newSizeFrames = ceil(w->getSize() * ratio);
- float* newData = new (std::nothrow) float[newSizeSamples];
- if (newData == nullptr) {
+ AudioBuffer newData;
+ if (!newData.alloc(newSizeFrames, w->getChannels())) {
gu_log("[waveManager::resample] unable to allocate memory\n");
return G_RES_ERR_MEMORY;
}
SRC_DATA src_data;
- src_data.data_in = w->getData();
+ src_data.data_in = w->getFrame(0);
src_data.input_frames = w->getSize();
- src_data.data_out = newData;
+ src_data.data_out = newData[0];
src_data.output_frames = newSizeFrames;
src_data.src_ratio = ratio;
- gu_log("[waveManager::resample] resampling: new size=%d (%d frames)\n",
- newSizeSamples, newSizeFrames);
+ gu_log("[waveManager::resample] resampling: new size=%d frames\n", newSizeFrames);
int ret = src_simple(&src_data, quality, w->getChannels());
if (ret != 0) {
gu_log("[waveManager::resample] resampling error: %s\n", src_strerror(ret));
- delete[] newData;
return G_RES_ERR_PROCESSING;
}
- w->free();
- w->setData(newData, newSizeSamples);
+ w->moveData(newData);
w->setRate(samplerate);
return G_RES_OK;
return G_RES_ERR_IO;
}
- if (sf_writef_float(file, w->getData(), w->getSize()) != w->getSize())
+ if (sf_writef_float(file, w->getFrame(0), w->getSize()) != w->getSize())
gu_log("[waveManager::save] warning: incomplete write!\n");
sf_close(file);
int create(const std::string& path, Wave** out);
/* createEmpty
-Creates a new silent Wave object. Note: 'size' must take 2 channels into account
-(stereo). */
+Creates a new silent Wave object. */
-int createEmpty(int size, int samplerate, const std::string& name, Wave** out);
+int createEmpty(int frames, int channels, int samplerate, const std::string& name,
+ Wave** out);
/* createFromWave
Creates a new Wave from an existing one, copying the data in range a - b. */
namespace c {
namespace channel
{
-namespace
-{
-bool soloSession__ = false;
-} // {anonymous}
-
-
-/* -------------------------------------------------------------------------- */
-/* -------------------------------------------------------------------------- */
-/* -------------------------------------------------------------------------- */
-
-
int loadChannel(SampleChannel* ch, const string& fname)
{
using namespace giada::m;
void toggleArm(Channel* ch, bool gui)
{
- ch->setArmed(!ch->isArmed());
+ ch->armed = !ch->armed;
if (!gui)
- ch->guiChannel->arm->value(ch->isArmed());
+ ch->guiChannel->arm->value(ch->armed);
}
void setVolume(Channel* ch, float v, bool gui, bool editor)
{
- ch->setVolume(v);
+ ch->volume = v;
/* Changing channel volume? Update wave editor (if it's shown). */
ch->readActions = false; // don't read actions while overdubbing
}
else
- recorder::stopOverdub(clock::getCurrentFrame(), clock::getTotalFrames(),
+ recorder::stopOverdub(clock::getCurrentFrame(), clock::getFramesInLoop(),
&mixer::mutex_recs);
}
void toggleSolo(Channel* ch, bool gui)
-{
- ch->solo ? setSoloOn(ch, gui) : setSoloOff(ch, gui);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void kill(Channel* ch)
-{
- ch->kill(0); // on frame 0: it's a user-generated event
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void setSoloOn(Channel* ch, bool gui)
{
using namespace giada::m;
-
- /* if there's no solo session, store mute configuration of all chans
- * and start the session */
-
- if (!soloSession__) {
- for (unsigned i=0; i<mixer::channels.size(); i++) {
- Channel *och = mixer::channels.at(i);
- och->mute_s = och->mute;
- }
- soloSession__ = true;
- }
-
+
ch->solo = !ch->solo;
- ch->sendMidiLsolo();
-
- /* mute all other channels and unmute this (if muted) */
-
- for (unsigned i=0; i<mixer::channels.size(); i++) {
- Channel *och = mixer::channels.at(i);
- if (!och->solo && !och->mute) {
- och->setMute(false);
- Fl::lock();
- och->guiChannel->mute->value(true);
- Fl::unlock();
- }
- }
-
- if (ch->mute) {
- ch->unsetMute(false);
- Fl::lock();
- ch->guiChannel->mute->value(false);
- Fl::unlock();
- }
-
+ mh::updateSoloCount();
+
if (!gui) {
Fl::lock();
- ch->guiChannel->solo->value(1);
+ ch->guiChannel->solo->value(ch->solo);
Fl::unlock();
}
}
/* -------------------------------------------------------------------------- */
-void setSoloOff(Channel* ch, bool gui)
+void kill(Channel* ch)
{
- using namespace giada::m;
-
- /* if this is uniqueSolo, stop solo session and restore mute status,
- * else mute this */
-
- if (mh::uniqueSolo(ch)) {
- soloSession__ = false;
- for (unsigned i=0; i<mixer::channels.size(); i++) {
- Channel *och = mixer::channels.at(i);
- if (och->mute_s) {
- och->setMute(false);
- Fl::lock();
- och->guiChannel->mute->value(true);
- Fl::unlock();
- }
- else {
- och->unsetMute(false);
- Fl::lock();
- och->guiChannel->mute->value(false);
- Fl::unlock();
- }
- och->mute_s = false;
- }
- }
- else {
- ch->setMute(false);
- Fl::lock();
- ch->guiChannel->mute->value(true);
- Fl::unlock();
- }
-
- ch->solo = !ch->solo;
- ch->sendMidiLsolo();
-
- if (!gui) {
- Fl::lock();
- ch->guiChannel->solo->value(0);
- Fl::unlock();
- }
+ ch->kill(0); // on frame 0: it's a user-generated event
}
void setName(Channel* ch, const string& name)
{
- ch->setName(name);
+ ch->name = name;
ch->guiChannel->update();
}
void toggleInputMonitor(Channel* ch);
void kill(Channel* ch);
void toggleMute(Channel* ch, bool gui=true);
-void setSoloOn(Channel* ch, bool gui=true);
-void setSoloOff(Channel* ch, bool gui=true);
void toggleSolo(Channel* ch, bool gui=true);
void setVolume(Channel* ch, float v, bool gui=true, bool editor=false);
void setName(Channel* ch, const std::string& name);
void keyPress(Channel* ch, bool ctrl, bool shift, int velocity)
{
- if (ch->type == CHANNEL_SAMPLE)
+ if (ch->type == G_CHANNEL_SAMPLE)
keyPress(static_cast<SampleChannel*>(ch), ctrl, shift, velocity);
else
keyPress(static_cast<MidiChannel*>(ch), ctrl, shift);
void keyRelease(Channel* ch, bool ctrl, bool shift)
{
- if (ch->type == CHANNEL_SAMPLE)
+ if (ch->type == G_CHANNEL_SAMPLE)
keyRelease(static_cast<SampleChannel*>(ch), ctrl, shift);
}
* other mode the KEY REL is meaningless. */
if (ch->mode == SINGLE_PRESS && recorder::canRec(ch, clock::isRunning(), mixer::recording))
- recorder::stopOverdub(clock::getCurrentFrame(), clock::getTotalFrames(),
+ recorder::stopOverdub(clock::getCurrentFrame(), clock::getFramesInLoop(),
&mixer::mutex_recs);
/* the GUI update is done by gui_refresh() */
for (Channel* ch : m::mixer::channels)
{
- if (ch->type == CHANNEL_MIDI)
+ if (ch->type == G_CHANNEL_MIDI)
continue;
SampleChannel* sch = static_cast<SampleChannel*>(ch);
G_MainWin->keyboard->setChannelWithActions(static_cast<geSampleChannel*>(sch->guiChannel));
beat. */
for (Channel* ch : mixer::channels) {
- if (ch->type == CHANNEL_MIDI)
+ if (ch->type == G_CHANNEL_MIDI)
continue;
SampleChannel* sch = static_cast<SampleChannel*>(ch);
- if (sch->mode & (LOOP_ANY) && sch->status == STATUS_OFF && sch->isArmed())
+ if (sch->mode & (LOOP_ANY) && sch->status == STATUS_OFF && sch->armed)
sch->start(clock::getCurrentFrame(), true, clock::getQuantize(),
clock::isRunning(), true, true);
}
float oldBpmF = clock::getBpm();
clock::setBpm(bpmF);
recorder::updateBpm(oldBpmF, bpmF, clock::getQuanto());
- mixer::allocVirtualInput(clock::getTotalFrames());
+ mixer::allocVirtualInput(clock::getFramesInLoop());
#ifdef __linux__
kernelAudio::jackSetBpm(clock::getBpm());
/* Temp vars to store old data (they are necessary) */
int oldBeats = clock::getBeats();
- unsigned oldTotalFrames = clock::getTotalFrames();
+ unsigned oldTotalFrames = clock::getFramesInLoop();
clock::setBeats(beats);
clock::setBars(bars);
clock::updateFrameBars();
- mixer::allocVirtualInput(clock::getTotalFrames());
+ mixer::allocVirtualInput(clock::getFramesInLoop());
/* Update recorded actions, if 'expand' required and an expansion is taking
place. */
if (expand && clock::getBeats() > oldBeats)
- recorder::expand(oldTotalFrames, clock::getTotalFrames());
+ recorder::expand(oldTotalFrames, clock::getFramesInLoop());
G_MainWin->mainTimer->setMeter(clock::getBeats(), clock::getBars());
gu_refreshActionEditor(); // in case the action editor is open
void glue_clearAllSamples()
{
clock::stop();
- for (unsigned i=0; i<mixer::channels.size(); i++) {
- mixer::channels.at(i)->empty();
- mixer::channels.at(i)->guiChannel->reset();
+ for (Channel* ch : mixer::channels) {
+ ch->empty();
+ ch->guiChannel->reset();
}
recorder::init();
return;
/* -------------------------------------------------------------------------- */
-void glue_clearAllRecs()
+void glue_clearAllActions()
{
recorder::init();
+ for (Channel* ch : mixer::channels)
+ ch->hasActions = false;
gu_updateControls();
}
gu_closeAllSubwindows();
mixer::close();
clock::init(conf::samplerate, conf::midiTCfps);
- mixer::init(clock::getTotalFrames(), kernelAudio::getRealBufSize());
+ mixer::init(clock::getFramesInLoop(), kernelAudio::getRealBufSize());
recorder::init();
#ifdef WITH_VST
pluginHost::freeAllStacks(&mixer::channels, &mixer::mutex_plugins);
#define G_GLUE_MAIN_H
-void glue_setBpm(const char *v1, const char *v2);
+void glue_setBpm(const char* v1, const char* v2);
void glue_setBpm(float v);
void glue_setBeats(int beats, int bars, bool expand);
void glue_quantize(int val);
void glue_setOutVol(float v, bool gui=true);
void glue_setInVol(float v, bool gui=true);
void glue_clearAllSamples();
-void glue_clearAllRecs();
+void glue_clearAllActions();
/* resetToInitState
* reset Giada to init state. If resetGui also refresh all widgets. If
void updateChannel(geChannel* gch)
{
gch->ch->hasActions = m::recorder::hasActions(gch->ch->index);
- if (gch->ch->type == CHANNEL_SAMPLE && !gch->ch->hasActions)
+ if (gch->ch->type == G_CHANNEL_SAMPLE && !gch->ch->hasActions)
static_cast<geSampleChannel*>(gch)->hideActionButton();
/* TODO - set mute=false */
gu_refreshActionEditor(); // refresh a.editor window, it could be open
/* Avoid frame overflow. */
- int overflow = frame_b - m::clock::getTotalFrames();
+ int overflow = frame_b - (m::clock::getFramesInLoop());
if (overflow > 0) {
frame_b -= overflow;
frame_a -= overflow;
namespace c {
namespace recorder
{
-void clearAllActions(geChannel *gch);
-void clearMuteActions(geChannel *gch);
-void clearVolumeActions(geChannel *gch);
-void clearStartStopActions(geChannel *gch);
+void clearAllActions(geChannel* gch);
+void clearMuteActions(geChannel* gch);
+void clearVolumeActions(geChannel* gch);
+void clearStartStopActions(geChannel* gch);
/* recordMidiAction
Records a new MIDI action at frame_a. If frame_b == 0, uses the default action
void cut(SampleChannel* ch, int a, int b)
{
copy(ch, a, b);
- if (!m::wfx::cut(ch->wave, a, b)) {
+ if (!m::wfx::cut(*ch->wave, a, b)) {
gdAlert("Unable to cut the sample!");
return;
}
return;
}
- m::wfx::paste(m_waveBuffer, ch->wave, a);
+ m::wfx::paste(*m_waveBuffer, *ch->wave, a);
/* Shift begin/end points to keep the previous position. */
void silence(SampleChannel* ch, int a, int b)
{
- m::wfx::silence(ch->wave, a, b);
+ m::wfx::silence(*ch->wave, a, b);
gdSampleEditor* gdEditor = getSampleEditorWindow();
gdEditor->waveTools->waveform->refresh();
}
void fade(SampleChannel* ch, int a, int b, int type)
{
- m::wfx::fade(ch->wave, a, b, type);
+ m::wfx::fade(*ch->wave, a, b, type);
gdSampleEditor* gdEditor = getSampleEditorWindow();
gdEditor->waveTools->waveform->refresh();
}
void smoothEdges(SampleChannel* ch, int a, int b)
{
- m::wfx::smooth(ch->wave, a, b);
+ m::wfx::smooth(*ch->wave, a, b);
gdSampleEditor* gdEditor = getSampleEditorWindow();
gdEditor->waveTools->waveform->refresh();
}
void reverse(SampleChannel* ch, int a, int b)
{
- m::wfx::reverse(ch->wave, a, b);
+ m::wfx::reverse(*ch->wave, a, b);
gdSampleEditor* gdEditor = getSampleEditorWindow();
gdEditor->waveTools->waveform->refresh();
}
void normalizeHard(SampleChannel* ch, int a, int b)
{
- m::wfx::normalizeHard(ch->wave, a, b);
+ m::wfx::normalizeHard(*ch->wave, a, b);
gdSampleEditor* gdEditor = getSampleEditorWindow();
gdEditor->waveTools->waveform->refresh();
}
void trim(SampleChannel* ch, int a, int b)
{
- if (!m::wfx::trim(ch->wave, a, b)) {
+ if (!m::wfx::trim(*ch->wave, a, b)) {
gdAlert("Unable to trim the sample!");
return;
}
void setPlayHead(SampleChannel* ch, int f)
{
- ch->setTrackerPreview(f);
+ ch->trackerPreview = f;
gdSampleEditor* gdEditor = getSampleEditorWindow();
gdEditor->waveTools->waveform->redraw();
}
void rewindPreview(SampleChannel* ch)
{
geWaveform* waveform = getSampleEditorWindow()->waveTools->waveform;
- if (waveform->isSelected() && ch->getTrackerPreview() != waveform->getSelectionA())
+ if (waveform->isSelected() && ch->trackerPreview != waveform->getSelectionA())
setPlayHead(ch, waveform->getSelectionA());
else
setPlayHead(ch, 0);
void toNewChannel(SampleChannel* ch, int a, int b)
{
SampleChannel* newCh = static_cast<SampleChannel*>(c::channel::addChannel(
- ch->guiChannel->getColumnIndex(), CHANNEL_SAMPLE, G_GUI_CHANNEL_H_1));
+ ch->guiChannel->getColumnIndex(), G_CHANNEL_SAMPLE, G_GUI_CHANNEL_H_1));
Wave* wave = nullptr;
int result = m::waveManager::createFromWave(ch->wave, a, b, &wave);
void shift(SampleChannel* ch, int offset)
{
- m::wfx::shift(ch->wave, offset - ch->getShift());
- ch->setShift(offset);
+ m::wfx::shift(*ch->wave, offset - ch->shift);
+ ch->shift = offset;
gdSampleEditor* gdEditor = getSampleEditorWindow();
gdEditor->shiftTool->refresh();
gdEditor->waveTools->waveform->refresh();
for (const patch::channel_t& pch : patch::channels) {
if (pch.column == col.index) {
Channel* ch = c::channel::addChannel(pch.column, pch.type, pch.size);
- ch->readPatch(basePath, k, &mixer::mutex_plugins, conf::samplerate,
- conf::rsmpQuality);
+ ch->readPatch(basePath, k);
}
browser->setStatusBar(steps);
k++;
}
}
- /* Fill Mixer. */
+ /* Prepare Mixer. */
+ mh::updateSoloCount();
mh::readPatch();
/* Let recorder recompute the actions' positions if the current
for (const Channel* ch : mixer::channels) {
- if (ch->type == CHANNEL_MIDI)
+ if (ch->type == G_CHANNEL_MIDI)
continue;
const SampleChannel* sch = static_cast<const SampleChannel*>(ch);
/* -------------------------------------------------------------------------- */
-void glue_saveSample(void *data)
+void glue_saveSample(void* data)
{
using namespace giada::m;
m_cancel = new geButton(m_ok->x() - 70 - G_GUI_OUTER_MARGIN, m_ok->y(), 70, G_GUI_UNIT, "Cancel");
end();
- m_name->value(m_ch->getName().c_str());
+ m_name->value(m_ch->name.c_str());
m_ok->shortcut(FL_Enter);
m_ok->callback(cb_update, (void*)this);
zoom = conf::actionEditorZoom;
}
- totalWidth = (int) std::ceil(clock::getFramesInSequencer() / (float) zoom);
+ totalWidth = (int) std::ceil(clock::getFramesInSeq() / (float) zoom);
/* container with zoom buttons and the action type selector. Scheme of
* the resizable boxes: |[--b1--][actionType][--b2--][+][-]| */
upperArea->begin();
- if (chan->type == CHANNEL_SAMPLE) {
+ if (chan->type == G_CHANNEL_SAMPLE) {
actionType = new geChoice(8, 8, 80, 20);
gridTool = new geGridTool(actionType->x()+actionType->w()+4, 8, this);
actionType->add("key press");
scroller = new geScroll(8, 36, w()-16, h()-44);
- if (chan->type == CHANNEL_SAMPLE) {
+ if (chan->type == G_CHANNEL_SAMPLE) {
SampleChannel *ch = (SampleChannel*) chan;
update();
- if (chan->type == CHANNEL_SAMPLE) {
+ if (chan->type == G_CHANNEL_SAMPLE) {
ac->size(totalWidth, ac->h());
mc->size(totalWidth, mc->h());
vc->size(totalWidth, vc->h());
update();
- if (chan->type == CHANNEL_SAMPLE) {
+ if (chan->type == G_CHANNEL_SAMPLE) {
ac->size(totalWidth, ac->h());
mc->size(totalWidth, mc->h());
vc->size(totalWidth, vc->h());
void gdActionEditor::update()
{
- totalWidth = (int) ceilf(clock::getFramesInSequencer() / (float) zoom);
+ totalWidth = (int) ceilf(clock::getFramesInSeq() / (float) zoom);
if (totalWidth < scroller->w()) {
totalWidth = scroller->w();
- zoom = (int) ceilf(clock::getFramesInSequencer() / (float) totalWidth);
+ zoom = (int) ceilf(clock::getFramesInSeq() / (float) totalWidth);
scroller->scroll_to(0, scroller->yposition());
}
}
label(title.c_str());
size_range(G_DEFAULT_MIDI_INPUT_UI_W, G_DEFAULT_MIDI_INPUT_UI_H);
- int extra = ch->type == CHANNEL_SAMPLE ? 28 : 0;
+ int extra = ch->type == G_CHANNEL_SAMPLE ? 28 : 0;
Fl_Group* groupHeader = new Fl_Group(G_GUI_OUTER_MARGIN, G_GUI_OUTER_MARGIN, w(), 20 + extra);
groupHeader->begin();
enable->value(ch->midiIn);
enable->callback(cb_enable, (void*)this);
- if (ch->type == CHANNEL_SAMPLE) {
+ if (ch->type == G_CHANNEL_SAMPLE) {
veloAsVol->value(static_cast<SampleChannel*>(ch)->midiInVeloAsVol);
veloAsVol->callback(cb_veloAsVol, (void*)this);
}
channel->add("Channel 14");
channel->add("Channel 15");
channel->add("Channel 16");
- channel->value(ch->getMidiInFilter() == -1 ? 0 : ch->getMidiInFilter() + 1);
+ channel->value(ch->midiInFilter == -1 ? 0 : ch->midiInFilter + 1);
channel->callback(cb_setChannel, (void*)this);
resizable(container);
new geMidiLearner(0, 0, LEARNER_WIDTH, "mute", cb_learn, &ch->midiInMute, ch);
new geMidiLearner(0, 0, LEARNER_WIDTH, "solo", cb_learn, &ch->midiInSolo, ch);
new geMidiLearner(0, 0, LEARNER_WIDTH, "volume", cb_learn, &ch->midiInVolume, ch);
- if (ch->type == CHANNEL_SAMPLE) {
+ if (ch->type == G_CHANNEL_SAMPLE) {
new geMidiLearner(0, 0, LEARNER_WIDTH, "pitch", cb_learn,
&(static_cast<SampleChannel*>(ch))->midiInPitch, ch);
new geMidiLearner(0, 0, LEARNER_WIDTH, "read actions", cb_learn,
void gdMidiInputChannel::cb_setChannel()
{
- ch->setMidiInFilter(channel->value() == 0 ? -1 : channel->value() - 1);
- gu_log("[gdMidiInputChannel] Set MIDI channel to %d\n",
- ch->getMidiInFilter());
+ ch->midiInFilter = channel->value() == 0 ? -1 : channel->value() - 1;
+ gu_log("[gdMidiInputChannel] Set MIDI channel to %d\n", ch->midiInFilter);
}
gu_setFavicon(this);
set_non_modal();
- copy_label(ch->getName().c_str());
+ copy_label(ch->name.c_str());
size_range(720, 480);
if (conf::sampleEditorX)
play->callback(cb_togglePreview, (void*)this);
rewind->callback(cb_rewindPreview, (void*)this);
- ch->setOnEndPreviewCb([this] {
+ ch->onPreviewEnd = [this] {
play->value(0);
- });
+ };
return g;
}
void geAction::addAction()
{
- /* always check frame parity */
-
- if (frame_a % 2 != 0)
- frame_a++;
-
/* anatomy of an action
* ____[#######]_____ (a) is the left margin, G_ACTION_KEYPRESS. (b) is
* a b the right margin, the G_ACTION_KEYREL. This is the
find the other piece (namely frame_b)
- not of types G_ACTION_KEYPRESS | G_ACTION_KEYREL | G_ACTION_KILL */
- if ((action->chan != pParent->chan->index) ||
- (recorder::frames.at(i) > clock::getTotalFrames()) ||
+ if ((action->chan != pParent->chan->index) ||
+ (recorder::frames.at(i) > clock::getFramesInLoop()) ||
(action->type == G_ACTION_KILL && ch->mode == SINGLE_PRESS) ||
- (action->type == G_ACTION_KEYREL && ch->mode == SINGLE_PRESS) ||
+ (action->type == G_ACTION_KEYREL && ch->mode == SINGLE_PRESS) ||
(action->type & ~(G_ACTION_KEYPRESS | G_ACTION_KEYREL | G_ACTION_KILL))
)
continue;
if (points.size() == 0) {
addPoint(0, 0, 1.0f, 0, 1);
recorder::rec(pParent->chan->index, type, 0, 0, 1.0f);
- addPoint(clock::getTotalFrames(), 0, 1.0f, pParent->coverX, 1);
- recorder::rec(pParent->chan->index, type, clock::getTotalFrames(), 0, 1.0f);
+ addPoint(clock::getFramesInLoop(), 0, 1.0f, pParent->coverX, 1);
+ recorder::rec(pParent->chan->index, type, clock::getFramesInLoop(), 0, 1.0f);
pParent->chan->hasActions = true;
}
if (newFrame < 0)
newFrame = 0;
- else if (newFrame > clock::getTotalFrames())
- newFrame = clock::getTotalFrames();
+ else if (newFrame > clock::getFramesInLoop())
+ newFrame = clock::getFramesInLoop();
/* vertical line check */
* put a concentrate of each block (which is totalFrames / zoom) */
int j = 0;
- int fpgc = floor(clock::getFramesPerBeat() / getValue()); // frames per grid cell
+ int fpgc = floor(clock::getFramesInBeat() / getValue()); // frames per grid cell
for (int i=1; i<parent->totalWidth; i++) { // if i=0, step=0 -> useless cycle
int step = parent->zoom*i;
- while (j < step && j < clock::getTotalFrames()) {
+ while (j < step && j < clock::getFramesInLoop()) {
if (j % fpgc == 0) {
points.push_back(i);
frames.push_back(j);
}
- if (j % clock::getFramesPerBeat() == 0)
+ if (j % clock::getFramesInBeat() == 0)
beats.push_back(i);
- if (j % clock::getFramesPerBar() == 0 && i != 1)
+ if (j % clock::getFramesInBar() == 0 && i != 1)
bars.push_back(i);
- if (j == clock::getTotalFrames() - 1)
+ if (j == clock::getFramesInLoop() - 1)
parent->coverX = i;
j++;
}
/* avoid overflow: frame_b must be within the sequencer range. In that
* case shift the ON-OFF block */
- if (frame_b >= clock::getTotalFrames()) {
- frame_b = clock::getTotalFrames();
+ if (frame_b >= clock::getFramesInLoop()) {
+ frame_b = clock::getFramesInLoop();
frame_a = frame_b-2048;
}
clear();
- int channel = pParent->chan->index;
- int maxFrame = m::clock::getTotalFrames();
+ int channel = pParent->chan->index;
+ int maxSample = m::clock::getFramesInLoop();
- vector<Composite> actions = c::recorder::getMidiActions(channel, maxFrame);
+ vector<Composite> actions = c::recorder::getMidiActions(channel, maxSample);
for (Composite composite : actions)
{
m::MidiEvent e1 = composite.a1.iValue;
void geChannel::cb_solo()
{
- solo->value() ? c::channel::setSoloOn(ch) : c::channel::setSoloOff(ch);
+ c::channel::toggleSolo(ch);
}
fl_rectf(x()+1, y()+1, w()-2, h()-2, G_COLOR_GREY_2); // status empty
- if (mixer::recording && ch->isArmed())
+ if (mixer::recording && ch->armed)
fl_rectf(x()+1, y()+1, w()-2, h()-2, G_COLOR_RED); // take in progress
else
if (recorder::active && recorder::canRec(ch, clock::isRunning(), mixer::recording))
for (string& path : paths) {
gu_log("[geColumn::handle] loading %s...\n", path.c_str());
SampleChannel* c = static_cast<SampleChannel*>(c::channel::addChannel(
- m_index, CHANNEL_SAMPLE, G_GUI_CHANNEL_H_1));
+ m_index, G_CHANNEL_SAMPLE, G_GUI_CHANNEL_H_1));
result = c::channel::loadChannel(c, gu_stripFileUrl(path));
if (result != G_RES_OK) {
deleteChannel(c->guiChannel);
/* All geChannels are added with y=0. That's not a problem, they will be
repositioned later on during geColumn::resize(). */
- if (ch->type == CHANNEL_SAMPLE)
+ if (ch->type == G_CHANNEL_SAMPLE)
gch = new geSampleChannel(x(), 0, w(), size, static_cast<SampleChannel*>(ch));
else
gch = new geMidiChannel(x(), 0, w(), size, static_cast<MidiChannel*>(ch));
if (!m) return 0;
if (strcmp(m->label(), "Sample channel") == 0)
- return CHANNEL_SAMPLE;
+ return G_CHANNEL_SAMPLE;
if (strcmp(m->label(), "MIDI channel") == 0)
- return CHANNEL_MIDI;
+ return G_CHANNEL_MIDI;
return 0;
}
geMidiChannel::geMidiChannel(int X, int Y, int W, int H, MidiChannel* ch)
- : geChannel(X, Y, W, H, CHANNEL_MIDI, ch)
+ : geChannel(X, Y, W, H, G_CHANNEL_MIDI, ch)
{
begin();
const MidiChannel* mch = static_cast<const MidiChannel*>(ch);
string label;
- if (mch->getName().empty())
+ if (mch->name.empty())
label = "-- MIDI --";
else
- label = mch->getName().c_str();
+ label = mch->name.c_str();
if (mch->midiOut)
label += " (ch " + gu_iToString(mch->midiOutChan + 1) + " out)";
mainButton->label(label.c_str());
- vol->value(mch->getVolume());
+ vol->value(mch->volume);
mute->value(mch->mute);
solo->value(mch->solo);
mainButton->setKey(mch->key);
- arm->value(mch->isArmed());
+ arm->value(mch->armed);
#ifdef WITH_VST
fx->status = mch->plugins.size() > 0;
geSampleChannel::geSampleChannel(int X, int Y, int W, int H, SampleChannel* ch)
- : geChannel(X, Y, W, H, CHANNEL_SAMPLE, ch)
+ : geChannel(X, Y, W, H, G_CHANNEL_SAMPLE, ch)
{
begin();
setColorsByStatus(ch->status, ch->recStatus);
if (static_cast<SampleChannel*>(ch)->wave != nullptr) {
- if (m::mixer::recording && ch->isArmed())
+ if (m::mixer::recording && ch->armed)
mainButton->setInputRecordMode();
if (m::recorder::active) {
if (m::recorder::canRec(ch, m::clock::isRunning(), m::mixer::recording))
mainButton->label("* file not found! *");
break;
default:
- if (sch->getName().empty())
+ if (sch->name.empty())
mainButton->label(sch->wave->getBasename(false).c_str());
else
- mainButton->label(sch->getName().c_str());
+ mainButton->label(sch->name.c_str());
break;
}
modeBox->value(sch->mode);
modeBox->redraw();
- vol->value(sch->getVolume());
+ vol->value(sch->volume);
mute->value(sch->mute);
solo->value(sch->solo);
mainButton->setKey(sch->key);
- arm->value(sch->isArmed());
+ arm->value(sch->armed);
#ifdef WITH_VST
fx->status = sch->plugins.size() > 0;
break;
}
for (unsigned i=0; i<mixer::channels.size(); i++)
- if (mixer::channels.at(i)->type == CHANNEL_SAMPLE)
+ if (mixer::channels.at(i)->type == G_CHANNEL_SAMPLE)
if (((SampleChannel*)mixer::channels.at(i))->wave != nullptr) {
menu[0].activate();
break;
if (!gdConfirmWin("Warning", "Clear all actions: are you sure?"))
return;
G_MainWin->delSubWindow(WID_ACTION_EDITOR);
- glue_clearAllRecs();
+ glue_clearAllActions();
return;
}
if (strcmp(m->label(), "Reset to init state") == 0) {
{
using namespace giada;
- float val = m::wfx::normalizeSoft(ch->wave);
+ float val = m::wfx::normalizeSoft(*ch->wave);
c::channel::setBoost(ch, val); // it's like a fake user moving the dial
static_cast<gdSampleEditor*>(window())->waveTools->updateWaveform();
}
void gePitchTool::__cb_setPitchToBar()
{
// TODO - opaque channel's count
- c::channel::setPitch(ch, (ch->getEnd()*2) / (float) m::clock::getFramesPerBar());
+ c::channel::setPitch(ch, (ch->getEnd()) / (float) m::clock::getFramesInBar());
}
void gePitchTool::__cb_setPitchToSong()
{
// TODO - opaque channel's count
- c::channel::setPitch(ch, (ch->getEnd()*2) / (float) m::clock::getTotalFrames());
+ c::channel::setPitch(ch, ch->getEnd() / (float) m::clock::getFramesInLoop());
}
m_shift->type(FL_INT_INPUT);
m_shift->when(FL_WHEN_RELEASE | FL_WHEN_ENTER_KEY); // on focus lost or enter key
- m_shift->value(gu_iToString(ch->getShift()).c_str());
+ m_shift->value(gu_iToString(ch->shift).c_str());
m_shift->callback(cb_setShift, (void*)this);
m_reset->callback(cb_reset, (void*)this);
void geShiftTool::refresh()
{
- m_shift->value(gu_iToString(m_ch->getShift()).c_str());
+ m_shift->value(gu_iToString(m_ch->shift).c_str());
}
using namespace giada::u;
string tmp;
- float dB = math::linearToDB(ch->getVolume());
+ float dB = math::linearToDB(ch->volume);
if (dB > -INFINITY) tmp = gu_fToString(dB, 2); // 2 digits
else tmp = "-inf";
input->value(tmp.c_str());
void geWaveform::drawPlayHead()
{
- int p = frameToPixel(m_ch->getTrackerPreview()) + x();
+ int p = frameToPixel(m_ch->trackerPreview) + x();
fl_color(G_COLOR_LIGHT_2);
fl_line(p, y() + 1, p, y() + h() - 2);
}
void geWaveform::setZoom(int type)
{
- if (!alloc(type == ZOOM_IN ? m_data.size * 2 : m_data.size / 2))
+ if (!alloc(type == ZOOM_IN ? m_data.size * G_GUI_ZOOM_FACTOR : m_data.size / G_GUI_ZOOM_FACTOR))
return;
size(m_data.size, h());
void gu_updateControls()
{
- for (unsigned i=0; i<mixer::channels.size(); i++)
- mixer::channels.at(i)->guiChannel->update();
+ for (const Channel* ch : mixer::channels)
+ ch->guiChannel->update();
G_MainWin->mainIO->setOutVol(mixer::outVol);
G_MainWin->mainIO->setInVol(mixer::inVol);
/* -------------------------------------------------------------------------- */
-void gu_updateMainWinLabel(const string &s)
+void gu_updateMainWinLabel(const string& s)
{
std::string out = std::string(G_APP_NAME) + " - " + s;
G_MainWin->copy_label(out.c_str());
/* -------------------------------------------------------------------------- */
-void gu_setFavicon(Fl_Window *w)
+void gu_setFavicon(Fl_Window* w)
{
#if defined(__linux__)
/* -------------------------------------------------------------------------- */
-void gu_openSubWindow(gdWindow *parent, gdWindow *child, int id)
+void gu_openSubWindow(gdWindow* parent, gdWindow* child, int id)
{
if (parent->hasWindow(id)) {
gu_log("[GU] parent has subwindow with id=%d, deleting\n", id);
{
/** TODO - why don't we simply call WID_ACTION_EDITOR->redraw()? */
- gdActionEditor *aeditor = (gdActionEditor*) G_MainWin->getChild(WID_ACTION_EDITOR);
+ gdActionEditor* aeditor = (gdActionEditor*) G_MainWin->getChild(WID_ACTION_EDITOR);
if (aeditor) {
Channel *chan = aeditor->chan;
G_MainWin->delSubWindow(WID_ACTION_EDITOR);
--- /dev/null
+#include <memory>
+#include "../src/core/audioBuffer.h"
+#include <catch.hpp>
+
+
+TEST_CASE("Test AudioBuffer class")
+{
+ using namespace giada::m;
+
+ static const int BUFFER_SIZE = 4096;
+
+ /* Each SECTION the TEST_CASE is executed from the start. Any code between
+ this comment and the first SECTION macro is exectuted before each SECTION. */
+
+ AudioBuffer buffer;
+ buffer.alloc(BUFFER_SIZE, 2);
+
+ SECTION("test allocation")
+ {
+ SECTION("test mono")
+ {
+ REQUIRE(buffer.alloc(BUFFER_SIZE, 1) == 1);
+ REQUIRE(buffer.countFrames() == BUFFER_SIZE);
+ REQUIRE(buffer.countSamples() == BUFFER_SIZE);
+ REQUIRE(buffer.countChannels() == 1);
+ }
+
+ SECTION("test stereo")
+ {
+ REQUIRE(buffer.countFrames() == BUFFER_SIZE);
+ REQUIRE(buffer.countSamples() == BUFFER_SIZE * 2);
+ REQUIRE(buffer.countChannels() == 2);
+ }
+
+ SECTION("test odd channels count")
+ {
+ REQUIRE(buffer.alloc(BUFFER_SIZE, 7) == 1);
+ REQUIRE(buffer.countFrames() == BUFFER_SIZE);
+ REQUIRE(buffer.countSamples() == BUFFER_SIZE * 7);
+ REQUIRE(buffer.countChannels() == 7);
+ }
+
+ buffer.free();
+
+ REQUIRE(buffer[0] == nullptr);
+ REQUIRE(buffer.countFrames() == 0);
+ REQUIRE(buffer.countSamples() == 0);
+ REQUIRE(buffer.countChannels() == 0);
+ }
+
+ SECTION("test clear all")
+ {
+ buffer.clear();
+ for (int i=0; i<buffer.countFrames(); i++)
+ for (int k=0; k<buffer.countChannels(); k++)
+ REQUIRE(buffer[i][k] == 0.0f);
+ buffer.free();
+ }
+
+ SECTION("test clear range")
+ {
+ for (int i=0; i<buffer.countFrames(); i++)
+ for (int k=0; k<buffer.countChannels(); k++)
+ buffer[i][k] = 1.0f;
+
+ buffer.clear(5, 6);
+
+ for (int k=0; k<buffer.countChannels(); k++)
+ REQUIRE(buffer[5][k] == 0.0f);
+
+ buffer.free();
+ }
+
+ SECTION("test copy")
+ {
+ int size = BUFFER_SIZE * 2;
+ float* data = new float[size];
+ for (int i=0; i<size; i++)
+ data[i] = (float) i;
+
+ SECTION("test full copy")
+ {
+ buffer.copyData(data, BUFFER_SIZE);
+
+ REQUIRE(buffer[0][0] == 0.0f);
+ REQUIRE(buffer[16][0] == 32.0f);
+ REQUIRE(buffer[32][0] == 64.0f);
+ REQUIRE(buffer[1024][0] == 2048.0f);
+ }
+
+ SECTION("test copy frame")
+ {
+ buffer.copyFrame(16, &data[32]);
+ REQUIRE(buffer[16][0] == 32.0f);
+ REQUIRE(buffer[16][1] == 33.0f);
+ }
+
+ delete[] data;
+ }
+}
channel1.plugins.push_back(plugin2);
#endif
- channel1.type = CHANNEL_SAMPLE;
+ channel1.type = G_CHANNEL_SAMPLE;
channel1.index = 666;
channel1.size = G_GUI_CHANNEL_H_1;
channel1.column = 0;
REQUIRE(column0.width == 500);
patch::channel_t channel0 = patch::channels.at(0);
- REQUIRE(channel0.type == CHANNEL_SAMPLE);
+ REQUIRE(channel0.type == G_CHANNEL_SAMPLE);
REQUIRE(channel0.index == 666);
REQUIRE(channel0.size == G_GUI_CHANNEL_H_1);
REQUIRE(channel0.column == 0);
/* Each SECTION the TEST_CASE is executed from the start. Any code between
this comment and the first SECTION macro is exectuted before each SECTION. */
- std::unique_ptr<Wave> wave;
-
- SECTION("test basename")
- {
- wave = std::unique_ptr<Wave>(new Wave(nullptr, BUFFER_SIZE, CHANNELS,
- SAMPLE_RATE, BIT_DEPTH, "path/to/sample.wav"));
-
- REQUIRE(wave->getPath() == "path/to/sample.wav");
- REQUIRE(wave->getBasename() == "sample");
- REQUIRE(wave->getBasename(true) == "sample.wav");
- }
- SECTION("test path")
+ SECTION("test allocation")
{
- wave = std::unique_ptr<Wave>(new Wave(nullptr, BUFFER_SIZE, CHANNELS,
- SAMPLE_RATE, BIT_DEPTH, "path/to/sample.wav"));
-
- wave->setPath("path/is/now/different.mp3");
+ Wave wave;
- REQUIRE(wave->getPath() == "path/is/now/different.mp3");
+ REQUIRE(wave.alloc(BUFFER_SIZE, CHANNELS, SAMPLE_RATE, BIT_DEPTH, "path/to/sample.wav") == true);
- wave->setPath("path/is/now/different.mp3", 5);
+ SECTION("test basename")
+ {
+ REQUIRE(wave.getPath() == "path/to/sample.wav");
+ REQUIRE(wave.getBasename() == "sample");
+ REQUIRE(wave.getBasename(true) == "sample.wav");
+ }
- REQUIRE(wave->getPath() == "path/is/now/different-5.mp3");
- }
+ SECTION("test path")
+ {
+ wave.setPath("path/is/now/different.mp3");
- SECTION("test change name")
- {
- wave = std::unique_ptr<Wave>(new Wave(nullptr, BUFFER_SIZE, CHANNELS,
- SAMPLE_RATE, BIT_DEPTH, "path/to/sample.wav"));
-
- REQUIRE(wave->getPath() == "path/to/sample.wav");
- REQUIRE(wave->getBasename() == "sample");
- REQUIRE(wave->getBasename(true) == "sample.wav");
- }
+ REQUIRE(wave.getPath() == "path/is/now/different.mp3");
- SECTION("test memory cleanup")
- {
- float* data = new float[BUFFER_SIZE];
+ wave.setPath("path/is/now/different.mp3", 5);
- wave = std::unique_ptr<Wave>(new Wave(data, BUFFER_SIZE, CHANNELS,
- SAMPLE_RATE, BIT_DEPTH, "path/to/sample.wav"));
- wave->clear();
+ REQUIRE(wave.getPath() == "path/is/now/different-5.mp3");
+ }
- REQUIRE(wave->getData() == nullptr);
- REQUIRE(wave->getPath() == "");
- REQUIRE(wave->getSize() == 0);
+ SECTION("test change name")
+ {
+ REQUIRE(wave.getPath() == "path/to/sample.wav");
+ REQUIRE(wave.getBasename() == "sample");
+ REQUIRE(wave.getBasename(true) == "sample.wav");
+ }
}
}
static const int BUFFER_SIZE = 4000;
static const int BIT_DEPTH = 32;
- std::unique_ptr<Wave> waveMono = std::unique_ptr<Wave>(new Wave(new float[BUFFER_SIZE],
- BUFFER_SIZE, 1, SAMPLE_RATE, BIT_DEPTH, "path/to/sample.wav"));
-
- std::unique_ptr<Wave> waveStereo = std::unique_ptr<Wave>(new Wave(new float[BUFFER_SIZE * 2],
- BUFFER_SIZE, 2, SAMPLE_RATE, BIT_DEPTH, "path/to/sample.wav"));
+ Wave waveMono;
+ Wave waveStereo;
+ waveMono.alloc(BUFFER_SIZE, 1, SAMPLE_RATE, BIT_DEPTH, "path/to/sample.wav");
+ waveStereo.alloc(BUFFER_SIZE, 2, SAMPLE_RATE, BIT_DEPTH, "path/to/sample.wav");
SECTION("test mono->stereo conversion")
{
- int prevSize = waveMono->getSize();
+ int prevSize = waveMono.getSize();
- REQUIRE(wfx::monoToStereo(waveMono.get()) == G_RES_OK);
- REQUIRE(waveMono->getSize() == prevSize); // size does not change, channels do
- REQUIRE(waveMono->getChannels() == 2);
+ REQUIRE(wfx::monoToStereo(waveMono) == G_RES_OK);
+ REQUIRE(waveMono.getSize() == prevSize); // size does not change, channels do
+ REQUIRE(waveMono.getChannels() == 2);
SECTION("test mono->stereo conversion for already stereo wave")
{
/* Should do nothing. */
- int prevSize = waveStereo->getSize();
+ int prevSize = waveStereo.getSize();
- REQUIRE(wfx::monoToStereo(waveStereo.get()) == G_RES_OK);
- REQUIRE(waveStereo->getSize() == prevSize);
- REQUIRE(waveStereo->getChannels() == 2);
+ REQUIRE(wfx::monoToStereo(waveStereo) == G_RES_OK);
+ REQUIRE(waveStereo.getSize() == prevSize);
+ REQUIRE(waveStereo.getChannels() == 2);
}
}
{
int a = 20;
int b = 57;
- wfx::silence(waveStereo.get(), a, b);
+ wfx::silence(waveStereo, a, b);
- for (int i=a; i<b; i++) {
- float* frame = waveStereo->getFrame(i);
- for (int k=0; k<waveStereo->getChannels(); k++)
- REQUIRE(frame[k] == 0.0f);
- }
+ for (int i=a; i<b; i++)
+ for (int k=0; k<waveStereo.getChannels(); k++)
+ REQUIRE(waveStereo[i][k] == 0.0f);
SECTION("test silence (mono)")
{
- wfx::silence(waveMono.get(), a, b);
+ wfx::silence(waveMono, a, b);
- for (int i=a; i<b; i++) {
- float* frame = waveMono->getFrame(i);
- for (int k=0; k<waveMono->getChannels(); k++)
- REQUIRE(frame[k] == 0.0f);
- }
+ for (int i=a; i<b; i++)
+ for (int k=0; k<waveMono.getChannels(); k++)
+ REQUIRE(waveMono[i][k] == 0.0f);
}
}
int a = 47;
int b = 210;
int range = b - a;
- int prevSize = waveStereo->getSize();
+ int prevSize = waveStereo.getSize();
- REQUIRE(wfx::cut(waveStereo.get(), a, b) == G_RES_OK);
- REQUIRE(waveStereo->getSize() == prevSize - range);
+ REQUIRE(wfx::cut(waveStereo, a, b) == G_RES_OK);
+ REQUIRE(waveStereo.getSize() == prevSize - range);
SECTION("test cut (mono)")
{
- prevSize = waveMono->getSize();
- REQUIRE(wfx::cut(waveMono.get(), a, b) == G_RES_OK);
- REQUIRE(waveMono->getSize() == prevSize - range);
+ prevSize = waveMono.getSize();
+ REQUIRE(wfx::cut(waveMono, a, b) == G_RES_OK);
+ REQUIRE(waveMono.getSize() == prevSize - range);
}
}
int b = 210;
int area = b - a;
- REQUIRE(wfx::trim(waveStereo.get(), a, b) == G_RES_OK);
- REQUIRE(waveStereo->getSize() == area);
+ REQUIRE(wfx::trim(waveStereo, a, b) == G_RES_OK);
+ REQUIRE(waveStereo.getSize() == area);
SECTION("test trim (mono)")
{
- REQUIRE(wfx::trim(waveMono.get(), a, b) == G_RES_OK);
- REQUIRE(waveMono->getSize() == area);
+ REQUIRE(wfx::trim(waveMono, a, b) == G_RES_OK);
+ REQUIRE(waveMono.getSize() == area);
}
}
int a = 47;
int b = 500;
- wfx::fade(waveStereo.get(), a, b, wfx::FADE_IN);
- wfx::fade(waveStereo.get(), a, b, wfx::FADE_OUT);
+ wfx::fade(waveStereo, a, b, wfx::FADE_IN);
+ wfx::fade(waveStereo, a, b, wfx::FADE_OUT);
- REQUIRE(waveStereo->getFrame(a)[0] == 0.0f);
- REQUIRE(waveStereo->getFrame(a)[1] == 0.0f);
- REQUIRE(waveStereo->getFrame(b)[0] == 0.0f);
- REQUIRE(waveStereo->getFrame(b)[1] == 0.0f);
+ REQUIRE(waveStereo.getFrame(a)[0] == 0.0f);
+ REQUIRE(waveStereo.getFrame(a)[1] == 0.0f);
+ REQUIRE(waveStereo.getFrame(b)[0] == 0.0f);
+ REQUIRE(waveStereo.getFrame(b)[1] == 0.0f);
SECTION("test fade (mono)")
{
- wfx::fade(waveMono.get(), a, b, wfx::FADE_IN);
- wfx::fade(waveMono.get(), a, b, wfx::FADE_OUT);
+ wfx::fade(waveMono, a, b, wfx::FADE_IN);
+ wfx::fade(waveMono, a, b, wfx::FADE_OUT);
- REQUIRE(waveMono->getFrame(a)[0] == 0.0f);
- REQUIRE(waveMono->getFrame(b)[0] == 0.0f);
+ REQUIRE(waveMono.getFrame(a)[0] == 0.0f);
+ REQUIRE(waveMono.getFrame(b)[0] == 0.0f);
}
}
int a = 11;
int b = 79;
- wfx::smooth(waveStereo.get(), a, b);
+ wfx::smooth(waveStereo, a, b);
- REQUIRE(waveStereo->getFrame(a)[0] == 0.0f);
- REQUIRE(waveStereo->getFrame(a)[1] == 0.0f);
- REQUIRE(waveStereo->getFrame(b)[0] == 0.0f);
- REQUIRE(waveStereo->getFrame(b)[1] == 0.0f);
+ REQUIRE(waveStereo.getFrame(a)[0] == 0.0f);
+ REQUIRE(waveStereo.getFrame(a)[1] == 0.0f);
+ REQUIRE(waveStereo.getFrame(b)[0] == 0.0f);
+ REQUIRE(waveStereo.getFrame(b)[1] == 0.0f);
SECTION("test smooth (mono)")
{
- wfx::smooth(waveMono.get(), a, b);
- REQUIRE(waveMono->getFrame(a)[0] == 0.0f);
- REQUIRE(waveMono->getFrame(b)[0] == 0.0f);
+ wfx::smooth(waveMono, a, b);
+ REQUIRE(waveMono.getFrame(a)[0] == 0.0f);
+ REQUIRE(waveMono.getFrame(b)[0] == 0.0f);
}
}
}
SECTION("test recording")
{
- int res = waveManager::createEmpty(G_BUFFER_SIZE, G_SAMPLE_RATE,
+ int res = waveManager::createEmpty(G_BUFFER_SIZE, G_MAX_IO_CHANS, G_SAMPLE_RATE,
"test.wav", &w);
std::unique_ptr<Wave> wave(w);
REQUIRE(res == G_RES_OK);
REQUIRE(wave->getRate() == G_SAMPLE_RATE);
- REQUIRE(wave->getSize() == G_BUFFER_SIZE / wave->getChannels());
+ REQUIRE(wave->getSize() == G_BUFFER_SIZE);
REQUIRE(wave->getChannels() == G_CHANNELS);
REQUIRE(wave->isLogical() == true);
REQUIRE(wave->isEdited() == false);