From ad6b0dd3181f93cbfb98d538b887551f15141e92 Mon Sep 17 00:00:00 2001 From: =?utf8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Wed, 9 May 2018 15:32:57 +0200 Subject: [PATCH] New upstream version 0.15.0+repack1 --- .travis/after_success.sh | 17 - .travis/before_install.sh | 12 - .travis/before_script.sh | 13 - .travis/install.sh | 89 --- .travis/script.sh | 14 - ChangeLog | 8 + Makefile.am | 6 + configure.ac | 2 +- src/core/audioBuffer.cpp | 127 ++++ src/core/audioBuffer.h | 75 +++ src/core/channel.cpp | 210 +----- src/core/channel.h | 122 ++-- src/core/channelManager.cpp | 314 +++++++++ src/core/channelManager.h | 56 ++ src/core/clock.cpp | 237 ++++--- src/core/clock.h | 17 +- src/core/const.h | 13 +- src/core/init.cpp | 8 +- src/core/init.h | 2 +- src/core/kernelAudio.cpp | 18 +- src/core/kernelAudio.h | 2 +- src/core/midiChannel.cpp | 56 +- src/core/midiChannel.h | 17 +- src/core/mixer.cpp | 306 ++++----- src/core/mixer.h | 38 +- src/core/mixerHandler.cpp | 132 ++-- src/core/mixerHandler.h | 8 +- src/core/patch.cpp | 617 +++++++++--------- src/core/pluginHost.cpp | 68 +- src/core/pluginHost.h | 11 +- src/core/recorder.cpp | 49 +- src/core/recorder.h | 18 +- src/core/sampleChannel.cpp | 360 +++------- src/core/sampleChannel.h | 122 ++-- src/core/wave.cpp | 97 +-- src/core/wave.h | 68 +- src/core/waveFx.cpp | 170 ++--- src/core/waveFx.h | 26 +- src/core/waveManager.cpp | 71 +- src/core/waveManager.h | 6 +- src/glue/channel.cpp | 119 +--- src/glue/channel.h | 2 - src/glue/io.cpp | 12 +- src/glue/main.cpp | 20 +- src/glue/main.h | 4 +- src/glue/recorder.cpp | 4 +- src/glue/recorder.h | 8 +- src/glue/sampleEditor.cpp | 26 +- src/glue/storage.cpp | 10 +- src/gui/dialogs/channelNameInput.cpp | 2 +- src/gui/dialogs/gd_actionEditor.cpp | 14 +- src/gui/dialogs/midiIO/midiInputChannel.cpp | 13 +- src/gui/dialogs/sampleEditor.cpp | 6 +- src/gui/elems/actionEditor/action.cpp | 5 - src/gui/elems/actionEditor/actionEditor.cpp | 6 +- src/gui/elems/actionEditor/envelopeEditor.cpp | 8 +- src/gui/elems/actionEditor/gridTool.cpp | 10 +- src/gui/elems/actionEditor/muteEditor.cpp | 4 +- src/gui/elems/actionEditor/pianoRoll.cpp | 6 +- src/gui/elems/mainWindow/keyboard/channel.cpp | 2 +- .../mainWindow/keyboard/channelStatus.cpp | 2 +- src/gui/elems/mainWindow/keyboard/column.cpp | 8 +- .../elems/mainWindow/keyboard/midiChannel.cpp | 10 +- .../mainWindow/keyboard/sampleChannel.cpp | 12 +- src/gui/elems/mainWindow/mainMenu.cpp | 4 +- src/gui/elems/sampleEditor/boostTool.cpp | 2 +- src/gui/elems/sampleEditor/pitchTool.cpp | 4 +- src/gui/elems/sampleEditor/shiftTool.cpp | 4 +- src/gui/elems/sampleEditor/volumeTool.cpp | 2 +- src/gui/elems/sampleEditor/waveform.cpp | 4 +- src/utils/gui.cpp | 12 +- tests/audioBuffer.cpp | 100 +++ tests/patch.cpp | 4 +- tests/wave.cpp | 60 +- tests/waveFx.cpp | 101 ++- tests/waveManager.cpp | 4 +- 76 files changed, 2094 insertions(+), 2122 deletions(-) delete mode 100755 .travis/after_success.sh delete mode 100755 .travis/before_install.sh delete mode 100755 .travis/before_script.sh delete mode 100755 .travis/install.sh delete mode 100755 .travis/script.sh create mode 100644 src/core/audioBuffer.cpp create mode 100644 src/core/audioBuffer.h create mode 100644 src/core/channelManager.cpp create mode 100644 src/core/channelManager.h create mode 100644 tests/audioBuffer.cpp diff --git a/.travis/after_success.sh b/.travis/after_success.sh deleted file mode 100755 index 64612e1..0000000 --- a/.travis/after_success.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/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 diff --git a/.travis/before_install.sh b/.travis/before_install.sh deleted file mode 100755 index 17d5a97..0000000 --- a/.travis/before_install.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/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 diff --git a/.travis/before_script.sh b/.travis/before_script.sh deleted file mode 100755 index 73e0df2..0000000 --- a/.travis/before_script.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/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 diff --git a/.travis/install.sh b/.travis/install.sh deleted file mode 100755 index 066b194..0000000 --- a/.travis/install.sh +++ /dev/null @@ -1,89 +0,0 @@ -#!/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 diff --git a/.travis/script.sh b/.travis/script.sh deleted file mode 100755 index af3c70e..0000000 --- a/.travis/script.sh +++ /dev/null @@ -1,14 +0,0 @@ -#!/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 diff --git a/ChangeLog b/ChangeLog index 76388e3..f593b9d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -12,6 +12,14 @@ -------------------------------------------------------------------------------- +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 diff --git a/Makefile.am b/Makefile.am index 9d0cfec..f007a12 100644 --- a/Makefile.am +++ b/Makefile.am @@ -125,6 +125,8 @@ src/core/midiMapConf.h \ 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 \ @@ -157,6 +159,8 @@ src/core/clock.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 \ @@ -389,6 +393,7 @@ tests/pluginHost.cpp \ tests/utils.cpp \ tests/recorder.cpp \ tests/waveFx.cpp \ +tests/audioBuffer.cpp \ src/core/conf.cpp \ src/core/wave.cpp \ src/core/waveManager.cpp \ @@ -398,6 +403,7 @@ src/core/patch.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 \ diff --git a/configure.ac b/configure.ac index 9f6e959..9f772f6 100644 --- a/configure.ac +++ b/configure.ac @@ -4,7 +4,7 @@ # 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]) diff --git a/src/core/audioBuffer.cpp b/src/core/audioBuffer.cpp new file mode 100644 index 0000000..ac64df9 --- /dev/null +++ b/src/core/audioBuffer.cpp @@ -0,0 +1,127 @@ +#include +#include +#include +#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 diff --git a/src/core/audioBuffer.h b/src/core/audioBuffer.h new file mode 100644 index 0000000..702f77a --- /dev/null +++ b/src/core/audioBuffer.h @@ -0,0 +1,75 @@ +#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; kcountFrames(), k++) + for (int i=0; icountChannels(); 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 diff --git a/src/core/channel.cpp b/src/core/channel.cpp index a1dda46..906b18f 100644 --- a/src/core/channel.cpp +++ b/src/core/channel.cpp @@ -30,7 +30,7 @@ #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" @@ -43,6 +43,7 @@ #include "patch.h" #include "waveFx.h" #include "midiMapConf.h" +#include "channel.h" using std::string; @@ -51,25 +52,23 @@ using namespace giada::m; 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), @@ -78,6 +77,7 @@ Channel::Channel(int type, int status, int bufferSize) midiInVolume (0x0), midiInMute (0x0), midiInSolo (0x0), + midiInFilter (-1), midiOutL (false), midiOutLplaying(0x0), midiOutLmute (0x0), @@ -92,8 +92,6 @@ Channel::Channel(int type, int status, int bufferSize) Channel::~Channel() { status = STATUS_OFF; - if (vChan != nullptr) - delete[] vChan; } @@ -102,12 +100,10 @@ Channel::~Channel() 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; } @@ -115,7 +111,7 @@ bool Channel::allocBuffers() /* -------------------------------------------------------------------------- */ -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; @@ -153,7 +149,7 @@ void Channel::copy(const Channel *src, pthread_mutex_t *pluginMutex) for (unsigned i=0; ichan == src->index) { recorder::rec(index, a->type, a->frame, a->iValue, a->fValue); hasActions = true; @@ -196,140 +192,18 @@ bool Channel::isPlaying() const /* -------------------------------------------------------------------------- */ -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; ichan == 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; igetUniqueId(); - pp.bypass = pPlugin->isBypassed(); - for (int k=0; kgetNumParameters(); k++) - pp.params.push_back(pPlugin->getParameter(k)); - for (unsigned k=0; kmidiInParams.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; jsetParameter(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); } @@ -395,18 +269,6 @@ void Channel::receiveMidi(const MidiEvent& midiEvent) /* -------------------------------------------------------------------------- */ -void Channel::setMidiInFilter(int c) -{ - midiInFilter = c; -} - - -int Channel::getMidiInFilter() const -{ - return midiInFilter; -} - - bool Channel::isMidiInAllowed(int c) const { return midiInFilter == -1 || midiInFilter == c; @@ -437,24 +299,12 @@ float Channel::getPan() const /* -------------------------------------------------------------------------- */ -void Channel::setVolume(float v) -{ - volume = v; -} - - void Channel::setVolumeI(float v) { volume_i = v; } -float Channel::getVolume() const -{ - return volume; -} - - /* -------------------------------------------------------------------------- */ @@ -487,36 +337,6 @@ bool Channel::isPreview() const /* -------------------------------------------------------------------------- */ -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() diff --git a/src/core/channel.h b/src/core/channel.h index d8394fb..8a78c55 100644 --- a/src/core/channel.h +++ b/src/core/channel.h @@ -35,6 +35,7 @@ #include "midiMapConf.h" #include "midiEvent.h" #include "recorder.h" +#include "audioBuffer.h" #ifdef WITH_VST #include "../deps/juce-config.h" @@ -50,6 +51,8 @@ class Channel { 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. */ @@ -61,6 +64,11 @@ protected: 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 @@ -76,28 +84,17 @@ protected: 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 @@ -105,23 +102,17 @@ public: 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) @@ -160,10 +151,11 @@ public: 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. */ @@ -204,11 +196,16 @@ public: 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. */ @@ -223,11 +220,7 @@ public: 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 @@ -243,12 +236,8 @@ public: 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 @@ -262,30 +251,43 @@ public: #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 diff --git a/src/core/channelManager.cpp b/src/core/channelManager.cpp new file mode 100644 index 0000000..addc5cb --- /dev/null +++ b/src/core/channelManager.cpp @@ -0,0 +1,314 @@ +/* ----------------------------------------------------------------------------- + * + * 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 + * . + * + * -------------------------------------------------------------------------- */ + + +#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; kgetNumParameters(); 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; jsetParameter(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 diff --git a/src/core/channelManager.h b/src/core/channelManager.h new file mode 100644 index 0000000..3e3a11c --- /dev/null +++ b/src/core/channelManager.h @@ -0,0 +1,56 @@ +/* ----------------------------------------------------------------------------- + * + * 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 + * . + * + * -------------------------------------------------------------------------- */ + + +#ifndef G_CHANNEL_MANAGER_H +#define G_CHANNEL_MANAGER_H + + +#include + + +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 diff --git a/src/core/clock.cpp b/src/core/clock.cpp index a62d0b0..9a38b6a 100644 --- a/src/core/clock.cpp +++ b/src/core/clock.cpp @@ -46,13 +46,14 @@ float bpm = G_DEFAULT_BPM; 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; @@ -67,10 +68,8 @@ kernelAudio::JackState jackStatePrev; void updateQuanto() { - if (quantize != 0) - quanto = framesPerBeat / quantize; - if (quanto % 2 != 0) - quanto++; + if (quantize != 0) + quanto = framesInBeat / quantize; } }; // {anonymous} @@ -83,13 +82,13 @@ void updateQuanto() 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(); } @@ -98,40 +97,40 @@ void init(int sampleRate, float midiTCfps) 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); @@ -141,79 +140,78 @@ void start() 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(); } @@ -222,29 +220,18 @@ void rewind() 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(); } @@ -253,13 +240,13 @@ void updateFrameBars() 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) { @@ -269,7 +256,7 @@ void sendMIDIsync() * range 1-4, if odd send 5-8. */ if (currentFrame % midiTCrate != 0) // no timecode frame passed - return; + return; /* frame low nibble * frame high nibble @@ -348,26 +335,26 @@ void sendMIDIrewind() 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 @@ -378,67 +365,67 @@ void recvJackSync() 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; } diff --git a/src/core/clock.h b/src/core/clock.h index de2972d..c7dd35e 100644 --- a/src/core/clock.h +++ b/src/core/clock.h @@ -29,11 +29,6 @@ #define G_CLOCK_H -class Conf; -class KernelMidi; -class KernelAudio; - - namespace giada { namespace m { namespace clock @@ -57,17 +52,17 @@ void recvJackSync(); 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(); diff --git a/src/core/const.h b/src/core/const.h index a587117..e93bcd0 100644 --- a/src/core/const.h +++ b/src/core/const.h @@ -46,10 +46,10 @@ /* -- version --------------------------------------------------------------- */ #define G_APP_NAME "Giada" -#define G_VERSION_STR "0.14.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" @@ -73,6 +73,7 @@ #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) @@ -105,6 +106,7 @@ #define G_MAX_BUF_SIZE 4096 #define G_MIN_GUI_WIDTH 816 #define G_MIN_GUI_HEIGHT 510 +#define G_MAX_IO_CHANS 2 @@ -145,7 +147,6 @@ #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 @@ -237,8 +238,8 @@ /* -- channel types --------------------------------------------------------- */ -#define CHANNEL_SAMPLE 0x01 -#define CHANNEL_MIDI 0x02 +#define G_CHANNEL_SAMPLE 0x01 +#define G_CHANNEL_MIDI 0x02 diff --git a/src/core/init.cpp b/src/core/init.cpp index 5db174c..1809e50 100644 --- a/src/core/init.cpp +++ b/src/core/init.cpp @@ -81,7 +81,7 @@ void init_prepareKernelAudio() { 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 @@ -194,12 +194,6 @@ void init_shutdown() } recorder::clearAll(); - for (unsigned i=0; ihasActions = 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 diff --git a/src/core/init.h b/src/core/init.h index b670580..8e10de6 100644 --- a/src/core/init.h +++ b/src/core/init.h @@ -30,7 +30,7 @@ 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(); diff --git a/src/core/kernelAudio.cpp b/src/core/kernelAudio.cpp index 421eb02..c1fa4e4 100644 --- a/src/core/kernelAudio.cpp +++ b/src/core/kernelAudio.cpp @@ -138,20 +138,16 @@ int openDevice() 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 @@ -420,7 +416,7 @@ int getDefaultOut() /* -------------------------------------------------------------------------- */ -int getDeviceByName(const char *name) +int getDeviceByName(const char* name) { for (unsigned i=0; i(src_); midiOut = src->midiOut; midiOutChan = src->midiOutChan; } @@ -107,17 +106,17 @@ void MidiChannel::empty() {} /* -------------------------------------------------------------------------- */ -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); } @@ -166,24 +165,23 @@ void MidiChannel::unsetMute(bool internal) /* -------------------------------------------------------------------------- */ -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; jmidiOut; - 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) @@ -300,15 +291,10 @@ void MidiChannel::rewind() /* -------------------------------------------------------------------------- */ -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); } diff --git a/src/core/midiChannel.h b/src/core/midiChannel.h index 4617c45..3ecad62 100644 --- a/src/core/midiChannel.h +++ b/src/core/midiChannel.h @@ -46,13 +46,10 @@ public: 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; @@ -62,10 +59,9 @@ public: 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, @@ -89,6 +85,9 @@ public: void addVstMidiEvent(uint32_t msg, int localFrame); #endif + + bool midiOut; // enable midi output + uint8_t midiOutChan; // midi output channel }; diff --git a/src/core/mixer.cpp b/src/core/mixer.cpp index f5ac5c8..7592927 100644 --- a/src/core/mixer.cpp +++ b/src/core/mixer.cpp @@ -40,6 +40,7 @@ #include "channel.h" #include "sampleChannel.h" #include "midiChannel.h" +#include "audioBuffer.h" #include "mixer.h" @@ -74,29 +75,63 @@ float tick[TICKSIZE] = { }; +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 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= clock::getFramesInLoop()) inputTracker = 0; } @@ -106,23 +141,19 @@ void lineInRec(float* inBuf, unsigned frame) /* 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; ichan; - 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); } @@ -177,13 +207,15 @@ void doQuantize(unsigned frame) if (clock::getQuantize() == 0 || !clock::quantoHasPassed()) return; + if (rewindWait) { rewindWait = false; rewind(); } + pthread_mutex_lock(&mutex_chans); for (unsigned i=0; iquantize(i, frame); + channels.at(i)->quantize(i, frame, clock::getCurrentFrame()); pthread_mutex_unlock(&mutex_chans); } @@ -192,15 +224,14 @@ void doQuantize(unsigned frame) /* 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; ktype == CHANNEL_SAMPLE) - static_cast(channels.at(k))->sum(frame, clock::isRunning()); - } + for (Channel* ch : channels) + if (ch->type == G_CHANNEL_SAMPLE) + static_cast(ch)->sum(frame, clock::isRunning()); pthread_mutex_unlock(&mutex_chans); } @@ -210,11 +241,11 @@ void sumChannels(unsigned frame) /* 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= TICKSIZE-1) { tockPlay = false; @@ -222,8 +253,8 @@ void renderMetronome(float* outBuf, unsigned frame) } } if (tickPlay) { - outBuf[frame] += tick[tickTracker]; - outBuf[frame+1] += tick[tickTracker]; + for (int i=0; i= TICKSIZE-1) { tickPlay = false; @@ -239,12 +270,13 @@ void renderMetronome(float* outBuf, unsigned frame) 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); @@ -262,31 +294,13 @@ void renderIO(float* outBuf, float* inBuf) /* 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 1.0f) + outBuf[frame][i] = 1.0f; + else if (outBuf[frame][i] < -1.0f) + outBuf[frame][i] = -1.0f; } @@ -296,16 +310,15 @@ void computePeak(float* outBuf, unsigned frame) 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; ionBar(frame); + for (Channel* ch : channels) + ch->onBar(frame); pthread_mutex_unlock(&mutex_chans); } @@ -338,8 +351,8 @@ void testFirstBeat(unsigned frame) if (!clock::isOnFirstBeat()) return; pthread_mutex_lock(&mutex_chans); - for (unsigned k=0; konZero(frame, conf::recsStopOnChanHalt); + for (Channel* ch : channels) + ch->onZero(frame, conf::recsStopOnChanHalt); pthread_mutex_unlock(&mutex_chans); } @@ -364,32 +377,17 @@ void testLastBeat() std::vector 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; @@ -399,21 +397,24 @@ pthread_mutex_t mutex_plugins; /* -------------------------------------------------------------------------- */ -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); @@ -425,21 +426,16 @@ void init(int framesInSeq, int audioBufferSize) /* -------------------------------------------------------------------------- */ -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) @@ -449,18 +445,20 @@ int masterPlay(void* _outBuf, void* _inBuf, unsigned bufferSize, 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 havoc. */ + out.setData(nullptr, 0, 0); + in.setData(nullptr, 0, 0); + return 0; } @@ -491,21 +493,11 @@ int masterPlay(void* _outBuf, void* _inBuf, unsigned bufferSize, /* -------------------------------------------------------------------------- */ -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; } @@ -514,8 +506,8 @@ int close() bool isSilent() { - for (unsigned i=0; istatus == STATUS_PLAY) + for (const Channel* ch : channels) + if (ch->status == STATUS_PLAY) return false; return true; } @@ -536,18 +528,26 @@ void rewind() /* -------------------------------------------------------------------------- */ -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(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(); } diff --git a/src/core/mixer.h b/src/core/mixer.h index 87aef5e..36c566b 100644 --- a/src/core/mixer.h +++ b/src/core/mixer.h @@ -41,21 +41,21 @@ namespace giada { 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? */ @@ -67,6 +67,11 @@ Rewinds sequencer to frame 0. */ 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(). */ @@ -88,29 +93,18 @@ extern std::vector channels; 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; diff --git a/src/core/mixerHandler.cpp b/src/core/mixerHandler.cpp index 119f95e..ecbf42f 100644 --- a/src/core/mixerHandler.cpp +++ b/src/core/mixerHandler.cpp @@ -26,6 +26,7 @@ #include +#include #include "../utils/fs.h" #include "../utils/string.h" #include "../utils/log.h" @@ -49,6 +50,7 @@ #include "midiChannel.h" #include "wave.h" #include "waveManager.h" +#include "channelManager.h" #include "mixerHandler.h" @@ -87,7 +89,7 @@ int readPatchPlugins(vector* list, int type) #endif -/* ------------------------------------------------------------------------ */ +/* -------------------------------------------------------------------------- */ int getNewChanIndex() @@ -118,7 +120,7 @@ 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(ch); if (sch->wave != nullptr && path == sch->wave->getPath()) @@ -133,18 +135,11 @@ bool uniqueSamplePath(const SampleChannel* skip, const string& path) 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) @@ -164,27 +159,16 @@ Channel* addChannel(int type) /* -------------------------------------------------------------------------- */ -int deleteChannel(Channel* ch) +void deleteChannel(Channel* target) { - int index = -1; - for (unsigned i=0; iindex); - 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; } } @@ -194,9 +178,9 @@ int deleteChannel(Channel* ch) Channel* getChannelByIndex(int index) { - for (unsigned i=0; iindex == 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; } @@ -207,11 +191,11 @@ Channel* getChannelByIndex(int index) bool hasLogicalSamples() { - for (unsigned i=0; itype != CHANNEL_SAMPLE) + for (const Channel* ch : mixer::channels) { + if (ch->type != G_CHANNEL_SAMPLE) continue; - SampleChannel *ch = static_cast(mixer::channels.at(i)); - if (ch->wave && ch->wave->isLogical()) + const SampleChannel* sch = static_cast(ch); + if (sch->wave != nullptr && sch->wave->isLogical()) return true; } return false; @@ -223,12 +207,11 @@ bool hasLogicalSamples() bool hasEditedSamples() { - for (unsigned i=0; itype != CHANNEL_SAMPLE) + for (const Channel* ch : mixer::channels) { + if (ch->type != G_CHANNEL_SAMPLE) continue; - SampleChannel *ch = static_cast(mixer::channels.at(i)); - if (ch->wave && ch->wave->isEdited()) + const SampleChannel* sch = static_cast(ch); + if (sch->wave != nullptr && sch->wave->isEdited()) return true; } return false; @@ -241,23 +224,22 @@ bool hasEditedSamples() void stopSequencer() { clock::stop(); - for (unsigned i=0; istopBySeq(conf::chansStopOnSeqHalt); + for (Channel* ch : mixer::channels) + ch->stopBySeq(conf::chansStopOnSeqHalt); } /* -------------------------------------------------------------------------- */ -bool uniqueSolo(Channel* ch) +void updateSoloCount() { - int solos = 0; - for (unsigned i=0; isolo) solos++; - if (solos > 1) return false; - } - return true; + for (Channel* ch : mixer::channels) + if (ch->solo) { + mixer::hasSolos = true; + return; + } + mixer::hasSolos = false; } @@ -289,7 +271,7 @@ void readPatch() very likely). */ mixer::rewind(); - mixer::allocVirtualInput(clock::getTotalFrames()); + mixer::allocVirtualInput(clock::getFramesInLoop()); mixer::ready = true; } @@ -313,40 +295,38 @@ bool startInputRec() { int channelsReady = 0; - for (Channel* channel : mixer::channels) { + for (Channel* ch : mixer::channels) { - if (!channel->canInputRec()) + if (!ch->canInputRec()) continue; - SampleChannel* ch = static_cast(channel); + SampleChannel* sch = static_cast(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; } @@ -367,11 +347,9 @@ void stopInputRec() bool hasArmedSampleChannels() { - for (unsigned i=0; itype == CHANNEL_SAMPLE && ch->isArmed()) + for (const Channel* ch : mixer::channels) + if (ch->type == G_CHANNEL_SAMPLE && ch->armed) return true; - } return false; } diff --git a/src/core/mixerHandler.h b/src/core/mixerHandler.h index c1cd2ba..ae160a9 100644 --- a/src/core/mixerHandler.h +++ b/src/core/mixerHandler.h @@ -48,7 +48,7 @@ Channel* addChannel(int type); /* deleteChannel Completely removes a channel from the stack. */ -int deleteChannel(Channel* ch); +void deleteChannel(Channel* ch); /* getChannelByIndex Returns channel with given index 'i'. */ @@ -72,10 +72,10 @@ void stopSequencer(); 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 diff --git a/src/core/patch.cpp b/src/core/patch.cpp index dfdc9dc..a834775 100644 --- a/src/core/patch.cpp +++ b/src/core/patch.cpp @@ -49,28 +49,44 @@ Internal sanity check. */ 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; iindex = col->index < 0 ? 0 : col->index; - col->width = col->width < G_MIN_COLUMN_WIDTH ? G_MIN_COLUMN_WIDTH : col->width; - } - - for (unsigned i=0; isize = ch->size < G_GUI_CHANNEL_H_1 || ch->size > G_GUI_CHANNEL_H_4 ? G_GUI_CHANNEL_H_1 : ch->size; - ch->volume = ch->volume < 0.0f || ch->volume > 1.0f ? G_DEFAULT_VOL : ch->volume; - ch->pan = ch->pan < 0.0f || ch->pan > 1.0f ? 1.0f : ch->pan; - ch->boost = ch->boost < 1.0f ? G_DEFAULT_BOOST : ch->boost; - ch->pitch = ch->pitch < 0.1f || ch->pitch > 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; iindex = col->index < 0 ? 0 : col->index; + col->width = col->width < G_MIN_COLUMN_WIDTH ? G_MIN_COLUMN_WIDTH : col->width; + } + + for (unsigned i=0; isize = ch->size < G_GUI_CHANNEL_H_1 || ch->size > G_GUI_CHANNEL_H_4 ? G_GUI_CHANNEL_H_1 : ch->size; + ch->volume = ch->volume < 0.0f || ch->volume > 1.0f ? G_DEFAULT_VOL : ch->volume; + ch->pan = ch->pan < 0.0f || ch->pan > 1.0f ? 1.0f : ch->pan; + ch->boost = ch->boost < 1.0f ? G_DEFAULT_BOOST : ch->boost; + ch->pitch = ch->pitch < 0.1f || ch->pitch > 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; + } } @@ -81,8 +97,8 @@ Helper function used to return invalid status while reading. */ int setInvalid(json_t* jRoot) { - json_decref(jRoot); - return PATCH_INVALID; + json_decref(jRoot); + return PATCH_INVALID; } @@ -91,22 +107,22 @@ int setInvalid(json_t* jRoot) 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; } @@ -117,44 +133,44 @@ bool readCommons(json_t* jContainer) bool readPlugins(json_t* jContainer, vector* 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 @@ -164,25 +180,25 @@ bool readPlugins(json_t* jContainer, vector* container, const char* ke 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; } @@ -191,67 +207,67 @@ bool readActions(json_t* jContainer, channel_t* channel) 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; } @@ -260,25 +276,25 @@ bool readChannels(json_t* jContainer) 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; } @@ -288,29 +304,29 @@ bool readColumns(json_t* jContainer) void writePlugins(json_t* jContainer, vector* plugins, const char* key) { - json_t* jPlugins = json_array(); - for (unsigned j=0; jsize(); 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; zsize(); 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* plugins, const char* key void writeColumns(json_t* jContainer, vector* columns) { - json_t* jColumns = json_array(); - for (unsigned i=0; isize(); 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; isize(); 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); } @@ -338,17 +354,17 @@ void writeColumns(json_t* jContainer, vector* columns) void writeActions(json_t*jContainer, vector*actions) { - json_t* jActions = json_array(); - for (unsigned k=0; ksize(); 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; ksize(); 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); } @@ -357,18 +373,18 @@ void writeActions(json_t*jContainer, vector*actions) 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)); @@ -380,59 +396,59 @@ void writeCommons(json_t* jContainer) void writeChannels(json_t* jContainer, vector* channels) { - json_t* jChannels = json_array(); - for (unsigned i=0; isize(); 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; isize(); 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} @@ -473,15 +489,15 @@ std::vector masterOutPlugins; 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; } @@ -490,21 +506,21 @@ void init() 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; } @@ -513,34 +529,35 @@ int write(const string& file) 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; } diff --git a/src/core/pluginHost.cpp b/src/core/pluginHost.cpp index f7faccb..153b149 100644 --- a/src/core/pluginHost.cpp +++ b/src/core/pluginHost.cpp @@ -28,6 +28,7 @@ #ifdef WITH_VST +#include #include "../utils/log.h" #include "../utils/fs.h" #include "../utils/string.h" @@ -146,20 +147,20 @@ void close() /* -------------------------------------------------------------------------- */ -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); } @@ -343,12 +344,12 @@ pluginHost::PluginInfo getAvailablePluginInfo(int i) { 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; } @@ -397,7 +398,7 @@ void freeStack(int stackType, pthread_mutex_t* mutex, Channel* ch) /* -------------------------------------------------------------------------- */ -void processStack(float* buffer, int stackType, Channel* ch) +void processStack(AudioBuffer& outBuf, int stackType, Channel* ch) { vector* pStack = getStack(stackType, ch); @@ -406,17 +407,18 @@ void processStack(float* buffer, int stackType, Channel* 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; iacceptsMidi()) { - juce::AudioBuffer tmp(2, buffersize); + juce::AudioBuffer tmp(audioBuffer.getNumChannels(), buffersize); plugin->process(tmp, ch->getPluginMidiEvents()); - for (int i=0; iprocess(audioBuffer, juce::MidiBuffer()); // Empty MIDI buffer @@ -462,10 +463,9 @@ void processStack(float* buffer, int stackType, Channel* ch) /* 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 f) +{ + /* TODO - Remove const is ugly. This is a temporary workaround until all + PluginHost functions params will be const-correct. */ + vector* stack = getStack(stackType, const_cast(ch)); + for (const Plugin* p : *stack) + f(p); +} + + }}}; // giada::m::pluginHost:: diff --git a/src/core/pluginHost.h b/src/core/pluginHost.h index a98cb02..dcb749f 100644 --- a/src/core/pluginHost.h +++ b/src/core/pluginHost.h @@ -34,6 +34,7 @@ #include #include #include "../deps/juce-config.h" +#include "audioBuffer.h" class Plugin; @@ -69,7 +70,9 @@ struct PluginInfo bool isInstrument; }; -void init(int bufSize, int frequency); +extern pthread_mutex_t mutex_midi; + +void init(int bufSize, int samplerate); void close(); /* scanDirs @@ -129,9 +132,9 @@ std::string getUnknownPluginInfo(int index); 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 given the stackType. If stackType == CHANNEL @@ -180,7 +183,7 @@ bool hasMissingPlugins(); void sortPlugins(int sortMethod); -extern pthread_mutex_t mutex_midi; +void forEachPlugin(int stackType, const Channel* ch, std::function f); }}}; // giada::m::pluginHost:: diff --git a/src/core/recorder.cpp b/src/core/recorder.cpp index ff319ad..7dddda1 100644 --- a/src/core/recorder.cpp +++ b/src/core/recorder.cpp @@ -111,7 +111,7 @@ bool canRec(Channel* ch, bool clockRunning, bool mixerRecording) if (!active || !clockRunning || mixerRecording || - (ch->type == CHANNEL_SAMPLE && ((SampleChannel*)ch)->wave == nullptr) + (ch->type == G_CHANNEL_SAMPLE && static_cast(ch)->wave == nullptr) ) return false; return true; @@ -123,14 +123,9 @@ bool canRec(Channel* ch, bool clockRunning, bool mixerRecording) 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; @@ -164,7 +159,7 @@ void rec(int index, int type, int frame, uint32_t iValue, float fValue) /* no duplicates, please */ for (unsigned t=0; tchan == index && ac->type == type && ac->frame == frame && @@ -195,7 +190,7 @@ void clearChan(int index) 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); @@ -220,7 +215,7 @@ void clearAction(int index, char act) 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); @@ -240,11 +235,6 @@ void clearAction(int index, char act) 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; @@ -256,7 +246,7 @@ void deleteAction(int chan, int frame, char type, bool checkValues, /* find the action in frame i */ for (unsigned j=0; j 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; iframe = frames.at(i); } } @@ -431,9 +416,6 @@ void updateSamplerate(int systemRate, int patchRate) frames.at(i) = (int) newFrame; - if (frames.at(i) % 2 != 0) - frames.at(i)++; - gu_log(", newFrame = %d\n", frames.at(i)); } @@ -441,7 +423,7 @@ void updateSamplerate(int systemRate, int patchRate) for (unsigned i=0; iframe = frames.at(i); } } @@ -469,7 +451,7 @@ void expand(int old_fpb, int new_fpb) frames.push_back(newframe); global.push_back(actions); for (unsigned k=0; kchan, a->type, newframe, a->iValue, a->fValue); } } @@ -611,7 +593,7 @@ void startOverdub(int index, char actionMask, int frame, unsigned bufferSize) 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) { @@ -674,4 +656,15 @@ void stopOverdub(int currentFrame, int totalFrames, pthread_mutex_t* mixerMutex) rec(cmp.a2.chan, cmp.a2.type, cmp.a2.frame); fixOverdubTruncation(cmp, mixerMutex); } + + +/* -------------------------------------------------------------------------- */ + + +void forEachAction(std::function f) +{ + for (const vector actions : recorder::global) + for (const action* action : actions) + f(action); +} }}}; // giada::m::recorder:: diff --git a/src/core/recorder.h b/src/core/recorder.h index 49a43a2..0f0b0e5 100644 --- a/src/core/recorder.h +++ b/src/core/recorder.h @@ -29,12 +29,9 @@ #define G_RECORDER_H -#ifdef __APPLE__ // our Clang still doesn't know about cstdint (c++11 stuff) - #include -#else - #include -#endif +#include #include +#include #include @@ -100,7 +97,7 @@ bool hasActions(int chanIndex); /* 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. */ @@ -121,14 +118,14 @@ void clearAction(int chan, char 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. */ @@ -183,6 +180,11 @@ pressing Mute button on a channel with some existing mute actions. */ 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 f); }}}; // giada::m::recorder:: #endif diff --git a/src/core/sampleChannel.cpp b/src/core/sampleChannel.cpp index 4ab3700..0a69632 100644 --- a/src/core/sampleChannel.cpp +++ b/src/core/sampleChannel.cpp @@ -32,6 +32,7 @@ #include "../utils/fs.h" #include "../utils/string.h" #include "patch.h" +#include "channelManager.h" #include "const.h" #include "conf.h" #include "clock.h" @@ -51,27 +52,25 @@ using namespace giada::m; 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) @@ -88,10 +87,6 @@ SampleChannel::~SampleChannel() delete wave; if (rsmp_state != nullptr) src_delete(rsmp_state); - if (pChan != nullptr) - delete[] pChan; - if (vChanPreview != nullptr) - delete[] vChanPreview; } @@ -103,20 +98,18 @@ bool SampleChannel::allocBuffers() 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; } @@ -128,10 +121,10 @@ bool SampleChannel::allocBuffers() /* -------------------------------------------------------------------------- */ -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(_src); + Channel::copy(src_, pluginMutex); + const SampleChannel* src = static_cast(src_); tracker = src->tracker; begin = src->begin; end = src->end; @@ -158,11 +151,11 @@ void SampleChannel::copy(const Channel* _src, pthread_mutex_t* pluginMutex) 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); @@ -181,8 +174,8 @@ void SampleChannel::calcVolumeEnv(int frame) { /* 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 @@ -203,7 +196,7 @@ void SampleChannel::calcVolumeEnv(int frame) 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; } @@ -212,8 +205,8 @@ void SampleChannel::calcVolumeEnv(int frame) 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); @@ -226,12 +219,12 @@ void SampleChannel::hardStop(int 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 @@ -250,19 +243,16 @@ void SampleChannel::onBar(int frame) 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; @@ -274,69 +264,24 @@ void SampleChannel::setBegin(int f) 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; } /* -------------------------------------------------------------------------- */ @@ -361,10 +306,7 @@ void SampleChannel::setPitch(float v) } -float SampleChannel::getPitch() -{ - return pitch; -} +float SampleChannel::getPitch() const { return pitch; } /* -------------------------------------------------------------------------- */ @@ -421,10 +363,6 @@ void SampleChannel::parseAction(recorder::action* a, int localFrame, 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; @@ -448,14 +386,14 @@ void SampleChannel::sum(int frame, bool running) * volume envelope. */ if (mute || mute_i) { - vChan[frame] = 0.0f; - vChan[frame+1] = 0.0f; + for (int i=0; i 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; igetChannels() stuff. */ - if (status & ~(STATUS_EMPTY | STATUS_MISSING | STATUS_OFF)) // if is not (...) - return (tracker - begin) / wave->getChannels(); + return tracker - begin; else return -1; } @@ -729,8 +662,8 @@ float SampleChannel::getBoost() const 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; } @@ -750,15 +683,6 @@ void SampleChannel::setReadActions(bool v, bool killOnFalse) /* -------------------------------------------------------------------------- */ -void SampleChannel::setOnEndPreviewCb(std::function f) -{ - onPreviewEnd = f; -} - - -/* -------------------------------------------------------------------------- */ - - void SampleChannel::setFadeIn(bool internal) { if (internal) mute_i = false; // remove mute before fading in @@ -799,13 +723,6 @@ void SampleChannel::setXFade(int frame) /* -------------------------------------------------------------------------- */ -/* 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) { @@ -813,6 +730,13 @@ 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); } @@ -847,7 +771,7 @@ void SampleChannel::pushWave(Wave* w) wave = w; status = STATUS_OFF; begin = 0; - end = (wave->getSize() - 1) * wave->getChannels(); // TODO - Opaque channels' count + end = wave->getSize() - 1; name = wave->getBasename(); } @@ -855,38 +779,40 @@ void SampleChannel::pushWave(Wave* w) /* -------------------------------------------------------------------------- */ -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; imode; - 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); } @@ -1109,91 +998,58 @@ void SampleChannel::start(int frame, bool doQuantize, int quantize, /* -------------------------------------------------------------------------- */ -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; diff --git a/src/core/sampleChannel.h b/src/core/sampleChannel.h index b290640..11af92e 100644 --- a/src/core/sampleChannel.h +++ b/src/core/sampleChannel.h @@ -47,56 +47,66 @@ private: 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 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: @@ -105,8 +115,8 @@ 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; @@ -116,10 +126,9 @@ public: 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, @@ -127,21 +136,10 @@ public: 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. */ @@ -149,28 +147,23 @@ public: 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); @@ -180,22 +173,17 @@ public: void setReadActions(bool v, bool killOnFalse); - void setBoost(float v); + /* onPreviewEnd + A callback fired when audio preview ends. */ - void setOnEndPreviewCb(std::function f); + std::function 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 */ diff --git a/src/core/wave.cpp b/src/core/wave.cpp index 8499324..5c95195 100644 --- a/src/core/wave.cpp +++ b/src/core/wave.cpp @@ -40,35 +40,20 @@ using std::string; 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]; } @@ -76,40 +61,28 @@ Wave::~Wave() 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; } @@ -126,10 +99,9 @@ string Wave::getBasename(bool ext) const 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; } @@ -140,7 +112,7 @@ bool Wave::isEdited() const { return m_edited; } int Wave::getDuration() const { - return m_size / m_channels / m_rate; + return buffer.countFrames() / m_rate; } @@ -158,21 +130,16 @@ std::string Wave::getExtension() const 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; } /* -------------------------------------------------------------------------- */ @@ -184,16 +151,22 @@ void Wave::setPath(const string& p, int id) 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 diff --git a/src/core/wave.h b/src/core/wave.h index 7491a14..ecdcc4a 100644 --- a/src/core/wave.h +++ b/src/core/wave.h @@ -34,74 +34,66 @@ #include #include #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; igetChannels(); 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 diff --git a/src/core/waveFx.cpp b/src/core/waveFx.cpp index e50fe70..9154939 100644 --- a/src/core/waveFx.cpp +++ b/src/core/waveFx.cpp @@ -26,6 +26,7 @@ #include +#include #include #include "../utils/log.h" #include "wave.h" @@ -38,25 +39,23 @@ namespace wfx { 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; jgetChannels(); j++) - frame[j] *= val; + for (int j=0; jgetFrame(i); - for (int j=0; jgetChannels(); j++) // Find highest value in any channel - abs = fabs(frame[j]); + for (int j=0; j peak) peak = abs; } @@ -70,9 +69,9 @@ float getPeak(Wave* w, int a, int b) /* -------------------------------------------------------------------------- */ -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 */ @@ -87,46 +86,39 @@ float normalizeSoft(Wave* w) /* -------------------------------------------------------------------------- */ -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; igetFrame(i); - for (int j=0; jgetChannels(); j++) - frame[j] = frame[j] * (1.0f / peak); + for (int j=0; jsetEdited(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; igetSize(); i++, k+=G_DEFAULT_AUDIO_CHANS) { - float* frame = w->getFrame(i); - for (int j=0; jfree(); - w->setData(newData, newSize); - w->setChannels(G_DEFAULT_AUDIO_CHANS); + w.moveData(newData); return G_RES_OK; } @@ -135,52 +127,50 @@ int monoToStereo(Wave* w) /* -------------------------------------------------------------------------- */ -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; igetFrame(i); - for (int j=0; jgetChannels(); j++) - frame[j] = 0.0f; + for (int j=0; jsetEdited(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; igetSize(); i++) { + for (int i=0, k=0; i= b) { - float* frame = w->getFrame(i); - for (int j=0; jgetChannels(); j++) - newData[k+j] = frame[j]; - k += w->getChannels(); + for (int j=0; jfree(); - w->setData(newData, newSize); - w->setEdited(true); + w.moveData(newData); + w.setEdited(true); return G_RES_OK; } @@ -189,29 +179,27 @@ int cut(Wave* w, int a, int b) /* -------------------------------------------------------------------------- */ -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; igetChannels()) { - float* frame = w->getFrame(i); - for (int j=0; jgetChannels(); j++) - newData[k+j] = frame[j]; - } + for (int i=0; ifree(); - w->setData(newData, newSize); - w->setEdited(true); + w.moveData(newData); + w.setEdited(true); return G_RES_OK; } @@ -220,39 +208,25 @@ int trim(Wave* w, int a, int b) /* -------------------------------------------------------------------------- */ -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; } @@ -261,7 +235,7 @@ int paste(Wave* src, Wave* des, int aFrame) /* -------------------------------------------------------------------------- */ -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); @@ -275,14 +249,14 @@ void fade(Wave* w, int a, int b, int type) 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. */ @@ -295,39 +269,39 @@ void smooth(Wave* w, int a, int b) 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 diff --git a/src/core/waveFx.h b/src/core/waveFx.h index b0ac02c..51146ba 100644 --- a/src/core/waveFx.h +++ b/src/core/waveFx.h @@ -43,35 +43,39 @@ static const int SMOOTH_SIZE = 32; /* 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:: diff --git a/src/core/waveManager.cpp b/src/core/waveManager.cpp index 2b3b394..b7905e6 100644 --- a/src/core/waveManager.cpp +++ b/src/core/waveManager.cpp @@ -89,33 +89,24 @@ int create(const string& path, Wave** out) 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; } @@ -131,21 +122,22 @@ int create(const string& path, Wave** out) /* -------------------------------------------------------------------------- */ -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; } @@ -156,23 +148,22 @@ int createEmpty(int size, int samplerate, const string& name, Wave** out) int createFromWave(const Wave* src, int a, int b, Wave** out) { - int numChans = src->getChannels(); - int size = (b - a) * numChans; - float* data = new (std::nothrow) float[size]; - if (data == nullptr) { + 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; } @@ -184,34 +175,30 @@ int createFromWave(const Wave* src, int a, int b, Wave** out) 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; @@ -235,7 +222,7 @@ int save(Wave* w, const string& path) 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); diff --git a/src/core/waveManager.h b/src/core/waveManager.h index 50da372..b350695 100644 --- a/src/core/waveManager.h +++ b/src/core/waveManager.h @@ -45,10 +45,10 @@ Creates a new Wave object with data read from file 'path'. */ 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. */ diff --git a/src/glue/channel.cpp b/src/glue/channel.cpp index 9fbba3e..4dd0171 100644 --- a/src/glue/channel.cpp +++ b/src/glue/channel.cpp @@ -72,17 +72,6 @@ namespace giada { namespace c { namespace channel { -namespace -{ -bool soloSession__ = false; -} // {anonymous} - - -/* -------------------------------------------------------------------------- */ -/* -------------------------------------------------------------------------- */ -/* -------------------------------------------------------------------------- */ - - int loadChannel(SampleChannel* ch, const string& fname) { using namespace giada::m; @@ -187,9 +176,9 @@ void freeChannel(Channel* ch) 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); } @@ -227,7 +216,7 @@ int cloneChannel(Channel* src) 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). */ @@ -292,7 +281,7 @@ void toggleMute(Channel* ch, bool gui) 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); } @@ -310,63 +299,15 @@ void toggleMute(Channel* ch, bool gui) 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; imute_s = och->mute; - } - soloSession__ = true; - } - + ch->solo = !ch->solo; - ch->sendMidiLsolo(); - - /* mute all other channels and unmute this (if muted) */ - - for (unsigned i=0; isolo && !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(); } } @@ -375,47 +316,9 @@ void setSoloOn(Channel* ch, bool gui) /* -------------------------------------------------------------------------- */ -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; imute_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 } @@ -439,7 +342,7 @@ void setBoost(SampleChannel* ch, float val) void setName(Channel* ch, const string& name) { - ch->setName(name); + ch->name = name; ch->guiChannel->update(); } diff --git a/src/glue/channel.h b/src/glue/channel.h index 2228622..e5212bb 100644 --- a/src/glue/channel.h +++ b/src/glue/channel.h @@ -73,8 +73,6 @@ void toggleArm(Channel* ch, bool gui=true); 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); diff --git a/src/glue/io.cpp b/src/glue/io.cpp index 7e85d80..2da007d 100644 --- a/src/glue/io.cpp +++ b/src/glue/io.cpp @@ -156,7 +156,7 @@ void cleanPress(SampleChannel* ch, int velocity) void keyPress(Channel* ch, bool ctrl, bool shift, int velocity) { - if (ch->type == CHANNEL_SAMPLE) + if (ch->type == G_CHANNEL_SAMPLE) keyPress(static_cast(ch), ctrl, shift, velocity); else keyPress(static_cast(ch), ctrl, shift); @@ -168,7 +168,7 @@ void keyPress(Channel* ch, bool ctrl, bool shift, int velocity) void keyRelease(Channel* ch, bool ctrl, bool shift) { - if (ch->type == CHANNEL_SAMPLE) + if (ch->type == G_CHANNEL_SAMPLE) keyRelease(static_cast(ch), ctrl, shift); } @@ -218,7 +218,7 @@ void keyRelease(SampleChannel* ch, bool ctrl, bool 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() */ @@ -270,7 +270,7 @@ void stopActionRec(bool gui) for (Channel* ch : m::mixer::channels) { - if (ch->type == CHANNEL_MIDI) + if (ch->type == G_CHANNEL_MIDI) continue; SampleChannel* sch = static_cast(ch); G_MainWin->keyboard->setChannelWithActions(static_cast(sch->guiChannel)); @@ -352,10 +352,10 @@ int stopInputRec(bool gui) beat. */ for (Channel* ch : mixer::channels) { - if (ch->type == CHANNEL_MIDI) + if (ch->type == G_CHANNEL_MIDI) continue; SampleChannel* sch = static_cast(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); } diff --git a/src/glue/main.cpp b/src/glue/main.cpp index 6de32a7..4fc529d 100644 --- a/src/glue/main.cpp +++ b/src/glue/main.cpp @@ -77,7 +77,7 @@ void glue_setBpm(const char *v1, const char *v2) 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()); @@ -118,18 +118,18 @@ void glue_setBeats(int beats, int bars, bool expand) /* 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 @@ -200,9 +200,9 @@ void glue_setInVol(float v, bool gui) void glue_clearAllSamples() { clock::stop(); - for (unsigned i=0; iempty(); - mixer::channels.at(i)->guiChannel->reset(); + for (Channel* ch : mixer::channels) { + ch->empty(); + ch->guiChannel->reset(); } recorder::init(); return; @@ -212,9 +212,11 @@ void glue_clearAllSamples() /* -------------------------------------------------------------------------- */ -void glue_clearAllRecs() +void glue_clearAllActions() { recorder::init(); + for (Channel* ch : mixer::channels) + ch->hasActions = false; gu_updateControls(); } @@ -227,7 +229,7 @@ void glue_resetToInitState(bool resetGui, bool createColumns) 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); diff --git a/src/glue/main.h b/src/glue/main.h index 3644f93..c684b22 100644 --- a/src/glue/main.h +++ b/src/glue/main.h @@ -36,14 +36,14 @@ #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 diff --git a/src/glue/recorder.cpp b/src/glue/recorder.cpp index 9e0a333..53670c3 100644 --- a/src/glue/recorder.cpp +++ b/src/glue/recorder.cpp @@ -51,7 +51,7 @@ namespace 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(gch)->hideActionButton(); /* TODO - set mute=false */ gu_refreshActionEditor(); // refresh a.editor window, it could be open @@ -117,7 +117,7 @@ void recordMidiAction(int chan, int note, int frame_a, int frame_b) /* 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; diff --git a/src/glue/recorder.h b/src/glue/recorder.h index 811e002..ab417db 100644 --- a/src/glue/recorder.h +++ b/src/glue/recorder.h @@ -40,10 +40,10 @@ namespace giada { 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 diff --git a/src/glue/sampleEditor.cpp b/src/glue/sampleEditor.cpp index da35360..568e508 100644 --- a/src/glue/sampleEditor.cpp +++ b/src/glue/sampleEditor.cpp @@ -102,7 +102,7 @@ void setBeginEnd(SampleChannel* ch, int b, int e) 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; } @@ -140,7 +140,7 @@ void paste(SampleChannel* ch, int a) 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. */ @@ -162,7 +162,7 @@ void paste(SampleChannel* ch, int a) 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(); } @@ -173,7 +173,7 @@ void silence(SampleChannel* ch, int a, int b) 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(); } @@ -184,7 +184,7 @@ void fade(SampleChannel* ch, int a, int b, int type) 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(); } @@ -195,7 +195,7 @@ void smoothEdges(SampleChannel* ch, int a, int b) 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(); } @@ -206,7 +206,7 @@ void reverse(SampleChannel* ch, int a, int b) 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(); } @@ -217,7 +217,7 @@ void normalizeHard(SampleChannel* ch, int a, int b) 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; } @@ -234,7 +234,7 @@ void trim(SampleChannel* ch, int a, int b) void setPlayHead(SampleChannel* ch, int f) { - ch->setTrackerPreview(f); + ch->trackerPreview = f; gdSampleEditor* gdEditor = getSampleEditorWindow(); gdEditor->waveTools->waveform->redraw(); } @@ -257,7 +257,7 @@ void setPreview(SampleChannel* ch, int mode) 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); @@ -270,7 +270,7 @@ void rewindPreview(SampleChannel* ch) void toNewChannel(SampleChannel* ch, int a, int b) { SampleChannel* newCh = static_cast(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); @@ -298,8 +298,8 @@ bool isWaveBufferFull() 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(); diff --git a/src/glue/storage.cpp b/src/glue/storage.cpp index 8fc44d8..4f99a00 100644 --- a/src/glue/storage.cpp +++ b/src/glue/storage.cpp @@ -284,16 +284,16 @@ void glue_loadPatch(void* data) 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 @@ -360,7 +360,7 @@ void glue_saveProject(void* data) for (const Channel* ch : mixer::channels) { - if (ch->type == CHANNEL_MIDI) + if (ch->type == G_CHANNEL_MIDI) continue; const SampleChannel* sch = static_cast(ch); @@ -410,7 +410,7 @@ void glue_loadSample(void* data) /* -------------------------------------------------------------------------- */ -void glue_saveSample(void *data) +void glue_saveSample(void* data) { using namespace giada::m; diff --git a/src/gui/dialogs/channelNameInput.cpp b/src/gui/dialogs/channelNameInput.cpp index f2423a2..9380852 100644 --- a/src/gui/dialogs/channelNameInput.cpp +++ b/src/gui/dialogs/channelNameInput.cpp @@ -54,7 +54,7 @@ gdChannelNameInput::gdChannelNameInput(Channel* ch) 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); diff --git a/src/gui/dialogs/gd_actionEditor.cpp b/src/gui/dialogs/gd_actionEditor.cpp index becac71..885f549 100644 --- a/src/gui/dialogs/gd_actionEditor.cpp +++ b/src/gui/dialogs/gd_actionEditor.cpp @@ -61,7 +61,7 @@ gdActionEditor::gdActionEditor(Channel *chan) 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--][+][-]| */ @@ -70,7 +70,7 @@ gdActionEditor::gdActionEditor(Channel *chan) 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"); @@ -99,7 +99,7 @@ gdActionEditor::gdActionEditor(Channel *chan) scroller = new geScroll(8, 36, w()-16, h()-44); - if (chan->type == CHANNEL_SAMPLE) { + if (chan->type == G_CHANNEL_SAMPLE) { SampleChannel *ch = (SampleChannel*) chan; @@ -188,7 +188,7 @@ void gdActionEditor::__cb_zoomIn() 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()); @@ -222,7 +222,7 @@ void gdActionEditor::__cb_zoomOut() 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()); @@ -254,10 +254,10 @@ void gdActionEditor::__cb_zoomOut() 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()); } } diff --git a/src/gui/dialogs/midiIO/midiInputChannel.cpp b/src/gui/dialogs/midiIO/midiInputChannel.cpp index dffd298..3fec317 100644 --- a/src/gui/dialogs/midiIO/midiInputChannel.cpp +++ b/src/gui/dialogs/midiIO/midiInputChannel.cpp @@ -59,7 +59,7 @@ gdMidiInputChannel::gdMidiInputChannel(Channel* ch) 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(); @@ -98,7 +98,7 @@ gdMidiInputChannel::gdMidiInputChannel(Channel* ch) 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(ch)->midiInVeloAsVol); veloAsVol->callback(cb_veloAsVol, (void*)this); } @@ -122,7 +122,7 @@ gdMidiInputChannel::gdMidiInputChannel(Channel* ch) 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); @@ -165,7 +165,7 @@ void gdMidiInputChannel::addChannelLearners() 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(ch))->midiInPitch, ch); new geMidiLearner(0, 0, LEARNER_WIDTH, "read actions", cb_learn, @@ -239,8 +239,7 @@ void gdMidiInputChannel::cb_veloAsVol() 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); } diff --git a/src/gui/dialogs/sampleEditor.cpp b/src/gui/dialogs/sampleEditor.cpp index 275f5d7..ee000d3 100644 --- a/src/gui/dialogs/sampleEditor.cpp +++ b/src/gui/dialogs/sampleEditor.cpp @@ -85,7 +85,7 @@ gdSampleEditor::gdSampleEditor(SampleChannel* ch) 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) @@ -199,9 +199,9 @@ Fl_Group* gdSampleEditor::createPreviewBox(int x, int y, int h) play->callback(cb_togglePreview, (void*)this); rewind->callback(cb_rewindPreview, (void*)this); - ch->setOnEndPreviewCb([this] { + ch->onPreviewEnd = [this] { play->value(0); - }); + }; return g; } diff --git a/src/gui/elems/actionEditor/action.cpp b/src/gui/elems/actionEditor/action.cpp index 8516970..ee8c954 100644 --- a/src/gui/elems/actionEditor/action.cpp +++ b/src/gui/elems/actionEditor/action.cpp @@ -173,11 +173,6 @@ int geAction::handle(int e) 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 diff --git a/src/gui/elems/actionEditor/actionEditor.cpp b/src/gui/elems/actionEditor/actionEditor.cpp index db60c35..5adfc13 100644 --- a/src/gui/elems/actionEditor/actionEditor.cpp +++ b/src/gui/elems/actionEditor/actionEditor.cpp @@ -67,10 +67,10 @@ geActionEditor::geActionEditor(int x, int y, gdActionEditor *pParent, SampleChan 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; diff --git a/src/gui/elems/actionEditor/envelopeEditor.cpp b/src/gui/elems/actionEditor/envelopeEditor.cpp index 0615b7f..22b97d5 100644 --- a/src/gui/elems/actionEditor/envelopeEditor.cpp +++ b/src/gui/elems/actionEditor/envelopeEditor.cpp @@ -195,8 +195,8 @@ int geEnvelopeEditor::handle(int e) { 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; } @@ -256,8 +256,8 @@ int geEnvelopeEditor::handle(int e) { if (newFrame < 0) newFrame = 0; - else if (newFrame > clock::getTotalFrames()) - newFrame = clock::getTotalFrames(); + else if (newFrame > clock::getFramesInLoop()) + newFrame = clock::getFramesInLoop(); /* vertical line check */ diff --git a/src/gui/elems/actionEditor/gridTool.cpp b/src/gui/elems/actionEditor/gridTool.cpp index a481fb3..4f17dbb 100644 --- a/src/gui/elems/actionEditor/gridTool.cpp +++ b/src/gui/elems/actionEditor/gridTool.cpp @@ -133,20 +133,20 @@ void geGridTool::calc() * 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; itotalWidth; 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++; } diff --git a/src/gui/elems/actionEditor/muteEditor.cpp b/src/gui/elems/actionEditor/muteEditor.cpp index cba778f..9ad1284 100644 --- a/src/gui/elems/actionEditor/muteEditor.cpp +++ b/src/gui/elems/actionEditor/muteEditor.cpp @@ -244,8 +244,8 @@ int geMuteEditor::handle(int e) { /* 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; } diff --git a/src/gui/elems/actionEditor/pianoRoll.cpp b/src/gui/elems/actionEditor/pianoRoll.cpp index e28656b..3466852 100644 --- a/src/gui/elems/actionEditor/pianoRoll.cpp +++ b/src/gui/elems/actionEditor/pianoRoll.cpp @@ -75,10 +75,10 @@ void gePianoRoll::build() clear(); - int channel = pParent->chan->index; - int maxFrame = m::clock::getTotalFrames(); + int channel = pParent->chan->index; + int maxSample = m::clock::getFramesInLoop(); - vector actions = c::recorder::getMidiActions(channel, maxFrame); + vector actions = c::recorder::getMidiActions(channel, maxSample); for (Composite composite : actions) { m::MidiEvent e1 = composite.a1.iValue; diff --git a/src/gui/elems/mainWindow/keyboard/channel.cpp b/src/gui/elems/mainWindow/keyboard/channel.cpp index a222174..48db620 100644 --- a/src/gui/elems/mainWindow/keyboard/channel.cpp +++ b/src/gui/elems/mainWindow/keyboard/channel.cpp @@ -92,7 +92,7 @@ void geChannel::cb_mute() void geChannel::cb_solo() { - solo->value() ? c::channel::setSoloOn(ch) : c::channel::setSoloOff(ch); + c::channel::toggleSolo(ch); } diff --git a/src/gui/elems/mainWindow/keyboard/channelStatus.cpp b/src/gui/elems/mainWindow/keyboard/channelStatus.cpp index 509df92..6d95b46 100644 --- a/src/gui/elems/mainWindow/keyboard/channelStatus.cpp +++ b/src/gui/elems/mainWindow/keyboard/channelStatus.cpp @@ -63,7 +63,7 @@ void geChannelStatus::draw() 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)) diff --git a/src/gui/elems/mainWindow/keyboard/column.cpp b/src/gui/elems/mainWindow/keyboard/column.cpp index 74861af..0f39ec5 100644 --- a/src/gui/elems/mainWindow/keyboard/column.cpp +++ b/src/gui/elems/mainWindow/keyboard/column.cpp @@ -112,7 +112,7 @@ int geColumn::handle(int e) for (string& path : paths) { gu_log("[geColumn::handle] loading %s...\n", path.c_str()); SampleChannel* c = static_cast(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); @@ -217,7 +217,7 @@ geChannel* geColumn::addChannel(Channel* ch, int size) /* 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(ch)); else gch = new geMidiChannel(x(), 0, w(), size, static_cast(ch)); @@ -281,9 +281,9 @@ int geColumn::openTypeMenu() 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; } diff --git a/src/gui/elems/mainWindow/keyboard/midiChannel.cpp b/src/gui/elems/mainWindow/keyboard/midiChannel.cpp index 8e1957c..9f913e7 100644 --- a/src/gui/elems/mainWindow/keyboard/midiChannel.cpp +++ b/src/gui/elems/mainWindow/keyboard/midiChannel.cpp @@ -148,7 +148,7 @@ void menuCallback(Fl_Widget* w, void* v) 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(); @@ -292,23 +292,23 @@ void geMidiChannel::update() const MidiChannel* mch = static_cast(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; diff --git a/src/gui/elems/mainWindow/keyboard/sampleChannel.cpp b/src/gui/elems/mainWindow/keyboard/sampleChannel.cpp index 08b582b..d477915 100644 --- a/src/gui/elems/mainWindow/keyboard/sampleChannel.cpp +++ b/src/gui/elems/mainWindow/keyboard/sampleChannel.cpp @@ -208,7 +208,7 @@ void menuCallback(Fl_Widget* w, void* v) geSampleChannel::geSampleChannel(int X, int Y, int W, int H, SampleChannel* ch) - : geChannel(X, Y, W, H, CHANNEL_SAMPLE, ch) + : geChannel(X, Y, W, H, G_CHANNEL_SAMPLE, ch) { begin(); @@ -380,7 +380,7 @@ void geSampleChannel::refresh() setColorsByStatus(ch->status, ch->recStatus); if (static_cast(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)) @@ -420,10 +420,10 @@ void geSampleChannel::update() 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; } @@ -439,13 +439,13 @@ void geSampleChannel::update() 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; diff --git a/src/gui/elems/mainWindow/mainMenu.cpp b/src/gui/elems/mainWindow/mainMenu.cpp index 572842a..ced6cb8 100644 --- a/src/gui/elems/mainWindow/mainMenu.cpp +++ b/src/gui/elems/mainWindow/mainMenu.cpp @@ -183,7 +183,7 @@ void geMainMenu::__cb_edit() break; } for (unsigned i=0; itype == CHANNEL_SAMPLE) + if (mixer::channels.at(i)->type == G_CHANNEL_SAMPLE) if (((SampleChannel*)mixer::channels.at(i))->wave != nullptr) { menu[0].activate(); break; @@ -209,7 +209,7 @@ void geMainMenu::__cb_edit() 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) { diff --git a/src/gui/elems/sampleEditor/boostTool.cpp b/src/gui/elems/sampleEditor/boostTool.cpp index a0e1eac..624658d 100644 --- a/src/gui/elems/sampleEditor/boostTool.cpp +++ b/src/gui/elems/sampleEditor/boostTool.cpp @@ -122,7 +122,7 @@ void geBoostTool::cb_normalize() { 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(window())->waveTools->updateWaveform(); } diff --git a/src/gui/elems/sampleEditor/pitchTool.cpp b/src/gui/elems/sampleEditor/pitchTool.cpp index 33173ca..0cc1e47 100644 --- a/src/gui/elems/sampleEditor/pitchTool.cpp +++ b/src/gui/elems/sampleEditor/pitchTool.cpp @@ -141,7 +141,7 @@ void gePitchTool::__cb_setPitchDouble() 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()); } @@ -151,7 +151,7 @@ void gePitchTool::__cb_setPitchToBar() 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()); } diff --git a/src/gui/elems/sampleEditor/shiftTool.cpp b/src/gui/elems/sampleEditor/shiftTool.cpp index bbcf127..5f0a2f8 100644 --- a/src/gui/elems/sampleEditor/shiftTool.cpp +++ b/src/gui/elems/sampleEditor/shiftTool.cpp @@ -53,7 +53,7 @@ geShiftTool::geShiftTool(int x, int y, SampleChannel* ch) 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); @@ -92,7 +92,7 @@ void geShiftTool::cb_reset() void geShiftTool::refresh() { - m_shift->value(gu_iToString(m_ch->getShift()).c_str()); + m_shift->value(gu_iToString(m_ch->shift).c_str()); } diff --git a/src/gui/elems/sampleEditor/volumeTool.cpp b/src/gui/elems/sampleEditor/volumeTool.cpp index ddcdfb3..4176150 100644 --- a/src/gui/elems/sampleEditor/volumeTool.cpp +++ b/src/gui/elems/sampleEditor/volumeTool.cpp @@ -71,7 +71,7 @@ void geVolumeTool::refresh() 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()); diff --git a/src/gui/elems/sampleEditor/waveform.cpp b/src/gui/elems/sampleEditor/waveform.cpp index 806356f..7e3c447 100644 --- a/src/gui/elems/sampleEditor/waveform.cpp +++ b/src/gui/elems/sampleEditor/waveform.cpp @@ -302,7 +302,7 @@ void geWaveform::drawStartEndPoints() 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); } @@ -621,7 +621,7 @@ void geWaveform::clearSel() 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()); diff --git a/src/utils/gui.cpp b/src/utils/gui.cpp index 8af7901..ec4de69 100644 --- a/src/utils/gui.cpp +++ b/src/utils/gui.cpp @@ -111,8 +111,8 @@ int gu_getBlinker() void gu_updateControls() { - for (unsigned i=0; iguiChannel->update(); + for (const Channel* ch : mixer::channels) + ch->guiChannel->update(); G_MainWin->mainIO->setOutVol(mixer::outVol); G_MainWin->mainIO->setInVol(mixer::inVol); @@ -132,7 +132,7 @@ void gu_updateControls() /* -------------------------------------------------------------------------- */ -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()); @@ -142,7 +142,7 @@ void gu_updateMainWinLabel(const string &s) /* -------------------------------------------------------------------------- */ -void gu_setFavicon(Fl_Window *w) +void gu_setFavicon(Fl_Window* w) { #if defined(__linux__) @@ -163,7 +163,7 @@ void gu_setFavicon(Fl_Window *w) /* -------------------------------------------------------------------------- */ -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); @@ -181,7 +181,7 @@ void gu_refreshActionEditor() { /** 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); diff --git a/tests/audioBuffer.cpp b/tests/audioBuffer.cpp new file mode 100644 index 0000000..254941b --- /dev/null +++ b/tests/audioBuffer.cpp @@ -0,0 +1,100 @@ +#include +#include "../src/core/audioBuffer.h" +#include + + +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 wave; - - SECTION("test basename") - { - wave = std::unique_ptr(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(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(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(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"); + } } } diff --git a/tests/waveFx.cpp b/tests/waveFx.cpp index 9c16d28..e12c8cb 100644 --- a/tests/waveFx.cpp +++ b/tests/waveFx.cpp @@ -15,28 +15,27 @@ TEST_CASE("Test waveFx") static const int BUFFER_SIZE = 4000; static const int BIT_DEPTH = 32; - std::unique_ptr waveMono = std::unique_ptr(new Wave(new float[BUFFER_SIZE], - BUFFER_SIZE, 1, SAMPLE_RATE, BIT_DEPTH, "path/to/sample.wav")); - - std::unique_ptr waveStereo = std::unique_ptr(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); } } @@ -44,23 +43,19 @@ TEST_CASE("Test waveFx") { int a = 20; int b = 57; - wfx::silence(waveStereo.get(), a, b); + wfx::silence(waveStereo, a, b); - for (int i=a; igetFrame(i); - for (int k=0; kgetChannels(); k++) - REQUIRE(frame[k] == 0.0f); - } + for (int i=a; igetFrame(i); - for (int k=0; kgetChannels(); k++) - REQUIRE(frame[k] == 0.0f); - } + for (int i=a; igetSize(); + 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); } } @@ -88,13 +83,13 @@ TEST_CASE("Test waveFx") 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); } } @@ -103,21 +98,21 @@ TEST_CASE("Test waveFx") 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); } } @@ -126,18 +121,18 @@ TEST_CASE("Test waveFx") 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); } } } diff --git a/tests/waveManager.cpp b/tests/waveManager.cpp index f9b2eb8..d32be1b 100644 --- a/tests/waveManager.cpp +++ b/tests/waveManager.cpp @@ -35,13 +35,13 @@ TEST_CASE("Test waveManager") 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(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); -- 2.30.2