--- /dev/null
+#!/usr/bin/env bash
+
+mkdir build
+
+if [[ $TRAVIS_OS_NAME == 'osx' ]]; then
+
+ cp giada_osx ./build
+ upx --best ./build/giada_osx
+
+elif [[ $TRAVIS_OS_NAME == 'linux' ]]; then
+
+ : # null command - nothing to do
+
+ # TODO
+ # cp giada_lin ./build
+
+fi
\ No newline at end of file
--- /dev/null
+#!/usr/bin/env bash
+
+if [[ $TRAVIS_OS_NAME == 'osx' ]]; then
+
+ echo ""
+
+elif [[ $TRAVIS_OS_NAME == 'linux' ]]; then
+
+ sudo add-apt-repository ppa:ubuntu-toolchain-r/test -y # for gcc 6
+ sudo apt-get update -qq
+
+fi
\ No newline at end of file
--- /dev/null
+#!/usr/bin/env bash
+
+if [[ $TRAVIS_OS_NAME == 'osx' ]]; then
+
+ ./autogen.sh
+ ./configure --target=osx --enable-vst
+
+elif [[ $TRAVIS_OS_NAME == 'linux' ]]; then
+
+ ./autogen.sh
+ ./configure --target=linux #--enable-vst
+
+fi
\ No newline at end of file
--- /dev/null
+#!/usr/bin/env bash
+
+if [[ $TRAVIS_OS_NAME == 'osx' ]]; then
+
+ brew update
+ brew install rtmidi
+ brew install jansson
+ brew install libsamplerate
+ brew install fltk
+ brew install libsndfile
+ brew install upx
+
+ #ls Remove dynamic libraries to force static linking.
+
+ rm -rf /usr/local/lib/librtmidi.dylib
+ rm -rf /usr/local/lib/librtmidi.4.dylib
+ rm -rf /usr/local/lib/libjansson.dylib
+ rm -rf /usr/local/lib/libjansson.4.dylib
+ rm -rf /usr/local/lib/libsamplerate.dylib
+ rm -rf /usr/local/lib/libsamplerate.0.dylib
+ rm -rf /usr/local/lib/libfltk.1.3.dylib
+ rm -rf /usr/local/lib/libfltk.dylib
+ rm -rf /usr/local/lib/libfltk_forms.1.3.dylib
+ rm -rf /usr/local/lib/libfltk_forms.dylib
+ rm -rf /usr/local/lib/libfltk_forms.dylib
+ rm -rf /usr/local/lib/libfltk_gl.1.3.dylib
+ rm -rf /usr/local/lib/libfltk_gl.dylib
+ rm -rf /usr/local/lib/libfltk_images.1.3.dylib
+ rm -rf /usr/local/lib/libfltk_images.dylib
+ rm -rf /usr/local/lib/libsndfile.1.dylib
+ rm -rf /usr/local/lib/libsndfile.dylib
+ rm -rf /usr/local/lib/libFLAC++.6.dylib
+ rm -rf /usr/local/lib/libFLAC++.dylib
+ rm -rf /usr/local/lib/libFLAC.8.dylib
+ rm -rf /usr/local/lib/libFLAC.dylib
+ rm -rf /usr/local/lib/libogg.0.dylib
+ rm -rf /usr/local/lib/libogg.dylib
+ rm -rf /usr/local/lib/libvorbis.0.dylib
+ rm -rf /usr/local/lib/libvorbis.dylib
+ rm -rf /usr/local/lib/libvorbisenc.2.dylib
+ rm -rf /usr/local/lib/libvorbisenc.dylib
+
+ # TODO - what about midimaps?
+
+elif [[ $TRAVIS_OS_NAME == 'linux' ]]; then
+
+ sudo apt-get install -y gcc-6 g++-6 libsndfile1-dev libsamplerate0-dev \
+ libfltk1.3-dev libasound2-dev libxpm-dev libpulse-dev libjack-dev \
+ libxrandr-dev libx11-dev libxinerama-dev libxcursor-dev
+
+ # Symlink gcc in order to use the latest version
+
+ sudo ln -f -s /usr/bin/g++-6 /usr/bin/g++
+
+ # Download and build latest version of RtMidi
+
+ wget https://github.com/thestk/rtmidi/archive/master.zip
+ unzip master.zip
+ cd rtmidi-master && ./autogen.sh && ./configure --with-jack --with-alsa && make && sudo make install || true
+ cd ..
+
+ #wget http://www.music.mcgill.ca/~gary/rtmidi/release/rtmidi-2.1.1.tar.gz
+ #tar -xvf rtmidi-2.1.1.tar.gz
+ #cd rtmidi-2.1.1 && ./configure --with-jack --with-alsa && make && sudo make install || true
+ #cd ..
+
+ # Download and install latest version of Jansson
+ # TODO - no longer needed! Use apt instead
+
+ wget http://www.digip.org/jansson/releases/jansson-2.7.tar.gz
+ tar -xvf jansson-2.7.tar.gz
+ cd jansson-2.7 && ./configure && make && sudo make install || true
+ sudo ldconfig
+ cd ..
+
+ # Download midimaps package for testing purposes
+
+ wget https://github.com/monocasual/giada-midimaps/archive/master.zip -O giada-midimaps-master.zip
+ unzip giada-midimaps-master.zip
+ mkdir -p $HOME/.giada/midimaps
+ cp giada-midimaps-master/midimaps/* $HOME/.giada/midimaps
+
+ # Download vst plugin for testing purposes
+
+ #- wget http://www.discodsp.com/download/?id=18 -O bliss-linux.zip
+ #- unzip bliss-linux.zip -d bliss-linux
+ #- cp bliss-linux/64-bit/Bliss64Demo.so .
+
+fi
\ No newline at end of file
--- /dev/null
+#!/usr/bin/env bash
+
+make -j 2
+make rename
+make check -j 2
\ No newline at end of file
--------------------------------------------------------------------------------
+0.14.5 --- 2018 . 01 . 15
+- OS X builds on Travis CI
+- AppImage executable for Linux
+- Support for multiple plug-in directories
+- New directory browser for adding plug-in directories
+- Update plug-in's parameters on program change in plug-in's window
+- Improved MIDI action management in Piano Roll
+- Simplified conditional rules in Makefile.am
+- Fix crash on MIDI learn for plug-in parameters
+- Fix crash in MIDI input window if MIDI in params are 0
+- Fix unwanted new action when dragging piano items in Piano Roll
+- Fix crash while recording on existing project (GitHub #161)
+- Fix crash on startup in Windows build
+
+
0.14.4 --- 2017 . 10 . 28
- Renameable channels
- Portable VST path
AUTOMAKE_OPTIONS = foreign
+# Define conditional variables -------------------------------------------------
+# WITH_VST, LINUX, WINDOWS and OSX are varibles defined via AM_CONDITIONAL
+# inside configure.ac.
+
+extraSources =
+cppFlags =
+cxxFlags = -std=c++11 -Wall -Werror
+ldAdd =
+ldFlags =
+
+if WITH_VST
+
+extraSources += \
+ src/deps/juce/modules/juce_audio_basics/juce_audio_basics.cpp \
+ src/deps/juce/modules/juce_audio_processors/juce_audio_processors.cpp \
+ src/deps/juce/modules/juce_core/juce_core.cpp \
+ src/deps/juce/modules/juce_data_structures/juce_data_structures.cpp \
+ src/deps/juce/modules/juce_events/juce_events.cpp \
+ src/deps/juce/modules/juce_graphics/juce_graphics.cpp \
+ src/deps/juce/modules/juce_gui_basics/juce_gui_basics.cpp \
+ src/deps/juce/modules/juce_gui_extra/juce_gui_extra.cpp
+
+cppFlags += \
+ -I./src/deps/juce/modules \
+ -I./src/deps/vst \
+ -I/usr/include \
+ -I/usr/include/freetype2 \
+ -DJUCE_GLOBAL_MODULE_SETTINGS_INCLUDED=1 \
+ -DJUCE_STANDALONE_APPLICATION=1 \
+ -DJUCE_PLUGINHOST_VST=1 \
+ -DJUCE_PLUGINHOST_VST3=0 \
+ -DJUCE_PLUGINHOST_AU=0 \
+ -DJUCE_WEB_BROWSER=0
+
+endif
+
+if WINDOWS
+
+extraSources += \
+ src/deps/rtaudio-mod/include/asio.h \
+ src/deps/rtaudio-mod/include/asio.cpp \
+ src/deps/rtaudio-mod/include/asiolist.h \
+ src/deps/rtaudio-mod/include/asiolist.cpp \
+ src/deps/rtaudio-mod/include/asiodrivers.h \
+ src/deps/rtaudio-mod/include/asiodrivers.cpp \
+ src/deps/rtaudio-mod/include/iasiothiscallresolver.h \
+ src/deps/rtaudio-mod/include/iasiothiscallresolver.cpp \
+ resource.rc
+
+cppFlags += \
+ -I./src/deps/rtaudio-mod/include \
+ -D__WINDOWS_ASIO__ \
+ -D__WINDOWS_WASAPI__ \
+ -D__WINDOWS_DS__
+
+cxxFlags += -Wno-error
+
+ldAdd += -ldsound -lwsock32 -lm -lfltk -lwininet -lgdi32 -lshell32 -lvfw32 \
+ -lrpcrt4 -luuid -lcomctl32 -lole32 -lws2_32 -lsndfile -lsamplerate -lrtmidi \
+ -lwinmm -lsetupapi -lksuser -ljansson -limm32 -lglu32 -lshell32 -lversion \
+ -lopengl32 -loleaut32 -lshlwapi -lcomdlg32
+
+# Generate a GUI application (-mwindows), make the build static (-static).
+ldFlags += -mwindows -static
+
+endif
+
+if LINUX
+
+# Add preprocessor flags to enable ALSA, Pulse and JACK in RtAudio.
+cppFlags += -D__LINUX_ALSA__ -D__LINUX_PULSE__ -D__UNIX_JACK__
+
+# Don't stop on JUCE's unused functions.
+cxxFlags += -Wno-error=unused-function
+
+ldAdd += -lsndfile -lfltk -lXext -lX11 -lXft -lXpm -lm -ljack -lasound \
+ -lpthread -ldl -lpulse-simple -lpulse -lsamplerate -lrtmidi -ljansson \
+ -lfreetype
+
+endif
+
+if OSX
+
+extraSources += src/utils/cocoa.mm src/utils/cocoa.h
+
+# -ObjC++: Juce requires to build some Objective C code
+cxxFlags += -ObjC++ -Wno-auto-var-id
+
+ldAdd += -lsndfile -lfltk -lrtmidi -lsamplerate -ljansson -lm -lpthread \
+ -lFLAC -logg -lvorbis -lvorbisenc
+
+ldFlags += -framework CoreAudio -framework Cocoa -framework Carbon \
+ -framework CoreMIDI -framework CoreFoundation -framework Accelerate \
+ -framework WebKit -framework QuartzCore -framework IOKit
+
+endif
+
# make giada -------------------------------------------------------------------
bin_PROGRAMS = giada
src/core/channel.cpp \
src/core/sampleChannel.h \
src/core/sampleChannel.cpp \
+src/core/midiDispatcher.h \
+src/core/midiDispatcher.cpp \
src/core/midiChannel.h \
src/core/midiChannel.cpp \
src/core/midiMapConf.h \
src/core/midiMapConf.cpp \
+src/core/midiEvent.h \
+src/core/midiEvent.cpp \
src/core/conf.h \
src/core/conf.cpp \
src/core/kernelAudio.h \
src/gui/dialogs/pluginChooser.cpp \
src/gui/dialogs/browser/browserBase.h \
src/gui/dialogs/browser/browserBase.cpp \
+src/gui/dialogs/browser/browserDir.h \
+src/gui/dialogs/browser/browserDir.cpp \
src/gui/dialogs/browser/browserLoad.h \
src/gui/dialogs/browser/browserLoad.cpp \
src/gui/dialogs/browser/browserSave.h \
src/utils/deps.h \
src/utils/deps.cpp \
src/utils/string.h \
-src/utils/string.cpp
-
-if WITH_VST
-giada_SOURCES += \
-src/deps/juce/modules/juce_audio_basics/juce_audio_basics.cpp \
-src/deps/juce/modules/juce_audio_processors/juce_audio_processors.cpp \
-src/deps/juce/modules/juce_core/juce_core.cpp \
-src/deps/juce/modules/juce_data_structures/juce_data_structures.cpp \
-src/deps/juce/modules/juce_events/juce_events.cpp \
-src/deps/juce/modules/juce_graphics/juce_graphics.cpp \
-src/deps/juce/modules/juce_gui_basics/juce_gui_basics.cpp \
-src/deps/juce/modules/juce_gui_extra/juce_gui_extra.cpp
-endif
+src/utils/string.cpp \
+src/deps/rtaudio-mod/RtAudio.h \
+src/deps/rtaudio-mod/RtAudio.cpp
-# Check for environment: WITH_VST, LINUX, WINDOWS and OSX are varibles defined
-# via AM_CONDITIONAL inside configure.ac.
-# Note: CPPFLAGS = C preprocessor flags, CXXFLAGS = C++ compiler flags.
-
-giada_CXXFLAGS = -std=c++11 -Wall -Werror
-giada_CPPFLAGS =
-
-# TODO - these are flags for Linux only!
-# Also, JUCE makes GCC complain if compiled with optimization set to -O2.
-# Call configure script as follows:
-#
-# ./configure CXXFLAGS='-g -O1 -pedantic' --target=linux --enable-vst
-#
-
-if WITH_VST
-giada_CPPFLAGS += \
- -I./src/deps/juce/modules \
- -I./src/deps/vst \
- -I/usr/include \
- -I/usr/include/freetype2 \
- -DJUCE_GLOBAL_MODULE_SETTINGS_INCLUDED=1 \
- -DJUCE_STANDALONE_APPLICATION=1 \
- -DJUCE_PLUGINHOST_VST=1 \
- -DJUCE_PLUGINHOST_VST3=0 \
- -DJUCE_PLUGINHOST_AU=0 \
- -DJUCE_WEB_BROWSER=0
-endif
-
-if LINUX
-giada_SOURCES += src/deps/rtaudio-mod/RtAudio.h src/deps/rtaudio-mod/RtAudio.cpp
-# -Wno-error=unused-function: don't stop on JUCE's unused functions
-giada_CXXFLAGS += -Wno-error=unused-function
-giada_CPPFLAGS += -D__LINUX_ALSA__ -D__LINUX_PULSE__ -D__UNIX_JACK__
-giada_LDADD = -lsndfile -lfltk -lXext -lX11 -lXft -lXpm -lm -ljack -lasound \
- -lpthread -ldl -lpulse-simple -lpulse -lsamplerate -lrtmidi -ljansson \
- -lfreetype
-endif
-
-if WINDOWS
-giada_SOURCES += \
-src/deps/rtaudio-mod/RtAudio.h \
-src/deps/rtaudio-mod/RtAudio.cpp \
-src/deps/rtaudio-mod/include/asio.h \
-src/deps/rtaudio-mod/include/asio.cpp \
-src/deps/rtaudio-mod/include/asiolist.h \
-src/deps/rtaudio-mod/include/asiolist.cpp \
-src/deps/rtaudio-mod/include/asiodrivers.h \
-src/deps/rtaudio-mod/include/asiodrivers.cpp \
-src/deps/rtaudio-mod/include/iasiothiscallresolver.h \
-src/deps/rtaudio-mod/include/iasiothiscallresolver.cpp
-giada_CXXFLAGS += -Wno-error
-giada_CPPFLAGS += \
--I./src/deps/rtaudio-mod/include \
--D__WINDOWS_ASIO__ \
--D__WINDOWS_WASAPI__ \
--D__WINDOWS_DS__
-giada_LDADD = -ldsound -lwsock32 -lm -lfltk -lwininet -lgdi32 \
- -lshell32 -lvfw32 -lrpcrt4 -luuid -lcomctl32 -lole32 -lws2_32 -lsndfile \
- -lsamplerate -lrtmidi -lwinmm -lsetupapi -lksuser -ljansson \
- -limm32 -lglu32 -lshell32 -lversion -lopengl32 -loleaut32 -lshlwapi -lcomdlg32
-giada_LDFLAGS = -mwindows -static
-giada_SOURCES += resource.rc
-endif
-
-if OSX
-giada_SOURCES += src/utils/cocoa.mm src/utils/cocoa.h
-# -ObjC++: Juce requires to build some Objective C code
-giada_CXXFLAGS += -ObjC++ -Wno-auto-var-id
-giada_LDADD = -lsndfile -lm -lpthread -lfltk -lrtmidi -lrtaudio \
- -lsamplerate -ljansson
-giada_LDFLAGS = -framework CoreAudio -framework Cocoa -framework Carbon \
- -framework CoreMIDI -framework CoreFoundation -framework Accelerate \
- -framework WebKit -framework QuartzCore -framework IOKit
-endif
-
-# used only under MinGW to compile the resource.rc file (program icon)
+giada_SOURCES += $(extraSources)
+giada_CPPFLAGS = $(cppFlags)
+giada_CXXFLAGS = $(cxxFlags)
+giada_LDADD = $(ldAdd)
+giada_LDFLAGS = $(ldFlags)
+# Used only under MinGW to compile the resource.rc file (program icon)
resource.o:
windres src/ext/resource.rc -o resource.o
-# make test --------------------------------------------------------------------
+# make check -------------------------------------------------------------------
TESTS = giada_tests
+
check_PROGRAMS = giada_tests
giada_tests_SOURCES = \
tests/main.cpp \
src/utils/time.cpp \
src/utils/log.cpp
-if WITH_VST
-giada_tests_SOURCES += \
-src/deps/juce/modules/juce_audio_basics/juce_audio_basics.cpp \
-src/deps/juce/modules/juce_audio_processors/juce_audio_processors.cpp \
-src/deps/juce/modules/juce_core/juce_core.cpp \
-src/deps/juce/modules/juce_data_structures/juce_data_structures.cpp \
-src/deps/juce/modules/juce_events/juce_events.cpp \
-src/deps/juce/modules/juce_graphics/juce_graphics.cpp \
-src/deps/juce/modules/juce_gui_basics/juce_gui_basics.cpp \
-src/deps/juce/modules/juce_gui_extra/juce_gui_extra.cpp
-endif
-
-giada_tests_LDADD = -ljansson -lsndfile -lsamplerate -lfltk -lXext -lX11 -lXft \
- -lXpm -lm -ljack -lasound -lpthread -ldl -lpulse-simple -lpulse -lrtmidi \
- -lfreetype
-
-giada_tests_CXXFLAGS = -std=c++11
-
-if WITH_VST
-giada_tests_CPPFLAGS = \
- -I./src/deps/juce/modules \
- -I./src/deps/vst \
- -I/usr/include \
- -I/usr/include/freetype2 \
- -DJUCE_GLOBAL_MODULE_SETTINGS_INCLUDED=1 \
- -DJUCE_STANDALONE_APPLICATION=1 \
- -DJUCE_PLUGINHOST_VST=1 \
- -DJUCE_PLUGINHOST_VST3=0 \
- -DJUCE_PLUGINHOST_AU=0 \
- -DJUCE_WEB_BROWSER=0
-endif
+giada_tests_SOURCES += $(extraSources)
+giada_tests_CPPFLAGS = $(cppFlags)
+giada_tests_CXXFLAGS = $(cxxFlags)
+giada_tests_LDADD = $(ldAdd)
+giada_tests_LDFLAGS = $(ldFlags)
# make rename ------------------------------------------------------------------
## Copyright
-Giada is Copyright (C) 2010-2017 by Giovanni A. Zuliani | Monocasual
+Giada is Copyright (C) 2010-2018 by Giovanni A. Zuliani | Monocasual
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.
)
AC_LANG_POP
-AC_LANG_PUSH([C++])
-AC_CHECK_HEADER(
- [rtmidi/RtMidi.h],
- [],
- [AC_MSG_ERROR([library 'rtMidi' not found!])]
-)
-AC_LANG_POP
+if test "x$os" = "xosx"; then
+ AC_LANG_PUSH([C++])
+ AC_CHECK_HEADER(
+ [RtMidi.h],
+ [],
+ [AC_MSG_ERROR([library 'rtMidi' not found!])]
+ )
+ AC_LANG_POP
+else
+ AC_LANG_PUSH([C++])
+ AC_CHECK_HEADER(
+ [rtmidi/RtMidi.h],
+ [],
+ [AC_MSG_ERROR([library 'rtMidi' not found!])]
+ )
+ AC_LANG_POP
+fi
+
AC_LANG_PUSH([C++])
AC_CHECK_HEADER(
#~ )
#~ AC_LANG_POP
-# brutal and temporary hack for OS X: don't use pkg-config
+AC_LANG_PUSH([C++])
+AC_CHECK_HEADER(
+ [samplerate.h],
+ [],
+ [AC_MSG_ERROR([library 'samplerate' not found!])]
+)
+AC_LANG_POP
-if test "x$os" = "xosx"; then
- AC_LANG_PUSH([C++])
- AC_CHECK_HEADER(
- [samplerate.h],
- [],
- [AC_MSG_ERROR([library 'samplerate' not found!])]
- )
- AC_LANG_POP
-else
-# PKG_CHECK_MODULES(
-# SAMPLERATE,
-# samplerate >= 0.1.8,
-# [],
-# AC_MSG_ERROR([library 'libsamplerate' not found!])
-# )
- AC_LANG_PUSH([C++])
- AC_CHECK_HEADER(
- [samplerate.h],
- [],
- [AC_MSG_ERROR([library 'samplerate' not found!])]
- )
- AC_LANG_POP
-fi
# ------------------------------------------------------------------------------
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
Channel::Channel(int type, int status, int bufferSize)
: bufferSize (bufferSize),
- midiFilter (-1),
+ midiInFilter (-1),
previewMode (G_PREVIEW_NONE),
pan (0.5f),
armed (false),
/* -------------------------------------------------------------------------- */
-void Channel::sendMidiLmessage(uint32_t learn, const midimap::message_t &msg)
+void Channel::sendMidiLmessage(uint32_t learn, const midimap::message_t& msg)
{
gu_log("[channel::sendMidiLmessage] learn=%#X, chan=%d, msg=%#X, offset=%d\n",
learn, msg.channel, msg.value, msg.offset);
pch.midiInArm = midiInArm;
pch.midiInVolume = midiInVolume;
pch.midiInMute = midiInMute;
+ pch.midiInFilter = midiInFilter;
pch.midiInSolo = midiInSolo;
pch.midiOutL = midiOutL;
pch.midiOutLplaying = midiOutLplaying;
midiInKill = pch->midiInKill;
midiInVolume = pch->midiInVolume;
midiInMute = pch->midiInMute;
+ midiInFilter = pch->midiInFilter;
midiInSolo = pch->midiInSolo;
midiOutL = pch->midiOutL;
midiOutLplaying = pch->midiOutLplaying;
#ifdef WITH_VST
for (const patch::plugin_t& ppl : pch->plugins) {
+
Plugin* plugin = pluginHost::addPlugin(ppl.path, pluginHost::CHANNEL,
pluginMutex, this);
+
if (plugin == nullptr) {
ret &= 0;
continue;
}
+
plugin->setBypass(ppl.bypass);
+
for (unsigned j=0; j<ppl.params.size(); j++)
plugin->setParameter(j, ppl.params.at(j));
- plugin->midiInParams.clear();
- for (uint32_t midiInParam : ppl.midiInParams)
- plugin->midiInParams.push_back(midiInParam);
+
+ /* 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;
}
/* -------------------------------------------------------------------------- */
-void Channel::receiveMidi(uint32_t msg)
+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;
+}
+
+
/* -------------------------------------------------------------------------- */
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
#include <string>
#include <pthread.h>
#include "midiMapConf.h"
+#include "midiEvent.h"
#include "recorder.h"
#ifdef WITH_VST
int bufferSize;
- /* midiFilter
+ /* midiInFilter
Which MIDI channel should be filtered out when receiving MIDI messages. -1
means 'all'. */
- int midiFilter;
+ int midiInFilter;
/* previewMode
Whether the channel is in audio preview mode or not. */
/* receiveMidi
Receives and processes midi messages from external devices. */
- virtual void receiveMidi(uint32_t msg);
+ virtual void receiveMidi(const giada::m::MidiEvent& midiEvent);
/* allocBuffers
Mandatory method to allocate memory for internal buffers. Call it after the
virtual bool allocBuffers();
- /* isPlaying
- * tell wether the channel is playing or is stopped. */
-
bool isPlaying() const;
+ float getPan() 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
+ and process MIDI events on MIDI channel 'c'. */
+
+ bool isMidiInAllowed(int c) const;
/* sendMidiL*
* send MIDI lightning events to a physical device. */
void sendMidiLplay();
void setPan(float v);
- float getPan() const;
-
void setArmed(bool b);
- bool isArmed() const;
-
- std::string getName() const;
void setName(const std::string& s);
-
void setPreviewMode(int m);
- bool isPreview() const;
+ void setMidiInFilter(int c);
#ifdef WITH_VST
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
int midiSync = MIDI_SYNC_NONE;
float midiTCfps = 25.0f;
+bool midiIn = false;
+int midiInFilter = -1;
uint32_t midiInRewind = 0x0;
uint32_t midiInStartStop = 0x0;
uint32_t midiInActionRec = 0x0;
/* -------------------------------------------------------------------------- */
+bool isMidiInAllowed(int c)
+{
+ return midiInFilter == -1 || midiInFilter == c;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
int read()
{
init();
if (!storager::setString(jRoot, CONF_KEY_LAST_MIDIMAP, lastFileMap)) return 0;
if (!storager::setInt(jRoot, CONF_KEY_MIDI_SYNC, midiSync)) return 0;
if (!storager::setFloat(jRoot, CONF_KEY_MIDI_TC_FPS, midiTCfps)) return 0;
- if (!storager::setUint32(jRoot, CONF_KEY_MIDI_IN_REWIND, midiInRewind)) return 0;
+ if (!storager::setBool(jRoot, CONF_KEY_MIDI_IN, midiIn)) return 0;
+ if (!storager::setInt(jRoot, CONF_KEY_MIDI_IN_FILTER, midiInFilter)) return 0;
+ if (!storager::setUint32(jRoot, CONF_KEY_MIDI_IN_REWIND, midiInRewind)) return 0;
if (!storager::setUint32(jRoot, CONF_KEY_MIDI_IN_START_STOP, midiInStartStop)) return 0;
if (!storager::setUint32(jRoot, CONF_KEY_MIDI_IN_ACTION_REC, midiInActionRec)) return 0;
if (!storager::setUint32(jRoot, CONF_KEY_MIDI_IN_INPUT_REC, midiInInputRec)) return 0;
if (!storager::setString(jRoot, CONF_KEY_PLUGINS_PATH, pluginPath)) return 0;
if (!storager::setString(jRoot, CONF_KEY_PATCHES_PATH, patchPath)) return 0;
if (!storager::setString(jRoot, CONF_KEY_SAMPLES_PATH, samplePath)) return 0;
-
if (!storager::setInt(jRoot, CONF_KEY_MAIN_WINDOW_X, mainWindowX)) return 0;
if (!storager::setInt(jRoot, CONF_KEY_MAIN_WINDOW_Y, mainWindowY)) return 0;
if (!storager::setInt(jRoot, CONF_KEY_MAIN_WINDOW_W, mainWindowW)) return 0;
json_object_set_new(jRoot, CONF_KEY_LAST_MIDIMAP, json_string(lastFileMap.c_str()));
json_object_set_new(jRoot, CONF_KEY_MIDI_SYNC, json_integer(midiSync));
json_object_set_new(jRoot, CONF_KEY_MIDI_TC_FPS, json_real(midiTCfps));
+ json_object_set_new(jRoot, CONF_KEY_MIDI_IN, json_boolean(midiIn));
+ json_object_set_new(jRoot, CONF_KEY_MIDI_IN_FILTER, json_integer(midiInFilter));
json_object_set_new(jRoot, CONF_KEY_MIDI_IN_REWIND, json_integer(midiInRewind));
json_object_set_new(jRoot, CONF_KEY_MIDI_IN_START_STOP, json_integer(midiInStartStop));
json_object_set_new(jRoot, CONF_KEY_MIDI_IN_ACTION_REC, json_integer(midiInActionRec));
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
int read();
int write();
+/* isMidiAllowed
+Given a MIDI channel 'c' tells whether this channel should be allowed to receive
+and process MIDI events on MIDI channel 'c'. */
+
+bool isMidiInAllowed(int c);
+
extern std::string header;
extern int logMode;
extern int midiSync; // see const.h
extern float midiTCfps;
+extern bool midiIn;
+extern int midiInFilter;
extern uint32_t midiInRewind;
extern uint32_t midiInStartStop;
extern uint32_t midiInActionRec;
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
/* -- version --------------------------------------------------------------- */
#define G_APP_NAME "Giada"
-#define G_VERSION_STR "0.14.4"
+#define G_VERSION_STR "0.14.5"
#define G_VERSION_MAJOR 0
#define G_VERSION_MINOR 14
-#define G_VERSION_PATCH 4
+#define G_VERSION_PATCH 5
#define CONF_FILENAME "giada.conf"
#define G_DEFAULT_SOUNDSYS G_SYS_API_CORE
#endif
-#define G_DEFAULT_SOUNDDEV_OUT 0 // FIXME - please override with rtAudio::getDefaultDevice (or similar)
-#define G_DEFAULT_SOUNDDEV_IN -1 // no recording by default: input disabled
-#define G_DEFAULT_MIDI_SYSTEM 0
-#define G_DEFAULT_MIDI_PORT_IN -1
-#define G_DEFAULT_MIDI_PORT_OUT -1
-#define G_DEFAULT_SAMPLERATE 44100
-#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
-#define G_DEFAULT_OUT_VOL 1.0f
-#define G_DEFAULT_IN_VOL 1.0f
-#define G_DEFAULT_CHANMODE SINGLE_BASIC
-#define G_DEFAULT_BPM 120.0f
-#define G_DEFAULT_BEATS 4
-#define G_DEFAULT_BARS 1
-#define G_DEFAULT_QUANTIZE 0 // quantizer off
-#define G_DEFAULT_FADEOUT_STEP 0.01f // micro-fadeout speed
-#define G_DEFAULT_COLUMN_WIDTH 380
-#define G_DEFAULT_PATCH_NAME "(default patch)"
-#define G_DEFAULT_MIDI_INPUT_UI_W 300
-#define G_DEFAULT_MIDI_INPUT_UI_H 350
+#define G_DEFAULT_SOUNDDEV_OUT 0 // FIXME - please override with rtAudio::getDefaultDevice (or similar)
+#define G_DEFAULT_SOUNDDEV_IN -1 // no recording by default: input disabled
+#define G_DEFAULT_MIDI_SYSTEM 0
+#define G_DEFAULT_MIDI_PORT_IN -1
+#define G_DEFAULT_MIDI_PORT_OUT -1
+#define G_DEFAULT_SAMPLERATE 44100
+#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
+#define G_DEFAULT_OUT_VOL 1.0f
+#define G_DEFAULT_IN_VOL 1.0f
+#define G_DEFAULT_CHANMODE SINGLE_BASIC
+#define G_DEFAULT_BPM 120.0f
+#define G_DEFAULT_BEATS 4
+#define G_DEFAULT_BARS 1
+#define G_DEFAULT_QUANTIZE 0 // quantizer off
+#define G_DEFAULT_FADEOUT_STEP 0.01f // micro-fadeout speed
+#define G_DEFAULT_COLUMN_WIDTH 380
+#define G_DEFAULT_PATCH_NAME "(default patch)"
+#define G_DEFAULT_MIDI_INPUT_UI_W 300
+#define G_DEFAULT_MIDI_INPUT_UI_H 350
+#define G_DEFAULT_MIDI_ACTION_SIZE 8192 // frames
#define PATCH_KEY_CHANNEL_MIDI_IN_ARM "midi_in_arm"
#define PATCH_KEY_CHANNEL_MIDI_IN_VOLUME "midi_in_volume"
#define PATCH_KEY_CHANNEL_MIDI_IN_MUTE "midi_in_mute"
+#define PATCH_KEY_CHANNEL_MIDI_IN_FILTER "midi_in_filter"
#define PATCH_KEY_CHANNEL_MIDI_IN_SOLO "midi_in_solo"
#define PATCH_KEY_CHANNEL_MIDI_OUT_L "midi_out_l"
#define PATCH_KEY_CHANNEL_MIDI_OUT_L_PLAYING "midi_out_l_playing"
#define CONF_KEY_LAST_MIDIMAP "last_midimap"
#define CONF_KEY_MIDI_SYNC "midi_sync"
#define CONF_KEY_MIDI_TC_FPS "midi_tc_fps"
+#define CONF_KEY_MIDI_IN "midi_in"
+#define CONF_KEY_MIDI_IN_FILTER "midi_in_filter"
#define CONF_KEY_MIDI_IN_REWIND "midi_in_rewind"
#define CONF_KEY_MIDI_IN_START_STOP "midi_in_start_stop"
#define CONF_KEY_MIDI_IN_ACTION_REC "midi_in_action_rec"
*
* ---------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* ---------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
/* -------------------------------------------------------------------------- */
-void init_startGUI(int argc, char **argv)
+void init_startGUI(int argc, char** argv)
{
G_MainWin = new gdMainWindow(G_GUI_WIDTH, G_GUI_HEIGHT, "", argc, argv);
G_MainWin->resize(conf::mainWindowX, conf::mainWindowY, conf::mainWindowW,
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
{
namespace
{
-RtAudio *rtSystem;
-bool status;
-unsigned numDevs;
-bool inputEnabled;
-unsigned realBufsize; // reale bufsize from the soundcard
-int api;
+RtAudio *rtSystem = nullptr;
+bool status = false;
+unsigned numDevs = 0;
+bool inputEnabled = false;
+unsigned realBufsize = 0; // reale bufsize from the soundcard
+int api = 0;
#ifdef __linux__
/* -------------------------------------------------------------------------- */
-void init()
-{
- rtSystem = nullptr;
- numDevs = 0;
- inputEnabled = 0;
- realBufsize = 0;
- api = 0;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
bool getStatus()
{
return status;
int openDevice()
{
api = conf::soundSystem;
- gu_log("[KA] using rtSystem 0x%x\n", api);
+ gu_log("[KA] using system 0x%x\n", api);
#if defined(__linux__)
#endif
- else
+ else {
+ gu_log("[KA] No API available, nothing to do!\n");
return 0;
+ }
gu_log("[KA] Opening devices %d (out), %d (in), f=%d...\n",
conf::soundDeviceOut, conf::soundDeviceIn, conf::samplerate);
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
#endif
-void init();
-
int openDevice();
int closeDevice();
int startStream();
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
* -------------------------------------------------------------------------- */
-#include <rtmidi/RtMidi.h>
-#include "../utils/log.h"
-#include "../glue/channel.h"
-#include "../glue/plugin.h"
-#include "../glue/main.h"
-#include "../glue/transport.h"
-#include "../glue/io.h"
-#include "mixer.h"
#include "const.h"
-#include "channel.h"
-#include "sampleChannel.h"
-#include "midiChannel.h"
-#include "conf.h"
-#include "midiMapConf.h"
-#ifdef WITH_VST
- #include "pluginHost.h"
- #include "plugin.h"
+#ifdef G_OS_MAC
+ #include <RtMidi.h>
+#else
+ #include <rtmidi/RtMidi.h>
#endif
+#include "../utils/log.h"
+#include "midiDispatcher.h"
+#include "midiMapConf.h"
#include "kernelMidi.h"
unsigned numOutPorts = 0;
unsigned numInPorts = 0;
-/* cb_learn
- * callback prepared by the gdMidiGrabber window and called by
- * kernelMidi. It contains things to do once the midi message has been
- * stored. */
-
-cb_midiLearn* cb_learn = nullptr;
-void* cb_data = nullptr;
-
-
-/* -------------------------------------------------------------------------- */
-
-
-#ifdef WITH_VST
-
-void processPlugins(Channel* ch, uint32_t pure, uint32_t value)
-{
- /* Plugins' parameters layout reflects the structure of the matrix
- Channel::midiInPlugins. It is safe to assume then that i (i.e. Plugin*) and k
- indexes match both the structure of Channel::midiInPlugins and
- vector<Plugin*>* plugins. */
-
- vector<Plugin*>* plugins = pluginHost::getStack(pluginHost::CHANNEL, ch);
-
- for (Plugin* plugin : *plugins) {
- for (unsigned k=0; k<plugin->midiInParams.size(); k++) {
- uint32_t midiInParam = plugin->midiInParams.at(k);
- if (pure != midiInParam)
- continue;
- float vf = (value >> 8) / 127.0f;
- c::plugin::setParameter(plugin, k, vf, false); // false: not from GUI
- gu_log(" >>> [plugin %d parameter %d] ch=%d (pure=0x%X, value=%d, float=%f)\n",
- plugin->getId(), k, ch->index, pure, value >> 8, vf);
- }
- }
-}
-
-#endif
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void processChannels(uint32_t pure, uint32_t value)
-{
- for (Channel* ch : mixer::channels) {
-
- if (!ch->midiIn)
- continue;
-
- if (pure == ch->midiInKeyPress) {
- gu_log(" >>> keyPress, ch=%d (pure=0x%X)\n", ch->index, pure);
- glue_keyPress(ch, false, false);
- }
- else if (pure == ch->midiInKeyRel) {
- gu_log(" >>> keyRel ch=%d (pure=0x%X)\n", ch->index, pure);
- glue_keyRelease(ch, false, false);
- }
- else if (pure == ch->midiInMute) {
- gu_log(" >>> mute ch=%d (pure=0x%X)\n", ch->index, pure);
- glue_toggleMute(ch, false);
- }
- else if (pure == ch->midiInKill) {
- gu_log(" >>> kill ch=%d (pure=0x%X)\n", ch->index, pure);
- glue_kill(ch);
- }
- else if (pure == ch->midiInArm) {
- gu_log(" >>> arm ch=%d (pure=0x%X)\n", ch->index, pure);
- glue_toggleArm(ch, false);
- }
- else if (pure == ch->midiInSolo) {
- gu_log(" >>> solo ch=%d (pure=0x%X)\n", ch->index, pure);
- glue_toggleSolo(ch, false);
- }
- else if (pure == ch->midiInVolume) {
- float vf = (value >> 8)/127.0f;
- gu_log(" >>> volume ch=%d (pure=0x%X, value=%d, float=%f)\n",
- ch->index, pure, value >> 8, vf);
- glue_setVolume(ch, vf, false);
- }
- else {
- SampleChannel* sch = static_cast<SampleChannel*>(ch);
- if (pure == sch->midiInPitch) {
- float vf = (value >> 8)/(127/4.0f); // [0-127] ~> [0.0-4.0]
- gu_log(" >>> pitch ch=%d (pure=0x%X, value=%d, float=%f)\n",
- sch->index, pure, value >> 8, vf);
- glue_setPitch(sch, vf);
- }
- else
- if (pure == sch->midiInReadActions) {
- gu_log(" >>> toggle read actions ch=%d (pure=0x%X)\n", sch->index, pure);
- glue_toggleReadingRecs(sch, false);
- }
- }
-
-#ifdef WITH_VST
- processPlugins(ch, pure, value); // Process plugins' parameters
-#endif
-
- /* Redirect full midi message (pure + value) to plugins. */
-
- ch->receiveMidi(pure | value);
- }
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void processMaster(uint32_t pure, uint32_t value)
-{
- if (pure == conf::midiInRewind) {
- gu_log(" >>> rewind (master) (pure=0x%X)\n", pure);
- glue_rewindSeq(false);
- }
- else if (pure == conf::midiInStartStop) {
- gu_log(" >>> startStop (master) (pure=0x%X)\n", pure);
- glue_startStopSeq(false);
- }
- else if (pure == conf::midiInActionRec) {
- gu_log(" >>> actionRec (master) (pure=0x%X)\n", pure);
- glue_startStopActionRec(false);
- }
- else if (pure == conf::midiInInputRec) {
- gu_log(" >>> inputRec (master) (pure=0x%X)\n", pure);
- glue_startStopInputRec(false);
- }
- else if (pure == conf::midiInMetronome) {
- gu_log(" >>> metronome (master) (pure=0x%X)\n", pure);
- glue_startStopMetronome(false);
- }
- else if (pure == conf::midiInVolumeIn) {
- float vf = (value >> 8)/127.0f;
- gu_log(" >>> input volume (master) (pure=0x%X, value=%d, float=%f)\n",
- pure, value >> 8, vf);
- glue_setInVol(vf, false);
- }
- else if (pure == conf::midiInVolumeOut) {
- float vf = (value >> 8)/127.0f;
- gu_log(" >>> output volume (master) (pure=0x%X, value=%d, float=%f)\n",
- pure, value >> 8, vf);
- glue_setOutVol(vf, false);
- }
- else if (pure == conf::midiInBeatDouble) {
- gu_log(" >>> sequencer x2 (master) (pure=0x%X)\n", pure);
- glue_beatsMultiply();
- }
- else if (pure == conf::midiInBeatHalf) {
- gu_log(" >>> sequencer /2 (master) (pure=0x%X)\n", pure);
- glue_beatsDivide();
- }
-}
-
-
-/* -------------------------------------------------------------------------- */
-
static void callback(double t, std::vector<unsigned char>* msg, void* data)
{
- /* 0.8.0 - for now we handle other MIDI signals (common and real-time
- messages) as unknown, for debugging purposes. */
-
if (msg->size() < 3) {
//gu_log("[KM] MIDI received - unknown signal - size=%d, value=0x", (int) msg->size());
//for (unsigned i=0; i<msg->size(); i++)
//gu_log("\n");
return;
}
-
- /* Here we want to catch two things: a) note on/note off from a keyboard and
- b) knob/wheel/slider movements from a controller. */
-
- uint32_t input = getIValue(msg->at(0), msg->at(1), msg->at(2));
- uint32_t chan = input & 0x0F000000;
- uint32_t value = input & 0x0000FF00;
- uint32_t pure = 0x00;
- if (!conf::noNoteOff)
- pure = input & 0xFFFF0000; // input without 'value' (i.e. velocity) byte
- else
- pure = input & 0xFFFFFF00; // input with 'value' (i.e. velocity) byte
-
- gu_log("[KM] MIDI received - 0x%X (chan %d)\n", input, chan >> 24);
-
- /* Start dispatcher. If midi learn is on don't parse channels, just learn
- incoming MIDI signal. Otherwise process master events first, then each channel
- in the stack. This way incoming signals don't get processed by glue_* when
- MIDI learning is on. */
-
- if (cb_learn)
- cb_learn(pure, cb_data);
- else {
- processMaster(pure, value);
- processChannels(pure, value);
- }
+ midiDispatcher::dispatch(msg->at(0), msg->at(1), msg->at(2));
}
void sendMidiLightningInitMsgs()
{
- for(unsigned i=0; i<midimap::initCommands.size(); i++) {
+ for(unsigned i=0; i<midimap::initCommands.size(); i++) {
midimap::message_t msg = midimap::initCommands.at(i);
if (msg.value != 0x0 && msg.channel != -1) {
gu_log("[KM] MIDI send (init) - Channel %x - Event 0x%X\n", msg.channel, msg.value);
/* -------------------------------------------------------------------------- */
-void startMidiLearn(cb_midiLearn *cb, void *data)
-{
- cb_learn = cb;
- cb_data = data;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void stopMidiLearn()
-{
- cb_learn = nullptr;
- cb_data = nullptr;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
void setApi(int _api)
{
api = _api;
try {
midiOut = new RtMidiOut((RtMidi::Api) api, "Giada MIDI Output");
status = true;
- }
- catch (RtMidiError &error) {
- gu_log("[KM] MIDI out device error: %s\n", error.getMessage().c_str());
- status = false;
- return 0;
- }
+ }
+ catch (RtMidiError &error) {
+ gu_log("[KM] MIDI out device error: %s\n", error.getMessage().c_str());
+ status = false;
+ return 0;
+ }
/* print output ports */
numOutPorts = midiOut->getPortCount();
- gu_log("[KM] %d output MIDI ports found\n", numOutPorts);
- for (unsigned i=0; i<numOutPorts; i++)
+ gu_log("[KM] %d output MIDI ports found\n", numOutPorts);
+ for (unsigned i=0; i<numOutPorts; i++)
gu_log(" %d) %s\n", i, getOutPortName(i).c_str());
/* try to open a port, if enabled */
try {
midiIn = new RtMidiIn((RtMidi::Api) api, "Giada MIDI input");
status = true;
- }
- catch (RtMidiError &error) {
- gu_log("[KM] MIDI in device error: %s\n", error.getMessage().c_str());
- status = false;
- return 0;
- }
+ }
+ catch (RtMidiError &error) {
+ gu_log("[KM] MIDI in device error: %s\n", error.getMessage().c_str());
+ status = false;
+ return 0;
+ }
/* print input ports */
numInPorts = midiIn->getPortCount();
- gu_log("[KM] %d input MIDI ports found\n", numInPorts);
- for (unsigned i=0; i<numInPorts; i++)
+ gu_log("[KM] %d input MIDI ports found\n", numInPorts);
+ for (unsigned i=0; i<numInPorts; i++)
gu_log(" %d) %s\n", i, getInPortName(i).c_str());
/* try to open a port, if enabled */
if (!status)
return;
- vector<unsigned char> msg(1, getB1(data));
- msg.push_back(getB2(data));
- msg.push_back(getB3(data));
+ vector<unsigned char> msg(1, getB1(data));
+ msg.push_back(getB2(data));
+ msg.push_back(getB3(data));
midiOut->sendMessage(&msg);
gu_log("[KM] send msg=0x%X (%X %X %X)\n", data, msg[0], msg[1], msg[2]);
unsigned countInPorts()
{
- return numInPorts;
+ return numInPorts;
}
unsigned countOutPorts()
{
- return numOutPorts;
+ return numOutPorts;
}
uint32_t getIValue(int b1, int b2, int b3)
{
- return (b1 << 24) | (b2 << 16) | (b3 << 8) | (0x00);
+ return (b1 << 24) | (b2 << 16) | (b3 << 8) | (0x00);
}
uint32_t setChannel(uint32_t iValue, int channel)
{
- uint32_t chanMask = 0xF << 24;
- return (iValue & (~chanMask)) | (channel << 24);
+ uint32_t chanMask = 0xF << 24;
+ return (iValue & (~chanMask)) | (channel << 24);
}
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
#include <cstdint>
#endif
#include <string>
-#include <vector>
namespace giada {
namespace m {
namespace kernelMidi
{
-typedef void (cb_midiLearn) (uint32_t, void *);
-
-void startMidiLearn(cb_midiLearn *cb, void *data);
-void stopMidiLearn();
-
int getB1(uint32_t iValue);
int getB2(uint32_t iValue);
int getB3(uint32_t iValue);
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
#include "conf.h"
#include "mixer.h"
#ifdef WITH_VST
- #include "pluginHost.h"
+ #include "pluginHost.h"
#endif
#include "kernelMidi.h"
MidiChannel::MidiChannel(int bufferSize)
: Channel (CHANNEL_MIDI, STATUS_OFF, bufferSize),
- midiOut (false),
- midiOutChan(MIDI_CHANS[0])
+ midiOut (false),
+ midiOutChan(MIDI_CHANS[0])
{
}
/* -------------------------------------------------------------------------- */
-void MidiChannel::receiveMidi(uint32_t msg)
+void MidiChannel::receiveMidi(const MidiEvent& midiEvent)
{
- if (!armed)
- return;
+ if (!armed)
+ return;
- /* Filter time, based on MIDI channel. If midiFilter == -1, all messages
- are taken into account. */
- /* TODO - actual MIDI filtering not yet implemented. */
+ /* Now all messages are turned into Channel-0 messages. Giada doesn't care about
+ holding MIDI channel information. Moreover, having all internal messages on
+ channel 0 is way easier. */
- if (midiFilter != -1)
- {
- gu_log("[Channel::processMidi] MIDI channel filtering not yet implemented\n");
- return;
- }
-
- /* Now all messages are turned into Channel-0 messages. Giada doesn't care about
- holding MIDI channel information. Moreover, having all internal messages on
- channel 0 is way easier. */
-
- msg = kernelMidi::setChannel(msg, 0);
+ MidiEvent midiEventFlat(midiEvent);
+ midiEventFlat.setChannel(0);
#ifdef WITH_VST
- while (true) {
- if (pthread_mutex_trylock(&pluginHost::mutex_midi) != 0)
- continue;
- gu_log("[Channel::processMidi] msg=%X\n", msg);
- addVstMidiEvent(msg, 0);
- pthread_mutex_unlock(&pluginHost::mutex_midi);
- break;
- }
+ while (true) {
+ if (pthread_mutex_trylock(&pluginHost::mutex_midi) != 0)
+ continue;
+ gu_log("[Channel::processMidi] msg=%X\n", midiEventFlat.getRaw());
+ addVstMidiEvent(midiEventFlat.getRaw(), 0);
+ pthread_mutex_unlock(&pluginHost::mutex_midi);
+ break;
+ }
#endif
if (recorder::canRec(this, clock::isRunning(), mixer::recording)) {
- recorder::rec(index, G_ACTION_MIDI, clock::getCurrentFrame(), msg);
- hasActions = true;
- }
+ recorder::rec(index, G_ACTION_MIDI, clock::getCurrentFrame(), midiEventFlat.getRaw());
+ hasActions = true;
+ }
}
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
void onBar(int frame) override;
void parseAction(giada::m::recorder::action* a, int localFrame, int globalFrame,
int quantize, bool mixerIsRunning) override;
- void receiveMidi(uint32_t msg) override;
+ void receiveMidi(const giada::m::MidiEvent& midiEvent) override;
bool canInputRec() override;
/* sendMidi
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#include <vector>
+#include "../glue/plugin.h"
+#include "../glue/io.h"
+#include "../glue/channel.h"
+#include "../glue/transport.h"
+#include "../glue/main.h"
+#include "../utils/log.h"
+#include "channel.h"
+#include "sampleChannel.h"
+#include "midiChannel.h"
+#include "conf.h"
+#include "mixer.h"
+#include "pluginHost.h"
+#include "plugin.h"
+#include "midiDispatcher.h"
+
+
+using std::vector;
+
+
+namespace giada {
+namespace m {
+namespace midiDispatcher
+{
+namespace
+{
+/* cb_midiLearn, cb_data
+Callback prepared by the gdMidiGrabber window and called by midiDispatcher. It
+contains things to do once the midi message has been stored. */
+
+cb_midiLearn* cb_learn = nullptr;
+void* cb_data = nullptr;
+
+
+/* -------------------------------------------------------------------------- */
+
+
+#ifdef WITH_VST
+
+void processPlugins(Channel* ch, const MidiEvent& midiEvent)
+{
+ /* Pure value: if 'noNoteOff' in global config, get the raw value with the
+ 'velocy' byte. Otherwise strip it off. */
+
+ uint32_t pure = midiEvent.getRaw(conf::noNoteOff);
+
+ /* Plugins' parameters layout reflects the structure of the matrix
+ Channel::midiInPlugins. It is safe to assume then that i (i.e. Plugin*) and k
+ indexes match both the structure of Channel::midiInPlugins and
+ vector<Plugin*>* plugins. */
+
+ vector<Plugin*>* plugins = pluginHost::getStack(pluginHost::CHANNEL, ch);
+
+ for (Plugin* plugin : *plugins) {
+ for (unsigned k=0; k<plugin->midiInParams.size(); k++) {
+ uint32_t midiInParam = plugin->midiInParams.at(k);
+ if (pure != midiInParam)
+ continue;
+ float vf = midiEvent.getVelocity() / 127.0f;
+ c::plugin::setParameter(plugin, k, vf, false); // false: not from GUI
+ gu_log(" >>> [plugin %d parameter %d] ch=%d (pure=0x%X, value=%d, float=%f)\n",
+ plugin->getId(), k, ch->index, pure, midiEvent.getVelocity(), vf);
+ }
+ }
+}
+
+#endif
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void processChannels(const MidiEvent& midiEvent)
+{
+ /* Pure value: if 'noNoteOff' in global config, get the raw value with the
+ 'velocy' byte. Otherwise strip it off. */
+
+ uint32_t pure = midiEvent.getRaw(conf::noNoteOff);
+
+ for (Channel* ch : mixer::channels) {
+
+ /* Do nothing on this channel if MIDI in is disabled or filtered out for
+ the current MIDI channel. */
+
+ if (!ch->midiIn || !ch->isMidiInAllowed(midiEvent.getChannel()))
+ continue;
+
+ if (pure == ch->midiInKeyPress) {
+ gu_log(" >>> keyPress, ch=%d (pure=0x%X)\n", ch->index, pure);
+ glue_keyPress(ch, false, false);
+ }
+ else if (pure == ch->midiInKeyRel) {
+ gu_log(" >>> keyRel ch=%d (pure=0x%X)\n", ch->index, pure);
+ glue_keyRelease(ch, false, false);
+ }
+ else if (pure == ch->midiInMute) {
+ gu_log(" >>> mute ch=%d (pure=0x%X)\n", ch->index, pure);
+ glue_toggleMute(ch, false);
+ }
+ else if (pure == ch->midiInKill) {
+ gu_log(" >>> kill ch=%d (pure=0x%X)\n", ch->index, pure);
+ glue_kill(ch);
+ }
+ else if (pure == ch->midiInArm) {
+ gu_log(" >>> arm ch=%d (pure=0x%X)\n", ch->index, pure);
+ glue_toggleArm(ch, false);
+ }
+ else if (pure == ch->midiInSolo) {
+ gu_log(" >>> solo ch=%d (pure=0x%X)\n", ch->index, pure);
+ glue_toggleSolo(ch, false);
+ }
+ else if (pure == ch->midiInVolume) {
+ float vf = midiEvent.getVelocity() / 127.0f;
+ gu_log(" >>> volume ch=%d (pure=0x%X, value=%d, float=%f)\n",
+ ch->index, pure, midiEvent.getVelocity(), vf);
+ glue_setVolume(ch, vf, false);
+ }
+ else {
+ SampleChannel* sch = static_cast<SampleChannel*>(ch);
+ if (pure == sch->midiInPitch) {
+ float vf = midiEvent.getVelocity() / (127/4.0f); // [0-127] ~> [0.0-4.0]
+ gu_log(" >>> pitch ch=%d (pure=0x%X, value=%d, float=%f)\n",
+ sch->index, pure, midiEvent.getVelocity(), vf);
+ glue_setPitch(sch, vf);
+ }
+ else
+ if (pure == sch->midiInReadActions) {
+ gu_log(" >>> toggle read actions ch=%d (pure=0x%X)\n", sch->index, pure);
+ glue_toggleReadingRecs(sch, false);
+ }
+ }
+
+#ifdef WITH_VST
+ processPlugins(ch, midiEvent); // Process plugins' parameters
+#endif
+
+ /* Redirect full midi message (pure + velocity) to plugins. */
+
+ ch->receiveMidi(midiEvent.getRaw());
+ }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void processMaster(const MidiEvent& midiEvent)
+{
+ /* Pure value: if 'noNoteOff' in global config, get the raw value with the
+ 'velocy' byte. Otherwise strip it off. */
+
+ uint32_t pure = midiEvent.getRaw(conf::noNoteOff);
+
+ if (pure == conf::midiInRewind) {
+ gu_log(" >>> rewind (master) (pure=0x%X)\n", pure);
+ glue_rewindSeq(false);
+ }
+ else if (pure == conf::midiInStartStop) {
+ gu_log(" >>> startStop (master) (pure=0x%X)\n", pure);
+ glue_startStopSeq(false);
+ }
+ else if (pure == conf::midiInActionRec) {
+ gu_log(" >>> actionRec (master) (pure=0x%X)\n", pure);
+ glue_startStopActionRec(false);
+ }
+ else if (pure == conf::midiInInputRec) {
+ gu_log(" >>> inputRec (master) (pure=0x%X)\n", pure);
+ glue_startStopInputRec(false);
+ }
+ else if (pure == conf::midiInMetronome) {
+ gu_log(" >>> metronome (master) (pure=0x%X)\n", pure);
+ glue_startStopMetronome(false);
+ }
+ else if (pure == conf::midiInVolumeIn) {
+ float vf = midiEvent.getVelocity() / 127.0f;
+ gu_log(" >>> input volume (master) (pure=0x%X, value=%d, float=%f)\n",
+ pure, midiEvent.getVelocity(), vf);
+ glue_setInVol(vf, false);
+ }
+ else if (pure == conf::midiInVolumeOut) {
+ float vf = midiEvent.getVelocity() / 127.0f;
+ gu_log(" >>> output volume (master) (pure=0x%X, value=%d, float=%f)\n",
+ pure, midiEvent.getVelocity(), vf);
+ glue_setOutVol(vf, false);
+ }
+ else if (pure == conf::midiInBeatDouble) {
+ gu_log(" >>> sequencer x2 (master) (pure=0x%X)\n", pure);
+ glue_beatsMultiply();
+ }
+ else if (pure == conf::midiInBeatHalf) {
+ gu_log(" >>> sequencer /2 (master) (pure=0x%X)\n", pure);
+ glue_beatsDivide();
+ }
+}
+
+} // {anonymous}
+
+
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+
+
+void startMidiLearn(cb_midiLearn* cb, void* data)
+{
+ cb_learn = cb;
+ cb_data = data;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void stopMidiLearn()
+{
+ cb_learn = nullptr;
+ cb_data = nullptr;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void dispatch(int byte1, int byte2, int byte3)
+{
+ /* Here we want to catch two things: a) note on/note off from a keyboard and
+ b) knob/wheel/slider movements from a controller. */
+
+ MidiEvent midiEvent(byte1, byte2, byte3);
+
+ gu_log("[midiDispatcher] MIDI received - 0x%X (chan %d)\n", midiEvent.getRaw(),
+ midiEvent.getChannel());
+
+ /* Start dispatcher. If midi learn is on don't parse channels, just learn
+ incoming MIDI signal. Learn callback wants 'pure' MIDI event: if 'noNoteOff'
+ in global config, get the raw value with the 'velocy' byte. Otherwise strip it
+ off. If midi learn is off process master events first, then each channel
+ in the stack. This way incoming signals don't get processed by glue_* when
+ MIDI learning is on. */
+
+ if (cb_learn)
+ cb_learn(midiEvent.getRaw(conf::noNoteOff), cb_data);
+ else {
+ processMaster(midiEvent);
+ processChannels(midiEvent);
+ }
+}
+}}}; // giada::m::midiDispatcher::
+
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#ifndef G_MIDI_DISPATCHER_H
+#define G_MIDI_DISPATCHER_H
+
+
+#ifdef __APPLE__ // our Clang still doesn't know about cstdint (c++11 stuff)
+ #include <stdint.h>
+#else
+ #include <cstdint>
+#endif
+
+
+namespace giada {
+namespace m {
+namespace midiDispatcher
+{
+typedef void (cb_midiLearn) (uint32_t, void*);
+
+void startMidiLearn(cb_midiLearn* cb, void* data);
+void stopMidiLearn();
+
+void dispatch(int byte1, int byte2, int byte3);
+
+}}}; // giada::m::midiDispatcher::
+
+
+#endif
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#include "midiEvent.h"
+
+
+namespace giada {
+namespace m
+{
+MidiEvent::MidiEvent()
+ : m_raw (0x0),
+ m_status (0),
+ m_channel (0),
+ m_note (0),
+ m_velocity(0),
+ m_delta (0)
+{
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+MidiEvent::MidiEvent(uint32_t raw)
+ : m_raw (raw),
+ m_status ((raw & 0xF0000000) >> 24),
+ m_channel ((raw & 0x0F000000) >> 24),
+ m_note ((raw & 0x00FF0000) >> 16),
+ m_velocity((raw & 0x0000FF00) >> 8),
+ m_delta (0) // not used
+{
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+MidiEvent::MidiEvent(int byte1, int byte2, int byte3)
+ : MidiEvent((byte1 << 24) | (byte2 << 16) | (byte3 << 8) | (0x00))
+{
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void MidiEvent::resetDelta()
+{
+ m_delta = 0;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void MidiEvent::setChannel(int c)
+{
+ m_channel = c;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int MidiEvent::getStatus() const
+{
+ return m_status;
+}
+
+
+int MidiEvent::getChannel() const
+{
+ return m_channel;
+}
+
+
+int MidiEvent::getNote() const
+{
+ return m_note;
+}
+
+
+int MidiEvent::getVelocity() const
+{
+ return m_velocity;
+}
+
+
+bool MidiEvent::isNoteOnOff() const
+{
+ return m_status == NOTE_ON || m_status == NOTE_OFF;
+}
+
+
+int MidiEvent::getDelta() const
+{
+ return m_delta;
+}
+
+
+uint32_t MidiEvent::getRaw(bool velocity) const
+{
+ if (!velocity)
+ return m_raw & 0xFFFF0000;
+ return m_raw;
+}
+
+
+}} // giada::m::
\ No newline at end of file
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#ifndef G_MIDI_EVENT_H
+#define G_MIDI_EVENT_H
+
+
+#include <cstdint>
+
+
+namespace giada {
+namespace m
+{
+class MidiEvent
+{
+public:
+
+ static const int NOTE_ON = 0x90;
+ static const int NOTE_OFF = 0x80;
+
+ MidiEvent();
+ MidiEvent(uint32_t raw);
+ MidiEvent(int byte1, int byte2, int byte3);
+
+ int getStatus() const;
+ int getChannel() const;
+ int getNote() const;
+ int getVelocity() const;
+ bool isNoteOnOff() const;
+ int getDelta() const;
+
+ /* getRaw
+ Returns the raw message. If 'velocity' is false, velocity byte is stripped
+ out. */
+
+ uint32_t getRaw(bool velocity=true) const;
+
+ void resetDelta();
+ void setChannel(int c);
+
+private:
+
+ uint32_t m_raw;
+ int m_status;
+ int m_channel;
+ int m_note;
+ int m_velocity;
+ int m_delta;
+};
+
+}} // giada::m::
+
+
+#endif
\ No newline at end of file
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
json_t *jInitCommand;
json_array_foreach(jInitCommands, commandIndex, jInitCommand) {
- string indexStr = "init command " + gu_toString(commandIndex);
+ string indexStr = "init command " + gu_iToString(commandIndex);
if (!storager::checkObject(jInitCommand, indexStr.c_str()))
return 0;
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
memset(vChanInToOut, 0, sizeof(float) * bufferSize); // inToOut vChan
pthread_mutex_lock(&mutex_chans);
- for (unsigned i=0; i<channels.size(); i++)
- channels.at(i)->clear();
+ for (Channel* channel : channels)
+ channel->clear();
pthread_mutex_unlock(&mutex_chans);
}
while (channels.size() > 0)
mh::deleteChannel(channels.at(0));
- if (vChanInput) {
- free(vChanInput);
+ if (vChanInput != nullptr) {
+ delete[] vChanInput;
vChanInput = nullptr;
}
- if (vChanInToOut) {
- free(vChanInToOut);
+ if (vChanInToOut != nullptr) {
+ delete[] vChanInToOut;
vChanInToOut = nullptr;
}
return 1;
{
clock::rewind();
if (clock::isRunning())
- for (unsigned i=0; i<channels.size(); i++)
- channels.at(i)->rewind();
+ for (Channel* ch : channels)
+ ch->rewind();
}
void mergeVirtualInput()
{
- for (unsigned i=0; i<channels.size(); i++) {
- if (channels.at(i)->type == CHANNEL_MIDI)
+ assert(vChanInput != nullptr);
+
+ for (Channel* ch : channels) {
+ if (ch->type == CHANNEL_MIDI)
continue;
- SampleChannel *ch = static_cast<SampleChannel*>(channels.at(i));
- if (ch->isArmed())
- memcpy(ch->wave->getData(), vChanInput, clock::getTotalFrames() * sizeof(float));
+ SampleChannel* sch = static_cast<SampleChannel*>(ch);
+ if (sch->isArmed())
+ memcpy(sch->wave->getData(), vChanInput, clock::getTotalFrames() * sizeof(float));
}
memset(vChanInput, 0, clock::getTotalFrames() * sizeof(float)); // clear vchan
}
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
int close();
/* masterPlay
- * core method (callback) */
+Core method (callback) */
int masterPlay(void *outBuf, void *inBuf, unsigned bufferSize, double streamTime,
RtAudioStreamStatus status, void *userData);
/* isSilent
- * is mixer silent? */
+Is mixer silent? */
bool isSilent();
/* rewind
- * rewind sequencer to sample 0. */
+Rewinds sequencer to frame 0. */
void rewind();
/* mergeVirtualInput
- * memcpy the virtual channel input in the channel designed for input
- * recording. Called by mixerHandler on stopInputRec() */
+Copies the virtual channel input in the channels designed for input recording.
+Called by mixerHandler on stopInputRec(). */
void mergeVirtualInput();
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
clock::setBars(patch::bars);
clock::setBeats(patch::beats);
clock::setQuantize(patch::quantize);
+ clock::updateFrameBars();
mixer::metronome = patch::metronome;
#ifdef WITH_VST
#endif
- /* rewind and update frames in Mixer (it's essential) */
+ /* Rewind and update frames in Mixer. Also alloc new space in the virtual
+ input buffer, in case the patch has a sequencer size != default one (which is
+ very likely). */
mixer::rewind();
- clock::updateFrameBars();
+ mixer::allocVirtualInput(clock::getTotalFrames());
mixer::ready = true;
}
Wave* wave = nullptr;
int result = waveManager::createEmpty(clock::getTotalFrames(),
- conf::samplerate, string("TAKE-" + gu_toString(patch::lastTakeId)), &wave);
+ 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);
}
ch->pushWave(wave);
- ch->setName("TAKE-" + gu_toString(patch::lastTakeId++)); // Increase lastTakeId
+ ch->setName("TAKE-" + gu_iToString(patch::lastTakeId++)); // Increase lastTakeId
channelsReady++;
gu_log("[startInputRec] start input recs using chan %d with size %d "
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
json_t* jChannel;
json_array_foreach(jChannels, channelIndex, jChannel) {
- string channelIndexStr = "channel " + gu_toString(channelIndex);
+ string channelIndexStr = "channel " + gu_iToString(channelIndex);
if (!storager::checkObject(jChannel, channelIndexStr.c_str()))
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;
json_t* jColumn;
json_array_foreach(jColumns, columnIndex, jColumn) {
- string columnIndexStr = "column " + gu_toString(columnIndex);
+ string columnIndexStr = "column " + gu_iToString(columnIndex);
if (!storager::checkObject(jColumn, columnIndexStr.c_str()))
return 0;
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));
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
uint32_t midiInVolume;
uint32_t midiInMute;
uint32_t midiInSolo;
+ int midiInFilter;
bool midiOutL;
uint32_t midiOutLplaying;
uint32_t midiOutLmute;
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
plugin->prepareToPlay(samplerate, buffersize);
- gu_log("[Plugin] plugin initialized and ready\n");
+ gu_log("[Plugin] plugin initialized and ready. MIDI input params: %lu\n",
+ midiInParams.size());
}
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
/* -------------------------------------------------------------------------- */
-int scanDir(const string& dirpath, void (*callback)(float progress, void* p),
- void* p)
+int scanDirs(const string& dirs, const std::function<void(float)>& cb)
{
- gu_log("[pluginHost::scanDir] requested directory: '%s'\n", dirpath.c_str());
+ gu_log("[pluginHost::scanDir] requested directories: '%s'\n", dirs.c_str());
gu_log("[pluginHost::scanDir] current plugins: %d\n", knownPluginList.getNumTypes());
knownPluginList.clear(); // clear up previous plugins
+ vector<string> dirVec;
+ gu_split(dirs, ";", &dirVec);
+
juce::VSTPluginFormat format;
- juce::FileSearchPath path(dirpath);
- juce::PluginDirectoryScanner scanner(knownPluginList, format, path,
- true, juce::File::nonexistent); // true: recursive
+ juce::FileSearchPath searchPath;
+ for (const string& dir : dirVec)
+ searchPath.add(juce::File(dir));
+
+ juce::PluginDirectoryScanner scanner(knownPluginList, format, searchPath,
+ true, juce::File::nonexistent); // true: recursive
- bool cont = true;
juce::String name;
- while (cont) {
+ while (scanner.scanNextFile(false, name)) {
gu_log("[pluginHost::scanDir] scanning '%s'\n", name.toRawUTF8());
- cont = scanner.scanNextFile(false, name);
- if (callback)
- callback(scanner.getProgress(), p);
+ cb(scanner.getProgress());
}
gu_log("[pluginHost::scanDir] %d plugin(s) found\n", knownPluginList.getNumTypes());
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
#define G_PLUGIN_HOST_H
+#include <functional>
#include <pthread.h>
#include "../deps/juce-config.h"
enum sortMethod
{
- NAME,
- CATEGORY,
- MANUFACTURER,
- FORMAT
+ NAME,
+ CATEGORY,
+ MANUFACTURER,
+ FORMAT
};
struct PluginInfo
{
- std::string uid;
- std::string name;
- std::string category;
- std::string manufacturerName;
- std::string format;
- bool isInstrument;
+ std::string uid;
+ std::string name;
+ std::string category;
+ std::string manufacturerName;
+ std::string format;
+ bool isInstrument;
};
void init(int bufSize, int frequency);
void close();
-/* scanDir
- * Parse plugin directory and store list in knownPluginList. The callback is
- * called on each plugin found. Used to update the main window from the GUI
- * thread. */
+/* scanDirs
+Parses plugin directories (semicolon-separated) and store list in
+knownPluginList. The callback is called on each plugin found. Used to update the
+main window from the GUI thread. */
-int scanDir(const std::string& path, void (*callback)(float progress, void* p)=nullptr,
- void* p=nullptr);
+int scanDirs(const std::string& paths, const std::function<void(float)>& cb);
/* (save|load)List
* (Save|Load) knownPluginList (in|from) an XML file. */
* ch - if stackType == CHANNEL. */
Plugin* addPlugin(const std::string& fid, int stackType, pthread_mutex_t* mutex,
- Channel* ch=nullptr);
+ Channel* ch=nullptr);
Plugin *addPlugin(int index, int stackType, pthread_mutex_t* mutex,
- Channel* ch=nullptr);
+ Channel* ch=nullptr);
/* countPlugins
* Return size of 'stackType'. */
/* swapPlugin */
void swapPlugin(unsigned indexA, unsigned indexB, int stackType,
- pthread_mutex_t* mutex, Channel* ch=nullptr);
+ pthread_mutex_t* mutex, Channel* ch=nullptr);
/* freePlugin.
Returns the internal stack index of the deleted plugin. */
int freePlugin(int id, int stackType, pthread_mutex_t *mutex,
- Channel* ch=nullptr);
+ Channel* ch=nullptr);
/* runDispatchLoop
* Wakes up plugins' GUI manager for N milliseconds. */
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
{
namespace
{
-/* composite
-A group of two actions (keypress+keyrel, muteon+muteoff) used during the overdub
+/* Composite
+A group of two actions (keypress+keyrel, muteon+muteoff) used during the overdub
process. */
-struct composite
-{
- action a1;
- action a2;
-} cmp;
+Composite cmp;
/* -------------------------------------------------------------------------- */
Overdub: ---|#######|---
fix: |#||#######|--- */
-void fixOverdubTruncation(const composite &comp, pthread_mutex_t *mixerMutex)
+void fixOverdubTruncation(const Composite& comp, pthread_mutex_t* mixerMutex)
{
- action *next = nullptr;
+ action* next = nullptr;
int res = getNextAction(comp.a2.chan, comp.a1.type | comp.a2.type, comp.a2.frame,
&next);
if (res != 1 || next->type != comp.a2.type)
/* -------------------------------------------------------------------------- */
-bool canRec(Channel *ch, bool clockRunning, bool mixerRecording)
+bool canRec(Channel* ch, bool clockRunning, bool mixerRecording)
{
/* NO recording if:
* recorder is inactive
void deleteAction(int chan, int frame, char type, bool checkValues,
- pthread_mutex_t *mixerMutex, uint32_t iValue, float fValue)
+ pthread_mutex_t* mixerMutex, uint32_t iValue, float fValue)
{
/* make sure frame is even */
void deleteActions(int chan, int frame_a, int frame_b, char type,
- pthread_mutex_t *mixerMutex)
+ pthread_mutex_t* mixerMutex)
{
sortActions();
vector<int> dels;
/* -------------------------------------------------------------------------- */
-int getNextAction(int chan, char type, int frame, action **out,
- uint32_t iValue)
+int getNextAction(int chan, char type, int fromFrame, action** out,
+ uint32_t iValue, uint32_t mask)
{
sortActions(); // mandatory
- unsigned i=0;
- while (i < frames.size() && frames.at(i) <= frame) i++;
+ /* Increase 'i' until it reaches 'fromFrame'. That's the point where to start
+ to look for the next action. */
+
+ unsigned i = 0;
+ while (i < frames.size() && frames.at(i) <= fromFrame) i++;
+
+ /* No other actions past 'fromFrame': there are no more actions to look for.
+ Return -1. */
- if (i == frames.size()) // no further actions past 'frame'
+ if (i == frames.size())
return -1;
- for (; i<global.size(); i++)
+ for (; i<global.size(); i++) {
+
for (unsigned j=0; j<global.at(i).size(); j++) {
- action *a = global.at(i).at(j);
- if (a->chan == chan && (type & a->type) == a->type) {
- //if (iValue == 0 || (iValue != 0 && a->iValue == iValue)) {
- if (iValue == 0 || (iValue != 0 && (iValue & a->iValue) == a->iValue )) {
- *out = global.at(i).at(j);
- return 1;
- }
+
+ action* a = global.at(i).at(j);
+
+ /* If the requested channel and type don't match, continue. */
+
+ if (a->chan != chan || (type & a->type) != a->type)
+ continue;
+
+ /* If no iValue has been specified (iValue == 0), then the next action has
+ been found, return it. Otherwise, make sure the iValue matches the
+ action's iValue, according to the mask provided. */
+
+ if (iValue == 0 || (iValue != 0 && (a->iValue | mask) == (iValue | mask))) {
+ *out = global.at(i).at(j);
+ return 1;
}
}
-
+ }
return -2; // no 'type' actions found
}
/* -------------------------------------------------------------------------- */
-int getAction(int chan, char action, int frame, struct action **out)
+int getAction(int chan, char action, int frame, struct action** out)
{
for (unsigned i=0; i<global.size(); i++)
for (unsigned j=0; j<global.at(i).size(); j++)
/* -------------------------------------------------------------------------- */
-void stopOverdub(int currentFrame, int totalFrames, pthread_mutex_t *mixerMutex)
+void stopOverdub(int currentFrame, int totalFrames, pthread_mutex_t* mixerMutex)
{
cmp.a2.frame = currentFrame;
bool ringLoop = false;
rec(cmp.a2.chan, cmp.a2.type, cmp.a2.frame);
fixOverdubTruncation(cmp, mixerMutex);
}
-
-
}}}; // giada::m::recorder::
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
uint32_t iValue; // used only for MIDI events
};
+/* Composite
+A group of two actions (keypress+keyrel, muteon+muteoff). */
+
+struct Composite
+{
+ action a1;
+ action a2;
+};
+
/* frames
Frame counter sentinel. It tells which frames contain actions. E.g.:
frames[0] = 155 // some actions on frame 155
void shrink(int new_fpb);
/* getNextAction
- * Return the nearest action in chan 'chan' of type 'action' starting
- * from 'frame'. Action can be a bitmask. If iValue != 0 search for
- * next action with iValue == iValue: useful for MIDI key_release. iValue
- * can be a bitmask. */
+Returns the nearest action in chan 'chan' of type 'action' starting from
+'frame'. Action can be a bitmask. If iValue != 0 search for next action with
+iValue == iValue with 'mask' to ignore bytes. Useful for MIDI key_release.
+Mask example:
+
+ iValue = 0x803D3F00
+ mask = 0x0000FF00 // ignore byte 3
+ action = 0x803D3200 // <--- this action will be found */
-int getNextAction(int chan, char action, int frame, struct action **out,
- uint32_t iValue=0);
+int getNextAction(int chan, char action, int frame, struct action** out,
+ uint32_t iValue=0, uint32_t mask=0);
/* getAction
- * return a pointer to action in chan 'chan' of type 'action' at frame
- * 'frame'. */
+Returns a pointer to action in chan 'chan' of type 'action' at frame 'frame'. */
-int getAction(int chan, char action, int frame, struct action **out);
+int getAction(int chan, char action, int frame, struct action** out);
/* start/stopOverdub
These functions are used when you overwrite existing actions. For example:
void startOverdub(int chan, char action, int frame, unsigned bufferSize);
void stopOverdub(int currentFrame, int totalFrames, pthread_mutex_t *mixerMutex);
-
}}}; // giada::m::recorder::
#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
if (id == -1)
m_path = p;
else
- m_path = gu_stripExt(p) + "-" + gu_toString(id) + "." + gu_getExt(p);
+ m_path = gu_stripExt(p) + "-" + gu_iToString(id) + "." + gu_getExt(p);
}
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
they must start playing right away at the current frame, not at the next first
beat. */
- for (unsigned i=0; i<mixer::channels.size(); i++) {
- if (mixer::channels.at(i)->type == CHANNEL_MIDI)
+ for (Channel* ch : mixer::channels) {
+ if (ch->type == CHANNEL_MIDI)
continue;
- SampleChannel *ch = (SampleChannel*) mixer::channels.at(i);
- if (ch->mode & (LOOP_ANY) && ch->status == STATUS_OFF && ch->isArmed())
- ch->start(clock::getCurrentFrame(), true, clock::getQuantize(),
+ SampleChannel* sch = static_cast<SampleChannel*>(ch);
+ if (sch->mode & (LOOP_ANY) && sch->status == STATUS_OFF && sch->isArmed())
+ sch->start(clock::getCurrentFrame(), true, clock::getQuantize(),
clock::isRunning(), true, true);
}
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
double fPpart = modf(v, &fIpart);
int iIpart = fIpart;
int iPpart = ceilf(fPpart);
- glue_setBpm(gu_toString(iIpart).c_str(), gu_toString(iPpart).c_str());
+ glue_setBpm(gu_iToString(iIpart).c_str(), gu_iToString(iPpart).c_str());
}
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
#include "../core/plugin.h"
#include "../core/channel.h"
#include "../core/const.h"
+#include "../core/conf.h"
#include "../utils/gui.h"
#include "../gui/dialogs/gd_mainWindow.h"
#include "../gui/dialogs/pluginWindow.h"
#include "../gui/dialogs/pluginList.h"
+#include "../gui/dialogs/gd_warnings.h"
+#include "../gui/dialogs/gd_config.h"
+#include "../gui/dialogs/browser/browserDir.h"
#include "plugin.h"
namespace c {
namespace plugin
{
+namespace
+{
+/* getPluginWindow
+Returns the plugInWindow (GUI-less one) with the parameter list. It might be
+nullptr if there is no plug-in window shown on screen. */
+
+gdPluginWindow* getPluginWindow(const Plugin* p)
+{
+ /* Get the parent window first: the plug-in list. Then, if it exists, get
+ the child window - the actual pluginWindow. */
+
+ gdPluginList* parent = static_cast<gdPluginList*>(gu_getSubwindow(G_MainWin, WID_FX_LIST));
+ if (parent == nullptr)
+ return nullptr;
+ return static_cast<gdPluginWindow*>(gu_getSubwindow(parent, p->getId() + 1));
+}
+} // {anonymous}
+
+
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+
+
Plugin* addPlugin(Channel* ch, int index, int stackType)
{
if (index >= pluginHost::countAvailablePlugins())
/* -------------------------------------------------------------------------- */
+void setProgram(Plugin* p, int index)
+{
+ p->setCurrentProgram(index);
+
+ /* No need to update plug-in editor if it has one: the plug-in's editor takes
+ care of it on its own. Conversely, update the specific parameter for UI-less
+ plug-ins. */
+
+ if (p->hasEditor())
+ return;
+
+ gdPluginWindow* child = getPluginWindow(p);
+ if (child == nullptr)
+ return;
+
+ child->updateParameters(true);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
void setParameter(Plugin* p, int index, float value, bool gui)
{
p->setParameter(index, value);
if (p->hasEditor())
return;
- gdPluginList* parent = static_cast<gdPluginList*>(gu_getSubwindow(G_MainWin, WID_FX_LIST));
- gdPluginWindow* child = static_cast<gdPluginWindow*>(gu_getSubwindow(parent, p->getId() + 1));
- if (child != nullptr) {
- Fl::lock();
- child->updateParameter(index, !gui);
- Fl::unlock();
+ gdPluginWindow* child = getPluginWindow(p);
+ if (child == nullptr)
+ return;
+
+ Fl::lock();
+ child->updateParameter(index, !gui);
+ Fl::unlock();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void setPluginPathCb(void* data)
+{
+ gdBrowserDir* browser = (gdBrowserDir*) data;
+
+ if (browser->getCurrentPath() == "") {
+ gdAlert("Invalid path.");
+ return;
}
+
+ if (!conf::pluginPath.empty() && conf::pluginPath.back() != ';')
+ conf::pluginPath += ";";
+ conf::pluginPath += browser->getCurrentPath();
+
+ browser->do_callback();
+
+ gdConfig* configWin = static_cast<gdConfig*>(gu_getSubwindow(G_MainWin, WID_CONFIG));
+ configWin->refreshVstPath();
}
}}}; // giada::c::plugin::
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
void swapPlugins(Channel* ch, int indexP1, int indexP2, int stackType);
void freePlugin(Channel* ch, int index, int stackType);
void setParameter(Plugin* p, int index, float value, bool gui=true);
+void setProgram(Plugin* p, int index);
+
+/* setPluginPathCb
+Callback attached to the DirBrowser for adding new Plug-in search paths in the
+configuration window. */
+
+void setPluginPathCb(void* data);
}}}; // giada::c::plugin::
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
#include "../gui/elems/mainWindow/keyboard/channel.h"
#include "../gui/elems/mainWindow/keyboard/sampleChannel.h"
#include "../core/const.h"
+#include "../core/clock.h"
+#include "../core/kernelMidi.h"
#include "../core/channel.h"
#include "../core/recorder.h"
#include "../utils/gui.h"
+#include "../utils/log.h"
#include "recorder.h"
-using namespace giada::m;
+using std::vector;
+using namespace giada;
+namespace giada {
+namespace c {
+namespace recorder
+{
namespace
{
-void updateChannel(geChannel *gch)
+void updateChannel(geChannel* gch)
{
- gch->ch->hasActions = recorder::hasActions(gch->ch->index);
- if (gch->ch->type == CHANNEL_SAMPLE && !gch->ch->hasActions)
- static_cast<geSampleChannel*>(gch)->hideActionButton();
- /* TODO - set mute=false */
- gu_refreshActionEditor(); // refresh a.editor window, it could be open
+ gch->ch->hasActions = m::recorder::hasActions(gch->ch->index);
+ if (gch->ch->type == CHANNEL_SAMPLE && !gch->ch->hasActions)
+ static_cast<geSampleChannel*>(gch)->hideActionButton();
+ /* TODO - set mute=false */
+ gu_refreshActionEditor(); // refresh a.editor window, it could be open
}
}; // {namespace}
/* -------------------------------------------------------------------------- */
-void glue_clearAllActions(geChannel *gch)
+void clearAllActions(geChannel* gch)
{
- if (!gdConfirmWin("Warning", "Clear all actions: are you sure?"))
- return;
- recorder::clearChan(gch->ch->index);
- updateChannel(gch);
+ if (!gdConfirmWin("Warning", "Clear all actions: are you sure?"))
+ return;
+ m::recorder::clearChan(gch->ch->index);
+ updateChannel(gch);
}
/* -------------------------------------------------------------------------- */
-void glue_clearVolumeActions(geChannel *gch)
+void clearVolumeActions(geChannel* gch)
{
- if (!gdConfirmWin("Warning", "Clear all volume actions: are you sure?"))
- return;
- recorder::clearAction(gch->ch->index, G_ACTION_VOLUME);
- updateChannel(gch);
+ if (!gdConfirmWin("Warning", "Clear all volume actions: are you sure?"))
+ return;
+ m::recorder::clearAction(gch->ch->index, G_ACTION_VOLUME);
+ updateChannel(gch);
}
/* -------------------------------------------------------------------------- */
-void glue_clearStartStopActions(geChannel *gch)
+void clearStartStopActions(geChannel* gch)
{
- if (!gdConfirmWin("Warning", "Clear all start/stop actions: are you sure?"))
- return;
- recorder::clearAction(gch->ch->index, G_ACTION_KEYPRESS | G_ACTION_KEYREL | G_ACTION_KILL);
- updateChannel(gch);
+ if (!gdConfirmWin("Warning", "Clear all start/stop actions: are you sure?"))
+ return;
+ m::recorder::clearAction(gch->ch->index, G_ACTION_KEYPRESS | G_ACTION_KEYREL | G_ACTION_KILL);
+ updateChannel(gch);
}
/* -------------------------------------------------------------------------- */
-void glue_clearMuteActions(geChannel *gch)
+void clearMuteActions(geChannel* gch)
{
- if (!gdConfirmWin("Warning", "Clear all mute actions: are you sure?"))
- return;
- recorder::clearAction(gch->ch->index, G_ACTION_MUTEON | G_ACTION_MUTEOFF);
- updateChannel(gch);
+ if (!gdConfirmWin("Warning", "Clear all mute actions: are you sure?"))
+ return;
+ m::recorder::clearAction(gch->ch->index, G_ACTION_MUTEON | G_ACTION_MUTEOFF);
+ updateChannel(gch);
}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void recordMidiAction(int chan, int note, int frame_a, int frame_b)
+{
+ if (frame_b == 0)
+ frame_b = frame_a + G_DEFAULT_MIDI_ACTION_SIZE;
+
+ /* Avoid frame overflow. */
+
+ int overflow = frame_b - m::clock::getTotalFrames();
+ if (overflow > 0) {
+ frame_b -= overflow;
+ frame_a -= overflow;
+ }
+
+ /* Prepare MIDI events, with maximum velocity (0x3F) for now. */
+
+ m::MidiEvent event_a = m::MidiEvent(m::MidiEvent::NOTE_ON, note, 0x3F);
+ m::MidiEvent event_b = m::MidiEvent(m::MidiEvent::NOTE_OFF, note, 0x3F);
+
+ /* Avoid overlapping actions. Find the next action past frame_a and compare
+ its frame: if smaller than frame_b, an overlap occurs. Shrink the new action
+ accordingly. */
+
+ m::recorder::action* next = nullptr;
+ m::recorder::getNextAction(chan, G_ACTION_MIDI, frame_a, &next, event_a.getRaw(),
+ 0x0000FF00);
+
+ if (next != nullptr && next->frame <= frame_b) {
+ frame_b = next->frame - 2;
+ gu_log("[recorder::recordMidiAction] Shrink new action, due to overlap\n");
+ }
+
+ m::recorder::rec(chan, G_ACTION_MIDI, frame_a, event_a.getRaw());
+ m::recorder::rec(chan, G_ACTION_MIDI, frame_b, event_b.getRaw());
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+vector<m::recorder::Composite> getMidiActions(int chan, int frameLimit)
+{
+ vector<m::recorder::Composite> out;
+
+ m::recorder::sortActions();
+
+ for (unsigned i=0; i<m::recorder::frames.size(); i++) {
+
+ if (m::recorder::frames.at(i) > frameLimit)
+ continue;
+
+ for (unsigned j=0; j<m::recorder::global.at(i).size(); j++) {
+
+ m::recorder::action* a1 = m::recorder::global.at(i).at(j);
+ m::recorder::action* a2 = nullptr;
+
+ m::MidiEvent a1midi(a1->iValue);
+
+ /* Skip action if:
+ - does not belong to this channel
+ - is not a MIDI action (we only want MIDI things here)
+ - is not a MIDI Note On type. We don't want any other kind of action here */
+
+ if (a1->chan != chan || a1->type != G_ACTION_MIDI ||
+ a1midi.getStatus() != m::MidiEvent::NOTE_ON)
+ continue;
+
+ /* Prepare the composite action. Action 1 exists for sure, so fill it up
+ right away. */
+
+ m::recorder::Composite cmp;
+ cmp.a1 = *a1;
+
+ /* Search for the next action. Must have: same channel, G_ACTION_MIDI,
+ greater than a1->frame and with MIDI properties of note_off (0x80), same
+ note of a1 and random velocity: we don't care about it (and so we mask it
+ with 0x0000FF00). */
+
+ m::MidiEvent a2midi(m::MidiEvent::NOTE_OFF, a1midi.getNote(), 0x0);
+
+ m::recorder::getNextAction(chan, G_ACTION_MIDI, a1->frame, &a2,
+ a2midi.getRaw(), 0x0000FF00);
+
+ /* If action 2 has been found, add it to the composite duo. Otherwise
+ set the action 2 frame to -1: it should be intended as "orphaned". */
+
+ if (a2 != nullptr)
+ cmp.a2 = *a2;
+ else
+ cmp.a2.frame = -1;
+
+ out.push_back(cmp);
+ }
+ }
+
+ return out;
+}
+
+}}} // giada::c::recorder::
\ No newline at end of file
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
#define G_GLUE_RECORDER_H
+#include <vector>
+#include "../core/recorder.h"
+
+
class geChannel;
-void glue_clearAllActions(geChannel *gch);
-void glue_clearMuteActions(geChannel *gch);
-void glue_clearVolumeActions(geChannel *gch);
-void glue_clearStartStopActions(geChannel *gch);
+namespace giada {
+namespace c {
+namespace recorder
+{
+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
+size. This function is designed for the Piano Roll (not for live recording). */
+
+void recordMidiAction(int chan, int note, int frame_a, int frame_b=0);
+
+/* getMidiActions
+Returns a list of Composite actions, ready to be displayed in a MIDI note
+editor as pairs of NoteOn+NoteOff. */
+
+std::vector<giada::m::recorder::Composite> getMidiActions(int channel,
+ int frameLimit);
+}}} // giada::c::recorder::
#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
namespace c {
namespace sampleEditor
{
-
-
/* setBeginEnd
Sets start/end points in the sample editor. */
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
static string glue_makeSamplePath__(const string& base, const Wave* w, int k)
{
- return base + G_SLASH + w->getBasename(false) + "-" + gu_toString(k) + "." + w->getExtension();
+ return base + G_SLASH + w->getBasename(false) + "-" + gu_iToString(k) + "." + w->getExtension();
}
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
end();
beats->maximum_size(2);
- beats->value(gu_toString(clock::getBeats()).c_str());
+ beats->value(gu_iToString(clock::getBeats()).c_str());
beats->type(FL_INT_INPUT);
bars->maximum_size(2);
- bars->value(gu_toString(clock::getBars()).c_str());
+ bars->value(gu_iToString(clock::getBars()).c_str());
bars->type(FL_INT_INPUT);
ok->shortcut(FL_Enter);
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
input_a->maximum_size(3);
input_a->type(FL_INT_INPUT);
- input_a->value(gu_toString(clock::getBpm()).c_str());
+ input_a->value(gu_fToString(clock::getBpm(), 0).c_str());
/* Use the decimal value from the string label. */
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
/* -------------------------------------------------------------------------- */
-string gdBrowserBase::getCurrentPath()
-{
- return where->value();
+string gdBrowserBase::getCurrentPath() const
+{
+ return where->value();
}
-/* -------------------------------------------------------------------------- */
+Channel* gdBrowserBase::getChannel() const
+{
+ return channel;
+}
-string gdBrowserBase::getSelectedItem()
+string gdBrowserBase::getSelectedItem() const
{
return browser->getSelectedItem();
}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gdBrowserBase::fireCallback() const
+{
+ callback((void*) this);
+}
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
#include "../window.h"
-class Channel;
class Fl_Group;
+class Channel;
class geCheck;
class geBrowser;
class geButton;
void (*callback)(void*);
gdBrowserBase(int x, int y, int w, int h, const std::string& title,
- const std::string& path, void (*callback)(void*));
+ const std::string& path, void (*callback)(void*));
public:
/* getSelectedItem
* Return the full path of the selected file. */
- std::string getSelectedItem();
+ std::string getSelectedItem() const;
+ std::string getCurrentPath() const;
+ Channel* getChannel() const;
+ void fireCallback() const;
+
/* setStatusBar
* Increment status bar for progress tracking. */
void showStatusBar();
void hideStatusBar();
- std::string getCurrentPath();
- Channel* getChannel() { return channel; }
- void fireCallback() { callback((void*) this); }
};
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#include "../../../utils/fs.h"
+#include "../../elems/browser.h"
+#include "../../elems/basics/button.h"
+#include "../../elems/basics/input.h"
+#include "browserDir.h"
+
+
+using std::string;
+
+
+gdBrowserDir::gdBrowserDir(int x, int y, int w, int h, const string& title,
+ const string& path, void (*callback)(void*))
+ : gdBrowserBase(x, y, w, h, title, path, callback)
+{
+ where->size(groupTop->w()-updir->w()-8, 20);
+
+ browser->callback(cb_down, (void*) this);
+
+ ok->label("Select");
+ ok->callback(cb_load, (void*) this);
+ ok->shortcut(FL_ENTER);
+
+ /* On OS X the 'where' input doesn't get resized properly on startup. Let's
+ force it. */
+
+ where->redraw();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gdBrowserDir::cb_load(Fl_Widget* v, void* p) { ((gdBrowserDir*)p)->cb_load(); }
+void gdBrowserDir::cb_down(Fl_Widget* v, void* p) { ((gdBrowserDir*)p)->cb_down(); }
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gdBrowserDir::cb_load()
+{
+ callback((void*) this);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gdBrowserDir::cb_down()
+{
+ string path = browser->getSelectedItem();
+
+ if (path.empty() || !gu_isDir(path)) // when click on an empty area or not a dir
+ return;
+
+ browser->loadDir(path);
+ where->value(browser->getCurrentDir().c_str());
+}
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#ifndef GD_BROWSER_DIR_H
+#define GD_BROWSER_DIR_H
+
+
+#include "browserBase.h"
+
+
+class Channel;
+
+
+class gdBrowserDir : public gdBrowserBase
+{
+private:
+
+ static void cb_load(Fl_Widget* w, void* p);
+ static void cb_down(Fl_Widget* w, void* p);
+ void cb_load();
+ void cb_down();
+
+public:
+
+ gdBrowserDir(int x, int y, int w, int h, const std::string& title,
+ const std::string& path, void (*callback)(void*));
+};
+
+
+#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
#define GD_BROWSER_LOAD_H
-#include "../window.h"
#include "browserBase.h"
class Channel;
-class Fl_Group;
-class geCheck;
-class geBrowser;
-class geButton;
-class geInput;
-class geProgress;
class gdBrowserLoad : public gdBrowserBase
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
/* -------------------------------------------------------------------------- */
-string gdBrowserSave::getName()
+string gdBrowserSave::getName() const
{
return name->value();
}
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
#define GD_BROWSER_SAVE_H
-#include "../window.h"
#include "browserBase.h"
class Channel;
-class Fl_Group;
-class geCheck;
-class geBrowser;
-class geButton;
class geInput;
-class geProgress;
class gdBrowserSave : public gdBrowserBase
const std::string& path, const std::string& name, void (*callback)(void*),
Channel* ch);
- std::string getName();
+ std::string getName() const;
};
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
#include "../../deps/juce-config.h"
#endif
#include "../../utils/gui.h"
+#include "../../utils/string.h"
#include "../../utils/deps.h"
#include "../elems/basics/button.h"
#include "../elems/basics/box.h"
#include "gd_about.h"
+using std::string;
using namespace giada::m;
using namespace giada::u;
logo->image(new Fl_Pixmap(giada_logo_xpm));
text->align(FL_ALIGN_CENTER | FL_ALIGN_INSIDE | FL_ALIGN_TOP);
- char message[512];
- sprintf(
- message,
+ string message = gu_format(
"Version " G_VERSION_STR " (" BUILD_DATE ")\n\n"
"Developed by Monocasual Laboratories\n"
"Based on FLTK (%d.%d.%d), RtAudio (%s),\n"
int tw = 0;
int th = 0;
- fl_measure(message, tw, th);
- text->copy_label(message);
+ fl_measure(message.c_str(), tw, th);
+ text->copy_label(message.c_str());
text->size(text->w(), th);
#ifdef WITH_VST
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
#include <cmath>
#include "../../utils/gui.h"
+#include "../../utils/string.h"
#include "../../core/graphics.h"
#include "../../core/conf.h"
#include "../../core/const.h"
#include "gd_actionEditor.h"
+using std::string;
using namespace giada::m;
gu_setFavicon(this);
- char buf[256];
- sprintf(buf, "Edit Actions in Channel %d", chan->index+1);
- label(buf);
+ string buf = "Edit Actions in Channel " + gu_iToString(chan->index+1);
+ label(buf.c_str());
set_non_modal();
size_range(640, 284);
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
gdConfig::gdConfig(int w, int h) : gdWindow(w, h, "Configuration")
{
- set_modal();
-
if (conf::configX)
resize(conf::configX, conf::configY, this->w(), this->h());
- Fl_Tabs *tabs = new Fl_Tabs(8, 8, w-16, h-44);
+ Fl_Tabs* tabs = new Fl_Tabs(8, 8, w-16, h-44);
tabs->box(G_CUSTOM_BORDER_BOX);
tabs->labelcolor(G_COLOR_LIGHT_2);
tabs->begin();
/* -------------------------------------------------------------------------- */
-void gdConfig::cb_save_config(Fl_Widget *w, void *p) { ((gdConfig*)p)->__cb_save_config(); }
-void gdConfig::cb_cancel (Fl_Widget *w, void *p) { ((gdConfig*)p)->__cb_cancel(); }
+void gdConfig::cb_save_config(Fl_Widget* w, void* p) { ((gdConfig*)p)->cb_save_config(); }
+void gdConfig::cb_cancel (Fl_Widget* w, void* p) { ((gdConfig*)p)->cb_cancel(); }
/* -------------------------------------------------------------------------- */
-void gdConfig::__cb_save_config()
+void gdConfig::cb_save_config()
{
tabAudio->save();
tabBehaviors->save();
/* -------------------------------------------------------------------------- */
-void gdConfig::__cb_cancel()
+void gdConfig::cb_cancel()
{
do_callback();
}
+
+
+/* -------------------------------------------------------------------------- */
+
+#ifdef WITH_VST
+
+void gdConfig::refreshVstPath()
+{
+ tabPlugins->refreshVstPath();
+}
+
+#endif
\ No newline at end of file
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
{
private:
- static void cb_save_config(Fl_Widget *w, void *p);
- static void cb_cancel (Fl_Widget *w, void *p);
- inline void __cb_save_config();
- inline void __cb_cancel();
+ static void cb_save_config(Fl_Widget* w, void* p);
+ static void cb_cancel(Fl_Widget* w, void* p);
+ void cb_save_config();
+ void cb_cancel();
public:
+ geTabAudio* tabAudio;
+ geTabBehaviors* tabBehaviors;
+ geTabMidi* tabMidi;
+ geTabMisc* tabMisc;
+#ifdef WITH_VST
+ geTabPlugins* tabPlugins;
+#endif
+ geButton* save;
+ geButton* cancel;
+
gdConfig(int w, int h);
~gdConfig();
- geTabAudio *tabAudio;
- geTabBehaviors *tabBehaviors;
- geTabMidi *tabMidi;
- geTabMisc *tabMisc;
#ifdef WITH_VST
- geTabPlugins *tabPlugins;
+ void refreshVstPath();
#endif
- geButton *save;
- geButton *cancel;
};
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
int lines = 7;
body = "Device name: " + kernelAudio::getDeviceName(dev) + "\n";
- body += "Total output(s): " + gu_toString(kernelAudio::getMaxOutChans(dev)) + "\n";
- body += "Total intput(s): " + gu_toString(kernelAudio::getMaxInChans(dev)) + "\n";
- body += "Duplex channel(s): " + gu_toString(kernelAudio::getDuplexChans(dev)) + "\n";
+ body += "Total output(s): " + gu_iToString(kernelAudio::getMaxOutChans(dev)) + "\n";
+ body += "Total intput(s): " + gu_iToString(kernelAudio::getMaxInChans(dev)) + "\n";
+ body += "Duplex channel(s): " + gu_iToString(kernelAudio::getDuplexChans(dev)) + "\n";
body += "Default output: " + string(kernelAudio::isDefaultOut(dev) ? "yes" : "no") + "\n";
body += "Default input: " + string(kernelAudio::isDefaultIn(dev) ? "yes" : "no") + "\n";
int totalFreq = kernelAudio::getTotalFreqs(dev);
- body += "Supported frequencies: " + gu_toString(totalFreq);
+ body += "Supported frequencies: " + gu_iToString(totalFreq);
for (int i=0; i<totalFreq; i++) {
if (i % 6 == 0) {
body += "\n "; // add new line each 6 printed freqs AND on the first line (i % 0 != 0)
lines++;
}
- body += gu_toString( kernelAudio::getFreq(dev, i)) + " ";
+ body += gu_iToString( kernelAudio::getFreq(dev, i)) + " ";
}
text->copy_label(body.c_str());
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
#include "../../utils/gui.h"
+#include "../../utils/string.h"
#include "../../core/conf.h"
#include "../../core/channel.h"
#include "../../core/sampleChannel.h"
extern gdMainWindow *mainWin;
+using std::string;
+
+
gdKeyGrabber::gdKeyGrabber(Channel *ch)
: gdWindow(300, 126, "Key configuration"), ch(ch)
{
void gdKeyGrabber::setButtonLabel(int key)
{
- char tmp[2]; sprintf(tmp, "%c", key);
- ch->guiChannel->mainButton->setKey(tmp);
+ ch->guiChannel->mainButton->setKey(key);
ch->key = key;
}
void gdKeyGrabber::updateText(int key)
{
- char tmp2[64];
+ string tmp = "Press a key.\n\nCurrent binding: ";
if (key != 0)
- sprintf(tmp2, "Press a key.\n\nCurrent binding: %c", key);
+ tmp += static_cast<char>(key);
else
- sprintf(tmp2, "Press a key.\n\nCurrent binding: [none]");
- text->copy_label(tmp2);
+ tmp += "[none]";
+ text->copy_label(tmp.c_str());
}
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
* -------------------------------------------------------------------------- */
-#include "../../../core/kernelMidi.h"
+#include "../../../core/midiDispatcher.h"
+#include "../../../core/channel.h"
+#include "../../../core/conf.h"
#include "../../../utils/log.h"
#include "../../elems/midiLearner.h"
#include "midiInputBase.h"
using namespace giada::m;
-gdMidiInputBase::gdMidiInputBase(int x, int y, int w, int h, const char *title)
+gdMidiInputBase::gdMidiInputBase(int x, int y, int w, int h, const char* title)
: gdWindow(x, y, w, h, title)
{
}
gdMidiInputBase::~gdMidiInputBase()
{
- kernelMidi::stopMidiLearn();
+ midiDispatcher::stopMidiLearn();
}
/* -------------------------------------------------------------------------- */
-void gdMidiInputBase::stopMidiLearn(geMidiLearner *learner)
+void gdMidiInputBase::stopMidiLearn(geMidiLearner* learner)
{
- kernelMidi::stopMidiLearn();
+ midiDispatcher::stopMidiLearn();
learner->updateValue();
}
/* -------------------------------------------------------------------------- */
-void gdMidiInputBase::__cb_learn(uint32_t *param, uint32_t msg, geMidiLearner *l)
+void gdMidiInputBase::cb_learn(uint32_t* param, uint32_t msg, geMidiLearner* l)
{
*param = msg;
stopMidiLearn(l);
/* -------------------------------------------------------------------------- */
-void gdMidiInputBase::cb_learn(uint32_t msg, void *d)
+void gdMidiInputBase::cb_learn(uint32_t msg, void* d)
{
- geMidiLearner::cbData_t *data = (geMidiLearner::cbData_t *) d;
- gdMidiInputBase *window = (gdMidiInputBase*) data->window;
- geMidiLearner *learner = data->learner;
- uint32_t *param = learner->param;
- window->__cb_learn(param, msg, learner);
+
+ geMidiLearner::cbData_t* data = (geMidiLearner::cbData_t*) d;
+ geMidiLearner* learner = data->learner;
+ Channel* channel = data->channel;
+ uint32_t* param = learner->param;
+ int midiChannel = (*param & 0x0F000000) >> 24; // Brutally extract channel
+
+ /* No MIDI learning if we are learning a Channel (channel != nullptr) and
+ the selected MIDI channel is filtered OR if we are learning a global parameter
+ (channel == nullptr) and the selected MIDI channel is filtered. */
+
+ if ((channel != nullptr && !channel->isMidiInAllowed(midiChannel)) ||
+ (channel == nullptr && !conf::isMidiInAllowed(midiChannel)))
+ return;
+
+ gdMidiInputBase* window = static_cast<gdMidiInputBase*>(data->window);
+ window->cb_learn(param, msg, learner);
}
/* -------------------------------------------------------------------------- */
-void gdMidiInputBase::cb_close(Fl_Widget *w, void *p) { ((gdMidiInputBase*)p)->__cb_close(); }
+void gdMidiInputBase::cb_close(Fl_Widget* w, void* p) { ((gdMidiInputBase*)p)->cb_close(); }
/* -------------------------------------------------------------------------- */
-void gdMidiInputBase::__cb_close()
+void gdMidiInputBase::cb_close()
{
do_callback();
}
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
{
protected:
- geButton *ok;
+ static const int LEARNER_WIDTH = 284;
- void stopMidiLearn(geMidiLearner *l);
+ geButton* ok;
+
+ void stopMidiLearn(geMidiLearner* l);
/* cb_learn
* callback attached to kernelMidi to learn various actions. */
- static void cb_learn (uint32_t msg, void *data);
- inline void __cb_learn(uint32_t *param, uint32_t msg, geMidiLearner *l);
-
- static void cb_close (Fl_Widget *w, void *p);
- inline void __cb_close();
+ static void cb_learn(uint32_t msg, void* data);
+ static void cb_close(Fl_Widget* w, void* p);
+ void cb_learn(uint32_t* param, uint32_t msg, geMidiLearner* l);
+ void cb_close();
public:
- gdMidiInputBase(int x, int y, int w, int h, const char *title);
+ gdMidiInputBase(int x, int y, int w, int h, const char* title);
~gdMidiInputBase();
};
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
#include <FL/Fl_Pack.H>
#include "../../../utils/gui.h"
+#include "../../../utils/log.h"
#include "../../../core/const.h"
#include "../../../core/conf.h"
#include "../../../core/sampleChannel.h"
#ifdef WITH_VST
- #include "../../../core/pluginHost.h"
- #include "../../../core/plugin.h"
+ #include "../../../core/pluginHost.h"
+ #include "../../../core/plugin.h"
#endif
#include "../../../utils/string.h"
#include "../../elems/midiLearner.h"
#include "../../elems/basics/scroll.h"
#include "../../elems/basics/box.h"
#include "../../elems/basics/button.h"
+#include "../../elems/basics/choice.h"
#include "../../elems/basics/check.h"
#include "midiInputChannel.h"
using namespace giada::m;
-gdMidiInputChannel::gdMidiInputChannel(Channel *ch)
+gdMidiInputChannel::gdMidiInputChannel(Channel* ch)
: gdMidiInputBase(conf::midiInputX, conf::midiInputY, conf::midiInputW,
- conf::midiInputH, "MIDI Input Setup"),
+ conf::midiInputH, "MIDI Input Setup"),
ch(ch)
{
- string title = "MIDI Input Setup (channel " + gu_toString(ch->index+1) + ")";
+ string title = "MIDI Input Setup (channel " + gu_iToString(ch->index+1) + ")";
label(title.c_str());
- size_range(G_DEFAULT_MIDI_INPUT_UI_W, G_DEFAULT_MIDI_INPUT_UI_H);
+ size_range(G_DEFAULT_MIDI_INPUT_UI_W, G_DEFAULT_MIDI_INPUT_UI_H);
- enable = new geCheck(8, 8, 120, 20, "enable MIDI input");
+ Fl_Group* groupHeader = new Fl_Group(G_GUI_OUTER_MARGIN, G_GUI_OUTER_MARGIN, w(), 20);
+ groupHeader->begin();
- container = new geScroll(8, enable->y()+enable->h()+4, w()-16, h()-68);
- container->begin();
+ enable = new geCheck(G_GUI_OUTER_MARGIN, G_GUI_OUTER_MARGIN, 120, G_GUI_UNIT,
+ "enable MIDI input");
+ channel = new geChoice(enable->x()+enable->w()+44, G_GUI_OUTER_MARGIN, 120, G_GUI_UNIT);
- addChannelLearners();
+ groupHeader->resizable(nullptr);
+ groupHeader->end();
+
+ container = new geScroll(G_GUI_OUTER_MARGIN, enable->y()+enable->h()+G_GUI_OUTER_MARGIN,
+ w()-16, h()-76);
+ container->begin();
+
+ addChannelLearners();
#ifdef WITH_VST
- addPluginLearners();
+ addPluginLearners();
#endif
- container->end();
+ container->end();
- Fl_Group *groupButtons = new Fl_Group(8, container->y()+container->h()+8, container->w(), 20);
- groupButtons->begin();
+ Fl_Group* groupButtons = new Fl_Group(8, container->y()+container->h()+8, container->w(), 20);
+ groupButtons->begin();
- geBox *spacer = new geBox(groupButtons->x(), groupButtons->y(), 100, 20); // spacer window border <-> buttons
- ok = new geButton(w()-88, groupButtons->y(), 80, 20, "Close");
+ geBox* spacer = new geBox(groupButtons->x(), groupButtons->y(), 100, 20); // spacer window border <-> buttons
+ ok = new geButton(w()-88, groupButtons->y(), 80, 20, "Close");
- groupButtons->resizable(spacer);
- groupButtons->end();
+ groupButtons->resizable(spacer);
+ groupButtons->end();
- ok->callback(cb_close, (void*)this);
+ ok->callback(cb_close, (void*)this);
- enable->value(ch->midiIn);
+ enable->value(ch->midiIn);
enable->callback(cb_enable, (void*)this);
- resizable(container);
-
- gu_setFavicon(this);
- set_modal();
+ channel->add("Channel (any)");
+ channel->add("Channel 1");
+ channel->add("Channel 2");
+ channel->add("Channel 3");
+ channel->add("Channel 4");
+ channel->add("Channel 5");
+ channel->add("Channel 6");
+ channel->add("Channel 7");
+ channel->add("Channel 8");
+ channel->add("Channel 9");
+ channel->add("Channel 10");
+ channel->add("Channel 11");
+ channel->add("Channel 12");
+ channel->add("Channel 13");
+ channel->add("Channel 14");
+ channel->add("Channel 15");
+ channel->add("Channel 16");
+ channel->value(ch->getMidiInFilter() == -1 ? 0 : ch->getMidiInFilter() + 1);
+ channel->callback(cb_setChannel, (void*)this);
+
+ resizable(container);
+
+ end();
+
+ gu_setFavicon(this);
+ set_modal();
show();
}
gdMidiInputChannel::~gdMidiInputChannel()
{
- conf::midiInputX = x();
- conf::midiInputY = y();
+ conf::midiInputX = x();
+ conf::midiInputY = y();
conf::midiInputW = w();
conf::midiInputH = h();
}
void gdMidiInputChannel::addChannelLearners()
{
- Fl_Pack *pack = new Fl_Pack(container->x(), container->y(), LEARNER_WIDTH, 200);
- pack->spacing(4);
- pack->begin();
-
- geBox *header = new geBox(0, 0, LEARNER_WIDTH, 20, "channel");
- header->box(FL_BORDER_BOX);
- new geMidiLearner(0, 0, LEARNER_WIDTH, "key press", cb_learn, &ch->midiInKeyPress);
- new geMidiLearner(0, 0, LEARNER_WIDTH, "key release", cb_learn, &ch->midiInKeyRel);
- new geMidiLearner(0, 0, LEARNER_WIDTH, "key kill", cb_learn, &ch->midiInKill);
- new geMidiLearner(0, 0, LEARNER_WIDTH, "arm", cb_learn, &ch->midiInArm);
- new geMidiLearner(0, 0, LEARNER_WIDTH, "mute", cb_learn, &ch->midiInMute);
- new geMidiLearner(0, 0, LEARNER_WIDTH, "solo", cb_learn, &ch->midiInSolo);
- new geMidiLearner(0, 0, LEARNER_WIDTH, "volume", cb_learn, &ch->midiInVolume);
- if (ch->type == CHANNEL_SAMPLE) {
- new geMidiLearner(0, 0, LEARNER_WIDTH, "pitch", cb_learn, &((SampleChannel*)ch)->midiInPitch);
- new geMidiLearner(0, 0, LEARNER_WIDTH, "read actions", cb_learn, &((SampleChannel*)ch)->midiInReadActions);
- }
-
- pack->end();
+ Fl_Pack* pack = new Fl_Pack(container->x(), container->y(), LEARNER_WIDTH, 200);
+ pack->spacing(4);
+ pack->begin();
+
+ geBox *header = new geBox(0, 0, LEARNER_WIDTH, 20, "channel");
+ header->box(FL_BORDER_BOX);
+ new geMidiLearner(0, 0, LEARNER_WIDTH, "key press", cb_learn, &ch->midiInKeyPress, ch);
+ new geMidiLearner(0, 0, LEARNER_WIDTH, "key release", cb_learn, &ch->midiInKeyRel, ch);
+ new geMidiLearner(0, 0, LEARNER_WIDTH, "key kill", cb_learn, &ch->midiInKill, ch);
+ new geMidiLearner(0, 0, LEARNER_WIDTH, "arm", cb_learn, &ch->midiInArm, ch);
+ 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) {
+ new geMidiLearner(0, 0, LEARNER_WIDTH, "pitch", cb_learn,
+ &(static_cast<SampleChannel*>(ch))->midiInPitch, ch);
+ new geMidiLearner(0, 0, LEARNER_WIDTH, "read actions", cb_learn,
+ &(static_cast<SampleChannel*>(ch))->midiInReadActions, ch);
+ }
+
+ pack->end();
}
void gdMidiInputChannel::addPluginLearners()
{
- vector <Plugin *> *plugins = pluginHost::getStack(pluginHost::CHANNEL, ch);
- for (unsigned i=0; i<plugins->size(); i++) {
+ vector<Plugin*>* plugins = pluginHost::getStack(pluginHost::CHANNEL, ch);
+ for (unsigned i=0; i<plugins->size(); i++) {
- Fl_Pack *pack = new Fl_Pack(container->x() + ((i + 1) * (LEARNER_WIDTH + 8)),
- container->y(), LEARNER_WIDTH, 200);
- pack->spacing(4);
- pack->begin();
+ Fl_Pack* pack = new Fl_Pack(container->x() + ((i + 1) * (LEARNER_WIDTH + 8)),
+ container->y(), LEARNER_WIDTH, 200);
+ pack->spacing(4);
+ pack->begin();
- Plugin *plugin = plugins->at(i);
+ Plugin* plugin = plugins->at(i);
- geBox *header = new geBox(0, 0, LEARNER_WIDTH, 20, plugin->getName().c_str());
- header->box(FL_BORDER_BOX);
+ geBox* header = new geBox(0, 0, LEARNER_WIDTH, 20, plugin->getName().c_str());
+ header->box(FL_BORDER_BOX);
- for (int k=0; k<plugin->getNumParameters(); k++)
- new geMidiLearner(0, 0, LEARNER_WIDTH, plugin->getParameterName(k).c_str(),
- cb_learn, &plugin->midiInParams.at(k));
+ for (int k=0; k<plugin->getNumParameters(); k++)
+ new geMidiLearner(0, 0, LEARNER_WIDTH, plugin->getParameterName(k).c_str(),
+ cb_learn, &plugin->midiInParams.at(k), ch);
- pack->end();
- }
+ pack->end();
+ }
}
#endif
/* -------------------------------------------------------------------------- */
-void gdMidiInputChannel::cb_enable(Fl_Widget *w, void *p) { ((gdMidiInputChannel*)p)->__cb_enable(); }
+void gdMidiInputChannel::cb_enable(Fl_Widget* w, void* p) { ((gdMidiInputChannel*)p)->cb_enable(); }
+void gdMidiInputChannel::cb_setChannel(Fl_Widget* w, void* p) { ((gdMidiInputChannel*)p)->cb_setChannel(); }
/* -------------------------------------------------------------------------- */
-void gdMidiInputChannel::__cb_enable()
+void gdMidiInputChannel::cb_enable()
{
ch->midiIn = enable->value();
+ enable->value() ? channel->activate() : channel->deactivate();
}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gdMidiInputChannel::cb_setChannel()
+{
+ ch->setMidiInFilter(channel->value() == 0 ? -1 : channel->value() - 1);
+ gu_log("[gdMidiInputChannel] Set MIDI channel to %d\n",
+ ch->getMidiInFilter());
+}
+
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
#include "midiInputBase.h"
+class Channel;
+class geScroll;
+class geCheck;
+class geChoice;
+
+
class gdMidiInputChannel : public gdMidiInputBase
{
private:
- static const int LEARNER_WIDTH = 284;
-
- class Channel *ch;
-
- class geScroll *container;
- class geCheck *enable;
+ Channel* ch;
- //gVector <geMidiLearner *> items; // plugins parameters
+ geScroll* container;
+ geCheck* enable;
+ geChoice* channel;
- static void cb_enable (Fl_Widget *w, void *p);
- inline void __cb_enable();
+ static void cb_enable(Fl_Widget* w, void* p);
+ static void cb_setChannel(Fl_Widget* w, void* p);
+ void cb_enable();
+ void cb_setChannel();
- void addChannelLearners();
+ void addChannelLearners();
#ifdef WITH_VST
- void addPluginLearners();
+ void addPluginLearners();
#endif
public:
- gdMidiInputChannel(class Channel *ch);
- ~gdMidiInputChannel();
+ gdMidiInputChannel(Channel* ch);
+ ~gdMidiInputChannel();
};
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
* -------------------------------------------------------------------------- */
+#include <FL/Fl_Pack.H>
#include "../../../utils/gui.h"
#include "../../../core/conf.h"
+#include "../../../core/const.h"
#include "../../elems/midiLearner.h"
#include "../../elems/basics/button.h"
+#include "../../elems/basics/check.h"
+#include "../../elems/basics/choice.h"
#include "midiInputMaster.h"
gdMidiInputMaster::gdMidiInputMaster()
- : gdMidiInputBase(0, 0, 300, 256, "MIDI Input Setup (global)")
+ : gdMidiInputBase(0, 0, 300, 284, "MIDI Input Setup (global)")
{
set_modal();
- new geMidiLearner(8, 8, w()-16, "rewind", &cb_learn, &conf::midiInRewind);
- new geMidiLearner(8, 32, w()-16, "play/stop", &cb_learn, &conf::midiInStartStop);
- new geMidiLearner(8, 56, w()-16, "action recording", &cb_learn, &conf::midiInActionRec);
- new geMidiLearner(8, 80, w()-16, "input recording", &cb_learn, &conf::midiInInputRec);
- new geMidiLearner(8, 104, w()-16, "metronome", &cb_learn, &conf::midiInMetronome);
- new geMidiLearner(8, 128, w()-16, "input volume", &cb_learn, &conf::midiInVolumeIn);
- new geMidiLearner(8, 152, w()-16, "output volume", &cb_learn, &conf::midiInVolumeOut);
- new geMidiLearner(8, 176, w()-16, "sequencer ×2", &cb_learn, &conf::midiInBeatDouble);
- new geMidiLearner(8, 200, w()-16, "sequencer ÷2", &cb_learn, &conf::midiInBeatHalf);
- ok = new geButton(w()-88, 228, 80, 20, "Close");
+ Fl_Group* groupHeader = new Fl_Group(G_GUI_OUTER_MARGIN, G_GUI_OUTER_MARGIN, w(), 20);
+ groupHeader->begin();
+
+ enable = new geCheck(G_GUI_OUTER_MARGIN, G_GUI_OUTER_MARGIN, 120, G_GUI_UNIT,
+ "enable MIDI input");
+ channel = new geChoice(enable->x()+enable->w()+44, G_GUI_OUTER_MARGIN, 120, G_GUI_UNIT);
+
+ groupHeader->resizable(nullptr);
+ groupHeader->end();
+
+ Fl_Pack* pack = new Fl_Pack(G_GUI_OUTER_MARGIN, groupHeader->y()+groupHeader->h()+G_GUI_OUTER_MARGIN,
+ LEARNER_WIDTH, 212);
+ pack->spacing(G_GUI_INNER_MARGIN);
+ pack->begin();
+
+ new geMidiLearner(0, 0, LEARNER_WIDTH, "rewind", &cb_learn, &conf::midiInRewind, nullptr);
+ new geMidiLearner(0, 0, LEARNER_WIDTH, "play/stop", &cb_learn, &conf::midiInStartStop, nullptr);
+ new geMidiLearner(0, 0, LEARNER_WIDTH, "action recording", &cb_learn, &conf::midiInActionRec, nullptr);
+ new geMidiLearner(0, 0, LEARNER_WIDTH, "input recording", &cb_learn, &conf::midiInInputRec, nullptr);
+ new geMidiLearner(0, 0, LEARNER_WIDTH, "metronome", &cb_learn, &conf::midiInMetronome, nullptr);
+ new geMidiLearner(0, 0, LEARNER_WIDTH, "input volume", &cb_learn, &conf::midiInVolumeIn, nullptr);
+ new geMidiLearner(0, 0, LEARNER_WIDTH, "output volume", &cb_learn, &conf::midiInVolumeOut, nullptr);
+ new geMidiLearner(0, 0, LEARNER_WIDTH, "sequencer ×2", &cb_learn, &conf::midiInBeatDouble, nullptr);
+ new geMidiLearner(0, 0, LEARNER_WIDTH, "sequencer ÷2", &cb_learn, &conf::midiInBeatHalf, nullptr);
+
+ pack->end();
+
+ ok = new geButton(w()-88, pack->y()+pack->h()+G_GUI_OUTER_MARGIN, 80, G_GUI_UNIT, "Close");
+
+ end();
ok->callback(cb_close, (void*)this);
+ enable->value(conf::midiIn);
+ enable->callback(cb_enable, (void*)this);
+
+ channel->add("Channel (any)");
+ channel->add("Channel 1");
+ channel->add("Channel 2");
+ channel->add("Channel 3");
+ channel->add("Channel 4");
+ channel->add("Channel 5");
+ channel->add("Channel 6");
+ channel->add("Channel 7");
+ channel->add("Channel 8");
+ channel->add("Channel 9");
+ channel->add("Channel 10");
+ channel->add("Channel 11");
+ channel->add("Channel 12");
+ channel->add("Channel 13");
+ channel->add("Channel 14");
+ channel->add("Channel 15");
+ channel->add("Channel 16");
+ channel->value(conf::midiInFilter -1 ? 0 : conf::midiInFilter + 1);
+ channel->callback(cb_setChannel, (void*)this);
+
gu_setFavicon(this);
show();
}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gdMidiInputMaster::cb_enable(Fl_Widget* w, void* p) { ((gdMidiInputMaster*)p)->cb_enable(); }
+void gdMidiInputMaster::cb_setChannel(Fl_Widget* w, void* p) { ((gdMidiInputMaster*)p)->cb_setChannel(); }
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gdMidiInputMaster::cb_enable()
+{
+ conf::midiIn = enable->value();
+ enable->value() ? channel->activate() : channel->deactivate();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gdMidiInputMaster::cb_setChannel()
+{
+ conf::midiInFilter = channel->value() == 0 ? -1 : channel->value() - 1;
+}
+
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
#include "midiInputBase.h"
+class geCheck;
+class geChoice;
+
+
class gdMidiInputMaster : public gdMidiInputBase
{
+private:
+
+ geCheck* enable;
+ geChoice* channel;
+
+ static void cb_enable(Fl_Widget* w, void* p);
+ static void cb_setChannel(Fl_Widget* w, void* p);
+ void cb_enable();
+ void cb_setChannel();
+
public:
gdMidiInputMaster();
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
#include "../../../utils/log.h"
+#include "../../../utils/string.h"
#include "../../elems/midiLearner.h"
#include "midiOutputBase.h"
+using std::string;
using namespace giada::m;
void gdMidiOutputBase::stopMidiLearn(geMidiLearner *learner)
{
- kernelMidi::stopMidiLearn();
+ midiDispatcher::stopMidiLearn();
learner->updateValue();
}
void gdMidiOutputBase::setTitle(int chanNum)
{
- char title[64];
- sprintf(title, "MIDI Output Setup (channel %d)", chanNum);
- copy_label(title);
+ string tmp = "MIDI Output Setup (channel " + gu_iToString(chanNum) + ")";
+ copy_label(tmp.c_str());
}
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
+
/* -----------------------------------------------------------------------------
*
- * Giada - Your Hardcore Loopmachine
+, ch * Giada - Your Hardcore Loopmachine
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
#include "midiOutputMidiCh.h"
-gdMidiOutputMidiCh::gdMidiOutputMidiCh(MidiChannel *ch)
+gdMidiOutputMidiCh::gdMidiOutputMidiCh(MidiChannel* ch)
: gdMidiOutputBase(300, 168), ch(ch)
{
setTitle(ch->index+1);
chanListOut = new geChoice(w()-108, y()+8, 100, 20);
enableLightning = new geCheck(x()+8, chanListOut->y()+chanListOut->h()+8, 120, 20, "Enable MIDI lightning output");
- new geMidiLearner(x()+8, enableLightning->y()+enableLightning->h()+8, w()-16, "playing", cb_learn, &ch->midiOutLplaying);
- new geMidiLearner(x()+8, enableLightning->y()+enableLightning->h()+32, w()-16, "mute", cb_learn, &ch->midiOutLmute);
- new geMidiLearner(x()+8, enableLightning->y()+enableLightning->h()+56, w()-16, "solo", cb_learn, &ch->midiOutLsolo);
+ new geMidiLearner(x()+8, enableLightning->y()+enableLightning->h()+8, w()-16, "playing",
+ cb_learn, &ch->midiOutLplaying, ch);
+ new geMidiLearner(x()+8, enableLightning->y()+enableLightning->h()+32, w()-16, "mute",
+ cb_learn, &ch->midiOutLmute, ch);
+ new geMidiLearner(x()+8, enableLightning->y()+enableLightning->h()+56, w()-16, "solo",
+ cb_learn, &ch->midiOutLsolo, ch);
close = new geButton(w()-88, enableLightning->y()+enableLightning->h()+84, 80, 20, "Close");
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
#include "midiOutputSampleCh.h"
-gdMidiOutputSampleCh::gdMidiOutputSampleCh(SampleChannel *ch)
+gdMidiOutputSampleCh::gdMidiOutputSampleCh(SampleChannel* ch)
: gdMidiOutputBase(300, 140), ch(ch)
{
setTitle(ch->index+1);
enableLightning = new geCheck(8, 8, 120, 20, "Enable MIDI lightning output");
- new geMidiLearner(8, enableLightning->y()+enableLightning->h()+8, w()-16, "playing", cb_learn, &ch->midiOutLplaying);
- new geMidiLearner(8, enableLightning->y()+enableLightning->h()+32, w()-16, "mute", cb_learn, &ch->midiOutLmute);
- new geMidiLearner(8, enableLightning->y()+enableLightning->h()+56, w()-16, "solo", cb_learn, &ch->midiOutLsolo);
+ new geMidiLearner(8, enableLightning->y()+enableLightning->h()+8, w()-16, "playing",
+ cb_learn, &ch->midiOutLplaying, ch);
+ new geMidiLearner(8, enableLightning->y()+enableLightning->h()+32, w()-16, "mute",
+ cb_learn, &ch->midiOutLmute, ch);
+ new geMidiLearner(8, enableLightning->y()+enableLightning->h()+56, w()-16, "solo",
+ cb_learn, &ch->midiOutLsolo, ch);
close = new geButton(w()-88, enableLightning->y()+enableLightning->h()+84, 80, 20, "Close");
close->callback(cb_close, (void*)this);
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
if (stackType == pluginHost::MASTER_IN)
label("Master In Plugins");
else {
- string l = "Channel " + gu_toString(ch->index+1) + " Plugins";
+ string l = "Channel " + gu_iToString(ch->index+1) + " Plugins";
copy_label(l.c_str());
}
/* -------------------------------------------------------------------------- */
-void gdPluginList::cb_addPlugin(Fl_Widget* v, void* p) { ((gdPluginList*)p)->__cb_addPlugin(); }
+void gdPluginList::cb_addPlugin(Fl_Widget* v, void* p) { ((gdPluginList*)p)->cb_addPlugin(); }
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
-void gdPluginList::__cb_addPlugin()
+void gdPluginList::cb_addPlugin()
{
/* the usual callback that gdWindow adds to each subwindow in this case
* is not enough, because when we close the browser the plugin list
/* -------------------------------------------------------------------------- */
-void gdPlugin::cb_removePlugin (Fl_Widget* v, void* p) { ((gdPlugin*)p)->__cb_removePlugin(); }
-void gdPlugin::cb_openPluginWindow(Fl_Widget* v, void* p) { ((gdPlugin*)p)->__cb_openPluginWindow(); }
-void gdPlugin::cb_setBypass (Fl_Widget* v, void* p) { ((gdPlugin*)p)->__cb_setBypass(); }
-void gdPlugin::cb_shiftUp (Fl_Widget* v, void* p) { ((gdPlugin*)p)->__cb_shiftUp(); }
-void gdPlugin::cb_shiftDown (Fl_Widget* v, void* p) { ((gdPlugin*)p)->__cb_shiftDown(); }
-void gdPlugin::cb_setProgram (Fl_Widget* v, void* p) { ((gdPlugin*)p)->__cb_setProgram(); }
+void gdPlugin::cb_removePlugin (Fl_Widget* v, void* p) { ((gdPlugin*)p)->cb_removePlugin(); }
+void gdPlugin::cb_openPluginWindow(Fl_Widget* v, void* p) { ((gdPlugin*)p)->cb_openPluginWindow(); }
+void gdPlugin::cb_setBypass (Fl_Widget* v, void* p) { ((gdPlugin*)p)->cb_setBypass(); }
+void gdPlugin::cb_shiftUp (Fl_Widget* v, void* p) { ((gdPlugin*)p)->cb_shiftUp(); }
+void gdPlugin::cb_shiftDown (Fl_Widget* v, void* p) { ((gdPlugin*)p)->cb_shiftDown(); }
+void gdPlugin::cb_setProgram (Fl_Widget* v, void* p) { ((gdPlugin*)p)->cb_setProgram(); }
/* -------------------------------------------------------------------------- */
-void gdPlugin::__cb_shiftUp()
+void gdPlugin::cb_shiftUp()
{
/*nothing to do if there's only one plugin */
/* -------------------------------------------------------------------------- */
-void gdPlugin::__cb_shiftDown()
+void gdPlugin::cb_shiftDown()
{
/*nothing to do if there's only one plugin */
/* -------------------------------------------------------------------------- */
-void gdPlugin::__cb_removePlugin()
+void gdPlugin::cb_removePlugin()
{
/* any subwindow linked to the plugin must be destroyed first */
/* -------------------------------------------------------------------------- */
-void gdPlugin::__cb_openPluginWindow()
+void gdPlugin::cb_openPluginWindow()
{
/* the new pluginWindow has id = id_plugin + 1, because id=0 is reserved
* for the parent window 'add plugin'. */
gu_log("[gdPlugin::__cb_openPluginWindow] Plug-in has editor, window id=%d\n", pwid);
w = new gdPluginWindowGUI(pPlugin);
}
- else
+ else {
w = new gdPluginWindow(pPlugin);
-
- gu_log("[gdPlugin::__cb_openPluginWindow] Plug-in has no editor, window id=%d\n", pwid);
-
+ gu_log("[gdPlugin::__cb_openPluginWindow] Plug-in has no editor, window id=%d\n", pwid);
+ }
+
if (pParent->hasWindow(pwid))
pParent->delSubWindow(pwid);
w->setId(pwid);
/* -------------------------------------------------------------------------- */
-void gdPlugin::__cb_setBypass()
+void gdPlugin::cb_setBypass()
{
pPlugin->toggleBypass();
}
/* -------------------------------------------------------------------------- */
-void gdPlugin::__cb_setProgram()
+void gdPlugin::cb_setProgram()
{
- pPlugin->setCurrentProgram(program->value());
+ //pPlugin->setCurrentProgram(program->value());
+ plugin::setProgram(pPlugin, program->value());
}
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
geButton *addPlugin;
Fl_Scroll *list;
- static void cb_addPlugin (Fl_Widget *v, void *p);
- inline void __cb_addPlugin();
+ static void cb_addPlugin(Fl_Widget *v, void *p);
+ void cb_addPlugin();
public:
gdPluginList *pParent;
Plugin *pPlugin;
- static void cb_removePlugin (Fl_Widget *v, void *p);
- static void cb_openPluginWindow (Fl_Widget *v, void *p);
- static void cb_setBypass (Fl_Widget *v, void *p);
- static void cb_shiftUp (Fl_Widget *v, void *p);
- static void cb_shiftDown (Fl_Widget *v, void *p);
- static void cb_setProgram (Fl_Widget *v, void *p);
- inline void __cb_removePlugin ();
- inline void __cb_openPluginWindow ();
- inline void __cb_setBypass ();
- inline void __cb_shiftUp ();
- inline void __cb_shiftDown ();
- inline void __cb_setProgram ();
+ static void cb_removePlugin(Fl_Widget *v, void *p);
+ static void cb_openPluginWindow(Fl_Widget *v, void *p);
+ static void cb_setBypass(Fl_Widget *v, void *p);
+ static void cb_shiftUp(Fl_Widget *v, void *p);
+ static void cb_shiftDown(Fl_Widget *v, void *p);
+ static void cb_setProgram(Fl_Widget *v, void *p);
+ void cb_removePlugin();
+ void cb_openPluginWindow();
+ void cb_setBypass();
+ void cb_shiftUp();
+ void cb_shiftDown();
+ void cb_setProgram();
public:
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
}
+void gdPluginWindow::updateParameters(bool changeSlider)
+{
+ for (int i=0; i<m_plugin->getNumParameters(); i++) {
+ static_cast<gePluginParameter*>(m_list->child(i))->update(changeSlider);
+ }
+}
+
+
/* -------------------------------------------------------------------------- */
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
gdPluginWindow(Plugin* p);
void updateParameter(int index, bool changeSlider=false);
+ void updateParameters(bool changeSlider=false);
};
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
if (conf::sampleEditorGridVal == 0)
grid->value(0);
else
- grid->value(grid->find_item(gu_toString(conf::sampleEditorGridVal).c_str()));
+ grid->value(grid->find_item(gu_iToString(conf::sampleEditorGridVal).c_str()));
grid->callback(cb_changeGrid, (void*)this);
snap->value(conf::sampleEditorGridOn);
void gdSampleEditor::updateInfo()
{
- string bitDepth = ch->wave->getBits() != 0 ? gu_toString(ch->wave->getBits()) : "(unknown)";
+ string bitDepth = ch->wave->getBits() != 0 ? gu_iToString(ch->wave->getBits()) : "(unknown)";
string infoText =
"File: " + ch->wave->getPath() + "\n"
- "Size: " + gu_toString(ch->wave->getSize()) + " frames\n"
- "Duration: " + gu_toString(ch->wave->getDuration()) + " seconds\n"
+ "Size: " + gu_iToString(ch->wave->getSize()) + " frames\n"
+ "Duration: " + gu_iToString(ch->wave->getDuration()) + " seconds\n"
"Bit depth: " + bitDepth + "\n"
- "Frequency: " + gu_toString(ch->wave->getRate()) + " Hz\n";
+ "Frequency: " + gu_iToString(ch->wave->getRate()) + " Hz\n";
info->copy_label(infoText.c_str());
}
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* ---------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* ------------------------------------------------------------------------------
*
-* Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+* Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
using namespace giada::m;
-gePianoItem::gePianoItem(int X, int Y, int rel_x, int rel_y, recorder::action *_a,
- recorder::action *_b, gdActionEditor *pParent)
+gePianoItem::gePianoItem(int X, int Y, int rel_x, int rel_y, recorder::action a,
+ recorder::action b, gdActionEditor* pParent)
: geBasePianoItem(X, Y, MIN_WIDTH, pParent),
- a (_a),
- b (_b),
- event_a (0x00),
- event_b (0x00),
+ a (a),
+ b (b),
changed (false)
{
- /* a is a pointer: action exists, needs to be displayed */
-
- if (a) {
- note = kernelMidi::getB2(a->iValue);
- frame_a = a->frame;
- frame_b = b->frame;
- event_a = a->iValue;
- event_b = b->iValue;
- int newX = rel_x + (frame_a / pParent->zoom);
- int newY = rel_y + getY(note);
- int newW = (frame_b - frame_a) / pParent->zoom;
- resize(newX, newY, newW, h());
- }
-
- /* a is null: action needs to be recorded from scratch */
-
- else {
- note = getNote(rel_y);
- frame_a = rel_x * pParent->zoom;
- frame_b = (rel_x + 20) * pParent->zoom;
- record();
- size((frame_b - frame_a) / pParent->zoom, h());
- }
+ int newX = rel_x + (a.frame / pParent->zoom);
+ int newY = rel_y + getY(kernelMidi::getB2(a.iValue));
+ int newW = (b.frame - a.frame) / pParent->zoom;
+ resize(newX, newY, newW, h());
}
void gePianoItem::reposition(int pianoRollX)
{
- int newX = pianoRollX + (frame_a / pParent->zoom);
- int newW = ((frame_b - frame_a) / pParent->zoom);
- if (newW < MIN_WIDTH)
- newW = MIN_WIDTH;
- resize(newX, y(), newW, h());
- redraw();
+ int newX = pianoRollX + (a.frame / pParent->zoom);
+ int newW = ((b.frame - a.frame) / pParent->zoom);
+ if (newW < MIN_WIDTH)
+ newW = MIN_WIDTH;
+ resize(newX, y(), newW, h());
+ redraw();
}
* end = the lowest value between the two ending points
* if start < end then there's an overlap of end-start pixels. */
- geNoteEditor *pPiano = static_cast<geNoteEditor*>(parent());
+ geNoteEditor* noteEditor = static_cast<geNoteEditor*>(parent());
- for (int i=0; i<pPiano->children(); i++) {
+ for (int i=0; i<noteEditor->children(); i++) {
- gePianoItem *pItem = static_cast<gePianoItem*>(pPiano->child(i));
+ gePianoItem* pItem = static_cast<gePianoItem*>(noteEditor->child(i));
/* don't check against itself and with different y positions */
/* -------------------------------------------------------------------------- */
-void gePianoItem::record()
+void gePianoItem::removeAction()
{
- /* avoid frame overflow */
-
- int overflow = frame_b - clock::getTotalFrames();
- if (overflow > 0) {
- frame_b -= overflow;
- frame_a -= overflow;
- }
-
- event_a |= (MIDI_NOTE_ON);
- event_a |= (note << 16); // note value
- event_a |= (MIDI_VELOCITY);
- event_a |= (0x00);
-
- event_b |= (MIDI_NOTE_OFF);
- event_b |= (note << 16); // note value
- event_b |= (MIDI_VELOCITY);
- event_b |= (0x00);
-
- recorder::rec(pParent->chan->index, G_ACTION_MIDI, frame_a, event_a);
- recorder::rec(pParent->chan->index, G_ACTION_MIDI, frame_b, event_b);
- pParent->chan->hasActions = true;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gePianoItem::remove()
-{
- MidiChannel *ch = static_cast<MidiChannel*>(pParent->chan);
- recorder::deleteAction(ch->index, frame_a, G_ACTION_MIDI, true,
- &mixer::mutex_recs, event_a, 0.0);
- recorder::deleteAction(ch->index, frame_b, G_ACTION_MIDI, true,
- &mixer::mutex_recs, event_b, 0.0);
+ MidiChannel* ch = static_cast<MidiChannel*>(pParent->chan);
+ recorder::deleteAction(ch->index, a.frame, G_ACTION_MIDI, true,
+ &mixer::mutex_recs, a.iValue, 0.0);
+ recorder::deleteAction(ch->index, b.frame, G_ACTION_MIDI, true,
+ &mixer::mutex_recs, b.iValue, 0.0);
/* Send a note-off in case we are deleting it in a middle of a key_on/key_off
- sequence. */
+ sequence. */
- ch->sendMidi(event_b);
- ch->hasActions = recorder::hasActions(ch->index);
- static_cast<gePianoRoll*>(parent())->cursorOnItem = false;
+ ch->sendMidi(b.iValue);
+ ch->hasActions = recorder::hasActions(ch->index);
}
switch (e) {
case FL_ENTER: {
- static_cast<gePianoRoll*>(parent())->cursorOnItem = true;
selected = true;
ret = 1;
redraw();
case FL_LEAVE: {
fl_cursor(FL_CURSOR_DEFAULT, FL_WHITE, FL_BLACK);
- static_cast<gePianoRoll*>(parent())->cursorOnItem = false;
selected = false;
ret = 1;
redraw();
}
case FL_PUSH: {
-
push_x = Fl::event_x() - x();
old_x = x();
old_w = w();
if (Fl::event_button3()) {
fl_cursor(FL_CURSOR_DEFAULT, FL_WHITE, FL_BLACK);
- remove();
+ removeAction();
hide(); // for Windows
Fl::delete_widget(this);
static_cast<geNoteEditor*>(parent())->redraw();
case FL_RELEASE: {
- /* delete & record the action, only if it doesn't overlap with
- * another one */
+
+ gePianoRoll* pianoRoll = static_cast<gePianoRoll*>(parent());
+
+ /* Delete and record the action, only if it doesn't overlap with another
+ existing one. */
if (overlap()) {
resize(old_x, y(), old_w, h());
}
else
if (changed) {
- remove();
- note = getNote(getRelY());
- frame_a = getRelX() * pParent->zoom;
- frame_b = (getRelX()+w()) * pParent->zoom;
- record();
+ removeAction();
+ int note = pianoRoll->yToNote(getRelY());
+ int frame_a = getRelX() * pParent->zoom;
+ int frame_b = (getRelX()+w()) * pParent->zoom;
+ pianoRoll->recordAction(note, frame_a, frame_b);
changed = false;
}
- static_cast<geNoteEditor*>(parent())->redraw();
+ pianoRoll->redraw();
ret = 1;
break;
/* -------------------------------------------------------------------------- */
-int gePianoItem::getNote(int rel_y)
-{
- return gePianoRoll::MAX_KEYS - (rel_y / gePianoRoll::CELL_H);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
int gePianoItem::getRelY()
{
- return y() - parent()->y();
+ return y() - parent()->y();
}
int gePianoItem::getRelX()
{
- return x() - parent()->x();
+ return x() - parent()->x();
}
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
#include "../../../core/recorder.h"
+#include "../../../core/midiEvent.h"
#include "basePianoItem.h"
{
private:
- /* getRelX/Y
- * return x/y point of this item, relative to piano roll (and not to
- * entire screen) */
-
- int getRelY();
- int getRelX();
-
- /* getNote
- * from a relative_y return the real MIDI note, range 0-127. 15 is
- * the hardcoded value for note height in pixels */
-
- int getNote(int rel_y);
-
- /* overlap
- * check if this item don't overlap with another one. */
-
- bool overlap();
-
- struct giada::m::recorder::action *a;
- struct giada::m::recorder::action *b;
-
- int push_x;
-
- /* MIDI note, start frame, end frame - Used only if it's a newly added
- * action */ /** FIXME - is it true? */
+ struct giada::m::recorder::action a;
+ struct giada::m::recorder::action b;
- int note;
- int frame_a;
- int frame_b;
+ int push_x;
- /* event - bitmasked MIDI events, generated by record() or by ctor if
- * not newly added action */
-
- int event_a;
- int event_b;
-
- /* changed - if Item has been moved or resized: re-recording needed */
+ /* changed
+ If Item has been moved or resized: re-recording needed. */
bool changed;
- /* onLeft,RightEdge - if cursor is on a widget's edge */
+ /* onLeft, RightEdge
+ If cursor is on a widget's edge. */
bool onLeftEdge;
bool onRightEdge;
- /* old_x, old_w - store previous width and position while dragging
- * and moving, in order to restore it if overlap */
+ /* old_x, old_w
+ Store previous width and position while dragging and moving, in order to
+ restore it if overlap. */
int old_x, old_w;
+ /* getRelX/Y
+ Returns x/y point of this item, relative to piano roll (and not to entire
+ screen). */
+
+ int getRelY();
+ int getRelX();
+
+ /* overlap
+ Checks if this item don't overlap with another one. */
+
+ bool overlap();
+
public:
static const int MIN_WIDTH = 10;
static const int HANDLE_WIDTH = 5;
- /* pianoItem ctor
- * if action *a == nullptr, record a new action */
-
gePianoItem(int x, int y, int rel_x, int rel_y,
- struct giada::m::recorder::action *a, struct giada::m::recorder::action *b,
- gdActionEditor *pParent);
-
+ struct giada::m::recorder::action a, struct giada::m::recorder::action b,
+ gdActionEditor* pParent);
+
void draw() override;
int handle(int e) override;
void reposition(int pianoRollX) override;
- void record();
- void remove();
+ void removeAction();
};
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
gePianoItemOrphaned::gePianoItemOrphaned(int x, int y, int xRel, int yRel,
- recorder::action *action, gdActionEditor *pParent)
+ recorder::action action, gdActionEditor* pParent)
: geBasePianoItem(x, y, WIDTH, pParent)
{
- note = kernelMidi::getB2(action->iValue);
- frame = action->frame;
- event = action->iValue;
+ note = kernelMidi::getB2(action.iValue);
+ frame = action.frame;
+ event = action.iValue;
int newX = xRel + (frame / pParent->zoom);
int newY = yRel + getY(note);
resize(newX, newY, w(), h());
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
static const int WIDTH = 12;
gePianoItemOrphaned(int x, int y, int xRel, int yRel,
- struct giada::m::recorder::action *action, gdActionEditor *pParent);
+ struct giada::m::recorder::action action, gdActionEditor* pParent);
void draw() override;
int handle(int e) override;
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
#include "../../../core/recorder.h"
#include "../../../core/kernelMidi.h"
#include "../../../utils/log.h"
+#include "../../../utils/string.h"
+#include "../../../glue/recorder.h"
#include "../../dialogs/gd_actionEditor.h"
#include "pianoItem.h"
#include "pianoItemOrphaned.h"
#include "pianoRoll.h"
-using namespace giada::m;
+using std::string;
+using std::vector;
+using namespace giada;
-gePianoRoll::gePianoRoll(int X, int Y, int W, gdActionEditor *pParent)
- : geBaseActionEditor(X, Y, W, 40, pParent),
- cursorOnItem (false)
+gePianoRoll::gePianoRoll(int X, int Y, int W, gdActionEditor* pParent)
+ : geBaseActionEditor(X, Y, W, 40, pParent)
{
+
resizable(nullptr); // don't resize children (i.e. pianoItem)
size(W, (MAX_KEYS+1) * CELL_H); // 128 MIDI channels * CELL_H height
- if (conf::pianoRollY == -1)
+ if (m::conf::pianoRollY == -1)
position(x(), y()-(h()/2)); // center
else
- position(x(), conf::pianoRollY);
+ position(x(), m::conf::pianoRollY);
drawSurface1();
drawSurface2();
- /* Add actions when the window is opened. Position is zoom-based. MIDI actions
- come always in pair: noteOn + noteOff. */
-
- recorder::sortActions();
-
- recorder::action *a2 = nullptr;
- recorder::action *prev = nullptr;
-
- for (unsigned i=0; i<recorder::frames.size(); i++) {
-
- if (recorder::frames.at(i) > clock::getTotalFrames()) // don't show actions > gray area
- continue;
-
- for (unsigned j=0; j<recorder::global.at(i).size(); j++) {
-
- recorder::action *a1 = recorder::global.at(i).at(j);
-
- /* Skip action if:
- - does not belong to this channel
- - is not a MIDI action (we only want MIDI things here)
- - is the previous one (we have already checked it)
- - (later on) is not a MIDI Note On type. We don't want any other kind of
- action here */
-
- if (a1->chan != pParent->chan->index)
- continue;
- if (a1->type != G_ACTION_MIDI)
- continue;
- if (a1 == prev)
- continue;
-
- /* Extract MIDI infos from a1: if is note off skip it, we are looking for
- noteOn only. */
-
- int a1_type = kernelMidi::getB1(a1->iValue);
- int a1_note = kernelMidi::getB2(a1->iValue);
-
- /* If two same notes are found (noteOn-noteOn, noteOff-noteOff) or the
- very first action is a noteOff, add orphaned item.*/
-
- if ((prev && a1_type == prev->type) || a1_type == 0x80) {
- gu_log("[geNoteEditor] invalid note pair! Add orphaned item\n");
- new gePianoItemOrphaned(0, 0, x(), y(), a1, pParent);
- a2 = nullptr;
- continue;
- }
-
- /* Now skip anything that is not a noteOn. */
-
- if (a1_type != 0x90)
- continue;
+ build();
+}
- /* Search for the next action. Must have: same channel, G_ACTION_MIDI,
- greater than a1->frame and with MIDI properties of note_off (0x80), same
- note of a1, any velocity value (0xFF) because we just don't care about the
- velocity of a note_off. */
- recorder::getNextAction(a1->chan, G_ACTION_MIDI, a1->frame, &a2,
- kernelMidi::getIValue(0x80, a1_note, 0xFF));
+/* -------------------------------------------------------------------------- */
- /* Next action note_off found: add a new gePianoItem to piano roll. Add
- an orphaned piano item otherwise. */
- if (a2) {
- new gePianoItem(0, 0, x(), y(), a1, a2, pParent);
- prev = a2;
- a2 = nullptr;
- }
- else {
- gu_log("[geNoteEditor] noteOff not found! Add orphaned item\n");
- new gePianoItemOrphaned(0, 0, x(), y(), a1, pParent);
- }
- }
+void gePianoRoll::build()
+{
+ using namespace m::recorder;
+
+ clear();
+
+ int channel = pParent->chan->index;
+ int maxFrame = m::clock::getTotalFrames();
+
+ vector<Composite> actions = c::recorder::getMidiActions(channel, maxFrame);
+ for (Composite composite : actions)
+ {
+ m::MidiEvent e1 = composite.a1.iValue;
+ m::MidiEvent e2 = composite.a2.iValue;
+
+ gu_log("[gePianoRoll] ((0x%X, 0x%X, f=%d) - (0x%X, 0x%X, f=%d))\n",
+ e1.getStatus(), e1.getNote(), composite.a1.frame,
+ e2.getStatus(), e2.getNote(), composite.a2.frame
+ );
+
+ if (composite.a2.frame != -1)
+ add(new gePianoItem(0, 0, x(), y(), composite.a1, composite.a2, pParent));
+ else
+ add(new gePianoItemOrphaned(0, 0, x(), y(), composite.a1, pParent));
}
- end();
+ redraw();
}
/* print key note label. C C# D D# E F F# G G# A A# B */
- char note[6];
+ string note = gu_iToString(octave);
switch (i % KEYS) {
case (int) Notes::G:
fl_rectf(0, i*CELL_H, CELL_W, CELL_H, G_COLOR_GREY_2);
- sprintf(note, "%d G", octave);
+ note += " G";
break;
case (int) Notes::FS:
- sprintf(note, "%d F#", octave);
+ note += " F#";
break;
case (int) Notes::F:
- sprintf(note, "%d F", octave);
+ note += " F";
break;
case (int) Notes::E:
fl_rectf(0, i*CELL_H, CELL_W, CELL_H, G_COLOR_GREY_2);
- sprintf(note, "%d E", octave);
+ note += " E";
break;
case (int) Notes::DS:
- sprintf(note, "%d D#", octave);
+ note += " D#";
break;
case (int) Notes::D:
fl_rectf(0, i*CELL_H, CELL_W, CELL_H, G_COLOR_GREY_2);
- sprintf(note, "%d D", octave);
+ note += " D";
break;
case (int) Notes::CS:
- sprintf(note, "%d C#", octave);
+ note += " C#";
break;
case (int) Notes::C:
- sprintf(note, "%d C", octave);
+ note += " C";
octave--;
break;
case (int) Notes::B:
fl_rectf(0, i*CELL_H, CELL_W, CELL_H, G_COLOR_GREY_2);
- sprintf(note, "%d B", octave);
+ note += " B";
break;
case (int) Notes::AS:
- sprintf(note, "%d A#", octave);
+ note += " A#";
break;
case (int) Notes::A:
fl_rectf(0, i*CELL_H, CELL_W, CELL_H, G_COLOR_GREY_2);
- sprintf(note, "%d A", octave);
+ note += " A";
break;
case (int) Notes::GS:
- sprintf(note, "%d G#", octave);
+ note += " G#";
break;
}
- /* Print note name */
+ /* Print note name */
fl_color(G_COLOR_GREY_3);
- fl_draw(note, 4, ((i-1)*CELL_H)+1, CELL_W, CELL_H,
- (Fl_Align) (FL_ALIGN_LEFT | FL_ALIGN_CENTER));
+ fl_draw(note.c_str(), 4, ((i-1)*CELL_H)+1, CELL_W, CELL_H,
+ (Fl_Align) (FL_ALIGN_LEFT | FL_ALIGN_CENTER));
/* Print horizontal line */
int gePianoRoll::handle(int e)
{
int ret = Fl_Group::handle(e);
-
switch (e) {
case FL_PUSH: {
break;
}
-
push_y = Fl::event_y() - y();
if (Fl::event_button1()) {
- /* ax is driven by grid, ay by the height in px of each note */
+ /* ax is driven by grid, ay by the height in px of each note. */
int ax = Fl::event_x();
int ay = Fl::event_y();
- /* vertical snap */
+ /* Vertical snap. */
int edge = (ay-y()) % CELL_H;
if (edge != 0) ay -= edge;
- /* if no overlap, add new piano item. Also check that it doesn't
- * overflow on the grey area, by shifting it to the left if
- * necessary. */
-
- if (!cursorOnItem) {
- int greyover = ax+20 - pParent->coverX-x();
- if (greyover > 0)
- ax -= greyover;
- add(new gePianoItem(ax, ay, ax-x(), ay-y(), nullptr, nullptr, pParent));
- redraw();
- }
+ /* If there are no pianoItems below the mouse, add a new one. */
+
+ gePianoItem* pianoItem = dynamic_cast<gePianoItem*>(Fl::belowmouse());
+ if (pianoItem == nullptr)
+ recordAction(yToNote(ay-y()), (ax-x()) * pParent->zoom);
}
ret = 1;
break;
}
+
case FL_DRAG: {
if (Fl::event_button3()) {
- geNoteEditor *prc = static_cast<geNoteEditor*>(parent());
+ geNoteEditor* prc = static_cast<geNoteEditor*>(parent());
position(x(), Fl::event_y() - push_y);
if (y() > prc->y())
ret = 1;
break;
}
+
case FL_MOUSEWHEEL: { // nothing to do, just avoid small internal scroll
ret = 1;
break;
/* -------------------------------------------------------------------------- */
+void gePianoRoll::recordAction(int note, int frame_a, int frame_b)
+{
+ c::recorder::recordMidiAction(pParent->chan->index, note, frame_a, frame_b);
+ pParent->chan->hasActions = true;
+ build();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int gePianoRoll::yToNote(int y)
+{
+ return gePianoRoll::MAX_KEYS - (y / gePianoRoll::CELL_H);
+}
+
+/* -------------------------------------------------------------------------- */
+
+
void gePianoRoll::updateActions()
{
for (int k=0; k<children(); k++)
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
A = 11, GS = 0
};
+ int push_y;
+
+ Fl_Offscreen surface1; // notes, no repeat
+ Fl_Offscreen surface2; // lines, x-repeat
+
/* drawSurface*
Generates a complex drawing in memory first and copy it to the screen at a
later point in time. Fl_Offscreen surface holds the necessary data. The first
void drawSurface1();
void drawSurface2();
- int push_y;
- Fl_Offscreen surface1; // notes, no repeat
- Fl_Offscreen surface2; // lines, x-repeat
+ void build();
public:
static const int CELL_H = 18;
static const int CELL_W = 40;
- gePianoRoll(int x, int y, int w, gdActionEditor *pParent);
+ gePianoRoll(int x, int y, int w, gdActionEditor* pParent);
- void draw();
- int handle(int e);
+ void draw() override;
+ int handle(int e) override;
/* updateActions
Repositions existing actions after a zoom gesture. */
- void updateActions();
+ void updateActions() override;
- /* cursorOnItem
- Defines wheter the cursor is over a piano item. This value is updated by each
- gePianoItem sub-widget. */
+ void recordAction(int note, int frame_a, int frame_b=0);
- bool cursorOnItem;
+ int yToNote(int y);
};
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
int handle(int e) override;
void draw() override;
+ void resize(int x, int y, int w, int h) override;
int getMinSize() const;
- void resize(int x, int y, int w, int h);
};
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
int nfreq = kernelAudio::getTotalFreqs(sounddevOut->value());
for (int i=0; i<nfreq; i++) {
int freq = kernelAudio::getFreq(sounddevOut->value(), i);
- samplerate->add(gu_toString(freq).c_str());
+ samplerate->add(gu_iToString(freq).c_str());
if (freq == conf::samplerate)
samplerate->value(i);
}
buffersize->add("1024");
buffersize->add("2048");
buffersize->add("4096");
- buffersize->showItem(gu_toString(conf::buffersize).c_str());
+ buffersize->showItem(gu_iToString(conf::buffersize).c_str());
rsmpQuality->add("Sinc best quality (very slow)");
rsmpQuality->add("Sinc medium quality (slow)");
rsmpQuality->add("Linear (very fast)");
rsmpQuality->value(conf::rsmpQuality);
- delayComp->value(gu_toString(conf::delayComp).c_str());
+ delayComp->value(gu_iToString(conf::delayComp).c_str());
delayComp->type(FL_INT_INPUT);
delayComp->maximum_size(5);
return;
}
for (unsigned i=0; i<chs; i+=2) {
- char str[16];
- sprintf(str, "%d-%d", (i+1), (i+2));
- channelsIn->add(str);
+ string tmp = gu_iToString(i+1) + "-" + gu_iToString(i+2);
+ channelsIn->add(tmp.c_str());
}
channelsIn->value(conf::channelsIn);
}
return;
}
for (unsigned i=0; i<chs; i+=2) {
- char str[16];
- sprintf(str, "%d-%d", (i+1), (i+2));
- channelsOut->add(str);
+ string tmp = gu_iToString(i+1) + "-" + gu_iToString(i+2);
+ channelsOut->add(tmp.c_str());
}
channelsOut->value(conf::channelsOut);
}
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
#include <string>
-#include <rtmidi/RtMidi.h>
#include "../../../core/const.h"
+#ifdef G_OS_MAC
+ #include <RtMidi.h>
+#else
+ #include <rtmidi/RtMidi.h>
+#endif
#include "../../../core/conf.h"
#include "../../../core/midiMapConf.h"
#include "../../../core/kernelMidi.h"
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
#ifdef WITH_VST
+#include <functional>
#include <FL/Fl.H>
#include "../../../core/const.h"
#include "../../../core/conf.h"
+#include "../../../core/graphics.h"
#include "../../../core/pluginHost.h"
+#include "../../../glue/plugin.h"
#include "../../../utils/string.h"
#include "../../../utils/fs.h"
+#include "../../../utils/gui.h"
+#include "../../dialogs/window.h"
+#include "../../dialogs/gd_mainWindow.h"
+#include "../../dialogs/browser/browserDir.h"
#include "../basics/box.h"
#include "../basics/radio.h"
#include "../basics/check.h"
#include "tabPlugins.h"
+extern gdMainWindow* G_MainWin;
+
+
using std::string;
using namespace giada::m;
geTabPlugins::geTabPlugins(int X, int Y, int W, int H)
: Fl_Group(X, Y, W, H, "Plugins")
{
- folderPath = new geInput(x()+w()-250, y()+8, 250, 20);
- scanButton = new geButton(x()+w()-120, folderPath->y()+folderPath->h()+8, 120, 20);
- info = new geBox(x(), scanButton->y()+scanButton->h()+8, w(), 242);
+ m_browse = new geButton(x()+w()-G_GUI_UNIT, y()+9, G_GUI_UNIT, G_GUI_UNIT, "", zoomInOff_xpm, zoomInOn_xpm);
+ m_folderPath = new geInput(m_browse->x()-258, y()+9, 250, G_GUI_UNIT);
+ m_scanButton = new geButton(x()+w()-120, m_folderPath->y()+m_folderPath->h()+8, 120, G_GUI_UNIT);
+ m_info = new geBox(x(), m_scanButton->y()+m_scanButton->h()+8, w(), 242);
end();
labelsize(G_GUI_FONT_SIZE_BASE);
- info->label("Scan in progress. Please wait...");
- info->hide();
+ m_info->label("Scan in progress. Please wait...");
+ m_info->hide();
+
+ m_folderPath->value(conf::pluginPath.c_str());
+ m_folderPath->label("Plugins folder");
- folderPath->value(conf::pluginPath.c_str());
- folderPath->label("Plugins folder");
+ m_browse->callback(cb_browse, (void*) this);
- scanButton->callback(cb_scan, (void*) this);
+ m_scanButton->callback(cb_scan, (void*) this);
- updateCount();
+ refreshCount();
}
/* -------------------------------------------------------------------------- */
-void geTabPlugins::updateCount()
+void geTabPlugins::refreshCount()
{
- string scanLabel = "Scan (" + gu_toString(pluginHost::countAvailablePlugins()) + " found)";
- scanButton->label(scanLabel.c_str());
+ string scanLabel = "Scan (" + gu_iToString(pluginHost::countAvailablePlugins()) + " found)";
+ m_scanButton->label(scanLabel.c_str());
}
/* -------------------------------------------------------------------------- */
-void geTabPlugins::cb_scan(Fl_Widget *w, void *p) { ((geTabPlugins*)p)->__cb_scan(w); }
+void geTabPlugins::cb_scan(Fl_Widget* w, void* p) { ((geTabPlugins*)p)->cb_scan(); }
+void geTabPlugins::cb_browse(Fl_Widget* w, void* p) { ((geTabPlugins*)p)->cb_browse(); }
/* -------------------------------------------------------------------------- */
-void geTabPlugins::cb_onScan(float progress, void *p)
+void geTabPlugins::cb_browse()
{
- string l = "Scan in progress (" + gu_toString((int)(progress*100)) + "%). Please wait...";
- ((geTabPlugins *)p)->info->label(l.c_str());
- Fl::wait();
+ gdBrowserDir* browser = new gdBrowserDir(0, 0, 800, 600, "Add plug-ins directory",
+ conf::patchPath, giada::c::plugin::setPluginPathCb);
+
+ static_cast<gdWindow*>(top_window())->addSubWindow(browser);
}
/* -------------------------------------------------------------------------- */
-void geTabPlugins::__cb_scan(Fl_Widget *w)
+void geTabPlugins::cb_scan()
{
- info->show();
- pluginHost::scanDir(folderPath->value(), cb_onScan, (void*) this);
+ std::function<void(float)> callback = [this] (float progress)
+ {
+ string l = "Scan in progress (" + gu_iToString((int)(progress*100)) + "%). Please wait...";
+ m_info->label(l.c_str());
+ Fl::wait();
+ };
+
+ m_info->show();
+ pluginHost::scanDirs(m_folderPath->value(), callback);
pluginHost::saveList(gu_getHomePath() + G_SLASH + "plugins.xml");
- info->hide();
- updateCount();
+ m_info->hide();
+ refreshCount();
}
void geTabPlugins::save()
{
- conf::pluginPath = folderPath->value();
+ conf::pluginPath = m_folderPath->value();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geTabPlugins::refreshVstPath()
+{
+ m_folderPath->value(conf::pluginPath.c_str());
+ m_folderPath->redraw();
}
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
{
private:
- static void cb_scan (Fl_Widget *w, void *p);
- static void cb_onScan(float progress, void *p);
- inline void __cb_scan(Fl_Widget *w);
+ geInput* m_folderPath;
+ geButton* m_browse;
+ geButton* m_scanButton;
+ geBox* m_info;
- void updateCount();
+ static void cb_scan(Fl_Widget* w, void* p);
+ static void cb_browse(Fl_Widget* w, void* p);
+ void cb_scan();
+ void cb_browse();
-public:
+ void refreshCount();
- geInput *folderPath;
- geButton *scanButton;
- geBox *info;
+public:
geTabPlugins(int x, int y, int w, int h);
void save();
+ void refreshVstPath();
};
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
#include <FL/fl_draw.H>
#include "../../../../core/const.h"
+#include "../../../../utils/string.h"
#include "channelButton.h"
{
if (k == 0)
m_key = "";
- else {
- // FIXME - this crap won't work with unicode/utf-8
- char c = (char) k;
- m_key = c;
- }
+ else
+ m_key = static_cast<char>(k); // FIXME - What about unicode/utf-8?
}
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
geChannelButton(int x, int y, int w, int h, const char* l=0);
- virtual int handle(int e) = 0;
-
void draw() override;
void setKey(const std::string& k);
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
using std::string;
-using namespace giada::m;
+using namespace giada;
namespace
gu_openSubWindow(G_MainWin, new gdActionEditor(gch->ch), WID_ACTION_EDITOR);
break;
case Menu::CLEAR_ACTIONS_ALL:
- glue_clearAllActions(gch);
+ c::recorder::clearAllActions(gch);
break;
case Menu::SETUP_KEYBOARD_INPUT:
gu_openSubWindow(G_MainWin, new gdKeyGrabber(gch->ch), 0);
label = mch->getName().c_str();
if (mch->midiOut)
- label += " (ch " + gu_toString(mch->midiOutChan + 1) + " out)";
+ label += " (ch " + gu_iToString(mch->midiOutChan + 1) + " out)";
mainButton->label(label.c_str());
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
extern gdMainWindow* G_MainWin;
-using namespace giada::m;
+using namespace giada;
namespace
break;
}
case Menu::LOAD_SAMPLE: {
- gdWindow *w = new gdBrowserLoad(conf::browserX, conf::browserY,
- conf::browserW, conf::browserH, "Browse sample",
- conf::samplePath.c_str(), glue_loadSample, gch->ch);
+ gdWindow *w = new gdBrowserLoad(m::conf::browserX, m::conf::browserY,
+ m::conf::browserW, m::conf::browserH, "Browse sample",
+ m::conf::samplePath.c_str(), glue_loadSample, gch->ch);
gu_openSubWindow(G_MainWin, w, WID_FILE_BROWSER);
break;
}
case Menu::EXPORT_SAMPLE: {
- gdWindow *w = new gdBrowserSave(conf::browserX, conf::browserY,
- conf::browserW, conf::browserH, "Save sample",
- conf::samplePath.c_str(), "", glue_saveSample, gch->ch);
+ gdWindow *w = new gdBrowserSave(m::conf::browserX, m::conf::browserY,
+ m::conf::browserW, m::conf::browserH, "Save sample",
+ m::conf::samplePath.c_str(), "", glue_saveSample, gch->ch);
gu_openSubWindow(G_MainWin, w, WID_FILE_BROWSER);
break;
}
case Menu::__END_RESIZE_SUBMENU__:
break;
case Menu::CLEAR_ACTIONS_ALL: {
- glue_clearAllActions(gch);
+ c::recorder::clearAllActions(gch);
break;
}
case Menu::CLEAR_ACTIONS_MUTE: {
- glue_clearMuteActions(gch);
+ c::recorder::clearMuteActions(gch);
break;
}
case Menu::CLEAR_ACTIONS_VOLUME: {
- glue_clearVolumeActions(gch);
+ c::recorder::clearVolumeActions(gch);
break;
}
case Menu::CLEAR_ACTIONS_START_STOP: {
- glue_clearStartStopActions(gch);
+ c::recorder::clearStartStopActions(gch);
break;
}
case Menu::RESIZE_H1: {
/* If you're recording (input or actions) no menu is allowed; you can't do
anything, especially deallocate the channel */
- if (mixer::recording || recorder::active)
+ if (m::mixer::recording || m::recorder::active)
return;
Fl_Menu_Item rclick_menu[] = {
setColorsByStatus(ch->status, ch->recStatus);
if (static_cast<SampleChannel*>(ch)->wave != nullptr) {
- if (mixer::recording && ch->isArmed())
+ if (m::mixer::recording && ch->isArmed())
mainButton->setInputRecordMode();
- if (recorder::active) {
- if (recorder::canRec(ch, clock::isRunning(), mixer::recording))
+ if (m::recorder::active) {
+ if (m::recorder::canRec(ch, m::clock::isRunning(), m::mixer::recording))
mainButton->setActionRecordMode();
}
status->redraw(); // status invisible? sampleButton too (see below)
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
void reset() override;
void update() override;
void refresh() override;
-
- void changeSize(int h);
+ void changeSize(int h) override;
/* show/hideActionButton
Adds or removes 'R' button when actions are available. */
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
-* Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+* Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
#include "../../../core/clock.h"
#include "../../../glue/main.h"
#include "../../../utils/gui.h"
+#include "../../../utils/string.h"
#include "../../elems/basics/button.h"
#include "../../elems/basics/choice.h"
#include "../../dialogs/gd_mainWindow.h"
extern gdMainWindow *G_MainWin;
+using std::string;
using namespace giada::m;
resizable(nullptr); // don't resize any widget
- char buf[6]; snprintf(buf, 6, "%f", clock::getBpm());
- bpm->copy_label(buf);
-
+ bpm->copy_label(gu_fToString(clock::getBpm(), 1).c_str());
bpm->callback(cb_bpm, (void*)this);
+
meter->callback(cb_meter, (void*)this);
+
multiplier->callback(cb_multiplier, (void*)this);
+
divider->callback(cb_divider, (void*)this);
quantizer->add("off", 0, cb_quantizer, (void*)this);
void geMainTimer::setBpm(float v)
{
- char buf[6];
- sprintf(buf, "%.01f", v); // only 1 decimal place (e.g. 120.0)
- bpm->copy_label(buf);
+ bpm->copy_label(gu_fToString((float) v, 1).c_str()); // Only 1 decimal place (e.g. 120.0)
}
void geMainTimer::setMeter(int beats, int bars)
{
- char buf[8];
- sprintf(buf, "%d/%d", beats, bars);
- meter->copy_label(buf);
+ string tmp = gu_iToString(beats) + "/" + gu_iToString(bars);
+ meter->copy_label(tmp.c_str());
}
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
* -------------------------------------------------------------------------- */
+#include "../../utils/string.h"
+#include "../dialogs/midiIO/midiInputBase.h"
#include "basics/boxtypes.h"
#include "basics/button.h"
#include "basics/box.h"
#include "midiLearner.h"
+using std::string;
using namespace giada::m;
-geMidiLearner::geMidiLearner(int X, int Y, int W, const char *l,
- kernelMidi::cb_midiLearn *cb, uint32_t *param)
+geMidiLearner::geMidiLearner(int X, int Y, int W, const char* l,
+ midiDispatcher::cb_midiLearn* cb, uint32_t* param, Channel* ch)
: Fl_Group(X, Y, W, 20),
callback(cb),
+ ch (ch),
param (param)
{
begin();
void geMidiLearner::updateValue()
{
- char buf[16];
- if (*param != 0x0)
- snprintf(buf, 9, "0x%X", *param);
+ string tmp;
+ if (*param != 0x0) {
+ tmp = "0x" + gu_iToString(*param, true); // true: hex mode
+ tmp.pop_back(); // Remove last two digits, useless in MIDI messages
+ tmp.pop_back(); // Remove last two digits, useless in MIDI messages
+ }
else
- snprintf(buf, 16, "(not set)");
- value->copy_label(buf);
+ tmp = "(not set)";
+ value->copy_label(tmp.c_str());
button->value(0);
}
/* -------------------------------------------------------------------------- */
-void geMidiLearner::cb_button(Fl_Widget *v, void *p) { ((geMidiLearner*)p)->__cb_button(); }
-void geMidiLearner::cb_value(Fl_Widget *v, void *p) { ((geMidiLearner*)p)->__cb_value(); }
+void geMidiLearner::cb_button(Fl_Widget* v, void* p) { ((geMidiLearner*)p)->cb_button(); }
+void geMidiLearner::cb_value(Fl_Widget* v, void* p) { ((geMidiLearner*)p)->cb_value(); }
/* -------------------------------------------------------------------------- */
-void geMidiLearner::__cb_value()
+void geMidiLearner::cb_value()
{
if (Fl::event_button() == FL_RIGHT_MOUSE) {
*param = 0x0;
/* -------------------------------------------------------------------------- */
-void geMidiLearner::__cb_button()
+void geMidiLearner::cb_button()
{
if (button->value() == 1) {
- cbData.window = (gdMidiInput*) parent(); // parent = gdMidiInput
+ cbData.window = static_cast<gdMidiInputBase*>(parent()); // parent = gdMidiInput
cbData.learner = this;
- kernelMidi::startMidiLearn(callback, (void*)&cbData);
+ cbData.channel = ch;
+ midiDispatcher::startMidiLearn(callback, (void*)&cbData);
}
else
- kernelMidi::stopMidiLearn();
+ midiDispatcher::stopMidiLearn();
}
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
#include <FL/Fl_Group.H>
-#include "../../core/kernelMidi.h"
+#include "../../core/midiDispatcher.h"
-class gdMidiInput;
+class Channel;
+class gdMidiInputBase;
class geMidiLearner;
class geBox;
class geButton;
private:
/* callback
- * cb to pass to kernelMidi. Requires two parameters:
+ Callback to pass to midiDispatcher. Requires two parameters:
* uint32_t msg - MIDI message
* void *data - extra data */
- giada::m::kernelMidi::cb_midiLearn *callback;
+ giada::m::midiDispatcher::cb_midiLearn* callback;
- geBox *text;
- geButton *value;
- geButton *button;
+ /* Channel it belongs to. Might be nullptr if the learner comes from the MIDI
+ input master window. */
- static void cb_button(Fl_Widget *v, void *p);
- static void cb_value (Fl_Widget *v, void *p);
- inline void __cb_button();
- inline void __cb_value();
+ Channel* ch;
+
+ geBox* text;
+ geButton* value;
+ geButton* button;
+
+ static void cb_button(Fl_Widget* v, void* p);
+ static void cb_value (Fl_Widget* v, void* p);
+ void cb_button();
+ void cb_value();
public:
/* cbData_t
- * struct we pass to kernelMidi as extra parameter. */
+ Struct we pass to midiDispatcher as extra parameter. */
struct cbData_t
{
- gdMidiInput *window;
- geMidiLearner *learner;
+ gdMidiInputBase* window;
+ geMidiLearner* learner;
+ Channel* channel;
} cbData;
/* param
* pointer to ch->midiIn[value] */
- uint32_t *param;
+ uint32_t* param;
- geMidiLearner(int x, int y, int w, const char *l,
- giada::m::kernelMidi::cb_midiLearn *cb, uint32_t *param);
+ geMidiLearner(int x, int y, int w, const char* l,
+ giada::m::midiDispatcher::cb_midiLearn* cb, uint32_t* param, Channel* ch);
void updateValue();
};
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
#include "../../../core/waveFx.h"
#include "../../../glue/channel.h"
#include "../../../utils/gui.h"
+#include "../../../utils/string.h"
#include "../../../utils/math.h"
#include "../../dialogs/sampleEditor.h"
#include "../basics/dial.h"
void geBoostTool::refresh()
{
- char buf[16];
- float dB = gu_linearToDB(ch->getBoost());
- sprintf(buf, "%.2f", dB);
- input->value(buf);
- // a dial > than it's max value goes crazy
+ input->value(gu_fToString(gu_linearToDB(ch->getBoost()), 2).c_str()); // 2 digits
+ // A dial greater than it's max value goes crazy
dial->value(ch->getBoost() <= 10.0f ? ch->getBoost() : 10.0f);
}
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
#include "../../../glue/channel.h"
#include "../../../utils/gui.h"
#include "../../../utils/math.h"
+#include "../../../utils/string.h"
#include "../../dialogs/sampleEditor.h"
#include "../basics/dial.h"
#include "../basics/input.h"
#include "panTool.h"
+using std::string;
+
+
gePanTool::gePanTool(int x, int y, SampleChannel *ch)
: Fl_Group(x, y, 200, 20),
ch (ch)
void gePanTool::refresh()
{
dial->value(ch->getPan());
- char buf[8];
+
if (ch->getPan() < 0.5f) {
- sprintf(buf, "%d L", (int) ((-ch->getPan() * 200.0f) + 100.0f));
- input->value(buf);
+ string tmp = gu_iToString((int) ((-ch->getPan() * 200.0f) + 100.0f)) + " L";
+ input->value(tmp.c_str());
}
else
if (ch->getPan() == 0.5)
input->value("C");
else {
- sprintf(buf, "%d R", (int) ((ch->getPan() * 200.0f) - 100.0f));
- input->value(buf);
+ string tmp = gu_iToString((int) ((ch->getPan() * 200.0f) - 100.0f)) + " R";
+ input->value(tmp.c_str());
}
}
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
#include "../../../core/clock.h"
#include "../../../glue/channel.h"
#include "../../../utils/gui.h"
+#include "../../../utils/string.h"
#include "../../dialogs/sampleEditor.h"
#include "../basics/dial.h"
#include "../basics/input.h"
void gePitchTool::refresh()
{
dial->value(ch->getPitch());
- char buf[16];
- sprintf(buf, "%.4f", ch->getPitch()); // 4 digits
- input->value(buf);
+ input->value(gu_fToString(ch->getPitch(), 4).c_str()); // 4 digits
}
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
void geRangeTool::refresh()
{
- m_begin->value(gu_toString(m_ch->getBegin()).c_str());
- m_end->value(gu_toString(m_ch->getEnd()).c_str());
+ m_begin->value(gu_iToString(m_ch->getBegin()).c_str());
+ m_end->value(gu_iToString(m_ch->getEnd()).c_str());
}
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
* -------------------------------------------------------------------------- */
+#include <cstdlib>
#include "../../../core/const.h"
#include "../../../core/sampleChannel.h"
#include "../../../utils/gui.h"
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_toString(ch->getShift()).c_str());
+ m_shift->value(gu_iToString(ch->getShift()).c_str());
m_shift->callback(cb_setShift, (void*)this);
m_reset->callback(cb_reset, (void*)this);
void geShiftTool::refresh()
{
- m_shift->value(gu_toString(m_ch->getShift()).c_str());
+ m_shift->value(gu_iToString(m_ch->getShift()).c_str());
}
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
#include <cmath>
+#include <cstdlib>
#include <FL/Fl_Pack.H>
#include "../../../core/sampleChannel.h"
#include "../../../core/const.h"
#include "../../../glue/channel.h"
#include "../../../utils/gui.h"
#include "../../../utils/math.h"
+#include "../../../utils/string.h"
#include "../basics/dial.h"
#include "../basics/input.h"
#include "../basics/box.h"
#include "volumeTool.h"
+using std::string;
+
+
geVolumeTool::geVolumeTool(int X, int Y, SampleChannel *ch)
: Fl_Group(X, Y, 150, 20),
ch (ch)
void geVolumeTool::refresh()
{
- char buf[16];
+ string tmp;
float dB = gu_linearToDB(ch->volume);
- if (dB > -INFINITY) sprintf(buf, "%.2f", dB);
- else sprintf(buf, "-inf");
- input->value(buf);
+ if (dB > -INFINITY) tmp = gu_fToString(dB, 2); // 2 digits
+ else tmp = "-inf";
+ input->value(tmp.c_str());
dial->value(ch->guiChannel->vol->value());
}
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
* -------------------------------------------------------------------------- */
-#include <rtmidi/RtMidi.h>
+#include "../core/const.h"
+#ifdef G_OS_MAC
+ #include <RtMidi.h>
+#else
+ #include <rtmidi/RtMidi.h>
+#endif
#include <sndfile.h>
#include "../deps/rtaudio-mod/RtAudio.h"
#include "deps.h"
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
* -------------------------------------------------------------------------- */
+#include <iomanip>
+#include <cstdarg>
#include <climits>
-#include <sstream>
#include "../core/const.h"
#include "string.h"
/* -------------------------------------------------------------------------- */
-string gu_toString(int i)
-{
- /* std::to_string is the way to go. Unfortunately it seems that it isn't
- available in gcc's standard library (libstdc++), it is however, available in
- libc++ which comes with LLVM/clang. */
-
-#ifdef G_OS_MAC
+/* TODO - use std::to_string() */
+string gu_fToString(float f, int precision)
+{
std::stringstream out;
- out << i;
+ out << std::fixed << std::setprecision(precision) << f;
return out.str();
-
-#else
-
- return std::to_string(i);
-
-#endif
}
/* -------------------------------------------------------------------------- */
+std::string gu_format(const char* format, ...)
+{
+ va_list args;
+
+ /* Compute the size of the new expanded string (i.e. with replacement taken
+ into account). */
+
+ size_t size = vsnprintf(nullptr, 0, format, args);
+
+ /* Create a new temporary char array to hold the new expanded string. */
+
+ std::unique_ptr<char[]> tmp(new char[size]);
+
+ /* Fill the temporary string with the formatted data. */
+
+ va_start(args, format);
+ vsprintf(tmp.get(), format, args);
+ va_end(args);
+
+ return string(tmp.get(), tmp.get() + size - 1);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
void gu_split(string in, string sep, vector<string>* v)
{
string full = in;
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
#include <string>
#include <vector>
+#include <sstream>
+#include "../core/const.h"
+template <typename T>
+std::string gu_iToString(T t, bool hex=false)
+{
+ std::stringstream out;
+ if (hex)
+ out << std::hex << std::uppercase << t;
+ else
+ out << t;
+ return out.str();
+}
+
std::string gu_getRealPath(const std::string& path);
std::string gu_replace(std::string in, const std::string& search,
std::string gu_trim(const std::string& s);
-std::string gu_toString(int i);
-
void gu_split(std::string in, std::string sep, std::vector<std::string>* v);
+std::string gu_fToString(float f, int precision);
+
+std::string gu_format(const char* format, ...);
+
#endif
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
*
* -----------------------------------------------------------------------------
*
- * Copyright (C) 2010-2017 Giovanni A. Zuliani | Monocasual
+ * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual
*
* This file is part of Giada - Your Hardcore Loopmachine.
*
TEST_CASE("Test Recorder")
{
- /* Each SECTION the TEST_CASE is executed from the start. The following
- code is exectuted before each SECTION. */
-
- pthread_mutex_t mutex;
- pthread_mutex_init(&mutex, nullptr);
-
- recorder::init();
- REQUIRE(recorder::frames.size() == 0);
- REQUIRE(recorder::global.size() == 0);
-
- SECTION("Test record single action")
- {
- recorder::rec(0, G_ACTION_KEYPRESS, 50, 1, 0.5f);
-
- REQUIRE(recorder::frames.size() == 1);
- REQUIRE(recorder::frames.at(0) == 50);
- REQUIRE(recorder::global.at(0).size() == 1); // 1 action on frame #0
- REQUIRE(recorder::global.at(0).at(0)->chan == 0);
- REQUIRE(recorder::global.at(0).at(0)->type == G_ACTION_KEYPRESS);
- REQUIRE(recorder::global.at(0).at(0)->frame == 50);
- REQUIRE(recorder::global.at(0).at(0)->iValue == 1);
- REQUIRE(recorder::global.at(0).at(0)->fValue == 0.5f);
- }
-
- SECTION("Test record, two actions on same frame")
- {
- recorder::rec(0, G_ACTION_KEYPRESS, 50, 6, 0.3f);
- recorder::rec(0, G_ACTION_KEYREL, 50, 1, 0.5f);
-
- REQUIRE(recorder::frames.size() == 1); // same frame, frames.size must stay 1
- REQUIRE(recorder::frames.at(0) == 50);
- REQUIRE(recorder::global.at(0).size() == 2); // 2 actions on frame #0
-
- REQUIRE(recorder::global.at(0).at(0)->chan == 0);
- REQUIRE(recorder::global.at(0).at(0)->type == G_ACTION_KEYPRESS);
- REQUIRE(recorder::global.at(0).at(0)->frame == 50);
- REQUIRE(recorder::global.at(0).at(0)->iValue == 6);
- REQUIRE(recorder::global.at(0).at(0)->fValue == 0.3f);
-
- REQUIRE(recorder::global.at(0).at(1)->chan == 0);
- REQUIRE(recorder::global.at(0).at(1)->type == G_ACTION_KEYREL);
- REQUIRE(recorder::global.at(0).at(1)->frame == 50);
- REQUIRE(recorder::global.at(0).at(1)->iValue == 1);
- REQUIRE(recorder::global.at(0).at(1)->fValue == 0.5f);
-
- SECTION("Test record, another action on a different frame")
- {
- recorder::rec(0, G_ACTION_KEYPRESS, 70, 1, 0.5f);
-
- REQUIRE(recorder::frames.size() == 2);
- REQUIRE(recorder::frames.at(1) == 70);
- REQUIRE(recorder::global.at(0).size() == 2); // 2 actions on frame #0
- REQUIRE(recorder::global.at(1).size() == 1); // 1 actions on frame #1
- REQUIRE(recorder::global.at(1).at(0)->chan == 0);
- REQUIRE(recorder::global.at(1).at(0)->type == G_ACTION_KEYPRESS);
- REQUIRE(recorder::global.at(1).at(0)->frame == 70);
- REQUIRE(recorder::global.at(1).at(0)->iValue == 1);
- REQUIRE(recorder::global.at(1).at(0)->fValue == 0.5f);
- }
- }
-
- SECTION("Test retrieval")
- {
- recorder::rec(0, G_ACTION_KEYPRESS, 50, 6, 0.3f);
- recorder::rec(0, G_ACTION_KEYREL, 70, 1, 0.5f);
- recorder::rec(1, G_ACTION_KEYPRESS, 50, 6, 0.3f);
- recorder::rec(1, G_ACTION_KEYREL, 70, 1, 0.5f);
- recorder::rec(2, G_ACTION_KEYPRESS, 100, 6, 0.3f);
- recorder::rec(2, G_ACTION_KEYREL, 120, 1, 0.5f);
-
- /* Give me action on chan 1, type G_ACTION_KEYREL, frame 70. */
- recorder::action *action = nullptr;
- REQUIRE(recorder::getAction(1, G_ACTION_KEYREL, 70, &action) == 1);
-
- REQUIRE(action != nullptr);
- REQUIRE(action->chan == 1);
- REQUIRE(action->type == G_ACTION_KEYREL);
- REQUIRE(action->frame == 70);
- REQUIRE(action->iValue == 1);
- REQUIRE(action->fValue == 0.5f);
-
- /* Give me *next* action on chan 0, type G_ACTION_KEYREL, starting from frame 20.
- Must be action #2 */
-
- REQUIRE(recorder::getNextAction(0, G_ACTION_KEYREL, 20, &action) == 1);
- REQUIRE(action != nullptr);
- REQUIRE(action->chan == 0);
- REQUIRE(action->type == G_ACTION_KEYREL);
- REQUIRE(action->frame == 70);
-
- /* Give me *next* action on chan 2, type G_ACTION_KEYPRESS, starting from
- frame 200. You are requesting frame outside boundaries. */
-
- REQUIRE(recorder::getNextAction(2, G_ACTION_KEYPRESS, 200, &action) == -1);
-
- /* Give me *next* action on chan 2, type G_ACTION_KEYPRESS, starting from
- frame 100. That action does not exist. */
-
- REQUIRE(recorder::getNextAction(2, G_ACTION_KEYPRESS, 100, &action) == -2);
- }
-
- SECTION("Test deletion, single action")
- {
- recorder::rec(0, G_ACTION_KEYPRESS, 50, 6, 0.3f);
- recorder::rec(0, G_ACTION_KEYREL, 60, 1, 0.5f);
- recorder::rec(1, G_ACTION_KEYPRESS, 70, 6, 0.3f);
- recorder::rec(1, G_ACTION_KEYREL, 80, 1, 0.5f);
-
- /* Delete action #0, don't check values. */
- recorder::deleteAction(0, 50, G_ACTION_KEYPRESS, false, &mutex);
-
- REQUIRE(recorder::frames.size() == 3);
- REQUIRE(recorder::global.size() == 3);
-
- SECTION("Test deletion checked")
- {
- /* Delete action #1, check values. */
- recorder::deleteAction(1, 70, G_ACTION_KEYPRESS, true, &mutex, 6, 0.3f);
-
- REQUIRE(recorder::frames.size() == 2);
- REQUIRE(recorder::global.size() == 2);
- }
- }
-
- SECTION("Test deletion, range of actions")
- {
- recorder::rec(0, G_ACTION_KEYPRESS, 50, 6, 0.3f);
- recorder::rec(0, G_ACTION_KEYREL, 60, 1, 0.5f);
- recorder::rec(0, G_ACTION_KEYPRESS, 70, 6, 0.3f);
- recorder::rec(0, G_ACTION_KEYREL, 80, 1, 0.5f);
- recorder::rec(1, G_ACTION_KEYPRESS, 100, 6, 0.3f);
- recorder::rec(1, G_ACTION_KEYREL, 120, 1, 0.5f);
-
- /* Delete any action on channel 0 of types KEYPRESS | KEYREL between
- frames 0 and 200. */
-
- recorder::deleteActions(0, 0, 200, G_ACTION_KEYPRESS | G_ACTION_KEYREL, &mutex);
-
- REQUIRE(recorder::frames.size() == 2);
- REQUIRE(recorder::global.size() == 2);
- REQUIRE(recorder::global.at(0).size() == 1);
- REQUIRE(recorder::global.at(1).size() == 1);
-
- REQUIRE(recorder::global.at(0).at(0)->chan == 1);
- REQUIRE(recorder::global.at(0).at(0)->type == G_ACTION_KEYPRESS);
- REQUIRE(recorder::global.at(0).at(0)->frame == 100);
- REQUIRE(recorder::global.at(0).at(0)->iValue == 6);
- REQUIRE(recorder::global.at(0).at(0)->fValue == 0.3f);
-
- REQUIRE(recorder::global.at(1).at(0)->chan == 1);
- REQUIRE(recorder::global.at(1).at(0)->type == G_ACTION_KEYREL);
- REQUIRE(recorder::global.at(1).at(0)->frame == 120);
- REQUIRE(recorder::global.at(1).at(0)->iValue == 1);
- REQUIRE(recorder::global.at(1).at(0)->fValue == 0.5f);
- }
-
- SECTION("Test action presence")
- {
- recorder::rec(0, G_ACTION_KEYREL, 80, 1, 0.5f);
- recorder::rec(1, G_ACTION_KEYPRESS, 100, 6, 0.3f);
- recorder::rec(1, G_ACTION_KEYREL, 120, 1, 0.5f);
-
- recorder::deleteAction(0, 80, G_ACTION_KEYREL, false, &mutex);
-
- REQUIRE(recorder::hasActions(0) == false);
- REQUIRE(recorder::hasActions(1) == true);
- }
-
- SECTION("Test clear actions by channel")
- {
- recorder::rec(0, G_ACTION_KEYREL, 80, 1, 0.5f);
- recorder::rec(1, G_ACTION_KEYPRESS, 100, 6, 0.3f);
- recorder::rec(1, G_ACTION_KEYREL, 120, 1, 0.5f);
-
- recorder::clearChan(1);
-
- REQUIRE(recorder::hasActions(0) == true);
- REQUIRE(recorder::hasActions(1) == false);
- REQUIRE(recorder::frames.size() == 1);
- REQUIRE(recorder::global.size() == 1);
- REQUIRE(recorder::global.at(0).size() == 1);
- }
-
- SECTION("Test clear actions by type")
- {
- recorder::rec(1, G_ACTION_KEYREL, 80, 1, 0.5f);
- recorder::rec(0, G_ACTION_KEYPRESS, 100, 6, 0.3f);
- recorder::rec(1, G_ACTION_KEYREL, 120, 1, 0.5f);
-
- /* Clear all actions of type KEYREL from channel 1. */
-
- recorder::clearAction(1, G_ACTION_KEYREL);
-
- REQUIRE(recorder::hasActions(0) == true);
- REQUIRE(recorder::hasActions(1) == false);
- REQUIRE(recorder::frames.size() == 1);
- REQUIRE(recorder::global.size() == 1);
- REQUIRE(recorder::global.at(0).size() == 1);
- }
-
- SECTION("Test clear all")
- {
- recorder::rec(0, G_ACTION_KEYPRESS, 0, 1, 0.5f);
- recorder::rec(1, G_ACTION_KEYPRESS, 0, 1, 0.5f);
- recorder::rec(0, G_ACTION_KEYREL, 80, 1, 0.5f);
- recorder::rec(1, G_ACTION_KEYREL, 100, 6, 0.3f);
- recorder::rec(2, G_ACTION_KILL, 120, 1, 0.5f);
-
- recorder::clearAll();
- REQUIRE(recorder::frames.size() == 0);
- REQUIRE(recorder::global.size() == 0);
- }
-
- SECTION("Test optimization")
- {
- recorder::rec(0, G_ACTION_KEYPRESS, 20, 1, 0.5f);
- recorder::rec(0, G_ACTION_KEYREL, 80, 1, 0.5f);
- recorder::rec(1, G_ACTION_KEYPRESS, 20, 1, 0.5f);
- recorder::rec(1, G_ACTION_KEYREL, 80, 1, 0.5f);
-
- /* Fake frame 80 without actions.*/
- recorder::global.at(1).clear();
-
- recorder::optimize();
-
- REQUIRE(recorder::frames.size() == 1);
- REQUIRE(recorder::global.size() == 1);
- REQUIRE(recorder::global.at(0).size() == 2);
- }
-
- SECTION("Test BPM update")
- {
- recorder::rec(0, G_ACTION_KEYPRESS, 0, 1, 0.5f);
- recorder::rec(0, G_ACTION_KEYREL, 80, 1, 0.5f);
-
- recorder::updateBpm(60.0f, 120.0f, 44100); // scaling up
-
- REQUIRE(recorder::frames.at(0) == 0);
- REQUIRE(recorder::frames.at(1) == 40);
-
- recorder::updateBpm(120.0f, 60.0f, 44100); // scaling down
-
- REQUIRE(recorder::frames.at(0) == 0);
- REQUIRE(recorder::frames.at(1) == 80);
- }
-
- SECTION("Test samplerate update")
- {
- recorder::rec(0, G_ACTION_KEYPRESS, 0, 1, 0.5f);
- recorder::rec(0, G_ACTION_KEYREL, 80, 1, 0.5f);
- recorder::rec(0, G_ACTION_KEYPRESS, 120, 1, 0.5f);
- recorder::rec(0, G_ACTION_KEYREL, 150, 1, 0.5f);
-
- recorder::updateSamplerate(44100, 22050); // scaling down
-
- REQUIRE(recorder::frames.at(0) == 0);
- REQUIRE(recorder::frames.at(1) == 160);
- REQUIRE(recorder::frames.at(2) == 240);
- REQUIRE(recorder::frames.at(3) == 300);
-
- recorder::updateSamplerate(22050, 44100); // scaling up
-
- REQUIRE(recorder::frames.at(0) == 0);
- REQUIRE(recorder::frames.at(1) == 80);
- REQUIRE(recorder::frames.at(2) == 120);
- REQUIRE(recorder::frames.at(3) == 150);
- }
-
- SECTION("Test expand")
- {
- recorder::rec(0, G_ACTION_KEYPRESS, 0, 1, 0.5f);
- recorder::rec(0, G_ACTION_KEYREL, 80, 1, 0.5f);
- recorder::rec(0, G_ACTION_KILL, 200, 1, 0.5f);
-
- recorder::expand(300, 600);
-
- REQUIRE(recorder::frames.size() == 6);
- REQUIRE(recorder::global.size() == 6);
- REQUIRE(recorder::frames.at(0) == 0);
- REQUIRE(recorder::frames.at(1) == 80);
- REQUIRE(recorder::frames.at(2) == 200);
- REQUIRE(recorder::frames.at(3) == 300);
- REQUIRE(recorder::frames.at(4) == 380);
- REQUIRE(recorder::frames.at(5) == 500);
- }
-
- SECTION("Test shrink")
- {
- recorder::rec(0, G_ACTION_KEYPRESS, 0, 1, 0.5f);
- recorder::rec(0, G_ACTION_KEYREL, 80, 1, 0.5f);
- recorder::rec(0, G_ACTION_KILL, 200, 1, 0.5f);
-
- recorder::shrink(100);
-
- REQUIRE(recorder::frames.size() == 2);
- REQUIRE(recorder::global.size() == 2);
- REQUIRE(recorder::frames.at(0) == 0);
- REQUIRE(recorder::frames.at(1) == 80);
- }
-
- SECTION("Test overdub, full overwrite")
- {
- recorder::rec(0, G_ACTION_MUTEON, 0, 1, 0.5f);
- recorder::rec(0, G_ACTION_MUTEOFF, 80, 1, 0.5f);
- recorder::rec(0, G_ACTION_MUTEON, 200, 1, 0.5f);
- recorder::rec(0, G_ACTION_MUTEOFF, 400, 1, 0.5f);
-
- /* Should delete all actions in between and keep the first one, plus a
- new last action on frame 500. */
- recorder::startOverdub(0, G_ACTION_MUTEON | G_ACTION_MUTEOFF, 0, 1024);
- recorder::stopOverdub(500, 500, &mutex);
-
- REQUIRE(recorder::frames.size() == 2);
- REQUIRE(recorder::global.size() == 2);
- REQUIRE(recorder::frames.at(0) == 0);
- REQUIRE(recorder::frames.at(1) == 500);
- REQUIRE(recorder::global.at(0).at(0)->frame == 0);
- REQUIRE(recorder::global.at(0).at(0)->type == G_ACTION_MUTEON);
- REQUIRE(recorder::global.at(1).at(0)->frame == 500);
- REQUIRE(recorder::global.at(1).at(0)->type == G_ACTION_MUTEOFF);
- }
-
- SECTION("Test overdub, left overlap")
- {
- recorder::rec(0, G_ACTION_MUTEON, 100, 1, 0.5f);
- recorder::rec(0, G_ACTION_MUTEOFF, 400, 1, 0.5f);
-
- /* Overdub part of the leftmost part of a composite action. Expected result:
- a new composite action.
- Original: ----|########|
- Overdub: |#######|-----
- Result: |#######|----- */
- recorder::startOverdub(0, G_ACTION_MUTEON | G_ACTION_MUTEOFF, 0, 16);
- recorder::stopOverdub(300, 500, &mutex);
-
- REQUIRE(recorder::frames.size() == 2);
- REQUIRE(recorder::global.size() == 2);
- REQUIRE(recorder::frames.at(0) == 0);
- REQUIRE(recorder::frames.at(1) == 300);
-
- REQUIRE(recorder::global.at(0).at(0)->frame == 0);
- REQUIRE(recorder::global.at(0).at(0)->type == G_ACTION_MUTEON);
- REQUIRE(recorder::global.at(1).at(0)->frame == 300);
- REQUIRE(recorder::global.at(1).at(0)->type == G_ACTION_MUTEOFF);
- }
-
- SECTION("Test overdub, right overlap")
- {
- recorder::rec(0, G_ACTION_MUTEON, 000, 1, 0.5f);
- recorder::rec(0, G_ACTION_MUTEOFF, 400, 1, 0.5f);
-
- /* Overdub part of the rightmost part of a composite action. Expected result:
- a new composite action.
- Original: |########|------
- Overdub: -----|#######|--
- Result: |###||#######|-- */
- recorder::startOverdub(0, G_ACTION_MUTEON | G_ACTION_MUTEOFF, 100, 16);
- recorder::stopOverdub(500, 500, &mutex);
-
- REQUIRE(recorder::frames.size() == 4);
- REQUIRE(recorder::global.size() == 4);
- REQUIRE(recorder::frames.at(0) == 0);
- REQUIRE(recorder::frames.at(1) == 84); // 100 - bufferSize (16)
- REQUIRE(recorder::frames.at(2) == 100);
- REQUIRE(recorder::frames.at(3) == 500);
-
- REQUIRE(recorder::global.at(0).at(0)->frame == 0);
- REQUIRE(recorder::global.at(0).at(0)->type == G_ACTION_MUTEON);
- REQUIRE(recorder::global.at(1).at(0)->frame == 84);
- REQUIRE(recorder::global.at(1).at(0)->type == G_ACTION_MUTEOFF);
-
- REQUIRE(recorder::global.at(2).at(0)->frame == 100);
- REQUIRE(recorder::global.at(2).at(0)->type == G_ACTION_MUTEON);
- REQUIRE(recorder::global.at(3).at(0)->frame == 500);
- REQUIRE(recorder::global.at(3).at(0)->type == G_ACTION_MUTEOFF);
- }
-
- SECTION("Test overdub, hole diggin'")
- {
- recorder::rec(0, G_ACTION_MUTEON, 0, 1, 0.5f);
- recorder::rec(0, G_ACTION_MUTEOFF, 400, 1, 0.5f);
-
- /* Overdub in the middle of a long, composite action. Expected result:
- original action trimmed down plus anther action next to it. Total frames
- should be 4.
- Original: |#############|
- Overdub: ---|#######|---
- Result: |#||#######|--- */
- recorder::startOverdub(0, G_ACTION_MUTEON | G_ACTION_MUTEOFF, 100, 16);
- recorder::stopOverdub(300, 500, &mutex);
-
- REQUIRE(recorder::frames.size() == 4);
- REQUIRE(recorder::global.size() == 4);
- REQUIRE(recorder::frames.at(0) == 0);
- REQUIRE(recorder::frames.at(1) == 84); // 100 - bufferSize (16)
- REQUIRE(recorder::frames.at(2) == 100);
- REQUIRE(recorder::frames.at(3) == 300);
-
- REQUIRE(recorder::global.at(0).at(0)->frame == 0);
- REQUIRE(recorder::global.at(0).at(0)->type == G_ACTION_MUTEON);
- REQUIRE(recorder::global.at(1).at(0)->frame == 84);
- REQUIRE(recorder::global.at(1).at(0)->type == G_ACTION_MUTEOFF);
-
- REQUIRE(recorder::global.at(2).at(0)->frame == 100);
- REQUIRE(recorder::global.at(2).at(0)->type == G_ACTION_MUTEON);
- REQUIRE(recorder::global.at(3).at(0)->frame == 300);
- REQUIRE(recorder::global.at(3).at(0)->type == G_ACTION_MUTEOFF);
- }
-
- SECTION("Test overdub, cover all")
- {
- recorder::rec(0, G_ACTION_MUTEON, 0, 1, 0.5f);
- recorder::rec(0, G_ACTION_MUTEOFF, 100, 1, 0.5f);
- recorder::rec(0, G_ACTION_MUTEON, 120, 1, 0.5f);
- recorder::rec(0, G_ACTION_MUTEOFF, 200, 1, 0.5f);
- recorder::rec(0, G_ACTION_MUTEON, 220, 1, 0.5f);
- recorder::rec(0, G_ACTION_MUTEOFF, 300, 1, 0.5f);
-
- /* Overdub all existing actions. Expected result: a single composite one. */
- recorder::startOverdub(0, G_ACTION_MUTEON | G_ACTION_MUTEOFF, 0, 16);
- recorder::stopOverdub(500, 500, &mutex);
-
- REQUIRE(recorder::frames.size() == 2);
- REQUIRE(recorder::global.size() == 2);
- REQUIRE(recorder::frames.at(0) == 0);
- REQUIRE(recorder::frames.at(1) == 500);
-
- REQUIRE(recorder::global.at(0).at(0)->frame == 0);
- REQUIRE(recorder::global.at(0).at(0)->type == G_ACTION_MUTEON);
- REQUIRE(recorder::global.at(1).at(0)->frame == 500);
- REQUIRE(recorder::global.at(1).at(0)->type == G_ACTION_MUTEOFF);
- }
-
- SECTION("Test overdub, null loop")
- {
- recorder::rec(0, G_ACTION_MUTEON, 0, 1, 0.5f);
- recorder::rec(0, G_ACTION_MUTEOFF, 500, 1, 0.5f);
-
- /* A null loop is a loop that begins and ends on the very same frame. */
- recorder::startOverdub(0, G_ACTION_MUTEON | G_ACTION_MUTEOFF, 300, 16);
- recorder::stopOverdub(300, 700, &mutex);
-
- REQUIRE(recorder::frames.size() == 2);
- REQUIRE(recorder::frames.at(0) == 0);
- REQUIRE(recorder::frames.at(1) == 284); // 300 - bufferSize (16)
-
- REQUIRE(recorder::global.at(0).at(0)->frame == 0);
- REQUIRE(recorder::global.at(0).at(0)->type == G_ACTION_MUTEON);
- REQUIRE(recorder::global.at(1).at(0)->frame == 284);
- REQUIRE(recorder::global.at(1).at(0)->type == G_ACTION_MUTEOFF);
- }
-
- SECTION("Test overdub, ring loop")
- {
- /* A ring loop occurs when you record the last action beyond the end of
- the sequencer.
- Original: ---|#######|---
- Overdub: #####|------|##
- Result: ---|#######||#| */
-
- recorder::rec(0, G_ACTION_MUTEON, 200, 1, 0.5f);
- recorder::rec(0, G_ACTION_MUTEOFF, 300, 1, 0.5f);
-
- recorder::startOverdub(0, G_ACTION_MUTEON | G_ACTION_MUTEOFF, 400, 16);
- recorder::stopOverdub(250, 700, &mutex);
-
- REQUIRE(recorder::frames.size() == 4);
- REQUIRE(recorder::frames.at(0) == 200);
- REQUIRE(recorder::frames.at(1) == 300);
- REQUIRE(recorder::frames.at(2) == 400);
- REQUIRE(recorder::frames.at(3) == 700);
- }
+ /* Each SECTION the TEST_CASE is executed from the start. The following
+ code is exectuted before each SECTION. */
+
+ pthread_mutex_t mutex;
+ pthread_mutex_init(&mutex, nullptr);
+
+ recorder::init();
+ REQUIRE(recorder::frames.size() == 0);
+ REQUIRE(recorder::global.size() == 0);
+
+ SECTION("Test record single action")
+ {
+ recorder::rec(0, G_ACTION_KEYPRESS, 50, 1, 0.5f);
+
+ REQUIRE(recorder::frames.size() == 1);
+ REQUIRE(recorder::frames.at(0) == 50);
+ REQUIRE(recorder::global.at(0).size() == 1); // 1 action on frame #0
+ REQUIRE(recorder::global.at(0).at(0)->chan == 0);
+ REQUIRE(recorder::global.at(0).at(0)->type == G_ACTION_KEYPRESS);
+ REQUIRE(recorder::global.at(0).at(0)->frame == 50);
+ REQUIRE(recorder::global.at(0).at(0)->iValue == 1);
+ REQUIRE(recorder::global.at(0).at(0)->fValue == 0.5f);
+ }
+
+ SECTION("Test record, two actions on same frame")
+ {
+ recorder::rec(0, G_ACTION_KEYPRESS, 50, 6, 0.3f);
+ recorder::rec(0, G_ACTION_KEYREL, 50, 1, 0.5f);
+
+ REQUIRE(recorder::frames.size() == 1); // same frame, frames.size must stay 1
+ REQUIRE(recorder::frames.at(0) == 50);
+ REQUIRE(recorder::global.at(0).size() == 2); // 2 actions on frame #0
+
+ REQUIRE(recorder::global.at(0).at(0)->chan == 0);
+ REQUIRE(recorder::global.at(0).at(0)->type == G_ACTION_KEYPRESS);
+ REQUIRE(recorder::global.at(0).at(0)->frame == 50);
+ REQUIRE(recorder::global.at(0).at(0)->iValue == 6);
+ REQUIRE(recorder::global.at(0).at(0)->fValue == 0.3f);
+
+ REQUIRE(recorder::global.at(0).at(1)->chan == 0);
+ REQUIRE(recorder::global.at(0).at(1)->type == G_ACTION_KEYREL);
+ REQUIRE(recorder::global.at(0).at(1)->frame == 50);
+ REQUIRE(recorder::global.at(0).at(1)->iValue == 1);
+ REQUIRE(recorder::global.at(0).at(1)->fValue == 0.5f);
+
+ SECTION("Test record, another action on a different frame")
+ {
+ recorder::rec(0, G_ACTION_KEYPRESS, 70, 1, 0.5f);
+
+ REQUIRE(recorder::frames.size() == 2);
+ REQUIRE(recorder::frames.at(1) == 70);
+ REQUIRE(recorder::global.at(0).size() == 2); // 2 actions on frame #0
+ REQUIRE(recorder::global.at(1).size() == 1); // 1 actions on frame #1
+ REQUIRE(recorder::global.at(1).at(0)->chan == 0);
+ REQUIRE(recorder::global.at(1).at(0)->type == G_ACTION_KEYPRESS);
+ REQUIRE(recorder::global.at(1).at(0)->frame == 70);
+ REQUIRE(recorder::global.at(1).at(0)->iValue == 1);
+ REQUIRE(recorder::global.at(1).at(0)->fValue == 0.5f);
+ }
+ }
+
+ SECTION("Test retrieval")
+ {
+ recorder::rec(0, G_ACTION_KEYPRESS, 50, 6, 0.3f);
+ recorder::rec(0, G_ACTION_KEYREL, 70, 1, 0.5f);
+ recorder::rec(1, G_ACTION_KEYPRESS, 50, 6, 0.3f);
+ recorder::rec(1, G_ACTION_KEYREL, 70, 1, 0.5f);
+ recorder::rec(2, G_ACTION_KEYPRESS, 100, 6, 0.3f);
+ recorder::rec(2, G_ACTION_KEYREL, 120, 1, 0.5f);
+
+ /* Give me action on chan 1, type G_ACTION_KEYREL, frame 70. */
+ recorder::action *action = nullptr;
+ REQUIRE(recorder::getAction(1, G_ACTION_KEYREL, 70, &action) == 1);
+
+ REQUIRE(action != nullptr);
+ REQUIRE(action->chan == 1);
+ REQUIRE(action->type == G_ACTION_KEYREL);
+ REQUIRE(action->frame == 70);
+ REQUIRE(action->iValue == 1);
+ REQUIRE(action->fValue == 0.5f);
+
+ /* Give me *next* action on chan 0, type G_ACTION_KEYREL, starting from frame 20.
+ Must be action #2 */
+
+ REQUIRE(recorder::getNextAction(0, G_ACTION_KEYREL, 20, &action) == 1);
+ REQUIRE(action != nullptr);
+ REQUIRE(action->chan == 0);
+ REQUIRE(action->type == G_ACTION_KEYREL);
+ REQUIRE(action->frame == 70);
+
+ /* Give me *next* action on chan 2, type G_ACTION_KEYPRESS, starting from
+ frame 200. You are requesting frame outside boundaries. */
+
+ REQUIRE(recorder::getNextAction(2, G_ACTION_KEYPRESS, 200, &action) == -1);
+
+ /* Give me *next* action on chan 2, type G_ACTION_KEYPRESS, starting from
+ frame 100. That action does not exist. */
+
+ REQUIRE(recorder::getNextAction(2, G_ACTION_KEYPRESS, 100, &action) == -2);
+ }
+
+ SECTION("Test retrieval MIDI")
+ {
+ recorder::rec(0, G_ACTION_MIDI, 0, 0x903C3F00, 0.0f);
+ recorder::rec(1, G_ACTION_MIDI, 0, 0x903D3F00, 0.0f);
+ recorder::rec(0, G_ACTION_MIDI, 1000, 0x803C2000, 0.0f);
+ recorder::rec(0, G_ACTION_MIDI, 1050, 0x903C3F00, 0.0f);
+ recorder::rec(0, G_ACTION_MIDI, 2000, 0x803C3F00, 0.0f);
+ recorder::rec(1, G_ACTION_MIDI, 90, 0x803D3F00, 0.0f);
+ recorder::rec(1, G_ACTION_MIDI, 1050, 0x903D3F00, 0.0f);
+ recorder::rec(1, G_ACTION_MIDI, 2000, 0x803D3F00, 0.0f);
+
+ recorder::action* result = nullptr;
+ recorder::getNextAction(0, G_ACTION_MIDI, 100, &result, 0x803CFF00, 0x0000FF00);
+
+ REQUIRE(result != nullptr);
+ REQUIRE(result->frame == 1000);
+ }
+
+ SECTION("Test deletion, single action")
+ {
+ recorder::rec(0, G_ACTION_KEYPRESS, 50, 6, 0.3f);
+ recorder::rec(0, G_ACTION_KEYREL, 60, 1, 0.5f);
+ recorder::rec(1, G_ACTION_KEYPRESS, 70, 6, 0.3f);
+ recorder::rec(1, G_ACTION_KEYREL, 80, 1, 0.5f);
+
+ /* Delete action #0, don't check values. */
+ recorder::deleteAction(0, 50, G_ACTION_KEYPRESS, false, &mutex);
+
+ REQUIRE(recorder::frames.size() == 3);
+ REQUIRE(recorder::global.size() == 3);
+
+ SECTION("Test deletion checked")
+ {
+ /* Delete action #1, check values. */
+ recorder::deleteAction(1, 70, G_ACTION_KEYPRESS, true, &mutex, 6, 0.3f);
+
+ REQUIRE(recorder::frames.size() == 2);
+ REQUIRE(recorder::global.size() == 2);
+ }
+ }
+
+ SECTION("Test deletion, range of actions")
+ {
+ recorder::rec(0, G_ACTION_KEYPRESS, 50, 6, 0.3f);
+ recorder::rec(0, G_ACTION_KEYREL, 60, 1, 0.5f);
+ recorder::rec(0, G_ACTION_KEYPRESS, 70, 6, 0.3f);
+ recorder::rec(0, G_ACTION_KEYREL, 80, 1, 0.5f);
+ recorder::rec(1, G_ACTION_KEYPRESS, 100, 6, 0.3f);
+ recorder::rec(1, G_ACTION_KEYREL, 120, 1, 0.5f);
+
+ /* Delete any action on channel 0 of types KEYPRESS | KEYREL between
+ frames 0 and 200. */
+
+ recorder::deleteActions(0, 0, 200, G_ACTION_KEYPRESS | G_ACTION_KEYREL, &mutex);
+
+ REQUIRE(recorder::frames.size() == 2);
+ REQUIRE(recorder::global.size() == 2);
+ REQUIRE(recorder::global.at(0).size() == 1);
+ REQUIRE(recorder::global.at(1).size() == 1);
+
+ REQUIRE(recorder::global.at(0).at(0)->chan == 1);
+ REQUIRE(recorder::global.at(0).at(0)->type == G_ACTION_KEYPRESS);
+ REQUIRE(recorder::global.at(0).at(0)->frame == 100);
+ REQUIRE(recorder::global.at(0).at(0)->iValue == 6);
+ REQUIRE(recorder::global.at(0).at(0)->fValue == 0.3f);
+
+ REQUIRE(recorder::global.at(1).at(0)->chan == 1);
+ REQUIRE(recorder::global.at(1).at(0)->type == G_ACTION_KEYREL);
+ REQUIRE(recorder::global.at(1).at(0)->frame == 120);
+ REQUIRE(recorder::global.at(1).at(0)->iValue == 1);
+ REQUIRE(recorder::global.at(1).at(0)->fValue == 0.5f);
+ }
+
+ SECTION("Test action presence")
+ {
+ recorder::rec(0, G_ACTION_KEYREL, 80, 1, 0.5f);
+ recorder::rec(1, G_ACTION_KEYPRESS, 100, 6, 0.3f);
+ recorder::rec(1, G_ACTION_KEYREL, 120, 1, 0.5f);
+
+ recorder::deleteAction(0, 80, G_ACTION_KEYREL, false, &mutex);
+
+ REQUIRE(recorder::hasActions(0) == false);
+ REQUIRE(recorder::hasActions(1) == true);
+ }
+
+ SECTION("Test clear actions by channel")
+ {
+ recorder::rec(0, G_ACTION_KEYREL, 80, 1, 0.5f);
+ recorder::rec(1, G_ACTION_KEYPRESS, 100, 6, 0.3f);
+ recorder::rec(1, G_ACTION_KEYREL, 120, 1, 0.5f);
+
+ recorder::clearChan(1);
+
+ REQUIRE(recorder::hasActions(0) == true);
+ REQUIRE(recorder::hasActions(1) == false);
+ REQUIRE(recorder::frames.size() == 1);
+ REQUIRE(recorder::global.size() == 1);
+ REQUIRE(recorder::global.at(0).size() == 1);
+ }
+
+ SECTION("Test clear actions by type")
+ {
+ recorder::rec(1, G_ACTION_KEYREL, 80, 1, 0.5f);
+ recorder::rec(0, G_ACTION_KEYPRESS, 100, 6, 0.3f);
+ recorder::rec(1, G_ACTION_KEYREL, 120, 1, 0.5f);
+
+ /* Clear all actions of type KEYREL from channel 1. */
+
+ recorder::clearAction(1, G_ACTION_KEYREL);
+
+ REQUIRE(recorder::hasActions(0) == true);
+ REQUIRE(recorder::hasActions(1) == false);
+ REQUIRE(recorder::frames.size() == 1);
+ REQUIRE(recorder::global.size() == 1);
+ REQUIRE(recorder::global.at(0).size() == 1);
+ }
+
+ SECTION("Test clear all")
+ {
+ recorder::rec(0, G_ACTION_KEYPRESS, 0, 1, 0.5f);
+ recorder::rec(1, G_ACTION_KEYPRESS, 0, 1, 0.5f);
+ recorder::rec(0, G_ACTION_KEYREL, 80, 1, 0.5f);
+ recorder::rec(1, G_ACTION_KEYREL, 100, 6, 0.3f);
+ recorder::rec(2, G_ACTION_KILL, 120, 1, 0.5f);
+
+ recorder::clearAll();
+ REQUIRE(recorder::frames.size() == 0);
+ REQUIRE(recorder::global.size() == 0);
+ }
+
+ SECTION("Test optimization")
+ {
+ recorder::rec(0, G_ACTION_KEYPRESS, 20, 1, 0.5f);
+ recorder::rec(0, G_ACTION_KEYREL, 80, 1, 0.5f);
+ recorder::rec(1, G_ACTION_KEYPRESS, 20, 1, 0.5f);
+ recorder::rec(1, G_ACTION_KEYREL, 80, 1, 0.5f);
+
+ /* Fake frame 80 without actions.*/
+ recorder::global.at(1).clear();
+
+ recorder::optimize();
+
+ REQUIRE(recorder::frames.size() == 1);
+ REQUIRE(recorder::global.size() == 1);
+ REQUIRE(recorder::global.at(0).size() == 2);
+ }
+
+ SECTION("Test BPM update")
+ {
+ recorder::rec(0, G_ACTION_KEYPRESS, 0, 1, 0.5f);
+ recorder::rec(0, G_ACTION_KEYREL, 80, 1, 0.5f);
+
+ recorder::updateBpm(60.0f, 120.0f, 44100); // scaling up
+
+ REQUIRE(recorder::frames.at(0) == 0);
+ REQUIRE(recorder::frames.at(1) == 40);
+
+ recorder::updateBpm(120.0f, 60.0f, 44100); // scaling down
+
+ REQUIRE(recorder::frames.at(0) == 0);
+ REQUIRE(recorder::frames.at(1) == 80);
+ }
+
+ SECTION("Test samplerate update")
+ {
+ recorder::rec(0, G_ACTION_KEYPRESS, 0, 1, 0.5f);
+ recorder::rec(0, G_ACTION_KEYREL, 80, 1, 0.5f);
+ recorder::rec(0, G_ACTION_KEYPRESS, 120, 1, 0.5f);
+ recorder::rec(0, G_ACTION_KEYREL, 150, 1, 0.5f);
+
+ recorder::updateSamplerate(44100, 22050); // scaling down
+
+ REQUIRE(recorder::frames.at(0) == 0);
+ REQUIRE(recorder::frames.at(1) == 160);
+ REQUIRE(recorder::frames.at(2) == 240);
+ REQUIRE(recorder::frames.at(3) == 300);
+
+ recorder::updateSamplerate(22050, 44100); // scaling up
+
+ REQUIRE(recorder::frames.at(0) == 0);
+ REQUIRE(recorder::frames.at(1) == 80);
+ REQUIRE(recorder::frames.at(2) == 120);
+ REQUIRE(recorder::frames.at(3) == 150);
+ }
+
+ SECTION("Test expand")
+ {
+ recorder::rec(0, G_ACTION_KEYPRESS, 0, 1, 0.5f);
+ recorder::rec(0, G_ACTION_KEYREL, 80, 1, 0.5f);
+ recorder::rec(0, G_ACTION_KILL, 200, 1, 0.5f);
+
+ recorder::expand(300, 600);
+
+ REQUIRE(recorder::frames.size() == 6);
+ REQUIRE(recorder::global.size() == 6);
+ REQUIRE(recorder::frames.at(0) == 0);
+ REQUIRE(recorder::frames.at(1) == 80);
+ REQUIRE(recorder::frames.at(2) == 200);
+ REQUIRE(recorder::frames.at(3) == 300);
+ REQUIRE(recorder::frames.at(4) == 380);
+ REQUIRE(recorder::frames.at(5) == 500);
+ }
+
+ SECTION("Test shrink")
+ {
+ recorder::rec(0, G_ACTION_KEYPRESS, 0, 1, 0.5f);
+ recorder::rec(0, G_ACTION_KEYREL, 80, 1, 0.5f);
+ recorder::rec(0, G_ACTION_KILL, 200, 1, 0.5f);
+
+ recorder::shrink(100);
+
+ REQUIRE(recorder::frames.size() == 2);
+ REQUIRE(recorder::global.size() == 2);
+ REQUIRE(recorder::frames.at(0) == 0);
+ REQUIRE(recorder::frames.at(1) == 80);
+ }
+
+ SECTION("Test overdub, full overwrite")
+ {
+ recorder::rec(0, G_ACTION_MUTEON, 0, 1, 0.5f);
+ recorder::rec(0, G_ACTION_MUTEOFF, 80, 1, 0.5f);
+ recorder::rec(0, G_ACTION_MUTEON, 200, 1, 0.5f);
+ recorder::rec(0, G_ACTION_MUTEOFF, 400, 1, 0.5f);
+
+ /* Should delete all actions in between and keep the first one, plus a
+ new last action on frame 500. */
+ recorder::startOverdub(0, G_ACTION_MUTEON | G_ACTION_MUTEOFF, 0, 1024);
+ recorder::stopOverdub(500, 500, &mutex);
+
+ REQUIRE(recorder::frames.size() == 2);
+ REQUIRE(recorder::global.size() == 2);
+ REQUIRE(recorder::frames.at(0) == 0);
+ REQUIRE(recorder::frames.at(1) == 500);
+ REQUIRE(recorder::global.at(0).at(0)->frame == 0);
+ REQUIRE(recorder::global.at(0).at(0)->type == G_ACTION_MUTEON);
+ REQUIRE(recorder::global.at(1).at(0)->frame == 500);
+ REQUIRE(recorder::global.at(1).at(0)->type == G_ACTION_MUTEOFF);
+ }
+
+ SECTION("Test overdub, left overlap")
+ {
+ recorder::rec(0, G_ACTION_MUTEON, 100, 1, 0.5f);
+ recorder::rec(0, G_ACTION_MUTEOFF, 400, 1, 0.5f);
+
+ /* Overdub part of the leftmost part of a composite action. Expected result:
+ a new composite action.
+ Original: ----|########|
+ Overdub: |#######|-----
+ Result: |#######|----- */
+ recorder::startOverdub(0, G_ACTION_MUTEON | G_ACTION_MUTEOFF, 0, 16);
+ recorder::stopOverdub(300, 500, &mutex);
+
+ REQUIRE(recorder::frames.size() == 2);
+ REQUIRE(recorder::global.size() == 2);
+ REQUIRE(recorder::frames.at(0) == 0);
+ REQUIRE(recorder::frames.at(1) == 300);
+
+ REQUIRE(recorder::global.at(0).at(0)->frame == 0);
+ REQUIRE(recorder::global.at(0).at(0)->type == G_ACTION_MUTEON);
+ REQUIRE(recorder::global.at(1).at(0)->frame == 300);
+ REQUIRE(recorder::global.at(1).at(0)->type == G_ACTION_MUTEOFF);
+ }
+
+ SECTION("Test overdub, right overlap")
+ {
+ recorder::rec(0, G_ACTION_MUTEON, 000, 1, 0.5f);
+ recorder::rec(0, G_ACTION_MUTEOFF, 400, 1, 0.5f);
+
+ /* Overdub part of the rightmost part of a composite action. Expected result:
+ a new composite action.
+ Original: |########|------
+ Overdub: -----|#######|--
+ Result: |###||#######|-- */
+ recorder::startOverdub(0, G_ACTION_MUTEON | G_ACTION_MUTEOFF, 100, 16);
+ recorder::stopOverdub(500, 500, &mutex);
+
+ REQUIRE(recorder::frames.size() == 4);
+ REQUIRE(recorder::global.size() == 4);
+ REQUIRE(recorder::frames.at(0) == 0);
+ REQUIRE(recorder::frames.at(1) == 84); // 100 - bufferSize (16)
+ REQUIRE(recorder::frames.at(2) == 100);
+ REQUIRE(recorder::frames.at(3) == 500);
+
+ REQUIRE(recorder::global.at(0).at(0)->frame == 0);
+ REQUIRE(recorder::global.at(0).at(0)->type == G_ACTION_MUTEON);
+ REQUIRE(recorder::global.at(1).at(0)->frame == 84);
+ REQUIRE(recorder::global.at(1).at(0)->type == G_ACTION_MUTEOFF);
+
+ REQUIRE(recorder::global.at(2).at(0)->frame == 100);
+ REQUIRE(recorder::global.at(2).at(0)->type == G_ACTION_MUTEON);
+ REQUIRE(recorder::global.at(3).at(0)->frame == 500);
+ REQUIRE(recorder::global.at(3).at(0)->type == G_ACTION_MUTEOFF);
+ }
+
+ SECTION("Test overdub, hole diggin'")
+ {
+ recorder::rec(0, G_ACTION_MUTEON, 0, 1, 0.5f);
+ recorder::rec(0, G_ACTION_MUTEOFF, 400, 1, 0.5f);
+
+ /* Overdub in the middle of a long, composite action. Expected result:
+ original action trimmed down plus anther action next to it. Total frames
+ should be 4.
+ Original: |#############|
+ Overdub: ---|#######|---
+ Result: |#||#######|--- */
+ recorder::startOverdub(0, G_ACTION_MUTEON | G_ACTION_MUTEOFF, 100, 16);
+ recorder::stopOverdub(300, 500, &mutex);
+
+ REQUIRE(recorder::frames.size() == 4);
+ REQUIRE(recorder::global.size() == 4);
+ REQUIRE(recorder::frames.at(0) == 0);
+ REQUIRE(recorder::frames.at(1) == 84); // 100 - bufferSize (16)
+ REQUIRE(recorder::frames.at(2) == 100);
+ REQUIRE(recorder::frames.at(3) == 300);
+
+ REQUIRE(recorder::global.at(0).at(0)->frame == 0);
+ REQUIRE(recorder::global.at(0).at(0)->type == G_ACTION_MUTEON);
+ REQUIRE(recorder::global.at(1).at(0)->frame == 84);
+ REQUIRE(recorder::global.at(1).at(0)->type == G_ACTION_MUTEOFF);
+
+ REQUIRE(recorder::global.at(2).at(0)->frame == 100);
+ REQUIRE(recorder::global.at(2).at(0)->type == G_ACTION_MUTEON);
+ REQUIRE(recorder::global.at(3).at(0)->frame == 300);
+ REQUIRE(recorder::global.at(3).at(0)->type == G_ACTION_MUTEOFF);
+ }
+
+ SECTION("Test overdub, cover all")
+ {
+ recorder::rec(0, G_ACTION_MUTEON, 0, 1, 0.5f);
+ recorder::rec(0, G_ACTION_MUTEOFF, 100, 1, 0.5f);
+ recorder::rec(0, G_ACTION_MUTEON, 120, 1, 0.5f);
+ recorder::rec(0, G_ACTION_MUTEOFF, 200, 1, 0.5f);
+ recorder::rec(0, G_ACTION_MUTEON, 220, 1, 0.5f);
+ recorder::rec(0, G_ACTION_MUTEOFF, 300, 1, 0.5f);
+
+ /* Overdub all existing actions. Expected result: a single composite one. */
+ recorder::startOverdub(0, G_ACTION_MUTEON | G_ACTION_MUTEOFF, 0, 16);
+ recorder::stopOverdub(500, 500, &mutex);
+
+ REQUIRE(recorder::frames.size() == 2);
+ REQUIRE(recorder::global.size() == 2);
+ REQUIRE(recorder::frames.at(0) == 0);
+ REQUIRE(recorder::frames.at(1) == 500);
+
+ REQUIRE(recorder::global.at(0).at(0)->frame == 0);
+ REQUIRE(recorder::global.at(0).at(0)->type == G_ACTION_MUTEON);
+ REQUIRE(recorder::global.at(1).at(0)->frame == 500);
+ REQUIRE(recorder::global.at(1).at(0)->type == G_ACTION_MUTEOFF);
+ }
+
+ SECTION("Test overdub, null loop")
+ {
+ recorder::rec(0, G_ACTION_MUTEON, 0, 1, 0.5f);
+ recorder::rec(0, G_ACTION_MUTEOFF, 500, 1, 0.5f);
+
+ /* A null loop is a loop that begins and ends on the very same frame. */
+ recorder::startOverdub(0, G_ACTION_MUTEON | G_ACTION_MUTEOFF, 300, 16);
+ recorder::stopOverdub(300, 700, &mutex);
+
+ REQUIRE(recorder::frames.size() == 2);
+ REQUIRE(recorder::frames.at(0) == 0);
+ REQUIRE(recorder::frames.at(1) == 284); // 300 - bufferSize (16)
+
+ REQUIRE(recorder::global.at(0).at(0)->frame == 0);
+ REQUIRE(recorder::global.at(0).at(0)->type == G_ACTION_MUTEON);
+ REQUIRE(recorder::global.at(1).at(0)->frame == 284);
+ REQUIRE(recorder::global.at(1).at(0)->type == G_ACTION_MUTEOFF);
+ }
+
+ SECTION("Test overdub, ring loop")
+ {
+ /* A ring loop occurs when you record the last action beyond the end of
+ the sequencer.
+ Original: ---|#######|---
+ Overdub: #####|------|##
+ Result: ---|#######||#| */
+
+ recorder::rec(0, G_ACTION_MUTEON, 200, 1, 0.5f);
+ recorder::rec(0, G_ACTION_MUTEOFF, 300, 1, 0.5f);
+
+ recorder::startOverdub(0, G_ACTION_MUTEON | G_ACTION_MUTEOFF, 400, 16);
+ recorder::stopOverdub(250, 700, &mutex);
+
+ REQUIRE(recorder::frames.size() == 4);
+ REQUIRE(recorder::frames.at(0) == 200);
+ REQUIRE(recorder::frames.at(1) == 300);
+ REQUIRE(recorder::frames.at(2) == 400);
+ REQUIRE(recorder::frames.at(3) == 700);
+ }
}
{
REQUIRE(gu_replace("Giada is cool", "cool", "hot") == "Giada is hot");
REQUIRE(gu_trim(" Giada is cool ") == "Giada is cool");
- REQUIRE(gu_toString(666) == "666");
+ REQUIRE(gu_iToString(666) == "666");
+ REQUIRE(gu_iToString(0x99AABB, true) == "99AABB");
+ REQUIRE(gu_fToString(3.14159, 2) == "3.14");
+ // Catch can't handle this so far?
+ //REQUIRE(gu_format("I see %d men with %s hats", 5, "strange") == "I see 5 men with strange hats");
vector<std::string> v;
gu_split("Giada is cool", " ", &v);