Projects
Multimedia
linuxsampler
Sign Up
Log In
Username
Password
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
Expand all
Collapse all
Changes of Revision 8
View file
linuxsampler.changes
Changed
@@ -1,4 +1,13 @@ ------------------------------------------------------------------- +Tue Feb 10 09:58:35 UTC 2015 - avvissu@yandex.ru + +- Update to svn trunk rev 2718 +- Remove linuxsampler-fix_lv2_include_path.patch: fixed in upstream +- Use pkgconfig in package names +- Fix wrong-file-end-of-line-encoding +- Add add new command line: lscp and ls_instr_script + +------------------------------------------------------------------- Mon Oct 20 02:16:49 UTC 2014 - avvissu@yandex.ru - Patch to fix build on ARM processors (add linuxsampler_arm.patch)
View file
linuxsampler.spec
Changed
@@ -17,8 +17,8 @@ %define soname 3 Name: linuxsampler -Summary: LinuxSampler - modular, streaming capable sampler -%define rev 2342 +Summary: Modular, streaming capable sampler +%define rev 2718 Version: 1.0.0+r%{rev} Release: 0.pm.1 License: GPL-2.0+ @@ -27,24 +27,26 @@ ## http://download.linuxsampler.org/packages/linuxsampler-%{version}.tar.bz2 Source0: linuxsampler-%{rev}.tar.bz2 Source1: linuxsampler.png -Patch1: linuxsampler-fix_lv2_include_path.patch #PATCH-FIX-UPSTREAM linuxsampler_arm.patchsf#32878699 <benoit.guchet@gmail.com> -- Fix build on ARM processors -Patch2: linuxsampler_arm.patch +Patch0: linuxsampler_arm.patch BuildRoot: %{_tmppath}/%{name}-%{version}-build -BuildRequires: alsa-devel >= 1.0.9 +BuildRequires: pkgconfig(alsa) >= 1.0.9 BuildRequires: libtool autoconf automake >= 1.5 BuildRequires: gcc-c++ BuildRequires: bison -BuildRequires: dssi-devel +BuildRequires: dos2unix +BuildRequires: pkgconfig(dssi) +BuildRequires: flex BuildRequires: ladspa-devel -BuildRequires: libgig-devel -BuildRequires: libjack-devel >= 0.103 +BuildRequires: pkgconfig(gig) >= 3.3.0+r%{rev} +BuildRequires: pkgconfig(jack) >= 0.103 +BuildRequires: perl-XML-Parser # It should be pkgconfig(lv2). But leave it like this for compatibility. # openSUSE 12.3 was the first version using lv2 instead of lv2core. BuildRequires: pkgconfig(lv2core) BuildRequires: pkgconfig -BuildRequires: sqlite3-devel -BuildRequires: libsndfile-devel >= 1.0.0 +BuildRequires: pkgconfig(sqlite3) +BuildRequires: pkgconfig(sndfile) >= 1.0.0 BuildRequires: doxygen BuildRequires: update-desktop-files Requires: alsa >= 1.0.9 @@ -126,8 +128,10 @@ %prep %setup -q -n linuxsampler -%patch1 -%patch2 -p1 +%patch0 -p1 + +#Fix wrong-file-end-of-line-encoding +dos2unix -k Documentation/lscp.xml %build -e ./configure || %__make -f Makefile.cvs configure @@ -139,6 +143,8 @@ --enable-plugin-dir="%{_prefix}/lib/%{name}/plugins" \ --enable-default-instruments-db-location="%{_var}/lib/%{name}/instruments.db" +make parser + %__make %{?_smp_flags} %install @@ -173,9 +179,7 @@ touch src/engines/InstrumentEditorFactory.h %__make docs -%__rm %{buildroot}%{_libdir}/%{name}/*.la -%__rm %{buildroot}%{_libdir}/dssi/*.la -%__rm %{buildroot}%{_libdir}/lv2/%{name}.lv2/%{name}.la +find %{buildroot} -type f -name "*.la" -delete -print %__rm Documentation/Engines/gig/Makefile* %__install -d "%{buildroot}%{_usr}/lib/%{name}/plugins" @@ -189,6 +193,8 @@ %files %defattr(-,root,root) %doc AUTHORS COPYING ChangeLog NEWS README +%{_bindir}/lscp +%{_bindir}/ls_instr_script %{_bindir}/%{name} %{_mandir}/man1/* %{_datadir}/pixmaps/%{name}.png @@ -220,11 +226,13 @@ %dir %{_libdir}/dssi %{_libdir}/dssi/%{name}.so +%if 0%{?suse_version} > 1140 %files -n lv2-linuxsampler %defattr(-,root,root) %dir %{_libdir}/lv2 %dir %{_libdir}/lv2/%{name}.lv2 %{_libdir}/lv2/%{name}.lv2/%{name}.so %{_libdir}/lv2/%{name}.lv2/*.ttl +%endif %changelog
View file
linuxsampler-fix_lv2_include_path.patch
Deleted
@@ -1,11 +0,0 @@ ---- src/hostplugins/lv2/lv2_state.h.orig 2012-04-16 18:31:29.532947979 +0200 -+++ src/hostplugins/lv2/lv2_state.h 2012-04-16 18:32:05.548946688 +0200 -@@ -26,7 +26,7 @@ - #include <stddef.h> - #include <stdint.h> - --#include "lv2/lv2plug.in/ns/lv2core/lv2.h" -+#include <lv2.h> - - #define LV2_STATE_URI "http://lv2plug.in/ns/ext/state" - #define LV2_STATE_PREFIX LV2_STATE_URI "#"
View file
linuxsampler-2342.tar.bz2/configure.in
Deleted
@@ -1,1435 +0,0 @@ -AC_INIT(configure.in) - -#------------------------------------------------------------------------------------ -# LinuxSampler's / liblinuxsampler's "official" release version: - -LINUXSAMPLER_RELEASE_MAJOR=1 -LINUXSAMPLER_RELEASE_MINOR=0 -LINUXSAMPLER_RELEASE_BUILD=0.svn18 - -#------------------------------------------------------------------------------------ -# The following is the libtool / shared library version. This doesn't have to -# do anything with the release version. It MUST conform to the following rules: -# -# 1. Start with version information of `0:0:0' for each libtool library. -# 2. Update the version information only immediately before a public release of -# your software. More frequent updates are unnecessary, and only guarantee -# that the current interface number gets larger faster. -# 3. If the library source code has changed at all since the last update, then -# increment revision (`c:r:a' becomes `c:r+1:a'). -# 4. If any interfaces have been added, removed, or changed since the last update, -# increment current, and set revision to 0. -# 5. If any interfaces have been added since the last public release, then increment -# age. -# 6. If any interfaces have been removed since the last public release, then set age -# to 0. - -LIBLINUXSAMPLER_LT_CURRENT=3 -LIBLINUXSAMPLER_LT_REVISION=0 -LIBLINUXSAMPLER_LT_AGE=0 -SHARED_VERSION_INFO="$LIBLINUXSAMPLER_LT_CURRENT:$LIBLINUXSAMPLER_LT_REVISION:$LIBLINUXSAMPLER_LT_AGE" - -#------------------------------------------------------------------------------------ -# the LSCP specification version this LinuSampler release complies with: - -LSCP_RELEASE_MAJOR=1 -LSCP_RELEASE_MINOR=5 - -AC_DEFINE_UNQUOTED(LSCP_RELEASE_MAJOR, ${LSCP_RELEASE_MAJOR}, LSCP spec major version this release complies with.) -AC_DEFINE_UNQUOTED(LSCP_RELEASE_MINOR, ${LSCP_RELEASE_MINOR}, LSCP spec minor version this release complies with.) - -AC_PROG_CXX -AC_LIBTOOL_WIN32_DLL -AM_PROG_LIBTOOL - -AC_SUBST(SHLIB_VERSION_ARG) -AC_SUBST(SHARED_VERSION_INFO) - -AC_C_BIGENDIAN -AC_CANONICAL_HOST - -PKG_PROG_PKG_CONFIG - -########################################################################### -# General Checks - -AM_CONDITIONAL(CROSS_COMPILING, test $cross_compiling = yes) - -AC_MSG_CHECKING(whether x86 architecture) -def_arch_x86=0 -case $host_cpu in - "i386" | "i486" | "i586" | "i686" | "i786" | "x86_64") - echo "yes" - def_arch_x86=1;; - *) - echo "no";; -esac -AC_DEFINE_UNQUOTED(ARCH_X86,$def_arch_x86,Define to 1 if you build for x86 architecture.) - -# determine the right gcc switch for CPU specific optimizations -# (only if the user did not provide one) -CXX_CPU_SWITCH= -if ! echo "X $CXXFLAGS " | grep -q -- " \(-march=\|-mcpu=\|-mtune=\|-arch=\)" ; then - if test "$def_arch_x86" = 1 -a "$host_cpu" != "x86_64"; then - CXX_CPU_SWITCH="-march=$host_cpu" - elif test "$target_cpu" = "ppc"; then - CXX_CPU_SWITCH="-arch=$host_cpu" - fi -fi -AC_SUBST(CXX_CPU_SWITCH) - -mac=no -linux=no -case "$host" in - *-*-darwin*) - mac=yes - ;; - *-*-linux*) - linux=yes - ;; -esac -AM_CONDITIONAL(LINUX, test "$linux" = yes) -AM_CONDITIONAL(MAC, test "$mac" = yes) - -# check if we're on MS Windows -AC_CHECK_HEADERS( - mmsystem.h, - have_windows=1, - have_windows=0, - #include <windef.h> -) -AM_CONDITIONAL(HAVE_WINDOWS, test $have_windows = "1") - -AC_MSG_CHECKING(whether UNIX98 compatible) -AC_LANG_SAVE -AC_LANG_C -AC_TRY_RUN( -#ifndef _GNU_SOURCE -#define _GNU_SOURCE 1 -#endif -#include <features.h> -void main(void) { -#if _XOPEN_SOURCE >= 500 -exit(0); /* UNIX98 compatible */ -#else -exit(-1); /* not UNIX98 compatible */ -#endif -} -, -have_unix98="yes", -have_unix98="no", -have_unix98="no" -) -AC_LANG_RESTORE -AC_MSG_RESULT($have_unix98) -if test "$have_unix98" = "no" -a "have_windows" = "0"; then - if test "x$HAVE_UNIX98" = "x"; then - echo "LinuxSampler only runs on UNIX98 compatible systems, which is mandatory for" - echo "pthread_mutexattr_settype() call in Mutex.cpp. You may want to run - echo "./configure with environment variable HAVE_UNIX98=1 in case you think you - echo "have a UNIX98 compatible system." - exit -1; - fi -fi - -# check for <features.h> -AC_CHECK_HEADERS(features.h) - -# test for POSIX thread library -m4_ifdef(m4_include(m4/pthread.m4),, - sinclude(m4/pthread.m4)) -ACX_PTHREAD -LIBS="$PTHREAD_LIBS $LIBS" -CFLAGS="$CFLAGS $PTHREAD_CFLAGS" -CXXFLAGS="$CXXFLAGS $PTHREAD_CFLAGS" -CC="$PTHREAD_CC" - -# check for a bug in NPTL-enabled glibc -# (see Gentoo bug report #194076) -AC_ARG_ENABLE(nptl-bug-check, - --disable-nptl-bug-check - Disable check for a bug in certain NPTL-enabled - glibc versions that caused crashs., - config_check_nptl_bug="$enableval", - config_check_nptl_bug="yes" -) -if test "$config_check_nptl_bug" = "yes"; then - m4_ifdef(m4_include(m4/nptl_bug.m4),, - sinclude(m4/nptl_bug.m4)) - ACX_NPTL_GLIBC_BUG( - echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" - echo "You seem to have a buggy PTHREAD library! LinuxSampler would" - echo "probably crash due to this. Please report us the system you are" - echo "using and/or file a bug report to the bug tracking system of" - echo "your distribution." - echo "If you have a NPTL-enabled glibc AND it was compiled for TLS as" - echo "well, you can try to circumvent this problem for now by setting" - echo "the environment variable LD_ASSUME_KERNEL=\"2.4.1\" , which" - echo "should cause this test to pass." - echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" - AC_MSG_ERROR(possibly NPTL glibc bug detected) - ) -else - echo "NPTL glibc bug check disabled" -fi - -# FIXME: this is actually a dependency of libgig, not of LS directly, why -# isn't it hidden by libgig? -AC_CHECK_HEADERS(uuid/uuid.h) -AC_SEARCH_LIBS(uuid_generate, uuid) - - - -########################################################################### -# Checks for available audio and MIDI systems / drivers -# (we throw an error if there's not at least one system for audio output and MIDI input available) - -have_midi_input_driver="false" -have_audio_output_driver="false" - -# ALSA -AC_ARG_ENABLE(alsa-driver, - --disable-alsa-driver - Disable support for the Advanced Linux Sound - Architecture (ALSA)., - config_alsa_driver="$enableval", - config_alsa_driver="yes" -) -have_alsa=0 -if test "$config_alsa_driver" = "yes"; then - AC_CHECK_HEADER(alsa/asoundlib.h, - AC_CHECK_LIB(asound, main, - have_alsa=1 - , - have_alsa=0 - ) - , - have_alsa=0 - ) - if test "$have_alsa" = "1"; then - have_midi_input_driver="true" - have_audio_output_driver="true"; - fi - - echo -n "checking Alsa version... " - AC_LANG_SAVE - AC_LANG_C - AC_TRY_RUN( - #include <alsa/asoundlib.h> - void main(void) { - /* ensure backward compatibility */ - #if !defined(SND_LIB_MAJOR) && defined(SOUNDLIB_VERSION_MAJOR) - #define SND_LIB_MAJOR SOUNDLIB_VERSION_MAJOR - #endif - exit(SND_LIB_MAJOR); - } - , - alsa_major=0, - alsa_major=$?, - alsa_major=0 - ) - AC_TRY_RUN( - #include <alsa/asoundlib.h> - void main(void) { - /* ensure backward compatibility */ - #if !defined(SND_LIB_MINOR) && defined(SOUNDLIB_VERSION_MINOR) - #define SND_LIB_MINOR SOUNDLIB_VERSION_MINOR - #endif - exit(SND_LIB_MINOR); - } - , - alsa_minor=0, - alsa_minor=$?, - alsa_minor=0 - ) - AC_TRY_RUN( - #include <alsa/asoundlib.h> - void main(void) { - /* ensure backward compatibility */ - #if !defined(SND_LIB_SUBMINOR) && defined(SOUNDLIB_VERSION_SUBMINOR) - #define SND_LIB_SUBMINOR SOUNDLIB_VERSION_SUBMINOR - #endif - exit(SND_LIB_SUBMINOR); - } - , - alsa_subminor=0, - alsa_subminor=$?, - alsa_subminor=0 - ) - AC_LANG_RESTORE - echo "$alsa_major.$alsa_minor.$alsa_subminor"; - AC_DEFINE_UNQUOTED(ALSA_MAJOR,$alsa_major,Define to the major version number of your Alsa installation.) - AC_DEFINE_UNQUOTED(ALSA_MINOR,$alsa_minor,Define to the minor version number of your Alsa installation.) - AC_DEFINE_UNQUOTED(ALSA_SUBMINOR,$alsa_subminor,Define to the subminor version number of your Alsa installation.) -else - echo "ALSA support disabled by configure script parameter" -fi -AM_CONDITIONAL(HAVE_ALSA, test $have_alsa = "1") -AC_DEFINE_UNQUOTED(HAVE_ALSA,$have_alsa,Define to 1 if you have ALSA installed.) -config_have_alsa="no" -if test $have_alsa = "1"; then - config_have_alsa="yes" -fi - -# JACK -AC_ARG_ENABLE(jack-driver, - --disable-jack-driver - Disable support for the Jack Audio Connection Kit - (JACK)., - config_jack_driver="$enableval", - config_jack_driver="yes" -) -have_jack=0 -if test "$config_jack_driver" = "yes"; then - PKG_CHECK_MODULES(JACK, jack, have_jack=1, have_jack=0) - if test $have_jack = "1"; then - AC_SUBST(JACK_LIBS) - AC_SUBST(JACK_CFLAGS) - linuxsampler_save_LIBS=$LIBS - LIBS="$JACK_LIBS $LIBS" - AC_CHECK_FUNCS(jack_client_name_size jack_client_open) - LIBS=$linuxsampler_save_LIBS - have_audio_output_driver="true"; - fi -else - echo "JACK support disabled by configure script parameter" -fi -AM_CONDITIONAL(HAVE_JACK, test $have_jack = "1") -AC_DEFINE_UNQUOTED(HAVE_JACK,$have_jack,Define to 1 if you have JACK installed.) -config_have_jack="no" -if test $have_jack = "1"; then - config_have_jack="yes" -fi - -# JACK MIDI -have_jack_midi=0 -if test $have_jack = "1"; then - linuxsampler_save_CFLAGS=$CFLAGS - linuxsampler_save_LIBS=$LIBS - CFLAGS="$JACK_CFLAGS $CFLAGS" - LIBS="$JACK_LIBS $LIBS" - AC_CHECK_HEADER(jack/midiport.h, have_jack_midi=1, have_jack_midi=0) - if test $have_jack_midi = "1"; then - AC_CHECK_FUNCS(jack_midi_get_event_count) - AC_COMPILE_IFELSE(AC_LANG_PROGRAM(#include <jack/midiport.h>, - jack_midi_clear_buffer(0, 0); - ), AC_DEFINE(JACK_MIDI_FUNCS_NEED_NFRAMES, 1, - Define to 1 if you have the old jack midi functions that need an nframes argument.)) - have_midi_input_driver="true" - fi - CFLAGS=$linuxsampler_save_CFLAGS - LIBS=$linuxsampler_save_LIBS -fi -AM_CONDITIONAL(HAVE_JACK_MIDI, test $have_jack_midi = "1") -AC_DEFINE_UNQUOTED(HAVE_JACK_MIDI, $have_jack_midi, - Define to 1 if you have JACK with MIDI support installed.) -config_have_jack_midi="no" -if test $have_jack_midi = "1"; then - config_have_jack_midi="yes" -fi - -# ARTS -AC_ARG_ENABLE(arts-driver, - --disable-arts-driver - Disable support for the Analogue Realtime System - (aRts)., - config_arts_driver="$enableval", - config_arts_driver="yes" -) -have_arts=0 -if test "$config_arts_driver" = "yes"; then - m4_ifdef(m4_include(m4/arts.m4),, - sinclude(m4/arts.m4)) - AM_PATH_ARTS(0.9.5, have_arts=1, have_arts=0) - if test "$have_arts" = "1"; then - have_audio_output_driver="true" - fi -else - echo "ARTS support disabled by configure script parameter" -fi -AM_CONDITIONAL(HAVE_ARTS, test "$have_arts" = "1") -AC_DEFINE_UNQUOTED(HAVE_ARTS,$have_arts,Define to 1 if you have aRts installed.) -config_have_arts="no" -if test $have_arts = "1"; then - config_have_arts="yes" -fi - -# ASIO AUDIO (Win32) -AC_ARG_ENABLE(asiosdk-dir, - --enable-asiosdk-dir - Directory where the ASIO SDK is located, this automatically - enables the compilation of the ASIO audio output driver., - config_asiosdk_dir="${enableval}", - config_asiosdk_dir="." -) -AC_SUBST(config_asiosdk_dir) - -AC_ARG_ENABLE(asio-driver, - --disable-asio-driver - Disable support for the Windows ASIO driver., - config_asio_driver="$enableval", - config_asio_driver="yes" -) -have_asio=0 -ASIOSDK_BASEDIR= -if test "$config_asio_driver" = "yes"; then - asiosdk_headerfile=$config_asiosdk_dir/ASIOSDK2/common/asio.h - echo -n "checking for ASIO headerfile: $asiosdk_headerfile ...." - if test -e $asiosdk_headerfile ; then - echo yes - have_asio=1 - ASIOSDK_BASEDIR="$config_asiosdk_dir" - else - echo no - have_asio=0 - fi - if test "$have_asio" = "1"; then - have_audio_output_driver="true" - fi -else - echo "Windows ASIO support disabled by configure script parameter" -fi -AC_SUBST(ASIOSDK_BASEDIR) -AM_CONDITIONAL(HAVE_ASIO, test $have_asio = "1") -AC_DEFINE_UNQUOTED(HAVE_ASIO,$have_asio,Define to 1 if you have ASIO installed.) -config_have_asio="no" -if test $have_asio = "1"; then - config_have_asio="yes" -fi - -# MidiShare (Linux, OS X, Windows) -AC_ARG_ENABLE(midishare-driver, - --disable-midishare-driver - Disable support for the MidiShare system., - config_midishare_driver="$enableval", - config_midishare_driver="yes" -) -have_midishare=0 -if test "$config_midishare_driver" = "yes"; then - AC_CHECK_HEADER(MidiShare.h, - AC_CHECK_LIB(MidiShare, MidiCountEvs, - have_midishare=1, - have_midishare=0 - ) - , - have_midishare=0 - ) - if test "$have_midishare" = "1"; then - have_midi_input_driver="true" - fi -else - echo "MidiShare support disabled by configure script parameter" -fi -AM_CONDITIONAL(HAVE_MIDISHARE, test $have_midishare = "1") -AC_DEFINE_UNQUOTED(HAVE_MIDISHARE,$have_midishare,Define to 1 if you have MidiShare installed.) -config_have_midishare="no" -if test $have_midishare = "1"; then - config_have_midishare="yes" -fi - -# CoreMIDI (OS X) -AC_ARG_ENABLE(coremidi-driver, - --disable-coremidi-driver - Disable support for the Apple CoreMIDI system., - config_coremidi_driver="$enableval", - config_coremidi_driver="yes" -) -have_coremidi=0 -if test "$config_coremidi_driver" = "yes"; then - AC_CHECK_HEADER(CoreMIDI/CoreMIDI.h, - have_coremidi=1, - have_coremidi=0 - ) - if test "$have_coremidi" = "1"; then - have_midi_input_driver="true" - fi -else - echo "CoreMIDI support disabled by configure script parameter" -fi -AM_CONDITIONAL(HAVE_COREMIDI, test $have_coremidi = "1") -AC_DEFINE_UNQUOTED(HAVE_COREMIDI,$have_coremidi,Define to 1 if you have CoreMIDI installed.) -config_have_coremidi="no" -if test $have_coremidi = "1"; then - config_have_coremidi="yes" -fi - -# CoreAudio (OS X) -AC_ARG_ENABLE(coreaudio-driver, - --disable-coreaudio-driver - Disable support for the Apple CoreAudio system., - config_coreaudio_driver="$enableval", - config_coreaudio_driver="yes" -) -have_coreaudio=0 -if test "$config_coreaudio_driver" = "yes"; then - AC_CHECK_HEADER(CoreAudio/CoreAudio.h, - have_coreaudio=1, - have_coreaudio=0 - ) - if test "$have_coreaudio" = "1"; then - have_audio_output_driver="true" - fi -else - echo "CoreAudio support disabled by configure script parameter" -fi -AM_CONDITIONAL(HAVE_COREAUDIO, test $have_coreaudio = "1") -AC_DEFINE_UNQUOTED(HAVE_COREAUDIO,$have_coreaudio,Define to 1 if you have CoreAudio installed.) -config_have_coreaudio="no" -if test $have_coreaudio = "1"; then - config_have_coreaudio="yes" -fi - -# MME MIDI (Win32) -AC_ARG_ENABLE(mmemidi-driver, - --disable-mmemidi-driver - Disable support for the Windows MME MIDI system., - config_mmemidi_driver="$enableval", - config_mmemidi_driver="yes" -) -have_mmemidi=0 -if test "$config_mmemidi_driver" = "yes"; then - AC_CHECK_HEADERS(mmsystem.h, - have_mmemidi=1, - have_mmemidi=0 - ) - if test "$have_mmemidi" = "1"; then - have_midi_input_driver="true" - fi -else - echo "MME MIDI support disabled by configure script parameter" -fi -AM_CONDITIONAL(HAVE_MME_MIDI, test $have_mmemidi = "1") -AC_DEFINE_UNQUOTED(HAVE_MME_MIDI,$have_mmemidi,Define to 1 if you have MME MIDI installed.) -config_have_mme="no" -if test $have_mmemidi = "1"; then - config_have_mme="yes" -fi - -# DSSI -AC_CHECK_HEADERS(dssi.h, - config_have_dssi="yes", - config_have_dssi="no") -AM_CONDITIONAL(HAVE_DSSI, test $config_have_dssi = "yes") - -# LV2 -PKG_CHECK_MODULES(LV2, lv2core, config_have_lv2="yes", config_have_lv2="no") -if test $config_have_lv2 = "no"; then - AC_CHECK_HEADER(lv2.h, config_have_lv2="yes", config_have_lv2="no") -fi -AM_CONDITIONAL(HAVE_LV2, test $config_have_lv2 = "yes") - -# VST -AC_ARG_ENABLE(vstsdk-dir, - --enable-vstsdk-dir - Directory where the VST SDK is located. - This automatically enables the compilation - of the VST plugin., - VSTSDK_DIR="${enableval}", - VSTSDK_DIR= -) -AC_SUBST(VSTSDK_DIR) -AM_CONDITIONAL(HAVE_VST, test "x$VSTSDK_DIR" != "x") -config_have_vst="no" -if test "x$VSTSDK_DIR" != "x"; then - config_have_vst="yes" -fi - -# AU -AC_CHECK_HEADERS(AudioUnit/AudioUnit.h, - config_have_au="yes", - config_have_au="no") -AM_CONDITIONAL(HAVE_AU, test $config_have_au = "yes") -AM_CONDITIONAL(HAVE_AUFLAGS, test "$AUFLAGS" != "") - -if test $config_have_au = "yes" ; then - if test "x$DEVELOPER_EXTRAS_DIR" = "x" ; then - if test -d /Developer/Extras ; then - DEVELOPER_EXTRAS_DIR=/Developer/Extras - else - DEVELOPER_EXTRAS_DIR=/Developer/Examples - fi - fi - AC_SUBST(DEVELOPER_EXTRAS_DIR) -fi - -if test "x$MAC_PLUGIN_INSTALL_DIR" = "x" ; then - MAC_PLUGIN_INSTALL_DIR=/Library/Audio/Plug-Ins -fi -AC_SUBST(MAC_PLUGIN_INSTALL_DIR) - -# have we found at least one MIDI input and one audio output driver ? -if test "$have_midi_input_driver" = "false"; then - echo "No supported MIDI input system found!" - echo "Sorry, LinuxSampler only supports the following MIDI drivers at the moment:" - echo "ALSA, JACK, MIDIShare, CoreMIDI, MME." - echo "If you think you have one of those available on your system, make sure you" - echo "also have the respective development (header) files installed." - exit -1; -fi -if test "$have_audio_output_driver" = "false"; then - echo "No supported audio output system found!" - echo "Sorry, LinuxSampler only supports ALSA, JACK, ARTS and ASIO as audio output" - echo "driver at the moment!" - exit -1; -fi - - - -########################################################################### -# Checks for various DLL libraries - -# Check presence of libgig -libgig_version="3.3.0" -PKG_CHECK_MODULES(GIG, gig >= $libgig_version, HAVE_GIG=true, HAVE_GIG=false) -if test "$HAVE_GIG" = "false"; then - echo "Required libgig version not found!" - echo "You need to have libgig version ${libgig_version} installed!" - exit -1; -else - echo "yes, found libgig $libgig_version" -fi -AC_SUBST(GIG_CFLAGS) -AC_SUBST(GIG_LIBS) - -# SF2 Engine (requires libgig) -AC_ARG_ENABLE(sf2-engine, - --disable-sf2-engine - Disable compilation of the SF2 engine. - You need to have libgig installed., - config_sf2_engine="$enableval", - config_sf2_engine="yes" -) -HAVE_SF2=0; -if test "$config_sf2_engine" = "yes"; then - HAVE_SF2=1 -else - echo "SF2 engine disabled by configure script parameter" -fi -AM_CONDITIONAL(HAVE_SF2, test $HAVE_SF2 = 1) -AC_DEFINE_UNQUOTED(HAVE_SF2,$HAVE_SF2,Define to 1 if you want SF2 engine and have libsf2 installed.) -config_have_sf2="no" -if test $HAVE_SF2 = "1"; then - config_have_sf2="yes" -fi - -# Check presence of libsndfile -libsndfile_version="1.0" -PKG_CHECK_MODULES(SNDFILE, sndfile >= $libsndfile_version, HAVE_SNDFILE=true, HAVE_SNDFILE=false) -if test "$HAVE_SNDFILE" = "false"; then - echo "Required libsndfile version not found!" - echo "You need to have libsndfile version ${libsndfile_version} installed!" - exit -1; -else - echo "yes, found libsndfile $libsndfile_version" -fi -AC_SUBST(SNDFILE_CFLAGS) -AC_SUBST(SNDFILE_LIBS) - -# Check for Vorbis support in libsndfile -linuxsampler_save_CFLAGS=$CFLAGS -CFLAGS="$SNDFILE_CFLAGS $CFLAGS" -AC_CHECK_DECLS(SF_FORMAT_VORBIS, , ,#include <sndfile.h>) - -# Check for loop functionality in libsndfile -AC_CHECK_MEMBERS(SF_INSTRUMENT.loops,, - AC_MSG_WARN(Your version of libsndfile does not support - reading of loop information. LinuxSampler will not be able to - extract loop information from sample files.), - #include <sndfile.h>) -CFLAGS=$linuxsampler_save_CFLAGS - -# Instruments DB feature (requires SQLite 3.3) -AC_ARG_ENABLE(instruments-db, - --disable-instruments-db - Disable compilation of the sampler's instruments - database feature. You need to have SQLite 3.3 - or younger installed for this feature., - config_instruments_db="$enableval", - config_instruments_db="yes" -) -HAVE_SQLITE3=0; -if test "$config_instruments_db" = "yes"; then - # Check presence of sqlite3 - sqlite_version="3.3" - PKG_CHECK_MODULES(SQLITE3, sqlite3 >= $sqlite_version, HAVE_SQLITE3=true, HAVE_SQLITE3=false) - AC_SUBST(SQLITE3_LIBS) - AC_SUBST(SQLITE3_CFLAGS) - if test $HAVE_SQLITE3 = false; then - HAVE_SQLITE3=0 - config_instruments_db="no" - echo "*** Required sqlite version not found!" - echo "*** You need to have sqlite version ${sqlite_version} or higher" - echo "*** for instruments database support to be enabled." - echo "*** Support for instruments DB will be disabled!" - else - HAVE_SQLITE3=1 - fi -else - echo "Instruments DB feature disabled by configure script parameter" -fi -AM_CONDITIONAL(HAVE_SQLITE3, test $HAVE_SQLITE3 = 1) -AC_DEFINE_UNQUOTED(HAVE_SQLITE3,$HAVE_SQLITE3,Define to 1 if you want the instruments DB feature and have SQLITE3 installed.) - - - -########################################################################### -# Handle Configuration Options - -# TODO: should we use AC_ARG_VAR(variable, description) instead? - -AC_ARG_ENABLE(asm, - --disable-asm - Enable hand-crafted assembly optimizations - (default=on). LinuxSampler provides CPU specific - assembly optimizations. This is currently limited - to just enter a fast (denormal) FPU mode on x86 - platforms. There are currently no synthesis core - assembly optimizations anymore since LS 0.4.0, - config_asm="$enableval", - config_asm="yes" -) -if test "$config_asm" = "yes"; then - AC_DEFINE_UNQUOTED(CONFIG_ASM, 1, Define to 1 if you want to enable asm optimizations.) -fi - -AC_ARG_ENABLE(dev-mode, - --enable-dev-mode - Enable development mode (default=off). In that mode - we do some extra sanity checks here and there. - This helps to spot possible problems, but reduces - efficiency a bit, - config_dev_mode="$enableval", - config_dev_mode="no" -) -if test "$config_dev_mode" = "yes"; then - AC_DEFINE_UNQUOTED(CONFIG_DEVMODE, 1, Define to 1 if you want to enable development mode.) -fi - -AC_ARG_ENABLE(debug-level, - --enable-debug-level - Specify verbosity of console messages (default=1). - The higher the value, the higher will be verbosity. - A value of 0 means no console output at all. - There's not really an upper limit but the usual - level of all messages is currently somewhere less - than 10., - config_debug_level="${enableval}", - config_debug_level="1" -) -AC_DEFINE_UNQUOTED(CONFIG_DEBUG_LEVEL, $config_debug_level, Define console verbosity.) - -AC_ARG_ENABLE(rt-exceptions, - --enable-rt-exceptions - Enable exceptions in the realtime thread - (default=no). If this is enabled, exceptions will - be thrown on critical errors in the realtime - context as well. Otherwise if disabled - segmentation faults will be forced by the - application on critical errors., - config_rt_exceptions="$enableval", - config_rt_exceptions="no" -) -if test "$config_rt_exceptions" = "yes"; then - AC_DEFINE_UNQUOTED(CONFIG_RT_EXCEPTIONS, 1, Define to 1 to allow exceptions in the realtime context.) -fi - -config_pthread_testcancel="$mac" -AC_ARG_ENABLE(pthread-testcancel, - --enable-pthread-testcancel - Enable pthread_testcancel() calls and avoid - asynchronous cancel of pthreads (default=yes - for Mac targets, no otherwise)., - config_pthread_testcancel="$enableval", - -) -if test "$config_pthread_testcancel" = "yes"; then - AC_DEFINE_UNQUOTED(CONFIG_PTHREAD_TESTCANCEL, 1, Define to 1 to enable pthread_testcancel() calls.) -fi - -AC_ARG_ENABLE(preload-samples, - --enable-preload-samples - Due to seeking and latency issues with hard drives - we have to cache a small part of samples' head in - RAM (default=32768). The higher this value the - more memory will be occupied for each sample, but - the safer this will be in regards of possible - droputs. A 'good' value depends on the running - system and usage dependant factors., - config_preload_samples="${enableval}", - config_preload_samples="32768" -) -AC_DEFINE_UNQUOTED(CONFIG_PRELOAD_SAMPLES, $config_preload_samples, Define amount of sample points to be cached in RAM.) - -AC_ARG_ENABLE(max-pitch, - --enable-max-pitch - Specify the maximum allowed pitch value in octaves - (default=4). To lower memory usage you might want - set a smaller value., - config_max_pitch="${enableval}", - config_max_pitch="4" -) -AC_DEFINE_UNQUOTED(CONFIG_MAX_PITCH, $config_max_pitch, Define max. allowed pitch.) - -AC_ARG_ENABLE(max-events, - --enable-max-events - Specify the maximum allowed amount of events to be - processed per fragment (default=1024)., - config_max_events="${enableval}", - config_max_events="1024" -) -AC_DEFINE_UNQUOTED(CONFIG_MAX_EVENTS_PER_FRAGMENT, $config_max_events, Define max. allowed events per fragment.) - -AC_ARG_ENABLE(eg-bottom, - --enable-eg-bottom - Bottom limit of envelope generators - (default=0.001). Certain kinds of curve types like - exponential curves converge against 0 but never - reach 0. So we have to define a certain low value - after which we should consider all smaller values - to be 'almost zero'. The smaller this value, the - longer will voices survive in EG's release stage - and thus waste voices. If this value is too high - will cause click sounds though., - config_eg_bottom="${enableval}", - config_eg_bottom="0.001" -) -AC_DEFINE_UNQUOTED(CONFIG_EG_BOTTOM, $config_eg_bottom, Define bottom limit of envelopes.) - -AC_ARG_ENABLE(eg-min-release-time, - --enable-eg-min-release-time - Specify the lowest allowed release time in seconds - (default=0.0025). This value will also be used to - ramp down voices on voice stealing. This value - should always be less than the period time of the - used audio driver, as in case of voice stealing - the killed voice needs to be completely ramped - down in the same fragment., - config_eg_min_release_time="${enableval}", - config_eg_min_release_time="0.0025" -) -AC_DEFINE_UNQUOTED(CONFIG_EG_MIN_RELEASE_TIME, $config_eg_min_release_time, Define min. release time.) - -AC_ARG_ENABLE(refill-streams, - --enable-refill-streams - Number of streams that should be refilled in each - disk thread cycle (default=4)., - config_refill_streams="${enableval}", - config_refill_streams="4" -) -AC_DEFINE_UNQUOTED(CONFIG_REFILL_STREAMS_PER_RUN, $config_refill_streams, Define amount of streams to be refilled per cycle.) - -AC_ARG_ENABLE(stream-min-refill, - --enable-stream-min-refill - Minimum refill size for disk streams (default=1024). - The disk thread will go to sleep for a while if no - stream had to be refilled more than this value in - a disk thread cycle., - config_stream_min_refill="${enableval}", - config_stream_min_refill="1024" -) -AC_DEFINE_UNQUOTED(CONFIG_STREAM_MIN_REFILL_SIZE, $config_stream_min_refill, Define min. stream refill size.) - -AC_ARG_ENABLE(stream-max-refill, - --enable-stream-max-refill - Maximum refill size for disk streams - (default=65536). The disk thread will refill - each stream only by a size of this value per - disk thread cycle., - config_stream_max_refill="${enableval}", - config_stream_max_refill="65536" -) -AC_DEFINE_UNQUOTED(CONFIG_STREAM_MAX_REFILL_SIZE, $config_stream_max_refill, Define max. stream refill size.) - -AC_ARG_ENABLE(stream-size, - --enable-stream-size - Size of each stream's ring buffer in sample points - (default=262144)., - config_stream_size="${enableval}", - config_stream_size="262144" -) -AC_DEFINE_UNQUOTED(CONFIG_STREAM_BUFFER_SIZE, $config_stream_size, Define each stream's ring buffer size.) - -AC_ARG_ENABLE(max-streams, - --enable-max-streams - Initial maximum amount of disk streams - (default=90). This value can be changed at - runtime. It should always be higher than the - maximum amount of voices., - config_max_streams="${enableval}", - config_max_streams="90" -) -AC_DEFINE_UNQUOTED(CONFIG_DEFAULT_MAX_STREAMS, $config_max_streams, Define initial max. streams.) - -AC_ARG_ENABLE(max-voices, - --enable-max-voices - Initial maximum amount of voices (default=64). - This value can be changed at runtime. It should - always be lower than the maximum amount of disk - streams., - config_max_voices="${enableval}", - config_max_voices="64" -) -AC_DEFINE_UNQUOTED(CONFIG_DEFAULT_MAX_VOICES, $config_max_voices, Define initial max. voices.) - -AC_ARG_ENABLE(subfragment-size, - --enable-subfragment-size - Every audio fragment will be splitted into - subfragments. Where each subfragment renders - audio with constant synthesis parameters. This is - done for efficiency reasons. This parameter - defines the default size of a subfragment in - sample points. A large value means less CPU time - whereas a low value means better audio quality - (default=32)., - config_subfragment_size="${enableval}", - config_subfragment_size="32" -) -AC_DEFINE_UNQUOTED(CONFIG_DEFAULT_SUBFRAGMENT_SIZE, $config_subfragment_size, Define default subfragment size (in sample points).) - -AC_ARG_ENABLE(global-attenuation-default, - --enable-global-attenuation-default - To prevent clipping all samples will be lowered - in amplitude by this given default factor (can - be overridden at runtime). - (default=0.35), - config_global_attenuation_default="${enableval}", - config_global_attenuation_default="0.35" -) -AC_DEFINE_UNQUOTED(CONFIG_GLOBAL_ATTENUATION_DEFAULT, $config_global_attenuation_default, Define default global volume attenuation (as floating point factor).) - -AC_ARG_ENABLE(voice-steal-algo, - --enable-voice-steal-algo - Voice stealing algorithm to be used. Currently - available options: - none: - Disable voice stealing completely. - oldestvoiceonkey (default): - Try to kill a voice on the same key first, - if no success, proceed with the oldest key. - oldestkey: - Try to kill a voice from the oldest active - key., - if test ! "(" "${enableval}" = "none" \ - -o "${enableval}" = "oldestvoiceonkey" \ - -o "${enableval}" = "oldestkey" ")" ; then - AC_MSG_ERROR(Unknown voice stealing algorithm for parameter --enable-voice-steal-algo) - else - config_voice_steal_algo="${enableval}" - fi - , - config_voice_steal_algo="oldestvoiceonkey" -) -AC_DEFINE_UNQUOTED(CONFIG_VOICE_STEAL_ALGO, voice_steal_algo_${config_voice_steal_algo}, Define voice stealing algorithm to be used.) - -AC_ARG_ENABLE(sysex-buffer-size, - --enable-sysex-buffer-size - System Exclusive Message buffer size in kB - (default=2048)., - config_sysex_buffer_size="${enableval}", - config_sysex_buffer_size="2048" -) -AC_DEFINE_UNQUOTED(CONFIG_SYSEX_BUFFER_SIZE, $config_sysex_buffer_size, Define SysEx buffer size.) - -AC_ARG_ENABLE(force-filter, - --enable-force-filter - If enabled will force filter to be used even if - no usage was define in instrument patch files. - (default=no)., - config_force_filter="$enableval", - config_force_filter="no" -) -if test "$config_force_filter" = "yes"; then - AC_DEFINE_UNQUOTED(CONFIG_FORCE_FILTER, 1, Define to 1 to force filter usage.) -fi - -AC_ARG_ENABLE(filter-cutoff-min, - --enable-filter-cutoff-min - Minimum filter cutoff frequency in Hz - (default=100.0)., - config_filter_cutoff_min="${enableval}", - config_filter_cutoff_min="100.0" -) -AC_DEFINE_UNQUOTED(CONFIG_FILTER_CUTOFF_MIN, ${config_filter_cutoff_min}f, Define min. filter cutoff frequency.) - -AC_ARG_ENABLE(filter-cutoff-max, - --enable-filter-cutoff-max - Maximum filter cutoff frequency in Hz - (default=10000.0)., - config_filter_cutoff_max="${enableval}", - config_filter_cutoff_max="10000.0" -) -AC_DEFINE_UNQUOTED(CONFIG_FILTER_CUTOFF_MAX, ${config_filter_cutoff_max}f, Define max. filter cutoff frequency.) - -AC_ARG_ENABLE(override-cutoff-ctrl, - --enable-override-cutoff-ctrl - Override filter cutoff MIDI controller (default=no). - Note: you have to define the MIDI controller number - here, it's not a boolean parameter type! If this - option is used, controller number given by - instrument patch will be ignored and instead this - supplied value will be used., - config_override_cutoff_ctrl="${enableval}", - config_override_cutoff_ctrl="no" -) -if test ! "$config_override_cutoff_ctrl" = "no"; then - AC_DEFINE_UNQUOTED(CONFIG_OVERRIDE_CUTOFF_CTRL, $config_override_cutoff_ctrl, Define to a MIDI controller number to override cutoff control.) -fi - -AC_ARG_ENABLE(override-resonance-ctrl, - --enable-override-resonance-ctrl - Override filter resonance MIDI controller - (default=no). Note: you have to define the MIDI - controller number here, it's not a boolean - parameter type! If this option is used, controller - number given by instrument patch will be ignored - and instead this supplied value will be used., - config_override_resonance_ctrl="${enableval}", - config_override_resonance_ctrl="no" -) -if test ! "$config_override_resonance_ctrl" = "no"; then - AC_DEFINE_UNQUOTED(CONFIG_OVERRIDE_RESONANCE_CTRL, $config_override_resonance_ctrl, Define to a MIDI controller number to override resonance control.) -fi - -AC_ARG_ENABLE(override-filter-type, - --enable-override-filter-type - Override filter type (default=no). Options: - hp: for highpass - bp: for bandpass - br: for bandreject - lp: for lowpass - lpt: for lowpass turbo, - if test "${enableval}" = "hp" ; then - config_override_filter_type="::gig::vcf_type_highpass" - elif test "${enableval}" = "bp" ; then - config_override_filter_type="::gig::vcf_type_bandpass" - elif test "${enableval}" = "br" ; then - config_override_filter_type="::gig::vcf_type_bandreject" - elif test "${enableval}" = "lp" ; then - config_override_filter_type="::gig::vcf_type_lowpass" - elif test "${enableval}" = "lpt" ; then - config_override_filter_type="::gig::vcf_type_lowpassturbo" - elif test ! "${enableval}" = "no"; then - AC_MSG_ERROR(Unknown filter type for parameter --enable-override-filter-type) - fi - , - config_override_filter_type="no" -) -if test ! "$config_override_filter_type" = "no"; then - AC_DEFINE_UNQUOTED(CONFIG_OVERRIDE_FILTER_TYPE, $config_override_filter_type, Define to a filter type to always force that filter type.) -fi - -AC_ARG_ENABLE(gs-checksum, - --enable-gs-checksum - Enable Roland General Synth SysEx checksum check - (default=no). If this is enabled, all GS SysEx - messages which do not provide a correct checksum - will be ignored. This is disabled by default as - not all devices honor GS checksums., - config_assert_gs_sysex_checksum="$enableval", - config_assert_gs_sysex_checksum="no" -) -if test "config_assert_gs_sysex_checksum" = "yes"; then - AC_DEFINE_UNQUOTED(CONFIG_ASSERT_GS_SYSEX_CHECKSUM, 1, Define to 1 if you want to enable GS SysEx check.) -fi - -AC_ARG_ENABLE(portamento-time-min, - --enable-portamento-time-min - Minimum Portamento time in seconds - (default=0.1)., - config_portamento_time_min="${enableval}", - config_portamento_time_min="0.1" -) -AC_DEFINE_UNQUOTED(CONFIG_PORTAMENTO_TIME_MIN, $config_portamento_time_min, Define min. portamento time.) - -AC_ARG_ENABLE(portamento-time-max, - --enable-portamento-time-max - Maximum Portamento time in seconds - (default=32)., - config_portamento_time_max="${enableval}", - config_portamento_time_max="32" -) -AC_DEFINE_UNQUOTED(CONFIG_PORTAMENTO_TIME_MAX, $config_portamento_time_max, Define max. portamento time.) - -AC_ARG_ENABLE(portamento-time-default, - --enable-portamento-time-default - Default Portamento time in seconds - (default=1)., - config_portamento_time_default="${enableval}", - config_portamento_time_default="1" -) -AC_DEFINE_UNQUOTED(CONFIG_PORTAMENTO_TIME_DEFAULT, $config_portamento_time_default, Define default portamento time.) - -AC_ARG_ENABLE(signed-triang-algo, - --enable-signed-triang-algo - Signed triangular wave algorithm to be used (e.g. for LFOs). - Currently available options: - intmath: - Uses integer math without any branch will then be - converted to floating point value for each sample point. - This int->float conversion might hurt on some systems. - intmathabs: - Similar to intmath but uses abs() function. - Depending on compiler and platrofm this could - perform better than integer math as it avoids - an extra integer multiply instruction. - diharmonic: - The triangular wave will be approximated by adding two - sinusoidials. This solution might especially hurt on - systems with weak floating point unit. - benchmark (default): - This is not an algorithm. Use this option if the - appropriate algorithm should be automatically - chosen by the configure script by performing a - benchmark between the algorithms mentioned above. - This will NOT work for cross compilation!, - if test ! "(" "${enableval}" = "intmath" \ - -o "${enableval}" = "intmathabs" \ - -o "${enableval}" = "diharmonic" ")" ; then - AC_MSG_ERROR(Unknown triangular wave algorithm for parameter --enable-signed-triang-algo) - else - config_signed_triang_algo="${enableval}" - fi - , - config_signed_triang_algo="benchmark" -) - -AC_ARG_ENABLE(unsigned-triang-algo, - --enable-unsigned-triang-algo - Unsigned triangular wave algorithm to be used (e.g. for LFOs). - Currently available options: - intmath: - Uses integer math without any branch will then be - converted to floating point value for each sample point. - This int->float conversion might hurt on some systems. - intmathabs: - Similar to intmath but uses abs() function. - Depending on compiler and platrofm this could - perform better than integer math as it avoids - an extra integer multiply instruction. - diharmonic: - The triangular wave will be approximated by adding two - sinusoidials. This solution might especially hurt on - systems with weak floating point unit. - benchmark (default): - This is not an algorithm. Use this option if the - appropriate algorithm should be automatically - chosen by the configure script by performing a - benchmark between the algorithms mentioned above. - This will NOT work for cross compilation!, - if test ! "(" "${enableval}" = "intmath" \ - -o "${enableval}" = "intmathabs" \ - -o "${enableval}" = "diharmonic" ")" ; then - AC_MSG_ERROR(Unknown triangular wave algorithm for parameter --enable-unsigned-triang-algo) - else - config_unsigned_triang_algo="${enableval}" - fi - , - config_unsigned_triang_algo="benchmark" -) - -AC_ARG_ENABLE(process-muted-channels, - --enable-process-muted-channels - Enable processing of muted channels (default=no). - In that mode all MIDI events in the muted channels - will be processed. This will provide information - about the active voices in the muted channels and - will not discard notes, triggered in mute mode, - when the channel is unmuted. But also will reduce - the efficiency., - config_process_muted_channels="$enableval", - config_process_muted_channels="no" -) -if test "$config_process_muted_channels" = "yes"; then - AC_DEFINE_UNQUOTED(CONFIG_PROCESS_MUTED_CHANNELS, 1, Define to 1 if you want to enable processing of muted channels.) -fi - -AC_ARG_ENABLE(process-all-notes-off, - --disable-process-all-notes-off - Disable interpretation of All-Notes-Off MIDI - messages (default=on). By default LS will release - all voices whenever it receives an All-Notes-Off - MIDI message. You can disable this behavior, so - that LS simply ignores such messages., - config_process_all_notes_off="$enableval", - config_process_all_notes_off="yes" -) -if test "$config_process_all_notes_off" = "yes"; then - AC_DEFINE_UNQUOTED(CONFIG_PROCESS_ALL_NOTES_OFF, 1, Define to 1 if you want to enable processing of All-Notes-Off MIDI messages.) -fi - -AC_ARG_ENABLE(interpolate-volume, - --disable-interpolate-volume - Disable interpolation of volume modulation - (default=on). With this enabled, the volume changes - generated by for example the envelope generator - will be smoother, minimizing the risk for audio - clicks. Disable it to reduce CPU usage., - config_interpolate_volume="$enableval", - config_interpolate_volume="yes" -) -if test "$config_interpolate_volume" = "yes"; then - AC_DEFINE_UNQUOTED(CONFIG_INTERPOLATE_VOLUME, 1, Define to 1 if you want to enable interpolation of volume modulation.) -fi - -AC_ARG_ENABLE(master-volume-sysex-by-port, - --enable-master-volume-sysex-by-port - Whether global volume sysex message should be - applied globally to the whole sampler or only to - the sampler channels connected to the same MIDI - input port on which the sysex message arrived on. - By default global volume sysex messages apply - globally to the whole sampler, since many MIDI - devices behave that way., - config_master_volume_sysex_by_port="$enableval", - config_master_volume_sysex_by_port="no" -) -if test "$config_master_volume_sysex_by_port" = "yes"; then - AC_DEFINE_UNQUOTED(CONFIG_MASTER_VOLUME_SYSEX_BY_PORT, 1, Define to 1 if you want global volume sysex message only be applied to the respective MIDI port.) -fi - -AC_ARG_ENABLE(plugin-dir, - --enable-plugin-dir - Directory where the sampler shall look for potential plugins, - that is 3rd party shared libraries that should be loaded by - the sampler on startup. By default the sampler will search - for plugins in the subdirectory "plugins" below its own - library directory. - (i.e. /usr/local/lib/linuxsampler/plugins), - config_plugin_dir="${enableval}", - config_plugin_dir="${libdir}/linuxsampler/plugins" -) -AC_SUBST(config_plugin_dir) - -AC_ARG_ENABLE(default-instruments-db-location, - --enable-default-instruments-db-location - Only when instruments DB feature is enabled: file name - which shall be taken as default location of the - instruments DB file. This location can still be - overridden at runtime with a command line switch. - (default: /var/lib/linuxsampler/instruments.db), - config_default_instruments_db_file="${enableval}", - config_default_instruments_db_file="/var/lib/linuxsampler/instruments.db" -) -AC_DEFINE_UNQUOTED( - CONFIG_DEFAULT_INSTRUMENTS_DB_LOCATION, - "$config_default_instruments_db_file", - Only when instruments DB feature is enabled: default location of the DB file. -) -AC_SUBST(config_default_instruments_db_file) - - -########################################################################### -# Automatic Benchmarks (to detect the best algorithms for the system) - -AC_LANG_SAVE - -if test "$config_signed_triang_algo" = "benchmark"; then - echo -n "benchmarking for the best (signed) triangular oscillator algorithm... " - AC_LANG_CPLUSPLUS - AC_TRY_RUN( - #define SIGNED 1 - #define SILENT 1 - #include "${srcdir}/benchmarks/triang.cpp" - , - triang_signed=0, - triang_signed=$?, - triang_signed=0 - ) - if test "$triang_signed" = "2"; then - config_signed_triang_algo="intmath" - echo "integer math" - elif test "$triang_signed" = "3"; then - config_signed_triang_algo="diharmonic" - echo "di harmonics" - elif test "$triang_signed" = "5"; then - config_signed_triang_algo="intmathabs" - echo "integer math using abs()" - else - echo "Benchmark of signed triangular wave algorithms failed!" - echo "Maybe you are doing cross compilation? In that case you have to select" - echo "an algorithm manually with './configure --enable-signed-triang-algo=...'" - echo "Call './configure --help' for further information or read configure.in." - exit -1; - fi -else - case "$config_signed_triang_algo" in - intmath) - triang_signed=2 ;; - diharmonic) - triang_signed=3 ;; - intmathabs) - triang_signed=5 ;; - esac -fi -AC_DEFINE_UNQUOTED(CONFIG_SIGNED_TRIANG_ALGO, ${triang_signed}, Define signed triangular wave algorithm to be used.) - -if test "$config_unsigned_triang_algo" = "benchmark"; then - echo -n "benchmarking for the best (unsigned) triangular oscillator algorithm... " - AC_LANG_CPLUSPLUS - AC_TRY_RUN( - #define SIGNED 0 - #define SILENT 1 - #include "${srcdir}/benchmarks/triang.cpp" - , - triang_unsigned=0, - triang_unsigned=$?, - triang_unsigned=0 - ) - if test "$triang_unsigned" = "2"; then - config_unsigned_triang_algo="intmath" - echo "integer math" - elif test "$triang_unsigned" = "3"; then - config_unsigned_triang_algo="diharmonic" - echo "di harmonics" - elif test "$triang_unsigned" = "5"; then - config_unsigned_triang_algo="intmathabs" - echo "integer math using abs()" - else - echo "Benchmark of unsigned triangular wave algorithms failed!" - echo "Maybe you are doing cross compilation? In that case you have to select" - echo "an algorithm manually with './configure --enable-unsigned-triang-algo=...'" - echo "Call './configure --help' for further information or read configure.in." - exit -1; - fi -else - case "$config_unsigned_triang_algo" in - intmath) - triang_unsigned=2 ;; - diharmonic) - triang_unsigned=3 ;; - intmathabs) - triang_unsigned=5 ;; - esac -fi -AC_DEFINE_UNQUOTED(CONFIG_UNSIGNED_TRIANG_ALGO, ${triang_unsigned}, Define unsigned triangular wave algorithm to be used.) - -AC_LANG_RESTORE - - -########################################################################### -# Create Build Files - -AM_CONFIG_HEADER(config.h) -AM_INIT_AUTOMAKE(linuxsampler, "$LINUXSAMPLER_RELEASE_MAJOR.$LINUXSAMPLER_RELEASE_MINOR.$LINUXSAMPLER_RELEASE_BUILD") - -AC_LANG_CPLUSPLUS - -# autoconf 2.59/libtool 1.5.12 bug? work-around. Without a check like -# this, the dlfcn.h check in am_prog_libtool may fail. -AC_CHECK_HEADER(stdlib.h) - -AC_OUTPUT( \ - Makefile \ - man/Makefile \ - man/linuxsampler.1 \ - src/Makefile \ - src/db/Makefile \ - src/network/Makefile \ - src/engines/Makefile \ - src/engines/gig/Makefile \ - src/engines/sf2/Makefile \ - src/engines/sfz/Makefile \ - src/engines/common/Makefile \ - src/effects/Makefile \ - src/common/Makefile \ - src/testcases/Makefile \ - src/drivers/Makefile \ - src/drivers/audio/Makefile \ - src/drivers/midi/Makefile \ - src/plugins/Makefile \ - src/hostplugins/Makefile \ - src/hostplugins/dssi/Makefile \ - src/hostplugins/lv2/Makefile \ - src/hostplugins/vst/Makefile \ - src/hostplugins/au/Makefile \ - linuxsampler.spec \ - debian/Makefile \ - Artwork/Makefile \ - scripts/Makefile \ - osx/Makefile \ - osx/linuxsampler.xcodeproj/Makefile \ - Documentation/Makefile \ - Documentation/Engines/Makefile \ - Documentation/Engines/gig/Makefile \ - linuxsampler.pc \ - Doxyfile \ -) - -# resolve all nested variables in '${config_plugin_dir}' -# (merely for providing a human readable summary below) -while test $config_plugin_dir != `eval echo ${config_plugin_dir}` ; do - config_plugin_dir=`eval echo ${config_plugin_dir}` -done - - -########################################################################### -# Output All Configuration Options - -echo "" -echo "#####################################################################" -echo "# LinuxSampler Configuration #" -echo "#-------------------------------------------------------------------#" -echo "# Release Version: ${LINUXSAMPLER_RELEASE_MAJOR}.${LINUXSAMPLER_RELEASE_MINOR}.${LINUXSAMPLER_RELEASE_BUILD}" -echo "# LSCP Version: ${LSCP_RELEASE_MAJOR}.${LSCP_RELEASE_MINOR}" -echo "#-------------------------------------------------------------------" -echo "# Assembly Optimizations: ${config_asm}" -echo "# Development Mode: ${config_dev_mode}" -echo "# Debug Level: ${config_debug_level}" -echo "# Use Exceptions in RT Context: ${config_rt_exceptions}" -echo "# Preload Samples: ${config_preload_samples}" -echo "# Maximum Pitch: ${config_max_pitch} (octaves)" -echo "# Maximum Events: ${config_max_events}" -echo "# Envelope Bottom Level: ${config_eg_bottom} (linear)" -echo "# Envelope Minimum Release Time: ${config_eg_min_release_time} s" -echo "# Streams to be refilled per Disk Thread Cycle: ${config_refill_streams}" -echo "# Minimum Stream Refill Size: ${config_stream_min_refill}" -echo "# Maximum Stream Refill Size: ${config_stream_max_refill}" -echo "# Stream Size: ${config_stream_size}" -echo "# Default Maximum Disk Streams: ${config_max_streams}" -echo "# Default Maximum Voices: ${config_max_voices}" -echo "# Default Subfragment Size: ${config_subfragment_size}" -echo "# Default Global Volume Attenuation: ${config_global_attenuation_default}" -echo "# Voice Stealing Algorithm: ${config_voice_steal_algo}" -echo "# Signed Triangular Oscillator Algorithm: ${config_signed_triang_algo}" -echo "# Unsigned Triangular Oscillator Algorithm: ${config_unsigned_triang_algo}" -echo "# SysEx Buffer Size: ${config_sysex_buffer_size} Byte" -echo "# Min. Portamento Time: ${config_portamento_time_min} s" -echo "# Max. Portamento Time: ${config_portamento_time_max} s" -echo "# Default Portamento Time: ${config_portamento_time_default} s" -echo "# Force Filter Usage: ${config_force_filter}" -echo "# Filter Cutoff Minimum: ${config_filter_cutoff_min} Hz" -echo "# Filter Cutoff Maximum: ${config_filter_cutoff_max} Hz" -echo "# Override Filter Cutoff Controller: ${config_override_cutoff_ctrl}" -echo "# Override Filter Resonance Controller: ${config_override_resonance_ctrl}" -echo "# Override Filter Type: ${config_override_filter_type}" -echo "# Assert GS SysEx Checksum: ${config_assert_gs_sysex_checksum}" -echo "# Process Muted Channels: ${config_process_muted_channels}" -echo "# Process All-Notes-Off MIDI message: ${config_process_all_notes_off}" -echo "# Apply global volume SysEx by MIDI port: ${config_master_volume_sysex_by_port}" -echo "# Interpolate Volume: ${config_interpolate_volume}" -echo "# Instruments database support: ${config_instruments_db}" -if test "$config_instruments_db" = "yes"; then -echo "# Instruments DB default location: ${config_default_instruments_db_file}" -fi -echo "# Plugin Path: ${config_plugin_dir}" -echo "#-------------------------------------------------------------------" -echo "# MIDI Input Drivers:" -echo "# ALSA: ${config_have_alsa}, JACK: ${config_have_jack_midi}, CoreMIDI: ${config_have_coremidi}, MME: ${config_have_mme}, MidiShare: ${config_have_midishare}" -echo "#-------------------------------------------------------------------" -echo "# Audio Output Drivers:" -echo "# ALSA: ${config_have_alsa}, JACK: ${config_have_jack}, ARTS: ${config_have_arts}, CoreAudio: ${config_have_coreaudio}, ASIO: ${config_have_asio}" -echo "#-------------------------------------------------------------------" -echo "# Sampler Engines:" -echo "# GIG: yes, SF2: ${config_have_sf2}, SFZ: yes" -echo "#-------------------------------------------------------------------" -echo "# Effect plugin systems for internal effects:" -echo "# LADSPA: yes" -echo "#-------------------------------------------------------------------" -echo "# Building sampler as plugin for following host standards:" -echo "# DSSI: ${config_have_dssi}, LV2: ${config_have_lv2}, VST: ${config_have_vst}, AU: ${config_have_au}" -echo "#-------------------------------------------------------------------#" -echo "# Read './configure --help' or file 'configure.in' for details. #" -echo "#####################################################################" -echo "" -echo "Good. Now type 'make' to compile, followed by 'make install' as root." -echo ""
View file
linuxsampler-2342.tar.bz2/scripts/generate_parser.sh
Deleted
@@ -1,34 +0,0 @@ -#!/bin/sh -# -# Generates the LSCP parser's C++ source files (src/network/lscpparser.cpp and -# src/network/lscpsymbols.h) according to the LSCP (BNF) grammar definition -# given by src/network/lscp.y - -SCRIPTS_DIR=`dirname $0` -NETWORK_SRC_DIR="$SCRIPTS_DIR/../src/network" - -echo -n "Searching for a parser generator..." -YACC_CMD=NONE -if which "bison" > /dev/null; then - YACC_CMD="`which bison` -y" -elif which "yacc" > /dev/null; then - YACC_CMD=`which yacc` -else - echo "Error: You need yacc (or bison) to generate the LSCP parser !" - exit -1 -fi -echo "OK ($YACC_CMD)" - -echo "Generating LSCP parser..." -( - cd $NETWORK_SRC_DIR - $YACC_CMD -d lscp.y - $YACC_CMD lscp.y - mv -f y.tab.h lscpsymbols.h - mv -f y.tab.c lscpparser.cpp -) -echo "Done" - -echo -n "Updating Documentation/lscp.xml..." -(cd $SCRIPTS_DIR && ./update_grammar.pl) -echo "Done"
View file
linuxsampler-2342.tar.bz2/src/hostplugins/lv2/lv2_event.h
Deleted
@@ -1,273 +0,0 @@ -/* lv2_event.h - C header file for the LV2 events extension. - * - * Copyright (C) 2006-2007 Lars Luthman <lars.luthman@gmail.com> - * Copyright (C) 2008 Dave Robillard <dave@drobilla.net> - * - * This header is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published - * by the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This header 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 Lesser General Public - * License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this header; if not, write to the Free Software Foundation, - * Inc., 59 Temple Place, Suite 330, Boston, MA 01222-1307 USA - */ - -#ifndef LV2_EVENT_H -#define LV2_EVENT_H - -#define LV2_EVENT_URI "http://lv2plug.in/ns/ext/event" -#define LV2_EVENT_AUDIO_STAMP 0 - -#include <stdint.h> - -/** @file - * This header defines the code portion of the LV2 events extension with - * URI <http://lv2plug.in/ns/ext/event>. - * - * Below, the URI prefix 'lv2ev' is assumed to expand to - * <http://lv2plug.in/ns/ext/event#>. - * - * This extension is a generic transport mechanism for time stamped events - * of any type (e.g. MIDI, OSC, ramps, etc). Each port can transport mixed - * events of any type; the type of events and timestamps are defined by a URI - * which is mapped to an integer by the host for performance reasons. - * - * This extension requires the host to support the LV2 URI Map extension. - * This requirement is implicit - a plugin does not have to list the URI Map - * feature as required or optional in its RDF data for the host to provide - * the URI Map LV2_Feature in the instantiation function. - * - * Any host which supports this extension MUST guarantee that any call to - * the LV2 URI Map uri_to_id function with the URI of this extension as the - * 'map' argument returns a value within the range of uint16_t. - */ - - -/** The best Pulses Per Quarter Note for tempo-based uint32_t timestmaps. - * Equal to 2^12 * 5 * 7 * 9 * 11 * 13 * 17, which is evenly divisble - * by all integers from 1 through 18 inclusive, and powers of 2 up to 2^12. - */ -static const uint32_t LV2_EVENT_PPQN = 3136573440U; - - -/** An LV2 event (header only). - * - * LV2 events are generic time-stamped containers for any type of event. - * The type field defines the format of a given event's contents. - * - * This struct defines the header of an LV2 event. An LV2 event, as specified - * in this extension, is a single chunk of POD (plain old data), usually - * contained in a flat buffer (see LV2_EventBuffer below), that can be safely - * copied using a simple: - * - * memcpy(ev_copy, ev, sizeof(LV2_Event) + ev->size); (or equivalent) - * - * However, events with event type 0 need to be handled specially (see below). - */ -typedef struct { - - /** The frames portion of timestamp. The units used here can optionally - * be set for a port (with lv2ev:supportsTimeStamp and related - * properties), otherwise this is audio frames, corresponding to the - * sample_count parameter of the LV2 run method (e.g. frame 0 is the - * first frame for that call to run). - */ - uint32_t frames; - - /** The sub-frames portion of timestamp. The units used here can - * optionally be set for a port (with lv2ev:supportsTimeStamp and - * related properties), otherwise this is 1/(2^32) of an audio frame. - */ - uint32_t subframes; - - /** The type of this event, as a number which represents some URI - * defining an event type. This value MUST be some value previously - * returned from a call to the uri_to_id function defined in the LV2 - * URI map extension (see lv2_uri_map.h). - * - * There are special rules which must be followed depending on the type - * of an event. If the plugin recognizes an event type, the definition - * of that event type will describe how to interpret the event, and - * any required behaviour. Otherwise, if the type is 0, this event is a - * non-POD event and lv2_event_unref MUST be called if the event is - * 'dropped' (see below). Even if the plugin does not understand an - * event, it may pass the event through to an output by simply copying - * (and NOT calling lv2_event_unref). These rules are designed to - * allow for generic event handling plugins and large non-POD events, - * but with minimal hassle on simple plugins that "don't care" about - * these more advanced features. - * - * Plugins should not interpret type 0 events in any way unless - * specified by another extension. - */ - uint16_t type; - - /** The size of the data portion of this event in bytes, which - * immediately follows. The header size (12 bytes) is not included in - * this value. - */ - uint16_t size; - - /* size bytes of data follow here */ - -} LV2_Event; - - - -/** A buffer of LV2 events (header only). - * - * This struct is used as the port buffer for LV2 plugin ports that have the - * port class lv2ev:EventPort. - * - * The data member points to a buffer that contains an event header (defined - * by struct* LV2_Event), followed by that event's contents (padded to 64 bits), - * followed by another header, etc: - * - * | | | | | | | - * | | | | | | | | | | | | | | | | | | | | | | | | | - * |FRAMES |SUBFRMS|TYP|LEN|DATA..DATA..PAD|FRAMES | ... - */ -typedef struct { - - /** The contents of the event buffer. This may or may not reside in the - * same block of memory as this header, plugins must not assume either. - * The host guarantees this points to at least capacity bytes of - * allocated memory (though only size bytes of that are valid events). - */ - uint8_t* data; - - /** The size of this event header in bytes (including everything). - * - * This is to allow for extending this header in the future without - * breaking binary compatibility. Whenever this header is copied, - * it MUST be done using this field (and NOT the sizeof this struct). - */ - uint16_t header_size; - - /** The type of the time stamps for events in this buffer. - * As a special exception, '0' always means audio frames and subframes - * (1/UINT32_MAX'th of a frame) in the sample rate passed to instantiate. - * INPUTS: The host must set this field to the numeric ID of some URI - * defining the meaning of the frames/subframes fields of contained - * events (obtained by the LV2 URI Map uri_to_id function with the URI - * of this extension as the 'map' argument, see lv2_uri_map.h). - * The host must never pass a plugin a buffer which uses a stamp type - * the plugin does not 'understand'. The value of this field must - * never change, except when connect_port is called on the input - * port, at which time the host MUST have set the stamp_type field to - * the value that will be used for all subsequent run calls. - * OUTPUTS: The plugin may set this to any value that has been returned - * from uri_to_id with the URI of this extension for a 'map' argument. - * When connected to a buffer with connect_port, output ports MUST set - * this field to the type of time stamp they will be writing. On any - * call to connect_port on an event input port, the plugin may change - * this field on any output port, it is the responsibility of the host - * to check if any of these values have changed and act accordingly. - */ - uint16_t stamp_type; - - /** The number of events in this buffer. - * INPUTS: The host must set this field to the number of events - * contained in the data buffer before calling run(). - * The plugin must not change this field. - * OUTPUTS: The plugin must set this field to the number of events it - * has written to the buffer before returning from run(). - * Any initial value should be ignored by the plugin. - */ - uint32_t event_count; - - /** The size of the data buffer in bytes. - * This is set by the host and must not be changed by the plugin. - * The host is allowed to change this between run() calls. - */ - uint32_t capacity; - - /** The size of the initial portion of the data buffer containing data. - * INPUTS: The host must set this field to the number of bytes used - * by all events it has written to the buffer (including headers) - * before calling the plugin's run(). - * The plugin must not change this field. - * OUTPUTS: The plugin must set this field to the number of bytes - * used by all events it has written to the buffer (including headers) - * before returning from run(). - * Any initial value should be ignored by the plugin. - */ - uint32_t size; - -} LV2_Event_Buffer; - - -/** Opaque pointer to host data. */ -typedef void* LV2_Event_Callback_Data; - - -/** The data field of the LV2_Feature for this extension. - * - * To support this extension the host must pass an LV2_Feature struct to the - * plugin's instantiate method with URI "http://lv2plug.in/ns/ext/event" - * and data pointed to an instance of this struct. The plugin does not have - * to list that URI as a required or optional feature in its RDF data - the - * host MUST pass this LV2_Feature if the plugin has an port of class - * lv2ev:EventPortthat the host connects to. - */ -typedef struct { - - /** Opaque pointer to host data. - * - * The plugin MUST pass this to any call to functions in this struct. - * Otherwise, it must not be interpreted in any way. - */ - LV2_Event_Callback_Data callback_data; - - /** Take a reference to a non-POD event. - * - * If a plugin receives an event with type 0, it means the event is a - * pointer to some object in memory and not a flat sequence of bytes - * in the buffer. When receiving a non-POD event, the plugin already - * has an implicit reference to the event. If the event is stored AND - * passed to an output, or passed to two outputs, lv2_event_ref MUST - * be called on that event. - * If the event is only stored OR passed through, this is not necessary - * (as the plugin already has 1 implicit reference). - * - * The host guarantees that this function is realtime safe if the - * plugin is. - * - * @param event An event received at an input that will be copied to - * more than one output or duplicated in some other way. - * - * PLUGINS THAT VIOLATE THESE RULES MAY CAUSE CRASHES AND MEMORY LEAKS. - */ - uint32_t (*lv2_event_ref)(LV2_Event_Callback_Data callback_data, - LV2_Event* event); - - /** Drop a reference to a non-POD event. - * - * If a plugin receives an event with type 0, it means the event is a - * pointer to some object in memory and not a flat sequence of bytes - * in the buffer. If the plugin does not pass the event through to - * an output or store it internally somehow, it MUST call this function - * on the event (more information on using non-POD events below). - * - * The host guarantees that this function is realtime safe if the - * plugin is. - * - * @param event An event received at an input that will not be copied to - * an output or stored in any way. - * - * PLUGINS THAT VIOLATE THESE RULES MAY CAUSE CRASHES AND MEMORY LEAKS. - */ - uint32_t (*lv2_event_unref)(LV2_Event_Callback_Data callback_data, - LV2_Event* event); - -} LV2_Event_Feature; - - -#endif // LV2_EVENT_H -
View file
linuxsampler-2342.tar.bz2/src/hostplugins/lv2/lv2_state.h
Deleted
@@ -1,359 +0,0 @@ -/* - Copyright 2010-2012 David Robillard <http://drobilla.net> - Copyright 2010 Leonard Ritter <paniq@paniq.org> - - Permission to use, copy, modify, and/or distribute this software for any - purpose with or without fee is hereby granted, provided that the above - copyright notice and this permission notice appear in all copies. - - THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -*/ - -/** - @file state.h - C API for the LV2 State extension <http://lv2plug.in/ns/ext/state>. -*/ - -#ifndef LV2_STATE_H -#define LV2_STATE_H - -#include <stddef.h> -#include <stdint.h> - -#include "lv2/lv2plug.in/ns/lv2core/lv2.h" - -#define LV2_STATE_URI "http://lv2plug.in/ns/ext/state" -#define LV2_STATE_PREFIX LV2_STATE_URI "#" - -#define LV2_STATE__State LV2_STATE_PREFIX "State" -#define LV2_STATE__interface LV2_STATE_PREFIX "interface" -#define LV2_STATE__makePath LV2_STATE_PREFIX "makePath" -#define LV2_STATE__mapPath LV2_STATE_PREFIX "mapPath" -#define LV2_STATE__state LV2_STATE_PREFIX "state" - -#ifdef __cplusplus -extern "C" { -#else -# include <stdbool.h> -#endif - -typedef void* LV2_State_Handle; -typedef void* LV2_State_Map_Path_Handle; -typedef void* LV2_State_Make_Path_Handle; - -/** - Flags describing value characteristics. - - These flags are used along with the value's type URI to determine how to - (de-)serialise the value data, or whether it is even possible to do so. -*/ -typedef enum { - - /** - Plain Old Data. - - Values with this flag contain no pointers or references to other areas - of memory. It is safe to copy POD values with a simple memcpy and store - them for the duration of the process. A POD value is not necessarily - safe to trasmit between processes or machines (e.g. filenames are POD), - see LV2_STATE_IS_PORTABLE for details. - - Implementations MUST NOT attempt to copy or serialise a non-POD value if - they do not understand its type (and thus know how to correctly do so). - */ - LV2_STATE_IS_POD = 1, - - /** - Portable (architecture independent) data. - - Values with this flag are in a format that is usable on any - architecture. A portable value saved on one machine can be restored on - another machine regardless of architecture. The format of portable - values MUST NOT depend on architecture-specific properties like - endianness or alignment. Portable values MUST NOT contain filenames. - */ - LV2_STATE_IS_PORTABLE = 1 << 1, - - /** - Native data. - - This flag is used by the host to indicate that the saved data is only - going to be used locally in the currently running process (e.g. for - instance duplication or snapshots), so the plugin should use the most - efficient representation possible and not worry about serialisation - and portability. - */ - LV2_STATE_IS_NATIVE = 1 << 2 - -} LV2_State_Flags; - -/** A status code for state functions. */ -typedef enum { - LV2_STATE_SUCCESS = 0, /**< Completed successfully. */ - LV2_STATE_ERR_UNKNOWN = 1, /**< Unknown error. */ - LV2_STATE_ERR_BAD_TYPE = 2, /**< Failed due to unsupported type. */ - LV2_STATE_ERR_BAD_FLAGS = 3, /**< Failed due to unsupported flags. */ - LV2_STATE_ERR_NO_FEATURE = 4, /**< Failed due to missing features. */ - LV2_STATE_ERR_NO_PROPERTY = 5 /**< Failed due to missing property. */ -} LV2_State_Status; - -/** - A host-provided function to store a property. - @param handle Must be the handle passed to LV2_State_Interface.save(). - @param key The key to store @p value under (URID). - @param value Pointer to the value to be stored. - @param size The size of @p value in bytes. - @param type The type of @p value (URID). - @param flags LV2_State_Flags for @p value. - @return 0 on success, otherwise a non-zero error code. - - The host passes a callback of this type to LV2_State_Interface.save(). This - callback is called repeatedly by the plugin to store all the properties that - describe its current state. - - DO NOT INVENT NONSENSE URI SCHEMES FOR THE KEY. Best is to use keys from - existing vocabularies. If nothing appropriate is available, use http URIs - that point to somewhere you can host documents so documentation can be made - resolvable (e.g. a child of the plugin or project URI). If this is not - possible, invent a URN scheme, e.g. urn:myproj:whatever. The plugin MUST - NOT pass an invalid URI key. - - The host MAY fail to store a property for whatever reason, but SHOULD - store any property that is LV2_STATE_IS_POD and LV2_STATE_IS_PORTABLE. - Implementations SHOULD use the types from the LV2 Atom extension - (http://lv2plug.in/ns/ext/atom) wherever possible. The plugin SHOULD - attempt to fall-back and avoid the error if possible. - - Note that @p size MUST be > 0, and @p value MUST point to a valid region of - memory @p size bytes long (this is required to make restore unambiguous). - - The plugin MUST NOT attempt to use this function outside of the - LV2_State_Interface.restore() context. -*/ -typedef LV2_State_Status (*LV2_State_Store_Function)( - LV2_State_Handle handle, - uint32_t key, - const void* value, - size_t size, - uint32_t type, - uint32_t flags); - -/** - A host-provided function to retrieve a property. - @param handle Must be the handle passed to LV2_State_Interface.restore(). - @param key The key of the property to retrieve (URID). - @param size (Output) If non-NULL, set to the size of the restored value. - @param type (Output) If non-NULL, set to the type of the restored value. - @param flags (Output) If non-NULL, set to the flags for the restored value. - @return A pointer to the restored value (object), or NULL if no value - has been stored under @p key. - - A callback of this type is passed by the host to - LV2_State_Interface.restore(). This callback is called repeatedly by the - plugin to retrieve any properties it requires to restore its state. - - The returned value MUST remain valid until LV2_State_Interface.restore() - returns. The plugin MUST NOT attempt to use this function, or any value - returned from it, outside of the LV2_State_Interface.restore() context. -*/ -typedef const void* (*LV2_State_Retrieve_Function)( - LV2_State_Handle handle, - uint32_t key, - size_t* size, - uint32_t* type, - uint32_t* flags); - -/** - LV2 Plugin State Interface. - - When the plugin's extension_data is called with argument - LV2_STATE__interface, the plugin MUST return an LV2_State_Interface - structure, which remains valid for the lifetime of the plugin. - - The host can use the contained function pointers to save and restore the - state of a plugin instance at any time, provided the threading restrictions - of the functions are met. - - Stored data is only guaranteed to be compatible between instances of plugins - with the same URI (i.e. if a change to a plugin would cause a fatal error - when restoring state saved by a previous version of that plugin, the plugin - URI MUST change just as it must when ports change incompatibly). Plugin - authors should consider this possibility, and always store sensible data - with meaningful types to avoid such problems in the future. -*/ -typedef struct _LV2_State_Interface { - - /** - Save plugin state using a host-provided @p store callback. - - @param instance The instance handle of the plugin. - @param store The host-provided store callback. - @param handle An opaque pointer to host data which MUST be passed as the - handle parameter to @p store if it is called. - @param flags Flags describing desired properties of this save. These - flags may be used to determine the most appropriate values to store. - @param features Extensible parameter for passing any additional - features to be used for this save. - - The plugin is expected to store everything necessary to completely - restore its state later. Plugins SHOULD store simple POD data whenever - possible, and consider the possibility of state being restored much - later on a different machine. - - The @p handle pointer and @p store function MUST NOT be used - beyond the scope of save(). - - This function has its own special threading class: it may not be called - concurrently with any "Instantiation" function, but it may be called - concurrently with functions in any other class, unless the definition of - that class prohibits it (e.g. it may not be called concurrently with a - "Discovery" function, but it may be called concurrently with an "Audio" - function. The plugin is responsible for any locking or lock-free - techniques necessary to make this possible. - - Note that in the simple case where state is only modified by restore(), - there are no synchronization issues since save() is never called - concurrently with restore() (though run() may read it during a save). - - Plugins that dynamically modify state while running, however, must take - care to do so in such a way that a concurrent call to save() will save a - consistent representation of plugin state for a single instant in time. - */ - LV2_State_Status (*save)(LV2_Handle instance, - LV2_State_Store_Function store, - LV2_State_Handle handle, - uint32_t flags, - const LV2_Feature *const * features); - - /** - Restore plugin state using a host-provided @p retrieve callback. - - @param instance The instance handle of the plugin. - @param retrieve The host-provided retrieve callback. - @param handle An opaque pointer to host data which MUST be passed as the - handle parameter to @p retrieve if it is called. - @param flags Currently unused. - @param features Extensible parameter for passing any additional - features to be used for this restore. - - The plugin MAY assume a restored value was set by a previous call to - LV2_State_Interface.save() by a plugin with the same URI. - - The plugin MUST gracefully fall back to a default value when a value can - not be retrieved. This allows the host to reset the plugin state with - an empty map. - - The @p handle pointer and @p store function MUST NOT be used - beyond the scope of restore(). - - This function is in the "Instantiation" threading class as defined by - LV2. This means it MUST NOT be called concurrently with any other - function on the same plugin instance. - */ - LV2_State_Status (*restore)(LV2_Handle instance, - LV2_State_Retrieve_Function retrieve, - LV2_State_Handle handle, - uint32_t flags, - const LV2_Feature *const * features); - -} LV2_State_Interface; - -/** - Feature data for state:mapPath (LV2_STATE__mapPath). -*/ -typedef struct { - - /** - Opaque host data. - */ - LV2_State_Map_Path_Handle handle; - - /** - Map an absolute path to an abstract path for use in plugin state. - @param handle MUST be the @p handle member of this struct. - @param absolute_path The absolute path of a file. - @return An abstract path suitable for use in plugin state. - - The plugin MUST use this function to map any paths that will be stored - in plugin state. The returned value is an abstract path which MAY not - be an actual file system path; @ref absolute_path() MUST be used to map - it to an actual path in order to use the file. - - Plugins MUST NOT make any assumptions about abstract paths except that - they can be mapped back to the absolute path of the "same" file (though - not necessarily the same original path) using @ref absolute_path(). - - This function may only be called within the context of - LV2_State_Interface methods. The caller is responsible for freeing the - returned value with free(). - */ - char* (*abstract_path)(LV2_State_Map_Path_Handle handle, - const char* absolute_path); - - /** - Map an abstract path from plugin state to an absolute path. - @param handle MUST be the @p handle member of this struct. - @param abstract_path An abstract path (e.g. a path from plugin state). - @return An absolute file system path. - - The plugin MUST use this function in order to actually open or otherwise - use any paths loaded from plugin state. - - This function may only be called within the context of - LV2_State_Interface methods. The caller is responsible for freeing the - returned value with free(). - */ - char* (*absolute_path)(LV2_State_Map_Path_Handle handle, - const char* abstract_path); - -} LV2_State_Map_Path; - -/** - Feature data for state:makePath (@ref LV2_STATE__makePath). -*/ -typedef struct { - - /** - Opaque host data. - */ - LV2_State_Make_Path_Handle handle; - - /** - Return a path the plugin may use to create a new file. - @param handle MUST be the @p handle member of this struct. - @param path The path of the new file within a namespace unique to this - plugin instance. - @return The absolute path to use for the new file. - - This function can be used by plugins to create files and directories, - either at state saving time (if this feature is passed to - LV2_State_Interface.save()) or any time (if this feature is passed to - LV2_Descriptor.instantiate()). - - The host MUST do whatever is necessary for the plugin to be able to - create a file at the returned path (e.g. using fopen), including - creating any leading directories. - - If this function is passed to LV2_Descriptor.instantiate(), it may be - called from any non-realtime context. If it is passed to - LV2_State_Interface.save(), it may only be called within the dynamic - scope of that function call. - - The caller is responsible for freeing the returned value with free(). - */ - char* (*path)(LV2_State_Make_Path_Handle handle, - const char* path); - -} LV2_State_Make_Path; - -#ifdef __cplusplus -} /* extern "C" */ -#endif - -#endif /* LV2_STATE_H */
View file
linuxsampler-2342.tar.bz2/src/hostplugins/lv2/lv2_uri_map.h
Deleted
@@ -1,87 +0,0 @@ -/* lv2_uri_map.h - C header file for the LV2 URI Map extension. - * - * Copyright (C) 2008-2009 David Robillard <http://drobilla.net> - * - * This header is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published - * by the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This header 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 Lesser General Public - * License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this header; if not, write to the Free Software Foundation, - * Inc., 59 Temple Place, Suite 330, Boston, MA 01222-1307 USA - */ - -/** @file - * C header for the LV2 URI Map extension <http://lv2plug.in/ns/ext/uri-map>. - * - * This extension defines a simple mechanism for plugins to map URIs to - * integers, usually for performance reasons (e.g. processing events - * typed by URIs in real time). The expected use case is for plugins to - * map URIs to integers for things they 'understand' at instantiation time, - * and store those values for use in the audio thread without doing any string - * comparison. This allows the extensibility of RDF with the performance of - * integers (or centrally defined enumerations). - */ - -#ifndef LV2_URI_MAP_H -#define LV2_URI_MAP_H - -#define LV2_URI_MAP_URI "http://lv2plug.in/ns/ext/uri-map" - -#include <stdint.h> - - -/** Opaque pointer to host data. */ -typedef void* LV2_URI_Map_Callback_Data; - - -/** The data field of the LV2_Feature for this extension. - * - * To support this feature the host must pass an LV2_Feature struct to the - * plugin's instantiate method with URI "http://lv2plug.in/ns/ext/uri-map" - * and data pointed to an instance of this struct. - */ -typedef struct { - - /** Opaque pointer to host data. - * - * The plugin MUST pass this to any call to functions in this struct. - * Otherwise, it must not be interpreted in any way. - */ - LV2_URI_Map_Callback_Data callback_data; - - /** Get the numeric ID of a URI from the host. - * - * @param callback_data Must be the callback_data member of this struct. - * @param map The 'context' of this URI. Certain extensions may define a - * URI that must be passed here with certain restrictions on the - * return value (e.g. limited range). This value may be NULL if - * the plugin needs an ID for a URI in general. - * @param uri The URI to be mapped to an integer ID. - * - * This function is referentially transparent - any number of calls with - * the same arguments is guaranteed to return the same value over the life - * of a plugin instance (though the same URI may return different values - * with a different map parameter). However, this function is not - * necessarily very fast: plugins should cache any IDs they might need in - * performance critical situations. - * The return value 0 is reserved and means an ID for that URI could not - * be created for whatever reason. Extensions may define more precisely - * what this means, but in general plugins should gracefully handle 0 - * and consider whatever they wanted the URI for "unsupported". - */ - uint32_t (*uri_to_id)(LV2_URI_Map_Callback_Data callback_data, - const char* map, - const char* uri); - -} LV2_URI_Map_Feature; - - -#endif /* LV2_URI_MAP_H */ -
View file
linuxsampler-2342.tar.bz2/src/hostplugins/lv2/manifest.ttl
Deleted
@@ -1,7 +0,0 @@ -@prefix lv2: <http://lv2plug.in/ns/lv2core#> . -@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> . - -<http://linuxsampler.org/plugins/linuxsampler> - a lv2:Plugin ; - lv2:binary <linuxsampler.so> ; - rdfs:seeAlso <linuxsampler.ttl> .
View file
linuxsampler-2342.tar.bz2/ChangeLog -> linuxsampler-2718.tar.bz2/ChangeLog
Changed
@@ -1,8 +1,5 @@ Version CVS HEAD (?) - * plugin changes: - - LV2 "state" extension support (patch by David Robillard) - * packaging changes: - fixed building with newer MinGW-w64 - Mac OS X: support the new dir for Core Audio SDK @@ -19,6 +16,27 @@ configure - Mac OS X: Makefile fix for the install-strip target - fixed compilation with gcc 4.7 + - fixed configure script error with old autoconf versions + - lsatomic.h: use gcc provided atomic functions if building with + gcc 4.7 and C++11 + - modernized configure script + - fixed linkage error when building with + LDFLAGS="-Wl,--no-undefined" (#190) + - fixed compilation with Clang 3.2 + - removed usage of deprecated Automake variable INCLUDES + - fixed building with C++11 + - build fix: ChangeFlagRelaxed.h was missing in makefile + - build fix: libsndfile compiler flags were missing in some + makefiles + - fix for building with bison 3.0 (#202) + - Mac OS X: added temporary hack allowing to spawn gigedit as callback + on the process's main thread + - fixed build error on newer MinGW + - support building with older jack versions + - support building with spaces in vst sdk path + - enabled automake 'subdir-objects' option and moved external + source references (vst, au, asio) from makefiles to cpp files, + in order to get rid of warnings from automake 1.14 * general changes: - Refactoring: moved the independent code from @@ -51,6 +69,44 @@ instrument on an engine channel (a.k.a. part). Some GigaStudio instruments for example are splitted over several files like "Foo.gig", "Foo.gx01", "Foo.gx02", ... + - Added new C++ API method Sampler::GetGlobalMaxVoices(). + - Added new C++ API method Sampler::GetGlobalMaxStreams(). + - Added new C++ API method Sampler::SetGlobalMaxVoices(). + - Added new C++ API method Sampler::SetGlobalMaxStreams(). + - Various "const" and "restrict" optimizations. + - all engines: add pan CC value to instrument pan parameter before + applying panning, instead of using two separate pan functions in + series (#182) + - added a lock guard class for exception safe mutex handling and + used it everywhere appropriate + - Immediately apply scale tuning changes to active voices. + - Exposed scale tuning to C++ API (along to the already existing standard + SysEx way). + - lsatomic.h fixes: seq_cst load and store were suboptimal for x86 + and broken for ppc64. (Seq_cst loads and stores are actually not + used in LS, so the bug wasn't noticable.) + - lsatomic.h: added ARMv7 support + - Added support for multiple MIDI input ports per sampler channel (and + added various new C++ methods for this new feature / design change, old + C++ API methods for managing SamplerChannel's MIDI inputs are now marked + as deprecated but are still there and should provide full behavior + backward compatibility). + - AbstractEngine::GSChecksum(): don't allocate memory on the stack (was + unsafe and caused compilation error with clang 2.x). + - Bugfix: only process the latest MIDI program change event. + - Introducing the LSCP shell, which provides convenient control of the + sampler from the command line by providing LSCP aware features. + - VirtualMidiDevice: Added support for program change. + - VirtualMidiDevice: Added support for bank select (MSB & LSB). + - VirtualMidiDevice: Added support for pitch bend. + - Aftertouch: extended API to explicitly handle channel pressure and + polyphonic key pressure events (so far polyphonic pressure was not + supported at all, and channel pressure was rerouted as CC128 but not + used so far). + - Added initial support for real-time instrument scripts. The script VM + code is shared by all sampler engine implemementations, however only the + gig file format currently provides support for storing instrument scripts + (as LinuxSampler extension to the original GigaStudio 4 format). * Gigasampler format engine: - implemented the "round robin keyboard" dimension @@ -58,6 +114,19 @@ dimension zones is not a power of two - made round robin use a counter for each region instead of each key + - bugfix: pitch LFO controller "internal+aftertouch" was broken + - bugfix: filter keyboard tracking was broken + - filter performance fix (an unnecessary copy was made of the + filter parameters in each sub fragment) + - handle special case when pan parameter in gig file has max or + min value + - Exclusive Groups: don't ever stop voices of the same note, + doesn't sound naturally with a drumkit + - fixed EG1 modulation when attack or release is zero + - Fixed support for 'aftertouch' attenuation controller. + - Fixed crash that happened with velocity split sounds under certain + conditions (see also previous commit on libgig). + - fixed behaviour of filter LFO * SFZ format engine: - Initial implementation (not usable yet) @@ -212,6 +281,17 @@ group and off_by opcodes (#168) - made end=0 play the whole sample - fixed support for lochan and hichan opcodes (#155) + - fixed crash when using lochan/hichan opcodes (#187) + - sfz parser: allow -200 to 200 for pan_oncc opcode (#182) + - added FLAC support (#191) + - sfz parser bugfix: lines starting with whitespace were ignored + - added amplitude opcode + - added support for "#include" instruction + (modified patch which was originally posted by Sergey on LS mailing list) + - bugfix: generation of velocity curves etc should not be done + after each "#include", only after the main file is parsed + - bugfix: line numbers in error messages were wrong after "#include" + - added support for float and 32 bit sample files * SoundFont format engine: - Initial implementation (not usable yet) @@ -222,13 +302,7 @@ - initial implementation of cutoff filter - use linear decay and release for filter and pitch EG - * Gigasampler format engine: - - bugfix: pitch LFO controller "internal+aftertouch" was broken - - bugfix: filter keyboard tracking was broken - - filter performance fix (an unnecessary copy was made of the - filter parameters in each sub fragment) - - * Host plugins: + * Host plugins (VST, AU, LV2, DSSI): - AU bugfix: failed to destroy its audio/MIDI devices - Listen to all interfaces on Mac OS X (INADDR_ANY) - VST bugfix: If the host called resume() before and after @@ -242,12 +316,34 @@ - VST: made it possible to build the VST plugin for Mac - AU: link AU plugin dynamically if --disable-shared isn't specified + - LV2 "state" extension support (patch by David Robillard) - VST bugfix: instrument loading hang and crashed the host when the plugin was loaded a second time (#174) - plugin bugfix: instrument loading hang when the plugin was loaded a second time (this time it's for Linux and Mac, previous similar fix was for Windows) - thread safety fixes for the instrument loading thread + - LV2: use the new lv2 package if present + - VST: try to open Fantasia automatically on Linux and Mac too (on + Linux, the Fantasia jar should be placed in <prefix>/share/java) + - VST: fixed crashes on Linux Ardour and EnergyXT + - DSSI bugfix: it wasn't possible to change engine type. The MIDI + port and audio channel routing for DSSI plugins are now visible. + - LV2: use urid and atom extensions instead of deprecated uri-map + and event + - LV2: lv2 package 1.0 is now required to build the LV2 plugin + - LV2: changed number of output channels to 16 stereo, just like + the VST and AU plugins + - LV2: fixed save/restore of SFZ state (patch by David Robillard) + - LV2: made LV2 plugin buildable on Windows and Mac + - VST: implemented retrieval and switching of programs using the + sampler's internal MIDI instrument mapping system + + * Instrument editor interface: + - Changed instrument editor plugin interface, providing additional + informations like the EngineChannel for which the instrument editor was + spawned for. This allows the instrument editors to interact more actively + with the sampler. * MIDI driver: - ALSA MIDI driver supports now "NAME" device parameter, for overriding @@ -256,9 +352,34 @@ there is no reason for this limit - MME: fixed memory handling bug found with cppcheck - MME: removed compiler warning + - CoreMIDI: implemented driver specific port parameter "CORE_MIDI_BINDINGS", + which allows to retrieve the list of CoreMIDI clients / ports and to connect + to them a la JACK, via the usual sampler APIs + - CoreMIDI: added driver specific port parameter "AUTO_BIND", if enabled + the driver will automatically connect to other CoreMIDI clients' ports + (e.g. external MIDI devices being attached to the Mac) + - added support for MIDI note on velocity filter + - CoreMIDI: fixed memory deallocation error + - Fixed variable underflow in VirtualMidiDevice, which caused graphical + virtual keyboards in frontends / instrument editors being stuck. + - Bugfix in VirtualMidiDevice: process note on with velocity 0 as note off. + - Implemented missing handling of MIDI "running status". + - CoreMIDI fix: a MIDIPacket can contain more than one event per packet. + - MME bugfix: driver wasn't closed properly + - CoreMIDI: automatically connect to all input sources by default (driver + parameter "AUTO_BIND"). * audio driver: - ASIO driver fixes for newer gcc versions (fix from PortAudio) + - JACK audio: react on sample rate changes. + - JACK audio: react on buffer size changes. + - JACK audio: jack_port_get_buffer() was cached and called outside + RT context. + - ASIO driver: removed compiler warnings + - CoreAudio: fixed minor error handling bug + - ASIO driver: be more verbose when no ASIO card could be found (fixes #203) + - JACK audio: return the JACK server's current sample rate as default value + for audio device parameter "SAMPLERATE" (fixes #166). * LSCP server: - added support for sending MIDI CC messages via LSCP command @@ -296,6 +417,71 @@ - added LSCP commands "SUBSCRIBE EFFECT_INSTANCE_COUNT", "SUBSCRIBE EFFECT_INSTANCE_INFO", "SUBSCRIBE SEND_EFFECT_CHAIN_COUNT", "SUBSCRIBE SEND_EFFECT_CHAIN_INFO" + - provide comprehensive error messages on LSCP syntax errors + (suggesting expected next non-terminal symbols) + + * LSCP shell: + - Added support for auto correction of obvious and trivial syntax mistakes. + - Added support for auto completion by tab key. + - Show currently available auto completion while typing. + - Added support for browsing command history with up / down keys. + - Show all possible next symbols immediately right to the current command + line while typing (no double tab required for this feature, as it would + be the case in other shells). + - Added support for moving cursor left/right with arrow keys. + - Added support for built-in LSCP reference documentation, which will + automatically show the relevant LSCP reference section on screen as soon + as one specific LSCP command was detected while typing on the command + line. + + * Real-time instrument scripts: + - Implemented built-in script array variable %CC. + - Implemented built-in script int variable $CC_NUM. + - Implemented built-in script int variable $EVENT_NOTE. + - Implemented built-in script int variable $EVENT_VELOCITY. + - Implemented built-in script constant variable $VCC_MONO_AT. + - Implemented built-in script constant variable $VCC_PITCH_BEND. + - Implemented execution of script event handler "init". + - Implemented execution of script event handler "controller". + - Implemented execution of script event handler "note". + - Implemented execution of script event handler "release". + - Implemented built-in script function "play_note()" (only two of the + max. four function arguments are currently implemented yet though). + - Implemented built-in script int variable $EVENT_ID. + - Implemented built-in script function "ignore_event()" + - Implemented built-in script function "ignore_controller()" (may have one + or no argument). + - Implemented built-in script function "set_controller()". + - Added extended script VM for the Gigasampler/GigaStudio format sampler + engine, which extends the general instrument script VM with Giga format + specific variables and functions. + - Giga format scripts: added built-in script int constant variables + $GIG_DIM_CHANNEL, $GIG_DIM_LAYER, $GIG_DIM_VELOCITY, $GIG_DIM_AFTERTOUCH, + $GIG_DIM_RELEASE, $GIG_DIM_KEYBOARD, $GIG_DIM_ROUNDROBIN, $GIG_DIM_RANDOM, + $GIG_DIM_SMARTMIDI, $GIG_DIM_ROUNDROBINKEY, $GIG_DIM_MODWHEEL, + $GIG_DIM_BREATH, $GIG_DIM_FOOT, $GIG_DIM_PORTAMENTOTIME, $GIG_DIM_EFFECT1, + $GIG_DIM_EFFECT2, $GIG_DIM_GENPURPOSE1, $GIG_DIM_GENPURPOSE2, + $GIG_DIM_GENPURPOSE3, $GIG_DIM_GENPURPOSE4, $GIG_DIM_SUSTAIN, + $GIG_DIM_PORTAMENTO, $GIG_DIM_SOSTENUTO, $GIG_DIM_SOFT, + $GIG_DIM_GENPURPOSE5, $GIG_DIM_GENPURPOSE6, $GIG_DIM_GENPURPOSE7, + $GIG_DIM_GENPURPOSE8, $GIG_DIM_EFFECT1DEPTH, $GIG_DIM_EFFECT2DEPTH, + $GIG_DIM_EFFECT3DEPTH, $GIG_DIM_EFFECT4DEPTH, $GIG_DIM_EFFECT5DEPTH. + - Giga format scripts: Implemented built-in script function + "gig_set_dim_zone(event_id, dimension, zone)". + - Implemented built-in script int array variable $KEY_DOWN. + - Implemented built-in script function "abs()". + - Implemented built-in script function "random()". + - Implemented built-in script function "num_elements()". + - Implemented built-in script function "note_off()". + - Implemented built-in script function "set_event_mark()". + - Implemented built-in script function "delete_event_mark()". + - Implemented built-in script function "by_marks()". + - Added built-in script int const variables $MARK_1 to $MARK_28. + - Built-in script functions "ignore_event()", "note_off()" and + "gig_set_dim_zone()" now also accept an array of event IDs as argument + (i.e. return value of new script function "by_marks()"). + - Pass/preserve polyphonic variable data from respective "note" event + handler to "release" event handler. * Bug fixes: - Fixed crash which may occur when MIDI key + transpose is out of range @@ -332,6 +518,23 @@ (#176) - more thread safety fixes for the instrument loading thread - sfz/sf2 engine: fixed crash when using small audio fragment size + - Mac OS X: fixed crash when unloading plugin on 10.7 and later + - Mac OS X: fixed process hang when unloading 32-bit plugin (bug + introduced in previous fix) + - fixed crash when a channel received a program change while + playing a note in a key group + - fixed erroneous error message piping in VoiceBase.h + (labelled "Disk stream not available in time") + - Update effects on sample rate & period size changes (to avoid + crashes and noise on such transitions). + - ignore missing LADSPA paths without ignoring valid LADSPA paths + (fixes #208) + - Fixed bug in LSCP grammar definition which caused a statement like + "GET SERVER INFOasdf\n" to be accepted as valid statement (was so far + practically irrelevant, however it caused problems with the new LSCP + shell's auto completion feature). + - Fixed MIDI program change messages being ignored if quickly executed + after each other (fixes #231). Version 1.0.0 (31 July 2009) @@ -396,6 +599,7 @@ - Windows: add the installation directory to the DLL search path when loading an editor plugin (solves problems with VST and gigedit on systems with other GTK versions installed) + - updated linuxsampler man page * audio driver: - removed the nonsense audio channel constraint (which was hard coded to
View file
linuxsampler-2342.tar.bz2/Documentation/lscp.xml -> linuxsampler-2718.tar.bz2/Documentation/lscp.xml
Changed
@@ -16,25 +16,27 @@ to an annoying "missing Normative/Informative References" error message --> <?rfc strict="no" ?> -<rfc category="std" ipr="full3978" docName="LSCP 1.5"> +<rfc category="std" ipr="full3978" docName="LSCP 1.6"> <front> <title>LinuxSampler Control Protocol</title> <author initials='C.S.' surname="Schoenebeck" fullname='C. Schoenebeck'> <organization> - Interessengemeinschaft Software Engineering e. V. + LinuxSampler.org </organization> <address> <postal> - <street>Max-Planck-Str. 39</street> + <street>Crudebyte Engineering</street> + <street>Hofgartenstr. 3</street> <!-- <code>74081</code> --> - <city>74081 Heilbronn</city> + <city>74189 Weinsberg</city> <country>Germany</country> </postal> - <email>schoenebeck at software minus engineering dot org</email> + <phone>+49 7134 911614</phone> + <email>cuse@users.sf.net</email> </address> </author> - <date month="June" year="2011"/> + <date month="January" year="2014"/> <workgroup>LinuxSampler Developers</workgroup> <keyword>LSCP</keyword> <abstract> @@ -514,7 +516,7 @@ what parameters drivers are offering, how to retrieve their possible values, etc.</t> - <section title="Getting amount of available audio output drivers" anchor="GET AVAILABLE_AUDIO_OUTPUT_DRIVERS"> + <section title="Getting amount of available audio output drivers" anchor="GET AVAILABLE_AUDIO_OUTPUT_DRIVERS" lscp_cmd="true"> <t>Use the following command to get the number of audio output drivers currently available for the LinuxSampler instance:</t> @@ -539,7 +541,7 @@ </t> </section> - <section title="Getting all available audio output drivers" anchor="LIST AVAILABLE_AUDIO_OUTPUT_DRIVERS"> + <section title="Getting all available audio output drivers" anchor="LIST AVAILABLE_AUDIO_OUTPUT_DRIVERS" lscp_cmd="true"> <t>Use the following command to list all audio output drivers currently available for the LinuxSampler instance:</t> @@ -566,7 +568,7 @@ </section> <section title="Getting information about a specific audio - output driver" anchor="GET AUDIO_OUTPUT_DRIVER INFO"> + output driver" anchor="GET AUDIO_OUTPUT_DRIVER INFO" lscp_cmd="true"> <t>Use the following command to get detailed information about a specific audio output driver:</t> <t> @@ -639,7 +641,7 @@ </section> <section title="Getting information about specific audio - output driver parameter" anchor="GET AUDIO_OUTPUT_DRIVER_PARAMETER INFO"> + output driver parameter" anchor="GET AUDIO_OUTPUT_DRIVER_PARAMETER INFO" lscp_cmd="true"> <t>Use the following command to get detailed information about a specific audio output driver parameter:</t> <t> @@ -827,7 +829,7 @@ </t> </section> - <section title="Creating an audio output device" anchor="CREATE AUDIO_OUTPUT_DEVICE"> + <section title="Creating an audio output device" anchor="CREATE AUDIO_OUTPUT_DEVICE" lscp_cmd="true"> <t>Use the following command to create a new audio output device for the desired audio output system:</t> <t> @@ -887,7 +889,7 @@ </t> </section> - <section title="Destroying an audio output device" anchor="DESTROY AUDIO_OUTPUT_DEVICE"> + <section title="Destroying an audio output device" anchor="DESTROY AUDIO_OUTPUT_DEVICE" lscp_cmd="true"> <t>Use the following command to destroy a created output device:</t> <t> <list> @@ -956,7 +958,7 @@ </t> </section> - <section title="Getting all created audio output device list" anchor="LIST AUDIO_OUTPUT_DEVICES"> + <section title="Getting all created audio output device list" anchor="LIST AUDIO_OUTPUT_DEVICES" lscp_cmd="true"> <t>Use the following command to list all created audio output devices:</t> <t> <list> @@ -979,7 +981,7 @@ </t> </section> - <section title="Getting current settings of an audio output device" anchor="GET AUDIO_OUTPUT_DEVICE INFO"> + <section title="Getting current settings of an audio output device" anchor="GET AUDIO_OUTPUT_DEVICE INFO" lscp_cmd="true"> <t>Use the following command to get current settings of a specific, created audio output device:</t> <t> <list> @@ -1052,7 +1054,7 @@ </section> - <section title="Changing settings of audio output devices" anchor="SET AUDIO_OUTPUT_DEVICE_PARAMETER"> + <section title="Changing settings of audio output devices" anchor="SET AUDIO_OUTPUT_DEVICE_PARAMETER" lscp_cmd="true"> <t>Use the following command to alter a specific setting of a created audio output device:</t> <t> <list> @@ -1097,7 +1099,7 @@ </t> </section> - <section title="Getting information about an audio channel" anchor="GET AUDIO_OUTPUT_CHANNEL INFO"> + <section title="Getting information about an audio channel" anchor="GET AUDIO_OUTPUT_CHANNEL INFO" lscp_cmd="true"> <t>Use the following command to get information about an audio channel:</t> <t> <list> @@ -1197,7 +1199,7 @@ </t> </section> - <section title="Getting information about specific audio channel parameter" anchor="GET AUDIO_OUTPUT_CHANNEL_PARAMETER INFO"> + <section title="Getting information about specific audio channel parameter" anchor="GET AUDIO_OUTPUT_CHANNEL_PARAMETER INFO" lscp_cmd="true"> <t>Use the following command to get detailed information about specific audio channel parameter:</t> <t> @@ -1303,7 +1305,7 @@ </t> </section> - <section title="Changing settings of audio output channels" anchor="SET AUDIO_OUTPUT_CHANNEL_PARAMETER"> + <section title="Changing settings of audio output channels" anchor="SET AUDIO_OUTPUT_CHANNEL_PARAMETER" lscp_cmd="true"> <t>Use the following command to alter a specific setting of an audio output channel:</t> <t> <list> @@ -1381,7 +1383,7 @@ showing how to retrieve what parameters drivers are offering, how to retrieve their possible values, etc.</t> - <section title="Getting amount of available MIDI input drivers" anchor="GET AVAILABLE_MIDI_INPUT_DRIVERS"> + <section title="Getting amount of available MIDI input drivers" anchor="GET AVAILABLE_MIDI_INPUT_DRIVERS" lscp_cmd="true"> <t>Use the following command to get the number of MIDI input drivers currently available for the LinuxSampler instance:</t> @@ -1406,7 +1408,7 @@ </t> </section> - <section title="Getting all available MIDI input drivers" anchor="LIST AVAILABLE_MIDI_INPUT_DRIVERS"> + <section title="Getting all available MIDI input drivers" anchor="LIST AVAILABLE_MIDI_INPUT_DRIVERS" lscp_cmd="true"> <t>Use the following command to list all MIDI input drivers currently available for the LinuxSampler instance:</t> <t> @@ -1430,7 +1432,7 @@ </t> </section> - <section title="Getting information about a specific MIDI input driver" anchor="GET MIDI_INPUT_DRIVER INFO"> + <section title="Getting information about a specific MIDI input driver" anchor="GET MIDI_INPUT_DRIVER INFO" lscp_cmd="true"> <t>Use the following command to get detailed information about a specific MIDI input driver:</t> <t> <list> @@ -1486,7 +1488,7 @@ </t> </section> - <section title="Getting information about specific MIDI input driver parameter" anchor="GET MIDI_INPUT_DRIVER_PARAMETER INFO"> + <section title="Getting information about specific MIDI input driver parameter" anchor="GET MIDI_INPUT_DRIVER_PARAMETER INFO" lscp_cmd="true"> <t>Use the following command to get detailed information about a specific parameter of a specific MIDI input driver:</t> <t> <list> @@ -1641,7 +1643,7 @@ </t> </section> - <section title="Creating a MIDI input device" anchor="CREATE MIDI_INPUT_DEVICE"> + <section title="Creating a MIDI input device" anchor="CREATE MIDI_INPUT_DEVICE" lscp_cmd="true"> <t>Use the following command to create a new MIDI input device for the desired MIDI input system:</t> <t> <list> @@ -1691,7 +1693,7 @@ </t> </section> - <section title="Destroying a MIDI input device" anchor="DESTROY MIDI_INPUT_DEVICE"> + <section title="Destroying a MIDI input device" anchor="DESTROY MIDI_INPUT_DEVICE" lscp_cmd="true"> <t>Use the following command to destroy a created MIDI input device:</t> <t> <list> @@ -1733,7 +1735,7 @@ </t> </section> - <section title="Getting all created MIDI input device count" anchor="GET MIDI_INPUT_DEVICES"> + <section title="Getting all created MIDI input device count" anchor="GET MIDI_INPUT_DEVICES" lscp_cmd="true"> <t>Use the following command to count all created MIDI input devices:</t> <t> <list> @@ -1757,7 +1759,7 @@ </section> - <section title="Getting all created MIDI input device list" anchor="LIST MIDI_INPUT_DEVICES"> + <section title="Getting all created MIDI input device list" anchor="LIST MIDI_INPUT_DEVICES" lscp_cmd="true"> <t>Use the following command to list all created MIDI input devices:</t> <t> <list> @@ -1786,7 +1788,7 @@ </t> </section> - <section title="Getting current settings of a MIDI input device" anchor="GET MIDI_INPUT_DEVICE INFO"> + <section title="Getting current settings of a MIDI input device" anchor="GET MIDI_INPUT_DEVICE INFO" lscp_cmd="true"> <t>Use the following command to get current settings of a specific, created MIDI input device:</t> <t> <list> @@ -1852,7 +1854,7 @@ </t> </section> - <section title="Changing settings of MIDI input devices" anchor="SET MIDI_INPUT_DEVICE_PARAMETER"> + <section title="Changing settings of MIDI input devices" anchor="SET MIDI_INPUT_DEVICE_PARAMETER" lscp_cmd="true"> <t>Use the following command to alter a specific setting of a created MIDI input device:</t> <t> <list> @@ -1898,7 +1900,7 @@ </t> </section> - <section title="Getting information about a MIDI port" anchor="GET MIDI_INPUT_PORT INFO"> + <section title="Getting information about a MIDI port" anchor="GET MIDI_INPUT_PORT INFO" lscp_cmd="true"> <t>Use the following command to get information about a MIDI port:</t> <t> <list> @@ -1942,7 +1944,7 @@ </t> </section> - <section title="Getting information about specific MIDI port parameter" anchor="GET MIDI_INPUT_PORT_PARAMETER INFO"> + <section title="Getting information about specific MIDI port parameter" anchor="GET MIDI_INPUT_PORT_PARAMETER INFO" lscp_cmd="true"> <t>Use the following command to get detailed information about specific MIDI port parameter:</t> <t> <list> @@ -2047,7 +2049,7 @@ </t> </section> - <section title="Changing settings of MIDI input ports" anchor="SET MIDI_INPUT_PORT_PARAMETER"> + <section title="Changing settings of MIDI input ports" anchor="SET MIDI_INPUT_PORT_PARAMETER" lscp_cmd="true"> <t>Use the following command to alter a specific setting of a MIDI input port:</t> <t> <list> @@ -2107,7 +2109,7 @@ sampler channel with a sampler engine, load instruments and connect sampler channels to MIDI and audio devices.</t> - <section title="Loading an instrument" anchor="LOAD INSTRUMENT"> + <section title="Loading an instrument" anchor="LOAD INSTRUMENT" lscp_cmd="true"> <t>An instrument file can be loaded and assigned to a sampler channel by one of the following commands:</t> <t> <list> @@ -2233,7 +2235,7 @@ </t> </section> - <section title="Getting all created sampler channel count" anchor="GET CHANNELS"> + <section title="Getting all created sampler channel count" anchor="GET CHANNELS" lscp_cmd="true"> <t>The number of sampler channels can change on runtime. To get the current amount of sampler channels, the front-end can send the following command:</t> @@ -2257,7 +2259,7 @@ </t> </section> - <section title="Getting all created sampler channel list" anchor="LIST CHANNELS"> + <section title="Getting all created sampler channel list" anchor="LIST CHANNELS" lscp_cmd="true"> <t>The number of sampler channels can change on runtime. To get the current list of sampler channels, the front-end can send the following command:</t> @@ -2282,7 +2284,7 @@ </t> </section> - <section title="Adding a new sampler channel" anchor="ADD CHANNEL"> + <section title="Adding a new sampler channel" anchor="ADD CHANNEL" lscp_cmd="true"> <t>A new sampler channel can be added to the end of the sampler channel list by sending the following command:</t> <t> @@ -2334,7 +2336,7 @@ </t> </section> - <section title="Removing a sampler channel" anchor="REMOVE CHANNEL"> + <section title="Removing a sampler channel" anchor="REMOVE CHANNEL" lscp_cmd="true"> <t>A sampler channel can be removed by sending the following command:</t> <t> <list> @@ -2380,7 +2382,7 @@ </t> </section> - <section title="Getting amount of available engines" anchor="GET AVAILABLE_ENGINES"> + <section title="Getting amount of available engines" anchor="GET AVAILABLE_ENGINES" lscp_cmd="true"> <t>The front-end can ask for the number of available engines by sending the following command:</t> <t> <list> @@ -2402,7 +2404,7 @@ </t> </section> - <section title="Getting all available engines" anchor="LIST AVAILABLE_ENGINES"> + <section title="Getting all available engines" anchor="LIST AVAILABLE_ENGINES" lscp_cmd="true"> <t>The front-end can ask for a list of all available engines by sending the following command:</t> <t> <list> @@ -2422,12 +2424,12 @@ <t> <list> <t>C: "LIST AVAILABLE_ENGINES"</t> - <t>S: "'GigEngine','AkaiEngine','DLSEngine','JoesCustomEngine'"</t> + <t>S: "'gig','sfz','sf2'"</t> </list> </t> </section> - <section title="Getting information about an engine" anchor="GET ENGINE INFO"> + <section title="Getting information about an engine" anchor="GET ENGINE INFO" lscp_cmd="true"> <t>The front-end can ask for information about a specific engine by sending the following command:</t> <t> @@ -2468,18 +2470,26 @@ <t>The mentioned fields above don't have to be in particular order.</t> - <t>Example:</t> + <t>Examples:</t> <t> <list> - <t>C: "GET ENGINE INFO JoesCustomEngine"</t> - <t>S: "DESCRIPTION: this is Joe's custom sampler engine"</t> - <t> "VERSION: testing-1.0"</t> + <t>C: "GET ENGINE INFO gig"</t> + <t>S: "DESCRIPTION: GigaSampler Format Engine"</t> + <t> "VERSION: 1.110"</t> + <t> "."</t> + <t>C: "GET ENGINE INFO sf2"</t> + <t>S: "DESCRIPTION: SoundFont Format Engine"</t> + <t> "VERSION: 1.4"</t> + <t> "."</t> + <t>C: "GET ENGINE INFO sfz"</t> + <t>S: "DESCRIPTION: SFZ Format Engine"</t> + <t> "VERSION: 1.11"</t> <t> "."</t> </list> </t> </section> - <section title="Getting sampler channel information" anchor="GET CHANNEL INFO"> + <section title="Getting sampler channel information" anchor="GET CHANNEL INFO" lscp_cmd="true"> <t>The front-end can ask for the current settings of a sampler channel by sending the following command:</t> <t> @@ -2567,17 +2577,31 @@ </t> <t>MIDI_INPUT_DEVICE - <list> + <t>DEPRECATED: THIS FIELD WILL DISAPPEAR!</t> <t>numerical ID of the MIDI input device which is currently connected to this sampler channel to deliver MIDI input commands, "-1" if there's no device connected to this sampler channel</t> + <t>Should not be used anymore as of LSCP v1.6 and younger. + This field is currently only preserved for backward compatibility. + </t> + <t>This field a relict from times where only one MIDI input per + sampler channel was allowed. Use <xref target="LIST CHANNEL MIDI_INPUTS">"GET CHANNEL MIDI_INPUTS"</xref> + instead.</t> </list> </t> <t>MIDI_INPUT_PORT - <list> + <t>DEPRECATED: THIS FIELD WILL DISAPPEAR!</t> <t>port number of the MIDI input device (in case a MIDI device was already assigned to the sampler channel)</t> + <t>Should not be used anymore as of LSCP v1.6 and younger. + This field is currently only preserved for backward compatibility. + </t> + <t>This field a relict from times where only one MIDI input per + sampler channel was allowed. Use <xref target="LIST CHANNEL MIDI_INPUTS">"GET CHANNEL MIDI_INPUTS"</xref> + instead.</t> </list> </t> <t>MIDI_INPUT_CHANNEL - @@ -2626,7 +2650,7 @@ <t> <list> <t>C: "GET CHANNEL INFO 34"</t> - <t>S: "ENGINE_NAME: GigEngine"</t> + <t>S: "ENGINE_NAME: gig"</t> <t> "VOLUME: 1.0"</t> <t> "AUDIO_OUTPUT_DEVICE: 0"</t> <t> "AUDIO_OUTPUT_CHANNELS: 2"</t> @@ -2647,7 +2671,7 @@ </t> </section> - <section title="Current number of active voices" anchor="GET CHANNEL VOICE_COUNT"> + <section title="Current number of active voices" anchor="GET CHANNEL VOICE_COUNT" lscp_cmd="true"> <t>The front-end can ask for the current number of active voices on a sampler channel by sending the following command:</t> <t> @@ -2674,7 +2698,7 @@ </t> </section> - <section title="Current number of active disk streams" anchor="GET CHANNEL STREAM_COUNT"> + <section title="Current number of active disk streams" anchor="GET CHANNEL STREAM_COUNT" lscp_cmd="true"> <t>The front-end can ask for the current number of active disk streams on a sampler channel by sending the following command:</t> <t> @@ -2703,7 +2727,7 @@ </t> </section> - <section title="Current fill state of disk stream buffers" anchor="GET CHANNEL BUFFER_FILL"> + <section title="Current fill state of disk stream buffers" anchor="GET CHANNEL BUFFER_FILL" lscp_cmd="true"> <t>The front-end can ask for the current fill state of all disk streams on a sampler channel by sending the following command:</t> <t> @@ -2756,7 +2780,7 @@ </t> </section> - <section title="Setting audio output device" anchor="SET CHANNEL AUDIO_OUTPUT_DEVICE"> + <section title="Setting audio output device" anchor="SET CHANNEL AUDIO_OUTPUT_DEVICE" lscp_cmd="true"> <t>The front-end can set the audio output device on a specific sampler channel by sending the following command:</t> <t> @@ -2802,8 +2826,8 @@ </t> </section> - <section title="Setting audio output type" anchor="SET CHANNEL AUDIO_OUTPUT_TYPE"> - <t>DEPRECATED: THIS COMMAND WILL DISAPPEAR SOON!</t> + <section title="Setting audio output type" anchor="SET CHANNEL AUDIO_OUTPUT_TYPE" lscp_cmd="true"> + <t>DEPRECATED: THIS COMMAND WILL DISAPPEAR!</t> <t>The front-end can alter the audio output type on a specific sampler channel by sending the following command:</t> @@ -2843,9 +2867,16 @@ <t></t> </list> </t> + <t>Deprecated:</t> + <t> + <list> + <t>Should not be used anymore. This command is currently only preserved for backward compatibility.</t> + <t>This command is a relict from times where there was no sophisticated driver management yet. Use <xref target="CREATE AUDIO_OUTPUT_DEVICE">"CREATE AUDIO_OUTPUT_DEVICE"</xref> and <xref target="SET CHANNEL AUDIO_OUTPUT_DEVICE">"SET CHANNEL AUDIO_OUTPUT_DEVICE"</xref> instead.</t> + </list> + </t> </section> - <section title="Setting audio output channel" anchor="SET CHANNEL AUDIO_OUTPUT_CHANNEL"> + <section title="Setting audio output channel" anchor="SET CHANNEL AUDIO_OUTPUT_CHANNEL" lscp_cmd="true"> <t>The front-end can alter the audio output channel on a specific sampler channel by sending the following command:</t> <t> @@ -2890,7 +2921,188 @@ </t> </section> - <section title="Setting MIDI input device" anchor="SET CHANNEL MIDI_INPUT_DEVICE"> + <section title="Add MIDI input to sampler channel" anchor="ADD CHANNEL MIDI_INPUT" lscp_cmd="true"> + <t>The front-end can add a MIDI input on a specific sampler + channel by sending the following command:</t> + <t> + <list> + <t>ADD CHANNEL MIDI_INPUT <sampler-channel> <midi-device-id> <midi-input-port></t> + </list> + </t> + <t>Where <sampler-channel> is the sampler channel number + as returned by the <xref target="ADD CHANNEL">"ADD CHANNEL"</xref> + or <xref target="LIST CHANNELS">"LIST CHANNELS"</xref> command + and <midi-device-id> is the numerical ID of the MIDI input + device as returned by the + <xref target="CREATE MIDI_INPUT_DEVICE">"CREATE MIDI_INPUT_DEVICE"</xref> + or <xref target="LIST MIDI_INPUT_DEVICES">"LIST MIDI_INPUT_DEVICES"</xref> command, + and <midi-input-port> is an optional MIDI input port number of that + MIDI input device. If <midi-input-port> is omitted, + then the MIDI input device's first port (port number 0) is + used. + </t> + + <t>Possible Answers:</t> + <t> + <list> + <t>"OK" - + <list> + <t>on success</t> + </list> + </t> + <t>"WRN:<warning-code>:<warning-message>" - + <list> + <t>if MIDI input port was connected, but there are noteworthy + issue(s) related, providing an appropriate warning code and + warning message</t> + </list> + </t> + <t>"ERR:<error-code>:<error-message>" - + <list> + <t>in case it failed, providing an appropriate error code and error message</t> + </list> + </t> + </list> + </t> + <t>Examples:</t> + <t> + <list> + <t>C: "ADD CHANNEL MIDI_INPUT 0 0"</t> + <t>S: "OK"</t> + <t>C: "ADD CHANNEL MIDI_INPUT 1 0"</t> + <t>S: "OK"</t> + <t>C: "ADD CHANNEL MIDI_INPUT 1 1 1"</t> + <t>S: "OK"</t> + <t>C: "ADD CHANNEL MIDI_INPUT 1 2 0"</t> + <t>S: "OK"</t> + </list> + </t> + <t>Since:</t> + <t> + <list> + <t>Introduced with LSCP v1.6</t> + </list> + </t> + </section> + + <section title="Remove MIDI input(s) from sampler channel" anchor="REMOVE CHANNEL MIDI_INPUT" lscp_cmd="true"> + <t>The front-end can remove one ore more MIDI input(s) on a + specific sampler channel by sending the following command:</t> + <t> + <list> + <t>REMOVE CHANNEL MIDI_INPUT <sampler-channel> <midi-device-id> <midi-input-port></t> + </list> + </t> + <t>Where <sampler-channel> is the sampler channel number + as returned by the <xref target="ADD CHANNEL">"ADD CHANNEL"</xref> + or <xref target="LIST CHANNELS">"LIST CHANNELS"</xref> command + and <midi-device-id> and <midi-input-port> are + optional numerical IDs defining the MIDI input device and + one of its MIDI ports as returned by the + <xref target="LIST CHANNEL MIDI_INPUTS">"LIST CHANNEL MIDI_INPUTS"</xref> command. + </t> + + <t> + If <midi-input-port> is omitted, then all MIDI input + ports of <midi-device-id> are disconnected from this + sampler channel. + </t> + + <t> + If both, <midi-device-id> and <midi-input-port> + are omitted, then all MIDI input ports currently connected + to this sampler channel are disconnected from this sampler + channel. + </t> + + <t>Possible Answers:</t> + <t> + <list> + <t>"OK" - + <list> + <t>on success</t> + </list> + </t> + <t>"WRN:<warning-code>:<warning-message>" - + <list> + <t>if MIDI input porst were disconnected, but there are noteworthy + issue(s) related, providing an appropriate warning code and + warning message</t> + </list> + </t> + <t>"ERR:<error-code>:<error-message>" - + <list> + <t>in case it failed, providing an appropriate error code and error message</t> + </list> + </t> + </list> + </t> + <t>Examples:</t> + <t> + <list> + <t>C: "REMOVE CHANNEL MIDI_INPUT 0"</t> + <t>S: "OK"</t> + <t>C: "REMOVE CHANNEL MIDI_INPUT 1"</t> + <t>S: "OK"</t> + <t>C: "REMOVE CHANNEL MIDI_INPUT 1 2 0"</t> + <t>S: "OK"</t> + </list> + </t> + <t>Since:</t> + <t> + <list> + <t>Introduced with LSCP v1.6</t> + </list> + </t> + </section> + + <section title="Getting all MIDI inputs of a sampler channel" anchor="LIST CHANNEL MIDI_INPUTS" lscp_cmd="true"> + <t>The front-end can query a list of all currently connected + MIDI inputs of a certain sampler channel by sending the following + command:</t> + <t> + <list> + <t>LIST CHANNEL MIDI_INPUTS <sampler-channel></t> + </list> + </t> + <t>Where <sampler-channel> is the sampler channel number + as returned by the <xref target="ADD CHANNEL">"ADD CHANNEL"</xref> + or <xref target="LIST CHANNELS">"LIST CHANNELS"</xref> command. + </t> + + <t>Possible Answers:</t> + <t> + <list> + <t>The sampler will answer by sending a comma separated + list of MIDI input device ID - MIDI input port number pairs, where + each pair is encapsulated into curly braces. The + list is returned in one single line. The MIDI input + device ID corresponds to the number returned by + <xref target="LIST MIDI_INPUT_DEVICES">"LIST MIDI_INPUT_DEVICES"</xref> + and the port number is the index of the respective MIDI + port of that MIDI input device.</t> + </list> + </t> + + <t>Example:</t> + <t> + <list> + <t>C: "LIST CHANNEL MIDI_INPUTS 0"</t> + <t>S: "{0,0},{1,3},{2,0}"</t> + </list> + </t> + + <t>Since:</t> + <t> + <list> + <t>Introduced with LSCP v1.6</t> + </list> + </t> + </section> + + <section title="Setting MIDI input device" anchor="SET CHANNEL MIDI_INPUT_DEVICE" lscp_cmd="true"> + <t>DEPRECATED: THIS COMMAND WILL DISAPPEAR!</t> + <t>The front-end can set the MIDI input device on a specific sampler channel by sending the following command:</t> <t> @@ -2905,6 +3117,16 @@ <xref target="CREATE MIDI_INPUT_DEVICE">"CREATE MIDI_INPUT_DEVICE"</xref> or <xref target="LIST MIDI_INPUT_DEVICES">"LIST MIDI_INPUT_DEVICES"</xref> command.</t> + <t> + If more than 1 MIDI inputs are currently connected to this + sampler channel: Sending this command will disconnect ALL + currently connected MIDI input ports connected to this + sampler channel before establishing the new MIDI input + connection. So this command does NOT add the connection, + it replaces all existing ones instead. This behavior is due + to preserving full behavior backward compatibility. + </t> + <t>Possible Answers:</t> <t> <list> @@ -2933,10 +3155,17 @@ <t></t> </list> </t> + <t>Deprecated:</t> + <t> + <list> + <t>Should not be used anymore as of LSCP v1.6 and younger. This command is currently only preserved for backward compatibility.</t> + <t>This command is a relict from times where only one MIDI input per sampler channel was allowed. Use <xref target="ADD CHANNEL MIDI_INPUT">"ADD CHANNEL MIDI_INPUT"</xref> and <xref target="REMOVE CHANNEL MIDI_INPUT">"REMOVE CHANNEL MIDI_INPUT"</xref> instead.</t> + </list> + </t> </section> - <section title="Setting MIDI input type" anchor="SET CHANNEL MIDI_INPUT_TYPE"> - <t>DEPRECATED: THIS COMMAND WILL DISAPPEAR SOON!</t> + <section title="Setting MIDI input type" anchor="SET CHANNEL MIDI_INPUT_TYPE" lscp_cmd="true"> + <t>DEPRECATED: THIS COMMAND WILL DISAPPEAR!</t> <t>The front-end can alter the MIDI input type on a specific sampler channel by sending the following command:</t> @@ -2948,6 +3177,16 @@ <t>Where <midi-input-type> is currently only "ALSA" and <sampler-channel> is the respective sampler channel number.</t> + <t> + If more than 1 MIDI inputs are currently connected to this + sampler channel: Sending this command will disconnect ALL + currently connected MIDI input ports connected to this + sampler channel before establishing the new MIDI input + connection. So this command does NOT add the connection, + it replaces all existing ones instead. This behavior is due + to preserving full behavior backward compatibility. + </t> + <t>Possible Answers:</t> <t> <list> @@ -2976,9 +3215,18 @@ <t></t> </list> </t> + <t>Deprecated:</t> + <t> + <list> + <t>Should not be used anymore. This command is currently only preserved for backward compatibility.</t> + <t>This command is a relict from times where only 1 MIDI input per sampler channels was allowed and where no sophisticated driver management existed yet. Use <xref target="ADD CHANNEL MIDI_INPUT">"ADD CHANNEL MIDI_INPUT"</xref> and <xref target="REMOVE CHANNEL MIDI_INPUT">"REMOVE CHANNEL MIDI_INPUT"</xref> instead.</t> + </list> + </t> </section> - <section title="Setting MIDI input port" anchor="SET CHANNEL MIDI_INPUT_PORT"> + <section title="Setting MIDI input port" anchor="SET CHANNEL MIDI_INPUT_PORT" lscp_cmd="true"> + <t>DEPRECATED: THIS COMMAND WILL DISAPPEAR!</t> + <t>The front-end can alter the MIDI input port on a specific sampler channel by sending the following command:</t> <t> @@ -2989,6 +3237,19 @@ <t>Where <midi-input-port> is a MIDI input port number of the MIDI input device connected to the sampler channel given by <sampler-channel>.</t> + + <t> + If more than 1 MIDI inputs are currently connected to this + sampler channel: Sending this command will switch the + connection of the first (and only the first) MIDI input port + currently being connected to this sampler channel, to + another port of the same MIDI input device. Or in other + words: the first MIDI input port currently connected to + this sampler channel will be disconnected, and the requested + other port of its MIDI input device will be connected to + this sampler channel instead. This behavior is due + to preserving full behavior backward compatibility. + </t> <t>Possible Answers:</t> <t> @@ -3018,9 +3279,16 @@ <t></t> </list> </t> + <t>Deprecated:</t> + <t> + <list> + <t>Should not be used anymore. This command is currently only preserved for backward compatibility.</t> + <t>This command is a relict from times where only one MIDI input per sampler channel was allowed. Use <xref target="ADD CHANNEL MIDI_INPUT">"ADD CHANNEL MIDI_INPUT"</xref> and <xref target="REMOVE CHANNEL MIDI_INPUT">"REMOVE CHANNEL MIDI_INPUT"</xref> instead.</t> + </list> + </t> </section> - <section title="Setting MIDI input channel" anchor="SET CHANNEL MIDI_INPUT_CHANNEL"> + <section title="Setting MIDI input channel" anchor="SET CHANNEL MIDI_INPUT_CHANNEL" lscp_cmd="true"> <t>The front-end can alter the MIDI channel a sampler channel should listen to by sending the following command:</t> <t> @@ -3028,8 +3296,9 @@ <t>SET CHANNEL MIDI_INPUT_CHANNEL <sampler-channel> <midi-input-chan></t> </list> </t> - <t>Where <midi-input-chan> is the number of the new MIDI input channel where - <sampler-channel> should listen to or "ALL" to listen on all 16 MIDI + <t>Where <midi-input-chan> is the number + of the new MIDI input channel (zero indexed!) where + <sampler-channel> should listen to, or "ALL" to listen on all 16 MIDI channels.</t> <t>Possible Answers:</t> @@ -3057,12 +3326,15 @@ <t>Examples:</t> <t> <list> - <t></t> + <t>C: "SET CHANNEL MIDI_INPUT_CHANNEL 0 0"</t> + <t>S: "OK"</t> + <t>C: "SET CHANNEL MIDI_INPUT_CHANNEL 1 ALL"</t> + <t>S: "OK"</t> </list> </t> </section> - <section title="Setting channel volume" anchor="SET CHANNEL VOLUME"> + <section title="Setting channel volume" anchor="SET CHANNEL VOLUME" lscp_cmd="true"> <t>The front-end can alter the volume of a sampler channel by sending the following command:</t> <t> @@ -3105,7 +3377,7 @@ </t> </section> - <section title="Muting a sampler channel" anchor="SET CHANNEL MUTE"> + <section title="Muting a sampler channel" anchor="SET CHANNEL MUTE" lscp_cmd="true"> <t>The front-end can mute/unmute a specific sampler channel by sending the following command:</t> <t> @@ -3149,7 +3421,7 @@ </t> </section> - <section title="Soloing a sampler channel" anchor="SET CHANNEL SOLO"> + <section title="Soloing a sampler channel" anchor="SET CHANNEL SOLO" lscp_cmd="true"> <t>The front-end can solo/unsolo a specific sampler channel by sending the following command:</t> <t> @@ -3193,7 +3465,7 @@ </t> </section> - <section title="Assigning a MIDI instrument map to a sampler channel" anchor="SET CHANNEL MIDI_INSTRUMENT_MAP"> + <section title="Assigning a MIDI instrument map to a sampler channel" anchor="SET CHANNEL MIDI_INSTRUMENT_MAP" lscp_cmd="true"> <t>The front-end can assign a MIDI instrument map to a specific sampler channel by sending the following command:</t> <t> @@ -3261,7 +3533,7 @@ </t> </section> - <section title="Adding an effect send to a sampler channel" anchor="CREATE FX_SEND"> + <section title="Adding an effect send to a sampler channel" anchor="CREATE FX_SEND" lscp_cmd="true"> <t>The front-end can create an additional effect send on a specific sampler channel by sending the following command:</t> <t> @@ -3331,7 +3603,7 @@ </t> </section> - <section title="Removing an effect send from a sampler channel" anchor="DESTROY FX_SEND"> + <section title="Removing an effect send from a sampler channel" anchor="DESTROY FX_SEND" lscp_cmd="true"> <t>The front-end can remove an existing effect send on a specific sampler channel by sending the following command:</t> <t> @@ -3373,7 +3645,7 @@ </t> </section> - <section title="Getting amount of effect sends on a sampler channel" anchor="GET FX_SENDS"> + <section title="Getting amount of effect sends on a sampler channel" anchor="GET FX_SENDS" lscp_cmd="true"> <t>The front-end can ask for the amount of effect sends on a specific sampler channel by sending the following command:</t> <t> @@ -3402,7 +3674,7 @@ </t> </section> - <section title="Listing all effect sends on a sampler channel" anchor="LIST FX_SENDS"> + <section title="Listing all effect sends on a sampler channel" anchor="LIST FX_SENDS" lscp_cmd="true"> <t>The front-end can ask for a list of effect sends on a specific sampler channel by sending the following command:</t> <t> @@ -3438,7 +3710,7 @@ </t> </section> - <section title="Getting effect send information" anchor="GET FX_SEND INFO"> + <section title="Getting effect send information" anchor="GET FX_SEND INFO" lscp_cmd="true"> <t>The front-end can ask for the current settings of an effect send entity by sending the following command:</t> <t> @@ -3543,7 +3815,7 @@ </t> </section> - <section title="Changing effect send's name" anchor="SET FX_SEND NAME"> + <section title="Changing effect send's name" anchor="SET FX_SEND NAME" lscp_cmd="true"> <t>The front-end can alter the current name of an effect send entity by sending the following command:</t> <t> @@ -3587,7 +3859,7 @@ </t> </section> - <section title="Altering effect send's audio routing" anchor="SET FX_SEND AUDIO_OUTPUT_CHANNEL"> + <section title="Altering effect send's audio routing" anchor="SET FX_SEND AUDIO_OUTPUT_CHANNEL" lscp_cmd="true"> <t>The front-end can alter the destination of an effect send's audio channel on a specific sampler channel by sending the following command:</t> <t> @@ -3649,7 +3921,7 @@ </t> </section> - <section title="Assigning destination effect to an effect send" anchor="SET FX_SEND EFFECT"> + <section title="Assigning destination effect to an effect send" anchor="SET FX_SEND EFFECT" lscp_cmd="true"> <t>The front-end can (re-)assign a destination effect to an effect send by sending the following command:</t> <t> @@ -3696,7 +3968,7 @@ </t> </section> - <section title="Removing destination effect from an effect send" anchor="REMOVE FX_SEND EFFECT"> + <section title="Removing destination effect from an effect send" anchor="REMOVE FX_SEND EFFECT" lscp_cmd="true"> <t>The front-end can (re-)assign a destination effect to an effect send by sending the following command:</t> <t> @@ -3741,7 +4013,7 @@ </t> </section> - <section title="Altering effect send's MIDI controller" anchor="SET FX_SEND MIDI_CONTROLLER"> + <section title="Altering effect send's MIDI controller" anchor="SET FX_SEND MIDI_CONTROLLER" lscp_cmd="true"> <t>The front-end can alter the MIDI controller of an effect send entity by sending the following command:</t> <t> @@ -3789,7 +4061,7 @@ </t> </section> - <section title="Altering effect send's send level" anchor="SET FX_SEND LEVEL"> + <section title="Altering effect send's send level" anchor="SET FX_SEND LEVEL" lscp_cmd="true"> <t>The front-end can alter the current send level of an effect send entity by sending the following command:</t> <t> @@ -3838,7 +4110,7 @@ </t> </section> - <section title="Sending MIDI messages to sampler channel" anchor="SEND CHANNEL MIDI_DATA"> + <section title="Sending MIDI messages to sampler channel" anchor="SEND CHANNEL MIDI_DATA" lscp_cmd="true"> <t>The front-end can send MIDI events to a specific sampler channel by sending the following command:</t> <t> @@ -3903,7 +4175,7 @@ </t> </section> - <section title="Resetting a sampler channel" anchor="RESET CHANNEL"> + <section title="Resetting a sampler channel" anchor="RESET CHANNEL" lscp_cmd="true"> <t>The front-end can reset a particular sampler channel by sending the following command:</t> <t> <list> @@ -3951,7 +4223,7 @@ <section title="Controlling connection"> <t>The following commands are used to control the connection to LinuxSampler.</t> - <section title="Register front-end for receiving event messages" anchor="SUBSCRIBE"> + <section title="Register front-end for receiving event messages" anchor="SUBSCRIBE" lscp_cmd="true"> <t>The front-end can register itself to the LinuxSampler application to be informed about noteworthy events by sending this command:</t> <t> @@ -3993,7 +4265,7 @@ </t> </section> - <section title="Unregister front-end for not receiving event messages" anchor="UNSUBSCRIBE"> + <section title="Unregister front-end for not receiving event messages" anchor="UNSUBSCRIBE" lscp_cmd="true"> <t>The front-end can unregister itself if it doesn't want to receive event messages anymore by sending the following command:</t> <t> @@ -4035,7 +4307,7 @@ </t> </section> - <section title="Enable or disable echo of commands" anchor="SET ECHO"> + <section title="Enable or disable echo of commands" anchor="SET ECHO" lscp_cmd="true"> <t>To enable or disable back sending of commands to the client the following command can be used:</t> <t> <list> @@ -4073,7 +4345,7 @@ </t> </section> - <section title="Close client connection" anchor="QUIT"> + <section title="Close client connection" anchor="QUIT" lscp_cmd="true"> <t>The client can close its network connection to LinuxSampler by sending the following command:</t> <t> <list> @@ -4088,7 +4360,7 @@ <section title="Global commands"> <t>The following commands have global impact on the sampler.</t> - <section title="Current number of active voices" anchor="GET TOTAL_VOICE_COUNT"> + <section title="Current number of active voices" anchor="GET TOTAL_VOICE_COUNT" lscp_cmd="true"> <t>The front-end can ask for the current number of active voices on the sampler by sending the following command:</t> <t> @@ -4106,7 +4378,7 @@ </t> </section> - <section title="Maximum amount of active voices" anchor="GET TOTAL_VOICE_COUNT_MAX"> + <section title="Maximum amount of active voices" anchor="GET TOTAL_VOICE_COUNT_MAX" lscp_cmd="true"> <t>The front-end can ask for the maximum number of active voices by sending the following command:</t> <t> @@ -4124,7 +4396,7 @@ </t> </section> - <section title="Current number of active disk streams" anchor="GET TOTAL_STREAM_COUNT"> + <section title="Current number of active disk streams" anchor="GET TOTAL_STREAM_COUNT" lscp_cmd="true"> <t>The front-end can ask for the current number of active disk streams on the sampler by sending the following command:</t> <t> @@ -4168,7 +4440,7 @@ </t> </section> - <section title="General sampler informations" anchor="GET SERVER INFO"> + <section title="General sampler informations" anchor="GET SERVER INFO" lscp_cmd="true"> <t>The client can ask for general informations about the LinuxSampler instance by sending the following command:</t> <t> @@ -4217,9 +4489,21 @@ </t> <t>The mentioned fields above don't have to be in particular order. Other fields might be added in future.</t> + + <t>Example:</t> + <t> + <list> + <t>C: "GET SERVER INFO"</t> + <t>S: "DESCRIPTION: LinuxSampler - modular, streaming capable sampler"</t> + <t> "VERSION: 1.0.0.svn23"</t> + <t> "PROTOCOL_VERSION: 1.5"</t> + <t> "INSTRUMENTS_DB_SUPPORT: no"</t> + <t> "."</t> + </list> + </t> </section> - <section title="Getting global volume attenuation" anchor="GET VOLUME"> + <section title="Getting global volume attenuation" anchor="GET VOLUME" lscp_cmd="true"> <t>The client can ask for the current global sampler-wide volume attenuation by sending the following command:</t> <t> @@ -4241,7 +4525,7 @@ use this parameter.</t> </section> - <section title="Setting global volume attenuation" anchor="SET VOLUME"> + <section title="Setting global volume attenuation" anchor="SET VOLUME" lscp_cmd="true"> <t>The client can alter the current global sampler-wide volume attenuation by sending the following command:</t> <t> @@ -4278,7 +4562,7 @@ </t> </section> - <section title="Getting global voice limit" anchor="GET VOICES"> + <section title="Getting global voice limit" anchor="GET VOICES" lscp_cmd="true"> <t>The client can ask for the current global sampler-wide limit for maximum voices by sending the following command:</t> <t> @@ -4303,7 +4587,7 @@ respective instrument and probably further criterias.</t> </section> - <section title="Setting global voice limit" anchor="SET VOICES"> + <section title="Setting global voice limit" anchor="SET VOICES" lscp_cmd="true"> <t>The client can alter the current global sampler-wide limit for maximum voices by sending the following command:</t> <t> @@ -4347,7 +4631,7 @@ adjust the disk stream limit respectively and vice versa.</t> </section> - <section title="Getting global disk stream limit" anchor="GET STREAMS"> + <section title="Getting global disk stream limit" anchor="GET STREAMS" lscp_cmd="true"> <t>The client can ask for the current global sampler-wide limit for maximum disk streams by sending the following command:</t> <t> @@ -4370,7 +4654,7 @@ to perform its streaming operations.</t> </section> - <section title="Setting global disk stream limit" anchor="SET STREAMS"> + <section title="Setting global disk stream limit" anchor="SET STREAMS" lscp_cmd="true"> <t>The client can alter the current global sampler-wide limit for maximum disk streams by sending the following command:</t> <t> @@ -4444,7 +4728,7 @@ cause the sampler to switch to the respective instrument as reflected by the current MIDI instrument map.</t> - <section title="Create a new MIDI instrument map" anchor="ADD MIDI_INSTRUMENT_MAP"> + <section title="Create a new MIDI instrument map" anchor="ADD MIDI_INSTRUMENT_MAP" lscp_cmd="true"> <t>The front-end can add a new MIDI instrument map by sending the following command:</t> <t> @@ -4500,7 +4784,7 @@ </t> </section> - <section title="Delete one particular or all MIDI instrument maps" anchor="REMOVE MIDI_INSTRUMENT_MAP"> + <section title="Delete one particular or all MIDI instrument maps" anchor="REMOVE MIDI_INSTRUMENT_MAP" lscp_cmd="true"> <t>The front-end can delete a particular MIDI instrument map by sending the following command:</t> <t> @@ -4550,7 +4834,7 @@ </t> </section> - <section title="Get amount of existing MIDI instrument maps" anchor="GET MIDI_INSTRUMENT_MAPS"> + <section title="Get amount of existing MIDI instrument maps" anchor="GET MIDI_INSTRUMENT_MAPS" lscp_cmd="true"> <t>The front-end can retrieve the current amount of MIDI instrument maps by sending the following command:</t> <t> @@ -4576,7 +4860,7 @@ </t> </section> - <section title="Getting all created MIDI instrument maps" anchor="LIST MIDI_INSTRUMENT_MAPS"> + <section title="Getting all created MIDI instrument maps" anchor="LIST MIDI_INSTRUMENT_MAPS" lscp_cmd="true"> <t>The number of MIDI instrument maps can change on runtime. To get the current list of MIDI instrument maps, the front-end can send the following command:</t> @@ -4601,7 +4885,7 @@ </t> </section> - <section title="Getting MIDI instrument map information" anchor="GET MIDI_INSTRUMENT_MAP INFO"> + <section title="Getting MIDI instrument map information" anchor="GET MIDI_INSTRUMENT_MAP INFO" lscp_cmd="true"> <t>The front-end can ask for the current settings of a MIDI instrument map by sending the following command:</t> <t> @@ -4656,7 +4940,7 @@ </t> </section> - <section title="Renaming a MIDI instrument map" anchor="SET MIDI_INSTRUMENT_MAP NAME"> + <section title="Renaming a MIDI instrument map" anchor="SET MIDI_INSTRUMENT_MAP NAME" lscp_cmd="true"> <t>The front-end can alter the custom name of a MIDI instrument map by sending the following command:</t> <t> @@ -4696,7 +4980,7 @@ </t> </section> - <section title="Create or replace a MIDI instrument map entry" anchor="MAP MIDI_INSTRUMENT"> + <section title="Create or replace a MIDI instrument map entry" anchor="MAP MIDI_INSTRUMENT" lscp_cmd="true"> <t>The front-end can create a new or replace an existing entry in a sampler's MIDI instrument map by sending the following command:</t> @@ -4869,7 +5153,7 @@ </t> </section> - <section title="Getting ammount of MIDI instrument map entries" anchor="GET MIDI_INSTRUMENTS"> + <section title="Getting amount of MIDI instrument map entries" anchor="GET MIDI_INSTRUMENTS" lscp_cmd="true"> <t>The front-end can query the amount of currently existing entries in a MIDI instrument map by sending the following command:</t> @@ -4909,7 +5193,7 @@ </t> </section> - <section title="Getting indeces of all entries of a MIDI instrument map" anchor="LIST MIDI_INSTRUMENTS"> + <section title="Getting indeces of all entries of a MIDI instrument map" anchor="LIST MIDI_INSTRUMENTS" lscp_cmd="true"> <t>The front-end can query a list of all currently existing entries in a certain MIDI instrument map by sending the following command:</t> @@ -4952,7 +5236,7 @@ </t> </section> - <section title="Remove an entry from the MIDI instrument map" anchor="UNMAP MIDI_INSTRUMENT"> + <section title="Remove an entry from the MIDI instrument map" anchor="UNMAP MIDI_INSTRUMENT" lscp_cmd="true"> <t>The front-end can delete an entry from a MIDI instrument map by sending the following command:</t> <t> @@ -4994,7 +5278,7 @@ </t> </section> - <section title="Get current settings of MIDI instrument map entry" anchor="GET MIDI_INSTRUMENT INFO"> + <section title="Get current settings of MIDI instrument map entry" anchor="GET MIDI_INSTRUMENT INFO" lscp_cmd="true"> <t>The front-end can retrieve the current settings of a certain instrument map entry by sending the following command:</t> <t> @@ -5091,7 +5375,7 @@ </t> </section> - <section title="Clear MIDI instrument map" anchor="CLEAR MIDI_INSTRUMENTS"> + <section title="Clear MIDI instrument map" anchor="CLEAR MIDI_INSTRUMENTS" lscp_cmd="true"> <t>The front-end can clear a whole MIDI instrument map, that is delete all its entries by sending the following command:</t> <t> @@ -5156,7 +5440,7 @@ </list> </t> - <section title="Creating a new instrument directory" anchor="ADD DB_INSTRUMENT_DIRECTORY"> + <section title="Creating a new instrument directory" anchor="ADD DB_INSTRUMENT_DIRECTORY" lscp_cmd="true"> <t>The front-end can add a new instrument directory to the instruments database by sending the following command:</t> <t> @@ -5194,7 +5478,7 @@ </t> </section> - <section title="Deleting an instrument directory" anchor="REMOVE DB_INSTRUMENT_DIRECTORY"> + <section title="Deleting an instrument directory" anchor="REMOVE DB_INSTRUMENT_DIRECTORY" lscp_cmd="true"> <t>The front-end can delete a particular instrument directory from the instruments database by sending the following command:</t> <t> @@ -5233,7 +5517,7 @@ </t> </section> - <section title="Getting amount of instrument directories" anchor="GET DB_INSTRUMENT_DIRECTORIES"> + <section title="Getting amount of instrument directories" anchor="GET DB_INSTRUMENT_DIRECTORIES" lscp_cmd="true"> <t>The front-end can retrieve the current amount of directories in a specific directory by sending the following command:</t> <t> @@ -5268,7 +5552,7 @@ </t> </section> - <section title="Listing all directories in specific directory" anchor="LIST DB_INSTRUMENT_DIRECTORIES"> + <section title="Listing all directories in specific directory" anchor="LIST DB_INSTRUMENT_DIRECTORIES" lscp_cmd="true"> <t>The front-end can retrieve the current list of directories in specific directory by sending the following command:</t> <t> @@ -5308,7 +5592,7 @@ </t> </section> - <section title="Getting instrument directory information" anchor="GET DB_INSTRUMENT_DIRECTORY INFO"> + <section title="Getting instrument directory information" anchor="GET DB_INSTRUMENT_DIRECTORY INFO" lscp_cmd="true"> <t>The front-end can ask for the current settings of an instrument directory by sending the following command:</t> <t> @@ -5367,7 +5651,7 @@ </t> </section> - <section title="Renaming an instrument directory" anchor="SET DB_INSTRUMENT_DIRECTORY NAME"> + <section title="Renaming an instrument directory" anchor="SET DB_INSTRUMENT_DIRECTORY NAME" lscp_cmd="true"> <t>The front-end can alter the name of a specific instrument directory by sending the following command:</t> <t> @@ -5405,7 +5689,7 @@ </t> </section> - <section title="Moving an instrument directory" anchor="MOVE DB_INSTRUMENT_DIRECTORY"> + <section title="Moving an instrument directory" anchor="MOVE DB_INSTRUMENT_DIRECTORY" lscp_cmd="true"> <t>The front-end can move a specific instrument directory by sending the following command:</t> <t> @@ -5447,7 +5731,7 @@ </t> </section> - <section title="Copying instrument directories" anchor="COPY DB_INSTRUMENT_DIRECTORY"> + <section title="Copying instrument directories" anchor="COPY DB_INSTRUMENT_DIRECTORY" lscp_cmd="true"> <t>The front-end can copy a specific instrument directory by sending the following command:</t> <t> @@ -5489,7 +5773,7 @@ </t> </section> - <section title="Changing the description of directory" anchor="SET DB_INSTRUMENT_DIRECTORY DESCRIPTION"> + <section title="Changing the description of directory" anchor="SET DB_INSTRUMENT_DIRECTORY DESCRIPTION" lscp_cmd="true"> <t>The front-end can alter the description of a specific instrument directory by sending the following command:</t> <t> @@ -5527,7 +5811,7 @@ </t> </section> - <section title="Finding directories" anchor="FIND DB_INSTRUMENT_DIRECTORIES"> + <section title="Finding directories" anchor="FIND DB_INSTRUMENT_DIRECTORIES" lscp_cmd="true"> <t>The front-end can search for directories in specific directory by sending the following command:</t> <t> @@ -5617,7 +5901,7 @@ </t> </section> - <section title="Adding instruments to the instruments database" anchor="ADD DB_INSTRUMENTS"> + <section title="Adding instruments to the instruments database" anchor="ADD DB_INSTRUMENTS" lscp_cmd="true"> <t>The front-end can add one or more instruments to the instruments database by sending the following command:</t> <t> @@ -5710,7 +5994,7 @@ </t> </section> - <section title="Removing an instrument" anchor="REMOVE DB_INSTRUMENT"> + <section title="Removing an instrument" anchor="REMOVE DB_INSTRUMENT" lscp_cmd="true"> <t>The front-end can remove a particular instrument from the instruments database by sending the following command:</t> <t> @@ -5747,7 +6031,7 @@ </t> </section> - <section title="Getting amount of instruments" anchor="GET DB_INSTRUMENTS"> + <section title="Getting amount of instruments" anchor="GET DB_INSTRUMENTS" lscp_cmd="true"> <t>The front-end can retrieve the current amount of instruments in a specific directory by sending the following command:</t> <t> @@ -5782,7 +6066,7 @@ </t> </section> - <section title="Listing all instruments in specific directory" anchor="LIST DB_INSTRUMENTS"> + <section title="Listing all instruments in specific directory" anchor="LIST DB_INSTRUMENTS" lscp_cmd="true"> <t>The front-end can retrieve the current list of instruments in specific directory by sending the following command:</t> <t> @@ -5822,7 +6106,7 @@ </t> </section> - <section title="Getting instrument information" anchor="GET DB_INSTRUMENT INFO"> + <section title="Getting instrument information" anchor="GET DB_INSTRUMENT INFO" lscp_cmd="true"> <t>The front-end can ask for the current settings of an instrument by sending the following command:</t> <t> @@ -5947,7 +6231,7 @@ </t> </section> - <section title="Renaming an instrument" anchor="SET DB_INSTRUMENT NAME"> + <section title="Renaming an instrument" anchor="SET DB_INSTRUMENT NAME" lscp_cmd="true"> <t>The front-end can alter the name of a specific instrument by sending the following command:</t> <t> @@ -5985,7 +6269,7 @@ </t> </section> - <section title="Moving an instrument" anchor="MOVE DB_INSTRUMENT"> + <section title="Moving an instrument" anchor="MOVE DB_INSTRUMENT" lscp_cmd="true"> <t>The front-end can move a specific instrument to another directory by sending the following command:</t> <t> @@ -6025,7 +6309,7 @@ </t> </section> - <section title="Copying instruments" anchor="COPY DB_INSTRUMENT"> + <section title="Copying instruments" anchor="COPY DB_INSTRUMENT" lscp_cmd="true"> <t>The front-end can copy a specific instrument to another directory by sending the following command:</t> <t> @@ -6065,7 +6349,7 @@ </t> </section> - <section title="Changing the description of instrument" anchor="SET DB_INSTRUMENT DESCRIPTION"> + <section title="Changing the description of instrument" anchor="SET DB_INSTRUMENT DESCRIPTION" lscp_cmd="true"> <t>The front-end can alter the description of a specific instrument by sending the following command:</t> <t> @@ -6103,7 +6387,7 @@ </t> </section> - <section title="Finding instruments" anchor="FIND DB_INSTRUMENTS"> + <section title="Finding instruments" anchor="FIND DB_INSTRUMENTS" lscp_cmd="true"> <t>The front-end can search for instruments in specific directory by sending the following command:</t> <t> @@ -6244,7 +6528,7 @@ </t> </section> - <section title="Getting job status information" anchor="GET DB_INSTRUMENTS_JOB INFO"> + <section title="Getting job status information" anchor="GET DB_INSTRUMENTS_JOB INFO" lscp_cmd="true"> <t>The front-end can ask for the current status of a particular database instruments job by sending the following command:</t> <t> @@ -6308,7 +6592,7 @@ </t> </section> - <section title="Formatting the instruments database" anchor="FORMAT INSTRUMENTS_DB"> + <section title="Formatting the instruments database" anchor="FORMAT INSTRUMENTS_DB" lscp_cmd="true"> <t>The front-end can remove all instruments and directories and re-create the instruments database structure (e.g., in case of a database corruption) by sending the following command:</t> @@ -6336,7 +6620,7 @@ </t> </section> - <section title="Checking for lost instrument files" anchor="FIND LOST DB_INSTRUMENT_FILES"> + <section title="Checking for lost instrument files" anchor="FIND LOST DB_INSTRUMENT_FILES" lscp_cmd="true"> <t>The front-end can retrieve the list of all instrument files in the instruments database that don't exist in the filesystem by sending the following command:</t> <t> @@ -6366,7 +6650,7 @@ </t> </section> - <section title="Replacing an instrument file" anchor="SET DB_INSTRUMENT FILE_PATH"> + <section title="Replacing an instrument file" anchor="SET DB_INSTRUMENT FILE_PATH" lscp_cmd="true"> <t>The front-end can substitute all occurrences of an instrument file in the instruments database with a new one by sending the following command:</t> <t> @@ -6417,7 +6701,7 @@ <t>At the moment there is only one command for this feature set, but this will most probably change in future.</t> - <section title="Opening an appropriate instrument editor application" anchor="EDIT INSTRUMENT"> + <section title="Opening an appropriate instrument editor application" anchor="EDIT INSTRUMENT" lscp_cmd="true"> <t>The front-end can request to open an appropriate instrument editor application by sending the following command:</t> <t> @@ -6487,7 +6771,7 @@ Using this command set allows to retrieve file informations even remotely from another machine.</t> - <section title="Retrieving amount of instruments of a file" anchor="GET FILE INSTRUMENTS"> + <section title="Retrieving amount of instruments of a file" anchor="GET FILE INSTRUMENTS" lscp_cmd="true"> <t>The front-end can retrieve the amount of instruments within a given instrument file by sending the following command:</t> @@ -6530,7 +6814,7 @@ </t> </section> - <section title="Retrieving all instruments of a file" anchor="LIST FILE INSTRUMENTS"> + <section title="Retrieving all instruments of a file" anchor="LIST FILE INSTRUMENTS" lscp_cmd="true"> <t>The front-end can retrieve a list of all instruments within a given instrument file by sending the following command:</t> @@ -6574,7 +6858,7 @@ </t> </section> - <section title="Retrieving informations about one instrument in a file" anchor="GET FILE INSTRUMENT INFO"> + <section title="Retrieving informations about one instrument in a file" anchor="GET FILE INSTRUMENT INFO" lscp_cmd="true"> <t>The front-end can retrieve detailed informations about a specific instrument within a given instrument file by sending the following command:</t> @@ -6705,7 +6989,7 @@ of a send effect chain, as well as directly to any other position of the send effect chain.</t> - <section title="Retrieve amount of available effects" anchor="GET AVAILABLE_EFFECTS"> + <section title="Retrieve amount of available effects" anchor="GET AVAILABLE_EFFECTS" lscp_cmd="true"> <t>The front-end can retrieve the amount of internal effects, available to the sampler by sending the following command:</t> @@ -6732,7 +7016,7 @@ </t> </section> - <section title="Get list of available effects" anchor="LIST AVAILABLE_EFFECTS"> + <section title="Get list of available effects" anchor="LIST AVAILABLE_EFFECTS" lscp_cmd="true"> <t>The set of available internal effects can change at runtime. The front-end can retrieve the list of internal effects, available to the sampler by sending the following @@ -6764,7 +7048,7 @@ </t> </section> - <section title="Retrieving general information about an effect" anchor="GET EFFECT INFO"> + <section title="Retrieving general information about an effect" anchor="GET EFFECT INFO" lscp_cmd="true"> <t>The front-end can ask for general informations about an effect by sending the following command:</t> <t> @@ -6843,7 +7127,7 @@ </t> </section> - <section title="Creating an instance of an effect by its portable ID" anchor="CREATE EFFECT_INSTANCE"> + <section title="Creating an instance of an effect by its portable ID" anchor="CREATE EFFECT_INSTANCE" lscp_cmd="true"> <t>The front-end can spawn an instance of the desired effect by sending the following command:</t> <t> @@ -6974,7 +7258,7 @@ </t> </section> - <section title="Destroy an effect instance" anchor="DESTROY EFFECT_INSTANCE"> + <section title="Destroy an effect instance" anchor="DESTROY EFFECT_INSTANCE" lscp_cmd="true"> <t>The front-end can destroy an unusued effect instance and thus freeing it from memory by sending the following command:</t> <t> @@ -7020,7 +7304,7 @@ </t> </section> - <section title="Retrieve amount of effect instances" anchor="GET EFFECT_INSTANCES"> + <section title="Retrieve amount of effect instances" anchor="GET EFFECT_INSTANCES" lscp_cmd="true"> <t>The front-end can retrieve the current amount of effect instances by sending the following command:</t> <t> @@ -7047,7 +7331,7 @@ </t> </section> - <section title="Get list of effect instances" anchor="LIST EFFECT_INSTANCES"> + <section title="Get list of effect instances" anchor="LIST EFFECT_INSTANCES" lscp_cmd="true"> <t>The front-end can retrieve the current list of effect instances by sending the following command:</t> <t> @@ -7074,7 +7358,7 @@ </t> </section> - <section title="Retrieving current information about an effect instance" anchor="GET EFFECT_INSTANCE INFO"> + <section title="Retrieving current information about an effect instance" anchor="GET EFFECT_INSTANCE INFO" lscp_cmd="true"> <t>The front-end can ask for the current informations about a particular effect instance by sending the following command:</t> <t> @@ -7165,7 +7449,7 @@ </t> </section> - <section title="Retrieving information about an effect parameter" anchor="GET EFFECT_INSTANCE_INPUT_CONTROL INFO"> + <section title="Retrieving information about an effect parameter" anchor="GET EFFECT_INSTANCE_INPUT_CONTROL INFO" lscp_cmd="true"> <t>Effects typically provide a certain set of effect parameters which can be altered by the user in realtime (e.g. depth of a reverb effect, duration of a delay effect, @@ -7276,7 +7560,7 @@ </t> </section> - <section title="Altering an effect parameter" anchor="SET EFFECT_INSTANCE_INPUT_CONTROL VALUE"> + <section title="Altering an effect parameter" anchor="SET EFFECT_INSTANCE_INPUT_CONTROL VALUE" lscp_cmd="true"> <t>The front-end can alter the current value of an effect parameter by sending the following command:</t> <t> @@ -7321,7 +7605,7 @@ </t> </section> - <section title="Retrieve amount of send effect chains" anchor="GET SEND_EFFECT_CHAINS"> + <section title="Retrieve amount of send effect chains" anchor="GET SEND_EFFECT_CHAINS" lscp_cmd="true"> <t>The front-end can retrieve the current amount of send effect chains of an audio output device by sending the following command:</t> @@ -7354,7 +7638,7 @@ </t> </section> - <section title="Retrieve list of send effect chains" anchor="LIST SEND_EFFECT_CHAINS"> + <section title="Retrieve list of send effect chains" anchor="LIST SEND_EFFECT_CHAINS" lscp_cmd="true"> <t>The front-end can retrieve the current list of send effect chains of an audio output device by sending the following command:</t> @@ -7388,7 +7672,7 @@ </t> </section> - <section title="Add send effect chain" anchor="ADD SEND_EFFECT_CHAIN"> + <section title="Add send effect chain" anchor="ADD SEND_EFFECT_CHAIN" lscp_cmd="true"> <t>The front-end can add a send effect chain by sending the following command:</t> <t> @@ -7430,7 +7714,7 @@ </t> </section> - <section title="Remove send effect chain" anchor="REMOVE SEND_EFFECT_CHAIN"> + <section title="Remove send effect chain" anchor="REMOVE SEND_EFFECT_CHAIN" lscp_cmd="true"> <t>The front-end can remove a send effect chain by sending the following command:</t> <t> @@ -7475,7 +7759,7 @@ </t> </section> - <section title="Retrieving information about a send effect chain" anchor="GET SEND_EFFECT_CHAIN INFO"> + <section title="Retrieving information about a send effect chain" anchor="GET SEND_EFFECT_CHAIN INFO" lscp_cmd="true"> <t>The front-end can ask for informations of a send effect chain by sending the following command:</t> <t> @@ -7537,7 +7821,7 @@ </t> </section> - <section title="Append effect instance to a send effect chain" anchor="APPEND SEND_EFFECT_CHAIN EFFECT"> + <section title="Append effect instance to a send effect chain" anchor="APPEND SEND_EFFECT_CHAIN EFFECT" lscp_cmd="true"> <t>The front-end can add an unused effect instance to the end of a send effect chain by sending the following command:</t> <t> @@ -7589,7 +7873,7 @@ </t> </section> - <section title="Insert effect instance to a send effect chain" anchor="INSERT SEND_EFFECT_CHAIN EFFECT"> + <section title="Insert effect instance to a send effect chain" anchor="INSERT SEND_EFFECT_CHAIN EFFECT" lscp_cmd="true"> <t>The front-end can add an unused effect instance to a certain position of a send effect chain by sending the following command:</t> @@ -7644,7 +7928,7 @@ </t> </section> - <section title="Remove effect instance from send effect chain" anchor="REMOVE SEND_EFFECT_CHAIN EFFECT"> + <section title="Remove effect instance from send effect chain" anchor="REMOVE SEND_EFFECT_CHAIN EFFECT" lscp_cmd="true"> <t>The front-end can remove an effect instance from a certain position of a send effect chain by sending the following command:</t> @@ -7708,22 +7992,28 @@ <t>input = <list> - <t>line LF + <t>line </t> - <t>/ line CR LF + <t>/ error </t> </list> </t> <t>line = <list> - <t>/* epsilon (empty line ignored) */ + <t>statement LF + </t> + <t>/ statement CR LF + </t> + </list> +</t> +<t>statement = + <list> + <t>/* epsilon (empty statement/line ignored) */ </t> <t>/ comment </t> <t>/ command </t> - <t>/ error - </t> </list> </t> <t>comment = @@ -7796,6 +8086,10 @@ <list> <t>CHANNEL </t> + <t>/ CHANNEL SP MIDI_INPUT SP sampler_channel SP device_index + </t> + <t>/ CHANNEL SP MIDI_INPUT SP sampler_channel SP device_index SP midi_input_port_index + </t> <t>/ DB_INSTRUMENT_DIRECTORY SP db_path </t> <t>/ DB_INSTRUMENTS SP NON_MODAL SP scan_mode SP db_path SP filename @@ -7972,6 +8266,12 @@ <list> <t>CHANNEL SP sampler_channel </t> + <t>/ CHANNEL SP MIDI_INPUT SP sampler_channel + </t> + <t>/ CHANNEL SP MIDI_INPUT SP sampler_channel SP device_index + </t> + <t>/ CHANNEL SP MIDI_INPUT SP sampler_channel SP device_index SP midi_input_port_index + </t> <t>/ MIDI_INSTRUMENT_MAP SP midi_map </t> <t>/ MIDI_INSTRUMENT_MAP SP ALL @@ -8140,6 +8440,12 @@ </t> <t>/ ECHO SP boolean </t> + <t>/ SHELL SP INTERACT SP boolean + </t> + <t>/ SHELL SP AUTO_CORRECT SP boolean + </t> + <t>/ SHELL SP DOC SP boolean + </t> <t>/ VOLUME SP volume_value </t> <t>/ VOICES SP number @@ -8320,6 +8626,8 @@ </t> <t>/ CHANNELS </t> + <t>/ CHANNEL SP MIDI_INPUTS SP sampler_channel + </t> <t>/ AVAILABLE_ENGINES </t> <t>/ AVAILABLE_EFFECTS @@ -8735,7 +9043,7 @@ <section title="Events" anchor="events"> <t>This chapter will describe all currently defined events supported by LinuxSampler.</t> - <section title="Number of audio output devices changed" anchor="SUBSCRIBE AUDIO_OUTPUT_DEVICE_COUNT"> + <section title="Number of audio output devices changed" anchor="SUBSCRIBE AUDIO_OUTPUT_DEVICE_COUNT" lscp_cmd="true"> <t>Client may want to be notified when the total number of audio output devices on the back-end changes by issuing the following command:</t> <t> @@ -8753,7 +9061,7 @@ of audio output devices.</t> </section> - <section title="Audio output device's settings changed" anchor="SUBSCRIBE AUDIO_OUTPUT_DEVICE_INFO"> + <section title="Audio output device's settings changed" anchor="SUBSCRIBE AUDIO_OUTPUT_DEVICE_INFO" lscp_cmd="true"> <t>Client may want to be notified when changes were made to audio output devices on the back-end by issuing the following command:</t> <t> @@ -8775,7 +9083,7 @@ message is sufficient here.</t> </section> - <section title="Number of MIDI input devices changed" anchor="SUBSCRIBE MIDI_INPUT_DEVICE_COUNT"> + <section title="Number of MIDI input devices changed" anchor="SUBSCRIBE MIDI_INPUT_DEVICE_COUNT" lscp_cmd="true"> <t>Client may want to be notified when the total number of MIDI input devices on the back-end changes by issuing the following command:</t> <t> @@ -8793,7 +9101,7 @@ of MIDI input devices.</t> </section> - <section title="MIDI input device's settings changed" anchor="SUBSCRIBE MIDI_INPUT_DEVICE_INFO"> + <section title="MIDI input device's settings changed" anchor="SUBSCRIBE MIDI_INPUT_DEVICE_INFO" lscp_cmd="true"> <t>Client may want to be notified when changes were made to MIDI input devices on the back-end by issuing the following command:</t> <t> @@ -8815,7 +9123,7 @@ message is sufficient here.</t> </section> - <section title="Number of sampler channels changed" anchor="SUBSCRIBE CHANNEL_COUNT"> + <section title="Number of sampler channels changed" anchor="SUBSCRIBE CHANNEL_COUNT" lscp_cmd="true"> <t>Client may want to be notified when the total number of channels on the back-end changes by issuing the following command:</t> <t> @@ -8833,7 +9141,7 @@ of sampler channels.</t> </section> - <section title="MIDI data on a sampler channel arrived" anchor="SUBSCRIBE CHANNEL_MIDI"> + <section title="MIDI data on a sampler channel arrived" anchor="SUBSCRIBE CHANNEL_MIDI" lscp_cmd="true"> <t>Client may want to be notified when MIDI data arrive on sampler channels on back-end side, by issuing the following command:</t> <t> @@ -8858,7 +9166,7 @@ thread unaffected by this feature.</t> </section> - <section title="MIDI data on a MIDI input device arrived" anchor="SUBSCRIBE DEVICE_MIDI"> + <section title="MIDI data on a MIDI input device arrived" anchor="SUBSCRIBE DEVICE_MIDI" lscp_cmd="true"> <t>Client may want to be notified when MIDI data arrive on MIDI input devices by issuing the following command:</t> <t> <list> @@ -8883,7 +9191,7 @@ thread unaffected by this feature.</t> </section> - <section title="Number of active voices changed" anchor="SUBSCRIBE VOICE_COUNT"> + <section title="Number of active voices changed" anchor="SUBSCRIBE VOICE_COUNT" lscp_cmd="true"> <t>Client may want to be notified when the number of voices on the back-end changes by issuing the following command:</t> <t> @@ -8902,7 +9210,7 @@ active voices on that channel.</t> </section> - <section title="Number of active disk streams changed" anchor="SUBSCRIBE STREAM_COUNT"> + <section title="Number of active disk streams changed" anchor="SUBSCRIBE STREAM_COUNT" lscp_cmd="true"> <t>Client may want to be notified when the number of streams on the back-end changes by issuing the following command: SUBSCRIBE STREAM_COUNT</t> <t> @@ -8921,7 +9229,7 @@ active disk streams on that channel.</t> </section> - <section title="Disk stream buffer fill state changed" anchor="SUBSCRIBE BUFFER_FILL"> + <section title="Disk stream buffer fill state changed" anchor="SUBSCRIBE BUFFER_FILL" lscp_cmd="true"> <t>Client may want to be notified when the buffer fill state of a disk stream on the back-end changes by issuing the following command:</t> <t> @@ -8942,7 +9250,7 @@ "GET CHANNEL BUFFER_FILL PERCENTAGE"</xref> command was issued on this channel.</t> </section> - <section title="Channel information changed" anchor="SUBSCRIBE CHANNEL_INFO"> + <section title="Channel information changed" anchor="SUBSCRIBE CHANNEL_INFO" lscp_cmd="true"> <t>Client may want to be notified when changes were made to sampler channels on the back-end by issuing the following command:</t> <t> @@ -8964,7 +9272,7 @@ message is sufficient here.</t> </section> - <section title="Number of effect sends changed" anchor="SUBSCRIBE FX_SEND_COUNT"> + <section title="Number of effect sends changed" anchor="SUBSCRIBE FX_SEND_COUNT" lscp_cmd="true"> <t>Client may want to be notified when the number of effect sends on a particular sampler channel is changed by issuing the following command:</t> <t> @@ -8983,7 +9291,7 @@ be replaced by the new number of effect sends on that channel.</t> </section> - <section title="Effect send information changed" anchor="SUBSCRIBE FX_SEND_INFO"> + <section title="Effect send information changed" anchor="SUBSCRIBE FX_SEND_INFO" lscp_cmd="true"> <t>Client may want to be notified when changes were made to effect sends on a a particular sampler channel by issuing the following command:</t> <t> @@ -9002,7 +9310,7 @@ be replaced by the numerical ID of the changed effect send.</t> </section> - <section title="Total number of active voices changed" anchor="SUBSCRIBE TOTAL_VOICE_COUNT"> + <section title="Total number of active voices changed" anchor="SUBSCRIBE TOTAL_VOICE_COUNT" lscp_cmd="true"> <t>Client may want to be notified when the total number of voices on the back-end changes by issuing the following command:</t> <t> @@ -9020,7 +9328,7 @@ all currently active voices.</t> </section> - <section title="Total number of active disk streams changed" anchor="SUBSCRIBE TOTAL_STREAM_COUNT"> + <section title="Total number of active disk streams changed" anchor="SUBSCRIBE TOTAL_STREAM_COUNT" lscp_cmd="true"> <t>Client may want to be notified when the total number of disk streams on the back-end changes by issuing the following command:</t> <t> @@ -9038,7 +9346,7 @@ all currently active disk streams.</t> </section> - <section title="Number of MIDI instrument maps changed" anchor="SUBSCRIBE MIDI_INSTRUMENT_MAP_COUNT"> + <section title="Number of MIDI instrument maps changed" anchor="SUBSCRIBE MIDI_INSTRUMENT_MAP_COUNT" lscp_cmd="true"> <t>Client may want to be notified when the number of MIDI instrument maps on the back-end changes by issuing the following command:</t> <t> @@ -9056,7 +9364,7 @@ of MIDI instrument maps.</t> </section> - <section title="MIDI instrument map information changed" anchor="SUBSCRIBE MIDI_INSTRUMENT_MAP_INFO"> + <section title="MIDI instrument map information changed" anchor="SUBSCRIBE MIDI_INSTRUMENT_MAP_INFO" lscp_cmd="true"> <t>Client may want to be notified when changes were made to MIDI instrument maps on the back-end by issuing the following command:</t> <t> @@ -9078,7 +9386,7 @@ message is sufficient here.</t> </section> - <section title="Number of MIDI instruments changed" anchor="SUBSCRIBE MIDI_INSTRUMENT_COUNT"> + <section title="Number of MIDI instruments changed" anchor="SUBSCRIBE MIDI_INSTRUMENT_COUNT" lscp_cmd="true"> <t>Client may want to be notified when the number of MIDI instrument maps on the back-end changes by issuing the following command:</t> <t> @@ -9097,7 +9405,7 @@ the new number of MIDI instruments in the specified map.</t> </section> - <section title="MIDI instrument information changed" anchor="SUBSCRIBE MIDI_INSTRUMENT_INFO"> + <section title="MIDI instrument information changed" anchor="SUBSCRIBE MIDI_INSTRUMENT_INFO" lscp_cmd="true"> <t>Client may want to be notified when changes were made to MIDI instruments on the back-end by issuing the following command:</t> <t> @@ -9120,7 +9428,7 @@ message is sufficient here.</t> </section> - <section title="Global settings changed" anchor="SUBSCRIBE GLOBAL_INFO"> + <section title="Global settings changed" anchor="SUBSCRIBE GLOBAL_INFO" lscp_cmd="true"> <t>Client may want to be notified when changes to the global settings of the sampler were made by issuing the following command:</t> <t> @@ -9151,7 +9459,7 @@ </t> </section> - <section title="Number of database instrument directories changed" anchor="SUBSCRIBE DB_INSTRUMENT_DIRECTORY_COUNT"> + <section title="Number of database instrument directories changed" anchor="SUBSCRIBE DB_INSTRUMENT_DIRECTORY_COUNT" lscp_cmd="true"> <t>Client may want to be notified when the number of instrument directories in a particular directory in the instruments database is changed by issuing the following command:</t> @@ -9173,7 +9481,7 @@ is not sent for the subdirectories in that directory.</t> </section> - <section title="Database instrument directory information changed" anchor="SUBSCRIBE DB_INSTRUMENT_DIRECTORY_INFO"> + <section title="Database instrument directory information changed" anchor="SUBSCRIBE DB_INSTRUMENT_DIRECTORY_INFO" lscp_cmd="true"> <t>Client may want to be notified when changes were made to directories in the instruments database by issuing the following command:</t> <t> @@ -9203,7 +9511,7 @@ the new name of the directory, encapsulated into apostrophes.</t> </section> - <section title="Number of database instruments changed" anchor="SUBSCRIBE DB_INSTRUMENT_COUNT"> + <section title="Number of database instruments changed" anchor="SUBSCRIBE DB_INSTRUMENT_COUNT" lscp_cmd="true"> <t>Client may want to be notified when the number of instruments in a particular directory in the instruments database is changed by issuing the following command:</t> @@ -9225,7 +9533,7 @@ is not sent for the instruments in that directory.</t> </section> - <section title="Database instrument information changed" anchor="SUBSCRIBE DB_INSTRUMENT_INFO"> + <section title="Database instrument information changed" anchor="SUBSCRIBE DB_INSTRUMENT_INFO" lscp_cmd="true"> <t>Client may want to be notified when changes were made to instruments in the instruments database by issuing the following command:</t> <t> @@ -9255,7 +9563,7 @@ the new name of the instrument, encapsulated into apostrophes.</t> </section> - <section title="Database job status information changed" anchor="SUBSCRIBE DB_INSTRUMENTS_JOB_INFO"> + <section title="Database job status information changed" anchor="SUBSCRIBE DB_INSTRUMENTS_JOB_INFO" lscp_cmd="true"> <t>Client may want to be notified when the status of particular database instruments job is changed by issuing the following command:</t> <t> @@ -9277,7 +9585,7 @@ message is sufficient here.</t> </section> - <section title="Number of effect instances changed" anchor="SUBSCRIBE EFFECT_INSTANCE_COUNT"> + <section title="Number of effect instances changed" anchor="SUBSCRIBE EFFECT_INSTANCE_COUNT" lscp_cmd="true"> <t>Client may want to be notified when the number of effect instances is changed by issuing the following command:</t> <t> @@ -9295,7 +9603,7 @@ of effect instances.</t> </section> - <section title="Effect instance information changed" anchor="SUBSCRIBE EFFECT_INSTANCE_INFO"> + <section title="Effect instance information changed" anchor="SUBSCRIBE EFFECT_INSTANCE_INFO" lscp_cmd="true"> <t>Client may want to be notified when changes were made to effect instances on the back-end by issuing the following command:</t> <t> @@ -9313,7 +9621,7 @@ of the effect instance.</t> </section> - <section title="Number of send effect chains changed" anchor="SUBSCRIBE SEND_EFFECT_CHAIN_COUNT"> + <section title="Number of send effect chains changed" anchor="SUBSCRIBE SEND_EFFECT_CHAIN_COUNT" lscp_cmd="true"> <t>Client may want to be notified when the number of send effect chains is changed by issuing the following command:</t> <t> @@ -9332,7 +9640,7 @@ <chains> will be replaced by the new number of send effect chains.</t> </section> - <section title="Send effect chain information changed" anchor="SUBSCRIBE SEND_EFFECT_CHAIN_INFO"> + <section title="Send effect chain information changed" anchor="SUBSCRIBE SEND_EFFECT_CHAIN_INFO" lscp_cmd="true"> <t>Client may want to be notified when changes were made to send effect chains on the back-end by issuing the following command:</t> <t> @@ -9354,7 +9662,7 @@ </t> </section> - <section title="Miscellaneous and debugging events" anchor="SUBSCRIBE MISCELLANEOUS"> + <section title="Miscellaneous and debugging events" anchor="SUBSCRIBE MISCELLANEOUS" lscp_cmd="true"> <t>Client may want to be notified of miscellaneous and debugging events occurring at the server by issuing the following command:</t> <t>
View file
linuxsampler-2342.tar.bz2/Doxyfile.in -> linuxsampler-2718.tar.bz2/Doxyfile.in
Changed
@@ -64,8 +64,10 @@ INPUT = @top_srcdir@/src/Sampler.h \ @top_srcdir@/src/EventListeners.h \ @top_srcdir@/src/common/global.h \ + @top_srcdir@/src/common/lsatomic.h \ @top_srcdir@/src/common/Exception.h \ @top_srcdir@/src/common/Thread.h \ + @top_srcdir@/src/common/SynchronizedConfig.h \ @top_srcdir@/src/drivers/Device.h \ @top_srcdir@/src/drivers/DeviceParameter.h \ @top_srcdir@/src/drivers/audio/AudioChannel.h \
View file
linuxsampler-2342.tar.bz2/Makefile.am -> linuxsampler-2718.tar.bz2/Makefile.am
Changed
@@ -37,6 +37,7 @@ # generate parser with yacc parser: @cd $(srcdir)/src/network && make $@ + @cd $(srcdir)/src/scriptvm && make $@ # compile test cases for the LinuxSampler codebase testcases:
View file
linuxsampler-2342.tar.bz2/Makefile.cvs -> linuxsampler-2718.tar.bz2/Makefile.cvs
Changed
@@ -4,16 +4,17 @@ all: configure -configure: configure.in m4/arts.m4 m4/nptl_bug.m4 m4/pthread.m4 +configure: configure.ac m4/arts.m4 m4/nptl_bug.m4 m4/pthread.m4 @aclocal -I m4 @libtoolize --force --copy @autoheader @automake --add-missing --copy @autoconf - @scripts/generate_parser.sh + @scripts/generate_lscp_parser.sh + @scripts/generate_instrument_script_parser.sh ###################################################################### - # If you are compiling LinuxSampler from CVS, make sure to use the # - # latest version of libgig from CVS as well ! # + # If you are compiling LinuxSampler from SVN, make sure to use the # + # latest version of libgig from SVN as well ! # ###################################################################### clean: @@ -21,6 +22,9 @@ @rm -rvf *.cache *.log *.status *.m4 stamp-h* config.* @rm -rvf `find . -name Makefile.in` @rm -rvf missing install-sh mkinstalldirs depcomp ltmain.sh configure + @rm -rvf src/scriptvm/parser.cpp src/scriptvm/parser.h + @rm -rvf src/scriptvm/scanner.cpp @rm -rvf src/network/lscpparser.cpp src/network/lscpsymbols.h + @rm -rvf src/network/lscp_shell_reference.cpp @rm -rvf linuxsampler-*.tar.* @rm -rvf Doxyfile
View file
linuxsampler-2718.tar.bz2/configure.ac
Added
@@ -0,0 +1,1455 @@ +#------------------------------------------------------------------------------------ +# LinuxSampler's / liblinuxsampler's "official" release version: + +m4_define(linuxsampler_release_major, 1) +m4_define(linuxsampler_release_minor, 0) +m4_define(linuxsampler_release_build, 0.svn60) + + +AC_INIT(linuxsampler,linuxsampler_release_major.linuxsampler_release_minor.linuxsampler_release_build) +AC_CONFIG_SRCDIR(configure.ac) + +#------------------------------------------------------------------------------------ +# The following is the libtool / shared library version. This doesn't have to +# do anything with the release version. It MUST conform to the following rules: +# +# 1. Start with version information of `0:0:0' for each libtool library. +# 2. Update the version information only immediately before a public release of +# your software. More frequent updates are unnecessary, and only guarantee +# that the current interface number gets larger faster. +# 3. If the library source code has changed at all since the last update, then +# increment revision (`c:r:a' becomes `c:r+1:a'). +# 4. If any interfaces have been added, removed, or changed since the last update, +# increment current, and set revision to 0. +# 5. If any interfaces have been added since the last public release, then increment +# age. +# 6. If any interfaces have been removed since the last public release, then set age +# to 0. + +LIBLINUXSAMPLER_LT_CURRENT=3 +LIBLINUXSAMPLER_LT_REVISION=0 +LIBLINUXSAMPLER_LT_AGE=0 +SHARED_VERSION_INFO="$LIBLINUXSAMPLER_LT_CURRENT:$LIBLINUXSAMPLER_LT_REVISION:$LIBLINUXSAMPLER_LT_AGE" + +#------------------------------------------------------------------------------------ +# the LSCP specification version this LinuSampler release complies with: + +LSCP_RELEASE_MAJOR=1 +LSCP_RELEASE_MINOR=6 + +AC_DEFINE_UNQUOTED(LSCP_RELEASE_MAJOR, ${LSCP_RELEASE_MAJOR}, LSCP spec major version this release complies with.) +AC_DEFINE_UNQUOTED(LSCP_RELEASE_MINOR, ${LSCP_RELEASE_MINOR}, LSCP spec minor version this release complies with.) + +AM_INIT_AUTOMAKE(subdir-objects) +AC_PROG_CC +AC_PROG_CXX +AC_LIBTOOL_WIN32_DLL +AC_PROG_LIBTOOL +AC_PROG_LEX +AC_PROG_YACC + +AC_SUBST(SHLIB_VERSION_ARG) +AC_SUBST(SHARED_VERSION_INFO) + +module=yes eval LIB_EXT=$shrext_cmds +AC_SUBST(LIB_EXT) + +AC_C_BIGENDIAN +AC_CANONICAL_HOST + +PKG_PROG_PKG_CONFIG + +########################################################################### +# General Checks + +AM_CONDITIONAL(CROSS_COMPILING, test $cross_compiling = yes) + +AC_MSG_CHECKING(whether x86 architecture) +def_arch_x86=0 +case $host_cpu in + "i386" | "i486" | "i586" | "i686" | "i786" | "x86_64") + echo "yes" + def_arch_x86=1;; + *) + echo "no";; +esac +AC_DEFINE_UNQUOTED(ARCH_X86,$def_arch_x86,Define to 1 if you build for x86 architecture.) + +# determine the right gcc switch for CPU specific optimizations +# (only if the user did not provide one) +CXX_CPU_SWITCH= +if ! echo "X $CXXFLAGS " | grep -q -- " \(-march=\|-mcpu=\|-mtune=\|-arch=\)" ; then + if test "$def_arch_x86" = 1 -a "$host_cpu" != "x86_64"; then + CXX_CPU_SWITCH="-march=$host_cpu" + elif test "$target_cpu" = "ppc"; then + CXX_CPU_SWITCH="-arch=$host_cpu" + fi +fi +AC_SUBST(CXX_CPU_SWITCH) + +mac=no +linux=no +case "$host" in + *-*-darwin*) + mac=yes + ;; + *-*-linux*) + linux=yes + ;; +esac +AM_CONDITIONAL(LINUX, test "$linux" = yes) +AM_CONDITIONAL(MAC, test "$mac" = yes) + +# check if we're on MS Windows +AC_CHECK_HEADERS( + mmsystem.h, + have_windows=1, + have_windows=0, + #include <windef.h> +) +AM_CONDITIONAL(HAVE_WINDOWS, test $have_windows = "1") + +AC_MSG_CHECKING(whether UNIX98 compatible) +AC_LANG_PUSH(C) +AC_RUN_IFELSE(AC_LANG_SOURCE( +#ifndef _GNU_SOURCE +#define _GNU_SOURCE 1 +#endif +#include <features.h> +void main(void) { +#if _XOPEN_SOURCE >= 500 +exit(0); /* UNIX98 compatible */ +#else +exit(-1); /* not UNIX98 compatible */ +#endif +} +), +have_unix98="yes", +have_unix98="no", +have_unix98="no" +) +AC_LANG_POP(C) +AC_MSG_RESULT($have_unix98) +if test "$have_unix98" = "no" -a "$have_windows" = "0" -a "$mac" = "no" ; then + if test "x$HAVE_UNIX98" = "x"; then + echo "LinuxSampler only runs on UNIX98 compatible systems, which is mandatory for" + echo "pthread_mutexattr_settype() call in Mutex.cpp. You may want to run" + echo "./configure with environment variable HAVE_UNIX98=1 in case you think you" + echo "have a UNIX98 compatible system." + exit -1; + fi +fi + +# check for <features.h> +AC_CHECK_HEADERS(features.h) + +# test for POSIX thread library +m4_ifdef(m4_include(m4/pthread.m4),, + sinclude(m4/pthread.m4)) +ACX_PTHREAD +LIBS="$PTHREAD_LIBS $LIBS" +CFLAGS="$CFLAGS $PTHREAD_CFLAGS" +CXXFLAGS="$CXXFLAGS $PTHREAD_CFLAGS" +CC="$PTHREAD_CC" + +# check for a bug in NPTL-enabled glibc +# (see Gentoo bug report #194076) +AC_ARG_ENABLE(nptl-bug-check, + --disable-nptl-bug-check + Disable check for a bug in certain NPTL-enabled + glibc versions that caused crashs., + config_check_nptl_bug="$enableval", + config_check_nptl_bug="yes" +) +if test "$config_check_nptl_bug" = "yes"; then + m4_ifdef(m4_include(m4/nptl_bug.m4),, + sinclude(m4/nptl_bug.m4)) + ACX_NPTL_GLIBC_BUG( + echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + echo "You seem to have a buggy PTHREAD library! LinuxSampler would" + echo "probably crash due to this. Please report us the system you are" + echo "using and/or file a bug report to the bug tracking system of" + echo "your distribution." + echo "If you have a NPTL-enabled glibc AND it was compiled for TLS as" + echo "well, you can try to circumvent this problem for now by setting" + echo "the environment variable LD_ASSUME_KERNEL=\"2.4.1\" , which" + echo "should cause this test to pass." + echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + AC_MSG_ERROR(possibly NPTL glibc bug detected) + ) +else + echo "NPTL glibc bug check disabled" +fi + +# FIXME: this is actually a dependency of libgig, not of LS directly, why +# isn't it hidden by libgig? +AC_CHECK_HEADERS(uuid/uuid.h) +AC_SEARCH_LIBS(uuid_generate, uuid) + +# In case Bison is available, determine the exact version, since we need to +# use different custom parser code for Bison 2.x vs. Bison 3.x generated +# parser yacc tables. +if echo "$YACC" | grep -q bison; then + # NOTE: m4 removes , that's why it needs to be escaped + bison_version=`$YACC --version | head -n 1 | sed -e 's/^0-9.*\(-0-9.\+\)$/\1/'` + bison_version_major=`echo $bison_version | cut -d. -f1` + bison_version_minor=`echo $bison_version | cut -d. -f2` + AC_DEFINE_UNQUOTED(HAVE_BISON_MAJ,$bison_version_major,Define to the major version of the GNU Bison program installed.) + AC_DEFINE_UNQUOTED(HAVE_BISON_MIN,$bison_version_minor,Define to the minor version of the GNU Bison program installed.) +fi + + + +########################################################################### +# Checks for available audio and MIDI systems / drivers +# (we throw an error if there's not at least one system for audio output and MIDI input available) + +have_midi_input_driver="false" +have_audio_output_driver="false" + +# ALSA +AC_ARG_ENABLE(alsa-driver, + --disable-alsa-driver + Disable support for the Advanced Linux Sound + Architecture (ALSA)., + config_alsa_driver="$enableval", + config_alsa_driver="yes" +) +have_alsa=0 +if test "$config_alsa_driver" = "yes"; then + AC_CHECK_HEADER(alsa/asoundlib.h, + AC_CHECK_LIB(asound, main, + have_alsa=1 + , + have_alsa=0 + ) + , + have_alsa=0 + ) + if test "$have_alsa" = "1"; then + have_midi_input_driver="true" + have_audio_output_driver="true"; + fi + + echo -n "checking Alsa version... " + AC_LANG_PUSH(C) + AC_RUN_IFELSE(AC_LANG_SOURCE( + #include <alsa/asoundlib.h> + void main(void) { + /* ensure backward compatibility */ + #if !defined(SND_LIB_MAJOR) && defined(SOUNDLIB_VERSION_MAJOR) + #define SND_LIB_MAJOR SOUNDLIB_VERSION_MAJOR + #endif + exit(SND_LIB_MAJOR); + } + ), + alsa_major=0, + alsa_major=$?, + alsa_major=0 + ) + AC_RUN_IFELSE(AC_LANG_SOURCE( + #include <alsa/asoundlib.h> + void main(void) { + /* ensure backward compatibility */ + #if !defined(SND_LIB_MINOR) && defined(SOUNDLIB_VERSION_MINOR) + #define SND_LIB_MINOR SOUNDLIB_VERSION_MINOR + #endif + exit(SND_LIB_MINOR); + } + ), + alsa_minor=0, + alsa_minor=$?, + alsa_minor=0 + ) + AC_RUN_IFELSE(AC_LANG_SOURCE( + #include <alsa/asoundlib.h> + void main(void) { + /* ensure backward compatibility */ + #if !defined(SND_LIB_SUBMINOR) && defined(SOUNDLIB_VERSION_SUBMINOR) + #define SND_LIB_SUBMINOR SOUNDLIB_VERSION_SUBMINOR + #endif + exit(SND_LIB_SUBMINOR); + } + ), + alsa_subminor=0, + alsa_subminor=$?, + alsa_subminor=0 + ) + AC_LANG_POP(C) + echo "$alsa_major.$alsa_minor.$alsa_subminor"; + AC_DEFINE_UNQUOTED(ALSA_MAJOR,$alsa_major,Define to the major version number of your Alsa installation.) + AC_DEFINE_UNQUOTED(ALSA_MINOR,$alsa_minor,Define to the minor version number of your Alsa installation.) + AC_DEFINE_UNQUOTED(ALSA_SUBMINOR,$alsa_subminor,Define to the subminor version number of your Alsa installation.) +else + echo "ALSA support disabled by configure script parameter" +fi +AM_CONDITIONAL(HAVE_ALSA, test $have_alsa = "1") +AC_DEFINE_UNQUOTED(HAVE_ALSA,$have_alsa,Define to 1 if you have ALSA installed.) +config_have_alsa="no" +if test $have_alsa = "1"; then + config_have_alsa="yes" +fi + +# JACK +AC_ARG_ENABLE(jack-driver, + --disable-jack-driver + Disable support for the Jack Audio Connection Kit + (JACK)., + config_jack_driver="$enableval", + config_jack_driver="yes" +) +have_jack=0 +if test "$config_jack_driver" = "yes"; then + PKG_CHECK_MODULES(JACK, jack, have_jack=1, have_jack=0) + if test $have_jack = "1"; then + AC_SUBST(JACK_LIBS) + AC_SUBST(JACK_CFLAGS) + linuxsampler_save_LIBS=$LIBS + LIBS="$JACK_LIBS $LIBS" + AC_CHECK_FUNCS(jack_client_name_size jack_client_open \ + jack_on_info_shutdown) + LIBS=$linuxsampler_save_LIBS + have_audio_output_driver="true"; + fi +else + echo "JACK support disabled by configure script parameter" +fi +AM_CONDITIONAL(HAVE_JACK, test $have_jack = "1") +AC_DEFINE_UNQUOTED(HAVE_JACK,$have_jack,Define to 1 if you have JACK installed.) +config_have_jack="no" +if test $have_jack = "1"; then + config_have_jack="yes" +fi + +# JACK MIDI +have_jack_midi=0 +if test $have_jack = "1"; then + linuxsampler_save_CFLAGS=$CFLAGS + linuxsampler_save_LIBS=$LIBS + CFLAGS="$JACK_CFLAGS $CFLAGS" + LIBS="$JACK_LIBS $LIBS" + AC_CHECK_HEADER(jack/midiport.h, have_jack_midi=1, have_jack_midi=0) + if test $have_jack_midi = "1"; then + AC_CHECK_FUNCS(jack_midi_get_event_count) + AC_COMPILE_IFELSE(AC_LANG_PROGRAM(#include <jack/midiport.h>, + jack_midi_clear_buffer(0, 0); + ), AC_DEFINE(JACK_MIDI_FUNCS_NEED_NFRAMES, 1, + Define to 1 if you have the old jack midi functions that need an nframes argument.)) + have_midi_input_driver="true" + fi + CFLAGS=$linuxsampler_save_CFLAGS + LIBS=$linuxsampler_save_LIBS +fi +AM_CONDITIONAL(HAVE_JACK_MIDI, test $have_jack_midi = "1") +AC_DEFINE_UNQUOTED(HAVE_JACK_MIDI, $have_jack_midi, + Define to 1 if you have JACK with MIDI support installed.) +config_have_jack_midi="no" +if test $have_jack_midi = "1"; then + config_have_jack_midi="yes" +fi + +# ARTS +AC_ARG_ENABLE(arts-driver, + --disable-arts-driver + Disable support for the Analogue Realtime System + (aRts)., + config_arts_driver="$enableval", + config_arts_driver="yes" +) +have_arts=0 +if test "$config_arts_driver" = "yes"; then + m4_ifdef(m4_include(m4/arts.m4),, + sinclude(m4/arts.m4)) + AM_PATH_ARTS(0.9.5, have_arts=1, have_arts=0) + if test "$have_arts" = "1"; then + have_audio_output_driver="true" + fi +else + echo "ARTS support disabled by configure script parameter" +fi +AM_CONDITIONAL(HAVE_ARTS, test "$have_arts" = "1") +AC_DEFINE_UNQUOTED(HAVE_ARTS,$have_arts,Define to 1 if you have aRts installed.) +config_have_arts="no" +if test $have_arts = "1"; then + config_have_arts="yes" +fi + +# ASIO AUDIO (Win32) +AC_ARG_ENABLE(asiosdk-dir, + --enable-asiosdk-dir + Directory where the ASIO SDK is located, this automatically + enables the compilation of the ASIO audio output driver., + config_asiosdk_dir="${enableval}", + config_asiosdk_dir="." +) +AC_SUBST(config_asiosdk_dir) + +AC_ARG_ENABLE(asio-driver, + --disable-asio-driver + Disable support for the Windows ASIO driver., + config_asio_driver="$enableval", + config_asio_driver="yes" +) +have_asio=0 +ASIOSDK_BASEDIR= +if test "$config_asio_driver" = "yes"; then + asiosdk_headerfile=$config_asiosdk_dir/ASIOSDK2/common/asio.h + echo -n "checking for ASIO headerfile: $asiosdk_headerfile ...." + if test -e $asiosdk_headerfile ; then + echo yes + have_asio=1 + ASIOSDK_BASEDIR="$config_asiosdk_dir" + else + echo no + have_asio=0 + fi + if test "$have_asio" = "1"; then + have_audio_output_driver="true" + fi +else + echo "Windows ASIO support disabled by configure script parameter" +fi +AC_SUBST(ASIOSDK_BASEDIR) +AM_CONDITIONAL(HAVE_ASIO, test $have_asio = "1") +AC_DEFINE_UNQUOTED(HAVE_ASIO,$have_asio,Define to 1 if you have ASIO installed.) +config_have_asio="no" +if test $have_asio = "1"; then + config_have_asio="yes" +fi + +# MidiShare (Linux, OS X, Windows) +AC_ARG_ENABLE(midishare-driver, + --disable-midishare-driver + Disable support for the MidiShare system., + config_midishare_driver="$enableval", + config_midishare_driver="yes" +) +have_midishare=0 +if test "$config_midishare_driver" = "yes"; then + AC_CHECK_HEADER(MidiShare.h, + AC_CHECK_LIB(MidiShare, MidiCountEvs, + have_midishare=1, + have_midishare=0 + ) + , + have_midishare=0 + ) + if test "$have_midishare" = "1"; then + have_midi_input_driver="true" + fi +else + echo "MidiShare support disabled by configure script parameter" +fi +AM_CONDITIONAL(HAVE_MIDISHARE, test $have_midishare = "1") +AC_DEFINE_UNQUOTED(HAVE_MIDISHARE,$have_midishare,Define to 1 if you have MidiShare installed.) +config_have_midishare="no" +if test $have_midishare = "1"; then + config_have_midishare="yes" +fi + +# CoreMIDI (OS X) +AC_ARG_ENABLE(coremidi-driver, + --disable-coremidi-driver + Disable support for the Apple CoreMIDI system., + config_coremidi_driver="$enableval", + config_coremidi_driver="yes" +) +have_coremidi=0 +if test "$config_coremidi_driver" = "yes"; then + AC_CHECK_HEADER(CoreMIDI/CoreMIDI.h, + have_coremidi=1, + have_coremidi=0 + ) + if test "$have_coremidi" = "1"; then + have_midi_input_driver="true" + fi +else + echo "CoreMIDI support disabled by configure script parameter" +fi +AM_CONDITIONAL(HAVE_COREMIDI, test $have_coremidi = "1") +AC_DEFINE_UNQUOTED(HAVE_COREMIDI,$have_coremidi,Define to 1 if you have CoreMIDI installed.) +config_have_coremidi="no" +if test $have_coremidi = "1"; then + config_have_coremidi="yes" +fi + +# CoreAudio (OS X) +AC_ARG_ENABLE(coreaudio-driver, + --disable-coreaudio-driver + Disable support for the Apple CoreAudio system., + config_coreaudio_driver="$enableval", + config_coreaudio_driver="yes" +) +have_coreaudio=0 +if test "$config_coreaudio_driver" = "yes"; then + AC_CHECK_HEADER(CoreAudio/CoreAudio.h, + have_coreaudio=1, + have_coreaudio=0 + ) + if test "$have_coreaudio" = "1"; then + have_audio_output_driver="true" + fi +else + echo "CoreAudio support disabled by configure script parameter" +fi +AM_CONDITIONAL(HAVE_COREAUDIO, test $have_coreaudio = "1") +AC_DEFINE_UNQUOTED(HAVE_COREAUDIO,$have_coreaudio,Define to 1 if you have CoreAudio installed.) +config_have_coreaudio="no" +if test $have_coreaudio = "1"; then + config_have_coreaudio="yes" +fi + +# MME MIDI (Win32) +AC_ARG_ENABLE(mmemidi-driver, + --disable-mmemidi-driver + Disable support for the Windows MME MIDI system., + config_mmemidi_driver="$enableval", + config_mmemidi_driver="yes" +) +have_mmemidi=0 +if test "$config_mmemidi_driver" = "yes"; then + AC_CHECK_HEADERS(mmsystem.h, + have_mmemidi=1, + have_mmemidi=0 + ) + if test "$have_mmemidi" = "1"; then + have_midi_input_driver="true" + fi +else + echo "MME MIDI support disabled by configure script parameter" +fi +AM_CONDITIONAL(HAVE_MME_MIDI, test $have_mmemidi = "1") +AC_DEFINE_UNQUOTED(HAVE_MME_MIDI,$have_mmemidi,Define to 1 if you have MME MIDI installed.) +config_have_mme="no" +if test $have_mmemidi = "1"; then + config_have_mme="yes" +fi + +# DSSI +AC_CHECK_HEADERS(dssi.h, + config_have_dssi="yes", + config_have_dssi="no") +AM_CONDITIONAL(HAVE_DSSI, test $config_have_dssi = "yes") + +# LV2 +PKG_CHECK_MODULES(LV2, lv2 >= 1.0.0, config_have_lv2="yes", config_have_lv2="no") +AC_SUBST(LV2_CFLAGS) +AC_SUBST(LV2_LIBS) +AM_CONDITIONAL(HAVE_LV2, test $config_have_lv2 = "yes") + +# VST +AC_ARG_ENABLE(vstsdk-dir, + --enable-vstsdk-dir + Directory where the VST SDK is located. + This automatically enables the compilation + of the VST plugin., + VSTSDK_DIR="${enableval}", + VSTSDK_DIR= +) +AC_SUBST(VSTSDK_DIR) +AM_CONDITIONAL(HAVE_VST, test "x$VSTSDK_DIR" != "x") +config_have_vst="no" +if test "x$VSTSDK_DIR" != "x"; then + config_have_vst="yes" +fi + +# AU +AC_CHECK_HEADERS(AudioUnit/AudioUnit.h, + config_have_au="yes", + config_have_au="no") +AM_CONDITIONAL(HAVE_AU, test $config_have_au = "yes") +AM_CONDITIONAL(HAVE_AUFLAGS, test "$AUFLAGS" != "") + +if test $config_have_au = "yes" ; then + if test "x$DEVELOPER_EXTRAS_DIR" = "x" ; then + if test -d /Developer/Extras ; then + DEVELOPER_EXTRAS_DIR=/Developer/Extras + else + DEVELOPER_EXTRAS_DIR=/Developer/Examples + fi + fi + AC_SUBST(DEVELOPER_EXTRAS_DIR) +fi + +if test "x$MAC_PLUGIN_INSTALL_DIR" = "x" ; then + MAC_PLUGIN_INSTALL_DIR=/Library/Audio/Plug-Ins +fi +AC_SUBST(MAC_PLUGIN_INSTALL_DIR) + +# have we found at least one MIDI input and one audio output driver ? +if test "$have_midi_input_driver" = "false"; then + echo "No supported MIDI input system found!" + echo "Sorry, LinuxSampler only supports the following MIDI drivers at the moment:" + echo "ALSA, JACK, MIDIShare, CoreMIDI, MME." + echo "If you think you have one of those available on your system, make sure you" + echo "also have the respective development (header) files installed." + exit -1; +fi +if test "$have_audio_output_driver" = "false"; then + echo "No supported audio output system found!" + echo "Sorry, LinuxSampler only supports ALSA, JACK, ARTS and ASIO as audio output" + echo "driver at the moment!" + exit -1; +fi + + + +########################################################################### +# Checks for various DLL libraries + +# Check presence of libgig +libgig_version="3.3.0" +PKG_CHECK_MODULES(GIG, gig >= $libgig_version, HAVE_GIG=true, HAVE_GIG=false) +if test "$HAVE_GIG" = "false"; then + echo "Required libgig version not found!" + echo "You need to have libgig version ${libgig_version} installed!" + exit -1; +else + echo "yes, found libgig $libgig_version" +fi +AC_SUBST(GIG_CFLAGS) +AC_SUBST(GIG_LIBS) + +# SF2 Engine (requires libgig) +AC_ARG_ENABLE(sf2-engine, + --disable-sf2-engine + Disable compilation of the SF2 engine. + You need to have libgig installed., + config_sf2_engine="$enableval", + config_sf2_engine="yes" +) +HAVE_SF2=0; +if test "$config_sf2_engine" = "yes"; then + HAVE_SF2=1 +else + echo "SF2 engine disabled by configure script parameter" +fi +AM_CONDITIONAL(HAVE_SF2, test $HAVE_SF2 = 1) +AC_DEFINE_UNQUOTED(HAVE_SF2,$HAVE_SF2,Define to 1 if you want SF2 engine and have libsf2 installed.) +config_have_sf2="no" +if test $HAVE_SF2 = "1"; then + config_have_sf2="yes" +fi + +# Check presence of libsndfile +libsndfile_version="1.0" +PKG_CHECK_MODULES(SNDFILE, sndfile >= $libsndfile_version, HAVE_SNDFILE=true, HAVE_SNDFILE=false) +if test "$HAVE_SNDFILE" = "false"; then + echo "Required libsndfile version not found!" + echo "You need to have libsndfile version ${libsndfile_version} installed!" + exit -1; +else + echo "yes, found libsndfile $libsndfile_version" +fi +AC_SUBST(SNDFILE_CFLAGS) +AC_SUBST(SNDFILE_LIBS) + +# Check for Vorbis and FLAC support in libsndfile +linuxsampler_save_CFLAGS=$CFLAGS +CFLAGS="$SNDFILE_CFLAGS $CFLAGS" +AC_CHECK_DECLS(SF_FORMAT_VORBIS, SF_FORMAT_FLAC, , , #include <sndfile.h>) + +# Check for loop functionality in libsndfile +AC_CHECK_MEMBERS(SF_INSTRUMENT.loops,, + AC_MSG_WARN(Your version of libsndfile does not support + reading of loop information. LinuxSampler will not be able to + extract loop information from sample files.), + #include <sndfile.h>) +CFLAGS=$linuxsampler_save_CFLAGS + +# Instruments DB feature (requires SQLite 3.3) +AC_ARG_ENABLE(instruments-db, + --disable-instruments-db + Disable compilation of the sampler's instruments + database feature. You need to have SQLite 3.3 + or younger installed for this feature., + config_instruments_db="$enableval", + config_instruments_db="yes" +) +HAVE_SQLITE3=0; +if test "$config_instruments_db" = "yes"; then + # Check presence of sqlite3 + sqlite_version="3.3" + PKG_CHECK_MODULES(SQLITE3, sqlite3 >= $sqlite_version, HAVE_SQLITE3=true, HAVE_SQLITE3=false) + AC_SUBST(SQLITE3_LIBS) + AC_SUBST(SQLITE3_CFLAGS) + if test $HAVE_SQLITE3 = false; then + HAVE_SQLITE3=0 + config_instruments_db="no" + echo "*** Required sqlite version not found!" + echo "*** You need to have sqlite version ${sqlite_version} or higher" + echo "*** for instruments database support to be enabled." + echo "*** Support for instruments DB will be disabled!" + else + HAVE_SQLITE3=1 + fi +else + echo "Instruments DB feature disabled by configure script parameter" +fi +AM_CONDITIONAL(HAVE_SQLITE3, test $HAVE_SQLITE3 = 1) +AC_DEFINE_UNQUOTED(HAVE_SQLITE3,$HAVE_SQLITE3,Define to 1 if you want the instruments DB feature and have SQLITE3 installed.) + + + +########################################################################### +# Handle Configuration Options + +# TODO: should we use AC_ARG_VAR(variable, description) instead? + +AC_ARG_ENABLE(asm, + --disable-asm + Enable hand-crafted assembly optimizations + (default=on). LinuxSampler provides CPU specific + assembly optimizations. This is currently limited + to just enter a fast (denormal) FPU mode on x86 + platforms. There are currently no synthesis core + assembly optimizations anymore since LS 0.4.0, + config_asm="$enableval", + config_asm="yes" +) +if test "$config_asm" = "yes"; then + AC_DEFINE_UNQUOTED(CONFIG_ASM, 1, Define to 1 if you want to enable asm optimizations.) +fi + +AC_ARG_ENABLE(dev-mode, + --enable-dev-mode + Enable development mode (default=off). In that mode + we do some extra sanity checks here and there. + This helps to spot possible problems, but reduces + efficiency a bit, + config_dev_mode="$enableval", + config_dev_mode="no" +) +if test "$config_dev_mode" = "yes"; then + AC_DEFINE_UNQUOTED(CONFIG_DEVMODE, 1, Define to 1 if you want to enable development mode.) +fi + +AC_ARG_ENABLE(debug-level, + --enable-debug-level + Specify verbosity of console messages (default=1). + The higher the value, the higher will be verbosity. + A value of 0 means no console output at all. + There's not really an upper limit but the usual + level of all messages is currently somewhere less + than 10., + config_debug_level="${enableval}", + config_debug_level="1" +) +AC_DEFINE_UNQUOTED(CONFIG_DEBUG_LEVEL, $config_debug_level, Define console verbosity.) + +AC_ARG_ENABLE(rt-exceptions, + --enable-rt-exceptions + Enable exceptions in the realtime thread + (default=no). If this is enabled, exceptions will + be thrown on critical errors in the realtime + context as well. Otherwise if disabled + segmentation faults will be forced by the + application on critical errors., + config_rt_exceptions="$enableval", + config_rt_exceptions="no" +) +if test "$config_rt_exceptions" = "yes"; then + AC_DEFINE_UNQUOTED(CONFIG_RT_EXCEPTIONS, 1, Define to 1 to allow exceptions in the realtime context.) +fi + +config_pthread_testcancel="$mac" +AC_ARG_ENABLE(pthread-testcancel, + --enable-pthread-testcancel + Enable pthread_testcancel() calls and avoid + asynchronous cancel of pthreads (default=yes + for Mac targets, no otherwise)., + config_pthread_testcancel="$enableval", + ) +if test "$config_pthread_testcancel" = "yes"; then + AC_DEFINE_UNQUOTED(CONFIG_PTHREAD_TESTCANCEL, 1, Define to 1 to enable pthread_testcancel() calls.) +fi + +AC_ARG_ENABLE(preload-samples, + --enable-preload-samples + Due to seeking and latency issues with hard drives + we have to cache a small part of samples' head in + RAM (default=32768). The higher this value the + more memory will be occupied for each sample, but + the safer this will be in regards of possible + droputs. A 'good' value depends on the running + system and usage dependant factors., + config_preload_samples="${enableval}", + config_preload_samples="32768" +) +AC_DEFINE_UNQUOTED(CONFIG_PRELOAD_SAMPLES, $config_preload_samples, Define amount of sample points to be cached in RAM.) + +AC_ARG_ENABLE(max-pitch, + --enable-max-pitch + Specify the maximum allowed pitch value in octaves + (default=4). To lower memory usage you might want + set a smaller value., + config_max_pitch="${enableval}", + config_max_pitch="4" +) +AC_DEFINE_UNQUOTED(CONFIG_MAX_PITCH, $config_max_pitch, Define max. allowed pitch.) + +AC_ARG_ENABLE(max-events, + --enable-max-events + Specify the maximum allowed amount of events to be + processed per fragment (default=1024)., + config_max_events="${enableval}", + config_max_events="1024" +) +AC_DEFINE_UNQUOTED(CONFIG_MAX_EVENTS_PER_FRAGMENT, $config_max_events, Define max. allowed events per fragment.) + +AC_ARG_ENABLE(eg-bottom, + --enable-eg-bottom + Bottom limit of envelope generators + (default=0.001). Certain kinds of curve types like + exponential curves converge against 0 but never + reach 0. So we have to define a certain low value + after which we should consider all smaller values + to be 'almost zero'. The smaller this value, the + longer will voices survive in EG's release stage + and thus waste voices. If this value is too high + will cause click sounds though., + config_eg_bottom="${enableval}", + config_eg_bottom="0.001" +) +AC_DEFINE_UNQUOTED(CONFIG_EG_BOTTOM, $config_eg_bottom, Define bottom limit of envelopes.) + +AC_ARG_ENABLE(eg-min-release-time, + --enable-eg-min-release-time + Specify the lowest allowed release time in seconds + (default=0.0025). This value will also be used to + ramp down voices on voice stealing. This value + should always be less than the period time of the + used audio driver, as in case of voice stealing + the killed voice needs to be completely ramped + down in the same fragment., + config_eg_min_release_time="${enableval}", + config_eg_min_release_time="0.0025" +) +AC_DEFINE_UNQUOTED(CONFIG_EG_MIN_RELEASE_TIME, $config_eg_min_release_time, Define min. release time.) + +AC_ARG_ENABLE(refill-streams, + --enable-refill-streams + Number of streams that should be refilled in each + disk thread cycle (default=4)., + config_refill_streams="${enableval}", + config_refill_streams="4" +) +AC_DEFINE_UNQUOTED(CONFIG_REFILL_STREAMS_PER_RUN, $config_refill_streams, Define amount of streams to be refilled per cycle.) + +AC_ARG_ENABLE(stream-min-refill, + --enable-stream-min-refill + Minimum refill size for disk streams (default=1024). + The disk thread will go to sleep for a while if no + stream had to be refilled more than this value in + a disk thread cycle., + config_stream_min_refill="${enableval}", + config_stream_min_refill="1024" +) +AC_DEFINE_UNQUOTED(CONFIG_STREAM_MIN_REFILL_SIZE, $config_stream_min_refill, Define min. stream refill size.) + +AC_ARG_ENABLE(stream-max-refill, + --enable-stream-max-refill + Maximum refill size for disk streams + (default=65536). The disk thread will refill + each stream only by a size of this value per + disk thread cycle., + config_stream_max_refill="${enableval}", + config_stream_max_refill="65536" +) +AC_DEFINE_UNQUOTED(CONFIG_STREAM_MAX_REFILL_SIZE, $config_stream_max_refill, Define max. stream refill size.) + +AC_ARG_ENABLE(stream-size, + --enable-stream-size + Size of each stream's ring buffer in sample points + (default=262144)., + config_stream_size="${enableval}", + config_stream_size="262144" +) +AC_DEFINE_UNQUOTED(CONFIG_STREAM_BUFFER_SIZE, $config_stream_size, Define each stream's ring buffer size.) + +AC_ARG_ENABLE(max-streams, + --enable-max-streams + Initial maximum amount of disk streams + (default=90). This value can be changed at + runtime. It should always be higher than the + maximum amount of voices., + config_max_streams="${enableval}", + config_max_streams="90" +) +AC_DEFINE_UNQUOTED(CONFIG_DEFAULT_MAX_STREAMS, $config_max_streams, Define initial max. streams.) + +AC_ARG_ENABLE(max-voices, + --enable-max-voices + Initial maximum amount of voices (default=64). + This value can be changed at runtime. It should + always be lower than the maximum amount of disk + streams., + config_max_voices="${enableval}", + config_max_voices="64" +) +AC_DEFINE_UNQUOTED(CONFIG_DEFAULT_MAX_VOICES, $config_max_voices, Define initial max. voices.) + +AC_ARG_ENABLE(subfragment-size, + --enable-subfragment-size + Every audio fragment will be splitted into + subfragments. Where each subfragment renders + audio with constant synthesis parameters. This is + done for efficiency reasons. This parameter + defines the default size of a subfragment in + sample points. A large value means less CPU time + whereas a low value means better audio quality + (default=32)., + config_subfragment_size="${enableval}", + config_subfragment_size="32" +) +AC_DEFINE_UNQUOTED(CONFIG_DEFAULT_SUBFRAGMENT_SIZE, $config_subfragment_size, Define default subfragment size (in sample points).) + +AC_ARG_ENABLE(global-attenuation-default, + --enable-global-attenuation-default + To prevent clipping all samples will be lowered + in amplitude by this given default factor (can + be overridden at runtime). + (default=0.35), + config_global_attenuation_default="${enableval}", + config_global_attenuation_default="0.35" +) +AC_DEFINE_UNQUOTED(CONFIG_GLOBAL_ATTENUATION_DEFAULT, $config_global_attenuation_default, Define default global volume attenuation (as floating point factor).) + +AC_ARG_ENABLE(voice-steal-algo, + --enable-voice-steal-algo + Voice stealing algorithm to be used. Currently + available options: + none: + Disable voice stealing completely. + oldestvoiceonkey (default): + Try to kill a voice on the same key first, + if no success, proceed with the oldest key. + oldestkey: + Try to kill a voice from the oldest active + key., + if test ! "(" "${enableval}" = "none" \ + -o "${enableval}" = "oldestvoiceonkey" \ + -o "${enableval}" = "oldestkey" ")" ; then + AC_MSG_ERROR(Unknown voice stealing algorithm for parameter --enable-voice-steal-algo) + else + config_voice_steal_algo="${enableval}" + fi + , + config_voice_steal_algo="oldestvoiceonkey" +) +AC_DEFINE_UNQUOTED(CONFIG_VOICE_STEAL_ALGO, voice_steal_algo_${config_voice_steal_algo}, Define voice stealing algorithm to be used.) + +AC_ARG_ENABLE(sysex-buffer-size, + --enable-sysex-buffer-size + System Exclusive Message buffer size in kB + (default=2048)., + config_sysex_buffer_size="${enableval}", + config_sysex_buffer_size="2048" +) +AC_DEFINE_UNQUOTED(CONFIG_SYSEX_BUFFER_SIZE, $config_sysex_buffer_size, Define SysEx buffer size.) + +AC_ARG_ENABLE(force-filter, + --enable-force-filter + If enabled will force filter to be used even if + no usage was define in instrument patch files. + (default=no)., + config_force_filter="$enableval", + config_force_filter="no" +) +if test "$config_force_filter" = "yes"; then + AC_DEFINE_UNQUOTED(CONFIG_FORCE_FILTER, 1, Define to 1 to force filter usage.) +fi + +AC_ARG_ENABLE(filter-cutoff-min, + --enable-filter-cutoff-min + Minimum filter cutoff frequency in Hz + (default=100.0)., + config_filter_cutoff_min="${enableval}", + config_filter_cutoff_min="100.0" +) +AC_DEFINE_UNQUOTED(CONFIG_FILTER_CUTOFF_MIN, ${config_filter_cutoff_min}f, Define min. filter cutoff frequency.) + +AC_ARG_ENABLE(filter-cutoff-max, + --enable-filter-cutoff-max + Maximum filter cutoff frequency in Hz + (default=10000.0)., + config_filter_cutoff_max="${enableval}", + config_filter_cutoff_max="10000.0" +) +AC_DEFINE_UNQUOTED(CONFIG_FILTER_CUTOFF_MAX, ${config_filter_cutoff_max}f, Define max. filter cutoff frequency.) + +AC_ARG_ENABLE(override-cutoff-ctrl, + --enable-override-cutoff-ctrl + Override filter cutoff MIDI controller (default=no). + Note: you have to define the MIDI controller number + here, it's not a boolean parameter type! If this + option is used, controller number given by + instrument patch will be ignored and instead this + supplied value will be used., + config_override_cutoff_ctrl="${enableval}", + config_override_cutoff_ctrl="no" +) +if test ! "$config_override_cutoff_ctrl" = "no"; then + AC_DEFINE_UNQUOTED(CONFIG_OVERRIDE_CUTOFF_CTRL, $config_override_cutoff_ctrl, Define to a MIDI controller number to override cutoff control.) +fi + +AC_ARG_ENABLE(override-resonance-ctrl, + --enable-override-resonance-ctrl + Override filter resonance MIDI controller + (default=no). Note: you have to define the MIDI + controller number here, it's not a boolean + parameter type! If this option is used, controller + number given by instrument patch will be ignored + and instead this supplied value will be used., + config_override_resonance_ctrl="${enableval}", + config_override_resonance_ctrl="no" +) +if test ! "$config_override_resonance_ctrl" = "no"; then + AC_DEFINE_UNQUOTED(CONFIG_OVERRIDE_RESONANCE_CTRL, $config_override_resonance_ctrl, Define to a MIDI controller number to override resonance control.) +fi + +AC_ARG_ENABLE(override-filter-type, + --enable-override-filter-type + Override filter type (default=no). Options: + hp: for highpass + bp: for bandpass + br: for bandreject + lp: for lowpass + lpt: for lowpass turbo, + if test "${enableval}" = "hp" ; then + config_override_filter_type="::gig::vcf_type_highpass" + elif test "${enableval}" = "bp" ; then + config_override_filter_type="::gig::vcf_type_bandpass" + elif test "${enableval}" = "br" ; then + config_override_filter_type="::gig::vcf_type_bandreject" + elif test "${enableval}" = "lp" ; then + config_override_filter_type="::gig::vcf_type_lowpass" + elif test "${enableval}" = "lpt" ; then + config_override_filter_type="::gig::vcf_type_lowpassturbo" + elif test ! "${enableval}" = "no"; then + AC_MSG_ERROR(Unknown filter type for parameter --enable-override-filter-type) + fi + , + config_override_filter_type="no" +) +if test ! "$config_override_filter_type" = "no"; then + AC_DEFINE_UNQUOTED(CONFIG_OVERRIDE_FILTER_TYPE, $config_override_filter_type, Define to a filter type to always force that filter type.) +fi + +AC_ARG_ENABLE(gs-checksum, + --enable-gs-checksum + Enable Roland General Synth SysEx checksum check + (default=no). If this is enabled, all GS SysEx + messages which do not provide a correct checksum + will be ignored. This is disabled by default as + not all devices honor GS checksums., + config_assert_gs_sysex_checksum="$enableval", + config_assert_gs_sysex_checksum="no" +) +if test "config_assert_gs_sysex_checksum" = "yes"; then + AC_DEFINE_UNQUOTED(CONFIG_ASSERT_GS_SYSEX_CHECKSUM, 1, Define to 1 if you want to enable GS SysEx check.) +fi + +AC_ARG_ENABLE(portamento-time-min, + --enable-portamento-time-min + Minimum Portamento time in seconds + (default=0.1)., + config_portamento_time_min="${enableval}", + config_portamento_time_min="0.1" +) +AC_DEFINE_UNQUOTED(CONFIG_PORTAMENTO_TIME_MIN, $config_portamento_time_min, Define min. portamento time.) + +AC_ARG_ENABLE(portamento-time-max, + --enable-portamento-time-max + Maximum Portamento time in seconds + (default=32)., + config_portamento_time_max="${enableval}", + config_portamento_time_max="32" +) +AC_DEFINE_UNQUOTED(CONFIG_PORTAMENTO_TIME_MAX, $config_portamento_time_max, Define max. portamento time.) + +AC_ARG_ENABLE(portamento-time-default, + --enable-portamento-time-default + Default Portamento time in seconds + (default=1)., + config_portamento_time_default="${enableval}", + config_portamento_time_default="1" +) +AC_DEFINE_UNQUOTED(CONFIG_PORTAMENTO_TIME_DEFAULT, $config_portamento_time_default, Define default portamento time.) + +AC_ARG_ENABLE(signed-triang-algo, + --enable-signed-triang-algo + Signed triangular wave algorithm to be used (e.g. for LFOs). + Currently available options: + intmath: + Uses integer math without any branch will then be + converted to floating point value for each sample point. + This int->float conversion might hurt on some systems. + intmathabs: + Similar to intmath but uses abs() function. + Depending on compiler and platrofm this could + perform better than integer math as it avoids + an extra integer multiply instruction. + diharmonic: + The triangular wave will be approximated by adding two + sinusoidials. This solution might especially hurt on + systems with weak floating point unit. + benchmark (default): + This is not an algorithm. Use this option if the + appropriate algorithm should be automatically + chosen by the configure script by performing a + benchmark between the algorithms mentioned above. + This will NOT work for cross compilation!, + if test ! "(" "${enableval}" = "intmath" \ + -o "${enableval}" = "intmathabs" \ + -o "${enableval}" = "diharmonic" ")" ; then + AC_MSG_ERROR(Unknown triangular wave algorithm for parameter --enable-signed-triang-algo) + else + config_signed_triang_algo="${enableval}" + fi + , + config_signed_triang_algo="benchmark" +) + +AC_ARG_ENABLE(unsigned-triang-algo, + --enable-unsigned-triang-algo + Unsigned triangular wave algorithm to be used (e.g. for LFOs). + Currently available options: + intmath: + Uses integer math without any branch will then be + converted to floating point value for each sample point. + This int->float conversion might hurt on some systems. + intmathabs: + Similar to intmath but uses abs() function. + Depending on compiler and platrofm this could + perform better than integer math as it avoids + an extra integer multiply instruction. + diharmonic: + The triangular wave will be approximated by adding two + sinusoidials. This solution might especially hurt on + systems with weak floating point unit. + benchmark (default): + This is not an algorithm. Use this option if the + appropriate algorithm should be automatically + chosen by the configure script by performing a + benchmark between the algorithms mentioned above. + This will NOT work for cross compilation!, + if test ! "(" "${enableval}" = "intmath" \ + -o "${enableval}" = "intmathabs" \ + -o "${enableval}" = "diharmonic" ")" ; then + AC_MSG_ERROR(Unknown triangular wave algorithm for parameter --enable-unsigned-triang-algo) + else + config_unsigned_triang_algo="${enableval}" + fi + , + config_unsigned_triang_algo="benchmark" +) + +AC_ARG_ENABLE(process-muted-channels, + --enable-process-muted-channels + Enable processing of muted channels (default=no). + In that mode all MIDI events in the muted channels + will be processed. This will provide information + about the active voices in the muted channels and + will not discard notes, triggered in mute mode, + when the channel is unmuted. But also will reduce + the efficiency., + config_process_muted_channels="$enableval", + config_process_muted_channels="no" +) +if test "$config_process_muted_channels" = "yes"; then + AC_DEFINE_UNQUOTED(CONFIG_PROCESS_MUTED_CHANNELS, 1, Define to 1 if you want to enable processing of muted channels.) +fi + +AC_ARG_ENABLE(process-all-notes-off, + --disable-process-all-notes-off + Disable interpretation of All-Notes-Off MIDI + messages (default=on). By default LS will release + all voices whenever it receives an All-Notes-Off + MIDI message. You can disable this behavior, so + that LS simply ignores such messages., + config_process_all_notes_off="$enableval", + config_process_all_notes_off="yes" +) +if test "$config_process_all_notes_off" = "yes"; then + AC_DEFINE_UNQUOTED(CONFIG_PROCESS_ALL_NOTES_OFF, 1, Define to 1 if you want to enable processing of All-Notes-Off MIDI messages.) +fi + +AC_ARG_ENABLE(interpolate-volume, + --disable-interpolate-volume + Disable interpolation of volume modulation + (default=on). With this enabled, the volume changes + generated by for example the envelope generator + will be smoother, minimizing the risk for audio + clicks. Disable it to reduce CPU usage., + config_interpolate_volume="$enableval", + config_interpolate_volume="yes" +) +if test "$config_interpolate_volume" = "yes"; then + AC_DEFINE_UNQUOTED(CONFIG_INTERPOLATE_VOLUME, 1, Define to 1 if you want to enable interpolation of volume modulation.) +fi + +AC_ARG_ENABLE(master-volume-sysex-by-port, + --enable-master-volume-sysex-by-port + Whether global volume sysex message should be + applied globally to the whole sampler or only to + the sampler channels connected to the same MIDI + input port on which the sysex message arrived on. + By default global volume sysex messages apply + globally to the whole sampler, since many MIDI + devices behave that way., + config_master_volume_sysex_by_port="$enableval", + config_master_volume_sysex_by_port="no" +) +if test "$config_master_volume_sysex_by_port" = "yes"; then + AC_DEFINE_UNQUOTED(CONFIG_MASTER_VOLUME_SYSEX_BY_PORT, 1, Define to 1 if you want global volume sysex message only be applied to the respective MIDI port.) +fi + +AC_ARG_ENABLE(plugin-dir, + --enable-plugin-dir + Directory where the sampler shall look for potential plugins, + that is 3rd party shared libraries that should be loaded by + the sampler on startup. By default the sampler will search + for plugins in the subdirectory "plugins" below its own + library directory. + (i.e. /usr/local/lib/linuxsampler/plugins), + config_plugin_dir="${enableval}", + config_plugin_dir="${libdir}/linuxsampler/plugins" +) +AC_SUBST(config_plugin_dir) + +AC_ARG_ENABLE(default-instruments-db-location, + --enable-default-instruments-db-location + Only when instruments DB feature is enabled: file name + which shall be taken as default location of the + instruments DB file. This location can still be + overridden at runtime with a command line switch. + (default: /var/lib/linuxsampler/instruments.db), + config_default_instruments_db_file="${enableval}", + config_default_instruments_db_file="/var/lib/linuxsampler/instruments.db" +) +AC_DEFINE_UNQUOTED( + CONFIG_DEFAULT_INSTRUMENTS_DB_LOCATION, + "$config_default_instruments_db_file", + Only when instruments DB feature is enabled: default location of the DB file. +) +AC_SUBST(config_default_instruments_db_file) + + +########################################################################### +# Automatic Benchmarks (to detect the best algorithms for the system) + +if test "$config_signed_triang_algo" = "benchmark"; then + echo -n "benchmarking for the best (signed) triangular oscillator algorithm... " + AC_LANG_PUSH(C++) + AC_RUN_IFELSE(AC_LANG_SOURCE( + #define SIGNED 1 + #define SILENT 1 + #include "${srcdir}/benchmarks/triang.cpp" + ), + triang_signed=0, + triang_signed=$?, + triang_signed=0 + ) + AC_LANG_POP(C++) + if test "$triang_signed" = "2"; then + config_signed_triang_algo="intmath" + echo "integer math" + elif test "$triang_signed" = "3"; then + config_signed_triang_algo="diharmonic" + echo "di harmonics" + elif test "$triang_signed" = "5"; then + config_signed_triang_algo="intmathabs" + echo "integer math using abs()" + else + echo "Benchmark of signed triangular wave algorithms failed!" + echo "Maybe you are doing cross compilation? In that case you have to select" + echo "an algorithm manually with './configure --enable-signed-triang-algo=...'" + echo "Call './configure --help' for further information or read configure.in." + exit -1; + fi +else + case "$config_signed_triang_algo" in + intmath) + triang_signed=2 ;; + diharmonic) + triang_signed=3 ;; + intmathabs) + triang_signed=5 ;; + esac +fi +AC_DEFINE_UNQUOTED(CONFIG_SIGNED_TRIANG_ALGO, ${triang_signed}, Define signed triangular wave algorithm to be used.) + +if test "$config_unsigned_triang_algo" = "benchmark"; then + echo -n "benchmarking for the best (unsigned) triangular oscillator algorithm... " + AC_LANG_PUSH(C++) + AC_RUN_IFELSE(AC_LANG_SOURCE( + #define SIGNED 0 + #define SILENT 1 + #include "${srcdir}/benchmarks/triang.cpp" + ), + triang_unsigned=0, + triang_unsigned=$?, + triang_unsigned=0 + ) + AC_LANG_POP(C++) + if test "$triang_unsigned" = "2"; then + config_unsigned_triang_algo="intmath" + echo "integer math" + elif test "$triang_unsigned" = "3"; then + config_unsigned_triang_algo="diharmonic" + echo "di harmonics" + elif test "$triang_unsigned" = "5"; then + config_unsigned_triang_algo="intmathabs" + echo "integer math using abs()" + else + echo "Benchmark of unsigned triangular wave algorithms failed!" + echo "Maybe you are doing cross compilation? In that case you have to select" + echo "an algorithm manually with './configure --enable-unsigned-triang-algo=...'" + echo "Call './configure --help' for further information or read configure.in." + exit -1; + fi +else + case "$config_unsigned_triang_algo" in + intmath) + triang_unsigned=2 ;; + diharmonic) + triang_unsigned=3 ;; + intmathabs) + triang_unsigned=5 ;; + esac +fi +AC_DEFINE_UNQUOTED(CONFIG_UNSIGNED_TRIANG_ALGO, ${triang_unsigned}, Define unsigned triangular wave algorithm to be used.) + + +########################################################################### +# Create Build Files + +AC_CONFIG_HEADERS(config.h) + +AC_LANG(C++) + +# autoconf 2.59/libtool 1.5.12 bug? work-around. Without a check like +# this, the dlfcn.h check in am_prog_libtool may fail. +AC_CHECK_HEADER(stdlib.h) + +AC_CONFIG_FILES(\ + Makefile \ + man/Makefile \ + man/linuxsampler.1 \ + man/lscp.1 \ + src/Makefile \ + src/db/Makefile \ + src/network/Makefile \ + src/engines/Makefile \ + src/engines/gig/Makefile \ + src/engines/sf2/Makefile \ + src/engines/sfz/Makefile \ + src/engines/common/Makefile \ + src/effects/Makefile \ + src/common/Makefile \ + src/testcases/Makefile \ + src/drivers/Makefile \ + src/drivers/audio/Makefile \ + src/drivers/midi/Makefile \ + src/plugins/Makefile \ + src/hostplugins/Makefile \ + src/hostplugins/dssi/Makefile \ + src/hostplugins/lv2/Makefile \ + src/hostplugins/lv2/manifest.ttl \ + src/hostplugins/vst/Makefile \ + src/hostplugins/au/Makefile \ + src/scriptvm/Makefile \ + src/shell/Makefile \ + linuxsampler.spec \ + debian/Makefile \ + Artwork/Makefile \ + scripts/Makefile \ + osx/Makefile \ + osx/linuxsampler.xcodeproj/Makefile \ + Documentation/Makefile \ + Documentation/Engines/Makefile \ + Documentation/Engines/gig/Makefile \ + linuxsampler.pc \ + Doxyfile \ +) +AC_OUTPUT + +# resolve all nested variables in '${config_plugin_dir}' +# (merely for providing a human readable summary below) +while test $config_plugin_dir != `eval echo ${config_plugin_dir}` ; do + config_plugin_dir=`eval echo ${config_plugin_dir}` +done + + +########################################################################### +# Output All Configuration Options + +echo "" +echo "#####################################################################" +echo "# LinuxSampler Configuration #" +echo "#-------------------------------------------------------------------#" +echo "# Release Version: ${VERSION}" +echo "# LSCP Version: ${LSCP_RELEASE_MAJOR}.${LSCP_RELEASE_MINOR}" +echo "#-------------------------------------------------------------------" +echo "# Assembly Optimizations: ${config_asm}" +echo "# Development Mode: ${config_dev_mode}" +echo "# Debug Level: ${config_debug_level}" +echo "# Use Exceptions in RT Context: ${config_rt_exceptions}" +echo "# Preload Samples: ${config_preload_samples}" +echo "# Maximum Pitch: ${config_max_pitch} (octaves)" +echo "# Maximum Events: ${config_max_events}" +echo "# Envelope Bottom Level: ${config_eg_bottom} (linear)" +echo "# Envelope Minimum Release Time: ${config_eg_min_release_time} s" +echo "# Streams to be refilled per Disk Thread Cycle: ${config_refill_streams}" +echo "# Minimum Stream Refill Size: ${config_stream_min_refill}" +echo "# Maximum Stream Refill Size: ${config_stream_max_refill}" +echo "# Stream Size: ${config_stream_size}" +echo "# Default Maximum Disk Streams: ${config_max_streams}" +echo "# Default Maximum Voices: ${config_max_voices}" +echo "# Default Subfragment Size: ${config_subfragment_size}" +echo "# Default Global Volume Attenuation: ${config_global_attenuation_default}" +echo "# Voice Stealing Algorithm: ${config_voice_steal_algo}" +echo "# Signed Triangular Oscillator Algorithm: ${config_signed_triang_algo}" +echo "# Unsigned Triangular Oscillator Algorithm: ${config_unsigned_triang_algo}" +echo "# SysEx Buffer Size: ${config_sysex_buffer_size} Byte" +echo "# Min. Portamento Time: ${config_portamento_time_min} s" +echo "# Max. Portamento Time: ${config_portamento_time_max} s" +echo "# Default Portamento Time: ${config_portamento_time_default} s" +echo "# Force Filter Usage: ${config_force_filter}" +echo "# Filter Cutoff Minimum: ${config_filter_cutoff_min} Hz" +echo "# Filter Cutoff Maximum: ${config_filter_cutoff_max} Hz" +echo "# Override Filter Cutoff Controller: ${config_override_cutoff_ctrl}" +echo "# Override Filter Resonance Controller: ${config_override_resonance_ctrl}" +echo "# Override Filter Type: ${config_override_filter_type}" +echo "# Assert GS SysEx Checksum: ${config_assert_gs_sysex_checksum}" +echo "# Process Muted Channels: ${config_process_muted_channels}" +echo "# Process All-Notes-Off MIDI message: ${config_process_all_notes_off}" +echo "# Apply global volume SysEx by MIDI port: ${config_master_volume_sysex_by_port}" +echo "# Interpolate Volume: ${config_interpolate_volume}" +echo "# Instruments database support: ${config_instruments_db}" +if test "$config_instruments_db" = "yes"; then +echo "# Instruments DB default location: ${config_default_instruments_db_file}" +fi +echo "# Plugin Path: ${config_plugin_dir}" +echo "#-------------------------------------------------------------------" +echo "# MIDI Input Drivers:" +echo "# ALSA: ${config_have_alsa}, JACK: ${config_have_jack_midi}, CoreMIDI: ${config_have_coremidi}, MME: ${config_have_mme}, MidiShare: ${config_have_midishare}" +echo "#-------------------------------------------------------------------" +echo "# Audio Output Drivers:" +echo "# ALSA: ${config_have_alsa}, JACK: ${config_have_jack}, ARTS: ${config_have_arts}, CoreAudio: ${config_have_coreaudio}, ASIO: ${config_have_asio}" +echo "#-------------------------------------------------------------------" +echo "# Sampler Engines:" +echo "# GIG: yes, SF2: ${config_have_sf2}, SFZ: yes" +echo "#-------------------------------------------------------------------" +echo "# Effect plugin systems for internal effects:" +echo "# LADSPA: yes" +echo "#-------------------------------------------------------------------" +echo "# Building sampler as plugin for following host standards:" +echo "# DSSI: ${config_have_dssi}, LV2: ${config_have_lv2}, VST: ${config_have_vst}, AU: ${config_have_au}" +echo "#-------------------------------------------------------------------#" +echo "# Read './configure --help' or file 'configure.in' for details. #" +echo "#####################################################################" +echo "" +echo "Good. Now type 'make' to compile, followed by 'make install' as root." +echo ""
View file
linuxsampler-2342.tar.bz2/debian/control -> linuxsampler-2718.tar.bz2/debian/control
Changed
@@ -23,6 +23,9 @@ based protocol called LSCP. You might consider to install a GUI frontend for LinuxSampler as well. . + This package also provides the LSCP shell, which can be used to control the + sampler from the command line. + . For more informations please visit http://www.linuxsampler.org Package: liblinuxsampler-dev
View file
linuxsampler-2342.tar.bz2/man/Makefile.am -> linuxsampler-2718.tar.bz2/man/Makefile.am
Changed
@@ -1,4 +1,4 @@ ## Process this file with automake to produce Makefile.in # all man files that should be installed -man_MANS = linuxsampler.1 +man_MANS = linuxsampler.1 lscp.1
View file
linuxsampler-2342.tar.bz2/man/linuxsampler.1.in -> linuxsampler-2718.tar.bz2/man/linuxsampler.1.in
Changed
@@ -1,15 +1,45 @@ -.TH "linuxsampler" "1" "04 Oct 2010" "linuxsampler @VERSION@" "User Manuals" +.TH "linuxsampler" "1" "05 Feb 2014" "linuxsampler @VERSION@" "User Manuals" .SH NAME linuxsampler \- modular, streaming capable sampler .SH SYNOPSIS .B linuxsampler OPTIONS .SH DESCRIPTION -LinuxSampler is a modular, streaming capable sampler. The sampler is designed as +LinuxSampler is a modular, streaming capable sampler. Currently it supports the +sampler formats Gigasampler/GigaStudio (.gig), SoundFont v2 (.sf2) and SFZ v2 +(.sfz). + +The sampler is designed as backend, that is server-like console application. You will need a frontend -application like QSampler or JSampler to control the sampler and manage sampler -sessions. Or you have to send LSCP commands manually to the sampler e.g. by -using telnet or netcat. +application like QSampler or JSampler/Fantasia, which provide a graphical user +interface (GUI) to control the sampler and manage sampler sessions conveniently. +If you prefer, you can also control the sampler from the command line instead, +for example by using the +.BR lscp (1) +shell application shipped with +LinuxSampler, which provides LSCP aware color highlighting, auto completion and +more. Since the LSCP network interface is based on a human readable text +protocol, you can also send LSCP commands by any other means to the sampler, +e.g. by using +.BR telnet (1) +or +.BR netcat (1). +You can find examples and detailed informations +about LinuxSampler's network protocol (LSCP) on +http://linuxsampler.org/documentation.html#lscp_spec + +Various audio and MIDI driver systems are supported, like ALSA (audio & MIDI), +JACK (audio & MIDI), ASIO, CoreAudio, CoreMIDI, Arts, MidiShare and MME. +LinuxSampler can also be run as virtual instrument plugin. The following +instrument plugin formats are currently supported: AudioUnit (AU), DSSI, LV2 +and VST. You can also load effect plugins into the sampler. Currently only the +LADSPA plugin format is supported for sampler internal effects. + +If you are using sounds in the Gigasampler/GigaStudio format, then you may also +want to install the graphical instrument editor application called "gigedit", +which can directly be launched by LinuxSampler, allowing to edit instruments in +real-time (changes immediately being audible, without needing to reload the +sound files in the sampler). .SH OPTIONS .IP "--help" Print command line help and exit. @@ -54,9 +84,12 @@ editor plugins. .IP "LADSPA_PATH" Allows to override the directory where LinuxSampler shall look for LADSPA -effect plugins. +effect plugins. Multiple paths may be given, separated by colon (":") character +on POSIX systems (i.e. Linux and Mac), or separated by semi-colon (";") +character on Windows systems. .SH "SEE ALSO" -qsampler(1) +.BR lscp (1), +.BR qsampler (1) .SH "BUGS" Report bugs to http://bugs.linuxsampler.org .SH "Author"
View file
linuxsampler-2718.tar.bz2/man/lscp.1.in
Added
@@ -0,0 +1,53 @@ +.TH "lscp" "1" "09 Mar 2014" "linuxsampler @VERSION@" "User Manuals" +.SH NAME +lscp \- the LinuxSampler Control Protocol (LSCP) shell +.SH SYNOPSIS +.B lscp +OPTIONS +.SH DESCRIPTION +The LSCP shell allows to control a running instance of +.BR linuxsampler (1) +from the command line. By default, the LSCP shell will assume LinuxSampler to +run on the local machine and accordingly will try to connect to LinuxSampler on +the local machine. You can however also connect to LinuxSampler instances +running on any another machine available on the network. Since LSCP is +essentially just a human readable text protocol, you can also control the +sampler simply by using +.BR telnet (1) +or +.BR netcat (1), +however since the LSCP shell is aware about the details of the LSCP protocol, +it has various advantages, i.e. it provides colored highlighting of correct, +incorrect and complete commands while typing them on the terminal, automatic +correction of obvious and trivial syntax errors, visual suggestion for +completing the current command and also supports auto completion by tab key. + +You can find examples and detailed informations +about LinuxSampler's network protocol (LSCP) on +http://linuxsampler.org/documentation.html#lscp_spec + +The LSCP shell is designed as thin client, that is it forwards the individual +key strokes to the sampler's LSCP server, which will actually perform all the +LSCP knowledge based operations like syntactical error checks and suggestions +for auto completions. The shell application then receives the generated +informations and handles only output formatting on the command line terminal. +This solution has one major advantage: one and the same LSCP shell can work +with multiple different LinuxSampler versions and accordingly will still handle +different LSCP versions correctly. +.SH OPTIONS +.IP "-h HOSTNAME or --host HOSTNAME" +Host name of LSCP server (default "localhost"). +.IP "-p PORTNR or --port PORTNR" +TCP port number of LSCP server (default 8888). +.IP "--no-auto-correct" +Don't perform auto correction of obvious syntax errors. +.IP "--no-doc" +Don't show LSCP reference documentation on screen. +.SH ENVIRONMENT VARIABLES +None (yet). +.SH "SEE ALSO" +.BR linuxsampler (1) +.SH "BUGS" +Report bugs to http://bugs.linuxsampler.org +.SH "Author" +Written by Christian Schoenebeck <cuse@users.sf.net>
View file
linuxsampler-2342.tar.bz2/scripts/Makefile.am -> linuxsampler-2718.tar.bz2/scripts/Makefile.am
Changed
@@ -3,5 +3,6 @@ EXTRA_DIST = \ create_instr_db.sh \ create_instr_db.sql \ + generate_lscp_shell_reference.pl \ generate_parser.sh \ update_grammar.pl
View file
linuxsampler-2718.tar.bz2/scripts/generate_instrument_script_parser.sh
Added
@@ -0,0 +1,48 @@ +#!/bin/sh +# +# Generates the instrument script parser's C++ source files +# (parser.h, parser.cpp and scanner.cpp in and src/scriptvm/) according to the +# instrument script (BNF) grammar definition given by src/scriptvm/parser.y and +# the terminal symbol scanner/lexer defintion given by src/scriptvm/scanner.l + +SCRIPTS_DIR=`dirname $0` +PARSER_SRC_DIR="$SCRIPTS_DIR/../src/scriptvm" + +echo -n "Searching for lexer and parser generator..." + +LEX_CMD=NONE +if which "flex" > /dev/null; then + LEX_CMD=`which flex` +elif which "lex" > /dev/null; then + LEX_CMD=`which lex` +else + echo "Error: You need lex (or flex) to generate the instrument script parser !" + exit -1 +fi + +YACC_CMD=NONE +if which "bison" > /dev/null; then + YACC_CMD="`which bison` -y" +elif which "yacc" > /dev/null; then + YACC_CMD=`which yacc` +else + echo "Error: You need yacc (or bison) to generate the instrument script parser !" + exit -1 +fi + +echo "OK ($LEX_CMD, $YACC_CMD)" + +echo -n "Generating instrument script parser ... " +( + cd $PARSER_SRC_DIR + + $LEX_CMD scanner.l + #mv -f lex.yy.c scanner.cpp + mv -f lex.InstrScript_.c scanner.cpp + + $YACC_CMD -d parser.y + $YACC_CMD parser.y + mv -f y.tab.h parser.h + mv -f y.tab.c parser.cpp +) +echo "Done"
View file
linuxsampler-2718.tar.bz2/scripts/generate_lscp_parser.sh
Added
@@ -0,0 +1,38 @@ +#!/bin/sh +# +# Generates the LSCP parser's C++ source files (src/network/lscpparser.cpp and +# src/network/lscpsymbols.h) according to the LSCP (BNF) grammar definition +# given by src/network/lscp.y + +SCRIPTS_DIR=`dirname $0` +NETWORK_SRC_DIR="$SCRIPTS_DIR/../src/network" + +echo -n "Searching for a parser generator..." +YACC_CMD=NONE +if which "bison" > /dev/null; then + YACC_CMD="`which bison` -y" +elif which "yacc" > /dev/null; then + YACC_CMD=`which yacc` +else + echo "Error: You need yacc (or bison) to generate the LSCP parser !" + exit -1 +fi +echo "OK ($YACC_CMD)" + +echo "Generating LSCP parser..." +( + cd $NETWORK_SRC_DIR + $YACC_CMD -d lscp.y + $YACC_CMD lscp.y + mv -f y.tab.h lscpsymbols.h + mv -f y.tab.c lscpparser.cpp +) +echo "Done" + +echo -n "Updating Documentation/lscp.xml..." +(cd $SCRIPTS_DIR && ./update_lscp_grammar.pl) +echo "Done" + +echo -n "Generating src/network/lscp_shell_reference.cpp..." +(cd $SCRIPTS_DIR && ./generate_lscp_shell_reference.pl) +echo "Done"
View file
linuxsampler-2718.tar.bz2/scripts/generate_lscp_shell_reference.pl
Added
@@ -0,0 +1,339 @@ +#!/usr/bin/perl -w + +# Updates the built-in LSCP documentation reference of the LSCP shell. +# +# Copyright (c) 2014 Christian Schoenebeck +# +# Extracts all sections from Documentation/lscp.xml marked with our magic +# XML attribute "lscp_cmd=true" (and uses the section's "anchor" XML attribute +# for *knowing* the respective exact LSCP command of the section). +# Then src/network/lscp_shell_reference.cpp is generated by this script with +# the documentation for each individual LSCP command extracted. +# +# Usage: generate_lscp_shell_reference.pl + +use XML::Parser; +use Data::Dumper; # just for debugging +use Storable qw(dclone); + +my $YACC_FILE = "../Documentation/lscp.xml"; +my $REFERENCE_CPP_FILE = "../src/network/lscp_shell_reference.cpp"; + +########################################################################### +# class MyDOM +# +# Wraps the data model returned by XML::Parser and provides convenient methods +# to access the model in DOM style. Because the tree model provided by +# XML::Parser uses a very inconvenient layout, which would require a lot of +# hard readable and error prone code if accessed directly. + +package MyDOM; + +# $dom = MyDOM->new($doc); +sub new { + my ($class, $self) = @_; + my $data = { + 'type' => 0, + 'attr' => 0, + 'content' => $self + }; +# print ::Dumper($self) . "\n"; + return bless $data, $class; +} + +# $element = $dom->element($name, $index = 0); +sub element { + my $self = shift @_; + my $name = shift @_; + my $nr = (@_) ? shift @_ : 0; + my $content = $self->{content}; + my $i = 0; + my $k = 0; + CYCLE: while ($i + 1 < @$content) { + my $type = $content->$i++; + my $subContent = $content->$i++; + + next CYCLE if ($type ne $name); + + if ($k == $nr) { + my $attr = ($type && @$subContent) ? $subContent->0 : 0; + my $subContentClone = ($type) ? ::dclone($subContent) : $subContent; # clone it, since we will modify it next + if ($attr && $type) { shift @$subContentClone; } # drop first element, which contains attributes + my $data = { + 'type' => $type, + 'attr' => $attr, + 'content' => $subContentClone + }; + return bless $data, 'MyDOM'; + } + $k++; + } + return 0; +} + +# $element = $dom->elementNr(4); +sub elementNr { + my $self = shift @_; + my $nr = shift @_; + my $content = $self->{content}; + my $i = 0; + my $k = 0; + while ($i + 1 < @$content) { + my $type = $content->$i++; + my $subContent = $content->$i++; + if ($k == $nr) { + my $attr = ($type && @$subContent) ? $subContent->0 : 0; +# print "type $type\n"; +# print ::Dumper($subContent) . "\n"; + my $subContentClone = ($type) ? ::dclone($subContent) : $subContent; # clone it, since we will modify it next + if ($attr && $type) { shift @$subContentClone; } # drop first element, which contains attributes + my $data = { + 'type' => $type, + 'attr' => $attr, + 'content' => $subContentClone + }; + return bless $data, 'MyDOM'; + } + $k++; + } + return 0; +} + +# $s = $element->name(); +sub name { + my $self = shift @_; + return $self->{type}; +} + +# $s = $element->attr("anchor"); +sub attr { + my $self = shift @_; + my $name = shift @_; + if (!$self->{attr} || !exists $self->{attr}->{$name}) { + return 0; + } + return $self->{attr}->{$name}; +} + +# $s = $element->body(); +sub body { + my $self = shift @_; + $s = ""; + if (!$self->{type}) { + return $self->{content}; + } + for (my $i = 0; $self->elementNr($i); $i++) { + $e = $self->elementNr($i); + if (!$e->name()) { + $s .= $e->{content}; + } + } + return $s; +} + +# $element->dumpMe(); +sub dumpMe { + my $self = shift @_; + print "dumpME(): " . ::Dumper($self->{content}) . "\n"; +} + +########################################################################### +# main app + +package main; + +# parse command line argument(s) +my $g_debug_xml_extract = 0; +if (defined($ARGV0) and $ARGV0 eq "--debug-xml-extract") { + $g_debug_xml_extract = 1; +} + +# will be populated by collectCommands() +my $g_cmds = { }; + +# collectCommands($dom); +sub collectCommands { + my $dom = shift @_; + for (my $i = 0; $dom->element("section", $i); $i++) { + my $section = $dom->element("section", $i); + if ($section->attr("lscp_cmd")) { + if (!$section->attr("anchor")) { + die "ERROR: Section deteced with 'lscp_cmd' attribute, but without 'anchor' attribute."; + } + my $name = $section->attr("anchor"); + if (exists $g_cmds->{$name}) { + die "ERROR: Multiple occurence of LSCP command detected: $name"; + } + $g_cmds->{$name} = $section; + } else { + collectCommands($section); + } + } +} + +# removes redundant white spaces +sub trimAll { + my $s = shift; + # replace tabs by space + $s =~ s/\t/ /g; + # replace occurences of more than one space character by only one space + # character (including new line character) + $s =~ s/\s+/ /g; + # remove leading white spaces + $s =~ s/^\s+//g; + # remove trailing white spaces + $s =~ s/\s+$//g; + return $s; +} + +# creates an optional space intended to be appended to the given string +sub wordSepFor { + my $s = shift; + if ($s eq '') { return ""; } + if ($s =~ /\n$/) { return ""; } + return " "; +} + +# $s = encodeXref($xref); +sub encodeXref { + my $xref = shift; + return trimAll($xref->body()); +} + +# $s = encodeT($t); +sub encodeT { + my $t = shift; + my $s = ""; + for (my $i = 0; $t->elementNr($i); $i++) { + $e = $t->elementNr($i); + $type = $e->name(); + if (!$type) { + $s .= wordSepFor($s); + $s .= trimAll($e->body()); + } elsif ($type eq "t") { + $s .= wordSepFor($s); + $s .= encodeT($e); + } elsif ($type eq "list") { + $s .= wordSepFor($s); + $s .= encodeSection($e); + } elsif ($type eq "xref") { + $s .= wordSepFor($s); + $s .= encodeXref($e); + } + } + if (!($s =~ /\n\n$/)) { $s .= "\n\n"; } + return $s; +} + +# $s = encodeSection($section); +sub encodeSection { + my $section = shift; + my $s = ""; + for (my $i = 0; $section->elementNr($i); $i++) { + $e = $section->elementNr($i); + $type = $e->name(); + if (!$type) { + # nothing here for now + } elsif ($type eq "t") { + $s .= wordSepFor($s); + $s .= encodeT($e); + } elsif ($type eq "list") { + $s .= wordSepFor($s); + $s .= encodeSection($e); + } elsif ($type eq "xref") { + $s .= wordSepFor($s); + $s .= encodeXref($e); + } + } + return $s; +} + +# open and parse lscp.xml +my $parser = XML::Parser->new(Style => 'Tree'); +my $doc = $parser->parsefile($YACC_FILE); +my $dom = MyDOM->new($doc); +my $middle = $dom->element("rfc")->element("middle"); + +# extract all sections from the document with the individual LSCP commands +collectCommands($middle); + +# if --debug-xml-extract is supplied, just show the result of XML parsing and exit +if ($g_debug_xml_extract) { + while (my ($name, $section) = each(%$g_cmds)) { + print "-> " . $name . "\n"; + print encodeSection($section); + } + exit(0); +} + +# start generating lscp_shell_reference.cpp ... +open(OUT, ">", $REFERENCE_CPP_FILE) || die "Can't open LSCP shell doc reference C++ file for output"; +print OUT <<EOF_BLOCK; +/***************************************************************************** + * * + * LSCP documentation reference built into LSCP shell. * + * * + * Copyright (c) 2014 Christian Schoenebeck * + * * + * This program is part of LinuxSampler and released under the same terms. * + * * + * This source file is auto generated by 'generate_lscp_shell_reference.pl' * + * from 'lscp.xml'. Thus do not modify this C++ file directly! * + * * + *****************************************************************************/ + +/* + This C++ file should automatically be re-generated if lscp.xml was + modified, if not, you may call "make parser" explicitly. + */ + +#include "lscp_shell_reference.h" +#include <string.h> + +static lscp_ref_entry_t lscp_reference = { +EOF_BLOCK +while (my ($name, $section) = each(%$g_cmds)) { + # convert reference string block into C-style string format + my $s = encodeSection($section); + $s =~ s/\n/\\n/g; + $s =~ s/\"/\\\"/g; + # split reference string into equal length chunks, so we can distribute + # them over lines, in order to not let them float behind 80 chars per line + my @lines = unpack("(A70)*", $s); + my $backSlashWrap = 0; + print OUT " { \"$name\",\n"; + foreach my $line (@lines) { + if ($backSlashWrap) { $line = "\\" . $line; } + $backSlashWrap = ($line =~ /\\$/); + if ($backSlashWrap) { chop $line; } + print OUT " \"$line\"\n"; + + } + print OUT " },\n"; +} +print OUT <<EOF_BLOCK; +}; + +lscp_ref_entry_t* lscp_reference_for_command(const char* cmd) { + const int n1 = strlen(cmd); + if (!n1) return NULL; + int foundLength = 0; + lscp_ref_entry_t* foundEntry = NULL; + for (int i = 0; i < sizeof(lscp_reference) / sizeof(lscp_ref_entry_t); ++i) { + const int n2 = strlen(lscp_referencei.name); + const int n = n1 < n2 ? n1 : n2; + if (!strncmp(cmd, lscp_referencei.name, n)) { + if (foundEntry) { + if (n1 < foundLength && n1 < n2) return NULL; + if (n2 == foundLength) return NULL; + if (n2 < foundLength) continue; + } + foundEntry = &lscp_referencei; + foundLength = n2; + } + } + return foundEntry; +} +EOF_BLOCK +close(OUT); +exit(0); # all done, success
View file
linuxsampler-2718.tar.bz2/scripts/update_lscp_grammar.pl
Changed
(renamed from scripts/update_grammar.pl)
View file
linuxsampler-2342.tar.bz2/src/Makefile.am -> linuxsampler-2718.tar.bz2/src/Makefile.am
Changed
@@ -1,5 +1,13 @@ # set the include path found by configure -INCLUDES= $(all_includes) $(GIG_CFLAGS) $(SQLITE3_CFLAGS) +AM_CPPFLAGS= $(all_includes) $(GIG_CFLAGS) $(SQLITE3_CFLAGS) + +if HAVE_WINDOWS +system_libs = +SHELL_SUBDIR = +else +system_libs = -lpthread -ldl +SHELL_SUBDIR = shell +endif if HAVE_COREMIDI coremidi_ldflags = -framework CoreAudio \ @@ -24,7 +32,8 @@ AM_CXXFLAGS = -Wreturn-type -ffast-math $(CXX_CPU_SWITCH) -SUBDIRS = db network engines common testcases drivers plugins effects . hostplugins +SUBDIRS = scriptvm db network engines common testcases drivers \ + plugins effects . hostplugins $(SHELL_SUBDIR) liblinuxsamplerincludedir = $(includedir)/linuxsampler liblinuxsamplerinclude_HEADERS = Sampler.h EventListeners.h @@ -33,6 +42,7 @@ liblinuxsampler_la_SOURCES = Sampler.cpp liblinuxsampler_la_LIBADD = \ $(sqlite3_lib) \ + $(top_builddir)/src/scriptvm/liblinuxsamplerscriptvm.la \ $(top_builddir)/src/network/liblinuxsamplernetwork.la \ $(top_builddir)/src/engines/gig/liblinuxsamplergigengine.la \ $(sf2_engine_lib) \ @@ -44,10 +54,16 @@ $(top_builddir)/src/drivers/midi/liblinuxsamplermididriver.la \ $(top_builddir)/src/plugins/liblinuxsamplerplugins.la \ $(top_builddir)/src/effects/liblinuxsamplereffects.la \ - $(top_builddir)/src/common/liblinuxsamplercommon.la + $(top_builddir)/src/common/liblinuxsamplercommon.la \ + $(system_libs) + liblinuxsampler_la_LDFLAGS = -version-info @SHARED_VERSION_INFO@ @SHLIB_VERSION_ARG@ -no-undefined -bin_PROGRAMS = linuxsampler +bin_PROGRAMS = linuxsampler ls_instr_script + linuxsampler_SOURCES = linuxsampler.cpp linuxsampler_LDADD = liblinuxsampler.la linuxsampler_LDFLAGS = $(coremidi_ldflags) + +ls_instr_script_SOURCES = ls_instr_script.cpp +ls_instr_script_LDADD = liblinuxsampler.la
View file
linuxsampler-2342.tar.bz2/src/Sampler.cpp -> linuxsampler-2718.tar.bz2/src/Sampler.cpp
Changed
@@ -3,7 +3,7 @@ * LinuxSampler - modular, streaming capable sampler * * * * Copyright (C) 2003, 2004 by Benno Senoner and Christian Schoenebeck * - * Copyright (C) 2005 - 2010 Christian Schoenebeck * + * Copyright (C) 2005 - 2014 Christian Schoenebeck * * * * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -44,7 +44,6 @@ pSampler = pS; pEngineChannel = NULL; pAudioOutputDevice = NULL; - pMidiInputDevice = NULL; iMidiPort = 0; midiChannel = midi_chan_all; iIndex = -1; @@ -55,9 +54,9 @@ Engine* engine = pEngineChannel->GetEngine(); if (pAudioOutputDevice) pAudioOutputDevice->Disconnect(engine); - MidiInputPort* pMidiInputPort = (pEngineChannel) ? pEngineChannel->GetMidiInputPort() : __GetMidiInputDevicePort(GetMidiInputChannel()); - if (pMidiInputPort) pMidiInputPort->Disconnect(pEngineChannel); if (pEngineChannel) { + pEngineChannel->DisconnectAllMidiInputPorts(); + if (pAudioOutputDevice) pEngineChannel->DisconnectAudioOutputDevice(); EngineChannelFactory::Destroy(pEngineChannel); @@ -84,35 +83,45 @@ EngineChannel* pNewEngineChannel = EngineChannelFactory::Create(EngineType); if (!pNewEngineChannel) throw Exception("Unknown engine type"); - pNewEngineChannel->SetSamplerChannel(this); - - // dereference midi input port. - MidiInputPort* pMidiInputPort = __GetMidiInputDevicePort(GetMidiInputPort()); + // remember current MIDI input connections + std::vector<MidiInputPort*> vMidiInputs = GetMidiInputPorts(); midi_chan_t midiChannel = GetMidiInputChannel(); - // disconnect old engine channel - if (pEngineChannel) { - Engine* engine = pEngineChannel->GetEngine(); - if (pAudioOutputDevice) pAudioOutputDevice->Disconnect(engine); + + try { + pNewEngineChannel->SetSamplerChannel(this); - if (pMidiInputPort) pMidiInputPort->Disconnect(pEngineChannel); - if (pAudioOutputDevice) pEngineChannel->DisconnectAudioOutputDevice(); - EngineChannelFactory::Destroy(pEngineChannel); + // disconnect old engine channel + if (pEngineChannel) { + Engine* engine = pEngineChannel->GetEngine(); + if (pAudioOutputDevice) pAudioOutputDevice->Disconnect(engine); - // reconnect engine if it still exists - const std::set<Engine*>& engines = EngineFactory::EngineInstances(); - if (engines.find(engine) != engines.end()) pAudioOutputDevice->Connect(engine); - } + pEngineChannel->DisconnectAllMidiInputPorts(); + if (pAudioOutputDevice) pEngineChannel->DisconnectAudioOutputDevice(); + EngineChannelFactory::Destroy(pEngineChannel); + pEngineChannel = NULL; + + // reconnect engine if it still exists + const std::set<Engine*>& engines = EngineFactory::EngineInstances(); + if (engines.find(engine) != engines.end()) pAudioOutputDevice->Connect(engine); + } - // connect new engine channel - if (pAudioOutputDevice) { - pNewEngineChannel->Connect(pAudioOutputDevice); - pAudioOutputDevice->Connect(pNewEngineChannel->GetEngine()); + // connect new engine channel + if (pAudioOutputDevice) { + pNewEngineChannel->Connect(pAudioOutputDevice); + pAudioOutputDevice->Connect(pNewEngineChannel->GetEngine()); + } + pNewEngineChannel->SetMidiChannel(midiChannel); + for (int i = 0; i < vMidiInputs.size(); ++i) { + pNewEngineChannel->Connect(vMidiInputsi); + } + } catch (...) { + EngineChannelFactory::Destroy(pNewEngineChannel); + throw; // re-throw the same exception } - if (pMidiInputPort) pMidiInputPort->Connect(pNewEngineChannel, midiChannel); pEngineChannel = pNewEngineChannel; - // from now on get MIDI device and port from EngineChannel object - this->pMidiInputDevice = NULL; + // from now on get MIDI input ports from EngineChannel object + this->vMidiInputs.clear(); this->iMidiPort = 0; pEngineChannel->StatusChanged(true); @@ -146,6 +155,78 @@ } } + void SamplerChannel::Connect(MidiInputPort* pPort) throw (Exception) { + if (!pPort) throw Exception("No MIDI input port provided"); + + // prevent attempts to connect non-autonomous MIDI ports + // (host plugins like VST, AU, LV2, DSSI) + if (!pPort->GetDevice()->isAutonomousDevice()) + throw Exception("The MIDI input port '" + pPort->GetDevice()->Driver() + "' cannot be managed manually!"); + + std::vector<MidiInputPort*> vMidiPorts = GetMidiInputPorts(); + + // ignore if port is already connected + for (int i = 0; i < vMidiPorts.size(); ++i) { + if (vMidiPortsi == pPort) return; + } + + // connect this new port + if (pEngineChannel) { + pEngineChannel->Connect(pPort); + } else { // no engine channel yet, remember it for future connection ... + const midi_conn_t c = { + static_cast<uint>(pPort->GetDevice()->MidiInputDeviceID()), + pPort->GetPortNumber() + }; + this->vMidiInputs.push_back(c); + } + } + + void SamplerChannel::Disconnect(MidiInputPort* pPort) throw (Exception) { + if (!pPort) return; + + // prevent attempts to alter channels with non-autonomous devices + // (host plugins like VST, AU, LV2, DSSI) + if (!pPort->GetDevice()->isAutonomousDevice()) + throw Exception("The MIDI input port '" + pPort->GetDevice()->Driver() + "' cannot be managed manually!"); + + // disconnect this port + if (pEngineChannel) { + pEngineChannel->Disconnect(pPort); + } else { // no engine channel yet, forget it regarding future connection ... + const midi_conn_t c = { + static_cast<uint>(pPort->GetDevice()->MidiInputDeviceID()), + pPort->GetPortNumber() + }; + for (int i = this->vMidiInputs.size() - 1; i >= 0; --i) { + if (this->vMidiInputsi == c) + this->vMidiInputs.erase(this->vMidiInputs.begin() + i); + // no break or return here, for safety reasons + // (just in case there were really duplicates for some reason) + } + } + } + + void SamplerChannel::DisconnectAllMidiInputPorts() throw (Exception) { + std::vector<MidiInputPort*> vMidiPorts = GetMidiInputPorts(); + for (int i = 0; i < vMidiPorts.size(); ++i) Disconnect(vMidiPortsi); + } + + std::vector<MidiInputPort*> SamplerChannel::GetMidiInputPorts() { + std::vector<MidiInputPort*> v; + if (pEngineChannel) { + MidiInputPort* pPort = pEngineChannel->GetMidiInputPort(0); + for (int i = 0; pPort; pPort = pEngineChannel->GetMidiInputPort(++i)) + v.push_back(pPort); + } else { + for (int i = 0; i < this->vMidiInputs.size(); ++i) { + MidiInputPort* pPort = _getPortForID(this->vMidiInputsi); + if (pPort) v.push_back(pPort); + } + } + return v; + } + void SamplerChannel::SetMidiInputDevice(MidiInputDevice* pDevice) throw (Exception) { SetMidiInput(pDevice, 0, GetMidiInputChannel()); } @@ -155,38 +236,46 @@ } void SamplerChannel::SetMidiInputChannel(midi_chan_t MidiChannel) { - SetMidiInput(GetMidiInputDevice(), GetMidiInputPort(), MidiChannel); + if (!isValidMidiChan(MidiChannel)) throw Exception("Invalid MIDI channel (" + ToString(int(MidiChannel)) + ")"); + if (pEngineChannel) pEngineChannel->SetMidiChannel(MidiChannel); + this->midiChannel = MidiChannel; } void SamplerChannel::SetMidiInput(MidiInputDevice* pDevice, int iMidiPort, midi_chan_t MidiChannel) throw (Exception) { if (!pDevice) throw Exception("No MIDI input device assigned."); - // get old and new midi input port - MidiInputPort* pOldMidiInputPort = __GetMidiInputDevicePort(GetMidiInputPort()); - MidiInputPort* pNewMidiInputPort = pDevice->GetPort(iMidiPort); + // apply new MIDI channel + SetMidiInputChannel(MidiChannel); - // disconnect old device port - if (pOldMidiInputPort && pEngineChannel) { - MidiInputDevice* pOldDevice = pOldMidiInputPort->GetDevice(); - if (pOldMidiInputPort != pNewMidiInputPort && - pOldDevice && !pOldDevice->isAutonomousDevice() - ) throw Exception("The MIDI input port '" + pOldDevice->Driver() + "' cannot be altered on this sampler channel!"); + MidiInputPort* pNewPort = pDevice->GetPort(iMidiPort); + if (!pNewPort) throw Exception("There is no MIDI input port with index " + ToString(iMidiPort) + "."); - pOldMidiInputPort->Disconnect(pEngineChannel); - } + std::vector<MidiInputPort*> vMidiPorts = GetMidiInputPorts(); - // remember new device, port and channel if not engine channel yet created - if (!pEngineChannel) { - this->pMidiInputDevice = pDevice; - this->iMidiPort = iMidiPort; - this->midiChannel = MidiChannel; + // prevent attempts to remove non-autonomous MIDI ports + // (host plugins like VST, AU, LV2, DSSI) + for (int i = 0; i < vMidiPorts.size(); ++i) { + if (vMidiPortsi == pNewPort) continue; + if (!vMidiPortsi->GetDevice()->isAutonomousDevice()) + throw Exception("The MIDI input port '" + vMidiPortsi->GetDevice()->Driver() + "' cannot be altered on this sampler channel!"); } - // connect new device port - if (pNewMidiInputPort && pEngineChannel) pNewMidiInputPort->Connect(pEngineChannel, MidiChannel); - // Ooops. - if (pNewMidiInputPort == NULL) - throw Exception("There is no MIDI input port with index " + ToString(iMidiPort) + "."); + if (pEngineChannel) { + // remove all current connections + pEngineChannel->DisconnectAllMidiInputPorts(); + // create the new connection (alone) + pEngineChannel->Connect(pNewPort); + } else { // if there is no engine channel yet, then store connection for future ... + // delete all previously scheduled connections + this->vMidiInputs.clear(); + // store the new connection (alone) + const midi_conn_t c = { + static_cast<uint>(pNewPort->GetDevice()->MidiInputDeviceID()), + pNewPort->GetPortNumber() + }; + this->vMidiInputs.push_back(c); + this->iMidiPort = iMidiPort; + } } EngineChannel* SamplerChannel::GetEngineChannel() { @@ -199,7 +288,7 @@ } int SamplerChannel::GetMidiInputPort() { - MidiInputPort* pMidiInputPort = (pEngineChannel) ? pEngineChannel->GetMidiInputPort() : NULL; + MidiInputPort* pMidiInputPort = (pEngineChannel) ? pEngineChannel->GetMidiInputPort(0) : NULL; if (pMidiInputPort) this->iMidiPort = (int) pMidiInputPort->GetPortNumber(); return iMidiPort; } @@ -210,8 +299,16 @@ MidiInputDevice* SamplerChannel::GetMidiInputDevice() { if (pEngineChannel) - pMidiInputDevice = (pEngineChannel->GetMidiInputPort()) ? pEngineChannel->GetMidiInputPort()->GetDevice() : NULL; - return pMidiInputDevice; + return (pEngineChannel->GetMidiInputPort(0)) ? pEngineChannel->GetMidiInputPort(0)->GetDevice() : NULL; + + if (vMidiInputs.empty()) + return NULL; + + std::map<uint, MidiInputDevice*> mAllDevices = MidiInputDeviceFactory::Devices(); + if (!mAllDevices.count(vMidiInputs0.deviceID)) + return NULL; + + return mAllDevicesvMidiInputs0.deviceID; } uint SamplerChannel::Index() { @@ -255,17 +352,20 @@ llEngineChangeListeners.GetListener(i)->EngineChanged(Index()); } } + + /** + * Takes a numeric MIDI device ID, port ID pair as argument and returns + * the actual MIDI input port associated with that unique ID pair. + */ + MidiInputPort* SamplerChannel::_getPortForID(const midi_conn_t& c) { + std::map<uint, MidiInputDevice*> mAllDevices = MidiInputDeviceFactory::Devices(); + if (!mAllDevices.count(c.deviceID)) + return NULL; - MidiInputPort* SamplerChannel::__GetMidiInputDevicePort(int iMidiPort) { - MidiInputPort* pMidiInputPort = NULL; - MidiInputDevice* pMidiInputDevice = GetMidiInputDevice(); - if (pMidiInputDevice) - pMidiInputPort = pMidiInputDevice->GetPort(iMidiPort); - return pMidiInputPort; + return mAllDevicesc.deviceID->GetPort(c.portNr); } - // ****************************************************************** // * Sampler @@ -622,8 +722,13 @@ if (pDevice) { // check if there are still sampler engines connected to this device for (SamplerChannelMap::iterator iterChan = mSamplerChannels.begin(); - iterChan != mSamplerChannels.end(); iterChan++ - ) if (iterChan->second->GetMidiInputDevice() == pDevice) throw Exception("Sampler channel " + ToString(iterChan->first) + " is still connected to the midi input device."); + iterChan != mSamplerChannels.end(); ++iterChan) + { + std::vector<MidiInputPort*> vPorts = iterChan->second->GetMidiInputPorts(); + for (int k = 0; k < vPorts.size(); ++k) + if (vPortsk->GetDevice() == pDevice) + throw Exception("Sampler channel " + ToString(iterChan->first) + " is still connected to the midi input device."); + } fireMidiDeviceToBeDestroyed(pDevice); MidiInputDeviceFactory::Destroy(pDevice); @@ -680,6 +785,40 @@ return count; } + int Sampler::GetGlobalMaxVoices() { + return GLOBAL_MAX_VOICES; // see common/global_private.cpp + } + + int Sampler::GetGlobalMaxStreams() { + return GLOBAL_MAX_STREAMS; // see common/global_private.cpp + } + + void Sampler::SetGlobalMaxVoices(int n) throw (Exception) { + if (n < 1) throw Exception("Maximum voices may not be less than 1"); + GLOBAL_MAX_VOICES = n; // see common/global_private.cpp + const std::set<Engine*>& engines = EngineFactory::EngineInstances(); + if (engines.size() > 0) { + std::set<Engine*>::iterator iter = engines.begin(); + std::set<Engine*>::iterator end = engines.end(); + for (; iter != end; ++iter) { + (*iter)->SetMaxVoices(n); + } + } + } + + void Sampler::SetGlobalMaxStreams(int n) throw (Exception) { + if (n < 0) throw Exception("Maximum disk streams may not be negative"); + GLOBAL_MAX_STREAMS = n; // see common/global_private.cpp + const std::set<Engine*>& engines = EngineFactory::EngineInstances(); + if (engines.size() > 0) { + std::set<Engine*>::iterator iter = engines.begin(); + std::set<Engine*>::iterator end = engines.end(); + for (; iter != end; ++iter) { + (*iter)->SetMaxDiskStreams(n); + } + } + } + void Sampler::Reset() { // delete sampler channels try { @@ -735,7 +874,7 @@ if (LSCPServer::EventSubscribers(events)) { - LSCPServer::LockRTNotify(); + LockGuard lock(LSCPServer::RTNotifyMutex); std::map<uint,SamplerChannel*> channels = GetSamplerChannels(); std::map<uint,SamplerChannel*>::iterator iter = channels.begin(); for (; iter != channels.end(); iter++) { @@ -751,8 +890,6 @@ fireTotalStreamCountChanged(GetDiskStreamCount()); fireTotalVoiceCountChanged(GetVoiceCount()); - - LSCPServer::UnlockRTNotify(); } }
View file
linuxsampler-2342.tar.bz2/src/Sampler.h -> linuxsampler-2718.tar.bz2/src/Sampler.h
Changed
@@ -3,7 +3,7 @@ * LinuxSampler - modular, streaming capable sampler * * * * Copyright (C) 2003, 2004 by Benno Senoner and Christian Schoenebeck * - * Copyright (C) 2005 - 2009 Christian Schoenebeck * + * Copyright (C) 2005 - 2014 Christian Schoenebeck * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -38,7 +38,7 @@ // just symbol prototyping class Sampler; - /** @brief LinuxSampler sampler channel + /** @brief LinuxSampler sampler channel (a.k.a. "sampler part") * * Encapsulates a channel of a specific sampler engine type, one * connection to a MIDI input device and one connection to an audio @@ -72,41 +72,125 @@ void SetAudioOutputDevice(AudioOutputDevice* pDevice) throw (Exception); /** + * Connect the given MIDIInputPort to this SamplerChannel. The + * connection is added to the sampler channel. So other MIDI input + * connections remain unaffected by this call. If the given port is + * already connected to this sampler channel, then this call is + * ignored. + * + * @param pPort - MIDI input port to connect to + * @throws Exception in case the MIDI device is tried to be changed + * while the sampler channel is being used by a + * host plugin (e.g. VST, AU, DSSI, LV2) which + * don't allow to change the MIDI port or even + * device + */ + void Connect(MidiInputPort* pPort) throw (Exception); + + /** + * Disconnects the given MidiInputPort from this SamplerChannel. + * All other MIDI input ports connected to this sampler channel + * remain unaffected. If the given port is not currently connected + * to this sampler channel, then this call is ignored. + * + * @param pPort - MIDI input port to disconnect + * @throws Exception in case the MIDI device is tried to be changed + * while the sampler channel is being used by a + * host plugin (e.g. VST, AU, DSSI, LV2) which + * don't allow to change the MIDI port or even + * device + */ + void Disconnect(MidiInputPort* pPort) throw (Exception); + + /** + * Disconnects all MIDI input ports currently connected with this + * SamplerChannel. + * + * @throws Exception in case the MIDI device is tried to be changed + * while the sampler channel is being used by a + * host plugin (e.g. VST, AU, DSSI, LV2) which + * don't allow to change the MIDI port or even + * device + */ + void DisconnectAllMidiInputPorts() throw (Exception); + + /** + * Returns all MIDI input ports currently connected to this sampler + * channel. + */ + std::vector<MidiInputPort*> GetMidiInputPorts(); + + /** * Connect this sampler channel to a MIDI input device. * + * This call will also disconnect <b>all</b> existing MIDI input + * connections from this sampler channel before establishing the + * new connection! Disconnection of all previous connections is + * done to preserve full behavior backward compatibility to times + * when this API only allowed one MIDI input port per sampler + * channel. + * * @param pDevice - MIDI input device to connect to * @throws Exception in case the MIDI device is tried to be changed * while the sampler channel is being used by a * host plugin (e.g. VST, AU, DSSI, LV2) which * don't allow to change the MIDI port or even * device + * @deprecated This method is only provided for backward + * compatibility. It is a relict from days where there + * was only 1 MIDI input allowed per SamplerChannel. */ - void SetMidiInputDevice(MidiInputDevice *pDevice) throw (Exception); + void SetMidiInputDevice(MidiInputDevice *pDevice) throw (Exception) DEPRECATED_API; /** - * Connect this sampler channel to a MIDI input port. + * Change the MIDI input port connected to this sampler channel. + * + * Calling this method will switch the connection of the first + * (and only the first) MIDIInputPort currently being connected to + * this sampler channel, to another port of the same + * MidiInputDevice. Or in other words: the first MIDIInputPort + * currently connected to this sampler channel will be disconnected, + * and the requested other port of its MIDIInputDevice will be + * connected to this sampler channel instead. + * + * This behavior is implemented to preserve full behavior backward + * compatibility to times when this API only allowed one MIDI input + * port per SamplerChannel. * * @param MidiPort - MIDI port to connect to * @throws Exception in case the MIDI port is tried to be changed * while the sampler channel is being used by a * host plugin (e.g. VST, AU, DSSI, LV2) which * don't allow to change the MIDI port + * @deprecated This method is only provided for backward + * compatibility. It is a relict from days where there + * was only 1 MIDI input allowed per SamplerChannel. */ - void SetMidiInputPort(int MidiPort) throw (Exception); + void SetMidiInputPort(int MidiPort) throw (Exception) DEPRECATED_API; /** * Define on which MIDI channel(s) this sampler channel should - * listen to. By default, that is after creation of a new - * sampler channel, the sampler channel will listen to all MIDI - * channels. + * listen to (on all MIDI ports and all virtual MIDI devices + * connected to this sampler channel). By default, that is after + * creation of a new sampler channel, the sampler channel will + * listen to all MIDI channels (a.k.a. "MIDI Omni mode"). * * @param MidiChannel - MIDI channel to listen + * @throws Exception if provided MidiChannel is not a valid constant + * as defined by midi_chan_t */ void SetMidiInputChannel(midi_chan_t MidiChannel); /** * Connect this sampler channel to a MIDI input triplet. * + * This call will also disconnect <b>all</b> existing MIDI input + * connections from this sampler channel before establishing the + * new connection! Disconnection of all previous connections is + * done to preserve full behavior backward compatibility to times + * when this API only allowed one MIDI input port per sampler + * channel. + * * @param pDevice - MIDI input device to connect to * @param iMidiPort - MIDI port to connect to * @param MidiChannel - optional: MIDI channel on which the @@ -116,8 +200,11 @@ * while the sampler channel is being used by a * host plugin (e.g. VST, AU, DSSI, LV2) which * don't allow to change the MIDI port + * @deprecated This method is only provided for backward + * compatibility. It is a relict from days where there + * was only 1 MIDI input allowed per sampler channel. */ - void SetMidiInput(MidiInputDevice* pDevice, int iMidiPort, midi_chan_t MidiChannel = midi_chan_all) throw (Exception); + void SetMidiInput(MidiInputDevice* pDevice, int iMidiPort, midi_chan_t MidiChannel = midi_chan_all) throw (Exception) DEPRECATED_API; /** * Returns the EngineChannel object that was deployed on this @@ -140,9 +227,14 @@ * Returns the MIDI input port number to which this sampler * channel is currently connected to. * + * This method should not be used in new applications anymore! + * * @returns MIDI input port number or -1 if not connected + * @deprecated This method is only provided for backward + * compatibility. It is a relict from days where there + * was only 1 MIDI input allowed per sampler channel. */ - int GetMidiInputPort(); + int GetMidiInputPort() DEPRECATED_API; /** * Returns the audio output device to which this sampler channel @@ -159,8 +251,11 @@ * * @returns pointer to MIDI input device or NULL if not * connected + * @deprecated This method is only provided for backward + * compatibility. It is a relict from days where there + * was only 1 MIDI input allowed per sampler channel. */ - MidiInputDevice* GetMidiInputDevice(); + MidiInputDevice* GetMidiInputDevice() DEPRECATED_API; /** * Returns the index number of this sampler channel within the @@ -207,20 +302,34 @@ SamplerChannel(Sampler* pS); virtual ~SamplerChannel(); - /** Getting MIDI input device port given its index number. */ - MidiInputPort* __GetMidiInputDevicePort(int iMidiPort); - Sampler* pSampler; EngineChannel* pEngineChannel; - AudioOutputDevice* pAudioOutputDevice; - MidiInputDevice* pMidiInputDevice; + AudioOutputDevice* pAudioOutputDevice; //FIXME: should be stored as numeric device ID instead of raw pointer to avoid pointer invalidation problems int iIndex; friend class Sampler; + private: - int iMidiPort; ///< Don't access directly, read GetMidiInputPort() instead ! + struct midi_conn_t { + uint deviceID; + uint portNr; + + bool operator== (const midi_conn_t& other) const { + return other.deviceID == this->deviceID && + other.portNr == this->portNr; + } + + bool operator< (const midi_conn_t& other) const { + return memcmp(this, &other, sizeof(midi_conn_t)) < 0; + } + }; + + int iMidiPort; ///< Don't access directly, read GetMidiInputPort() instead ! @deprecated This variable is just for backward compatibility from days when there was only one MIDI connection per SamplerChannel. midi_chan_t midiChannel; ///< Don't access directly, read GetMidiInputChannel() instead ! + std::vector<midi_conn_t> vMidiInputs; ///< MIDI input ports connected to this sampler channel. Only used as "cache" (device id, port nr pair) in initial situation where no engine type is selected yet, and accordingly no EngineChannel instance exists which actually manages the device connections. This way users can "connect" MIDI input ports to this SamplerChannel before an engine type is chosen. ListenerList<EngineChangeListener*> llEngineChangeListeners; + + static MidiInputPort* _getPortForID(const midi_conn_t& c); }; /** @brief LinuxSampler main class @@ -228,23 +337,22 @@ * This is the toplevel class for a LinuxSampler instance. * * LinuxSampler can have arbitrary numbers of sampler channels. Each - * sampler channel can individually be deployed with it's own sampler - * engine, connected to an arbitrary MIDI input device and connected to - * an arbitrary audio output device. Here an example setup: + * sampler channel (a.k.a. "sampler part") can individually be deployed + * with it's own sampler engine, connected to an arbitrary MIDI input + * device and connected to an arbitrary audio output device. Here an + * example setup: * @code * S.Channel MIDI in S.Engine Audio out * ------------------------------------------------------------------- * 0 Alsa -> gig::Engine -> Jack - * 1 VSTi -> Akai::Engine -> VSTi - * 2 Jack -> DLS::Engine -> Jack - * 3 Jack -> SF::Engine -> Alsa + * 1 VSTi -> gig::Engine -> VSTi + * 2 Jack -> sfz::Engine -> Jack + * 3 Jack -> SF2::Engine -> Alsa + * 4 LV2 -> sfz::Engine -> LV2 * * ... (and so on) ... * @endcode * - * Note that not all audio and MIDI backends and sampler engines listed - * in the example above might already been implemented! - * * As you can see in the example setup, LinuxSampler is capable to use * several, different audio output and MIDI input systems * simultaniously at the same time. Here the example setup shown in the @@ -457,6 +565,36 @@ int GetVoiceCount(); /** + * @see SetGlobalMaxVoices() + */ + int GetGlobalMaxVoices(); + + /** + * @see SetGlobalMaxStreams() + */ + int GetGlobalMaxStreams(); + + /** + * Sets the global maximum amount limit of voices. + * + * Note that this voice limit can also be altered for + * each sampler engine instance individually instead. + * + * @throws Exception if \a n is invalid + */ + void SetGlobalMaxVoices(int n) throw (Exception); + + /** + * Sets the global maximum amount limit of disk streams. + * + * Note that this stream limit can also be altered for + * each sampler engine instance individually instead. + * + * @throws Exception if \a n is invalid + */ + void SetGlobalMaxStreams(int n) throw (Exception); + + /** * Reset the whole sampler. Destroy all engines, sampler * channels, MIDI input devices and audio output devices. */
View file
linuxsampler-2342.tar.bz2/src/common/ArrayList.h -> linuxsampler-2718.tar.bz2/src/common/ArrayList.h
Changed
@@ -1,6 +1,6 @@ /*************************************************************************** * * - * Copyright (C) 2005 - 2010 Christian Schoenebeck * + * Copyright (C) 2005 - 2014 Christian Schoenebeck * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -90,6 +90,21 @@ } /** + * Increase or decrease the size of this list to the new amount of + * elements given by \a cnt. + */ + void resize(int cnt) { + T* pNewArray = new Tcnt; + if (pData) { + for (int i = 0; i < cnt; i++) + pNewArrayi = pDatai; + delete pData; + } + pData = pNewArray; + iSize = cnt; + } + + /** * Remove all elements from the list. */ void clear() { @@ -114,14 +129,14 @@ /** * Number of elements currently on the list. */ - inline int size() { + inline int size() const { return iSize; } /** * Returns true if the list is empty. */ - inline bool empty() { + inline bool empty() const { return (bool) !iSize; } @@ -133,6 +148,13 @@ } /** + * Access element at \a iPosition. + */ + inline const T& operator(int iPosition) const { + return pDataiPosition; + } + + /** * Copy constructor. */ ArrayList(const ArrayList& list) {
View file
linuxsampler-2718.tar.bz2/src/common/ChangeFlagRelaxed.h
Added
@@ -0,0 +1,42 @@ +/* + Copyright (C) 2013 Christian Schoenebeck + */ + +#ifndef LS_CHANGEFLAGRELAXED_H +#define LS_CHANGEFLAGRELAXED_H + +#include "atomic.h" + +/** + * May be used as boolean variable to synchronize that some shared resource has + * been changed, with relaxed memory order. It is designed for exactly 1 reading + * thread (calling readAndReset()) and n writing threads (calling raise()). + * Synchronization with "relaxed memory order" means here that the information + * written by raise() calls are never lost, but it may take some time to + * propagate to the reading thread, thus readAndReset() may not always return + * the very latest information. So the informations might be delivered with a + * delay. + */ +class ChangeFlagRelaxed { +public: + ChangeFlagRelaxed() { + newval = (atomic_t) ATOMIC_INIT(0); + oldval = 0; + } + + inline bool readAndReset() { + int v = atomic_read(&newval); + bool changed = (oldval != v); + oldval = v; + return changed; + } + + inline void raise() { + atomic_inc(&newval); + } +private: + atomic_t newval; + int oldval; +}; + +#endif // LS_CHANGEFLAGRELAXED_H
View file
linuxsampler-2342.tar.bz2/src/common/Condition.cpp -> linuxsampler-2718.tar.bz2/src/common/Condition.cpp
Changed
@@ -3,7 +3,7 @@ * LinuxSampler - modular, streaming capable sampler * * * * Copyright (C) 2003, 2004 by Benno Senoner and Christian Schoenebeck * - * Copyright (C) 2005 - 2012 Christian Schoenebeck * + * Copyright (C) 2005 - 2013 Christian Schoenebeck * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -358,7 +358,7 @@ void Condition::Set(bool bCondition) { dmsg(7,("Condition::Set() -> LOCK()\n")); - Lock(); + LockGuard lock(*this); dmsg(7,("Condition::Set() -> LOCK() passed\n")); if (this->bCondition != bCondition) { this->bCondition = bCondition; @@ -379,7 +379,6 @@ #endif } } - Unlock(); } bool Condition::GetUnsafe() {
View file
linuxsampler-2718.tar.bz2/src/common/ConstCapacityArray.h
Added
@@ -0,0 +1,200 @@ +/* + * Copyright (c) 2014 Christian Schoenebeck + * + * http://www.linuxsampler.org + * + * This file is part of LinuxSampler and released under the same terms. + * See README file for details. + */ + +#ifndef LS_CONSTCAPACITYARRAY_H +#define LS_CONSTCAPACITYARRAY_H + +#include <stdlib.h> +#include <stdint.h> + +namespace LinuxSampler { + + /** + * Very simple container with array implementation which ensures a constant + * access time of Theta(1) on individual elements. Internally it uses a + * consecutive, dense C array (on heap) where its capacity is defined by the + * constructor of this class. That internal array will never be reallocated + * during the lifetime of a ConstCapacityArray object and thus C pointers to + * elements of this array remain always valid. + * + * This class has methods to add and remove elements from the array and + * "resizing" the array. All those operations are guaranteed to be real-time + * safe. When elements are removed from the array, members past the removed + * element(s) will be moved towards the array start, to always ensure that + * the array is kept dense (without any gaps). + */ + template<typename T> + class ConstCapacityArray { + public: + /** + * Create a ConstCapacityArray with the given capacity. The initial + * size of the array will be zero. ConstCapacityArray's size can never + * grow larger than its capacity defined with the constructor here. + * + * This constructor <b>must not</b> be called in a real-time context! + * + * @param capacity - maximum size this object may ever grow + */ + ConstCapacityArray(int capacity) : + m_data(new Tcapacity), m_capacity(capacity), m_size(0) {} + + /** + * Destructor. Frees the internal array allocated by this object. + * + * This destructor <b>must not</b> be called in a real-time context! + */ + ~ConstCapacityArray() { + if (m_data) delete m_data; + } + + /** + * Amount of elements this array currently holds. The array's size may + * vary between 0 ... capacity() during its lifetime. + * + * Access time is always constant with Theta(1). + * + * This method is guaranteed to be real-time safe. + * + * @see capacity() + */ + inline uint size() const { + return m_size; + } + + /** + * Maximum size this array may ever grow to in its lifetime. + * + * Access time is always constant with Theta(1). + * + * This method is guaranteed to be real-time safe. + * + * @see size() + */ + inline uint capacity() const { + return m_capacity; + } + + /** + * Returns true if this array is empty. + * + * Access time is always constant with Theta(1). + * + * This method is guaranteed to be real-time safe. + */ + inline bool empty() const { + return !m_size; + } + + /** + * Add a new element to the end of the array. Size is incremented by + * one. You cannot add more elements than capacity() reflects. + * + * This method is guaranteed to be real-time safe. + * + * @param element - value to be added as new element + */ + inline void append(const T& element) { + if (m_size == m_capacity) return; // max. size already exhausted + m_datam_size++ = element; + } + + /** + * Remove @a count elements from the array at position @a index. + * Elements past the removed element(s) will be moved towards the + * beginning of the array. So the array always remains dense (without + * gaps). + * + * This method is guaranteed to be real-time safe. + * + * @param index - position where element(s) shall be removed + * @param count - amount of elements to be removed + */ + inline void remove(uint index, uint count = 1) { + if (index >= m_size || index + count > m_size) + return; + // don't use memmove() here! Since it is not RT safe with all libc + // implementations and on all architectures + for (uint i = 0; i < count; ++i) + m_dataindex + i = m_dataindex + i + count; + m_size -= count; + } + + /** + * Returns true in case there is an element with given @a value in this + * array + * + * @param value - value to be searched for + */ + bool contains(const T& value) const { + for (uint i = 0; i < m_size; ++i) + if (m_datai == value) return true; + return false; + } + + /** + * Searches the array for an element with given index, if found it + * returns the element's index, if no such element was found an invalid + * index will be returned instead. + * + * @param value - value to be searched for + * @returns index of found element + */ + uint find(const T& value) const { + for (uint i = 0; i < m_size; ++i) + if (m_datai == value) return i; + return -1; + } + + /** + * Remove all elements from the array. Size of array will be zero after + * this call. + * + * Access time is always constant with Theta(1). + * + * This method is guaranteed to be real-time safe. + */ + inline void clear() { + m_size = 0; + } + + /** + * Access element at position @a index (for read/write access). + * + * Access time is always constant with Theta(1). + * + * This method is guaranteed to be real-time safe. + * + * @param index - position of array to access + */ + inline T& operator(uint index) { + return m_dataindex; + } + + /** + * Access element at position @a index (for read only access). + * + * Access time is always constant with Theta(1). + * + * This method is guaranteed to be real-time safe. + * + * @param index - position of array to access + */ + inline const T& operator(uint index) const { + return m_dataindex; + } + + private: + T* __restrict const m_data; + int m_capacity; + int m_size; + }; + +} // namespace LinuxSampler + +#endif // LS_CONSTCAPACITYARRAY_H
View file
linuxsampler-2342.tar.bz2/src/common/File.h -> linuxsampler-2718.tar.bz2/src/common/File.h
Changed
@@ -26,9 +26,7 @@ #include <vector> #include "Mutex.h" -#ifndef WIN32 #include <sys/stat.h> -#endif namespace LinuxSampler {
View file
linuxsampler-2342.tar.bz2/src/common/Makefile.am -> linuxsampler-2718.tar.bz2/src/common/Makefile.am
Changed
@@ -1,4 +1,4 @@ -INCLUDES = $(all_includes) +AM_CPPFLAGS = $(all_includes) METASOURCES = AUTO AM_CXXFLAGS = -Wreturn-type -ffast-math $(CXX_CPU_SWITCH) @@ -12,6 +12,7 @@ Mutex.h \ SynchronizedConfig.h \ Condition.h \ + ConstCapacityArray.h \ lsatomic.h noinst_LTLIBRARIES = liblinuxsamplercommon.la @@ -35,7 +36,9 @@ WorkerThread.cpp WorkerThread.h \ Path.cpp Path.h \ File.cpp File.h \ - ladspa.h + ladspa.h \ + Ref.h Ref.cpp \ + ChangeFlagRelaxed.h # create the plugins directory (i.e. /usr/lib/linuxsampler/plugins) install-exec-hook:
View file
linuxsampler-2342.tar.bz2/src/common/Mutex.h -> linuxsampler-2718.tar.bz2/src/common/Mutex.h
Changed
@@ -3,7 +3,7 @@ * LinuxSampler - modular, streaming capable sampler * * * * Copyright (C) 2003, 2004 by Benno Senoner and Christian Schoenebeck * - * Copyright (C) 2005, 2006 Christian Schoenebeck * + * Copyright (C) 2005 - 2014 Christian Schoenebeck * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -88,16 +88,46 @@ * Lock() call, one of them will be awaken. */ void Unlock(); - + protected: #if defined(WIN32) - HANDLE hMutex; + HANDLE hMutex; #else pthread_mutex_t __posix_mutex; pthread_mutexattr_t __posix_mutexattr; #endif }; +// Lock guard for exception safe locking +class LockGuard { +public: + LockGuard(Mutex& m) : pm(&m) { + m.Lock(); + } + + /** + * Empty LockGuard. This constructor can be used to implement conditional + * mutex protection like: + * @code + * Mutex m; + * LockGuard g; + * if (requiresMutexProtection()) g = LockGuard(m); + * @endcode + */ + LockGuard() : pm(NULL) { + } + + LockGuard(const LockGuard& g) : pm(g.pm) { + if (pm) pm->Lock(); + } + + ~LockGuard() { + if (pm) pm->Unlock(); + } +private: + Mutex* pm; +}; + } // namespace LinuxSampler #endif // __MUTEX_H__
View file
linuxsampler-2342.tar.bz2/src/common/Path.cpp -> linuxsampler-2718.tar.bz2/src/common/Path.cpp
Changed
@@ -1,6 +1,6 @@ /*************************************************************************** * * - * Copyright (C) 2007 Christian Schoenebeck * + * Copyright (C) 2007-2014 Christian Schoenebeck * * * * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -27,7 +27,15 @@ namespace LinuxSampler { -Path::Path() : drive(0) { +Path::Path() : drive(0), absolute(false) { +} + +Path::Path(std::string path) { + #if WIN32 + *this = fromWindows(path); + #else + *this = fromPosix(path); + #endif } void Path::appendNode(std::string Name) { @@ -37,6 +45,7 @@ void Path::setDrive(const char& Drive) { drive = Drive; + absolute = true; } std::string Path::toPosix() const { @@ -188,6 +197,9 @@ s.replace(pos, 3, pcAscii); } } + // check whether given string reflects an absolute path + // (indicated by a forward slash as first character on POSIX) + result.absolute = !path.empty() && path0 == '/'; return result; } @@ -241,6 +253,10 @@ } } + // check whether given string reflects an absolute path + // (indicated either by a backslash or drive at the beginning on Windows) + result.absolute = result.drive || (!path.empty() && path0 == '\\'); + return result; } @@ -298,4 +314,8 @@ return p.stripLastName(); } +bool Path::isAbsolute() const { + return absolute; +} + } // namespace LinuxSampler
View file
linuxsampler-2342.tar.bz2/src/common/Path.h -> linuxsampler-2718.tar.bz2/src/common/Path.h
Changed
@@ -1,6 +1,6 @@ /*************************************************************************** * * - * Copyright (C) 2007 Christian Schoenebeck * + * Copyright (C) 2007-2014 Christian Schoenebeck * * * * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -44,9 +44,18 @@ */ class Path { public: + /** + * Default constructor. + */ Path(); /** + * Creates a path object according to the local system's path conventions. + * @see fromPosix(), fromWindows(), fromDbPath() + */ + Path(std::string path); + + /** * Concatenate exactly one path node or filename to the end of this Path * object. This can be used to build up a full qualified path, directory * by directory. @@ -149,9 +158,16 @@ */ static std::string getBaseName(std::string path); + /** + * Returns true if the path is reflecting an absolute path, false if it is + * rather a relative path. + */ + bool isAbsolute() const; + private: std::vector<std::string> elements; ///< stores the path names raw = unencoded, each element is one node of the path char drive; + bool absolute; }; } // namespace LinuxSampler
View file
linuxsampler-2342.tar.bz2/src/common/Pool.h -> linuxsampler-2718.tar.bz2/src/common/Pool.h
Changed
@@ -3,7 +3,7 @@ * LinuxSampler - modular, streaming capable sampler * * * * Copyright (C) 2003, 2004 by Benno Senoner and Christian Schoenebeck * - * Copyright (C) 2005 - 2012 Christian Schoenebeck * + * Copyright (C) 2005 - 2014 Christian Schoenebeck * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -61,6 +61,7 @@ #if CONFIG_DEVMODE RTListBase<T1>* list; // list to which this node currently belongs to #endif // CONFIG_DEVMODE + int reincarnation; // just for Pool::fromID() _Node() { next = NULL; @@ -69,6 +70,7 @@ #if CONFIG_DEVMODE list = NULL; #endif // CONFIG_DEVMODE + reincarnation = 0; } }; typedef _Node<T> Node; @@ -145,7 +147,20 @@ } #endif // CONFIG_DEVMODE return *current->data; + } + inline const T1& operator*() const { + #if CONFIG_DEVMODE + if (!isValid()) { // if iterator became invalidated + #if CONFIG_RT_EXCEPTIONS + throw std::runtime_error(__err_msg_iterator_invalidated); + #else + std::cerr << __err_msg_iterator_invalidated << std::endl << std::flush; + return *((const T1*)NULL); // force segfault if iterator became invalidated + #endif // CONFIG_RT_EXCEPTIONS + } + #endif // CONFIG_DEVMODE + return *current->data; } inline T1* operator->() { @@ -162,11 +177,25 @@ return current->data; } - inline bool operator==(const _Iterator<T1> other) { + inline const T1* operator->() const { + #if CONFIG_DEVMODE + if (!isValid()) { // if iterator became invalidated + #if CONFIG_RT_EXCEPTIONS + throw std::runtime_error(__err_msg_iterator_invalidated); + #else + std::cerr << __err_msg_iterator_invalidated << std::endl << std::flush; + return (const T1*)NULL; // force segfault if iterator became invalidated + #endif // CONFIG_RT_EXCEPTIONS + } + #endif // CONFIG_DEVMODE + return current->data; + } + + inline bool operator==(const _Iterator<T1> other) const { return current == other.current; } - inline bool operator!=(const _Iterator<T1> other) { + inline bool operator!=(const _Iterator<T1> other) const { return current != other.current; } @@ -195,7 +224,7 @@ } #if CONFIG_DEVMODE - inline bool isValid() { + inline bool isValid() const { return current->list == list; } #endif // CONFIG_DEVMODE @@ -232,6 +261,19 @@ #endif // CONFIG_DEVMODE } + inline const Node* node() const { + #if CONFIG_DEVMODE + #if CONFIG_RT_EXCEPTIONS + if (isValid()) return current; + else throw std::runtime_error(__err_msg_iterator_invalidated); + #else + return (isValid()) ? current : (const Node*)NULL; // force segfault if iterator became invalidated + #endif // CONFIG_RT_EXCEPTIONS + #else + return current; + #endif // CONFIG_DEVMODE + } + inline void detach() { RTListBase<T1>::detach(*this); } @@ -258,7 +300,7 @@ return Iterator(&_end, Iterator::dir_backward); } - inline bool isEmpty() { + inline bool isEmpty() const { return _begin.next == &_end; } @@ -399,7 +441,7 @@ clear(); } - inline bool poolIsEmpty() { + inline bool poolIsEmpty() const { return pPool->poolIsEmpty(); } @@ -438,6 +480,18 @@ } } + inline int getID(const T* obj) const { + return pPool->getID(obj); + } + + inline int getID(const Iterator& it) const { + return pPool->getID(&*it); + } + + inline Iterator fromID(int id) const { + return pPool->fromID(id); + } + protected: Pool<T>* pPool; }; @@ -462,7 +516,7 @@ if (data) delete data; } - inline bool poolIsEmpty() { + inline bool poolIsEmpty() const { return freelist.isEmpty(); } @@ -507,6 +561,74 @@ _init(Elements); } + /** + * Returns an abstract, unique numeric ID for the given object of + * this pool, it returns -1 in case the passed object is not a member + * of this Pool, i.e. because it is simply an invalid pointer or member + * of another Pool. The returned ID is unique among all elements of this + * Pool and it differs with each reincarnation of an object. That means + * each time you free an element to and allocate the same element back + * from the Pool, it will have a different ID. + * + * Members are always translated both, from Iterators/pointers to IDs, + * and from IDs to Iterators/pointers in constant time. + * + * You might want to use this alternative approach of referencing Pool + * members under certain scenarios. For example if you need to expose + * an ID to the end user and/or if you want to represent an object of + * this pool by a smaller number instead of a native pointer (i.e. 16 + * bits vs. 64 bits). You can also detect this way whether the object + * has already been freed / reallocated from the Pool in the meantime. + * + * @param obj - raw pointer to a data member of this Pool + * @returns unique numeric ID of @a obj or -1 if pointer was invalid + */ + int getID(const T* obj) const { + if (!poolsize) return -1; + int index = obj - &data0; + if (index < 0 || index >= poolsize) return -1; + return (nodesindex.reincarnation << bitsForSize(poolsize)) | index; + } + + /** + * Overridden convenience method, behaves like the method above. + */ + int getID(const Iterator& it) const { + return getID(&*it); + } + + /** + * Returns an Iterator object of the Pool data member reflected by the + * given abstract, unique numeric ID, it returns an invalid Iterator in + * case the ID is invalid or if the Pool's data element reflected by + * given ID was at least once released/freed back to the Pool in the + * meantime. + * + * Members are always translated both, from Iterators/pointers to IDs, + * and from IDs to Iterators/pointers in constant time. + * + * You might want to use this alternative approach of referencing Pool + * members under certain scenarios. For example if you need to expose + * an ID to the end user and/or if you want to represent an object of + * this pool by a smaller number instead of a native pointer (i.e. 16 + * bits vs. 64 bits). You can also detect this way whether the object + * has already been freed / reallocated from the Pool in the meantime. + * + * @param id - unique ID of a Pool's data member + * @returns Iterator object pointing to Pool's data element, invalid + * Iterator in case ID was invalid or data element was freed + */ + Iterator fromID(int id) const { + if (id < 0) return Iterator(); // invalid iterator + const uint bits = bitsForSize(poolsize); + uint index = id & ((1 << bits) - 1); + if (index >= poolsize) return Iterator(); // invalid iterator + Node* node = &nodesindex; + int reincarnation = uint(id) >> bits; + if (reincarnation != node->reincarnation) return Iterator(); // invalid iterator + return Iterator(node); + } + protected: // caution: assumes pool (that is freelist) is not empty! inline Iterator alloc() { @@ -516,10 +638,15 @@ } inline void freeToPool(Iterator itElement) { + itElement.node()->reincarnation++; freelist.append(itElement); } inline void freeToPool(Iterator itFirst, Iterator itLast) { + for (Node* n = itFirst.node(); true; n = n->next) { + n->reincarnation++; + if (n == itLast.node()) break; + } freelist.append(itFirst, itLast); } @@ -535,6 +662,14 @@ } poolsize = Elements; } + + inline static int bitsForSize(int size) { + if (!size) return 0; + size--; + int bits = 0; + for (; size > 1; bits += 2, size >>= 2); + return bits + size; + } }; #endif // __LS_POOL_H__
View file
linuxsampler-2718.tar.bz2/src/common/Ref.cpp
Added
@@ -0,0 +1,18 @@ +/* + * Copyright (c) 2014 Christian Schoenebeck + * + * http://www.linuxsampler.org + * + * This file is part of LinuxSampler and released under the same terms. + * See README file for details. + */ + +#include "Ref.h" + +namespace LinuxSampler { + + #if LS_REF_ASSERT_MODE + std::set<void*> _allRefPtrs; + #endif + +} // namespace LinuxSampler
View file
linuxsampler-2718.tar.bz2/src/common/Ref.h
Added
@@ -0,0 +1,375 @@ +/* + * Copyright (c) 2014 Christian Schoenebeck + * + * http://www.linuxsampler.org + * + * This file is part of LinuxSampler and released under the same terms. + * See README file for details. + */ + +#ifndef LS_REF_H +#define LS_REF_H + +#include <set> +#include <stdio.h> + +// You may enable this while developing or at least when you encounter any kind +// of crashes or other misbehaviors in conjunction with Ref class guarded code. +// Enabling the following macro will add a bunch of sanity checks for easing +// debugging of such issues, however it comes with the cost that everything will +// be much slower. +#define LS_REF_ASSERT_MODE 0 + +#if LS_REF_ASSERT_MODE +# warning LS_REF_ASSERT_MODE is enabled which will decrease runtime efficiency! +#endif + +// Enable this for VERY verbose debug messages for debbugging deep issues with +// Ref class. +#define LS_REF_VERBOSE_DEBUG_MSG 0 + +#if LS_REF_ASSERT_MODE +# include <assert.h> +#endif + +namespace LinuxSampler { + + //TODO: make reference count increment/decrement thread safe + + template<typename T, typename T_BASE> class Ref; + + extern std::set<void*> _allRefPtrs; + + /** + * Exists just for implementation detail purpose, you cannot use it + * directly. Use its derived template class Ref instead. + * + * @see Ref + */ + template<typename T_BASE> + class RefBase { + public: + template<typename T_BASE1> + class _RefCounter { + public: + _RefCounter(T_BASE1* p, int refs) : + references(refs), ptr(p) + { + #if LS_REF_VERBOSE_DEBUG_MSG + printf("Ref 0x%lx: new counter (refs=%d)\n", (long long)ptr, references); + #endif + #if LS_REF_ASSERT_MODE + assert(p); + assert(refs > 0); + assert(!_allRefPtrs.count(p)); + _allRefPtrs.insert(p); + #endif + } + + virtual ~_RefCounter() { + #if LS_REF_VERBOSE_DEBUG_MSG + printf("Ref 0x%lx: counter destructor (refs=%d)\n", (long long)ptr, references); + #endif + fflush(stdout); + } + + void retain() { + references++; + #if LS_REF_VERBOSE_DEBUG_MSG + printf("Ref 0x%lx: retain (refs=%d)\n", (long long)ptr, references); + #endif + } + + void release() { + if (!references) return; + references--; + #if LS_REF_VERBOSE_DEBUG_MSG + printf("Ref 0x%lx: release (refs=%d)\n", (long long)ptr, references); + #endif + if (!references) deletePtr(); + } + //protected: + void deletePtr() { + #if LS_REF_VERBOSE_DEBUG_MSG + printf("RefCounter 0x%lx: deletePtr() (refs=%d)\n", (long long)ptr, references); + #endif + #if LS_REF_ASSERT_MODE + assert(!references); + _allRefPtrs.erase(ptr); + #endif + delete ptr; + delete this; + } + + int references; + T_BASE1* ptr; + //friend class ... todo + }; + typedef _RefCounter<T_BASE> RefCounter; + + virtual ~RefBase() { + if (refCounter) refCounter->release(); + refCounter = NULL; + } + + //protected: + RefCounter* refCounter; + //friend class Ref<T_BASE, T_BASE>; + + protected: + RefBase() : refCounter(NULL) { + #if LS_REF_VERBOSE_DEBUG_MSG + printf("(RefBase empty ctor)\n"); + #endif + } +/* + RefBase(RefCounter* rc) { + refCounter = rc; + } + + RefBase(const RefBase& r) { + refCounter = r.refCounter; + if (refCounter) refCounter->retain(); + } +*/ + }; + + /** + * Replicates a std::shared_ptr template class, to avoid a build requirement + * of having a C++11 compliant compiler (std::shared_ptr was not part of the + * C++03 standard). + * + * In contrast to the STL implementation though this implementation here + * also supports copying references of derived, different types (in a type + * safe manner). You can achieve that by providing a second template + * argument (which is optional), for declaring a common subtype. For example + * the following code would not compile: + * @code + * void example(UILabel* pLabel) { + * Ref<UILabel> lbl = pLabel; + * Ref<UIWidget> w = lbl; // compile error, incompatible Ref types + * w->resize(16,300); + * } + * @endcode + * Whereas the following would work: + * @code + * void example(UILabel* pLabel) { + * Ref<UILabel,UIWidget> lbl = pLabel; + * Ref<UIWidget> w = lbl; // works (assuming that UILabel is a subclass of UIWidget) + * w->resize(16,300); + * } + * @endcode + * Like the STL's std::shared_ptr, this class also emulates raw pointer + * access and operators. With one addition: if used in the derived common + * subtype manner as shown above, access to the actual data and boolean + * operator will also check whether the underlying pointer (of the common + * subclass) can actually be casted safely to the objects main type (first + * template argument of this class). For example: + * @code + * void example(UILabel* pLabel) { // assuming pLabel is not NULL ... + * Ref<UILabel,UIWidget> lbl = pLabel; + * Ref<UIDialog,UIWidget> dlg = lbl; + * bool b1 = lbl; // will be true (assuming pLabel was not NULL) + * bool b2 = dlg; // will be false (assuming that UIDialog is not derived from UILabel) + * lbl->setText("foo"); // works + * dlg->showModal(); // would crash with -> operator providing a NULL pointer + * } + * @endcode + * Like with std::shared_ptr you must be @b very cautious that you + * initialize only one Ref class object directly with the same raw pointer. + * If you forget this fundamental rule somewhere, your code will crash! + * @code + * UIWidget* ptr = new UIWidget(); + * Ref<UIWidget> w1 = ptr; + * Ref<UIWidget> w2 = w1; // this is OK, copy from a Ref object + * Ref<UIWidget> w3 = ptr; // illegal! 2nd direct init from same raw pointer. This will crash! + * @endcode + * It would be possible to write an implementation of the Ref class that + * could handle the case above as well without crashing, however it would be + * too slow for practice. Because it would require a global lookup table + * maintaining all memory pointers which are currently already guarded by + * this class. Plus it would need an expensive synchronization to prevent + * concurrent access on that global lookup table. + */ + template<typename T, typename T_BASE = T> + class Ref : public RefBase<T_BASE> { + public: + typedef RefBase<T_BASE> RefBaseT; + typedef typename RefBase<T_BASE>::RefCounter RefCounter; + + Ref() : RefBaseT() { + #if LS_REF_VERBOSE_DEBUG_MSG + printf("Ref empty ctor Ref:0x%lx\n", (long long)this); + #endif + } + + Ref(const T_BASE* p) : RefBaseT() { + #if LS_REF_VERBOSE_DEBUG_MSG + printf("Ref base ptr ctor Ref:0x%lx <- p:0x%lx\n", (long long)this, (long long)p); + #endif + RefBaseT::refCounter = p ? new RefCounter((T_BASE*)p, 1) : NULL; + } + + Ref(const T* p) : RefBaseT() { + #if LS_REF_VERBOSE_DEBUG_MSG + printf("Ref main ptr ctor Ref:0x%lx <- p:0x%lx\n", (long long)this, (long long)p); + #endif + RefBaseT::refCounter = p ? new RefCounter((T*)p, 1) : NULL; + } + + Ref(const RefBaseT& r) : RefBaseT() { + #if LS_REF_VERBOSE_DEBUG_MSG + printf("Ref base ref ctor Ref:0x%lx <- Ref:0x%lx\n", (long long)this, (long long)&r); + #endif + RefBaseT::refCounter = r.refCounter; + if (RefBaseT::refCounter) + RefBaseT::refCounter->retain(); + } + + Ref(const Ref& r) : RefBaseT() { + #if LS_REF_VERBOSE_DEBUG_MSG + printf("Ref main ref ctor Ref:0x%lx <- Ref:0x%lx\n", (long long)this, (long long)&r); + #endif + RefBaseT::refCounter = r.refCounter; + if (RefBaseT::refCounter) + RefBaseT::refCounter->retain(); + } + + inline T* operator->() { + return dynamic_cast<T*>( RefBaseT::refCounter->ptr ); + } + + inline const T* operator->() const { + return dynamic_cast<const T*>( RefBaseT::refCounter->ptr ); + } + + inline T& operator*() { + return *dynamic_cast<T*>( RefBaseT::refCounter->ptr ); + } + + inline const T& operator*() const { + return *dynamic_cast<const T*>( RefBaseT::refCounter->ptr ); + } + + inline bool operator==(const RefBaseT& other) const { + return RefBaseT::refCounter == other.refCounter; + } + + inline bool operator!=(const RefBaseT& other) const { + return RefBaseT::refCounter != other.refCounter; + } + + inline operator bool() const { + return RefBaseT::refCounter && RefBaseT::refCounter->ptr && + dynamic_cast<const T*>( RefBaseT::refCounter->ptr ); + } + + inline bool operator!() const { + return !( RefBaseT::refCounter && RefBaseT::refCounter->ptr && + dynamic_cast<const T*>( RefBaseT::refCounter->ptr ) ); + } + +/* + inline operator RefBaseT&() { + return *this; + } + + inline operator const RefBaseT&() const { + return *this; + } +*/ + inline bool isEquivalent(const RefBaseT& other) const { + if (static_cast<const RefBaseT*>(this) == &other) + return true; + return (RefBaseT::refCounter == other.refCounter); + } + + inline bool isEquivalent(const T_BASE* const other) const { + if (!other) return !RefBaseT::refCounter; + if (!RefBaseT::refCounter) return false; + return other == RefBaseT::refCounter->ptr; + } + + Ref<T,T_BASE>& operator=(const RefBaseT& other) { + #if LS_REF_VERBOSE_DEBUG_MSG + printf("Ref base ref assignment Ref:0x%lx <- Ref:0x%lx\n", (long long)this, (long long)&other); + #endif + if (isEquivalent(other)) { + #if LS_REF_VERBOSE_DEBUG_MSG + printf("Ref 0x%lx WRN: equivalent ref assignment ignored.\n", (long long)this); + #endif + return *this; + } + if (RefBaseT::refCounter) { + RefBaseT::refCounter->release(); + RefBaseT::refCounter = NULL; + } + RefBaseT::refCounter = other.refCounter; + if (RefBaseT::refCounter) + RefBaseT::refCounter->retain(); + return *this; + } + + Ref<T,T_BASE>& operator=(const Ref<T,T_BASE>& other) { + #if LS_REF_VERBOSE_DEBUG_MSG + printf("Ref main ref assignment Ref:0x%lx <- Ref:0x%lx\n", (long long)this, (long long)&other); + #endif + if (isEquivalent(other)) { + #if LS_REF_VERBOSE_DEBUG_MSG + printf("Ref 0x%lx WRN: equivalent ref assignment ignored.\n", (long long)this); + #endif + return *this; + } + if (RefBaseT::refCounter) { + RefBaseT::refCounter->release(); + RefBaseT::refCounter = NULL; + } + RefBaseT::refCounter = other.refCounter; + if (RefBaseT::refCounter) + RefBaseT::refCounter->retain(); + return *this; + } + + Ref<T,T_BASE>& operator=(const T* p) { + #if LS_REF_VERBOSE_DEBUG_MSG + printf("Ref main ptr assignment Ref:0x%lx <- p:0x%lx\n", (long long)this, p); + #endif + if (isEquivalent(p)) { + #if LS_REF_VERBOSE_DEBUG_MSG + printf("Ref 0x%lx WRN: equivalent ptr assignment ignored.\n", (long long)this); + #endif + return *this; + } + if (RefBaseT::refCounter) { + RefBaseT::refCounter->release(); + RefBaseT::refCounter = NULL; + } + RefBaseT::refCounter = p ? new RefCounter((T*)p, 1) : NULL; + #if LS_REF_VERBOSE_DEBUG_MSG + printf("Ref main ptr assignment done\n"); + #endif + return *this; + } + + Ref<T,T_BASE>& operator=(const T_BASE* p) { + #if LS_REF_VERBOSE_DEBUG_MSG + printf("Ref base ptr assignment Ref:0x%lx <- p:0x%lx\n", (long long)this, p); + #endif + if (isEquivalent(p)) { + #if LS_REF_VERBOSE_DEBUG_MSG + printf("Ref 0x%lx WRN: equivalent ptr assignment ignored.\n", (long long)this); + #endif + return *this; + } + if (RefBaseT::refCounter) { + RefBaseT::refCounter->release(); + RefBaseT::refCounter = NULL; + } + RefBaseT::refCounter = p ? new RefCounter((T*)p, 1) : NULL; + return *this; + } + }; + +} // namespace LinuxSampler + +#endif // LS_REF_H
View file
linuxsampler-2342.tar.bz2/src/common/RingBuffer.h -> linuxsampler-2718.tar.bz2/src/common/RingBuffer.h
Changed
@@ -3,7 +3,7 @@ * LinuxSampler - modular, streaming capable sampler * * * * Copyright (C) 2003, 2004 by Benno Senoner and Christian Schoenebeck * - * Copyright (C) 2005 - 2008 Christian Schoenebeck * + * Copyright (C) 2005 - 2012 Christian Schoenebeck * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -68,24 +68,31 @@ { public: RingBuffer (int sz, int wrap_elements = DEFAULT_WRAP_ELEMENTS) : - write_ptr(0), read_ptr(0) { - int power_of_two; - - this->wrap_elements = wrap_elements; - - // the write-with-wrap functions need wrap_elements extra - // space in the buffer to be able to copy the wrap space - sz += wrap_elements; - - for (power_of_two = 1; - 1<<power_of_two < sz; - power_of_two++); - - size = 1<<power_of_two; - size_mask = size; - size_mask -= 1; - buf = new Tsize + wrap_elements; - }; + write_ptr(0), read_ptr(0) + { + _allocBuffer(sz, wrap_elements); + } + + /** + * Resize this ring buffer to the given size. This operation + * is not thread safe! Any operations using this RingBuffer + * have to be stopped before calling this method. + * + * @param sz - new size (amount of elements) + * @param wrap_elements - (optional) if supplied, the new amount + * of wrap elements to be used beyond + * official buffer end, if not provided + * the amount wrap_elements remains as it was + * before + */ + void resize(int sz, int wrap_elements = -1) { + if (wrap_elements == -1) + wrap_elements = this->wrap_elements; + + delete buf; + + _allocBuffer(sz, wrap_elements); + } virtual ~RingBuffer() { delete buf; @@ -320,6 +327,17 @@ inline void operator--(int) { --*this; } + + /** + * "Increment assign" operator, for advancing NonVolatileReader's + * read position by @a n elements. + * + * @param n - amount of elements to advance read position + */ + inline void operator+=(int n) { + if (read_space() < n) return; + read_ptr = (read_ptr+n) & pBuf->size_mask; + } /** * Returns pointer to the RingBuffer data of current @@ -430,6 +448,24 @@ * \a pSrc to the buffer given by \a pDst. */ inline static void copy(T* pDst, T* pSrc, int n); + + void _allocBuffer(int sz, int wrap_elements) { + this->wrap_elements = wrap_elements; + + // the write-with-wrap functions need wrap_elements extra + // space in the buffer to be able to copy the wrap space + sz += wrap_elements; + + int power_of_two; + for (power_of_two = 1; + 1<<power_of_two < sz; + power_of_two++); + + size = 1<<power_of_two; + size_mask = size; + size_mask -= 1; + buf = new Tsize + wrap_elements; + } friend class _NonVolatileReader<T,T_DEEP_COPY>; };
View file
linuxsampler-2342.tar.bz2/src/common/SynchronizedConfig.h -> linuxsampler-2718.tar.bz2/src/common/SynchronizedConfig.h
Changed
@@ -1,6 +1,6 @@ /*************************************************************************** * * - * Copyright (C) 2006-2009 Andreas Persson * + * Copyright (C) 2006-2014 Andreas Persson * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -24,11 +24,12 @@ #include <set> #include <unistd.h> #include "lsatomic.h" +#include "Mutex.h" namespace LinuxSampler { /** - * Thread safe management of configuration data, where the data is + * Thread-safe management of configuration data, where the data is * updated by a single non real time thread and read by a number * of real time threads. * @@ -46,6 +47,14 @@ * the data to be read, and Unlock() must be called when finished * reading the data. (Neither Lock nor Unlock will block the real * time thread, or use any system calls.) + * + * Note that the non real time side isn't safe for concurrent + * access, so if there are multiple non real time threads that + * update the configuration data, a mutex has to be used. + * + * Implementation note: the memory order parameters and fences are + * very carefully chosen to make the code fast but still safe for + * memory access reordering made by the CPU. */ template<class T> class SynchronizedConfig { @@ -65,10 +74,10 @@ * object to be read by the real time * thread */ - const T& Lock() { + /*const*/ T& Lock() { //TODO const currently commented for the DoubleBuffer usage below lock.store(lockCount += 2, memory_order_relaxed); atomic_thread_fence(memory_order_seq_cst); - return parent.configparent.indexAtomic.load( + return parent->configparent->indexAtomic.load( memory_order_acquire); } @@ -85,10 +94,11 @@ } Reader(SynchronizedConfig& config); - ~Reader(); + Reader(SynchronizedConfig* config); + virtual ~Reader(); private: friend class SynchronizedConfig; - SynchronizedConfig& parent; + SynchronizedConfig* parent; int lockCount; // increased in every Lock(), // lowest bit is always set. atomic<int> lock; // equals lockCount when inside @@ -113,6 +123,13 @@ T& GetConfigForUpdate(); /** + * Get the data on update side for read-only access. + */ + const T& GetUnsafeUpdateConfig() const { + return configupdateIndex; + } + + /** * Atomically switch the newly updated configuration * object with the one used by the real time thread, then * wait for the real time thread to finish working with @@ -180,14 +197,211 @@ template <class T> SynchronizedConfig<T>::Reader::Reader(SynchronizedConfig& config) : + parent(&config), lock(0), lockCount(1) { + parent->readers.insert(this); + } + + template <class T> + SynchronizedConfig<T>::Reader::Reader(SynchronizedConfig* config) : parent(config), lock(0), lockCount(1) { - parent.readers.insert(this); + parent->readers.insert(this); } template <class T> SynchronizedConfig<T>::Reader::~Reader() { - parent.readers.erase(this); + parent->readers.erase(this); } + + + // ----- Convenience classes on top of SynchronizedConfig ---- + + + /** + * Base interface class for classes that implement synchronization of data + * shared between multiple threads. + */ + template<class T> + class Synchronizer { + public: + /** + * Signal intention to enter a synchronized code block. Depending + * on the actual implementation, this call may block the calling + * thread until it is safe to actually use the protected data. After + * this call returns, it is safe for the calling thread to access and + * modify the shared data. As soon as the thread is done with accessing + * the shared data, it MUST call endSync(). + * + * @return the shared protected data + */ + virtual void beginSync() = 0; //TODO: or call it lock() instead ? + + /** + * Retrieve reference to critical, shared data. This method shall be + * called between a beginSync() and endSync() call pair, to be sure + * that shared data can be accessed safely. + */ + virtual T& syncedData() = 0; + + /** + * Signal that the synchronized code block has been left. Depending + * on the actual implementation, this call may block the calling + * thread for a certain amount of time. + */ + virtual void endSync() = 0; //TODO: or call it unlock() instead ? + }; + + /** + * Wraps as a kind of pointer class some data object shared with other + * threads, to protect / synchronize the shared data against + * undeterministic concurrent access. It does so by locking the shared + * data in the Sync constructor and unlocking the shared data in the Sync + * destructor. Accordingly it can always be considered safe to access the + * shared data during the whole life time of the Sync object. Due to + * this design, a Sync object MUST only be accessed and destroyed + * by exactly one and the same thread which created that same Sync object. + */ + template<class T> + class Sync { + public: + Sync(Synchronizer<T>* syncer) { + this->syncer = syncer; + syncer->beginSync(); + } + + virtual ~Sync() { + syncer->endSync(); + } + + /*Sync& operator =(const Sync& arg) { + *this->data = *arg.data; + return *this; + }*/ + + /*Sync& operator =(const T& arg) { + *this->data = arg; + return *this; + }*/ + + const T& operator *() const { return syncer->syncedData(); } + T& operator *() { return syncer->syncedData(); } + + const T* operator ->() const { return &syncer->syncedData(); } + T* operator ->() { return &syncer->syncedData(); } + + private: + Synchronizer<T>* syncer; ///< Points to the object that shall be responsible to protect the shared data. + }; + + /** + * BackBuffer object to be accessed by multiple non-real-time threads. + * + * Since a back buffer is designed for being accessed by non-real-time + * threads, its methods involved may block the calling thread for a long + * amount of time. + */ + template<class T> + class BackBuffer : public SynchronizedConfig<T>, public Synchronizer<T> { + public: + virtual void beginSync() OVERRIDE { + mutex.Lock(); + } + + virtual T& syncedData() OVERRIDE { + return SynchronizedConfig<T>::GetConfigForUpdate(); + } + + virtual void endSync() OVERRIDE { + const T clone = SynchronizedConfig<T>::GetConfigForUpdate(); + SynchronizedConfig<T>::SwitchConfig() = clone; + mutex.Unlock(); + } + + const T& unsafeData() const { + return SynchronizedConfig<T>::GetUnsafeUpdateConfig(); + } + + private: + Mutex mutex; + }; + + /** + * FrontBuffer object to be accessed by exactly ONE real-time thread. + * A front buffer is designed for real-time access. That is, its methods + * involved are lock free, that is none of them block the calling thread + * for a long time. + * + * If you need the front buffer's data to be accessed by multiple real-time + * threads instead, then you need to create multiple instances of the + * FrontBuffer object. They would point to the same data, but ensure + * protection against concurrent access among those real-time threads. + */ + template<class T> + class FrontBuffer : public SynchronizedConfig<T>::Reader, public Synchronizer<T> { + public: + FrontBuffer(BackBuffer<T>& backBuffer) : SynchronizedConfig<T>::Reader::Reader(&backBuffer) {} + virtual void beginSync() OVERRIDE { data = &SynchronizedConfig<T>::Reader::Lock(); } + virtual T& syncedData() OVERRIDE { return *data; } + virtual void endSync() OVERRIDE { SynchronizedConfig<T>::Reader::Unlock(); } + private: + T* data; + }; + + /** + * Synchronization / protection of data shared between multiple threads by + * using a double buffer design. The FrontBuffer is meant to be accessed by + * exactly one real-time thread, whereas the BackBuffer is meant to be + * accessed by multiple non-real-time threads. + * + * This class is built on top of SynchronizedConfig as convenient API to + * reduce the amount of code required to protect shared data. + */ + template<class T> + class DoubleBuffer { + public: + DoubleBuffer() : m_front(m_back) {} + + /** + * Synchronized access of the shared data for EXACTLY one real-time + * thread. + * + * The returned shared data is wrapped into a Sync object, which + * ensures that the shared data is protected against concurrent access + * during the life time of the returned Sync object. + */ + inline + Sync<T> front() { return Sync<T>(&m_front); } + + /** + * Synchronized access of the shared data for multiple non-real-time + * threads. + * + * The returned shared data is wrapped into a Sync object, which + * ensures that the shared data is protected against concurrent access + * during the life time of the returned Sync object. + * + * As soon as the returned Sync object is destroyed, the FrontBuffer + * will automatically be exchanged by the hereby modified BackBuffer. + */ + inline + Sync<T> back() { return Sync<T>(&m_back); } + + /** + * Get the backbuffer data <b>unprotected</b>, that is <b>without</b> + * locking or any means of synchronizations. + * + * Due to its nature this must only be called for read access and + * you have to make sure by yourself, that the data/member you + * access is really safe for concurrent read access (i.e. SGI's + * implementation of std::vector::size() would be safe). + * + * Only use this when you are absolutely sure what you are doing! + */ + const T& unsafeBack() const { return m_back.unsafeData(); } + + private: + BackBuffer<T> m_back; ///< Back buffer (non real-time thread(s) side). + FrontBuffer<T> m_front; ///< Front buffer (real-time thread side). + }; } // namespace LinuxSampler
View file
linuxsampler-2342.tar.bz2/src/common/WorkerThread.cpp -> linuxsampler-2718.tar.bz2/src/common/WorkerThread.cpp
Changed
@@ -1,6 +1,6 @@ /*************************************************************************** * * - * Copyright (C) 2007 Christian Schoenebeck, Grigor Iliev * + * Copyright (C) 2007 - 2013 Christian Schoenebeck, Grigor Iliev * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -27,10 +27,10 @@ WorkerThread::WorkerThread() : Thread(true, false, 0, -4) { } void WorkerThread::Execute(Runnable* pJob) { - mutex.Lock(); - queue.push_back(pJob); - mutex.Unlock(); - + { + LockGuard lock(mutex); + queue.push_back(pJob); + } StartThread(); // ensure thread is running conditionJobsLeft.Set(true); // wake up thread } @@ -39,16 +39,20 @@ int WorkerThread::Main() { while (true) { - #if CONFIG_PTHREAD_TESTCANCEL - TestCancel(); - #endif - while (!queue.empty()) { + #if CONFIG_PTHREAD_TESTCANCEL + TestCancel(); + #endif + while (true) { Runnable* pJob; // grab a new job from the queue - mutex.Lock(); - pJob = queue.front(); - mutex.Unlock(); + { + LockGuard lock(mutex); + if (queue.empty()) break; + + pJob = queue.front(); + queue.pop_front(); + } try { pJob->Run(); @@ -59,11 +63,6 @@ std::cerr << std::endl << std::flush; } - // remove processed job from the queue - mutex.Lock(); - queue.pop_front(); - mutex.Unlock(); - delete pJob; }
View file
linuxsampler-2342.tar.bz2/src/common/WorkerThread.h -> linuxsampler-2718.tar.bz2/src/common/WorkerThread.h
Changed
@@ -44,7 +44,7 @@ /** * The entry point of the thread. */ - virtual int Main(); + virtual int Main() OVERRIDE; private: std::list<Runnable*> queue; // the queue, which holds the jobs to be executed
View file
linuxsampler-2342.tar.bz2/src/common/global.h -> linuxsampler-2718.tar.bz2/src/common/global.h
Changed
@@ -3,7 +3,7 @@ * LinuxSampler - modular, streaming capable sampler * * * * Copyright (C) 2003, 2004 by Benno Senoner and Christian Schoenebeck * - * Copyright (C) 2005 - 2010 Christian Schoenebeck * + * Copyright (C) 2005 - 2013 Christian Schoenebeck * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -63,4 +63,16 @@ #define DEPRECATED_API #endif +// whether compiler is C++11 standard compliant +#if defined(__cplusplus) && __cplusplus >= 201103L +# define IS_CPP11 1 +#endif + +// C++ "override" keyword introduced with C++11 standard +#if IS_CPP11 +# define OVERRIDE override +#else +# define OVERRIDE +#endif + #endif // __LS_GLOBAL_H__
View file
linuxsampler-2342.tar.bz2/src/common/global_private.cpp -> linuxsampler-2718.tar.bz2/src/common/global_private.cpp
Changed
@@ -3,7 +3,7 @@ * LinuxSampler - modular, streaming capable sampler * * * * Copyright (C) 2003, 2004 by Benno Senoner and Christian Schoenebeck * - * Copyright (C) 2005 - 2008 Christian Schoenebeck * + * Copyright (C) 2005 - 2013 Christian Schoenebeck * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -98,6 +98,14 @@ // this is the sampler global setting for maximum disk streams int GLOBAL_MAX_STREAMS = CONFIG_DEFAULT_MAX_STREAMS; +//TODO: (hopefully) just a temporary nasty hack for launching gigedit on the main thread on Mac (see comments in gigedit.cpp for details) +#if defined(__APPLE__) +bool g_mainThreadCallbackSupported = false; +void (*g_mainThreadCallback)(void* info) = 0; +void* g_mainThreadCallbackInfo = 0; +bool g_fireMainThreadCallback = false; +#endif + int hexToNumber(char hex_digit) { switch (hex_digit) { case '0': return 0;
View file
linuxsampler-2342.tar.bz2/src/common/global_private.h -> linuxsampler-2718.tar.bz2/src/common/global_private.h
Changed
@@ -3,7 +3,7 @@ * LinuxSampler - modular, streaming capable sampler * * * * Copyright (C) 2003, 2004 by Benno Senoner and Christian Schoenebeck * - * Copyright (C) 2005 - 2009 Christian Schoenebeck * + * Copyright (C) 2005 - 2013 Christian Schoenebeck * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -57,6 +57,13 @@ typedef float v4sf __attribute__ ((vector_size(16))); #endif +// circumvents a bug in GCC 4.x which causes a sizeof() expression applied +// on a class member to throw a compiler error, i.e. with GCC 4.4: +// "object missing in reference to 'LinuxSampler::AbstractEngineChannel::ControllerTable'") +// or with GCC 4.0: +// "invalid use of non-static data member 'LinuxSampler::AbstractEngineChannel::ControllerTable'" +#define _MEMBER_SIZEOF(T_Class, Member) sizeof(((T_Class*)NULL)->Member) + /** * Whether a function / method call was successful, or if warnings or even an * error occured. @@ -107,6 +114,14 @@ extern int GLOBAL_MAX_VOICES; extern int GLOBAL_MAX_STREAMS; +//TODO: (hopefully) just a temporary nasty hack for launching gigedit on the main thread on Mac (see comments in gigedit.cpp for details) +#if defined(__APPLE__) +extern bool g_mainThreadCallbackSupported; +extern void (*g_mainThreadCallback)(void* info); +extern void* g_mainThreadCallbackInfo; +extern bool g_fireMainThreadCallback; +#endif + // I read with some Linux kernel versions (between 2.4.18 and 2.4.21) // sscanf() might be buggy regarding parsing of hex characters, so ... int hexToNumber(char hex_digit);
View file
linuxsampler-2342.tar.bz2/src/common/lsatomic.h -> linuxsampler-2718.tar.bz2/src/common/lsatomic.h
Changed
@@ -1,6 +1,6 @@ /*************************************************************************** * * - * Copyright (C) 2008-2009 Andreas Persson * + * Copyright (C) 2008-2013 Andreas Persson * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -21,19 +21,83 @@ #ifndef LSATOMIC_H #define LSATOMIC_H -/* - * Implementation of a small subset of the C++0x atomic operations - * (cstdatomic). +/** @file * - * The supported operations are: + * Implementation of a small subset of the C++11 atomic operations. + * + * Note: When working with multithreading on modern CPUs, it's + * important not only to make sure that concurrent access to shared + * variables is made atomically, but also to be aware of the order the + * stores get visible to the loads in other threads. For example, if x + * and y are shared variables with initial values of 0, the following + * program: + * + * @code + * // thread 1: + * x.store(1, memory_order_relaxed); + * r1 = y.load(memory_order_relaxed); + * + * // thread 2: + * y.store(1, memory_order_relaxed); + * r2 = x.load(memory_order_relaxed); + * @endcode + * + * would have a possible outcome of r1 == 0 and r2 == 0. The threads + * might for example run on separate CPU cores with separate caches, + * and the propagation of the store to the other core might be delayed + * and done after the loads. In that case, both loads will read the + * original value of 0 from the core's own cache. + * + * The C++11 style operations use the memory_order parameter to let + * the programmer control the way shared memory stores get visible to + * loads in other threads. In the example above, relaxed order was + * used, which allows the CPU and compiler to reorder the memory + * accesses very freely. If memory_order_seq_cst had been used + * instead, the r1 == 0 and r2 == 0 outcome would have been + * impossible, as sequential consistency means that the execution of + * the program can be modeled by simply interleaving the instructions + * of the threads. + * + * The default order is memory_order_seq_cst, as it is the easiest one + * to understand. It is however also the slowest. The relaxed order is + * the fastest, but it can't be used if the shared variable is used to + * synchronize threads for any other shared data. The third order is + * acquire/release, where an acquire-load is synchronizing with a + * release-store to the same variable. + * + * See for example http://gcc.gnu.org/wiki/Atomic/GCCMM/AtomicSync for + * more information about the memory order parameter. + * + * The supported operations of the implementation in this file are: * * - fences (acquire, release and seq_cst) * * - load and store of atomic<int> with relaxed, acquire/release or * seq_cst memory ordering * - * The supported architectures are x86 and powerpc. + * The supported architectures are x86, powerpc and ARMv7. */ + + +// if C++11 and gcc 4.7 or later is used, then use the standard +// implementation +#if __cplusplus >= 201103L && \ + (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7)) + +#include <atomic> + +namespace LinuxSampler { + using std::memory_order_relaxed; + using std::memory_order_acquire; + using std::memory_order_release; + using std::memory_order_seq_cst; + using std::atomic_thread_fence; + using std::atomic; +} + +#else + + namespace LinuxSampler { enum memory_order { memory_order_relaxed, memory_order_acquire, @@ -51,6 +115,8 @@ asm volatile("lwsync" : : : "memory"); #elif defined(_ARCH_PPC) asm volatile("sync" : : : "memory"); +#elif defined(__ARM_ARCH_7A__) + asm volatile("dmb" : : : "memory"); #else asm volatile("" : : : "memory"); #endif @@ -63,6 +129,8 @@ asm volatile("lock; addl $0,0(%%esp)" : : : "memory"); #elif defined(__x86_64__) asm volatile("mfence" : : : "memory"); +#elif defined(__ARM_ARCH_7A__) + asm volatile("dmb" : : : "memory"); #else asm volatile("" : : : "memory"); #endif @@ -84,7 +152,9 @@ case memory_order_seq_cst: case memory_order_release: // (invalid) +#ifdef _ARCH_PPC atomic_thread_fence(memory_order_seq_cst); +#endif // fall-through case memory_order_acquire: @@ -100,7 +170,7 @@ : "memory", "cr0"); #else m = f; - asm volatile("" : : : "memory"); + atomic_thread_fence(memory_order_acquire); #endif break; } @@ -120,9 +190,14 @@ case memory_order_seq_cst: case memory_order_acquire: // (invalid) +#ifdef _ARCH_PPC + atomic_thread_fence(memory_order_seq_cst); + f = m; +#else atomic_thread_fence(memory_order_release); f = m; atomic_thread_fence(memory_order_seq_cst); +#endif break; } } @@ -133,3 +208,4 @@ }; } #endif +#endif
View file
linuxsampler-2342.tar.bz2/src/db/InstrumentsDb.cpp -> linuxsampler-2718.tar.bz2/src/db/InstrumentsDb.cpp
Changed
@@ -1,6 +1,6 @@ /*************************************************************************** * * - * Copyright (C) 2007-2009 Grigor Iliev, Benno Senoner * + * Copyright (C) 2007-2013 Grigor Iliev, Benno Senoner * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -111,13 +111,11 @@ } void InstrumentsDb::SetDbFile(String File) { - DbInstrumentsMutex.Lock(); + LockGuard lock(DbInstrumentsMutex); if (File.empty() || DbFile.length() > 0) { - DbInstrumentsMutex.Unlock(); throw Exception("Failed to set the database file"); } DbFile = File; - DbInstrumentsMutex.Unlock(); } sqlite3* InstrumentsDb::GetDb() { @@ -464,17 +462,10 @@ bool InstrumentsDb::DirectoryExist(String Dir) { dmsg(2,("InstrumentsDb: DirectoryExist(Dir=%s)\n", Dir.c_str())); - bool b; - - DbInstrumentsMutex.Lock(); - try { b = GetDirectoryId(Dir) != -1; } - catch (Exception e) { - DbInstrumentsMutex.Unlock(); - throw e; + { + LockGuard lock(DbInstrumentsMutex); + return GetDirectoryId(Dir) != -1; } - DbInstrumentsMutex.Unlock(); - - return b; } DbDirectory InstrumentsDb::GetDirectoryInfo(String Dir) { @@ -714,8 +705,9 @@ dmsg(2,("InstrumentsDb: AddInstruments(DbDir=%s,insDir=%d,FilePath=%s,Index=%d)\n", DbDir.c_str(), insDir, FilePath.c_str(), Index)); if (DbDir.empty() || FilePath.empty()) return; - DbInstrumentsMutex.Lock(); - try { + { + LockGuard lock(DbInstrumentsMutex); + int dirId = GetDirectoryId(DbDir); if (dirId == -1) throw Exception("Invalid DB directory: " + toEscapedText(DbDir)); @@ -734,20 +726,16 @@ String dir = insDir ? PrepareSubdirectory(DbDir, FilePath) : DbDir; AddInstrumentsFromFile(dir, FilePath, Index, pProgress); - } catch (Exception e) { - DbInstrumentsMutex.Unlock(); - throw e; } - - DbInstrumentsMutex.Unlock(); } void InstrumentsDb::AddInstrumentsNonrecursive(String DbDir, String FsDir, bool insDir, ScanProgress* pProgress) { dmsg(2,("InstrumentsDb: AddInstrumentsNonrecursive(DbDir=%s,FsDir=%s,insDir=%d)\n", DbDir.c_str(), FsDir.c_str(), insDir)); if (DbDir.empty() || FsDir.empty()) return; - DbInstrumentsMutex.Lock(); - try { + { + LockGuard lock(DbInstrumentsMutex); + int dirId = GetDirectoryId(DbDir); if (dirId == -1) throw Exception("Invalid DB directory: " + toEscapedPath(DbDir)); @@ -774,15 +762,8 @@ } } catch(Exception e) { e.PrintMessage(); - DbInstrumentsMutex.Unlock(); - return; } - } catch (Exception e) { - DbInstrumentsMutex.Unlock(); - throw e; } - - DbInstrumentsMutex.Unlock(); } void InstrumentsDb::AddInstrumentsRecursive(String DbDir, String FsDir, bool Flat, bool insDir, ScanProgress* pProgress) { @@ -1466,7 +1447,7 @@ } InTransaction = false; - if(db == NULL) { + if (db == NULL) { DbInstrumentsMutex.Unlock(); return; } @@ -1741,29 +1722,25 @@ } void InstrumentsDb::Format() { - DbInstrumentsMutex.Lock(); - if (db != NULL) { - sqlite3_close(db); - db = NULL; - } + { + LockGuard lock(DbInstrumentsMutex); - if (DbFile.empty()) DbFile = CONFIG_DEFAULT_INSTRUMENTS_DB_LOCATION; - String bkp = DbFile + ".bkp"; - remove(bkp.c_str()); - if (rename(DbFile.c_str(), bkp.c_str()) && errno != ENOENT) { - DbInstrumentsMutex.Unlock(); - throw Exception(String("Failed to backup database: ") + strerror(errno)); - } + if (db != NULL) { + sqlite3_close(db); + db = NULL; + } + + if (DbFile.empty()) DbFile = CONFIG_DEFAULT_INSTRUMENTS_DB_LOCATION; + String bkp = DbFile + ".bkp"; + remove(bkp.c_str()); + if (rename(DbFile.c_str(), bkp.c_str()) && errno != ENOENT) { + throw Exception(String("Failed to backup database: ") + strerror(errno)); + } - String f = DbFile; - DbFile = ""; - try { CreateInstrumentsDb(f); } - catch(Exception e) { - DbInstrumentsMutex.Unlock(); - throw e; + String f = DbFile; + DbFile = ""; + CreateInstrumentsDb(f); } - DbInstrumentsMutex.Unlock(); - FireDirectoryCountChanged("/"); FireInstrumentCountChanged("/"); }
View file
linuxsampler-2342.tar.bz2/src/db/InstrumentsDbUtilities.h -> linuxsampler-2718.tar.bz2/src/db/InstrumentsDbUtilities.h
Changed
@@ -277,7 +277,7 @@ String CurrentFile; int JobId; - struct ::gig::progress_t GigFileProgress; + ::gig::progress_t GigFileProgress; private: int TotalFileCount;
View file
linuxsampler-2342.tar.bz2/src/db/Makefile.am -> linuxsampler-2718.tar.bz2/src/db/Makefile.am
Changed
@@ -1,5 +1,5 @@ if HAVE_SQLITE3 -INCLUDES = $(all_includes) $(GIG_CFLAGS) +AM_CPPFLAGS = $(all_includes) $(GIG_CFLAGS) AM_CXXFLAGS = -Wreturn-type -ffast-math $(CXX_CPU_SWITCH) METASOURCES = AUTO @@ -23,4 +23,4 @@ liblinuxsamplerdb_la_LIBADD = $(sqlite3_lflags) liblinuxsamplerdb_la_CFLAGS = $(sqlite3_cflags) liblinuxsamplerdb_la_CXXFLAGS = $(sqlite3_cflags) -endif \ No newline at end of file +endif
View file
linuxsampler-2342.tar.bz2/src/drivers/DeviceParameter.h -> linuxsampler-2718.tar.bz2/src/drivers/DeviceParameter.h
Changed
@@ -3,7 +3,7 @@ * LinuxSampler - modular, streaming capable sampler * * * * Copyright (C) 2003, 2004 by Benno Senoner and Christian Schoenebeck * - * Copyright (C) 2005, 2006 Christian Schoenebeck * + * Copyright (C) 2005 - 2013 Christian Schoenebeck * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -33,161 +33,750 @@ #include "Device.h" namespace LinuxSampler { + + ///////////////////////////////////////////////////////////////// + // "Runtime" parameters // TODO: All plurar parameter classes (except for String) have to be added (namely DeviceRuntimeParameterBools, DeviceRuntimeParameterInts, DeviceRuntimeParameterFloats, DeviceCreationParameterBools, DeviceCreationParameterInts, DeviceCreationParameterFloats), I ignored them for the moment, because they were not that necessary. + /** @brief Abstracet base class for all driver parameters of the sampler. + * + * All audio / MIDI drivers for the sampler are using dynamic driver + * parameters based on this base class. The main purpose behind this + * concept is to be able to write GUI frontends for the sampler, which + * don't need to know about specific drivers the sampler provides, nor the + * exact list of parameters those drivers offer. Instead a frontend can + * use this API to retrieve what kind of parameters the respective + * available drivers offer at runtime. This way new drivers can be added + * or existing ones being changed arbitrarily to the sampler at any time, + * without a GUI frontend to be changed or recompiled. + * + * There are various parameter classes deriving from this base class, which + * implement convenient handling for the various common value types like + * bool, int, float, String. A driver would rather use one of those + * type specialized classes, instead of this abstract base class. Because + * the methods of this very base parameter class here are generalized being + * encoded in String type for all parameter types. + * + * Besides that, there are 2 distinct sets of parameter types: + * - "Runtime" parameters which can be set and changed by the user (i.e. + * with a GUI frontend) at any time. + * - "Creation" parameters which can only be set by the user before a + * driver is instantiated (a driver instance is called a "device" in + * the sampler's terms). After the driver is instantiated, "creation" + * parameters are read only for the life time of a driver (device) + * instance. Typical example for a "creation" parameter would be an + * audio driver that allows to select a specific sound card. + * + * @see DeviceCreationParameter + */ class DeviceRuntimeParameter { public: - virtual String Type() = 0; - virtual String Description() = 0; - virtual bool Fix() = 0; - virtual bool Multiplicity() = 0; - virtual optional<String> RangeMin() = 0; - virtual optional<String> RangeMax() = 0; + /** + * Some name reflecting the parameter's value type, like "BOOL, + * "INT", "FLOAT", "STRING", "STRINGS". Upon the value returned + * here, the object can be casted to the respective implementing + * parameter class. + */ + virtual String Type() = 0; + + /** + * A human readable description, explaining the exact purpose of + * the driver parameter. The text returned here can be used to + * display the user in a GUI frontend some helping text, that + * explains what the parameter actually does. + */ + virtual String Description() = 0; + + /** + * Whether the parameter is read only. Not to be confused with + * "creation" parameters! A driver parameter which returns true + * here can never be set or altered at any time. Not even at + * instanciation time of the driver! A typical example would be + * a parameter "SAMPLERATE" for a specific sound card, where the + * specific sound card does not allow to switch the sound card's + * sample rate in any way. Yet the value returned by the parameter + * (read only) might be different, depending on the actual sound + * card the user selected with the audio driver. + */ + virtual bool Fix() = 0; + + /** + * Whether the parameter only allows to set one scalar value, or + * if @c true is returned here, the parameter allows to manage a + * list of values instead. A typical example of multiplicity + * parameter is i.e. a "ROUTING" parameter, that would allow a + * user to interconnect the sampler with other apps and devices + * with drivers that support such concepts (like JACK and ALSA MIDI + * do). + */ + virtual bool Multiplicity() = 0; + + /** + * The driver parameter might (optionally) return a minimum value + * for the parameter. If some actual value is returned here, the + * sampler automatically performs bounds checking of parameter + * values to be set for such a parameter and a GUI frontend might + * display a spin box in such a case to the user, honoring the + * returned minimum value. + * + * You probably don't want to call this method directly, but instead + * cast this object to the respective deriving parameter class like + * DeviceRuntimeParameterInt, and use its RangeMinAsInt() method + * instead, which conveniently returns a value in its value type. + * So you don't need to parse this return value here. + */ + virtual optional<String> RangeMin() = 0; + + /** + * The driver parameter might (optionally) return a maximum value + * for the parameter. If some actual value is returned here, the + * sampler automatically performs bounds checking of parameter + * values to be set for such a parameter and a GUI frontend might + * display a spin box in such a case to the user, honoring the + * returned maximum value. + * + * You probably don't want to call this method directly, but instead + * cast this object to the respective deriving parameter class like + * DeviceRuntimeParameterInt, and use its RangeMaxAsInt() method + * instead, which conveniently returns a value in its value type. + * So you don't need to parse this return value here. + */ + virtual optional<String> RangeMax() = 0; + + /** + * The driver parameter might (optionally) return a list of + * possible values for this parameter, encoded as comma separated + * list. For example an audio driver might return "44100,96000" for + * a "SAMPLERATE" parameter for a specific sound card. + * + * You probably don't want to call this method directly, but instead + * cast this object to the respective deriving parameter class like + * DeviceRuntimeParameterInt, and use its PossibilitiesAsInt() method + * instead, which conveniently returns a vector in its value type. + * So you don't need to parse this return value here. + */ virtual optional<String> Possibilities() = 0; - virtual String Value() = 0; - virtual void SetValue(String val) throw (Exception) = 0; + + /** + * The current value of this parameter (encoded as String). + * You might want to cast to the respective deriving parameter + * class like DeviceRuntimeParameterInt and use its method + * ValueAsInt() for not being forced to parse the String here. + */ + virtual String Value() = 0; + + /** + * Alter the parameter with the value given by @a val. The + * respective deriving parameter class automatically parses the + * String value supplied here, and converts it into its native + * value type like int, float or String vector ("Strings"). + * + * @param - new parameter value encoded as string + * @throws Exception - if @a val is out of bounds, not encoded + * correctly in its string representation or + * any other reason the driver might not want + * to accept the given value. + */ + virtual void SetValue(String val) throw (Exception) = 0; + + /** @brief Destructor. + * + * Virtual base destructor which enforces that all destructors of + * all deriving classes are called automatically upon object + * destruction. + */ virtual ~DeviceRuntimeParameter(){}; }; + /** @brief Abstract base class for driver parameters of type @c bool. + * + * Implements a "runtime" driver parameter for value type @c bool. + * A driver offering a parameter of type @c bool would derive its + * parameter class from this class and implement the abstract method + * OnSetValue() to react on a parameter value being set. + * + * See DeviceCreationParameter and DeviceRuntimeParameter for a discussion + * about "runtime" parameters vs. "creation" parameters. + * + * @see DeviceCreationParameterBool + */ class DeviceRuntimeParameterBool : public DeviceRuntimeParameter { public: + /** @brief Constructor for value type @c bool. + * + * Sets an initial value for the parameter. + * + * @param bVal - initial boolean value + */ DeviceRuntimeParameterBool(bool bVal); - virtual String Type(); - virtual bool Multiplicity(); - virtual optional<String> RangeMin(); - virtual optional<String> RangeMax(); - virtual optional<String> Possibilities(); - virtual String Value(); - virtual void SetValue(String val) throw (Exception); + + ///////////////////////////////////////////////////////////////// + // derived methods, implementing type "BOOL" + // (usually not to be overriden by descendant) + + virtual String Type() OVERRIDE; + virtual bool Multiplicity() OVERRIDE; + virtual optional<String> RangeMin() OVERRIDE; + virtual optional<String> RangeMax() OVERRIDE; + virtual optional<String> Possibilities() OVERRIDE; + virtual String Value() OVERRIDE; + virtual void SetValue(String val) throw (Exception) OVERRIDE; + + ///////////////////////////////////////////////////////////////// + // convenience methods for type "BOOL" + // (usually not to be overriden by descendant) virtual bool ValueAsBool(); virtual void SetValue(bool b) throw (Exception); + ///////////////////////////////////////////////////////////////// + // abstract methods + // (these have to be implemented by the descendant) + + /** + * Must be implemented be a driver's parameter class to react on + * the parameter value being set / altered. + * + * @param b - new parameter value set by the user + * @throws Exception - might be thrown by the driver in case it + * cannot handle the supplied parameter value + * for whatever reason + */ virtual void OnSetValue(bool b) throw (Exception) = 0; protected: bool bVal; }; + /** @brief Abstract base class for driver parameters of type @c int. + * + * Implements a "runtime" driver parameter for value type @c int. + * A driver offering a parameter of type @c int would derive its + * parameter class from this class and implement the abstract methods + * OnSetValue(), RangeMinAsInt(), RangeMaxAsInt(), PossibilitiesAsInt(). + * + * See DeviceCreationParameter and DeviceRuntimeParameter for a discussion + * about "runtime" parameters vs. "creation" parameters. + * + * @see DeviceCreationParameterInt + */ class DeviceRuntimeParameterInt : public DeviceRuntimeParameter { public: + /** @brief Constructor for value type @c int. + * + * Sets an initial value for the parameter. + * + * @param iVal - initial integer value + */ DeviceRuntimeParameterInt(int iVal); - virtual String Type(); - virtual bool Multiplicity(); - virtual optional<String> RangeMin(); - virtual optional<String> RangeMax(); - virtual optional<String> Possibilities(); - virtual String Value(); - virtual void SetValue(String val) throw (Exception); + + ///////////////////////////////////////////////////////////////// + // derived methods, implementing type "INT" + // (usually not to be overriden by descendant) + + virtual String Type() OVERRIDE; + virtual bool Multiplicity() OVERRIDE; + virtual optional<String> RangeMin() OVERRIDE; + virtual optional<String> RangeMax() OVERRIDE; + virtual optional<String> Possibilities() OVERRIDE; + virtual String Value() OVERRIDE; + virtual void SetValue(String val) throw (Exception) OVERRIDE; + + ///////////////////////////////////////////////////////////////// + // convenience methods for type "INT" + // (usually not to be overriden by descendant) virtual int ValueAsInt(); virtual void SetValue(int i) throw (Exception); - - virtual optional<int> RangeMinAsInt() = 0; - virtual optional<int> RangeMaxAsInt() = 0; + + ///////////////////////////////////////////////////////////////// + // abstract methods + // (these have to be implemented by the descendant) + + /** + * Must be implemented by descendant, returning a minimum int value + * for the parameter. If a minimum value does not make sense for + * the specific driver parameter, then the implementation should + * return @c optional<int>::nothing. + */ + virtual optional<int> RangeMinAsInt() = 0; + + /** + * Must be implemented by descendant, returning a maximum int value + * for the parameter. If a maximum value does not make sense for + * the specific driver parameter, then the implementation should + * return @c optional<int>::nothing. + */ + virtual optional<int> RangeMaxAsInt() = 0; + + /** + * Must be implemented by descendant, returning a list of possible + * int values for the parameter. If a list of possible values does + * not make sense, the implementation should return an empty + * vector. + */ virtual std::vector<int> PossibilitiesAsInt() = 0; - virtual void OnSetValue(int i) throw (Exception) = 0; + + /** + * Must be implemented be a driver's parameter class to react on + * the parameter value being set / altered. + * + * @param i - new parameter value set by the user + * @throws Exception - might be thrown by the driver in case it + * cannot handle the supplied parameter value + * for whatever reason + */ + virtual void OnSetValue(int i) throw (Exception) = 0; protected: int iVal; }; + /** @brief Abstract base class for driver parameters of type @c float. + * + * Implements a "runtime" driver parameter for value type @c float. + * A driver offering a parameter of type @c float would derive its + * parameter class from this class and implement the abstract methods + * OnSetValue(), RangeMinAsFloat(), RangeMaxAsFloat() and + * PossibilitiesAsFloat(). + * + * See DeviceCreationParameter and DeviceRuntimeParameter for a discussion + * about "runtime" parameters vs. "creation" parameters. + * + * @see DeviceCreationParameterFloat + */ class DeviceRuntimeParameterFloat : public DeviceRuntimeParameter { public: + /** @brief Constructor for value type @c float. + * + * Sets an initial value for the parameter. + * + * @param fVal - initial float value + */ DeviceRuntimeParameterFloat(float fVal); - virtual String Type(); - virtual bool Multiplicity(); - virtual optional<String> RangeMin(); - virtual optional<String> RangeMax(); - virtual optional<String> Possibilities(); - virtual String Value(); - virtual void SetValue(String val) throw (Exception); - + + ///////////////////////////////////////////////////////////////// + // derived methods, implementing type "FLOAT" + // (usually not to be overriden by descendant) + + virtual String Type() OVERRIDE; + virtual bool Multiplicity() OVERRIDE; + virtual optional<String> RangeMin() OVERRIDE; + virtual optional<String> RangeMax() OVERRIDE; + virtual optional<String> Possibilities() OVERRIDE; + virtual String Value() OVERRIDE; + virtual void SetValue(String val) throw (Exception) OVERRIDE; + + ///////////////////////////////////////////////////////////////// + // convenience methods for type "FLOAT" + // (usually not to be overriden by descendant) + virtual float ValueAsFloat(); virtual void SetValue(float f) throw (Exception); - - virtual optional<float> RangeMinAsFloat() = 0; - virtual optional<float> RangeMaxAsFloat() = 0; - virtual std::vector<float> PossibilitiesAsFloat() = 0; - virtual void OnSetValue(float f) = 0; + + ///////////////////////////////////////////////////////////////// + // abstract methods + // (these have to be implemented by the descendant) + + /** + * Must be implemented by descendant, returning a minimum float + * value for the parameter. If a minimum value does not make sense + * for the specific driver parameter, then the implementation + * should return @c optional<float>::nothing. + */ + virtual optional<float> RangeMinAsFloat() = 0; + + /** + * Must be implemented by descendant, returning a maximum float + * value for the parameter. If a maximum value does not make sense + * for the specific driver parameter, then the implementation + * should return @c optional<float>::nothing. + */ + virtual optional<float> RangeMaxAsFloat() = 0; + + /** + * Must be implemented by descendant, returning a list of possible + * float values for the parameter. If a list of possible values + * does not make sense, the implementation should return an empty + * vector. + */ + virtual std::vector<float> PossibilitiesAsFloat() = 0; + + /** + * Must be implemented be a driver's parameter class to react on + * the parameter value being set / altered. + * + * @param f - new parameter value set by the user + * @throws Exception - might be thrown by the driver in case it + * cannot handle the supplied parameter value + * for whatever reason + */ + virtual void OnSetValue(float f) = 0; protected: float fVal; }; + /** @brief Abstract base class for driver parameters of type @c String. + * + * Implements a "runtime" driver parameter for value type @c String. + * A driver offering a parameter of type @c String would derive its + * parameter class from this class and implement the abstract methods + * OnSetValue() and PossibilitiesAsString(). + * + * See DeviceCreationParameter and DeviceRuntimeParameter for a discussion + * about "runtime" parameters vs. "creation" parameters. + * + * @see DeviceCreationParameterString + */ class DeviceRuntimeParameterString : public DeviceRuntimeParameter { public: + /** @brief Constructor for value type @c String. + * + * Sets an initial value for the parameter. + * + * @param sVal - initial String value + */ DeviceRuntimeParameterString(String sVal); + + /// @brief Destructor. virtual ~DeviceRuntimeParameterString(){} - virtual String Type(); - virtual bool Multiplicity(); - virtual optional<String> RangeMin(); - virtual optional<String> RangeMax(); - virtual optional<String> Possibilities(); - virtual String Value(); - virtual void SetValue(String val) throw (Exception); + + ///////////////////////////////////////////////////////////////// + // derived methods, implementing type "STRING" + // (usually not to be overriden by descendant) + + virtual String Type() OVERRIDE; + virtual bool Multiplicity() OVERRIDE; + virtual optional<String> RangeMin() OVERRIDE; + virtual optional<String> RangeMax() OVERRIDE; + virtual optional<String> Possibilities() OVERRIDE; + virtual String Value() OVERRIDE; + virtual void SetValue(String val) throw (Exception) OVERRIDE; + + ///////////////////////////////////////////////////////////////// + // convenience methods for type "STRING" + // (usually not to be overriden by descendant) virtual String ValueAsString(); virtual void SetValueAsString(String s) throw (Exception); - + + ///////////////////////////////////////////////////////////////// + // abstract methods + // (these have to be implemented by the descendant) + + /** + * Must be implemented by descendant, returning a list of possible + * String values for the parameter. If a list of possible values + * does not make sense, the implementation should return an empty + * vector. + */ virtual std::vector<String> PossibilitiesAsString() = 0; - virtual void OnSetValue(String s) = 0; + + /** + * Must be implemented be a driver's parameter class to react on + * the parameter value being set / altered. + * + * @param s - new parameter value set by the user + * @throws Exception - might be thrown by the driver in case it + * cannot handle the supplied parameter value + * for whatever reason + */ + virtual void OnSetValue(String s) = 0; protected: String sVal; }; + /** @brief Abstract base class for driver parameters of a @c String list type. + * + * Implements a "runtime" driver parameter for multiple values of type + * @c String. A driver offering a parameter of a @c String list type would + * derive its parameter class from this class and implement the abstract + * methods OnSetValue() and PossibilitiesAsString(). + * + * See DeviceCreationParameter and DeviceRuntimeParameter for a discussion + * about "runtime" parameters vs. "creation" parameters. + * + * @see DeviceCreationParameterStrings + */ class DeviceRuntimeParameterStrings : public DeviceRuntimeParameter { public: + /** @brief Constructor for value type @c String. + * + * Sets an initial list of values for the parameter. + * + * @param sVal - initial String value + */ DeviceRuntimeParameterStrings(std::vector<String> vS); + + /// @brief Destructor. virtual ~DeviceRuntimeParameterStrings(){} - virtual String Type(); - virtual bool Multiplicity(); - virtual optional<String> RangeMin(); - virtual optional<String> RangeMax(); - virtual optional<String> Possibilities(); - virtual String Value(); - virtual void SetValue(String val) throw (Exception); + + ///////////////////////////////////////////////////////////////// + // derived methods, implementing type "STRINGS" + // (usually not to be overriden by descendant) + + virtual String Type() OVERRIDE; + virtual bool Multiplicity() OVERRIDE; + virtual optional<String> RangeMin() OVERRIDE; + virtual optional<String> RangeMax() OVERRIDE; + virtual optional<String> Possibilities() OVERRIDE; + virtual String Value() OVERRIDE; + virtual void SetValue(String val) throw (Exception) OVERRIDE; + + ///////////////////////////////////////////////////////////////// + // convenience methods for type "STRINGS" + // (usually not to be overriden by descendant) virtual std::vector<String> ValueAsStrings(); virtual void SetValue(std::vector<String> vS) throw (Exception); - - virtual std::vector<String> PossibilitiesAsString() = 0; - virtual void OnSetValue(std::vector<String> vS) = 0; + + ///////////////////////////////////////////////////////////////// + // abstract methods + // (these have to be implemented by the descendant) + + /** + * Must be implemented by descendant, returning a list of possible + * String values for the parameter. If a list of possible values + * does not make sense, the implementation should return an empty + * vector. + */ + virtual std::vector<String> PossibilitiesAsString() = 0; + + /** + * Must be implemented be a driver's parameter class to react on + * the parameter value being set / altered. + * + * @param vS - new parameter values set by the user + * @throws Exception - might be thrown by the driver in case it + * cannot handle the supplied parameter values + * for whatever reason + */ + virtual void OnSetValue(std::vector<String> vS) = 0; protected: std::vector<String> sVals; }; - - + + ///////////////////////////////////////////////////////////////// + // "Creation" parameters + + /** @brief Abstract base class for parameters at driver instanciation time. + * + * Device "creation" parameters are special parameters, that are meant to be only + * set when a device (driver instance) is created. After device creation those + * parameters act as read only parameters. See DeviceRuntimeParameter for a + * discussion about this topic. + * + * In addition to "runtime" parameters, "creation" parameters also handle + * additional meta informations required in the specific situations of + * creating a device (driver instance). For example "creation" parameters + * might indicate whether they MUST be provided explicitly + * ( @c Mandatory() ) by the user (i.e. a parameter "CARD" selecting a + * specific sound card) for being able to create an instance of the driver. + * And "creation" parameters might indicate being dependent to other + * parameters, i.e. a parameter "SAMPLERATE" might depend on a parameter + * "CARD", to be able to actually provide a list of meaningful + * possibilities for sample rates the respective sound card supports. + */ class DeviceCreationParameter : public DeviceRuntimeParameter { public: - DeviceCreationParameter ( void ) { pDevice = NULL; } - virtual bool Mandatory() = 0; - virtual optional<String> Depends(); + /// @brief Constructor. + DeviceCreationParameter ( void ) { pDevice = NULL; } + + /** + * Whether the parameter must be supplied by the user at device + * creation time. If this method return @c false, then the + * parameter is optional + */ + virtual bool Mandatory() = 0; + + /** + * Might return a comma separated list of parameter names this + * parameter depends on. See this class's introduction about a + * discussion of parameters with dependencies. If this method + * returns @c optional<String>::nothing, then it does not have any + * dependencies to other parameters. + * + * This method already provides an implementation, which usually is + * not overridden by descendants. A descendant would rather + * implement DependsAsParameters() instead. + * + * @see DependsAsParameters() + */ + virtual optional<String> Depends(); + + /** + * Might return a unique key-value pair list (map) reflecting the + * dependencies of this parameter to other parameters. Each entry + * in the map consists of a key-value pair, the key being the + * parameter name of the respective dependent parameter, and the + * value being an instance of the dependency parameter of that name. + * + * A descendant MUST implement this method, informing about its + * dependencies. If the parameter does not have any dependencies it + * should return an empty map. + * + * See this class's introduction about a discussion of parameters + * with dependencies. + */ virtual std::map<String,DeviceCreationParameter*> DependsAsParameters() = 0; - virtual optional<String> Default(); - virtual optional<String> Default(std::map<String,String> Parameters) = 0; - virtual optional<String> RangeMin(); - virtual optional<String> RangeMin(std::map<String,String> Parameters) = 0; - virtual optional<String> RangeMax(); - virtual optional<String> RangeMax(std::map<String,String> Parameters) = 0; - virtual optional<String> Possibilities(); - virtual optional<String> Possibilities(std::map<String,String> Parameters) = 0; - void Attach(Device* pDevice) { this->pDevice = pDevice; } + + /** + * Might return a default value for this parameter. The default + * value will be used if the user does not provide a specific value + * for this parameter at device creation time. If the parameter + * does not have a reasonable default value, it will return + * @c optional<String>::nothing. + * + * This method already provides an implementation, which usually is + * not overridden by descendants. A descendant would rather + * implement the other Default() method taking arguments. + */ + virtual optional<String> Default(); + + /** + * Might return a default value for this parameter. The default + * value will be used if the user does not provide a specific value + * for this parameter at device creation time. + * + * This method must be implemented by descendants. If the parameter + * does not have a reasonable default value, it should return + * @c optional<String>::nothing. + * + * As arguments, the parameters are passed to this method + * automatically, which the user already provided. For example a + * parameter "CARD" the user already did set for selecting a + * specific sound card. So such arguments resolve dependencies + * of this parameter. + * + * @param Parameters - other parameters which the user already + * supplied + */ + virtual optional<String> Default(std::map<String,String> Parameters) = 0; + + /** + * Might return a minimum value for this parameter. If the + * parameter does not have a reasonable minimum value, it will + * return @c optional<String>::nothing. + * + * This method already provides an implementation, which usually is + * not overridden by descendants. A descendant would rather + * implement the other RangeMin() method taking arguments. + */ + virtual optional<String> RangeMin(); + + /** + * Might return a minimum value for this parameter. + * + * This method must be implemented by descendants. If the parameter + * does not have a reasonable minimum value, it should return + * @c optional<String>::nothing. + * + * As arguments, the parameters are passed to this method + * automatically, which the user already provided. For example a + * parameter "CARD" the user already did set for selecting a + * specific sound card. So such arguments resolve dependencies + * of this parameter. + * + * @param Parameters - other parameters which the user already + * supplied + */ + virtual optional<String> RangeMin(std::map<String,String> Parameters) = 0; + + /** + * Might return a maximum value for this parameter. If the + * parameter does not have a reasonable maximum value, it will + * return @c optional<String>::nothing. + * + * This method already provides an implementation, which usually is + * not overridden by descendants. A descendant would rather + * implement the other RangeMax() method taking arguments. + */ + virtual optional<String> RangeMax(); + + /** + * Might return a maximum value for this parameter. + * + * This method must be implemented by descendants. If the parameter + * does not have a reasonable maximum value, it should return + * @c optional<String>::nothing. + * + * As arguments, the parameters are passed to this method + * automatically, which the user already provided. For example a + * parameter "CARD" the user already did set for selecting a + * specific sound card. So such arguments resolve dependencies + * of this parameter. + * + * @param Parameters - other parameters which the user already + * supplied + */ + virtual optional<String> RangeMax(std::map<String,String> Parameters) = 0; + + /** + * Might return a comma separated list as String with possible + * values for this parameter. If the parameter does not have + * reasonable, specific possible values, it will return + * @c optional<String>::nothing. + * + * This method already provides an implementation, which usually is + * not overridden by descendants. A descendant would rather + * implement the other Possibilities() method taking arguments. + */ + virtual optional<String> Possibilities(); + + /** + * Might return a comma separated list as String with possible + * values for this parameter. + * + * This method must be implemented by descendants. If the parameter + * does not have reasonable, specific possible values, it should + * return @c optional<String>::nothing. + * + * As arguments, the parameters are passed to this method + * automatically, which the user already provided. For example a + * parameter "CARD" the user already did set for selecting a + * specific sound card. So such arguments resolve dependencies + * of this parameter. + * + * @param Parameters - other parameters which the user already + * supplied + */ + virtual optional<String> Possibilities(std::map<String,String> Parameters) = 0; + + /** + * Sets the internal device pointer to a specific device (driver + * instance) for this parameter object. Descendants usually use + * that internal pointer to interact with their specific driver + * implementation class. + */ + void Attach(Device* pDevice) { this->pDevice = pDevice; } protected: Device* pDevice; }; + /** @brief Abstract base class for driver parameters of type @c Bool. + * + * Implements a "creation" driver parameter for value type @c bool. + * A driver offering a parameter of type @c bool would derive its + * parameter class from this class and implement the abstract methods + * OnSetValue() and DefaultAsBool(). + * + * See DeviceCreationParameter and DeviceRuntimeParameter for a discussion + * about "runtime" parameters vs. "creation" parameters. + * + * @see DeviceRuntimeParameterBool + */ class DeviceCreationParameterBool : public DeviceCreationParameter { public: DeviceCreationParameterBool(bool bVal = false); DeviceCreationParameterBool(String val) throw (Exception); - virtual String Type(); - virtual bool Multiplicity(); - virtual optional<String> Default(std::map<String,String> Parameters); - virtual optional<String> RangeMin(std::map<String,String> Parameters); - virtual optional<String> RangeMax(std::map<String,String> Parameters); - virtual optional<String> Possibilities(std::map<String,String> Parameters); - virtual String Value(); - virtual void SetValue(String val) throw (Exception); + virtual String Type() OVERRIDE; + virtual bool Multiplicity() OVERRIDE; + virtual optional<String> Default(std::map<String,String> Parameters) OVERRIDE; + virtual optional<String> RangeMin(std::map<String,String> Parameters) OVERRIDE; + virtual optional<String> RangeMax(std::map<String,String> Parameters) OVERRIDE; + virtual optional<String> Possibilities(std::map<String,String> Parameters) OVERRIDE; + virtual String Value() OVERRIDE; + virtual void SetValue(String val) throw (Exception) OVERRIDE; virtual bool ValueAsBool(); virtual void SetValue(bool b) throw (Exception); @@ -200,18 +789,31 @@ private: }; + /** @brief Abstract base class for driver parameters of type @c int. + * + * Implements a "creation" driver parameter for value type @c int. + * A driver offering a parameter of type @c int would derive its + * parameter class from this class and implement the abstract methods + * OnSetValue(), PossibilitiesAsInt(), RangeMinAsInt(), RangeMaxAsInt() + * and DefaultAsInt(). + * + * See DeviceCreationParameter and DeviceRuntimeParameter for a discussion + * about "runtime" parameters vs. "creation" parameters. + * + * @see DeviceRuntimeParameterInt + */ class DeviceCreationParameterInt : public DeviceCreationParameter { public: DeviceCreationParameterInt(int iVal = 0); DeviceCreationParameterInt(String val) throw (Exception); - virtual String Type(); - virtual bool Multiplicity(); - virtual optional<String> Default(std::map<String,String> Parameters); - virtual optional<String> RangeMin(std::map<String,String> Parameters); - virtual optional<String> RangeMax(std::map<String,String> Parameters); - virtual optional<String> Possibilities(std::map<String,String> Parameters); - virtual String Value(); - virtual void SetValue(String val) throw (Exception); + virtual String Type() OVERRIDE; + virtual bool Multiplicity() OVERRIDE; + virtual optional<String> Default(std::map<String,String> Parameters) OVERRIDE; + virtual optional<String> RangeMin(std::map<String,String> Parameters) OVERRIDE; + virtual optional<String> RangeMax(std::map<String,String> Parameters) OVERRIDE; + virtual optional<String> Possibilities(std::map<String,String> Parameters) OVERRIDE; + virtual String Value() OVERRIDE; + virtual void SetValue(String val) throw (Exception) OVERRIDE; virtual int ValueAsInt(); virtual void SetValue(int i) throw (Exception); @@ -227,18 +829,31 @@ private: }; + /** @brief Abstract base class for driver parameters of type @c float. + * + * Implements a "creation" driver parameter for value type @c float. + * A driver offering a parameter of type @c float would derive its + * parameter class from this class and implement the abstract methods + * OnSetValue(), PossibilitiesAsFloat(), RangeMinAsFloat(), + * RangeMaxAsFloat() and DefaultAsFloat(). + * + * See DeviceCreationParameter and DeviceRuntimeParameter for a discussion + * about "runtime" parameters vs. "creation" parameters. + * + * @see DeviceRuntimeParameterFloat + */ class DeviceCreationParameterFloat : public DeviceCreationParameter { public: DeviceCreationParameterFloat(float fVal = 0.0); DeviceCreationParameterFloat(String val) throw (Exception); - virtual String Type(); - virtual bool Multiplicity(); - virtual optional<String> Default(std::map<String,String> Parameters); - virtual optional<String> RangeMin(std::map<String,String> Parameters); - virtual optional<String> RangeMax(std::map<String,String> Parameters); - virtual optional<String> Possibilities(std::map<String,String> Parameters); - virtual String Value(); - virtual void SetValue(String val) throw (Exception); + virtual String Type() OVERRIDE; + virtual bool Multiplicity() OVERRIDE; + virtual optional<String> Default(std::map<String,String> Parameters) OVERRIDE; + virtual optional<String> RangeMin(std::map<String,String> Parameters) OVERRIDE; + virtual optional<String> RangeMax(std::map<String,String> Parameters) OVERRIDE; + virtual optional<String> Possibilities(std::map<String,String> Parameters) OVERRIDE; + virtual String Value() OVERRIDE; + virtual void SetValue(String val) throw (Exception) OVERRIDE; virtual float ValueAsFloat(); virtual void SetValue(float f) throw (Exception); @@ -254,18 +869,30 @@ private: }; + /** @brief Abstract base class for driver parameters of type @c String. + * + * Implements a "creation" driver parameter for value type @c String. + * A driver offering a parameter of type @c String would derive its + * parameter class from this class and implement the abstract methods + * OnSetValue(), PossibilitiesAsString() and DefaultAsString(). + * + * See DeviceCreationParameter and DeviceRuntimeParameter for a discussion + * about "runtime" parameters vs. "creation" parameters. + * + * @see DeviceRuntimeParameterString + */ class DeviceCreationParameterString : public DeviceCreationParameter { public: DeviceCreationParameterString(String sVal = String()); virtual ~DeviceCreationParameterString(){} - virtual String Type(); - virtual bool Multiplicity(); - virtual optional<String> Default(std::map<String,String> Parameters); - virtual optional<String> RangeMin(std::map<String,String> Parameters); - virtual optional<String> RangeMax(std::map<String,String> Parameters); - virtual optional<String> Possibilities(std::map<String,String> Parameters); - virtual String Value(); - virtual void SetValue(String val) throw (Exception); + virtual String Type() OVERRIDE; + virtual bool Multiplicity() OVERRIDE; + virtual optional<String> Default(std::map<String,String> Parameters) OVERRIDE; + virtual optional<String> RangeMin(std::map<String,String> Parameters) OVERRIDE; + virtual optional<String> RangeMax(std::map<String,String> Parameters) OVERRIDE; + virtual optional<String> Possibilities(std::map<String,String> Parameters) OVERRIDE; + virtual String Value() OVERRIDE; + virtual void SetValue(String val) throw (Exception) OVERRIDE; virtual String ValueAsString(); virtual void SetValueAsString(String s) throw (Exception); @@ -279,20 +906,33 @@ private: }; + /** @brief Abstract base class for driver parameters of a @c String list type. + * + * Implements a "creation" driver parameter for @c String value types. + * A driver offering a parameter allowing to set multiple values of type + * @c String would derive its parameter class from this class and implement + * the abstract methods OnSetValue(), PossibilitiesAsString() and + * DefaultAsStrings(). + * + * See DeviceCreationParameter and DeviceRuntimeParameter for a discussion + * about "runtime" parameters vs. "creation" parameters. + * + * @see DeviceRuntimeParameterStrings + */ class DeviceCreationParameterStrings : public DeviceCreationParameter { public: DeviceCreationParameterStrings(); DeviceCreationParameterStrings(std::vector<String> sVals); DeviceCreationParameterStrings(String val) throw (Exception); virtual ~DeviceCreationParameterStrings(){} - virtual String Type(); - virtual bool Multiplicity(); - virtual optional<String> Default(std::map<String,String> Parameters); - virtual optional<String> RangeMin(std::map<String,String> Parameters); - virtual optional<String> RangeMax(std::map<String,String> Parameters); - virtual optional<String> Possibilities(std::map<String,String> Parameters); - virtual String Value(); - virtual void SetValue(String val) throw (Exception); + virtual String Type() OVERRIDE; + virtual bool Multiplicity() OVERRIDE; + virtual optional<String> Default(std::map<String,String> Parameters) OVERRIDE; + virtual optional<String> RangeMin(std::map<String,String> Parameters) OVERRIDE; + virtual optional<String> RangeMax(std::map<String,String> Parameters) OVERRIDE; + virtual optional<String> Possibilities(std::map<String,String> Parameters) OVERRIDE; + virtual String Value() OVERRIDE; + virtual void SetValue(String val) throw (Exception) OVERRIDE; virtual std::vector<String> ValueAsStrings(); virtual void SetValue(std::vector<String> vS) throw (Exception);
View file
linuxsampler-2342.tar.bz2/src/drivers/DeviceParameterFactory.h -> linuxsampler-2718.tar.bz2/src/drivers/DeviceParameterFactory.h
Changed
@@ -3,7 +3,7 @@ * LinuxSampler - modular, streaming capable sampler * * * * Copyright (C) 2003, 2004 by Benno Senoner and Christian Schoenebeck * - * Copyright (C) 2005 - 2009 Christian Schoenebeck * + * Copyright (C) 2005 - 2014 Christian Schoenebeck * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -41,6 +41,7 @@ class InnerFactory { public: InnerFactory(DeviceParameterFactory* pParent) { this->pParent = pParent; } + virtual ~InnerFactory() {} virtual DeviceCreationParameter* Create(std::map<String,String> Parameters = StringMap()) = 0; virtual DeviceCreationParameter* Create(String val) = 0; protected:
View file
linuxsampler-2342.tar.bz2/src/drivers/Makefile.am -> linuxsampler-2718.tar.bz2/src/drivers/Makefile.am
Changed
@@ -1,4 +1,4 @@ -INCLUDES = $(all_includes) $(GIG_CFLAGS) $(SQLITE3_CFLAGS) +AM_CPPFLAGS = $(all_includes) $(GIG_CFLAGS) $(SQLITE3_CFLAGS) METASOURCES = AUTO AM_CXXFLAGS = -Wreturn-type -ffast-math $(CXX_CPU_SWITCH)
View file
linuxsampler-2342.tar.bz2/src/drivers/audio/AudioChannel.cpp -> linuxsampler-2718.tar.bz2/src/drivers/audio/AudioChannel.cpp
Changed
@@ -3,7 +3,7 @@ * LinuxSampler - modular, streaming capable sampler * * * * Copyright (C) 2003, 2004 by Benno Senoner and Christian Schoenebeck * - * Copyright (C) 2005 - 2009 Christian Schoenebeck * + * Copyright (C) 2005 - 2012 Christian Schoenebeck * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -115,7 +115,7 @@ * @param Samples - amount of sample points to be copied */ void AudioChannel::CopyTo(AudioChannel* pDst, const uint Samples) { - memcpy(pDst->Buffer(), Buffer(), Samples * sizeof(float)); + memcpy((float* __restrict)pDst->Buffer(), (const float* __restrict)Buffer(), Samples * sizeof(float)); } /** @@ -133,13 +133,19 @@ void AudioChannel::CopyTo(AudioChannel* pDst, const uint Samples, const float fLevel) { if (fLevel == 1.0f) CopyTo(pDst, Samples); else { - float* pSrcBuf = Buffer(); - float* pDstBuf = pDst->Buffer(); + const float* __restrict pSrcBuf = Buffer(); + float* __restrict pDstBuf = pDst->Buffer(); #if HAVE_GCC_VECTOR_EXTENSIONS if ((size_t)pSrcBuf % 16 == 0 && (size_t)pDstBuf % 16 == 0) { const v4sf vcoeff = { fLevel, fLevel, fLevel, fLevel }; - const v4sf* src = static_cast<v4sf*>((void*)Buffer()); - v4sf* dst = static_cast<v4sf*>((void*)pDst->Buffer()); + const v4sf* __restrict src = + static_cast<const v4sf* __restrict>( + (const void* __restrict)pSrcBuf + ); + v4sf* __restrict dst = + static_cast<v4sf* __restrict>( + (void* __restrict)pDstBuf + ); const int cells = Samples / 4; for (int i = 0; i < cells; ++i) dsti = srci * vcoeff; @@ -161,12 +167,18 @@ * @param Samples - amount of sample points to be mixed over */ void AudioChannel::MixTo(AudioChannel* pDst, const uint Samples) { - float* pSrcBuf = Buffer(); - float* pDstBuf = pDst->Buffer(); + const float* __restrict pSrcBuf = Buffer(); + float* __restrict pDstBuf = pDst->Buffer(); #if HAVE_GCC_VECTOR_EXTENSIONS if ((size_t)pSrcBuf % 16 == 0 && (size_t)pDstBuf % 16 == 0) { - const v4sf* src = static_cast<v4sf*>((void*)Buffer()); - v4sf* dst = static_cast<v4sf*>((void*)pDst->Buffer()); + const v4sf* __restrict src = + static_cast<const v4sf* __restrict>( + (const void* __restrict)pSrcBuf + ); + v4sf* __restrict dst = + static_cast<v4sf* __restrict>( + (void* __restrict)pDstBuf + ); const int cells = Samples / 4; for (int i = 0; i < cells; ++i) dsti += srci; @@ -191,13 +203,19 @@ void AudioChannel::MixTo(AudioChannel* pDst, const uint Samples, const float fLevel) { if (fLevel == 1.0f) MixTo(pDst, Samples); else { - float* pSrcBuf = Buffer(); - float* pDstBuf = pDst->Buffer(); + const float* __restrict pSrcBuf = Buffer(); + float* __restrict pDstBuf = pDst->Buffer(); #if HAVE_GCC_VECTOR_EXTENSIONS if ((size_t)pSrcBuf % 16 == 0 && (size_t)pDstBuf % 16 == 0) { const v4sf vcoeff = { fLevel, fLevel, fLevel, fLevel }; - const v4sf* src = static_cast<v4sf*>((void*)Buffer()); - v4sf* dst = static_cast<v4sf*>((void*)pDst->Buffer()); + const v4sf* __restrict src = + static_cast<const v4sf* __restrict>( + (const void* __restrict)pSrcBuf + ); + v4sf* __restrict dst = + static_cast<v4sf* __restrict>( + (void* __restrict)pDstBuf + ); const int cells = Samples / 4; for (int i = 0; i < cells; ++i) dsti += srci * vcoeff;
View file
linuxsampler-2342.tar.bz2/src/drivers/audio/AudioChannel.h -> linuxsampler-2718.tar.bz2/src/drivers/audio/AudioChannel.h
Changed
@@ -3,7 +3,7 @@ * LinuxSampler - modular, streaming capable sampler * * * * Copyright (C) 2003, 2004 by Benno Senoner and Christian Schoenebeck * - * Copyright (C) 2005 - 2007 Christian Schoenebeck * + * Copyright (C) 2005 - 2013 Christian Schoenebeck * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -58,29 +58,29 @@ class ParameterName : public DeviceRuntimeParameterString { public: ParameterName(String s) : DeviceRuntimeParameterString(s) {} - virtual String Description() { return "Arbitrary name"; } - virtual bool Fix() { return false; } - virtual std::vector<String> PossibilitiesAsString() { return std::vector<String>(); } - virtual void OnSetValue(String s) { /* nothing to do */ } + virtual String Description() OVERRIDE { return "Arbitrary name"; } + virtual bool Fix() OVERRIDE { return false; } + virtual std::vector<String> PossibilitiesAsString() OVERRIDE { return std::vector<String>(); } + virtual void OnSetValue(String s) OVERRIDE { /* nothing to do */ } }; class ParameterIsMixChannel : public DeviceRuntimeParameterBool { public: ParameterIsMixChannel(bool b) : DeviceRuntimeParameterBool(b) {} - virtual String Description() { return "Whether real channel or mixed to another channel"; } - virtual bool Fix() { return true; } - virtual void OnSetValue(bool b) throw (Exception) { /* cannot happen, as parameter is fix */ } + virtual String Description() OVERRIDE { return "Whether real channel or mixed to another channel"; } + virtual bool Fix() OVERRIDE { return true; } + virtual void OnSetValue(bool b) throw (Exception) OVERRIDE { /* cannot happen, as parameter is fix */ } }; class ParameterMixChannelDestination : public DeviceRuntimeParameterInt { public: ParameterMixChannelDestination(int i) : DeviceRuntimeParameterInt(i) {} - virtual String Description() { return "Destination channel of this mix channel"; } - virtual bool Fix() { return true; } - virtual optional<int> RangeMinAsInt() { return optional<int>::nothing; /*TODO: needs to be implemented */ } - virtual optional<int> RangeMaxAsInt() { return optional<int>::nothing; /*TODO: needs to be implemented */ } - virtual std::vector<int> PossibilitiesAsInt() { return std::vector<int>(); /*TODO: needs to be implemented */ } - virtual void OnSetValue(int i) throw (Exception) { /*TODO: needs to be implemented */ } + virtual String Description() OVERRIDE { return "Destination channel of this mix channel"; } + virtual bool Fix() OVERRIDE { return true; } + virtual optional<int> RangeMinAsInt() OVERRIDE { return optional<int>::nothing; /*TODO: needs to be implemented */ } + virtual optional<int> RangeMaxAsInt() OVERRIDE { return optional<int>::nothing; /*TODO: needs to be implemented */ } + virtual std::vector<int> PossibilitiesAsInt() OVERRIDE { return std::vector<int>(); /*TODO: needs to be implemented */ } + virtual void OnSetValue(int i) throw (Exception) OVERRIDE { /*TODO: needs to be implemented */ } }; // attributes
View file
linuxsampler-2342.tar.bz2/src/drivers/audio/AudioOutputDevice.cpp -> linuxsampler-2718.tar.bz2/src/drivers/audio/AudioOutputDevice.cpp
Changed
@@ -3,7 +3,7 @@ * LinuxSampler - modular, streaming capable sampler * * * * Copyright (C) 2003, 2004 by Benno Senoner and Christian Schoenebeck * - * Copyright (C) 2005 - 2010 Christian Schoenebeck * + * Copyright (C) 2005 - 2013 Christian Schoenebeck * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -240,6 +240,26 @@ //pEngine->DisconnectAudioOutputDevice(); } } + + void AudioOutputDevice::ReconnectAll() { + // copy by value, not by reference here ! + std::set<Engine*> engines = Engines.GetConfigForUpdate(); + { + std::set<Engine*>::iterator iterEngine = engines.begin(); + std::set<Engine*>::iterator end = engines.end(); + for (; iterEngine != end; iterEngine++) { + (*iterEngine)->ReconnectAudioOutputDevice(); + } + } + + // update all effects as well + for (std::vector<EffectChain*>::iterator it = vEffectChains.begin(); + it != vEffectChains.end(); ++it) + { + EffectChain* pChain = *it; + pChain->Reconnect(this); + } + } AudioChannel* AudioOutputDevice::Channel(uint ChannelIndex) { return (ChannelIndex < Channels.size()) ? ChannelsChannelIndex : NULL; @@ -315,6 +335,10 @@ uint AudioOutputDevice::MasterEffectChainCount() const { return SendEffectChainCount(); } + + float AudioOutputDevice::latency() { + return float(MaxSamplesPerCycle()) / float(SampleRate()); + } int AudioOutputDevice::RenderAudio(uint Samples) { if (Channels.empty()) return 0;
View file
linuxsampler-2342.tar.bz2/src/drivers/audio/AudioOutputDevice.h -> linuxsampler-2718.tar.bz2/src/drivers/audio/AudioOutputDevice.h
Changed
@@ -3,7 +3,7 @@ * LinuxSampler - modular, streaming capable sampler * * * * Copyright (C) 2003, 2004 by Benno Senoner and Christian Schoenebeck * - * Copyright (C) 2005 - 2010 Christian Schoenebeck * + * Copyright (C) 2005 - 2013 Christian Schoenebeck * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -65,12 +65,12 @@ public: ParameterActive(); ParameterActive(String s); - virtual String Description(); - virtual bool Fix(); - virtual bool Mandatory(); - virtual std::map<String,DeviceCreationParameter*> DependsAsParameters(); - virtual optional<bool> DefaultAsBool(std::map<String,String> Parameters); - virtual void OnSetValue(bool b) throw (Exception); + virtual String Description() OVERRIDE; + virtual bool Fix() OVERRIDE; + virtual bool Mandatory() OVERRIDE; + virtual std::map<String,DeviceCreationParameter*> DependsAsParameters() OVERRIDE; + virtual optional<bool> DefaultAsBool(std::map<String,String> Parameters) OVERRIDE; + virtual void OnSetValue(bool b) throw (Exception) OVERRIDE; static String Name(); }; @@ -82,16 +82,16 @@ public: ParameterSampleRate(); ParameterSampleRate(String s); - virtual String Description(); - virtual bool Fix(); - virtual bool Mandatory(); - virtual std::map<String,DeviceCreationParameter*> DependsAsParameters(); - virtual optional<int> DefaultAsInt(std::map<String,String> Parameters); - virtual optional<int> RangeMinAsInt(std::map<String,String> Parameters); - virtual optional<int> RangeMaxAsInt(std::map<String,String> Parameters); - virtual std::vector<int> PossibilitiesAsInt(std::map<String,String> Parameters); - virtual int ValueAsInt(); - virtual void OnSetValue(int i) throw (Exception); + virtual String Description() OVERRIDE; + virtual bool Fix() OVERRIDE; + virtual bool Mandatory() OVERRIDE; + virtual std::map<String,DeviceCreationParameter*> DependsAsParameters() OVERRIDE; + virtual optional<int> DefaultAsInt(std::map<String,String> Parameters) OVERRIDE; + virtual optional<int> RangeMinAsInt(std::map<String,String> Parameters) OVERRIDE; + virtual optional<int> RangeMaxAsInt(std::map<String,String> Parameters) OVERRIDE; + virtual std::vector<int> PossibilitiesAsInt(std::map<String,String> Parameters) OVERRIDE; + virtual int ValueAsInt() OVERRIDE; + virtual void OnSetValue(int i) throw (Exception) OVERRIDE; static String Name(); }; @@ -104,15 +104,15 @@ public: ParameterChannels(); ParameterChannels(String s); - virtual String Description(); - virtual bool Fix(); - virtual bool Mandatory(); - virtual std::map<String,DeviceCreationParameter*> DependsAsParameters(); - virtual optional<int> DefaultAsInt(std::map<String,String> Parameters); - virtual optional<int> RangeMinAsInt(std::map<String,String> Parameters); - virtual optional<int> RangeMaxAsInt(std::map<String,String> Parameters); - virtual std::vector<int> PossibilitiesAsInt(std::map<String,String> Parameters); - virtual void OnSetValue(int i) throw (Exception); + virtual String Description() OVERRIDE; + virtual bool Fix() OVERRIDE; + virtual bool Mandatory() OVERRIDE; + virtual std::map<String,DeviceCreationParameter*> DependsAsParameters() OVERRIDE; + virtual optional<int> DefaultAsInt(std::map<String,String> Parameters) OVERRIDE; + virtual optional<int> RangeMinAsInt(std::map<String,String> Parameters) OVERRIDE; + virtual optional<int> RangeMaxAsInt(std::map<String,String> Parameters) OVERRIDE; + virtual std::vector<int> PossibilitiesAsInt(std::map<String,String> Parameters) OVERRIDE; + virtual void OnSetValue(int i) throw (Exception) OVERRIDE; static String Name(); }; @@ -181,6 +181,23 @@ * AcquireChannels(). Each driver must implement it. */ virtual AudioChannel* CreateChannel(uint ChannelNr) = 0; + + /** + * Might be optionally implemented by the deriving driver. If + * implemented, this method will return the current audio + * latency in seconds. The driver might measure the actual + * latency with timing functions to return the real latency. + * + * If this method is not implemented / overridden by the + * descending driver, it will simply return the theoretical + * latency, that is MaxSamplesPerCycle() / SampleRate(). + * + * Currently this method is not used by the sampler itself. + * It can be used however for GUIs built on top of the sampler + * for example, to i.e. display the user the currently + * effective audio latency in real-time. + */ + virtual float latency(); @@ -206,6 +223,20 @@ * @param pEngine - sampler engine */ void Disconnect(Engine* pEngine); + + /** + * This method can or should be called by the deriving driver in + * case some fundamental parameter changed, especially if the + * values returned by MaxSamplesPerCycle() and SamplerRate() have + * changed, those are values which the sampler engines assume to be + * constant for the life time of an audio device ! + * + * By forcing a re-connection with this method, the sampler engines + * are forced to update those informations metntioned above as well, + * avoiding crashes or other undesired misbehaviors in such + * circumstances. + */ + void ReconnectAll(); /** * Returns audio channel with index \a ChannelIndex or NULL if
View file
linuxsampler-2342.tar.bz2/src/drivers/audio/AudioOutputDeviceAlsa.cpp -> linuxsampler-2718.tar.bz2/src/drivers/audio/AudioOutputDeviceAlsa.cpp
Changed
@@ -736,7 +736,7 @@ } String AudioOutputDeviceAlsa::Version() { - String s = "$Revision: 1.25 $"; + String s = "$Revision: 2494 $"; return s.substr(11, s.size() - 13); // cut dollar signs, spaces and CVS macro keyword }
View file
linuxsampler-2342.tar.bz2/src/drivers/audio/AudioOutputDeviceAlsa.h -> linuxsampler-2718.tar.bz2/src/drivers/audio/AudioOutputDeviceAlsa.h
Changed
@@ -3,7 +3,7 @@ * LinuxSampler - modular, streaming capable sampler * * * * Copyright (C) 2003, 2004 by Benno Senoner and Christian Schoenebeck * - * Copyright (C) 2005 - 2007 Christian Schoenebeck * + * Copyright (C) 2005 - 2013 Christian Schoenebeck * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -45,14 +45,14 @@ ~AudioOutputDeviceAlsa(); // derived abstract methods from class 'AudioOutputDevice' - virtual void Play(); - virtual bool IsPlaying(); - virtual void Stop(); - virtual uint MaxSamplesPerCycle(); - virtual uint SampleRate(); - virtual AudioChannel* CreateChannel(uint ChannelNr); - - virtual String Driver(); + virtual void Play() OVERRIDE; + virtual bool IsPlaying() OVERRIDE; + virtual void Stop() OVERRIDE; + virtual uint MaxSamplesPerCycle() OVERRIDE; + virtual uint SampleRate() OVERRIDE; + virtual AudioChannel* CreateChannel(uint ChannelNr) OVERRIDE; + virtual String Driver() OVERRIDE; + static String Name(); static String Description(); static String Version(); @@ -65,13 +65,13 @@ public: ParameterCard(); ParameterCard(String s) throw (Exception); - virtual String Description(); - virtual bool Fix(); - virtual bool Mandatory(); - virtual std::map<String,DeviceCreationParameter*> DependsAsParameters(); - virtual optional<String> DefaultAsString(std::map<String,String> Parameters); - virtual std::vector<String> PossibilitiesAsString(std::map<String,String> Parameters); - virtual void OnSetValue(String s) throw (Exception); + virtual String Description() OVERRIDE; + virtual bool Fix() OVERRIDE; + virtual bool Mandatory() OVERRIDE; + virtual std::map<String,DeviceCreationParameter*> DependsAsParameters() OVERRIDE; + virtual optional<String> DefaultAsString(std::map<String,String> Parameters) OVERRIDE; + virtual std::vector<String> PossibilitiesAsString(std::map<String,String> Parameters) OVERRIDE; + virtual void OnSetValue(String s) throw (Exception) OVERRIDE; static String Name(); }; @@ -83,10 +83,10 @@ public: ParameterSampleRate(); ParameterSampleRate(String s); - virtual std::map<String,DeviceCreationParameter*> DependsAsParameters(); - virtual optional<int> DefaultAsInt(std::map<String,String> Parameters); - virtual optional<int> RangeMinAsInt(std::map<String,String> Parameters); - virtual optional<int> RangeMaxAsInt(std::map<String,String> Parameters); + virtual std::map<String,DeviceCreationParameter*> DependsAsParameters() OVERRIDE; + virtual optional<int> DefaultAsInt(std::map<String,String> Parameters) OVERRIDE; + virtual optional<int> RangeMinAsInt(std::map<String,String> Parameters) OVERRIDE; + virtual optional<int> RangeMaxAsInt(std::map<String,String> Parameters) OVERRIDE; }; /** Device Parameters 'CHANNELS' @@ -98,10 +98,10 @@ public: ParameterChannels(); ParameterChannels(String s); - virtual std::map<String,DeviceCreationParameter*> DependsAsParameters(); - virtual optional<int> DefaultAsInt(std::map<String,String> Parameters); - virtual optional<int> RangeMinAsInt(std::map<String,String> Parameters); - virtual optional<int> RangeMaxAsInt(std::map<String,String> Parameters); + virtual std::map<String,DeviceCreationParameter*> DependsAsParameters() OVERRIDE; + virtual optional<int> DefaultAsInt(std::map<String,String> Parameters) OVERRIDE; + virtual optional<int> RangeMinAsInt(std::map<String,String> Parameters) OVERRIDE; + virtual optional<int> RangeMaxAsInt(std::map<String,String> Parameters) OVERRIDE; }; /** Device Parameter 'FRAGMENTS' @@ -112,15 +112,15 @@ public: ParameterFragments(); ParameterFragments(String s) throw (Exception); - virtual String Description(); - virtual bool Fix(); - virtual bool Mandatory(); - virtual std::map<String,DeviceCreationParameter*> DependsAsParameters(); - virtual optional<int> DefaultAsInt(std::map<String,String> Parameters); - virtual optional<int> RangeMinAsInt(std::map<String,String> Parameters); - virtual optional<int> RangeMaxAsInt(std::map<String,String> Parameters); - virtual std::vector<int> PossibilitiesAsInt(std::map<String,String> Parameters); - virtual void OnSetValue(int i) throw (Exception); + virtual String Description() OVERRIDE; + virtual bool Fix() OVERRIDE; + virtual bool Mandatory() OVERRIDE; + virtual std::map<String,DeviceCreationParameter*> DependsAsParameters() OVERRIDE; + virtual optional<int> DefaultAsInt(std::map<String,String> Parameters) OVERRIDE; + virtual optional<int> RangeMinAsInt(std::map<String,String> Parameters) OVERRIDE; + virtual optional<int> RangeMaxAsInt(std::map<String,String> Parameters) OVERRIDE; + virtual std::vector<int> PossibilitiesAsInt(std::map<String,String> Parameters) OVERRIDE; + virtual void OnSetValue(int i) throw (Exception) OVERRIDE; static String Name(); }; @@ -132,15 +132,15 @@ public: ParameterFragmentSize(); ParameterFragmentSize(String s) throw (Exception); - virtual String Description(); - virtual bool Fix(); - virtual bool Mandatory(); - virtual std::map<String,DeviceCreationParameter*> DependsAsParameters(); - virtual optional<int> DefaultAsInt(std::map<String,String> Parameters); - virtual optional<int> RangeMinAsInt(std::map<String,String> Parameters); - virtual optional<int> RangeMaxAsInt(std::map<String,String> Parameters); - virtual std::vector<int> PossibilitiesAsInt(std::map<String,String> Parameters); - virtual void OnSetValue(int i) throw (Exception); + virtual String Description() OVERRIDE; + virtual bool Fix() OVERRIDE; + virtual bool Mandatory() OVERRIDE; + virtual std::map<String,DeviceCreationParameter*> DependsAsParameters() OVERRIDE; + virtual optional<int> DefaultAsInt(std::map<String,String> Parameters) OVERRIDE; + virtual optional<int> RangeMinAsInt(std::map<String,String> Parameters) OVERRIDE; + virtual optional<int> RangeMaxAsInt(std::map<String,String> Parameters) OVERRIDE; + virtual std::vector<int> PossibilitiesAsInt(std::map<String,String> Parameters) OVERRIDE; + virtual void OnSetValue(int i) throw (Exception) OVERRIDE; static String Name(); };
View file
linuxsampler-2342.tar.bz2/src/drivers/audio/AudioOutputDeviceArts.cpp -> linuxsampler-2718.tar.bz2/src/drivers/audio/AudioOutputDeviceArts.cpp
Changed
@@ -161,7 +161,7 @@ } String AudioOutputDeviceArts::Version() { - String s = "$Revision: 1.2 $"; + String s = "$Revision: 2494 $"; return s.substr(11, s.size() - 13); // cut dollar signs, spaces and CVS macro keyword }
View file
linuxsampler-2342.tar.bz2/src/drivers/audio/AudioOutputDeviceArts.h -> linuxsampler-2718.tar.bz2/src/drivers/audio/AudioOutputDeviceArts.h
Changed
@@ -1,6 +1,6 @@ /*************************************************************************** * * - * Copyright (C) 2006, 2007 Christian Schoenebeck * + * Copyright (C) 2006, 2007, 2013 Christian Schoenebeck * * * * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -49,31 +49,31 @@ public: ParameterName(); ParameterName(String s) throw (Exception); - virtual String Description(); - virtual bool Fix(); - virtual bool Mandatory(); - virtual std::map<String,DeviceCreationParameter*> DependsAsParameters(); - virtual std::vector<String> PossibilitiesAsString(std::map<String,String> Parameters); - virtual optional<String> DefaultAsString(std::map<String,String> Parameters); - virtual void OnSetValue(String s) throw (Exception); + virtual String Description() OVERRIDE; + virtual bool Fix() OVERRIDE; + virtual bool Mandatory() OVERRIDE; + virtual std::map<String,DeviceCreationParameter*> DependsAsParameters() OVERRIDE; + virtual std::vector<String> PossibilitiesAsString(std::map<String,String> Parameters) OVERRIDE; + virtual optional<String> DefaultAsString(std::map<String,String> Parameters) OVERRIDE; + virtual void OnSetValue(String s) throw (Exception) OVERRIDE; static String Name(); }; // derived abstract methods from class 'AudioOutputDevice' - virtual void Play(); - virtual bool IsPlaying(); - virtual void Stop(); - virtual uint MaxSamplesPerCycle(); - virtual uint SampleRate(); - virtual AudioChannel* CreateChannel(uint ChannelNr); - - virtual String Driver(); + virtual void Play() OVERRIDE; + virtual bool IsPlaying() OVERRIDE; + virtual void Stop() OVERRIDE; + virtual uint MaxSamplesPerCycle() OVERRIDE; + virtual uint SampleRate() OVERRIDE; + virtual AudioChannel* CreateChannel(uint ChannelNr) OVERRIDE; + virtual String Driver() OVERRIDE; + static String Name(); static String Description(); static String Version(); protected: - int Main(); ///< Implementation of virtual method from class Thread + virtual int Main() OVERRIDE; ///< Implementation of virtual method from class Thread private: unsigned int uiArtsChannels;
View file
linuxsampler-2342.tar.bz2/src/drivers/audio/AudioOutputDeviceAsio.cpp -> linuxsampler-2718.tar.bz2/src/drivers/audio/AudioOutputDeviceAsio.cpp
Changed
@@ -3,7 +3,7 @@ * LinuxSampler - modular, streaming capable sampler * * * * Copyright (C) 2003, 2004 by Benno Senoner and Christian Schoenebeck * - * Copyright (C) 2005 - 2008 Christian Schoenebeck * + * Copyright (C) 2005 - 2014 Christian Schoenebeck * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -595,7 +595,12 @@ optional<String> AudioOutputDeviceAsio::ParameterCard::DefaultAsString(std::map<String,String> Parameters) { std::vector<String> cards = PossibilitiesAsString(Parameters); - if (cards.empty()) throw Exception("AudioOutputDeviceAsio: Can't find any card"); + if (cards.empty()) throw Exception( + "AudioOutputDeviceAsio: Can't find any ASIO-capable sound card. Make " + "sure you have an ASIO driver installed for your sound device. If you " + "are using a consumer sound card that does not provide an ASIO driver " + "to be installed, then consider installing an alternative like ASIO4ALL." + ); // If currentAsioDriverName is empty then return the first card, // otherwise return the currentAsioDriverName. This avoids closing @@ -968,7 +973,7 @@ } String AudioOutputDeviceAsio::Version() { - String s = "$Revision: 1.6 $"; + String s = "$Revision: 2509 $"; return s.substr(11, s.size() - 13); // cut dollar signs, spaces and CVS macro keyword }
View file
linuxsampler-2342.tar.bz2/src/drivers/audio/AudioOutputDeviceAsio.h -> linuxsampler-2718.tar.bz2/src/drivers/audio/AudioOutputDeviceAsio.h
Changed
@@ -3,7 +3,7 @@ * LinuxSampler - modular, streaming capable sampler * * * * Copyright (C) 2003, 2004 by Benno Senoner and Christian Schoenebeck * - * Copyright (C) 2005 - 2007 Christian Schoenebeck * + * Copyright (C) 2005 - 2013 Christian Schoenebeck * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -49,14 +49,14 @@ ~AudioOutputDeviceAsio(); // derived abstract methods from class 'AudioOutputDevice' - virtual void Play(); - virtual bool IsPlaying(); - virtual void Stop(); - virtual uint MaxSamplesPerCycle(); - virtual uint SampleRate(); - virtual AudioChannel* CreateChannel(uint ChannelNr); - - virtual String Driver(); + virtual void Play() OVERRIDE; + virtual bool IsPlaying() OVERRIDE; + virtual void Stop() OVERRIDE; + virtual uint MaxSamplesPerCycle() OVERRIDE; + virtual uint SampleRate() OVERRIDE; + virtual AudioChannel* CreateChannel(uint ChannelNr) OVERRIDE; + virtual String Driver() OVERRIDE; + static String Name(); static String Description(); static String Version(); @@ -69,13 +69,13 @@ public: ParameterCard(); ParameterCard(String s) throw (Exception); - virtual String Description(); - virtual bool Fix(); - virtual bool Mandatory(); - virtual std::map<String,DeviceCreationParameter*> DependsAsParameters(); - virtual optional<String> DefaultAsString(std::map<String,String> Parameters); - virtual std::vector<String> PossibilitiesAsString(std::map<String,String> Parameters); - virtual void OnSetValue(String s) throw (Exception); + virtual String Description() OVERRIDE; + virtual bool Fix() OVERRIDE; + virtual bool Mandatory() OVERRIDE; + virtual std::map<String,DeviceCreationParameter*> DependsAsParameters() OVERRIDE; + virtual optional<String> DefaultAsString(std::map<String,String> Parameters) OVERRIDE; + virtual std::vector<String> PossibilitiesAsString(std::map<String,String> Parameters) OVERRIDE; + virtual void OnSetValue(String s) throw (Exception) OVERRIDE; static String Name(); }; @@ -87,9 +87,9 @@ public: ParameterSampleRate(); ParameterSampleRate(String s); - virtual std::map<String,DeviceCreationParameter*> DependsAsParameters(); - virtual optional<int> DefaultAsInt(std::map<String,String> Parameters); - virtual std::vector<int> PossibilitiesAsInt(std::map<String,String> Parameters); + virtual std::map<String,DeviceCreationParameter*> DependsAsParameters() OVERRIDE; + virtual optional<int> DefaultAsInt(std::map<String,String> Parameters) OVERRIDE; + virtual std::vector<int> PossibilitiesAsInt(std::map<String,String> Parameters) OVERRIDE; }; /** Device Parameters 'CHANNELS' @@ -101,10 +101,10 @@ public: ParameterChannels(); ParameterChannels(String s); - virtual std::map<String,DeviceCreationParameter*> DependsAsParameters(); - virtual optional<int> DefaultAsInt(std::map<String,String> Parameters); - virtual optional<int> RangeMinAsInt(std::map<String,String> Parameters); - virtual optional<int> RangeMaxAsInt(std::map<String,String> Parameters); + virtual std::map<String,DeviceCreationParameter*> DependsAsParameters() OVERRIDE; + virtual optional<int> DefaultAsInt(std::map<String,String> Parameters) OVERRIDE; + virtual optional<int> RangeMinAsInt(std::map<String,String> Parameters) OVERRIDE; + virtual optional<int> RangeMaxAsInt(std::map<String,String> Parameters) OVERRIDE; }; @@ -116,32 +116,28 @@ public: ParameterFragmentSize(); ParameterFragmentSize(String s) throw (Exception); - virtual String Description(); - virtual bool Fix(); - virtual bool Mandatory(); - virtual std::map<String,DeviceCreationParameter*> DependsAsParameters(); - virtual optional<int> DefaultAsInt(std::map<String,String> Parameters); - virtual optional<int> RangeMinAsInt(std::map<String,String> Parameters); - virtual optional<int> RangeMaxAsInt(std::map<String,String> Parameters); - virtual std::vector<int> PossibilitiesAsInt(std::map<String,String> Parameters); - virtual void OnSetValue(int i) throw (Exception); + virtual String Description() OVERRIDE; + virtual bool Fix() OVERRIDE; + virtual bool Mandatory() OVERRIDE; + virtual std::map<String,DeviceCreationParameter*> DependsAsParameters() OVERRIDE; + virtual optional<int> DefaultAsInt(std::map<String,String> Parameters) OVERRIDE; + virtual optional<int> RangeMinAsInt(std::map<String,String> Parameters) OVERRIDE; + virtual optional<int> RangeMaxAsInt(std::map<String,String> Parameters) OVERRIDE; + virtual std::vector<int> PossibilitiesAsInt(std::map<String,String> Parameters) OVERRIDE; + virtual void OnSetValue(int i) throw (Exception) OVERRIDE; static String Name(); }; - // make protected method public - AudioOutputDevice::RenderAudio; - AudioOutputDevice::Channels; - - - protected: - int Main(); ///< Implementation of virtual method from class Thread + // make protected methods public + using AudioOutputDevice::RenderAudio; + using AudioOutputDevice::Channels; private: uint uiAsioChannels; uint uiSamplerate; uint FragmentSize; - bool asioIsPlaying; + bool asioIsPlaying; static void bufferSwitch(long index, ASIOBool processNow); static ASIOTime* bufferSwitchTimeInfo(ASIOTime *timeInfo, long index, ASIOBool processNow);
View file
linuxsampler-2342.tar.bz2/src/drivers/audio/AudioOutputDeviceCoreAudio.cpp -> linuxsampler-2718.tar.bz2/src/drivers/audio/AudioOutputDeviceCoreAudio.cpp
Changed
@@ -2,7 +2,7 @@ * * * LinuxSampler - modular, streaming capable sampler * * * - * Copyright (C) 2009 Grigor Iliev * + * Copyright (C) 2009 - 2013 Grigor Iliev * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -143,9 +143,10 @@ AudioOutputDeviceCoreAudio::~AudioOutputDeviceCoreAudio() { atomic_set(&(aqPlayerState.mIsRunning), 0); - destroyMutex.Lock(); - AudioQueueDispose(aqPlayerState.mQueue, true); - destroyMutex.Unlock(); + { + LockGuard lock(destroyMutex); + AudioQueueDispose(aqPlayerState.mQueue, true); + } delete aqPlayerState.mBuffers; CurrentDevice.RemoveListener(this); @@ -160,7 +161,7 @@ } String AudioOutputDeviceCoreAudio::Version() { - String s = "$Revision: 1.5 $"; + String s = "$Revision: 2494 $"; return s.substr(11, s.size() - 13); // cut dollar signs, spaces and CVS macro keyword } @@ -317,36 +318,33 @@ } } - destroyMutex.Lock(); - do { - if(atomic_read(&pausedNew) != pausedOld) { - pausedOld = atomic_read(&pausedNew); - - if(pausedOld) { - res = AudioQueuePause(aqPlayerState.mQueue); - if(res) std::cerr << "AudioQueuePause: Error " << res << std::endl; - } else { - res = AudioQueuePrime(aqPlayerState.mQueue, 0, NULL); - if(res) std::cerr << "AudioQueuePrime: Error " << res << std::endl; - res = AudioQueueStart(aqPlayerState.mQueue, NULL); - if(res) std::cerr << "AudioQueueStart: Error " << res << std::endl; + { + LockGuard lock(destroyMutex); + do { + if(atomic_read(&pausedNew) != pausedOld) { + pausedOld = atomic_read(&pausedNew); + + if(pausedOld) { + res = AudioQueuePause(aqPlayerState.mQueue); + if(res) std::cerr << "AudioQueuePause: Error " << res << std::endl; + } else { + res = AudioQueuePrime(aqPlayerState.mQueue, 0, NULL); + if(res) std::cerr << "AudioQueuePrime: Error " << res << std::endl; + res = AudioQueueStart(aqPlayerState.mQueue, NULL); + if(res) std::cerr << "AudioQueueStart: Error " << res << std::endl; + } } - } - if(atomic_read(&restartQueue)) { - DestroyAudioQueue(); - try { CreateAndStartAudioQueue(); } - catch(Exception e) { - destroyMutex.Unlock(); - throw e; + if(atomic_read(&restartQueue)) { + DestroyAudioQueue(); + CreateAndStartAudioQueue(); + atomic_set(&restartQueue, 0); + dmsg(1,("Audio queue restarted")); } - atomic_set(&restartQueue, 0); - dmsg(1,("Audio queue restarted")); - } - CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.2, false); - } while (atomic_read(&(aqPlayerState.mIsRunning))); - destroyMutex.Unlock(); + CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.2, false); + } while (atomic_read(&(aqPlayerState.mIsRunning))); + } dmsg(2,("CoreAudio thread stopped\n"));
View file
linuxsampler-2342.tar.bz2/src/drivers/audio/AudioOutputDeviceCoreAudio.h -> linuxsampler-2718.tar.bz2/src/drivers/audio/AudioOutputDeviceCoreAudio.h
Changed
@@ -60,13 +60,13 @@ virtual void DeviceChanged(); // from CAAudioDeviceModelListener // derived abstract methods from class 'AudioOutputDevice' - virtual void Play(); - virtual bool IsPlaying(); - virtual void Stop(); - virtual uint MaxSamplesPerCycle(); - virtual uint SampleRate(); - virtual String Driver(); - virtual AudioChannel* CreateChannel(uint ChannelNr); + virtual void Play() OVERRIDE; + virtual bool IsPlaying() OVERRIDE; + virtual void Stop() OVERRIDE; + virtual uint MaxSamplesPerCycle() OVERRIDE; + virtual uint SampleRate() OVERRIDE; + virtual String Driver() OVERRIDE; + virtual AudioChannel* CreateChannel(uint ChannelNr) OVERRIDE; static String Name(); static String Description(); @@ -83,13 +83,13 @@ public: ParameterDevice(); ParameterDevice(String s) throw (Exception); - virtual String Description(); - virtual bool Fix(); - virtual bool Mandatory(); - virtual std::map<String,DeviceCreationParameter*> DependsAsParameters(); - virtual optional<String> DefaultAsString(std::map<String,String> Parameters); - virtual std::vector<String> PossibilitiesAsString(std::map<String,String> Parameters); - virtual void OnSetValue(String s) throw (Exception); + virtual String Description() OVERRIDE; + virtual bool Fix() OVERRIDE; + virtual bool Mandatory() OVERRIDE; + virtual std::map<String,DeviceCreationParameter*> DependsAsParameters() OVERRIDE; + virtual optional<String> DefaultAsString(std::map<String,String> Parameters) OVERRIDE; + virtual std::vector<String> PossibilitiesAsString(std::map<String,String> Parameters) OVERRIDE; + virtual void OnSetValue(String s) throw (Exception) OVERRIDE; int GetDeviceIndex(); static String Name(); @@ -105,9 +105,9 @@ public: ParameterSampleRate(); ParameterSampleRate(String s); - virtual std::map<String,DeviceCreationParameter*> DependsAsParameters(); - virtual optional<int> DefaultAsInt(std::map<String,String> Parameters); - virtual std::vector<int> PossibilitiesAsInt(std::map<String,String> Parameters); + virtual std::map<String,DeviceCreationParameter*> DependsAsParameters() OVERRIDE; + virtual optional<int> DefaultAsInt(std::map<String,String> Parameters) OVERRIDE; + virtual std::vector<int> PossibilitiesAsInt(std::map<String,String> Parameters) OVERRIDE; }; /** Device Parameters 'CHANNELS' @@ -119,8 +119,8 @@ public: ParameterChannels(); ParameterChannels(String s); - virtual optional<int> DefaultAsInt(std::map<String,String> Parameters); - virtual std::vector<int> PossibilitiesAsInt(std::map<String,String> Parameters); + virtual optional<int> DefaultAsInt(std::map<String,String> Parameters) OVERRIDE; + virtual std::vector<int> PossibilitiesAsInt(std::map<String,String> Parameters) OVERRIDE; }; /** Device Parameter 'BUFFERS' @@ -131,15 +131,15 @@ public: ParameterBuffers(); ParameterBuffers(String s) throw (Exception); - virtual String Description(); - virtual bool Fix(); - virtual bool Mandatory(); - virtual std::map<String,DeviceCreationParameter*> DependsAsParameters(); - virtual optional<int> DefaultAsInt(std::map<String,String> Parameters); - virtual optional<int> RangeMinAsInt(std::map<String,String> Parameters); - virtual optional<int> RangeMaxAsInt(std::map<String,String> Parameters); - virtual std::vector<int> PossibilitiesAsInt(std::map<String,String> Parameters); - virtual void OnSetValue(int i) throw (Exception); + virtual String Description() OVERRIDE; + virtual bool Fix() OVERRIDE; + virtual bool Mandatory() OVERRIDE; + virtual std::map<String,DeviceCreationParameter*> DependsAsParameters() OVERRIDE; + virtual optional<int> DefaultAsInt(std::map<String,String> Parameters) OVERRIDE; + virtual optional<int> RangeMinAsInt(std::map<String,String> Parameters) OVERRIDE; + virtual optional<int> RangeMaxAsInt(std::map<String,String> Parameters) OVERRIDE; + virtual std::vector<int> PossibilitiesAsInt(std::map<String,String> Parameters) OVERRIDE; + virtual void OnSetValue(int i) throw (Exception) OVERRIDE; static String Name(); }; @@ -151,20 +151,20 @@ public: ParameterBufferSize(); ParameterBufferSize(String s) throw (Exception); - virtual String Description(); - virtual bool Fix(); - virtual bool Mandatory(); - virtual std::map<String,DeviceCreationParameter*> DependsAsParameters(); - virtual optional<int> DefaultAsInt(std::map<String,String> Parameters); - virtual optional<int> RangeMinAsInt(std::map<String,String> Parameters); - virtual optional<int> RangeMaxAsInt(std::map<String,String> Parameters); - virtual std::vector<int> PossibilitiesAsInt(std::map<String,String> Parameters); - virtual void OnSetValue(int i) throw (Exception); + virtual String Description() OVERRIDE; + virtual bool Fix() OVERRIDE; + virtual bool Mandatory() OVERRIDE; + virtual std::map<String,DeviceCreationParameter*> DependsAsParameters() OVERRIDE; + virtual optional<int> DefaultAsInt(std::map<String,String> Parameters) OVERRIDE; + virtual optional<int> RangeMinAsInt(std::map<String,String> Parameters) OVERRIDE; + virtual optional<int> RangeMaxAsInt(std::map<String,String> Parameters) OVERRIDE; + virtual std::vector<int> PossibilitiesAsInt(std::map<String,String> Parameters) OVERRIDE; + virtual void OnSetValue(int i) throw (Exception) OVERRIDE; static String Name(); }; protected: - int Main(); ///< Implementation of virtual method from class Thread + virtual int Main() OVERRIDE; ///< Implementation of virtual method from class Thread private: CAAudioDeviceModel CurrentDevice;
View file
linuxsampler-2342.tar.bz2/src/drivers/audio/AudioOutputDeviceFactory.h -> linuxsampler-2718.tar.bz2/src/drivers/audio/AudioOutputDeviceFactory.h
Changed
@@ -3,7 +3,7 @@ * LinuxSampler - modular, streaming capable sampler * * * * Copyright (C) 2003, 2004 by Benno Senoner and Christian Schoenebeck * - * Copyright (C) 2005 - 2009 Christian Schoenebeck * + * Copyright (C) 2005 - 2014 Christian Schoenebeck * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -42,6 +42,7 @@ public: class InnerFactory { public: + virtual ~InnerFactory() {} virtual AudioOutputDevice* Create(std::map<String,DeviceCreationParameter*> Parameters) = 0; virtual String Description() = 0; virtual String Version() = 0;
View file
linuxsampler-2342.tar.bz2/src/drivers/audio/AudioOutputDeviceJack.cpp -> linuxsampler-2718.tar.bz2/src/drivers/audio/AudioOutputDeviceJack.cpp
Changed
@@ -3,7 +3,7 @@ * LinuxSampler - modular, streaming capable sampler * * * * Copyright (C) 2003, 2004 by Benno Senoner and Christian Schoenebeck * - * Copyright (C) 2005 - 2008 Christian Schoenebeck * + * Copyright (C) 2005 - 2014 Christian Schoenebeck * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -80,7 +80,7 @@ // disconnect all current bindings first for (int i = 0; i < Bindings.size(); i++) { String dst_name = Bindingsi; - int res = jack_disconnect(pChannel->pDevice->hJackClient, src_name.c_str(), dst_name.c_str()); + /*int res =*/ jack_disconnect(pChannel->pDevice->hJackClient, src_name.c_str(), dst_name.c_str()); } // connect new bindings for (int i = 0; i < vS.size(); i++) { @@ -120,6 +120,12 @@ if (!hJackPort) throw AudioOutputException("Jack: Cannot register Jack output port."); return (float*) jack_port_get_buffer(hJackPort, pDevice->uiMaxSamplesPerCycle); } + + void AudioOutputDeviceJack::AudioChannelJack::UpdateJackBuffer(uint size) { + SetBuffer( + (float*)jack_port_get_buffer(hJackPort, size) + ); + } @@ -167,6 +173,40 @@ +// *************** AudioOutputDeviceJack::ParameterSampleRate *************** +// * + + AudioOutputDeviceJack::ParameterSampleRate::ParameterSampleRate() : AudioOutputDevice::ParameterSampleRate() { + } + + AudioOutputDeviceJack::ParameterSampleRate::ParameterSampleRate(String s) : AudioOutputDevice::ParameterSampleRate(s) { + } + + optional<int> AudioOutputDeviceJack::ParameterSampleRate::DefaultAsInt(std::map<String,String> Parameters) { + static int i = 0; + // arbitrary JACK client name, that shall not clash with other ones + String name = "LinSmPSR" + ToString(i++); +#if HAVE_JACK_CLIENT_OPEN + jack_client_t* hClient = jack_client_open(name.c_str(), JackNullOption, NULL); +#else + jack_client_t* hClient = jack_client_new(name.c_str()); +#endif + // Better return "nothing" instead of i.e. 44100 here, so a frontend + // like QSampler will not be tempted to pass SAMPLERATE as creation + // parameter when creating a new JACK audio device instance. + if (!hClient) return optional<int>::nothing; + int sampleRate = jack_get_sample_rate(hClient); + jack_client_close(hClient); + return sampleRate; + } + + void AudioOutputDeviceJack::ParameterSampleRate::OnSetValue(int i) throw (Exception) { + // nothing to do: the JACK API does currently not allow JACK clients to + // change the sample rate + } + + + // *************** AudioOutputDeviceJack *************** // * @@ -178,7 +218,7 @@ * @see AcquireChannels() */ AudioOutputDeviceJack::AudioOutputDeviceJack(std::map<String,DeviceCreationParameter*> Parameters) : AudioOutputDevice(Parameters) { - JackClient* pJackClient = JackClient::CreateAudio(((DeviceCreationParameterString*)Parameters"NAME")->ValueAsString()); + pJackClient = JackClient::CreateAudio(((DeviceCreationParameterString*)Parameters"NAME")->ValueAsString()); existingJackDevices++; pJackClient->SetAudioOutputDevice(this); hJackClient = pJackClient->hJackClient; @@ -203,6 +243,11 @@ */ int AudioOutputDeviceJack::Process(uint Samples) { int res; + + // in recent versions of JACK2 and JACk1, we are forced to + // re-retrieve the audio buffer pointers in each process period + UpdateJackBuffers(Samples); + if (csIsPlaying.Pop()) { // let all connected engines render 'Samples' sample points res = RenderAudio(Samples); @@ -214,6 +259,22 @@ csIsPlaying.RttDone(); return res; } + + void AudioOutputDeviceJack::UpdateJackBuffers(uint size) { + for (int i = 0; i < Channels.size(); ++i) + static_cast<AudioChannelJack*>(Channelsi)->UpdateJackBuffer(size); + } + + float AudioOutputDeviceJack::latency() { + if (!hJackClient) return -1; + const float size = jack_get_buffer_size(hJackClient); + const float rate = jack_get_sample_rate(hJackClient); + return size / rate; + } + + jack_client_t* AudioOutputDeviceJack::jackClientHandle() { + return hJackClient; + } void AudioOutputDeviceJack::Play() { csIsPlaying.PushAndUnlock(true); @@ -238,6 +299,10 @@ uint AudioOutputDeviceJack::SampleRate() { return jack_get_sample_rate(hJackClient); } + + void AudioOutputDeviceJack::addListener(JackListener* listener) { + pJackClient->addListener(listener); + } String AudioOutputDeviceJack::Name() { return "JACK"; @@ -252,7 +317,7 @@ } String AudioOutputDeviceJack::Version() { - String s = "$Revision: 1.25 $"; + String s = "$Revision: 2512 $"; return s.substr(11, s.size() - 13); // cut dollar signs, spaces and CVS macro keyword } @@ -267,9 +332,48 @@ return static_cast<JackClient*>(arg)->Process(nframes); } - void linuxsampler_libjack_shutdown_callback(void* arg) { - static_cast<JackClient*>(arg)->Stop(); +#if HAVE_JACK_ON_INFO_SHUTDOWN + void JackClient::libjackShutdownCallback(jack_status_t code, const char* reason, void *arg) +#else + void JackClient::libjackShutdownCallback(void *arg) +#endif + { + JackClient* jackClient = static_cast<JackClient*>(arg); + jackClient->Stop(); fprintf(stderr, "Jack: Jack server shutdown, exiting.\n"); + for (int i = 0; i < jackClient->jackListeners.size(); ++i) { + JackListener* listener = jackClient->jackListenersi; +#if HAVE_JACK_ON_INFO_SHUTDOWN + listener->onJackShutdown(code, reason); +#else + listener->onJackShutdown(JackFailure, "unknown"); +#endif + } + } + + int JackClient::libjackSampleRateCallback(jack_nframes_t nframes, void *arg) { + JackClient* client = static_cast<JackClient*>(arg); + const config_t& config = client->ConfigReader.Lock(); + if (config.AudioDevice) + config.AudioDevice->ReconnectAll(); + client->ConfigReader.Unlock(); + return 0; + } + + int JackClient::libjackBufferSizeCallback(jack_nframes_t nframes, void *arg) { + dmsg(1,("libjackBufferSizeCallback(%d)\n",nframes)); + JackClient* client = static_cast<JackClient*>(arg); + const config_t& config = client->ConfigReader.Lock(); + if (config.AudioDevice) { + config.AudioDevice->UpdateJackBuffers(nframes); + config.AudioDevice->ReconnectAll(); + } + client->ConfigReader.Unlock(); + return 0; + } + + void JackClient::addListener(JackListener* listener) { + jackListeners.push_back(listener); } std::map<String, JackClient*> JackClient::Clients; @@ -316,7 +420,14 @@ if (!hJackClient) throw Exception("Seems Jack server is not running."); jack_set_process_callback(hJackClient, linuxsampler_libjack_process_callback, this); - jack_on_shutdown(hJackClient, linuxsampler_libjack_shutdown_callback, this); +#if HAVE_JACK_ON_INFO_SHUTDOWN + jack_on_info_shutdown(hJackClient, libjackShutdownCallback, this); +#else + jack_on_shutdown(hJackClient, libjackShutdownCallback, this); +#endif + jack_set_buffer_size_callback(hJackClient, libjackBufferSizeCallback, this); + jack_set_sample_rate_callback(hJackClient, libjackSampleRateCallback, this); + if (jack_activate(hJackClient)) throw Exception("Jack: Cannot activate Jack client."); }
View file
linuxsampler-2342.tar.bz2/src/drivers/audio/AudioOutputDeviceJack.h -> linuxsampler-2718.tar.bz2/src/drivers/audio/AudioOutputDeviceJack.h
Changed
@@ -3,7 +3,7 @@ * LinuxSampler - modular, streaming capable sampler * * * * Copyright (C) 2003, 2004 by Benno Senoner and Christian Schoenebeck * - * Copyright (C) 2005 - 2008 Christian Schoenebeck * + * Copyright (C) 2005 - 2014 Christian Schoenebeck * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -40,6 +40,9 @@ #endif namespace LinuxSampler { + + class JackClient; + class JackListener; /** JACK audio output driver * @@ -62,7 +65,7 @@ class ParameterName : public AudioChannel::ParameterName { public: ParameterName(AudioChannelJack* pChannel); - virtual void OnSetValue(String s); + virtual void OnSetValue(String s) OVERRIDE; protected: AudioChannelJack* pChannel; }; @@ -74,10 +77,10 @@ class ParameterJackBindings : public DeviceRuntimeParameterStrings { public: ParameterJackBindings(AudioChannelJack* pChannel); - virtual String Description(); - virtual bool Fix(); - virtual std::vector<String> PossibilitiesAsString(); - virtual void OnSetValue(std::vector<String> vS); + virtual String Description() OVERRIDE; + virtual bool Fix() OVERRIDE; + virtual std::vector<String> PossibilitiesAsString() OVERRIDE; + virtual void OnSetValue(std::vector<String> vS) OVERRIDE; static String Name(); protected: AudioChannelJack* pChannel; @@ -86,6 +89,7 @@ protected: AudioChannelJack(uint ChannelNr, AudioOutputDeviceJack* pDevice) throw (AudioOutputException); ~AudioChannelJack(); + void UpdateJackBuffer(uint size); friend class AudioOutputDeviceJack; private: AudioOutputDeviceJack* pDevice; @@ -104,45 +108,68 @@ public: ParameterName(); ParameterName(String s) throw (Exception); - virtual String Description(); - virtual bool Fix(); - virtual bool Mandatory(); - virtual std::map<String,DeviceCreationParameter*> DependsAsParameters(); - virtual std::vector<String> PossibilitiesAsString(std::map<String,String> Parameters); - virtual optional<String> DefaultAsString(std::map<String,String> Parameters); - virtual void OnSetValue(String s) throw (Exception); + virtual String Description() OVERRIDE; + virtual bool Fix() OVERRIDE; + virtual bool Mandatory() OVERRIDE; + virtual std::map<String,DeviceCreationParameter*> DependsAsParameters() OVERRIDE; + virtual std::vector<String> PossibilitiesAsString(std::map<String,String> Parameters) OVERRIDE; + virtual optional<String> DefaultAsString(std::map<String,String> Parameters) OVERRIDE; + virtual void OnSetValue(String s) throw (Exception) OVERRIDE; static String Name(); }; - // derived abstract methods from class 'AudioOutputDevice' - virtual void Play(); - virtual bool IsPlaying(); - virtual void Stop(); - virtual uint MaxSamplesPerCycle(); - virtual uint SampleRate(); - virtual AudioChannel* CreateChannel(uint ChannelNr); - + /** Audio Device Parameter 'SAMPLERATE' + * + * Used to retrieve the sample rate of the JACK audio output + * device. Even though the sample rate of the JACK server might + * change during runtime, the JACK API currently however does not + * allow clients to change the sample rate. So this parameter is + * read only. + * + * This base parameter class has just been overridden for this JACK + * driver to implement a valid default value for sample rate. The + * default value will simply return the sample rate of the currently + * running JACK server. It will return "nothing" if the JACK server + * is not running at that point. + */ + class ParameterSampleRate : public AudioOutputDevice::ParameterSampleRate { + public: + ParameterSampleRate(); + ParameterSampleRate(String s); + virtual optional<int> DefaultAsInt(std::map<String,String> Parameters) OVERRIDE; + virtual void OnSetValue(int i) throw (Exception) OVERRIDE; + }; + // derived abstract methods from class 'AudioOutputDevice' + virtual void Play() OVERRIDE; + virtual bool IsPlaying() OVERRIDE; + virtual void Stop() OVERRIDE; + virtual uint MaxSamplesPerCycle() OVERRIDE; + virtual uint SampleRate() OVERRIDE; + virtual AudioChannel* CreateChannel(uint ChannelNr) OVERRIDE; + virtual String Driver() OVERRIDE; + virtual float latency() OVERRIDE; + static String Name(); - virtual String Driver(); - static String Description(); static String Version(); int Process(uint Samples); // FIXME: should be private + void UpdateJackBuffers(uint size); + void addListener(JackListener* listener); + jack_client_t* jackClientHandle(); protected: AudioOutputDeviceJack(String* AutoConnectPortIDs = NULL, uint AutoConnectPorts = 0); private: ConditionServer csIsPlaying; uint uiMaxSamplesPerCycle; jack_client_t* hJackClient; + JackClient* pJackClient; }; // Callback functions for the libjack API int linuxsampler_libjack_process_callback(jack_nframes_t nframes, void* arg); - void linuxsampler_libjack_shutdown_callback(void* arg); - /** JACK client * @@ -164,10 +191,12 @@ #if HAVE_JACK_MIDI void SetMidiInputDevice(MidiInputDeviceJack* device); #endif + void addListener(JackListener* listener); jack_client_t* hJackClient; private: + std::vector<JackListener*> jackListeners; static std::map<String, JackClient*> Clients; struct config_t { AudioOutputDeviceJack* AudioDevice; @@ -182,6 +211,28 @@ JackClient(String Name); ~JackClient(); + + // Callback functions for the libjack API +#if HAVE_JACK_ON_INFO_SHUTDOWN + static void libjackShutdownCallback(jack_status_t code, const char* reason, void *arg); +#else + static void libjackShutdownCallback(void *arg); +#endif + static int libjackSampleRateCallback(jack_nframes_t nframes, void *arg); + static int libjackBufferSizeCallback(jack_nframes_t nframes, void *arg); + }; + + /** + * Currently not derived / instantiated by the sampler itself, however this + * class can be subclassed and used i.e. by a GUI build on top of the sampler, + * to react on JACK events. Because registering JACK callback functions through + * the general JACK API is not possible after the JACK client has been activated, + * and the latter is already the case as soon as an AudioOutputDeviceJack object + * has been instantiated. + */ + class JackListener { + public: + virtual void onJackShutdown(jack_status_t code, const char* reason) = 0; }; }
View file
linuxsampler-2342.tar.bz2/src/drivers/audio/AudioOutputDevicePlugin.cpp -> linuxsampler-2718.tar.bz2/src/drivers/audio/AudioOutputDevicePlugin.cpp
Changed
@@ -1,6 +1,6 @@ /*************************************************************************** * * - * Copyright (C) 2008 - 2009 Andreas Persson * + * Copyright (C) 2008 - 2012 Andreas Persson * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -19,6 +19,8 @@ ***************************************************************************/ #include "AudioOutputDevicePlugin.h" +#include "../../common/global_private.h" +#include <algorithm> namespace LinuxSampler { @@ -30,6 +32,15 @@ } +// *************** ParameterChannelsPlugin *************** +// * + + void AudioOutputDevicePlugin::ParameterChannelsPlugin::ForceSetValue(int channels) { + OnSetValue(channels); + iVal = channels; + } + + // *************** ParameterFragmentSize *************** // * @@ -123,7 +134,7 @@ } String AudioOutputDevicePlugin::Version() { - String s = "$Revision: 1.2 $"; + String s = "$Revision: 2494 $"; return s.substr(11, s.size() - 13); // cut dollar signs, spaces and CVS macro keyword } @@ -142,4 +153,24 @@ bool AudioOutputDevicePlugin::isAutonomousDriver() { return false; } + + + void AudioOutputDevicePlugin::AddChannels(int newChannels) { + static_cast<ParameterChannelsPlugin*>( + Parameters"CHANNELS")->ForceSetValue(Channels.size() + newChannels); + } + + void AudioOutputDevicePlugin::RemoveChannel(AudioChannel* pChannel) { + std::vector<AudioChannel*>::iterator i = find(Channels.begin(), Channels.end(), pChannel); + int channelNumber = i - Channels.begin(); + delete *i; + Channels.erase(i); + + for ( ; channelNumber < Channels.size() ; channelNumber++) { + static_cast<AudioChannelPlugin*>(ChannelschannelNumber)->ChannelNr = channelNumber; + ChannelschannelNumber->ChannelParameters()"NAME"->SetValue("Channel " + ToString(channelNumber)); + } + static_cast<ParameterChannelsPlugin*>( + Parameters"CHANNELS")->ForceSetValue(Channels.size()); + } }
View file
linuxsampler-2342.tar.bz2/src/drivers/audio/AudioOutputDevicePlugin.h -> linuxsampler-2718.tar.bz2/src/drivers/audio/AudioOutputDevicePlugin.h
Changed
@@ -1,6 +1,6 @@ /*************************************************************************** * * - * Copyright (C) 2008 - 2009 Andreas Persson * + * Copyright (C) 2008 - 2013 Andreas Persson * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -54,7 +54,8 @@ public: ParameterChannelsPlugin() : ParameterChannels() { } ParameterChannelsPlugin(String s) : ParameterChannels(s) { } - virtual bool Fix() { return true; } + virtual bool Fix() OVERRIDE { return true; } + void ForceSetValue(int channels); }; /** @@ -66,27 +67,27 @@ public: ParameterFragmentSize(); ParameterFragmentSize(String s) throw (Exception); - String Description(); - bool Fix(); - bool Mandatory(); - std::map<String,DeviceCreationParameter*> DependsAsParameters(); - optional<int> DefaultAsInt(std::map<String,String> Parameters); - optional<int> RangeMinAsInt(std::map<String,String> Parameters); - optional<int> RangeMaxAsInt(std::map<String,String> Parameters); - std::vector<int> PossibilitiesAsInt(std::map<String,String> Parameters); - void OnSetValue(int i) throw (Exception); + String Description() OVERRIDE; + bool Fix() OVERRIDE; + bool Mandatory() OVERRIDE; + std::map<String,DeviceCreationParameter*> DependsAsParameters() OVERRIDE; + optional<int> DefaultAsInt(std::map<String,String> Parameters) OVERRIDE; + optional<int> RangeMinAsInt(std::map<String,String> Parameters) OVERRIDE; + optional<int> RangeMaxAsInt(std::map<String,String> Parameters) OVERRIDE; + std::vector<int> PossibilitiesAsInt(std::map<String,String> Parameters) OVERRIDE; + void OnSetValue(int i) throw (Exception) OVERRIDE; static String Name(); }; // derived abstract methods from class 'AudioOutputDevice' - void Play(); - bool IsPlaying(); - void Stop(); - uint MaxSamplesPerCycle(); - uint SampleRate(); - String Driver(); - AudioChannel* CreateChannel(uint ChannelNr); - bool isAutonomousDevice(); + void Play() OVERRIDE; + bool IsPlaying() OVERRIDE; + void Stop() OVERRIDE; + uint MaxSamplesPerCycle() OVERRIDE; + uint SampleRate() OVERRIDE; + String Driver() OVERRIDE; + AudioChannel* CreateChannel(uint ChannelNr) OVERRIDE; + bool isAutonomousDevice() OVERRIDE; static String Name(); static String Version(); static String Description(); @@ -103,6 +104,9 @@ */ int Render(uint Samples) { return RenderAudio(Samples); } + void AddChannels(int newChannels); + void RemoveChannel(AudioChannel* pChannel); + private: uint uiSampleRate; uint uiMaxSamplesPerCycle;
View file
linuxsampler-2342.tar.bz2/src/drivers/audio/CAAudioDeviceModel.cpp -> linuxsampler-2718.tar.bz2/src/drivers/audio/CAAudioDeviceModel.cpp
Changed
@@ -2,7 +2,7 @@ * * * LinuxSampler - modular, streaming capable sampler * * * - * Copyright (C) 2009 Grigor Iliev * + * Copyright (C) 2009 - 2013 Grigor Iliev * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -247,27 +247,24 @@ } void CAAudioDeviceListModel::FireDeviceChangedEvent(AudioDeviceID devID) { - DeviceMutex.Lock(); + LockGuard lock(DeviceMutex); for(int i = 0; i < GetListenerCount(); i++) { GetListener(i)->DeviceChanged(devID); } - DeviceMutex.Unlock(); } void CAAudioDeviceListModel::FireDeviceListChangedEvent() { - DeviceMutex.Lock(); + LockGuard lock(DeviceMutex); for(int i = 0; i < GetListenerCount(); i++) { GetListener(i)->DeviceListChanged(); } - DeviceMutex.Unlock(); } void CAAudioDeviceListModel::FireDefaultOutputDeviceChangedEvent(AudioDeviceID newID) { - DeviceMutex.Lock(); + LockGuard lock(DeviceMutex); for(int i = 0; i < GetListenerCount(); i++) { GetListener(i)->DefaultOutputDeviceChanged(newID); } - DeviceMutex.Unlock(); } AudioDeviceID CAAudioDeviceListModel::GetDefaultOutputDeviceID() { @@ -275,51 +272,42 @@ } UInt32 CAAudioDeviceListModel::GetOutputDeviceCount() { - DeviceMutex.Lock(); - int size = outDevices.size(); - DeviceMutex.Unlock(); - return size; + LockGuard lock(DeviceMutex); + return outDevices.size(); } CAAudioDeviceModel CAAudioDeviceListModel::GetOutputDevice(UInt32 Index) { - DeviceMutex.Lock(); + LockGuard lock(DeviceMutex); if(Index < 0 || Index >= GetOutputDeviceCount()) { - DeviceMutex.Unlock(); throw Exception("Device index out of bounds"); } - CAAudioDeviceModel dev = outDevicesIndex; - DeviceMutex.Unlock(); - return dev; + return outDevicesIndex; } CAAudioDeviceModel CAAudioDeviceListModel::GetOutputDeviceByID(AudioDeviceID devID) { - DeviceMutex.Lock(); + LockGuard lock(DeviceMutex); for(int i = 0; i < outDevices.size(); i++) { if(outDevicesi.GetID() == devID) { CAAudioDeviceModel dev = outDevicesi; - DeviceMutex.Unlock(); return dev; } } - DeviceMutex.Unlock(); throw Exception("Unknown audio device ID: " + ToString(devID)); } UInt32 CAAudioDeviceListModel::GetOutputDeviceIndex(AudioDeviceID devID) { - DeviceMutex.Lock(); + LockGuard lock(DeviceMutex); for(UInt32 i = 0; i < outDevices.size(); i++) { if(outDevicesi.GetID() == devID) { - DeviceMutex.Unlock(); return i; } } - DeviceMutex.Unlock(); throw Exception("Unknown audio device ID: " + ToString(devID)); } void CAAudioDeviceListModel::RescanDevices() { - DeviceMutex.Lock(); + LockGuard lock(DeviceMutex); inDevices.clear(); outDevices.clear(); @@ -334,20 +322,17 @@ if(res) { std::cerr << "Failed to get device list: " << res << std::endl; - DeviceMutex.Unlock(); return; } UInt32 deviceNumber = outSize / sizeof(AudioDeviceID); if(deviceNumber < 1) { std::cerr << "No audio devices found" << std::endl; - DeviceMutex.Unlock(); return; } if(deviceNumber * sizeof(AudioDeviceID) != outSize) { std::cerr << "Invalid device size. This is a bug!" << std::endl; - DeviceMutex.Unlock(); return; } @@ -360,7 +345,6 @@ if(res) { std::cerr << "Failed to get device IDs: " << res << std::endl; delete devs; - DeviceMutex.Unlock(); return; } @@ -430,7 +414,6 @@ } std::cout << std::endl; }*/ - DeviceMutex.Unlock(); } void CAAudioDeviceListModel::UpdateDefaultOutputDevice() { @@ -470,7 +453,7 @@ &outSize, pBufferList ); - if(res = noErr) { + if(res) { std::cerr << "Failed to get channel number: " << res << std::endl; free(pBufferList); return;
View file
linuxsampler-2342.tar.bz2/src/drivers/audio/Makefile.am -> linuxsampler-2718.tar.bz2/src/drivers/audio/Makefile.am
Changed
@@ -32,12 +32,12 @@ if HAVE_ASIO asio_includes = -I@ASIOSDK_BASEDIR@/ASIOSDK2/common -I@ASIOSDK_BASEDIR@/ASIOSDK2/host -I@ASIOSDK_BASEDIR@/ASIOSDK2/host/pc -asio_3rd_party_src = @ASIOSDK_BASEDIR@/ASIOSDK2/common/asio.cpp @ASIOSDK_BASEDIR@/ASIOSDK2/common/asio.h @ASIOSDK_BASEDIR@/ASIOSDK2/host/asiodrivers.cpp @ASIOSDK_BASEDIR@/ASIOSDK2/host/asiodrivers.h @ASIOSDK_BASEDIR@/ASIOSDK2/host/pc/asiolist.cpp @ASIOSDK_BASEDIR@/ASIOSDK2/host/pc/asiolist.h -asio_src = AudioOutputDeviceAsio.cpp AudioOutputDeviceAsio.h iasiothiscallresolver.cpp iasiothiscallresolver.h +asio_src = AudioOutputDeviceAsio.cpp AudioOutputDeviceAsio.h \ + iasiothiscallresolver.cpp iasiothiscallresolver.h \ + asiosdk-asio.cpp asiosdk-asiodrivers.cpp asiosdk-asiolist.cpp asio_ladd = else asio_src = -asio_3rd_party_src = asio_includes = asio_ladd = endif @@ -57,7 +57,7 @@ AudioChannel.h \ AudioOutputDevice.h -INCLUDES = $(all_includes) $(arts_includes) $(asio_includes) $(jack_includes) +AM_CPPFLAGS = $(all_includes) $(arts_includes) $(asio_includes) $(jack_includes) noinst_LTLIBRARIES = liblinuxsampleraudiodriver.la liblinuxsampleraudiodriver_la_SOURCES = \ @@ -67,7 +67,5 @@ $(alsa_src) $(jack_src) $(arts_src) $(asio_src) $(coreaudio_src) \ AudioOutputDevicePlugin.cpp AudioOutputDevicePlugin.h -nodist_liblinuxsampleraudiodriver_la_SOURCES = \ - $(asio_3rd_party_src) liblinuxsampleraudiodriver_la_LIBADD = $(alsa_ladd) $(arts_ladd) liblinuxsampleraudiodriver_la_LDFLAGS = $(jack_lflags) $(coreaudio_ldflags)
View file
linuxsampler-2718.tar.bz2/src/drivers/audio/asiosdk-asio.cpp
Added
@@ -0,0 +1,1 @@ +#include "asio.cpp"
View file
linuxsampler-2718.tar.bz2/src/drivers/audio/asiosdk-asiodrivers.cpp
Added
@@ -0,0 +1,1 @@ +#include "asiodrivers.cpp"
View file
linuxsampler-2718.tar.bz2/src/drivers/audio/asiosdk-asiolist.cpp
Added
@@ -0,0 +1,1 @@ +#include "asiolist.cpp"
View file
linuxsampler-2342.tar.bz2/src/drivers/midi/Makefile.am -> linuxsampler-2718.tar.bz2/src/drivers/midi/Makefile.am
Changed
@@ -50,7 +50,7 @@ MidiInstrumentMapper.h \ VirtualMidiDevice.h -INCLUDES = $(all_includes) $(jackmidi_includes) $(GIG_CFLAGS) +AM_CPPFLAGS = $(all_includes) $(jackmidi_includes) $(GIG_CFLAGS) noinst_LTLIBRARIES = liblinuxsamplermididriver.la liblinuxsamplermididriver_la_SOURCES = \
View file
linuxsampler-2342.tar.bz2/src/drivers/midi/MidiInputDevice.cpp -> linuxsampler-2718.tar.bz2/src/drivers/midi/MidiInputDevice.cpp
Changed
@@ -3,7 +3,7 @@ * LinuxSampler - modular, streaming capable sampler * * * * Copyright (C) 2003, 2004 by Benno Senoner and Christian Schoenebeck * - * Copyright (C) 2005 - 2010 Christian Schoenebeck * + * Copyright (C) 2005 - 2014 Christian Schoenebeck * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -25,6 +25,7 @@ #include "../../common/global_private.h" #include "../../Sampler.h" +#include "MidiInputDeviceFactory.h" namespace LinuxSampler { @@ -118,15 +119,16 @@ std::map<uint, SamplerChannel*>::iterator iter = channels.begin(); for (; iter != channels.end(); iter++) { SamplerChannel* chn = iter->second; - if (chn->GetMidiInputDevice() == NULL || chn->GetMidiInputDevice() != pDevice) { - continue; - } - - int port = chn->GetMidiInputPort(); - if (port >= i) { - String err = "Sampler channel " + ToString(iter->first); - err += " is still connected to MIDI port " + ToString(port); - throw Exception(err); + std::vector<MidiInputPort*> vPorts = chn->GetMidiInputPorts(); + for (int k = 0; k < vPorts.size(); ++k) { + if (vPortsk->GetDevice() != pDevice) + continue; + int port = vPortsk->GetPortNumber(); + if (port >= i) { + String err = "Sampler channel " + ToString(iter->first); + err += " is still connected to MIDI port " + ToString(port); + throw Exception(err); + } } } @@ -169,6 +171,16 @@ return Parameters; } + int MidiInputDevice::MidiInputDeviceID() { + std::map<uint, MidiInputDevice*> mDevices = MidiInputDeviceFactory::Devices(); + for (std::map<uint, MidiInputDevice*>::const_iterator it = mDevices.begin(); it != mDevices.end(); ++it) { + if (it->second == this) { + return it->first; + } + } + return -1; + } + void MidiInputDevice::AddMidiPortCountListener(MidiPortCountListener* l) { portCountListeners.AddListener(l); }
View file
linuxsampler-2342.tar.bz2/src/drivers/midi/MidiInputDevice.h -> linuxsampler-2718.tar.bz2/src/drivers/midi/MidiInputDevice.h
Changed
@@ -3,7 +3,7 @@ * LinuxSampler - modular, streaming capable sampler * * * * Copyright (C) 2003, 2004 by Benno Senoner and Christian Schoenebeck * - * Copyright (C) 2005 - 2009 Christian Schoenebeck * + * Copyright (C) 2005 - 2014 Christian Schoenebeck * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -77,12 +77,12 @@ public: ParameterActive(); ParameterActive(String active); - virtual String Description(); - virtual bool Fix(); - virtual bool Mandatory(); - virtual std::map<String,DeviceCreationParameter*> DependsAsParameters(); - virtual optional<bool> DefaultAsBool(std::map<String,String> Parameters); - virtual void OnSetValue(bool b) throw (Exception); + virtual String Description() OVERRIDE; + virtual bool Fix() OVERRIDE; + virtual bool Mandatory() OVERRIDE; + virtual std::map<String,DeviceCreationParameter*> DependsAsParameters() OVERRIDE; + virtual optional<bool> DefaultAsBool(std::map<String,String> Parameters) OVERRIDE; + virtual void OnSetValue(bool b) throw (Exception) OVERRIDE; static String Name(); }; @@ -95,15 +95,15 @@ public: ParameterPorts(); ParameterPorts(String val); - virtual String Description(); - virtual bool Fix(); - virtual bool Mandatory(); - virtual std::map<String,DeviceCreationParameter*> DependsAsParameters(); - virtual optional<int> DefaultAsInt(std::map<String,String> Parameters); - virtual optional<int> RangeMinAsInt(std::map<String,String> Parameters); - virtual optional<int> RangeMaxAsInt(std::map<String,String> Parameters); - virtual std::vector<int> PossibilitiesAsInt(std::map<String,String> Parameters); - virtual void OnSetValue(int i) throw (Exception); + virtual String Description() OVERRIDE; + virtual bool Fix() OVERRIDE; + virtual bool Mandatory() OVERRIDE; + virtual std::map<String,DeviceCreationParameter*> DependsAsParameters() OVERRIDE; + virtual optional<int> DefaultAsInt(std::map<String,String> Parameters) OVERRIDE; + virtual optional<int> RangeMinAsInt(std::map<String,String> Parameters) OVERRIDE; + virtual optional<int> RangeMaxAsInt(std::map<String,String> Parameters) OVERRIDE; + virtual std::vector<int> PossibilitiesAsInt(std::map<String,String> Parameters) OVERRIDE; + virtual void OnSetValue(int i) throw (Exception) OVERRIDE; static String Name(); }; @@ -165,6 +165,17 @@ std::map<String,DeviceCreationParameter*> DeviceParameters(); /** + * Returns the unique ID number associated with this MIDIInputDevice + * instance. This ID number is unique among all MIDIInputDevice + * instances of the same Sampler instance and during the whole + * lifetime of the Sampler instance. + * + * @returns a value equal or larger than 0, a negative value only + * on severe internal problems + */ + int MidiInputDeviceID(); + + /** * Registers the specified listener to be notified * when the number of MIDI input ports is changed. */
View file
linuxsampler-2342.tar.bz2/src/drivers/midi/MidiInputDeviceAlsa.cpp -> linuxsampler-2718.tar.bz2/src/drivers/midi/MidiInputDeviceAlsa.cpp
Changed
@@ -3,7 +3,7 @@ * LinuxSampler - modular, streaming capable sampler * * * * Copyright (C) 2003, 2004 by Benno Senoner and Christian Schoenebeck * - * Copyright (C) 2005 - 2009 Christian Schoenebeck * + * Copyright (C) 2005 - 2014 Christian Schoenebeck * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -286,7 +286,7 @@ } String MidiInputDeviceAlsa::Version() { - String s = "$Revision: 1.24 $"; + String s = "$Revision: 2559 $"; return s.substr(11, s.size() - 13); // cut dollar signs, spaces and CVS macro keyword } @@ -315,7 +315,11 @@ break; case SND_SEQ_EVENT_CHANPRESS: - pMidiInputPort->DispatchControlChange(128, ev->data.control.value, ev->data.control.channel); + pMidiInputPort->DispatchChannelPressure(ev->data.control.value, ev->data.control.channel); + break; + + case SND_SEQ_EVENT_KEYPRESS: + pMidiInputPort->DispatchPolyphonicKeyPressure(ev->data.note.note, ev->data.note.velocity, ev->data.control.channel); break; case SND_SEQ_EVENT_PITCHBEND:
View file
linuxsampler-2342.tar.bz2/src/drivers/midi/MidiInputDeviceAlsa.h -> linuxsampler-2718.tar.bz2/src/drivers/midi/MidiInputDeviceAlsa.h
Changed
@@ -3,7 +3,7 @@ * LinuxSampler - modular, streaming capable sampler * * * * Copyright (C) 2003, 2004 by Benno Senoner and Christian Schoenebeck * - * Copyright (C) 2005 - 2009 Christian Schoenebeck * + * Copyright (C) 2005 - 2013 Christian Schoenebeck * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -52,7 +52,7 @@ class ParameterName : public MidiInputPort::ParameterName { public: ParameterName(MidiInputPort* pPort) throw (Exception); - virtual void OnSetValue(String s) throw (Exception); + virtual void OnSetValue(String s) throw (Exception) OVERRIDE; }; /** MIDI Port Parameter 'ALSA_SEQ_BINDINGS' @@ -62,10 +62,10 @@ class ParameterAlsaSeqBindings : public DeviceRuntimeParameterStrings { public: ParameterAlsaSeqBindings(MidiInputPortAlsa* pPort); - virtual String Description(); - virtual bool Fix(); - virtual std::vector<String> PossibilitiesAsString(); - virtual void OnSetValue(std::vector<String> vS) throw (Exception); + virtual String Description() OVERRIDE; + virtual bool Fix() OVERRIDE; + virtual std::vector<String> PossibilitiesAsString() OVERRIDE; + virtual void OnSetValue(std::vector<String> vS) throw (Exception) OVERRIDE; protected: MidiInputPortAlsa* pPort; }; @@ -78,10 +78,10 @@ class ParameterAlsaSeqId : public DeviceRuntimeParameterString { public: ParameterAlsaSeqId(MidiInputPortAlsa* pPort); - virtual String Description(); - virtual bool Fix(); - virtual std::vector<String> PossibilitiesAsString(); - virtual void OnSetValue(String s); + virtual String Description() OVERRIDE; + virtual bool Fix() OVERRIDE; + virtual std::vector<String> PossibilitiesAsString() OVERRIDE; + virtual void OnSetValue(String s) OVERRIDE; }; void ConnectToAlsaMidiSource(const char* MidiSource); @@ -108,13 +108,13 @@ public: ParameterName(); ParameterName(String s); - virtual String Description(); - virtual bool Fix(); - virtual bool Mandatory(); - virtual std::map<String,DeviceCreationParameter*> DependsAsParameters(); - virtual std::vector<String> PossibilitiesAsString(std::map<String,String> Parameters); - virtual optional<String> DefaultAsString(std::map<String,String> Parameters); - virtual void OnSetValue(String s) throw (Exception); + virtual String Description() OVERRIDE; + virtual bool Fix() OVERRIDE; + virtual bool Mandatory() OVERRIDE; + virtual std::map<String,DeviceCreationParameter*> DependsAsParameters() OVERRIDE; + virtual std::vector<String> PossibilitiesAsString(std::map<String,String> Parameters) OVERRIDE; + virtual optional<String> DefaultAsString(std::map<String,String> Parameters) OVERRIDE; + virtual void OnSetValue(String s) throw (Exception) OVERRIDE; static String Name(); }; @@ -122,9 +122,9 @@ ~MidiInputDeviceAlsa(); // derived abstract methods from class 'MidiInputDevice' - void Listen(); - void StopListen(); - virtual String Driver(); + void Listen() OVERRIDE; + void StopListen() OVERRIDE; + virtual String Driver() OVERRIDE; static String Name(); static String Description(); static String Version();
View file
linuxsampler-2342.tar.bz2/src/drivers/midi/MidiInputDeviceCoreMidi.cpp -> linuxsampler-2718.tar.bz2/src/drivers/midi/MidiInputDeviceCoreMidi.cpp
Changed
@@ -1,7 +1,7 @@ /*************************************************************************** * * * Copyright (C) 2004, 2005 Grame * - * Copyright (C) 2005 - 2009 Christian Schoenebeck * + * Copyright (C) 2005 - 2014 Christian Schoenebeck * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -23,19 +23,48 @@ #include "MidiInputDeviceFactory.h" namespace LinuxSampler { - - int MidiInputDeviceCoreMidi::MidiInputPortCoreMidi::pPortID = 0; + +// *************** privat static functions *************** +// * + + static void _bridgeInputEvent(const MIDIPacketList* packetList, void* /*device*/, void* port) { + //MidiInputDeviceCoreMidi* pDevice = (MidiInputDeviceCoreMidi*) device; + MidiInputDeviceCoreMidi::MidiInputPortCoreMidi* pPort = (MidiInputDeviceCoreMidi::MidiInputPortCoreMidi*) port; + pPort->ProcessMidiEvents(packetList); + } + + static String _getDisplayName(MIDIObjectRef object) { + CFStringRef name = nil; + if (MIDIObjectGetStringProperty(object, kMIDIPropertyDisplayName, &name) != noErr) { + dmsg(1,("CoreMIDI: could not resolve display name of object\n")); + return ""; + } + // convert NSString to C string + char* buf = new char256; + if (!CFStringGetCString(name, buf, 256, kCFStringEncodingUTF8)) { + dmsg(1,("CoreMIDI: could not convert display name string\n")); + delete buf; + return ""; + } + String result = buf; + delete buf; + return result; + } + + +// *************** ParameterName *************** +// * MidiInputDeviceCoreMidi::MidiInputPortCoreMidi::ParameterName::ParameterName(MidiInputPort* pPort) throw (Exception) : MidiInputPort::ParameterName(pPort, "Port " + ToString(pPort->GetPortNumber())) { OnSetValue(ValueAsString()); // initialize port name } void MidiInputDeviceCoreMidi::MidiInputPortCoreMidi::ParameterName::OnSetValue(String s) throw (Exception) { - + //TODO: renaming of port to be implemented } - // *************** ParameterCoreMidiBindings *************** - // * +// *************** ParameterCoreMidiBindings *************** +// * MidiInputDeviceCoreMidi::MidiInputPortCoreMidi::ParameterCoreMidiBindings::ParameterCoreMidiBindings(MidiInputPortCoreMidi* pPort) : DeviceRuntimeParameterStrings( std::vector<String>() ) { this->pPort = pPort; @@ -50,19 +79,65 @@ std::vector<String> MidiInputDeviceCoreMidi::MidiInputPortCoreMidi::ParameterCoreMidiBindings::PossibilitiesAsString() { std::vector<String> res; - // Connections + + const ItemCount sourceCount = MIDIGetNumberOfSources(); + for (ItemCount i = 0; i < sourceCount; ++i) { + MIDIEndpointRef source = MIDIGetSource(i); + res.push_back( + _getDisplayName(source) + ); + } + return res; } void MidiInputDeviceCoreMidi::MidiInputPortCoreMidi::ParameterCoreMidiBindings::OnSetValue(std::vector<String> vS) throw (Exception) { - // to finish + for (int k = 0; k < vS.size(); ++k) { + const ItemCount sourceCount = MIDIGetNumberOfSources(); + for (ItemCount i = 0; i < sourceCount; ++i) { + MIDIEndpointRef source = MIDIGetSource(i); + String name = _getDisplayName(source); + if (name == vSk) { + pPort->connectToSource(source); + goto matchFound; + } + } + throw MidiInputException("No CoreMIDI source '" + vSk + "' found to connect to"); + matchFound: + ; // noop + } } + +// *************** ParameterAutoBind *************** +// * + + MidiInputDeviceCoreMidi::MidiInputPortCoreMidi::ParameterAutoBind::ParameterAutoBind(MidiInputPortCoreMidi* pPort) + : DeviceRuntimeParameterBool(true) + { + this->pPort = pPort; + } + + String MidiInputDeviceCoreMidi::MidiInputPortCoreMidi::ParameterAutoBind::Description() { + return "Whether port shall automatically be connected to all CoreMIDI source endpoints."; + } + + bool MidiInputDeviceCoreMidi::MidiInputPortCoreMidi::ParameterAutoBind::Fix() { + return false; + } + + void MidiInputDeviceCoreMidi::MidiInputPortCoreMidi::ParameterAutoBind::OnSetValue(bool b) throw (Exception) { + if (b) pPort->connectToAllSources(); + } // *************** MidiInputPortCoreMidi *************** // * + + int MidiInputDeviceCoreMidi::MidiInputPortCoreMidi::pPortID = 0; MidiInputDeviceCoreMidi::MidiInputPortCoreMidi::MidiInputPortCoreMidi(MidiInputDeviceCoreMidi* pDevice) throw (MidiInputException) : MidiInputPort(pDevice, -1) { + this->pDevice = pDevice; + // create CoreMidi virtual destination /* 20080105 Toshi Nagata */ @@ -78,6 +153,7 @@ Parameters"NAME" = new ParameterName(this); Parameters"CORE_MIDI_BINDINGS" = new ParameterCoreMidiBindings(this); + Parameters"AUTO_BIND" = new ParameterAutoBind(this); } MidiInputDeviceCoreMidi::MidiInputPortCoreMidi::~MidiInputPortCoreMidi() { @@ -87,17 +163,66 @@ void MidiInputDeviceCoreMidi::MidiInputPortCoreMidi::ReadProc(const MIDIPacketList* pktlist, void* refCon, void* connRefCon) { MidiInputPortCoreMidi* port = (MidiInputPortCoreMidi*)refCon; + port->ProcessMidiEvents(pktlist); + } + + void MidiInputDeviceCoreMidi::MidiInputPortCoreMidi::ProcessMidiEvents(const MIDIPacketList *pktlist) { MIDIPacket *packet = (MIDIPacket *)pktlist->packet; - for (unsigned int i = 0; i < pktlist->numPackets; ++i) { - - // To be checked : several events per packet - - port->DispatchRaw(packet->data); - + uint8_t* pData = (uint8_t*) packet->data; + int k = 0; + // A MIDIPacket can have more than one (non SysEx) MIDI event in one + // packet. However SysEx messages are guaranteed to be alone in one + // MIDIPacket. + do { + int eventSize = expectedEventSize(pDatak); + if (eventSize < 0) eventSize = packet->length - k; + + if (k + eventSize > packet->length) goto next_packet; + + DispatchRaw(&pDatak); + k += eventSize; + } while (k < packet->length); + + next_packet: packet = MIDIPacketNext(packet); } } + + void MidiInputDeviceCoreMidi::MidiInputPortCoreMidi::connectToSource(MIDIEndpointRef source) { + // check if we already have such a connection, if yes, ignore it + for (uint i = 0; i < bindings.size(); ++i) { + if (bindingsi == source) return; + } + // now establish the CoreMIDI connection + MIDIPortConnectSource(pDevice->pBridge, source, this); + // and remember it + bindings.push_back(source); + } + + void MidiInputDeviceCoreMidi::MidiInputPortCoreMidi::connectToAllSources() { + const ItemCount sourceCount = MIDIGetNumberOfSources(); + for (ItemCount i = 0; i < sourceCount; ++i) { + MIDIEndpointRef source = MIDIGetSource(i); + connectToSource(source); + dmsg(1,("Auto binded to CoreMIDI source '%s'\n", _getDisplayName(source).c_str())); + } + } + + void MidiInputDeviceCoreMidi::MidiInputPortCoreMidi::onNewSourceAppeared(MIDIEndpointRef source) { + if (((ParameterAutoBind*)Parameters"AUTO_BIND")->ValueAsBool()) { + connectToSource(source); + dmsg(1,("Auto binded to new CoreMIDI source '%s'\n", _getDisplayName(source).c_str())); + } + } + + void MidiInputDeviceCoreMidi::MidiInputPortCoreMidi::onNewSourceDisappeared(MIDIEndpointRef source) { + std::vector<MIDIEndpointRef>::iterator iter = std::find(bindings.begin(), bindings.end(), source); + if (iter != bindings.end()) { + dmsg(1,("CoreMIDI source '%s' disappeared, disconnecting it.\n", _getDisplayName(source).c_str())); + bindings.erase(iter); + } + } // *************** MidiInputDeviceCoreMidi *************** @@ -105,8 +230,12 @@ MidiInputDeviceCoreMidi::MidiInputDeviceCoreMidi(std::map<String,DeviceCreationParameter*> Parameters, void* pSampler) : MidiInputDevice(Parameters, pSampler) { - MIDIClientCreate(CFSTR("LinuxSampler"), NotifyProc, NULL, &hCoreMidiClient); + MIDIClientCreate(CFSTR("LinuxSampler"), NotifyProc, this, &hCoreMidiClient); if (!hCoreMidiClient) throw MidiInputException("Error opening CoreMidi client"); + + OSStatus status = MIDIInputPortCreate(hCoreMidiClient, CFSTR("bridge"), _bridgeInputEvent, this, &pBridge); + if (status != noErr) throw MidiInputException("Could not create bridge port for CoreMIDI client"); + AcquirePorts(((DeviceCreationParameterInt*)Parameters"PORTS")->ValueAsInt()); } @@ -134,17 +263,50 @@ } String MidiInputDeviceCoreMidi::Version() { - String s = "$Revision: 1.12 $"; + String s = "$Revision: 2568 $"; return s.substr(11, s.size() - 13); // cut dollar signs, spaces and CVS macro keyword } void MidiInputDeviceCoreMidi::NotifyProc(const MIDINotification* message, void* refCon) { - // to be finished - if (message->messageID == kMIDIMsgSetupChanged) { - printf("kMIDIMsgSetupChanged\n"); + MidiInputDeviceCoreMidi* pDevice = (MidiInputDeviceCoreMidi*) refCon; + + switch (message->messageID) { + case kMIDIMsgObjectAdded: { + MIDIObjectAddRemoveNotification* notification = (MIDIObjectAddRemoveNotification*) message; + if (notification->childType == kMIDIObjectType_Source) { + for (std::map<int,MidiInputPort*>::iterator iter = pDevice->Ports.begin(); + iter != pDevice->Ports.end(); ++iter) + { + MidiInputPortCoreMidi* pPort = (MidiInputPortCoreMidi*) iter->second; + MIDIEndpointRef source = (MIDIEndpointRef) notification->child; + pPort->onNewSourceAppeared(source); + } + } + break; + } + + case kMIDIMsgObjectRemoved: { + MIDIObjectAddRemoveNotification* notification = (MIDIObjectAddRemoveNotification*) message; + if (notification->childType == kMIDIObjectType_Source) { + for (std::map<int,MidiInputPort*>::iterator iter = pDevice->Ports.begin(); + iter != pDevice->Ports.end(); ++iter) + { + MidiInputPortCoreMidi* pPort = (MidiInputPortCoreMidi*) iter->second; + MIDIEndpointRef source = (MIDIEndpointRef) notification->child; + pPort->onNewSourceDisappeared(source); + } + } + break; + } + + case kMIDIMsgSetupChanged: + case kMIDIMsgPropertyChanged: + case kMIDIMsgThruConnectionsChanged: + case kMIDIMsgSerialPortOwnerChanged: + case kMIDIMsgIOError: + break; } } - } // namespace LinuxSampler
View file
linuxsampler-2342.tar.bz2/src/drivers/midi/MidiInputDeviceCoreMidi.h -> linuxsampler-2718.tar.bz2/src/drivers/midi/MidiInputDeviceCoreMidi.h
Changed
@@ -1,7 +1,7 @@ /*************************************************************************** * * * Copyright (C) 2004, 2005 Grame * - * Copyright (C) 2007 Christian Schoenebeck * + * Copyright (C) 2005 - 2013 Christian Schoenebeck * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -48,7 +48,7 @@ class ParameterName : public MidiInputPort::ParameterName { public: ParameterName(MidiInputPort* pPort) throw (Exception); - virtual void OnSetValue(String s) throw (Exception); + virtual void OnSetValue(String s) throw (Exception) OVERRIDE; }; /** MIDI Port Parameter 'CORE_MIDI_BINDINGS' @@ -59,13 +59,30 @@ class ParameterCoreMidiBindings : public DeviceRuntimeParameterStrings { public: ParameterCoreMidiBindings(MidiInputPortCoreMidi* pPort); - virtual String Description(); - virtual bool Fix(); - virtual std::vector<String> PossibilitiesAsString(); - virtual void OnSetValue(std::vector<String> vS) throw (Exception); + virtual String Description() OVERRIDE; + virtual bool Fix() OVERRIDE; + virtual std::vector<String> PossibilitiesAsString() OVERRIDE; + virtual void OnSetValue(std::vector<String> vS) throw (Exception) OVERRIDE; protected: MidiInputPortCoreMidi* pPort; }; + + /** MIDI Port Parameter 'AUTO_BIND' + * + * If enabled, the port will automatically be connected to all + * CoreMIDI source endpoints at present and future. + */ + class ParameterAutoBind : public DeviceRuntimeParameterBool { + public: + ParameterAutoBind(MidiInputPortCoreMidi* pPort); + virtual String Description() OVERRIDE; + virtual bool Fix() OVERRIDE; + virtual void OnSetValue(bool b) throw (Exception) OVERRIDE; + protected: + MidiInputPortCoreMidi* pPort; + }; + + void ProcessMidiEvents(const MIDIPacketList *pktlist); static void ReadProc(const MIDIPacketList *pktlist, void *refCon, void *connRefCon); static int pPortID; @@ -73,10 +90,15 @@ protected: MidiInputPortCoreMidi(MidiInputDeviceCoreMidi* pDevice) throw (MidiInputException); ~MidiInputPortCoreMidi(); + void connectToSource(MIDIEndpointRef source); + void connectToAllSources(); + void onNewSourceAppeared(MIDIEndpointRef source); + void onNewSourceDisappeared(MIDIEndpointRef source); friend class MidiInputDeviceCoreMidi; private: - MidiInputPortCoreMidi* pDevice; + MidiInputDeviceCoreMidi* pDevice; MIDIEndpointRef pDestination; + std::vector<MIDIEndpointRef> bindings; //TODO: shall probably be protected by a mutex (since the CoreMIDI notification callback thread might also modify it when new sources appear or disappear) friend class ParameterName; friend class ParameterCoreMidiBindings; @@ -86,9 +108,9 @@ virtual ~MidiInputDeviceCoreMidi(); // derived abstract methods from class 'MidiInputDevice' - void Listen(){} - void StopListen(){} - virtual String Driver(); + void Listen() OVERRIDE {} + void StopListen() OVERRIDE {} + virtual String Driver() OVERRIDE; static String Name(); static String Description(); static String Version(); @@ -100,6 +122,7 @@ private: MIDIClientRef hCoreMidiClient; + MIDIPortRef pBridge; }; }
View file
linuxsampler-2342.tar.bz2/src/drivers/midi/MidiInputDeviceFactory.h -> linuxsampler-2718.tar.bz2/src/drivers/midi/MidiInputDeviceFactory.h
Changed
@@ -3,7 +3,7 @@ * LinuxSampler - modular, streaming capable sampler * * * * Copyright (C) 2003, 2004 by Benno Senoner and Christian Schoenebeck * - * Copyright (C) 2005 - 2009 Christian Schoenebeck * + * Copyright (C) 2005 - 2014 Christian Schoenebeck * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -43,6 +43,7 @@ public: class InnerFactory { public: + virtual ~InnerFactory() {} virtual MidiInputDevice* Create(std::map<String,DeviceCreationParameter*>& Parameters, Sampler* pSampler) = 0; virtual String Description() = 0; virtual String Version() = 0;
View file
linuxsampler-2342.tar.bz2/src/drivers/midi/MidiInputDeviceJack.cpp -> linuxsampler-2718.tar.bz2/src/drivers/midi/MidiInputDeviceJack.cpp
Changed
@@ -74,7 +74,7 @@ // disconnect all current bindings first for (int i = 0; i < Bindings.size(); i++) { String src_name = Bindingsi; - int res = jack_disconnect(pPort->pDevice->hJackClient, src_name.c_str(), dst_name.c_str()); + /*int res =*/ jack_disconnect(pPort->pDevice->hJackClient, src_name.c_str(), dst_name.c_str()); } // connect new bindings for (int i = 0; i < vS.size(); i++) { @@ -210,7 +210,7 @@ } String MidiInputDeviceJack::Version() { - String s = "$Revision: 1.5 $"; + String s = "$Revision: 2494 $"; return s.substr(11, s.size() - 13); // cut dollar signs, spaces and CVS macro keyword }
View file
linuxsampler-2342.tar.bz2/src/drivers/midi/MidiInputDeviceJack.h -> linuxsampler-2718.tar.bz2/src/drivers/midi/MidiInputDeviceJack.h
Changed
@@ -1,6 +1,6 @@ /*************************************************************************** * * - * Copyright (C) 2008 Andreas Persson * + * Copyright (C) 2008 - 2013 Andreas Persson * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -48,7 +48,7 @@ class ParameterName : public MidiInputPort::ParameterName { public: ParameterName(MidiInputPortJack* pPort) throw (Exception); - virtual void OnSetValue(String s) throw (Exception); + virtual void OnSetValue(String s) throw (Exception) OVERRIDE; protected: MidiInputPortJack* pPort; }; @@ -60,10 +60,10 @@ class ParameterJackBindings : public DeviceRuntimeParameterStrings { public: ParameterJackBindings(MidiInputPortJack* pPort); - virtual String Description(); - virtual bool Fix(); - virtual std::vector<String> PossibilitiesAsString(); - virtual void OnSetValue(std::vector<String> vS); + virtual String Description() OVERRIDE; + virtual bool Fix() OVERRIDE; + virtual std::vector<String> PossibilitiesAsString() OVERRIDE; + virtual void OnSetValue(std::vector<String> vS) OVERRIDE; static String Name(); protected: MidiInputPortJack* pPort; @@ -88,13 +88,13 @@ public: ParameterName(); ParameterName(String s); - virtual String Description(); - virtual bool Fix(); - virtual bool Mandatory(); - virtual std::map<String,DeviceCreationParameter*> DependsAsParameters(); - virtual std::vector<String> PossibilitiesAsString(std::map<String,String> Parameters); - virtual optional<String> DefaultAsString(std::map<String,String> Parameters); - virtual void OnSetValue(String s) throw (Exception); + virtual String Description() OVERRIDE; + virtual bool Fix() OVERRIDE; + virtual bool Mandatory() OVERRIDE; + virtual std::map<String,DeviceCreationParameter*> DependsAsParameters() OVERRIDE; + virtual std::vector<String> PossibilitiesAsString(std::map<String,String> Parameters) OVERRIDE; + virtual optional<String> DefaultAsString(std::map<String,String> Parameters) OVERRIDE; + virtual void OnSetValue(String s) throw (Exception) OVERRIDE; static String Name(); }; @@ -102,9 +102,9 @@ ~MidiInputDeviceJack(); // derived abstract methods from class 'MidiInputDevice' - void Listen(); - void StopListen(); - String Driver(); + void Listen() OVERRIDE; + void StopListen() OVERRIDE; + String Driver() OVERRIDE; static String Name(); static String Description(); static String Version();
View file
linuxsampler-2342.tar.bz2/src/drivers/midi/MidiInputDeviceMidiShare.cpp -> linuxsampler-2718.tar.bz2/src/drivers/midi/MidiInputDeviceMidiShare.cpp
Changed
@@ -4,6 +4,7 @@ * * * Copyright (C) 2003, 2004 by Benno Senoner and Christian Schoenebeck * * Copyright (C) 2004 Grame * + * Copyright (C) 2014 Christian Schoenebeck * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -69,8 +70,10 @@ MidiAcceptType(hMidiFilter, typeKeyOn, 1); MidiAcceptType(hMidiFilter, typeKeyOff, 1); MidiAcceptType(hMidiFilter, typeCtrlChange, 1); - //MidiAcceptType(hMidiFilter, typeProgChange, 1); + MidiAcceptType(hMidiFilter, typeProgChange, 1); MidiAcceptType(hMidiFilter, typePitchWheel, 1); + MidiAcceptType(hMidiFilter, typeChanPress, 1); + MidiAcceptType(hMidiFilter, typeKeyPress, 1); /* set the filter */ MidiSetFilter(hRefnum, hMidiFilter); @@ -174,7 +177,17 @@ driver->DispatchPitchbend(((MidiGetField(ev,0)+(MidiGetField(ev,1) << 7)) - 8192),Chan(ev)); MidiFreeEv(ev); break; - + + case typeChanPress: + driver->DispatchChannelPressure(MidiGetField(ev,0),Chan(ev)); + MidiFreeEv(ev); + break; + + case typeKeyPress: + driver->DispatchPolyphonicKeyPressure(Pitch(ev),Vel(ev),Chan(ev)); + MidiFreeEv(ev); + break; + case typeNote: driver->DispatchNoteOn(Pitch(ev),Vel(ev),Chan(ev)); MidiTask(KeyOffTask,Date(ev)+Dur(ev),ref,(long)ev,0,0);
View file
linuxsampler-2342.tar.bz2/src/drivers/midi/MidiInputDeviceMidiShare.h -> linuxsampler-2718.tar.bz2/src/drivers/midi/MidiInputDeviceMidiShare.h
Changed
@@ -41,9 +41,9 @@ ~MidiInputDeviceMidiShare(); // derived abstract methods from class 'MidiInputDevice' - void Listen(){} - void StopListen(){} - virtual String Driver(); + void Listen() OVERRIDE {} + void StopListen() OVERRIDE {} + virtual String Driver() OVERRIDE; static String Name(); static String Description(); static String Version();
View file
linuxsampler-2342.tar.bz2/src/drivers/midi/MidiInputDeviceMme.cpp -> linuxsampler-2718.tar.bz2/src/drivers/midi/MidiInputDeviceMme.cpp
Changed
@@ -151,6 +151,7 @@ } MidiInputDeviceMme::MidiInputPortMme::~MidiInputPortMme() { + CloseMmeMidiPort(); delete TmpSysExBuf; delete SysExBuf; } @@ -246,9 +247,13 @@ data = (unsigned char *)&dwParam1; switch(uMsg) { - case MIM_DATA: + case MIM_DATA: { + //FIXME: passing timeStamp this way here does not work, since the DispatchRaw() expects it to be in period position, not miliseconds, requires additional code in RTMath to be able to transform the value for this purpose here + //int32_t timeStamp = dwParam2; + //DispatchRaw(data, timeStamp); DispatchRaw(data); break; + } case MIM_LONGDATA: if(!ExitFlag) { @@ -334,7 +339,7 @@ } String MidiInputDeviceMme::Version() { - String s = "$Revision: 1.5 $"; + String s = "$Revision: 2494 $"; return s.substr(11, s.size() - 13); // cut dollar signs, spaces and CVS macro keyword }
View file
linuxsampler-2342.tar.bz2/src/drivers/midi/MidiInputDeviceMme.h -> linuxsampler-2718.tar.bz2/src/drivers/midi/MidiInputDeviceMme.h
Changed
@@ -3,7 +3,7 @@ * LinuxSampler - modular, streaming capable sampler * * * * Copyright (C) 2003, 2004 by Benno Senoner and Christian Schoenebeck * - * Copyright (C) 2005 - 2012 Christian Schoenebeck * + * Copyright (C) 2005 - 2013 Christian Schoenebeck * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -46,15 +46,15 @@ public: ParameterPorts(); ParameterPorts(String val); - virtual String Description(); - virtual bool Fix(); - virtual bool Mandatory(); - virtual std::map<String,DeviceCreationParameter*> DependsAsParameters(); - virtual optional<int> DefaultAsInt(std::map<String,String> Parameters); - virtual optional<int> RangeMinAsInt(std::map<String,String> Parameters); - virtual optional<int> RangeMaxAsInt(std::map<String,String> Parameters); - virtual std::vector<int> PossibilitiesAsInt(std::map<String,String> Parameters); - virtual void OnSetValue(int i) throw (Exception); + virtual String Description() OVERRIDE; + virtual bool Fix() OVERRIDE; + virtual bool Mandatory() OVERRIDE; + virtual std::map<String,DeviceCreationParameter*> DependsAsParameters() OVERRIDE; + virtual optional<int> DefaultAsInt(std::map<String,String> Parameters) OVERRIDE; + virtual optional<int> RangeMinAsInt(std::map<String,String> Parameters) OVERRIDE; + virtual optional<int> RangeMaxAsInt(std::map<String,String> Parameters) OVERRIDE; + virtual std::vector<int> PossibilitiesAsInt(std::map<String,String> Parameters) OVERRIDE; + virtual void OnSetValue(int i) throw (Exception) OVERRIDE; static String Name(); }; @@ -63,9 +63,6 @@ */ class MidiInputPortMme : public MidiInputPort { public: - - - /** MIDI Port Parameter 'Port' * @@ -75,19 +72,16 @@ class ParameterPort : public DeviceRuntimeParameterString { public: ParameterPort(MidiInputPortMme* pPort); - virtual bool Fix(); - virtual String Description(); - virtual std::vector<String> PossibilitiesAsString(); - virtual void OnSetValue(String s); + virtual bool Fix() OVERRIDE; + virtual String Description() OVERRIDE; + virtual std::vector<String> PossibilitiesAsString() OVERRIDE; + virtual void OnSetValue(String s) OVERRIDE; private: MidiInputPortMme* pPort; }; - protected: - - MidiInputPortMme(MidiInputDeviceMme* pDevice) throw (MidiInputException); ~MidiInputPortMme(); void ConnectToMmeMidiSource(const char* MidiSource); @@ -112,9 +106,9 @@ ~MidiInputDeviceMme(); // derived abstract methods from class 'MidiInputDevice' - void Listen(); - void StopListen(); - virtual String Driver(); + void Listen() OVERRIDE; + void StopListen() OVERRIDE; + virtual String Driver() OVERRIDE; static String Name(); static String Description(); static String Version();
View file
linuxsampler-2342.tar.bz2/src/drivers/midi/MidiInputDevicePlugin.cpp -> linuxsampler-2718.tar.bz2/src/drivers/midi/MidiInputDevicePlugin.cpp
Changed
@@ -1,6 +1,6 @@ /*************************************************************************** * * - * Copyright (C) 2008 - 2009 Andreas Persson * + * Copyright (C) 2008 - 2012 Andreas Persson * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -19,6 +19,7 @@ ***************************************************************************/ #include "MidiInputDevicePlugin.h" +#include "../../common/global_private.h" namespace LinuxSampler { @@ -32,13 +33,22 @@ +// *************** ParameterPortsPlugin *************** +// * + + void MidiInputDevicePlugin::ParameterPortsPlugin::ForceSetValue(int ports) { + OnSetValue(ports); + iVal = ports; + } + + // *************** MidiInputDevicePlugin *************** // * MidiInputDevicePlugin::MidiInputDevicePlugin(std::map<String, DeviceCreationParameter*> Parameters, void* pSampler) : MidiInputDevice(Parameters, pSampler) { - AcquirePorts(1); + AcquirePorts(((DeviceCreationParameterInt*)Parameters"PORTS")->ValueAsInt()); } MidiInputDevicePlugin::~MidiInputDevicePlugin() { @@ -64,7 +74,7 @@ } String MidiInputDevicePlugin::Version() { - String s = "$Revision: 1.3 $"; + String s = "$Revision: 2494 $"; return s.substr(11, s.size() - 13); // cut dollar signs, spaces and CVS macro keyword } @@ -76,8 +86,30 @@ return new MidiInputPortPlugin(this, Ports.size()); } - void MidiInputDevicePlugin::DeleteMidiPort(MidiInputPort* pPort) { - delete (MidiInputPortPlugin*)pPort; + void MidiInputDevicePlugin::AddMidiPort() { + static_cast<ParameterPortsPlugin*>( + Parameters"PORTS")->ForceSetValue(Ports.size() + 1); + } + + void MidiInputDevicePlugin::RemoveMidiPort(MidiInputPort* pPort) { + // reorder map so pPort is last + int portNumber = 0; + std::map<int, MidiInputPort*>::iterator i = Ports.begin(); + for ( ; i != Ports.end(); ++i, portNumber++) { + if (i->second == pPort) break; + } + std::map<int, MidiInputPort*>::iterator previ = i; + for (++i ; i != Ports.end(); ++i, portNumber++) { + previ->second = i->second; + static_cast<MidiInputPortPlugin*>(previ->second)->portNumber = portNumber; + previ->second->PortParameters()"NAME"->SetValue("Port " + ToString(portNumber)); + previ = i; + } + previ->second = pPort; + + // delete the last port + static_cast<ParameterPortsPlugin*>( + Parameters"PORTS")->ForceSetValue(Ports.size() - 1); } bool MidiInputDevicePlugin::isAutonomousDevice() {
View file
linuxsampler-2342.tar.bz2/src/drivers/midi/MidiInputDevicePlugin.h -> linuxsampler-2718.tar.bz2/src/drivers/midi/MidiInputDevicePlugin.h
Changed
@@ -1,6 +1,6 @@ /*************************************************************************** * * - * Copyright (C) 2008 - 2009 Andreas Persson * + * Copyright (C) 2008 - 2013 Andreas Persson * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -54,14 +54,15 @@ public: ParameterPortsPlugin() : ParameterPorts() { } ParameterPortsPlugin(String s) : ParameterPorts(s) { } - virtual bool Fix() { return true; } + virtual bool Fix() OVERRIDE { return true; } + void ForceSetValue(int ports); }; // derived abstract methods from class 'MidiInputDevice' - void Listen(); - void StopListen(); - String Driver(); - bool isAutonomousDevice(); + void Listen() OVERRIDE; + void StopListen() OVERRIDE; + String Driver() OVERRIDE; + bool isAutonomousDevice() OVERRIDE; static String Name(); static String Version(); static String Description(); @@ -77,7 +78,8 @@ return Ports0; } - static void DeleteMidiPort(MidiInputPort* pPort); + void AddMidiPort(); + void RemoveMidiPort(MidiInputPort* pPort); }; }
View file
linuxsampler-2342.tar.bz2/src/drivers/midi/MidiInputPort.cpp -> linuxsampler-2718.tar.bz2/src/drivers/midi/MidiInputPort.cpp
Changed
@@ -3,7 +3,7 @@ * LinuxSampler - modular, streaming capable sampler * * * * Copyright (C) 2003, 2004 by Benno Senoner and Christian Schoenebeck * - * Copyright (C) 2005 - 2012 Christian Schoenebeck * + * Copyright (C) 2005 - 2014 Christian Schoenebeck * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -77,9 +77,12 @@ MidiInputPort::MidiInputPort(MidiInputDevice* pDevice, int portNumber) : MidiChannelMapReader(MidiChannelMap), SysexListenersReader(SysexListeners), - virtualMidiDevicesReader(virtualMidiDevices) { + virtualMidiDevicesReader(virtualMidiDevices), + noteOnVelocityFilterReader(noteOnVelocityFilter) + { this->pDevice = pDevice; this->portNumber = portNumber; + runningStatusBuf0 = 0; Parameters"NAME" = new ParameterName(this); } @@ -97,6 +100,12 @@ void MidiInputPort::DispatchNoteOn(uint8_t Key, uint8_t Velocity, uint MidiChannel) { if (Key > 127 || Velocity > 127 || MidiChannel > 16) return; + + // apply velocity filter (if any) + const std::vector<uint8_t>& velocityFilter = noteOnVelocityFilterReader.Lock(); + if (!velocityFilter.empty()) Velocity = velocityFilterVelocity; + noteOnVelocityFilterReader.Unlock(); + const MidiChannelMap_t& midiChannelMap = MidiChannelMapReader.Lock(); // dispatch event for engines listening to the same MIDI channel { @@ -122,6 +131,12 @@ void MidiInputPort::DispatchNoteOn(uint8_t Key, uint8_t Velocity, uint MidiChannel, int32_t FragmentPos) { if (Key > 127 || Velocity > 127 || MidiChannel > 16) return; + + // apply velocity filter (if any) + const std::vector<uint8_t>& velocityFilter = noteOnVelocityFilterReader.Lock(); + if (!velocityFilter.empty()) Velocity = velocityFilterVelocity; + noteOnVelocityFilterReader.Unlock(); + const MidiChannelMap_t& midiChannelMap = MidiChannelMapReader.Lock(); // dispatch event for engines listening to the same MIDI channel { @@ -231,6 +246,78 @@ MidiChannelMapReader.Unlock(); } + void MidiInputPort::DispatchChannelPressure(uint8_t Value, uint MidiChannel) { + if (Value > 127 || MidiChannel > 16) return; + const MidiChannelMap_t& midiChannelMap = MidiChannelMapReader.Lock(); + // dispatch event for engines listening to the same MIDI channel + { + std::set<EngineChannel*>::iterator engineiter = midiChannelMapMidiChannel.begin(); + std::set<EngineChannel*>::iterator end = midiChannelMapMidiChannel.end(); + for (; engineiter != end; engineiter++) (*engineiter)->SendChannelPressure(Value, MidiChannel); + } + // dispatch event for engines listening to ALL MIDI channels + { + std::set<EngineChannel*>::iterator engineiter = midiChannelMapmidi_chan_all.begin(); + std::set<EngineChannel*>::iterator end = midiChannelMapmidi_chan_all.end(); + for (; engineiter != end; engineiter++) (*engineiter)->SendChannelPressure(Value, MidiChannel); + } + MidiChannelMapReader.Unlock(); + } + + void MidiInputPort::DispatchChannelPressure(uint8_t Value, uint MidiChannel, int32_t FragmentPos) { + if (Value > 127 || MidiChannel > 16) return; + const MidiChannelMap_t& midiChannelMap = MidiChannelMapReader.Lock(); + // dispatch event for engines listening to the same MIDI channel + { + std::set<EngineChannel*>::iterator engineiter = midiChannelMapMidiChannel.begin(); + std::set<EngineChannel*>::iterator end = midiChannelMapMidiChannel.end(); + for (; engineiter != end; engineiter++) (*engineiter)->SendChannelPressure(Value, MidiChannel, FragmentPos); + } + // dispatch event for engines listening to ALL MIDI channels + { + std::set<EngineChannel*>::iterator engineiter = midiChannelMapmidi_chan_all.begin(); + std::set<EngineChannel*>::iterator end = midiChannelMapmidi_chan_all.end(); + for (; engineiter != end; engineiter++) (*engineiter)->SendChannelPressure(Value, MidiChannel, FragmentPos); + } + MidiChannelMapReader.Unlock(); + } + + void MidiInputPort::DispatchPolyphonicKeyPressure(uint8_t Key, uint8_t Value, uint MidiChannel) { + if (Key > 127 || Value > 127 || MidiChannel > 16) return; + const MidiChannelMap_t& midiChannelMap = MidiChannelMapReader.Lock(); + // dispatch event for engines listening to the same MIDI channel + { + std::set<EngineChannel*>::iterator engineiter = midiChannelMapMidiChannel.begin(); + std::set<EngineChannel*>::iterator end = midiChannelMapMidiChannel.end(); + for (; engineiter != end; engineiter++) (*engineiter)->SendPolyphonicKeyPressure(Key, Value, MidiChannel); + } + // dispatch event for engines listening to ALL MIDI channels + { + std::set<EngineChannel*>::iterator engineiter = midiChannelMapmidi_chan_all.begin(); + std::set<EngineChannel*>::iterator end = midiChannelMapmidi_chan_all.end(); + for (; engineiter != end; engineiter++) (*engineiter)->SendPolyphonicKeyPressure(Key, Value, MidiChannel); + } + MidiChannelMapReader.Unlock(); + } + + void MidiInputPort::DispatchPolyphonicKeyPressure(uint8_t Key, uint8_t Value, uint MidiChannel, int32_t FragmentPos) { + if (Key > 127 || Value > 127 || MidiChannel > 16) return; + const MidiChannelMap_t& midiChannelMap = MidiChannelMapReader.Lock(); + // dispatch event for engines listening to the same MIDI channel + { + std::set<EngineChannel*>::iterator engineiter = midiChannelMapMidiChannel.begin(); + std::set<EngineChannel*>::iterator end = midiChannelMapMidiChannel.end(); + for (; engineiter != end; engineiter++) (*engineiter)->SendPolyphonicKeyPressure(Key, Value, MidiChannel, FragmentPos); + } + // dispatch event for engines listening to ALL MIDI channels + { + std::set<EngineChannel*>::iterator engineiter = midiChannelMapmidi_chan_all.begin(); + std::set<EngineChannel*>::iterator end = midiChannelMapmidi_chan_all.end(); + for (; engineiter != end; engineiter++) (*engineiter)->SendPolyphonicKeyPressure(Key, Value, MidiChannel, FragmentPos); + } + MidiChannelMapReader.Unlock(); + } + void MidiInputPort::DispatchControlChange(uint8_t Controller, uint8_t Value, uint MidiChannel) { if (Controller > 128 || Value > 127 || MidiChannel > 16) return; const MidiChannelMap_t& midiChannelMap = MidiChannelMapReader.Lock(); @@ -363,8 +450,46 @@ } MidiChannelMapReader.Unlock(); } + + /** + * Handles the so called MIDI "running status" mode, which allows devices + * to reduce bandwidth (data reduction). + * + * If the passed in MIDI data is regular MIDI data, this method will simply + * return the original data pointer and just stores the status byte for + * potential "running status" event eventually coming next. + * + * If the passed in MIDI data however seems to be in "running status" mode, + * this method will return another buffer, which allows the MIDI parser + * to handle the MIDI data as usually with "normal" MIDI data. + */ + uint8_t* MidiInputPort::handleRunningStatus(uint8_t* pData) { + if ((pData0 & 0x80) || !runningStatusBuf0) { + // store status byte for eventual "running status" in next event + if (pData0 & 0x80) { + if (pData0 < 0xf0) { + // "running status" is only allowed for channel messages + runningStatusBuf0 = pData0; + } else if (pData0 < 0xf8) { + // "system common" messages (0xf0..0xf7) shall reset any running + // status, however "realtime" messages (0xf8..0xff) shall be + // ignored here + runningStatusBuf0 = 0; + } + } + // it's either a regular status byte, or some invalid "running status" + return pData; + } else { // "running status" mode ... + const uint8_t type = runningStatusBuf0 & 0xf0; + const int size = (type == 0xc0 || type == 0xd0) ? 1 : 2; // only program change & channel pressure have 1 data bytes + memcpy(&runningStatusBuf1, pData, size); + return runningStatusBuf; + } + } void MidiInputPort::DispatchRaw(uint8_t* pData) { + pData = handleRunningStatus(pData); + uint8_t channel = pData0 & 0x0f; switch (pData0 & 0xf0) { case 0x80: @@ -377,6 +502,9 @@ DispatchNoteOff(pData1, pData2, channel); } break; + case 0xA0: + DispatchPolyphonicKeyPressure(pData1, pData2, channel); + break; case 0xb0: if (pData1 == 0) { DispatchBankSelectMsb(pData2, channel); @@ -389,7 +517,7 @@ DispatchProgramChange(pData1, channel); break; case 0xd0: - DispatchControlChange(128, pData1, channel); + DispatchChannelPressure(pData1, channel); break; case 0xe0: DispatchPitchbend((pData1 | pData2 << 7) - 8192, channel); @@ -398,6 +526,8 @@ } void MidiInputPort::DispatchRaw(uint8_t* pData, int32_t FragmentPos) { + pData = handleRunningStatus(pData); + uint8_t channel = pData0 & 0x0f; switch (pData0 & 0xf0) { case 0x80: @@ -410,6 +540,9 @@ DispatchNoteOff(pData1, pData2, channel, FragmentPos); } break; + case 0xA0: + DispatchPolyphonicKeyPressure(pData1, pData2, channel, FragmentPos); + break; case 0xb0: if (pData1 == 0) { DispatchBankSelectMsb(pData2, channel); @@ -422,36 +555,64 @@ DispatchProgramChange(pData1, channel); break; case 0xd0: - DispatchControlChange(128, pData1, channel, FragmentPos); + DispatchChannelPressure(pData1, channel, FragmentPos); break; case 0xe0: DispatchPitchbend((pData1 | pData2 << 7) - 8192, channel, FragmentPos); break; } } + + void MidiInputPort::SetNoteOnVelocityFilter(const std::vector<uint8_t>& filter) { + if (filter.size() != 128 && filter.size() != 0) + throw MidiInputException("Note on velocity filter must be either of size 128 or 0"); + + // check the value range of the filter + if (!filter.empty()) + for (int i = 0; i < 128; i++) + if (filteri > 127) + throw MidiInputException("Invalid note on velocity filter, values must be in range 0 .. 127"); + + // apply new filter ... + LockGuard lock(noteOnVelocityFilterMutex); + // double buffer ... double work ... + { + std::vector<uint8_t>& config = + noteOnVelocityFilter.GetConfigForUpdate(); + config = filter; + } + { + std::vector<uint8_t>& config = + noteOnVelocityFilter.SwitchConfig(); + config = filter; + } + } void MidiInputPort::Connect(EngineChannel* pEngineChannel, midi_chan_t MidiChannel) { if (MidiChannel < 0 || MidiChannel > 16) throw MidiInputException("MIDI channel index out of bounds"); // first check if desired connection is already established - MidiChannelMapMutex.Lock(); - MidiChannelMap_t& midiChannelMap = MidiChannelMap.GetConfigForUpdate(); - bool bAlreadyDone = midiChannelMapMidiChannel.count(pEngineChannel); - MidiChannelMapMutex.Unlock(); - if (bAlreadyDone) return; + { + LockGuard lock(MidiChannelMapMutex); + MidiChannelMap_t& midiChannelMap = MidiChannelMap.GetConfigForUpdate(); + if (midiChannelMapMidiChannel.count(pEngineChannel)) return; + } // remove all other connections of that engine channel (if any) Disconnect(pEngineChannel); // register engine channel on the desired MIDI channel - MidiChannelMapMutex.Lock(); - MidiChannelMap.GetConfigForUpdate()MidiChannel.insert(pEngineChannel); - MidiChannelMap.SwitchConfig()MidiChannel.insert(pEngineChannel); - MidiChannelMapMutex.Unlock(); + { + LockGuard lock(MidiChannelMapMutex); + MidiChannelMap.GetConfigForUpdate()MidiChannel.insert(pEngineChannel); + MidiChannelMap.SwitchConfig()MidiChannel.insert(pEngineChannel); + } // inform engine channel about this connection - pEngineChannel->Connect(this, MidiChannel); + pEngineChannel->Connect(this); + if (pEngineChannel->MidiChannel() != MidiChannel) + pEngineChannel->SetMidiChannel(MidiChannel); // mark engine channel as changed pEngineChannel->StatusChanged(true); @@ -463,8 +624,8 @@ bool bChannelFound = false; // unregister engine channel from all MIDI channels - MidiChannelMapMutex.Lock(); try { + LockGuard lock(MidiChannelMapMutex); { MidiChannelMap_t& midiChannelMap = MidiChannelMap.GetConfigForUpdate(); for (int i = 0; i <= 16; i++) { @@ -482,10 +643,9 @@ } } catch(...) { /* NOOP */ } - MidiChannelMapMutex.Unlock(); // inform engine channel about the disconnection (if there is one) - if (bChannelFound) pEngineChannel->DisconnectMidiInputPort(); + if (bChannelFound) pEngineChannel->Disconnect(this); // mark engine channel as changed pEngineChannel->StatusChanged(true); @@ -505,7 +665,7 @@ } void MidiInputPort::Connect(VirtualMidiDevice* pDevice) { - virtualMidiDevicesMutex.Lock(); + LockGuard lock(virtualMidiDevicesMutex); // double buffer ... double work ... { std::vector<VirtualMidiDevice*>& devices = @@ -517,11 +677,10 @@ virtualMidiDevices.SwitchConfig(); devices.push_back(pDevice); } - virtualMidiDevicesMutex.Unlock(); } void MidiInputPort::Disconnect(VirtualMidiDevice* pDevice) { - virtualMidiDevicesMutex.Lock(); + LockGuard lock(virtualMidiDevicesMutex); // double buffer ... double work ... { std::vector<VirtualMidiDevice*>& devices = @@ -533,7 +692,23 @@ virtualMidiDevices.SwitchConfig(); devices.erase(std::find(devices.begin(), devices.end(), pDevice)); } - virtualMidiDevicesMutex.Unlock(); + } + + int MidiInputPort::expectedEventSize(unsigned char byte) { + if (!(byte & 0x80) && runningStatusBuf0) + byte = runningStatusBuf0; // "running status" mode + + if (byte < 0x80) return -1; // not a valid status byte + if (byte < 0xC0) return 3; // note on/off, note pressure, control change + if (byte < 0xE0) return 2; // program change, channel pressure + if (byte < 0xF0) return 3; // pitch wheel + if (byte == 0xF0) return -1; // sysex message (variable size) + if (byte == 0xF1) return 2; // time code per quarter frame + if (byte == 0xF2) return 3; // sys. common song position pointer + if (byte == 0xF3) return 2; // sys. common song select + if (byte == 0xF4) return -1; // sys. common undefined / reserved + if (byte == 0xF5) return -1; // sys. common undefined / reserved + return 1; // tune request, end of SysEx, system real-time events } } // namespace LinuxSampler
View file
linuxsampler-2342.tar.bz2/src/drivers/midi/MidiInputPort.h -> linuxsampler-2718.tar.bz2/src/drivers/midi/MidiInputPort.h
Changed
@@ -3,7 +3,7 @@ * LinuxSampler - modular, streaming capable sampler * * * * Copyright (C) 2003, 2004 by Benno Senoner and Christian Schoenebeck * - * Copyright (C) 2005 - 2009 Christian Schoenebeck * + * Copyright (C) 2005 - 2014 Christian Schoenebeck * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -54,10 +54,10 @@ public: ParameterName(MidiInputPort* pPort); ParameterName(MidiInputPort* pPort, String val); - virtual String Description(); - virtual bool Fix(); - virtual std::vector<String> PossibilitiesAsString(); - virtual void OnSetValue(String s) throw (Exception); + virtual String Description() OVERRIDE; + virtual bool Fix() OVERRIDE; + virtual std::vector<String> PossibilitiesAsString() OVERRIDE; + virtual void OnSetValue(String s) throw (Exception) OVERRIDE; protected: MidiInputPort* pPort; }; @@ -133,6 +133,29 @@ * Disconnect the previously connected virtual MIDI device. */ void Disconnect(VirtualMidiDevice* pDevice); + + /** + * Registers the given @a filter to be applied against all note on + * events, adjusting their MIDI velocity data. The vector supplied + * here must exactly be either of size 128 or 0. In case the given + * vector is of size 128, it will be used as lookup table by this + * MIDI inpurt port to remap any incoming MIDI note on velocity + * data to the respective value in that vector, or exactly: + * @code + * velocity = filtervelocity; + * @endcode + * Accordingly the values in the vector have to be in the range + * 0..127, however note that a value 0 should actually never be + * used, since by MIDI specification, a note on with velocity 0 is + * interpreted as note off event instead! + * + * If a vector of size 0 is supplied, this note on velocity filter + * mechanism will be disabled. + * + * @param filter - lookup table in the format described above + * @throws MidiInputException - if filter is in invalid format + */ + void SetNoteOnVelocityFilter(const std::vector<uint8_t>& filter); ///////////////////////////////////////////////////////////////// @@ -276,6 +299,78 @@ void DispatchControlChange(uint8_t Controller, uint8_t Value, uint MidiChannel, int32_t FragmentPos); /** + * Should be called by the implementing MIDI input device whenever + * a channel pressure event arrived (a.k.a. aftertouch), this will + * cause the channel pressure event to be forwarded to all engines + * on the corresponding MIDI channel. + * + * This method is meant for realtime rendering, this way an event + * is immediately created with the current system time as time + * stamp. + * + * @param Value - MIDI channel pressure value (0..127) + * @param MidiChannel - MIDI channel on which event occured on + * (low level indexing, means 0..15) + * @see DispatchPolyphonicKeyPressure() + */ + void DispatchChannelPressure(uint8_t Value, uint MidiChannel); + + /** + * Should be called by the implementing MIDI input device whenever + * a channel pressure event arrived (a.k.a. aftertouch), this will + * cause the channel pressure event to be forwarded to all engines + * on the corresponding MIDI channel. + * + * This method is meant for offline rendering and / or in case the + * exact fragment position of the event is already known. + * + * @param Value - MIDI channel pressure value (0..127) + * @param MidiChannel - MIDI channel on which event occured on + * (low level indexing, means 0..15) + * @param FragmentPos - event's sample point position in the + * current audio fragment + * @see DispatchPolyphonicKeyPressure() + */ + void DispatchChannelPressure(uint8_t Value, uint MidiChannel, int32_t FragmentPos); + + /** + * Should be called by the implementing MIDI input device whenever + * a polyphonic key pressure event arrived (a.k.a. polyphonic + * aftertouch), this will cause the polyphonic key pressure event + * to be forwarded to all engines on the corresponding MIDI channel. + * + * This method is meant for realtime rendering, this way an event + * is immediately created with the current system time as time + * stamp. + * + * @param Key - MIDI key number of the key where pressure changed + * @param Value - MIDI key pressure value (0..127) + * @param MidiChannel - MIDI channel on which event occured on + * (low level indexing, means 0..15) + * @see DispatchChannelPressure() + */ + void DispatchPolyphonicKeyPressure(uint8_t Key, uint8_t Value, uint MidiChannel); + + /** + * Should be called by the implementing MIDI input device whenever + * a polyphonic key pressure event arrived (a.k.a. polyphonic + * aftertouch), this will cause the polyphonic key pressure event + * to be forwarded to all engines on the corresponding MIDI channel. + * + * This method is meant for offline rendering and / or in case the + * exact fragment position of the event is already known. + * + * @param Key - MIDI key number of the key where pressure changed + * @param Value - MIDI key pressure value (0..127) + * @param MidiChannel - MIDI channel on which event occured on + * (low level indexing, means 0..15) + * @param FragmentPos - event's sample point position in the + * current audio fragment + * @see DispatchChannelPressure() + */ + void DispatchPolyphonicKeyPressure(uint8_t Key, uint8_t Value, uint MidiChannel, int32_t FragmentPos); + + /** * Should be called by the implementing MIDI input device * whenever a program change event arrived. In case the * respective sampler channel(s) are enabled for MIDI @@ -297,8 +392,48 @@ */ void DispatchProgramChange(uint8_t Program, uint MidiChannel); + /** + * Should be called by the implementing MIDI input device whenever + * a MIDI bank select MSB (Most Significant Byte) event arrived. + * + * In case the respective sampler channel(s) are enabled for "MIDI + * instrument mapping", a subsequent MIDI program change event can + * be used to let the respective sampler engine load another + * instrument according to the respective entry in the MIDI + * instrument map. + * + * MIDI sources can either a) just send bank select MSB events, or + * b) just send bank select LSB events or c) they can send both bank + * select MSB and LSB events. The sampler will automatically detect + * which style the MIDI source is using and appropriately interpret + * it for selecting the appropriate bank. + * + * @param BankMsb - Most Significant Byte of the bank number + * @param MidiChannel - MIDI channel on which this bank select + * occurred + */ void DispatchBankSelectMsb(uint8_t BankMsb, uint MidiChannel); + /** + * Should be called by the implementing MIDI input device whenever + * a MIDI bank select LSB (Least Significant Byte) event arrived. + * + * In case the respective sampler channel(s) are enabled for "MIDI + * instrument mapping", a subsequent MIDI program change event can + * be used to let the respective sampler engine load another + * instrument according to the respective entry in the MIDI + * instrument map. + * + * MIDI sources can either a) just send bank select MSB events, or + * b) just send bank select LSB events or c) they can send both bank + * select MSB and LSB events. The sampler will automatically detect + * which style the MIDI source is using and appropriately interpret + * it for selecting the appropriate bank. + * + * @param BankMsb - Most Significant Byte of the bank number + * @param MidiChannel - MIDI channel on which this bank select + * occurred + */ void DispatchBankSelectLsb(uint8_t BankLsb, uint MidiChannel); /** @@ -315,7 +450,7 @@ * Helper function for MIDI input devices that have the * MIDI data as raw bytes. * - * @param pData - pointer to the raw MIDI data + * @param pData - pointer to the raw MIDI data */ void DispatchRaw(uint8_t* pData); @@ -341,6 +476,10 @@ SynchronizedConfig<std::vector<VirtualMidiDevice*> > virtualMidiDevices; SynchronizedConfig<std::vector<VirtualMidiDevice*> >::Reader virtualMidiDevicesReader; Mutex virtualMidiDevicesMutex; + SynchronizedConfig<std::vector<uint8_t> > noteOnVelocityFilter; + SynchronizedConfig<std::vector<uint8_t> >::Reader noteOnVelocityFilterReader; + Mutex noteOnVelocityFilterMutex; + uint8_t runningStatusBuf3; /** * Constructor @@ -351,11 +490,31 @@ * Destructor */ virtual ~MidiInputPort(); + + /** + * Takes a MIDI status byte (the first byte of each MIDI event) as + * argument and returns the expected size of the associated MIDI + * event according to the MIDI protocol. Returns -1 on invalid + * status bytes AND on variable size events (SysEx events). + * + * This method can be used for drivers which have to deal with raw + * MIDI data, like the CoreMIDI driver, which can receive MIDI + * packets with more than one event per packet. + * + * This method handles "MIDI running status" as well. That is, in + * case the supplied byte is not a status byte but a data byte, + * it expects the event to be in "running status" and accordingly + * uses the status byte of the previous event (processed by the + * Dispatch*() methods). + */ + int expectedEventSize(unsigned char byte); friend class MidiInputDevice; - private: + private: static SynchronizedConfig<std::set<Engine*> > SysexListeners; ///< All engines that are listening to sysex messages. + + uint8_t* handleRunningStatus(uint8_t* pData); }; } // namsepace LinuxSampler
View file
linuxsampler-2342.tar.bz2/src/drivers/midi/MidiInstrumentMapper.cpp -> linuxsampler-2718.tar.bz2/src/drivers/midi/MidiInstrumentMapper.cpp
Changed
@@ -1,6 +1,6 @@ /*************************************************************************** * * - * Copyright (C) 2006 - 2009 Christian Schoenebeck * + * Copyright (C) 2006 - 2013 Christian Schoenebeck * * * * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -128,12 +128,12 @@ Entry.Volume,Entry.LoadMode) ); } - midiMapsMutex.Lock(); - if (midiMaps.empty()) { - midiMapsMutex.Unlock(); - throw Exception("There is no MIDI instrument map, you have to add one first."); + { + LockGuard lock(midiMapsMutex); + if (midiMaps.empty()) { + throw Exception("There is no MIDI instrument map, you have to add one first."); + } } - midiMapsMutex.Unlock(); if (!Entry.InstrumentFile.size()) throw Exception("No instrument file name given"); // TODO: an easy one - we should check here if given file exists and throw an exception if it doesn't @@ -166,21 +166,22 @@ bool Replaced = false; int InstrCount = 0; - - midiMapsMutex.Lock(); - std::map<int,MidiInstrumentMap>::iterator iterMap = midiMaps.find(Map); - if (iterMap != midiMaps.end()) { // map found - Replaced = (iterMap->second.find(Index) != iterMap->second.end()); - iterMap->secondIndex = privateEntry; - InstrCount = iterMap->second.size(); - } else { // no such map - midiMapsMutex.Unlock(); - EngineFactory::Destroy(pEngine); - throw Exception("There is no MIDI instrument map " + ToString(Map)); + bool MapFound = false; + { + LockGuard lock(midiMapsMutex); + std::map<int,MidiInstrumentMap>::iterator iterMap = midiMaps.find(Map); + if (iterMap != midiMaps.end()) { // map found + MapFound = true; + Replaced = (iterMap->second.find(Index) != iterMap->second.end()); + iterMap->secondIndex = privateEntry; + InstrCount = iterMap->second.size(); + } } - midiMapsMutex.Unlock(); EngineFactory::Destroy(pEngine); - + if (!MapFound) { + throw Exception("There is no MIDI instrument map " + ToString(Map)); + } + if (Replaced) { int Bank = (int(Index.midi_bank_msb) << 7) | int(Index.midi_bank_lsb); fireMidiInstrumentInfoChanged(Map, Bank, Index.midi_prog); @@ -210,10 +211,10 @@ } MidiInstrumentMapper::entry_t MidiInstrumentMapper::GetEntry(int Map, uint MidiBank, uint MidiProg) { - midiMapsMutex.Lock(); + LockGuard lock(midiMapsMutex); + std::map<int,MidiInstrumentMap>::iterator iterMap = midiMaps.find(Map); if (iterMap == midiMaps.end()) { // no such map - midiMapsMutex.Unlock(); throw Exception("There is no MIDI instrument map " + ToString(Map)); } @@ -224,7 +225,6 @@ std::map<midi_prog_index_t,private_entry_t>::iterator iterEntry = iterMap->second.find(idx); if (iterEntry == iterMap->second.end()) { - midiMapsMutex.Unlock(); throw Exception("There is no map entry with that index"); } @@ -238,25 +238,23 @@ try { SetLoadMode(&entry); } catch(Exception e) { - midiMapsMutex.Unlock(); throw e; } - midiMapsMutex.Unlock(); - return entry; } void MidiInstrumentMapper::RemoveEntry(int Map, midi_prog_index_t Index) { int InstrCount = -1; + { + LockGuard lock(midiMapsMutex); - midiMapsMutex.Lock(); - std::map<int,MidiInstrumentMap>::iterator iterMap = midiMaps.find(Map); - if (iterMap != midiMaps.end()) { // map found - iterMap->second.erase(Index); // remove entry - InstrCount = iterMap->second.size(); + std::map<int,MidiInstrumentMap>::iterator iterMap = midiMaps.find(Map); + if (iterMap != midiMaps.end()) { // map found + iterMap->second.erase(Index); // remove entry + InstrCount = iterMap->second.size(); + } } - midiMapsMutex.Unlock(); if (InstrCount != -1) { fireMidiInstrumentCountChanged(Map, InstrCount); @@ -265,14 +263,15 @@ void MidiInstrumentMapper::RemoveAllEntries(int Map) { int InstrCount = -1; + { + LockGuard lock(midiMapsMutex); - midiMapsMutex.Lock(); - std::map<int,MidiInstrumentMap>::iterator iterMap = midiMaps.find(Map); - if (iterMap != midiMaps.end()) { // map found - iterMap->second.clear(); // clear that map - InstrCount = 0; + std::map<int,MidiInstrumentMap>::iterator iterMap = midiMaps.find(Map); + if (iterMap != midiMaps.end()) { // map found + iterMap->second.clear(); // clear that map + InstrCount = 0; + } } - midiMapsMutex.Unlock(); if (InstrCount != -1) { fireMidiInstrumentCountChanged(Map, InstrCount); @@ -283,24 +282,25 @@ std::map<midi_prog_index_t,entry_t> result; // copy the internal map first - midiMapsMutex.Lock(); - std::map<int,MidiInstrumentMap>::iterator iterMap = midiMaps.find(Map); - if (iterMap == midiMaps.end()) { // no such map - midiMapsMutex.Unlock(); - throw Exception("There is no MIDI instrument map " + ToString(Map)); - } - for (std::map<midi_prog_index_t,private_entry_t>::iterator iterEntry = iterMap->second.begin(); - iterEntry != iterMap->second.end(); iterEntry++) { - entry_t entry; - entry.EngineName = iterEntry->second.EngineName; - entry.InstrumentFile = iterEntry->second.InstrumentFile; - entry.InstrumentIndex = iterEntry->second.InstrumentIndex; - entry.Volume = iterEntry->second.Volume; - entry.Name = iterEntry->second.Name; - resultiterEntry->first = entry; + LockGuard lock(midiMapsMutex); + + std::map<int,MidiInstrumentMap>::iterator iterMap = midiMaps.find(Map); + if (iterMap == midiMaps.end()) { // no such map + throw Exception("There is no MIDI instrument map " + ToString(Map)); + } + for (std::map<midi_prog_index_t,private_entry_t>::iterator iterEntry = iterMap->second.begin(); + iterEntry != iterMap->second.end(); iterEntry++) + { + entry_t entry; + entry.EngineName = iterEntry->second.EngineName; + entry.InstrumentFile = iterEntry->second.InstrumentFile; + entry.InstrumentIndex = iterEntry->second.InstrumentIndex; + entry.Volume = iterEntry->second.Volume; + entry.Name = iterEntry->second.Name; + resultiterEntry->first = entry; + } } - midiMapsMutex.Unlock(); // complete it with current LoadMode of each entry for (std::map<midi_prog_index_t,entry_t>::iterator iter = result.begin(); @@ -318,52 +318,46 @@ std::vector<int> MidiInstrumentMapper::Maps() { std::vector<int> result; - midiMapsMutex.Lock(); + LockGuard lock(midiMapsMutex); for (std::map<int,MidiInstrumentMap>::iterator iterMap = midiMaps.begin(); iterMap != midiMaps.end(); iterMap++) { result.push_back(iterMap->first); } - midiMapsMutex.Unlock(); return result; } int MidiInstrumentMapper::GetMapCount() { - midiMapsMutex.Lock(); - int i = midiMaps.size(); - midiMapsMutex.Unlock(); - return i; + LockGuard lock(midiMapsMutex); + return midiMaps.size(); } int MidiInstrumentMapper::GetInstrumentCount(int Map) { - midiMapsMutex.Lock(); + LockGuard lock(midiMapsMutex); std::map<int,MidiInstrumentMap>::iterator iterMap = midiMaps.find(Map); if (iterMap == midiMaps.end()) { // no such map - midiMapsMutex.Unlock(); throw Exception("There is no MIDI instrument map " + ToString(Map)); } - int i = iterMap->second.size(); - midiMapsMutex.Unlock(); - return i; + return iterMap->second.size(); } int MidiInstrumentMapper::GetInstrumentCount() { int count = 0; - midiMapsMutex.Lock(); + LockGuard lock(midiMapsMutex); std::map<int,MidiInstrumentMap>::iterator iterMap = midiMaps.begin(); for (;iterMap != midiMaps.end(); iterMap++) { count += iterMap->second.size(); } - midiMapsMutex.Unlock(); return count; } int MidiInstrumentMapper::AddMap(String MapName) throw (Exception) { int ID; - midiMapsMutex.Lock(); + LockGuard lock(midiMapsMutex); + if (midiMaps.empty()) ID = 0; else { // get the highest existing map ID @@ -387,72 +381,69 @@ fireMidiInstrumentMapCountChanged(Maps().size()); // If there were no maps until now we must set a default map. if (midiMaps.size() == 1) SetDefaultMap(ID); - midiMapsMutex.Unlock(); return ID; } String MidiInstrumentMapper::MapName(int Map) throw (Exception) { - String result; - midiMapsMutex.Lock(); + LockGuard lock(midiMapsMutex); std::map<int,MidiInstrumentMap>::iterator iterMap = midiMaps.find(Map); if (iterMap == midiMaps.end()) { - midiMapsMutex.Unlock(); throw Exception("There is no MIDI instrument map " + ToString(Map)); } - result = iterMap->second.name; - midiMapsMutex.Unlock(); - return result; + return iterMap->second.name; } void MidiInstrumentMapper::RenameMap(int Map, String NewName) throw (Exception) { - midiMapsMutex.Lock(); - std::map<int,MidiInstrumentMap>::iterator iterMap = midiMaps.find(Map); - if (iterMap == midiMaps.end()) { - midiMapsMutex.Unlock(); - throw Exception("There is no MIDI instrument map " + ToString(Map)); + { + LockGuard lock(midiMapsMutex); + std::map<int,MidiInstrumentMap>::iterator iterMap = midiMaps.find(Map); + if (iterMap == midiMaps.end()) { + throw Exception("There is no MIDI instrument map " + ToString(Map)); + } + iterMap->second.name = NewName; } - iterMap->second.name = NewName; - midiMapsMutex.Unlock(); fireMidiInstrumentMapInfoChanged(Map); } void MidiInstrumentMapper::RemoveMap(int Map) { - midiMapsMutex.Lock(); + LockGuard lock(midiMapsMutex); + midiMaps.erase(Map); - if(Map == GetDefaultMap()) { + if (Map == GetDefaultMap()) { SetDefaultMap(midiMaps.empty() ? -1 : (*(midiMaps.begin())).first); } fireMidiInstrumentMapCountChanged(Maps().size()); - midiMapsMutex.Unlock(); } void MidiInstrumentMapper::RemoveAllMaps() { - midiMapsMutex.Lock(); + LockGuard lock(midiMapsMutex); + midiMaps.clear(); SetDefaultMap(-1); fireMidiInstrumentMapCountChanged(Maps().size()); - midiMapsMutex.Unlock(); } int MidiInstrumentMapper::GetDefaultMap() { - midiMapsMutex.Lock(); - int i = DefaultMap; - midiMapsMutex.Unlock(); - return i; + LockGuard lock(midiMapsMutex); + + return DefaultMap; } void MidiInstrumentMapper::SetDefaultMap(int MapId) { - midiMapsMutex.Lock(); - DefaultMap = MapId; - midiMapsMutex.Unlock(); + { + LockGuard lock(midiMapsMutex); + + DefaultMap = MapId; + } if (MapId != -1) fireMidiInstrumentMapInfoChanged(MapId); } optional<MidiInstrumentMapper::entry_t> MidiInstrumentMapper::GetEntry(int Map, midi_prog_index_t Index) { optional<entry_t> result; - midiMapsMutex.Lock(); + LockGuard lock(midiMapsMutex); + std::map<int,MidiInstrumentMap>::iterator iterMap = midiMaps.find(Map); if (iterMap != midiMaps.end()) { // map found std::map<midi_prog_index_t,private_entry_t>::iterator iterEntry = iterMap->second.find(Index); @@ -466,7 +457,6 @@ result = entry; } } - midiMapsMutex.Unlock(); return result; }
View file
linuxsampler-2342.tar.bz2/src/drivers/midi/VirtualMidiDevice.cpp -> linuxsampler-2718.tar.bz2/src/drivers/midi/VirtualMidiDevice.cpp
Changed
@@ -1,5 +1,5 @@ /* - Copyright (C) 2008 - 2009 Christian Schoenebeck + Copyright (C) 2008 - 2014 Christian Schoenebeck */ #include "VirtualMidiDevice.h" @@ -50,9 +50,16 @@ VirtualMidiDevice::~VirtualMidiDevice() { delete p; } + + void VirtualMidiDevice::SetMaxEvents(int n) { + p->events.resize(n); + } bool VirtualMidiDevice::SendNoteOnToSampler(uint8_t Key, uint8_t Velocity) { if (Key >= MIDI_KEYS || Velocity > 127) return false; + if (Velocity == 0) { + return SendNoteOffToSampler(Key, Velocity); + } event_t ev = { EVENT_TYPE_NOTEON, Key, Velocity }; if (p->events.write_space() <= 0) return false; p->events.push(&ev); @@ -75,6 +82,28 @@ return true; } + bool VirtualMidiDevice::SendPitchBendToSampler(int Pitch) { + if (Pitch < -8192 || Pitch > 8191) return false; + Pitch += 8192; + // order: LSB, MSB like it would be in a regular pitch bend MIDI message + event_t ev = { + EVENT_TYPE_PITCHBEND, + static_cast<uint8_t>(Pitch & 0x7f), + static_cast<uint8_t>((Pitch >> 7) & 0x7f) + }; + if (p->events.write_space() <= 0) return false; + p->events.push(&ev); + return true; + } + + bool VirtualMidiDevice::SendProgramChangeToSampler(uint8_t Program) { + if (Program > 127) return false; + event_t ev = { EVENT_TYPE_PROGRAM, Program, 0 }; + if (p->events.write_space() <= 0) return false; + p->events.push(&ev); + return true; + } + bool VirtualMidiDevice::GetMidiEventFromDevice(event_t& Event) { return (p->events.pop(&Event) > 0); } @@ -121,6 +150,10 @@ void VirtualMidiDevice::SendNoteOnToDevice(uint8_t Key, uint8_t Velocity) { if (Key >= MIDI_KEYS) return; + if (Velocity == 0) { + SendNoteOffToDevice(Key, Velocity); + return; + } atomic_set( &(p->pNoteOnVelocity)Key, Velocity ); atomic_inc( &(p->pNoteIsActive)Key ); atomic_inc( &(p->pNoteChanged)Key ); @@ -130,7 +163,8 @@ void VirtualMidiDevice::SendNoteOffToDevice(uint8_t Key, uint8_t Velocity) { if (Key >= MIDI_KEYS) return; atomic_set( &(p->pNoteOffVelocity)Key, Velocity ); - atomic_dec( &(p->pNoteIsActive)Key ); + if (atomic_read( &(p->pNoteIsActive)Key )) // only decrement if not zero + atomic_dec( &(p->pNoteIsActive)Key ); atomic_inc( &(p->pNoteChanged)Key ); atomic_inc( &p->notesChanged ); }
View file
linuxsampler-2342.tar.bz2/src/drivers/midi/VirtualMidiDevice.h -> linuxsampler-2718.tar.bz2/src/drivers/midi/VirtualMidiDevice.h
Changed
@@ -1,5 +1,5 @@ /* - Copyright (C) 2008 - 2009 Christian Schoenebeck + Copyright (C) 2008 - 2014 Christian Schoenebeck */ #ifndef LS_VIRTUALMIDIDEVICE_H @@ -22,7 +22,9 @@ enum event_type_t { EVENT_TYPE_NOTEON = 1, EVENT_TYPE_NOTEOFF = 2, - EVENT_TYPE_CC = 3 + EVENT_TYPE_CC = 3, + EVENT_TYPE_PITCHBEND, + EVENT_TYPE_PROGRAM }; struct event_t { @@ -60,6 +62,30 @@ bool SendCCToSampler(uint8_t Controller, uint8_t Value); /** + * Sends a MIDI @e Pitch @e Bend event to the sampler. + * + * @param Pitch - MIDI pitch value (-8192 ... +8191) + * + * @returns true on success, false if internal FIFO full + * (or provided pitch value out of valid range) + */ + bool SendPitchBendToSampler(int Pitch); + + /** + * Sends a MIDI @e Program @e Change event to the sampler. + * + * If you want to change the sound bank, call SendCCToSampler() (with + * controller = 0 for bank select MSB and/or controller = 32 for bank select + * LSB) before calling this method. + * + * @param Program - MIDI program number + * + * @returns true on success, false if internal FIFO full + * (or provided value invalid) + */ + bool SendProgramChangeToSampler(uint8_t Program); + + /** * Can be called by the virtual MIDI device to check whether a new note * on or note off MIDI event arrived to the sampler during the last * call to this method. So this is a asynchronously, "polling" based @@ -169,6 +195,19 @@ * @returns true on success, false if no event pending */ bool GetMidiEventFromDevice(event_t& Event); + + ///////////////////////////////////////////////////////////////// + // General Purpose Methods + + /** + * Adjusts the internal event buffer to cover at least the given + * amount of MIDI events. This might be useful, since the internal + * event buffer is by default quite small (i.e. just 12 events). + * + * This method is not thread safe! Any operations upon this device + * have to be stopped before calling this method! + */ + void SetMaxEvents(int n); /** * Constructor
View file
linuxsampler-2342.tar.bz2/src/drivers/midi/midi.h -> linuxsampler-2718.tar.bz2/src/drivers/midi/midi.h
Changed
@@ -1,6 +1,6 @@ /*************************************************************************** * * - * Copyright (C) 2005, 2006 Christian Schoenebeck * + * Copyright (C) 2005, 2006, 2014 Christian Schoenebeck * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -64,6 +64,10 @@ } }; + inline bool isValidMidiChan(const midi_chan_t& ch) { + return ch >= 0 && ch <= midi_chan_all; + } + } // namsepace LinuxSampler #endif // __LS_MIDI_H__
View file
linuxsampler-2342.tar.bz2/src/effects/Effect.h -> linuxsampler-2718.tar.bz2/src/effects/Effect.h
Changed
@@ -1,6 +1,6 @@ /*************************************************************************** * * - * Copyright (C) 2008, 2010 Christian Schoenebeck * + * Copyright (C) 2008, 2010, 2013 Christian Schoenebeck * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -65,6 +65,12 @@ * This is the perfect place to create the required audio input and * output channels! ;-) * + * CAUTION: InitEffect() might be called several times! For example it + * will be called again if some audio context parameter of the audio + * output driver in use, has been changed, like particulary sample rate + * changes and max. samples per cycle (period size) changes. So take + * care not to create memory leaks due to this circumstance. + * * @param pDevice - audio output device which is going to play the signal * @throws Exception - if effect could not be initialized successfully */
View file
linuxsampler-2342.tar.bz2/src/effects/EffectChain.cpp -> linuxsampler-2718.tar.bz2/src/effects/EffectChain.cpp
Changed
@@ -1,6 +1,6 @@ /*************************************************************************** * * - * Copyright (C) 2008 - 2010 Christian Schoenebeck * + * Copyright (C) 2008 - 2013 Christian Schoenebeck * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -95,6 +95,13 @@ int EffectChain::EffectCount() const { return vEntries.size(); } + +void EffectChain::Reconnect(AudioOutputDevice* pDevice) { + for (int i = 0; i < vEntries.size(); ++i) { + Effect* pEffect = vEntriesi.pEffect; + pEffect->InitEffect(pDevice); + } +} void EffectChain::SetEffectActive(int iChainPos, bool bOn) throw (Exception) { if (iChainPos < 0 || iChainPos >= vEntries.size())
View file
linuxsampler-2342.tar.bz2/src/effects/EffectChain.h -> linuxsampler-2718.tar.bz2/src/effects/EffectChain.h
Changed
@@ -1,6 +1,6 @@ /*************************************************************************** * * - * Copyright (C) 2008 - 2010 Christian Schoenebeck * + * Copyright (C) 2008 - 2013 Christian Schoenebeck * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -39,7 +39,7 @@ * Constructor. * * @param pDevice - audio output context for the effects, providing - * informations like samplerate + * informations like samplerate and buffer size * @param iEffectChainId - (optional) numerical ID of the effect chain, * intended for master effect chains, unique among * all master effect chains which share the same @@ -97,6 +97,18 @@ * Whether the given effect is currently enabled. */ bool IsEffectActive(int iChainPos) const; + + /** + * Should / will be called whenever the effect chaing is moving to + * another audio output device and also when an important audio + * parameter like sample rate or buffer size has been changed. + * Calling this method will cause all effects to inform about this + * change and prepare them for the new given audio output device. + * + * @param pDevice - audio output context for the effects, providing + * informations like samplerate and buffer size + */ + void Reconnect(AudioOutputDevice* pDevice); /** * Clears the audio input and output channels of all effects in the chain.
View file
linuxsampler-2342.tar.bz2/src/effects/LadspaEffect.cpp -> linuxsampler-2718.tar.bz2/src/effects/LadspaEffect.cpp
Changed
@@ -1,5 +1,5 @@ /* - Copyright (C) 2010 - 2012 Christian Schoenebeck + Copyright (C) 2010 - 2014 Christian Schoenebeck */ #include "LadspaEffect.h" @@ -378,20 +378,20 @@ char* pcLadspaPath = getenv("LADSPA_PATH"); String ladspaDir = pcLadspaPath ? pcLadspaPath : defaultLadspaDir(); - try { - std::istringstream ss(ladspaDir); - std::string path; - while (std::getline(ss, path, File::PathSeparator)) { - if (!path.empty()) { + std::istringstream ss(ladspaDir); + std::string path; + while (std::getline(ss, path, File::PathSeparator)) { + if (!path.empty()) { + try { DynamicLibrariesSearch(path.c_str(), "ladspa_descriptor", _foundLadspaDll, &v); + } catch (Exception e) { + std::cerr << "Could not scan LADSPA effects: " << e.Message() + << std::endl << std::flush; + } catch (...) { + std::cerr << "Could not scan LADSPA effects: unknown exception\n" + << std::flush; } } - } catch (Exception e) { - std::cerr << "Could not scan LADSPA effects: " << e.Message() - << std::endl << std::flush; - } catch (...) { - std::cerr << "Could not scan LADSPA effects: unknown exception\n" - << std::flush; } return v;
View file
linuxsampler-2342.tar.bz2/src/effects/LadspaEffect.h -> linuxsampler-2718.tar.bz2/src/effects/LadspaEffect.h
Changed
@@ -1,5 +1,5 @@ /* - Copyright (C) 2010 Christian Schoenebeck + Copyright (C) 2010 - 2013 Christian Schoenebeck */ #ifndef LS_LADSPAEFFECT_H @@ -27,9 +27,9 @@ public: LadspaEffect(EffectInfo* pInfo) throw (Exception); ~LadspaEffect(); - EffectInfo* GetEffectInfo(); - void RenderAudio(uint Samples); - void InitEffect(AudioOutputDevice* pDevice) throw (Exception); + EffectInfo* GetEffectInfo() OVERRIDE; + void RenderAudio(uint Samples) OVERRIDE; + void InitEffect(AudioOutputDevice* pDevice) throw (Exception) OVERRIDE; static std::vector<EffectInfo*> AvailableEffects(); private:
View file
linuxsampler-2342.tar.bz2/src/effects/Makefile.am -> linuxsampler-2718.tar.bz2/src/effects/Makefile.am
Changed
@@ -1,4 +1,4 @@ -INCLUDES = $(all_includes) +AM_CPPFLAGS = $(all_includes) AM_CXXFLAGS = -Wreturn-type -ffast-math $(CXX_CPU_SWITCH) liblinuxsamplereffectsincludedir = $(includedir)/linuxsampler/effects
View file
linuxsampler-2342.tar.bz2/src/engines/AbstractEngine.cpp -> linuxsampler-2718.tar.bz2/src/engines/AbstractEngine.cpp
Changed
@@ -4,7 +4,8 @@ * * * Copyright (C) 2003,2004 by Benno Senoner and Christian Schoenebeck * * Copyright (C) 2005-2008 Christian Schoenebeck * - * Copyright (C) 2009-2010 Christian Schoenebeck and Grigor Iliev * + * Copyright (C) 2009-2012 Christian Schoenebeck and Grigor Iliev * + * Copyright (C) 2013-2014 Christian Schoenebeck and Andreas Persson * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -60,6 +61,7 @@ } else { // create a new engine (and disk thread) instance for the given audio output device dmsg(4,("Creating new Engine.\n")); pEngine = (AbstractEngine*) EngineFactory::Create(pChannel->EngineName()); + pEngine->CreateInstrumentScriptVM(); pEngine->Connect(pDevice); enginespChannel->GetEngineFormat()pDevice = pEngine; } @@ -81,6 +83,7 @@ FrameTime = 0; RandomSeed = 0; pDedicatedVoiceChannelLeft = pDedicatedVoiceChannelRight = NULL; + pScriptVM = NULL; } AbstractEngine::~AbstractEngine() { @@ -91,9 +94,16 @@ if (pSysexBuffer) delete pSysexBuffer; if (pDedicatedVoiceChannelLeft) delete pDedicatedVoiceChannelLeft; if (pDedicatedVoiceChannelRight) delete pDedicatedVoiceChannelRight; + if (pScriptVM) delete pScriptVM; Unregister(); } + void AbstractEngine::CreateInstrumentScriptVM() { + dmsg(2,("Created sampler format independent instrument script VM.\n")); + if (pScriptVM) return; + pScriptVM = new InstrumentScriptVM; // format independent script runner + } + /** * Once an engine channel is disconnected from an audio output device, * it will immediately call this method to unregister itself from the @@ -165,6 +175,7 @@ */ void AbstractEngine::ResetScaleTuning() { memset(&ScaleTuning0, 0x00, 12); + ScaleTuningChanged.raise(); } /** @@ -346,11 +357,13 @@ */ uint8_t AbstractEngine::GSCheckSum(const RingBuffer<uint8_t,false>::NonVolatileReader AddrReader, uint DataSize) { RingBuffer<uint8_t,false>::NonVolatileReader reader = AddrReader; - uint bytes = 3 /*addr*/ + DataSize; - uint8_t addr_and_databytes; - reader.read(&addr_and_data0, bytes); + uint bytes = 3 /*addr*/ + DataSize; uint8_t sum = 0; - for (uint i = 0; i < bytes; i++) sum += addr_and_datai; + uint8_t c; + for (uint i = 0; i < bytes; ++i) { + if (!reader.pop(&c)) break; + sum += c; + } return 128 - sum % 128; } @@ -359,8 +372,13 @@ * * @param ScaleTunes - detuning of all twelve semitones (in cents) */ - void AbstractEngine::AdjustScale(int8_t ScaleTunes12) { - memcpy(&this->ScaleTuning0, &ScaleTunes0, 12); //TODO: currently not sample accurate + void AbstractEngine::AdjustScaleTuning(const int8_t ScaleTunes12) { + memcpy(&this->ScaleTuning0, &ScaleTunes0, 12); + ScaleTuningChanged.raise(); + } + + void AbstractEngine::GetScaleTuning(int8_t* pScaleTunes) { + memcpy(pScaleTunes, &this->ScaleTuning0, 12); } uint AbstractEngine::VoiceCount() { @@ -376,9 +394,7 @@ } /** - * Moves pitchbend event from the general (input) event list to the engine - * channel's event list. It will actually processed later by the - * respective voice. + * Stores the latest pitchbend event as current pitchbend scalar value. * * @param pEngineChannel - engine channel on which this event occured on * @param itPitchbendEvent - absolute pitch value and time stamp of the event @@ -415,6 +431,7 @@ Event event = pEventGenerator->CreateEvent(); event.Type = Event::type_sysex; event.Param.Sysex.Size = Size; + memset(&event.Format, 0, sizeof(event.Format)); // init format speific stuff with zeroes event.pEngineChannel = NULL; // as Engine global event event.pMidiInputPort = pSender; if (pEventQueue->write_space() > 0) { @@ -507,9 +524,13 @@ for (int i = 0; i < engineChannels.size(); ++i) { AbstractEngineChannel* pEngineChannel = static_cast<AbstractEngineChannel*>(engineChannelsi); - if (pEngineChannel->GetMidiInputPort() == itSysexEvent->pMidiInputPort) { - KillAllVoices(pEngineChannel, itSysexEvent); - pEngineChannel->ResetControllers(); + Sync< ArrayList<MidiInputPort*> > midiInputs = pEngineChannel->midiInputs.front(); + for (int k = 0; k < midiInputs->size(); ++k) { + if ((*midiInputs)k == itSysexEvent->pMidiInputPort) { + KillAllVoices(pEngineChannel, itSysexEvent); + pEngineChannel->ResetControllers(); + break; + } } } } @@ -530,7 +551,7 @@ if (GSCheckSum(checksum_reader, 12)) goto free_sysex_data; #endif // CONFIG_ASSERT_GS_SYSEX_CHECKSUM for (int i = 0; i < 12; i++) scale_tunesi -= 64; - AdjustScale((int8_t*) scale_tunes); + AdjustScaleTuning((int8_t*) scale_tunes); dmsg(3,("\t\t\tNew scale applied.\n")); break; } @@ -542,19 +563,23 @@ for (int i = 0; i < engineChannels.size(); ++i) { AbstractEngineChannel* pEngineChannel = static_cast<AbstractEngineChannel*>(engineChannelsi); - if ( - (pEngineChannel->midiChannel == part || - pEngineChannel->midiChannel == midi_chan_all) && - pEngineChannel->GetMidiInputPort() == itSysexEvent->pMidiInputPort - ) { - try { - pEngineChannel->SetMidiInstrumentMap(map); - } catch (Exception e) { - dmsg(2,("\t\t\tCould not apply MIDI instrument map %d to part %d: %s\n", map, part, e.Message().c_str())); - goto free_sysex_data; - } catch (...) { - dmsg(2,("\t\t\tCould not apply MIDI instrument map %d to part %d (unknown exception)\n", map, part)); - goto free_sysex_data; + if (pEngineChannel->midiChannel == part || + pEngineChannel->midiChannel == midi_chan_all) + { + Sync< ArrayList<MidiInputPort*> > midiInputs = pEngineChannel->midiInputs.front(); + for (int k = 0; k < midiInputs->size(); ++k) { + if ((*midiInputs)k == itSysexEvent->pMidiInputPort) { + try { + pEngineChannel->SetMidiInstrumentMap(map); + } catch (Exception e) { + dmsg(2,("\t\t\tCould not apply MIDI instrument map %d to part %d: %s\n", map, part, e.Message().c_str())); + goto free_sysex_data; + } catch (...) { + dmsg(2,("\t\t\tCould not apply MIDI instrument map %d to part %d (unknown exception)\n", map, part)); + goto free_sysex_data; + } + break; + } } } }
View file
linuxsampler-2342.tar.bz2/src/engines/AbstractEngine.h -> linuxsampler-2718.tar.bz2/src/engines/AbstractEngine.h
Changed
@@ -4,7 +4,7 @@ * * * Copyright (C) 2003,2004 by Benno Senoner and Christian Schoenebeck * * Copyright (C) 2005-2008 Christian Schoenebeck * - * Copyright (C) 2009-2010 Christian Schoenebeck and Grigor Iliev * + * Copyright (C) 2009-2013 Christian Schoenebeck and Grigor Iliev * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -31,9 +31,12 @@ #include "../common/ConditionServer.h" #include "../common/Pool.h" #include "../common/RingBuffer.h" +#include "../common/ChangeFlagRelaxed.h" +#include "../common/ResourceManager.h" #include "../drivers/audio/AudioOutputDevice.h" #include "common/Event.h" #include "common/SignalUnitRack.h" +#include "common/InstrumentScriptVM.h" namespace LinuxSampler { @@ -51,26 +54,37 @@ virtual ~AbstractEngine(); // implementation of abstract methods derived from class 'LinuxSampler::Engine' - virtual void SendSysex(void* pData, uint Size, MidiInputPort* pSender); - virtual void Reset(); - virtual void Enable(); - virtual void Disable(); - virtual uint VoiceCount(); - virtual uint VoiceCountMax(); - virtual String EngineName(); + virtual void SendSysex(void* pData, uint Size, MidiInputPort* pSender) OVERRIDE; + virtual void Reset() OVERRIDE; + virtual void Enable() OVERRIDE; + virtual void Disable() OVERRIDE; + virtual uint VoiceCount() OVERRIDE; + virtual uint VoiceCountMax() OVERRIDE; + virtual String EngineName() OVERRIDE; + virtual void AdjustScaleTuning(const int8_t ScaleTunes12) OVERRIDE; + virtual void GetScaleTuning(int8_t* pScaleTunes) OVERRIDE; + virtual void ResetScaleTuning() OVERRIDE; virtual Format GetEngineFormat() = 0; virtual void Connect(AudioOutputDevice* pAudioOut) = 0; virtual void DisableAndLock(); - void SetVoiceCount(uint Count);// Simple array wrapper just to make sure memory is freed - // when liblinuxsampler is unloaded + void SetVoiceCount(uint Count); + + /** + * Returns event with the given event ID. + */ + RTList<Event>::Iterator EventByID(int id) { + return pEventPool->fromID(id); + } float Random() { RandomSeed = RandomSeed * 1103515245 + 12345; // classic pseudo random number generator return RandomSeed / 4294967296.0f; } - + + // Simple array wrapper just to make sure memory is freed + // when liblinuxsampler is unloaded class FloatTable { private: const float* array; @@ -95,10 +109,11 @@ template<class V, class R, class I> friend class EngineChannelBase; template<class EC, class R, class S, class D> friend class VoiceBase; - protected: + //protected: ArrayList<EngineChannel*> engineChannels; ///< All engine channels of a Engine instance. ConditionServer EngineDisabled; int8_t ScaleTuning12; ///< contains optional detune factors (-64..+63 cents) for all 12 semitones of an octave + ChangeFlagRelaxed ScaleTuningChanged; ///< Boolean flag indicating whenever ScaleTuning has been modified by a foreign thread (i.e. by API). RingBuffer<Event,false>* pEventQueue; ///< Input event queue for engine global events (e.g. SysEx messages). EventGenerator* pEventGenerator; RTList<Event>* pGlobalEvents; ///< All engine global events for the current audio fragment (usually only SysEx messages). @@ -110,13 +125,13 @@ int ActiveVoiceCountMax; ///< the maximum voice usage since application start atomic_t ActiveVoiceCount; ///< number of currently active voices int VoiceSpawnsLeft; ///< We only allow CONFIG_MAX_VOICES voices to be spawned per audio fragment, we use this variable to ensure this limit. + InstrumentScriptVM* pScriptVM; ///< Real-time instrument script virtual machine runner for this engine. void RouteAudio(EngineChannel* pEngineChannel, uint Samples); void RouteDedicatedVoiceChannels(EngineChannel* pEngineChannel, optional<float> FxSendLevels2, uint Samples); void ClearEventLists(); void ImportEvents(uint Samples); void ProcessSysex(Pool<Event>::Iterator& itSysexEvent); - void ResetScaleTuning(); void ProcessPitchbend(AbstractEngineChannel* pEngineChannel, Pool<Event>::Iterator& itPitchbendEvent); void ProcessFxSendControllers ( @@ -131,7 +146,10 @@ virtual void ProcessNoteOn(EngineChannel* pEngineChannel, Pool<Event>::Iterator& itNoteOnEvent) = 0; virtual void ProcessNoteOff(EngineChannel* pEngineChannel, Pool<Event>::Iterator& itNoteOffEvent) = 0; virtual void ProcessControlChange(EngineChannel* pEngineChannel, Pool<Event>::Iterator& itControlChangeEvent) = 0; + virtual void ProcessChannelPressure(EngineChannel* pEngineChannel, Pool<Event>::Iterator& itChannelPressureEvent) = 0; + virtual void ProcessPolyphonicKeyPressure(EngineChannel* pEngineChannel, Pool<Event>::Iterator& itNotePressureEvent) = 0; virtual int GetMinFadeOutSamples() = 0; + virtual void CreateInstrumentScriptVM(); private: static std::map<Format, std::map<AudioOutputDevice*,AbstractEngine*> > engines; @@ -142,7 +160,6 @@ static float* InitCrossfadeCurve(); static float* InitCurve(const float* segments, int size = 128); - void AdjustScale(int8_t ScaleTunes12); bool RouteFxSend(FxSend* pFxSend, AudioChannel* ppSource2, float FxSendLevel, uint Samples); };
View file
linuxsampler-2342.tar.bz2/src/engines/AbstractEngineChannel.cpp -> linuxsampler-2718.tar.bz2/src/engines/AbstractEngineChannel.cpp
Changed
@@ -5,6 +5,7 @@ * Copyright (C) 2003,2004 by Benno Senoner and Christian Schoenebeck * * Copyright (C) 2005-2008 Christian Schoenebeck * * Copyright (C) 2009-2012 Christian Schoenebeck and Grigor Iliev * + * Copyright (C) 2013-2014 Christian Schoenebeck and Andreas Persson * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -41,11 +42,11 @@ pChannelRight = NULL; AudioDeviceChannelLeft = -1; AudioDeviceChannelRight = -1; - pMidiInputPort = NULL; midiChannel = midi_chan_all; ResetControllers(); PortamentoMode = false; PortamentoTime = CONFIG_PORTAMENTO_TIME_DEFAULT; + pScript = NULL; } AbstractEngineChannel::~AbstractEngineChannel() { @@ -112,8 +113,6 @@ Pitch = 0; GlobalVolume = 1.0f; MidiVolume = 1.0; - GlobalPanLeft = 1.0f; - GlobalPanRight = 1.0f; iLastPanRequest = 64; GlobalTranspose = 0; // set all MIDI controller values to zero @@ -182,8 +181,6 @@ int iMidiPan = int(f * 64.0f) + 64; if (iMidiPan > 127) iMidiPan = 127; else if (iMidiPan < 0) iMidiPan = 0; - GlobalPanLeft = AbstractEngine::PanCurve128 - iMidiPan; - GlobalPanRight = AbstractEngine::PanCurveiMidiPan; iLastPanRequest = iMidiPan; } @@ -196,10 +193,8 @@ * device from other threads than the lscp thread. */ AudioOutputDevice* AbstractEngineChannel::GetAudioOutputDeviceSafe() { - EngineMutex.Lock(); - AudioOutputDevice* res = GetAudioOutputDevice(); - EngineMutex.Unlock(); - return res; + LockGuard lock(EngineMutex); + return GetAudioOutputDevice(); } void AbstractEngineChannel::SetOutputChannel(uint EngineAudioChannel, uint AudioDeviceChannel) { @@ -234,28 +229,109 @@ } } + void AbstractEngineChannel::Connect(MidiInputPort* pMidiPort) { + if (!pMidiPort) return; + + Sync< ArrayList<MidiInputPort*> > connections = midiInputs.back(); + + // check if connection already exists + for (int i = 0; i < connections->size(); ++i) + if ((*connections)i == pMidiPort) + return; // to avoid endless recursion + + connections->add(pMidiPort); + + // inform MIDI port about this new connection + pMidiPort->Connect(this, MidiChannel()); + } + + void AbstractEngineChannel::Disconnect(MidiInputPort* pMidiPort) { + if (!pMidiPort) return; + + Sync< ArrayList<MidiInputPort*> > connections = midiInputs.back(); + + for (int i = 0; i < connections->size(); ++i) { + if ((*connections)i == pMidiPort) { + connections->remove(i); + // inform MIDI port about this disconnection + pMidiPort->Disconnect(this); + return; + } + } + } + + void AbstractEngineChannel::DisconnectAllMidiInputPorts() { + Sync< ArrayList<MidiInputPort*> > connections = midiInputs.back(); + ArrayList<MidiInputPort*> clonedList = *connections; + connections->clear(); + for (int i = 0; i < clonedList.size(); ++i) clonedListi->Disconnect(this); + } + + uint AbstractEngineChannel::GetMidiInputPortCount() { + Sync< ArrayList<MidiInputPort*> > connections = midiInputs.back(); + return connections->size(); + } + + MidiInputPort* AbstractEngineChannel::GetMidiInputPort(uint index) { + Sync< ArrayList<MidiInputPort*> > connections = midiInputs.back(); + return (index < connections->size()) ? (*connections)index : NULL; + } + + // deprecated (just for API backward compatibility) - may be removed in future void AbstractEngineChannel::Connect(MidiInputPort* pMidiPort, midi_chan_t MidiChannel) { - if (!pMidiPort || pMidiPort == this->pMidiInputPort) return; - DisconnectMidiInputPort(); - this->pMidiInputPort = pMidiPort; - this->midiChannel = MidiChannel; + if (!pMidiPort) return; + + Sync< ArrayList<MidiInputPort*> > connections = midiInputs.back(); + + // check against endless recursion + if (connections->size() == 1 && (*connections)0 == pMidiPort && this->midiChannel == MidiChannel) + return; + + if (!isValidMidiChan(MidiChannel)) + throw MidiInputException("Invalid MIDI channel (" + ToString(int(MidiChannel)) + ")"); + + this->midiChannel = MidiChannel; + + // disconnect all currently connected MIDI ports + ArrayList<MidiInputPort*> clonedList = *connections; + connections->clear(); + for (int i = 0; i < clonedList.size(); ++i) + clonedListi->Disconnect(this); + + // connect the new port + connections->add(pMidiPort); pMidiPort->Connect(this, MidiChannel); } + // deprecated (just for API backward compatibility) - may be removed in future void AbstractEngineChannel::DisconnectMidiInputPort() { - MidiInputPort* pOldPort = this->pMidiInputPort; - this->pMidiInputPort = NULL; - if (pOldPort) pOldPort->Disconnect(this); + DisconnectAllMidiInputPorts(); } + // deprecated (just for API backward compatibility) - may be removed in future MidiInputPort* AbstractEngineChannel::GetMidiInputPort() { - return pMidiInputPort; + return GetMidiInputPort(0); } midi_chan_t AbstractEngineChannel::MidiChannel() { return midiChannel; } + void AbstractEngineChannel::SetMidiChannel(midi_chan_t MidiChannel) { + if (this->midiChannel == MidiChannel) return; + if (!isValidMidiChan(MidiChannel)) + throw MidiInputException("Invalid MIDI channel (" + ToString(int(MidiChannel)) + ")"); + + this->midiChannel = MidiChannel; + + Sync< ArrayList<MidiInputPort*> > connections = midiInputs.back(); + ArrayList<MidiInputPort*> clonedList = *connections; + + DisconnectAllMidiInputPorts(); + + for (int i = 0; i < clonedList.size(); ++i) Connect(clonedListi); + } + void AbstractEngineChannel::Connect(VirtualMidiDevice* pDevice) { // double buffer ... double work ... { @@ -291,11 +367,16 @@ */ void AbstractEngineChannel::SendNoteOn(uint8_t Key, uint8_t Velocity, uint8_t MidiChannel) { if (pEngine) { + // protection in case there are more than 1 MIDI input threads sending MIDI events to this EngineChannel + LockGuard g; + if (hasMultipleMIDIInputs()) g = LockGuard(MidiInputMutex); + Event event = pEngine->pEventGenerator->CreateEvent(); event.Type = Event::type_note_on; event.Param.Note.Key = Key; event.Param.Note.Velocity = Velocity; event.Param.Note.Channel = MidiChannel; + memset(&event.Format, 0, sizeof(event.Format)); // init format speific stuff with zeroes event.pEngineChannel = this; if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event); else dmsg(1,("EngineChannel: Input event queue full!")); @@ -328,11 +409,16 @@ dmsg(1,("EngineChannel::SendNoteOn(): negative FragmentPos! Seems MIDI driver is buggy!")); } else if (pEngine) { + // protection in case there are more than 1 MIDI input threads sending MIDI events to this EngineChannel + LockGuard g; + if (hasMultipleMIDIInputs()) g = LockGuard(MidiInputMutex); + Event event = pEngine->pEventGenerator->CreateEvent(FragmentPos); event.Type = Event::type_note_on; event.Param.Note.Key = Key; event.Param.Note.Velocity = Velocity; event.Param.Note.Channel = MidiChannel; + memset(&event.Format, 0, sizeof(event.Format)); // init format speific stuff with zeroes event.pEngineChannel = this; if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event); else dmsg(1,("EngineChannel: Input event queue full!")); @@ -360,11 +446,16 @@ */ void AbstractEngineChannel::SendNoteOff(uint8_t Key, uint8_t Velocity, uint8_t MidiChannel) { if (pEngine) { + // protection in case there are more than 1 MIDI input threads sending MIDI events to this EngineChannel + LockGuard g; + if (hasMultipleMIDIInputs()) g = LockGuard(MidiInputMutex); + Event event = pEngine->pEventGenerator->CreateEvent(); event.Type = Event::type_note_off; event.Param.Note.Key = Key; event.Param.Note.Velocity = Velocity; event.Param.Note.Channel = MidiChannel; + memset(&event.Format, 0, sizeof(event.Format)); // init format speific stuff with zeroes event.pEngineChannel = this; if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event); else dmsg(1,("EngineChannel: Input event queue full!")); @@ -397,11 +488,16 @@ dmsg(1,("EngineChannel::SendNoteOff(): negative FragmentPos! Seems MIDI driver is buggy!")); } else if (pEngine) { + // protection in case there are more than 1 MIDI input threads sending MIDI events to this EngineChannel + LockGuard g; + if (hasMultipleMIDIInputs()) g = LockGuard(MidiInputMutex); + Event event = pEngine->pEventGenerator->CreateEvent(FragmentPos); event.Type = Event::type_note_off; event.Param.Note.Key = Key; event.Param.Note.Velocity = Velocity; event.Param.Note.Channel = MidiChannel; + memset(&event.Format, 0, sizeof(event.Format)); // init format speific stuff with zeroes event.pEngineChannel = this; if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event); else dmsg(1,("EngineChannel: Input event queue full!")); @@ -428,10 +524,15 @@ */ void AbstractEngineChannel::SendPitchbend(int Pitch, uint8_t MidiChannel) { if (pEngine) { + // protection in case there are more than 1 MIDI input threads sending MIDI events to this EngineChannel + LockGuard g; + if (hasMultipleMIDIInputs()) g = LockGuard(MidiInputMutex); + Event event = pEngine->pEventGenerator->CreateEvent(); event.Type = Event::type_pitchbend; event.Param.Pitch.Pitch = Pitch; event.Param.Pitch.Channel = MidiChannel; + memset(&event.Format, 0, sizeof(event.Format)); // init format speific stuff with zeroes event.pEngineChannel = this; if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event); else dmsg(1,("EngineChannel: Input event queue full!")); @@ -453,10 +554,15 @@ dmsg(1,("AbstractEngineChannel::SendPitchBend(): negative FragmentPos! Seems MIDI driver is buggy!")); } else if (pEngine) { + // protection in case there are more than 1 MIDI input threads sending MIDI events to this EngineChannel + LockGuard g; + if (hasMultipleMIDIInputs()) g = LockGuard(MidiInputMutex); + Event event = pEngine->pEventGenerator->CreateEvent(FragmentPos); event.Type = Event::type_pitchbend; event.Param.Pitch.Pitch = Pitch; event.Param.Pitch.Channel = MidiChannel; + memset(&event.Format, 0, sizeof(event.Format)); // init format speific stuff with zeroes event.pEngineChannel = this; if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event); else dmsg(1,("AbstractEngineChannel: Input event queue full!")); @@ -474,11 +580,16 @@ */ void AbstractEngineChannel::SendControlChange(uint8_t Controller, uint8_t Value, uint8_t MidiChannel) { if (pEngine) { + // protection in case there are more than 1 MIDI input threads sending MIDI events to this EngineChannel + LockGuard g; + if (hasMultipleMIDIInputs()) g = LockGuard(MidiInputMutex); + Event event = pEngine->pEventGenerator->CreateEvent(); event.Type = Event::type_control_change; event.Param.CC.Controller = Controller; event.Param.CC.Value = Value; event.Param.CC.Channel = MidiChannel; + memset(&event.Format, 0, sizeof(event.Format)); // init format speific stuff with zeroes event.pEngineChannel = this; if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event); else dmsg(1,("AbstractEngineChannel: Input event queue full!")); @@ -501,17 +612,94 @@ dmsg(1,("AbstractEngineChannel::SendControlChange(): negative FragmentPos! Seems MIDI driver is buggy!")); } else if (pEngine) { + // protection in case there are more than 1 MIDI input threads sending MIDI events to this EngineChannel + LockGuard g; + if (hasMultipleMIDIInputs()) g = LockGuard(MidiInputMutex); + Event event = pEngine->pEventGenerator->CreateEvent(FragmentPos); event.Type = Event::type_control_change; event.Param.CC.Controller = Controller; event.Param.CC.Value = Value; event.Param.CC.Channel = MidiChannel; + memset(&event.Format, 0, sizeof(event.Format)); // init format speific stuff with zeroes event.pEngineChannel = this; if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event); else dmsg(1,("AbstractEngineChannel: Input event queue full!")); } } + void AbstractEngineChannel::SendChannelPressure(uint8_t Value, uint8_t MidiChannel) { + if (pEngine) { + // protection in case there are more than 1 MIDI input threads sending MIDI events to this EngineChannel + LockGuard g; + if (hasMultipleMIDIInputs()) g = LockGuard(MidiInputMutex); + + Event event = pEngine->pEventGenerator->CreateEvent(); + event.Type = Event::type_channel_pressure; + event.Param.ChannelPressure.Controller = CTRL_TABLE_IDX_AFTERTOUCH; // required for instrument scripts + event.Param.ChannelPressure.Value = Value; + event.Param.ChannelPressure.Channel = MidiChannel; + memset(&event.Format, 0, sizeof(event.Format)); // init format speific stuff with zeroes + event.pEngineChannel = this; + if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event); + else dmsg(1,("AbstractEngineChannel: Input event queue full!")); + } + } + + void AbstractEngineChannel::SendChannelPressure(uint8_t Value, uint8_t MidiChannel, int32_t FragmentPos) { + if (pEngine) { + // protection in case there are more than 1 MIDI input threads sending MIDI events to this EngineChannel + LockGuard g; + if (hasMultipleMIDIInputs()) g = LockGuard(MidiInputMutex); + + Event event = pEngine->pEventGenerator->CreateEvent(FragmentPos); + event.Type = Event::type_channel_pressure; + event.Param.ChannelPressure.Controller = CTRL_TABLE_IDX_AFTERTOUCH; // required for instrument scripts + event.Param.ChannelPressure.Value = Value; + event.Param.ChannelPressure.Channel = MidiChannel; + memset(&event.Format, 0, sizeof(event.Format)); // init format speific stuff with zeroes + event.pEngineChannel = this; + if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event); + else dmsg(1,("AbstractEngineChannel: Input event queue full!")); + } + } + + void AbstractEngineChannel::SendPolyphonicKeyPressure(uint8_t Key, uint8_t Value, uint8_t MidiChannel) { + if (pEngine) { + // protection in case there are more than 1 MIDI input threads sending MIDI events to this EngineChannel + LockGuard g; + if (hasMultipleMIDIInputs()) g = LockGuard(MidiInputMutex); + + Event event = pEngine->pEventGenerator->CreateEvent(); + event.Type = Event::type_note_pressure; + event.Param.NotePressure.Key = Key; + event.Param.NotePressure.Value = Value; + event.Param.NotePressure.Channel = MidiChannel; + memset(&event.Format, 0, sizeof(event.Format)); // init format speific stuff with zeroes + event.pEngineChannel = this; + if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event); + else dmsg(1,("AbstractEngineChannel: Input event queue full!")); + } + } + + void AbstractEngineChannel::SendPolyphonicKeyPressure(uint8_t Key, uint8_t Value, uint8_t MidiChannel, int32_t FragmentPos) { + if (pEngine) { + // protection in case there are more than 1 MIDI input threads sending MIDI events to this EngineChannel + LockGuard g; + if (hasMultipleMIDIInputs()) g = LockGuard(MidiInputMutex); + + Event event = pEngine->pEventGenerator->CreateEvent(FragmentPos); + event.Type = Event::type_note_pressure; + event.Param.NotePressure.Key = Key; + event.Param.NotePressure.Value = Value; + event.Param.NotePressure.Channel = MidiChannel; + memset(&event.Format, 0, sizeof(event.Format)); // init format speific stuff with zeroes + event.pEngineChannel = this; + if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event); + else dmsg(1,("AbstractEngineChannel: Input event queue full!")); + } + } + /** * Copy all events from the engine channel's input event queue buffer to * the internal event list. This will be done at the beginning of each @@ -556,16 +744,34 @@ event.Param.Note.Channel = channel; break; case VirtualMidiDevice::EVENT_TYPE_CC: - event.Type = Event::type_control_change; - event.Param.CC.Controller = devEvent.Arg1; - event.Param.CC.Value = devEvent.Arg2; - event.Param.CC.Channel = channel; + switch (devEvent.Arg1) { + case 0: // bank select MSB ... + SetMidiBankMsb(devEvent.Arg2); + continue; // don't push this event into FIFO + case 32: // bank select LSB ... + SetMidiBankLsb(devEvent.Arg2); + continue; // don't push this event into FIFO + default: // regular MIDI CC ... + event.Type = Event::type_control_change; + event.Param.CC.Controller = devEvent.Arg1; + event.Param.CC.Value = devEvent.Arg2; + event.Param.CC.Channel = channel; + } + break; + case VirtualMidiDevice::EVENT_TYPE_PITCHBEND: + event.Type = Event::type_pitchbend; + event.Param.Pitch.Pitch = int(devEvent.Arg2 << 7 | devEvent.Arg1) - 8192; + event.Param.Pitch.Channel = channel; break; + case VirtualMidiDevice::EVENT_TYPE_PROGRAM: + SendProgramChange(devEvent.Arg1); + continue; // don't push this event into FIFO default: std::cerr << "AbstractEngineChannel::ImportEvents() ERROR: unknown event type (" << devEvent.Type << "). This is a bug!"; continue; } + memset(&event.Format, 0, sizeof(event.Format)); // init format specific stuff with zeroes event.pEngineChannel = this; // copy event to internal event list if (pEvents->poolIsEmpty()) { @@ -602,6 +808,31 @@ eventQueueReader.free(); // free all copied events from input queue } + /** + * Called by real-time instrument script functions to schedule a new event + * somewhere in future. + * + * @returns unique event ID of scheduled new event + */ + int AbstractEngineChannel::ScheduleEvent(const Event* pEvent, int delay) { //TODO: delay not implemented yet + // since delay is not implemented yet, we simply add the new event + // to the event list of the current audio fragmet cycle for now + RTList<Event>::Iterator itEvent = pEvents->allocAppend(); + if (itEvent) *itEvent = *pEvent; // copy event + return pEvents->getID(itEvent); + } + + /** + * Called by real-time instrument script functions to ignore the event + * reflected by given event ID. The event will be freed immediately to its + * pool and cannot be dereferenced by its old ID anymore. Even if its + * allocated back from the Pool later on, it will have a different ID. + */ + void AbstractEngineChannel::IgnoreEvent(int id) { + RTList<Event>::Iterator it = pEvents->fromID(id); + if (it) pEvents->free(it); + } + FxSend* AbstractEngineChannel::AddFxSend(uint8_t MidiCtrl, String Name) throw (Exception) { if (pEngine) pEngine->DisableAndLock(); FxSend* pFxSend = new FxSend(this, MidiCtrl, Name);
View file
linuxsampler-2342.tar.bz2/src/engines/AbstractEngineChannel.h -> linuxsampler-2718.tar.bz2/src/engines/AbstractEngineChannel.h
Changed
@@ -5,6 +5,7 @@ * Copyright (C) 2003,2004 by Benno Senoner and Christian Schoenebeck * * Copyright (C) 2005-2008 Christian Schoenebeck * * Copyright (C) 2009-2012 Christian Schoenebeck and Grigor Iliev * + * Copyright (C) 2013-2014 Christian Schoenebeck and Andreas Persson * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -30,50 +31,73 @@ #include "../common/Pool.h" #include "../common/RingBuffer.h" +#include "../common/ResourceManager.h" +#include "common/AbstractInstrumentManager.h" +#include "common/InstrumentScriptVM.h" + +#define CTRL_TABLE_IDX_AFTERTOUCH 128 +#define CTRL_TABLE_IDX_PITCHBEND 129 namespace LinuxSampler { - class AbstractEngineChannel: public EngineChannel { + class MidiKeyboardManagerBase; + + class AbstractEngineChannel: public EngineChannel, public InstrumentScriptConsumer { public: // implementation of abstract methods derived from interface class 'LinuxSampler::EngineChannel' - virtual void PrepareLoadInstrument(const char* FileName, uint Instrument); - virtual void Reset(); - virtual void SendNoteOn(uint8_t Key, uint8_t Velocity, uint8_t MidiChannel); - virtual void SendNoteOn(uint8_t Key, uint8_t Velocity, uint8_t MidiChannel, int32_t FragmentPos); - virtual void SendNoteOff(uint8_t Key, uint8_t Velocity, uint8_t MidiChannel); - virtual void SendNoteOff(uint8_t Key, uint8_t Velocity, uint8_t MidiChannel, int32_t FragmentPos); - virtual void SendPitchbend(int Pitch, uint8_t MidiChannel); - virtual void SendPitchbend(int Pitch, uint8_t MidiChannel, int32_t FragmentPos); - virtual void SendControlChange(uint8_t Controller, uint8_t Value, uint8_t MidiChannel); - virtual void SendControlChange(uint8_t Controller, uint8_t Value, uint8_t MidiChannel, int32_t FragmentPos); - virtual bool StatusChanged(bool bNewStatus = false); - virtual float Volume(); - virtual void Volume(float f); - virtual float Pan(); - virtual void Pan(float f); - virtual uint Channels(); - virtual AudioOutputDevice* GetAudioOutputDevice(); - virtual void SetOutputChannel(uint EngineAudioChannel, uint AudioDeviceChannel); - virtual int OutputChannel(uint EngineAudioChannel); - virtual void Connect(MidiInputPort* pMidiPort, midi_chan_t MidiChannel); - virtual void DisconnectMidiInputPort(); - virtual MidiInputPort* GetMidiInputPort(); - virtual midi_chan_t MidiChannel(); - virtual String InstrumentFileName(); - virtual String InstrumentName(); - virtual int InstrumentIndex(); - virtual int InstrumentStatus(); - virtual Engine* GetEngine(); - virtual String EngineName(); - virtual FxSend* AddFxSend(uint8_t MidiCtrl, String Name = "") throw (Exception); - virtual FxSend* GetFxSend(uint FxSendIndex); - virtual uint GetFxSendCount(); - virtual void RemoveFxSend(FxSend* pFxSend); - virtual void Connect(VirtualMidiDevice* pDevice); - virtual void Disconnect(VirtualMidiDevice* pDevice); - + virtual void PrepareLoadInstrument(const char* FileName, uint Instrument) OVERRIDE; + virtual void Reset() OVERRIDE; + virtual void SendNoteOn(uint8_t Key, uint8_t Velocity, uint8_t MidiChannel) OVERRIDE; + virtual void SendNoteOn(uint8_t Key, uint8_t Velocity, uint8_t MidiChannel, int32_t FragmentPos) OVERRIDE; + virtual void SendNoteOff(uint8_t Key, uint8_t Velocity, uint8_t MidiChannel) OVERRIDE; + virtual void SendNoteOff(uint8_t Key, uint8_t Velocity, uint8_t MidiChannel, int32_t FragmentPos) OVERRIDE; + virtual void SendPitchbend(int Pitch, uint8_t MidiChannel) OVERRIDE; + virtual void SendPitchbend(int Pitch, uint8_t MidiChannel, int32_t FragmentPos) OVERRIDE; + virtual void SendControlChange(uint8_t Controller, uint8_t Value, uint8_t MidiChannel) OVERRIDE; + virtual void SendControlChange(uint8_t Controller, uint8_t Value, uint8_t MidiChannel, int32_t FragmentPos) OVERRIDE; + virtual void SendChannelPressure(uint8_t Value, uint8_t MidiChannel) OVERRIDE; + virtual void SendChannelPressure(uint8_t Value, uint8_t MidiChannel, int32_t FragmentPos) OVERRIDE; + virtual void SendPolyphonicKeyPressure(uint8_t Key, uint8_t Value, uint8_t MidiChannel) OVERRIDE; + virtual void SendPolyphonicKeyPressure(uint8_t Key, uint8_t Value, uint8_t MidiChannel, int32_t FragmentPos) OVERRIDE; + virtual bool StatusChanged(bool bNewStatus = false) OVERRIDE; + virtual float Volume() OVERRIDE; + virtual void Volume(float f) OVERRIDE; + virtual float Pan() OVERRIDE; + virtual void Pan(float f) OVERRIDE; + virtual uint Channels() OVERRIDE; + virtual AudioOutputDevice* GetAudioOutputDevice() OVERRIDE; + virtual void SetOutputChannel(uint EngineAudioChannel, uint AudioDeviceChannel) OVERRIDE; + virtual int OutputChannel(uint EngineAudioChannel) OVERRIDE; + virtual void Connect(MidiInputPort* pMidiPort) OVERRIDE; + virtual void Disconnect(MidiInputPort* pMidiPort) OVERRIDE; + virtual void DisconnectAllMidiInputPorts() OVERRIDE; + virtual uint GetMidiInputPortCount() OVERRIDE; + virtual MidiInputPort* GetMidiInputPort(uint index) OVERRIDE; + virtual midi_chan_t MidiChannel() OVERRIDE; + virtual void SetMidiChannel(midi_chan_t MidiChannel) OVERRIDE; + virtual void Connect(MidiInputPort* pMidiPort, midi_chan_t MidiChannel) OVERRIDE; // deprecated, may be removed + virtual void DisconnectMidiInputPort() OVERRIDE; // deprecated, may be removed + virtual MidiInputPort* GetMidiInputPort() OVERRIDE; // deprecated, may be removed + virtual String InstrumentFileName() OVERRIDE; + virtual String InstrumentName() OVERRIDE; + virtual int InstrumentIndex() OVERRIDE; + virtual int InstrumentStatus() OVERRIDE; + virtual Engine* GetEngine() OVERRIDE; + virtual String EngineName() OVERRIDE; + virtual FxSend* AddFxSend(uint8_t MidiCtrl, String Name = "") throw (Exception) OVERRIDE; + virtual FxSend* GetFxSend(uint FxSendIndex) OVERRIDE; + virtual uint GetFxSendCount() OVERRIDE; + virtual void RemoveFxSend(FxSend* pFxSend) OVERRIDE; + virtual void Connect(VirtualMidiDevice* pDevice) OVERRIDE; + virtual void Disconnect(VirtualMidiDevice* pDevice) OVERRIDE; + + // implementation of abstract methods derived from AbstractEngine::ScriptConsumer + virtual void ResourceToBeUpdated(VMParserContext* pResource, void*& pUpdateArg) OVERRIDE {} + virtual void ResourceUpdated(VMParserContext* pOldResource, VMParserContext* pNewResource, void* pUpdateArg) OVERRIDE {} + virtual void OnResourceProgress(float fProgress) OVERRIDE {} virtual AbstractEngine::Format GetEngineFormat() = 0; + virtual MidiKeyboardManagerBase* GetMidiKeyboardManager() = 0; AudioOutputDevice* GetAudioOutputDeviceSafe(); @@ -82,32 +106,30 @@ template<class TV, class TRR, class TR, class TD, class TIM, class TI> friend class EngineBase; template<class EC, class R, class S, class D> friend class VoiceBase; - protected: + //protected: AbstractEngineChannel(); virtual ~AbstractEngineChannel(); AbstractEngine* pEngine; Mutex EngineMutex; ///< protects the Engine from access by the instrument loader thread when lscp is disconnecting + Mutex MidiInputMutex; ///< Introduced when support for multiple MIDI inputs per engine channel was added: protects the MIDI event input ringbuffer on this engine channel to be accessed concurrently by multiple midi input threads. As alternative one might also move the ringbuffer from this engine channel to the individual MIDI ports/devices and let the sampler engine read the events from there instead of receiving them here. - public: // TODO: should be protected + //protected: AudioChannel* pChannelLeft; ///< encapsulates the audio rendering buffer (left) AudioChannel* pChannelRight; ///< encapsulates the audio rendering buffer (right) - protected: int AudioDeviceChannelLeft; ///< audio device channel number to which the left channel is connected to int AudioDeviceChannelRight; ///< audio device channel number to which the right channel is connected to - MidiInputPort* pMidiInputPort; ///< Points to the connected MIDI input port or NULL if none assigned. - midi_chan_t midiChannel; ///< MIDI channel(s) on which this engine channel listens to. + DoubleBuffer< ArrayList<MidiInputPort*> > midiInputs; ///< MIDI input ports on which this sampler engine channel shall listen to. + midi_chan_t midiChannel; ///< MIDI channel(s) on which this engine channel listens to (on all MIDI input ports). RingBuffer<Event,false>* pEventQueue; ///< Input event queue. RTList<Event>* pEvents; ///< All engine channel specific events for the current audio fragment. - uint8_t ControllerTable129; ///< Reflects the current values (0-127) of all MIDI controllers for this engine / sampler channel. Number 128 is for channel pressure (mono aftertouch). + uint8_t ControllerTable130; ///< Reflects the current values (0-127) of all MIDI controllers for this engine / sampler channel. Number 128 is for channel pressure (mono aftertouch), 129 for pitch bend. String InstrumentFile; int InstrumentIdx; String InstrumentIdxName; int InstrumentStat; double GlobalVolume; ///< Master volume factor set through the C++ API / LSCP (a value < 1.0 means attenuation, a value > 1.0 means amplification) double MidiVolume; ///< Volume factor altered by MIDI CC#7 (a value < 1.0 means attenuation, a value > 1.0 means amplification) - float GlobalPanLeft; - float GlobalPanRight; int Pitch; ///< Current (absolute) MIDI pitch value. float CurrentKeyDimension; ///< Current value (0-1.0) for the keyboard dimension, altered by pressing a keyswitching key. bool PortamentoMode; ///< in Portamento Mode we slide the pitch from the last note to the current note. @@ -119,6 +141,7 @@ int iEngineIndexSelf; ///< Reflects the index of this EngineChannel in the Engine's ArrayList. bool bStatusChanged; ///< true in case an engine parameter has changed (e.g. new instrument, another volumet) uint32_t RoundRobinIndex; ///< counter for round robin sample selection, incremented for each note on + InstrumentScript* pScript; ///< Points to the real-time instrument script(s) to be executed, NULL if current instrument does not have an instrument script. Even though the underlying VM representation of the script is shared among multiple sampler channels, the InstrumentScript object here is not shared though, it exists for each sampler channel separately. SynchronizedConfig< ArrayList<VirtualMidiDevice*> > virtualMidiDevices; SynchronizedConfig< ArrayList<VirtualMidiDevice*> >::Reader virtualMidiDevicesReader_AudioThread; @@ -147,11 +170,27 @@ virtual void RemoveAllFxSends(); void ImportEvents(uint Samples); + int ScheduleEvent(const Event* pEvent, int delay); //TODO: delay not implemented yet + void IgnoreEvent(int id); void AddGroup(uint group); void HandleKeyGroupConflicts(uint KeyGroup, Pool<Event>::Iterator& itNoteOnEvent); void ClearGroupEventLists(); void DeleteGroupEventLists(); + + private: + /** + * Returns @c true if there are 2 ore more MidiInputPorts connected + * to this engine channel. + * + * This method is currently only used to prevent unnecessary + * MidiInputMutex.Lock() if there is not more than 1 MIDI input on + * this engine channel. + */ + inline bool hasMultipleMIDIInputs() const { + //FIXME: leaves tiny time frames open (shortly after 1->2 devices connected or 2->1 disconnected) which could lead to concurrency issue for the purpose described above, however in practice it "should" be acceptable + return midiInputs.unsafeBack().size() > 1; + } }; } // namespace LinuxSampler
View file
linuxsampler-2342.tar.bz2/src/engines/Engine.h -> linuxsampler-2718.tar.bz2/src/engines/Engine.h
Changed
@@ -2,8 +2,8 @@ * * * LinuxSampler - modular, streaming capable sampler * * * - * Copyright (C) 2003,2004 by Benno Senoner and Christian Schoenebeck * - * Copyright (C) 2005-2008 Christian Schoenebeck * + * Copyright (C) 2003,2004 by Benno Senoner and Christian Schoenebeck * + * Copyright (C) 2005-2013 Christian Schoenebeck * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -75,6 +75,38 @@ * the same InstrumentManager object. */ virtual InstrumentManager* GetInstrumentManager() = 0; + + /** + * Will be called by audio output drivers in case some + * fundamental audio driver parameter like sample rate or + * max. samples per cycle changed. + */ + virtual void ReconnectAudioOutputDevice() = 0; + + /** + * Modifies the scale tuning from standard well tempered chromatic + * scaling to any other kind of scaling. + * + * ScaleTunes - detune factors (-64..+63 cents) for all 12 + * semitones of an octave + */ + virtual void AdjustScaleTuning(const int8_t ScaleTunes12) = 0; + + /** + * Expects a byte array with 12 elements as argument @a pScaleTunes, + * where the currently effectve scale tuning setup is written to. + * + * pScaleTunes - output: detune factors (-64..+63 cents) for all 12 + * semitones of an octave + */ + virtual void GetScaleTuning(int8_t* pScaleTunes) = 0; + + /** + * Reset to standard well tempered chromatic scaling, i.e. after + * being altered with AdjustScaleTuning() or after having sent the + * respective standard scale tuning SysEx MIDI message. + */ + virtual void ResetScaleTuning() = 0; protected: virtual ~Engine() {}; // MUST only be destroyed by EngineFactory
View file
linuxsampler-2342.tar.bz2/src/engines/EngineBase.h -> linuxsampler-2718.tar.bz2/src/engines/EngineBase.h
Changed
@@ -4,7 +4,7 @@ * * * Copyright (C) 2003,2004 by Benno Senoner and Christian Schoenebeck * * Copyright (C) 2005-2008 Christian Schoenebeck * - * Copyright (C) 2009-2012 Christian Schoenebeck and Grigor Iliev * + * Copyright (C) 2009-2013 Christian Schoenebeck and Grigor Iliev * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -104,7 +104,7 @@ * @param Samples - number of sample points to be rendered * @returns 0 on success */ - virtual int RenderAudio(uint Samples) { + virtual int RenderAudio(uint Samples) OVERRIDE { dmsg(8,("RenderAudio(Samples=%d)\n", Samples)); // return if engine disabled @@ -143,6 +143,10 @@ } } } + + // In case scale tuning has been changed, recalculate pitch for + // all active voices. + ProcessScaleTuningChange(); // reset internal voice counter (just for statistic of active voices) ActiveVoiceCountTemp = 0; @@ -205,9 +209,9 @@ return 0; } - virtual int MaxVoices() { return pVoicePool->poolSize(); } + virtual int MaxVoices() OVERRIDE { return pVoicePool->poolSize(); } - virtual void SetMaxVoices(int iVoices) throw (Exception) { + virtual void SetMaxVoices(int iVoices) throw (Exception) OVERRIDE { if (iVoices < 1) throw Exception("Maximum voices for an engine cannot be set lower than 1"); @@ -250,11 +254,11 @@ /** Called after the new max number of voices is set and before resuming the engine. */ virtual void PostSetMaxVoices(int iVoices) { } - virtual uint DiskStreamCount() { return (pDiskThread) ? pDiskThread->GetActiveStreamCount() : 0; } - virtual uint DiskStreamCountMax() { return (pDiskThread) ? pDiskThread->ActiveStreamCountMax : 0; } - virtual int MaxDiskStreams() { return iMaxDiskStreams; } + virtual uint DiskStreamCount() OVERRIDE { return (pDiskThread) ? pDiskThread->GetActiveStreamCount() : 0; } + virtual uint DiskStreamCountMax() OVERRIDE { return (pDiskThread) ? pDiskThread->ActiveStreamCountMax : 0; } + virtual int MaxDiskStreams() OVERRIDE { return iMaxDiskStreams; } - virtual void SetMaxDiskStreams(int iStreams) throw (Exception) { + virtual void SetMaxDiskStreams(int iStreams) throw (Exception) OVERRIDE { if (iStreams < 0) throw Exception("Maximum disk streams for an engine cannot be set lower than 0"); @@ -269,9 +273,9 @@ ResumeAll(); } - virtual String DiskStreamBufferFillBytes() { return (pDiskThread) ? pDiskThread->GetBufferFillBytes() : ""; } - virtual String DiskStreamBufferFillPercentage() { return (pDiskThread) ? pDiskThread->GetBufferFillPercentage() : ""; } - virtual InstrumentManager* GetInstrumentManager() { return &instruments; } + virtual String DiskStreamBufferFillBytes() OVERRIDE { return (pDiskThread) ? pDiskThread->GetBufferFillBytes() : ""; } + virtual String DiskStreamBufferFillPercentage() OVERRIDE { return (pDiskThread) ? pDiskThread->GetBufferFillPercentage() : ""; } + virtual InstrumentManager* GetInstrumentManager() OVERRIDE { return &instruments; } /** * Connect this engine instance with the given audio output device. @@ -282,7 +286,7 @@ * * @param pAudioOut - audio output device to connect to */ - virtual void Connect(AudioOutputDevice* pAudioOut) { + virtual void Connect(AudioOutputDevice* pAudioOut) OVERRIDE { // caution: don't ignore if connecting to the same device here, // because otherwise SetMaxDiskStreams() implementation won't work anymore! @@ -368,6 +372,13 @@ pDedicatedVoiceChannelLeft = new AudioChannel(0, MaxSamplesPerCycle); pDedicatedVoiceChannelRight = new AudioChannel(1, MaxSamplesPerCycle); } + + // Implementattion for abstract method derived from Engine. + virtual void ReconnectAudioOutputDevice() OVERRIDE { + SuspendAll(); + if (pAudioOutputDevice) Connect(pAudioOutputDevice); + ResumeAll(); + } /** * Similar to @c Disable() but this method additionally kills all voices @@ -422,11 +433,12 @@ */ virtual void Suspend(RR* pRegion) { dmsg(2,("EngineBase: Suspending Region %x ...\n",pRegion)); - SuspendedRegionsMutex.Lock(); - SuspensionChangeOngoing.Set(true); - pPendingRegionSuspension = pRegion; - SuspensionChangeOngoing.WaitAndUnlockIf(true); - SuspendedRegionsMutex.Unlock(); + { + LockGuard lock(SuspendedRegionsMutex); + SuspensionChangeOngoing.Set(true); + pPendingRegionSuspension = pRegion; + SuspensionChangeOngoing.WaitAndUnlockIf(true); + } dmsg(2,("EngineBase: Region %x suspended.",pRegion)); } @@ -438,11 +450,12 @@ */ virtual void Resume(RR* pRegion) { dmsg(2,("EngineBase: Resuming Region %x ...\n",pRegion)); - SuspendedRegionsMutex.Lock(); - SuspensionChangeOngoing.Set(true); - pPendingRegionResumption = pRegion; - SuspensionChangeOngoing.WaitAndUnlockIf(true); - SuspendedRegionsMutex.Unlock(); + { + LockGuard lock(SuspendedRegionsMutex); + SuspensionChangeOngoing.Set(true); + pPendingRegionResumption = pRegion; + SuspensionChangeOngoing.WaitAndUnlockIf(true); + } dmsg(2,("EngineBase: Region %x resumed.\n",pRegion)); } @@ -565,12 +578,13 @@ public: int PendingStreamDeletions; RR* pPendingRegionSuspension; + SuspensionVoiceHandler(RR* pPendingRegionSuspension) { PendingStreamDeletions = 0; this->pPendingRegionSuspension = pPendingRegionSuspension; } - virtual bool Process(MidiKey* pMidiKey) { + virtual bool Process(MidiKey* pMidiKey) OVERRIDE { VoiceIterator itVoice = pMidiKey->pActiveVoices->first(); // if current key is not associated with this region, skip this key if (itVoice->GetRegion()->GetParent() != pPendingRegionSuspension) return false; @@ -578,7 +592,7 @@ return true; } - virtual void Process(VoiceIterator& itVoice) { + virtual void Process(VoiceIterator& itVoice) OVERRIDE { // request a notification from disk thread side for stream deletion const Stream::Handle hStream = itVoice->KillImmediately(true); if (hStream != Stream::INVALID_HANDLE) { // voice actually used a stream @@ -617,7 +631,46 @@ AbstractEngineChannel* pChannel = static_cast<AbstractEngineChannel*>(pEngineChannel); pChannel->ImportEvents(Samples); - // process events + // if a valid real-time instrument script is loaded, pre-process + // the event list by running the script now, since the script + // might filter events or add new ones for this cycle + if (pChannel->pScript) { + // resume any suspended script executions still hanging + // around of previous audio fragment cycles + for (RTList<ScriptEvent>::Iterator itEvent = pChannel->pScript->pEvents->first(), + end = pChannel->pScript->pEvents->end(); itEvent != end; ++itEvent) + { + ResumeScriptEvent(pChannel, itEvent); //TODO: implement support for actual suspension time (i.e. passed to a script's wait() function call) + } + + // spawn new script executions for the new MIDI events of + // this audio fragment cycle + for (RTList<Event>::Iterator itEvent = pChannel->pEvents->first(), + end = pChannel->pEvents->end(); itEvent != end; ++itEvent) + { + switch (itEvent->Type) { + case Event::type_note_on: + if (pChannel->pScript->handlerNote) + ProcessEventByScript(pChannel, itEvent, pChannel->pScript->handlerNote); + break; + case Event::type_note_off: + if (pChannel->pScript->handlerRelease) + ProcessEventByScript(pChannel, itEvent, pChannel->pScript->handlerRelease); + break; + case Event::type_control_change: + case Event::type_channel_pressure: + case Event::type_pitchbend: + if (pChannel->pScript->handlerController) + ProcessEventByScript(pChannel, itEvent, pChannel->pScript->handlerController); + break; + case Event::type_note_pressure: + //TODO: ... + break; + } + } + } + + // now process all events regularly { RTList<Event>::Iterator itEvent = pChannel->pEvents->first(); RTList<Event>::Iterator end = pChannel->pEvents->end(); @@ -635,6 +688,14 @@ dmsg(5,("Engine: MIDI CC received\n")); ProcessControlChange((EngineChannel*)itEvent->pEngineChannel, itEvent); break; + case Event::type_channel_pressure: + dmsg(5,("Engine: MIDI Chan. Pressure received\n")); + ProcessChannelPressure((EngineChannel*)itEvent->pEngineChannel, itEvent); + break; + case Event::type_note_pressure: + dmsg(5,("Engine: MIDI Note Pressure received\n")); + ProcessPolyphonicKeyPressure((EngineChannel*)itEvent->pEngineChannel, itEvent); + break; case Event::type_pitchbend: dmsg(5,("Engine: Pitchbend received\n")); ProcessPitchbend(static_cast<AbstractEngineChannel*>(itEvent->pEngineChannel), itEvent); @@ -651,6 +712,140 @@ pLastStolenChannel = NULL; } + /** @brief Call instrument script's event handler for this event. + * + * Causes a new execution instance of the currently loaded real-time + * instrument script's event handler (callback) to be spawned for + * the given MIDI event. + * + * @param pChannel - engine channel on which the MIDI event occured + * @param itEvent - MIDI event that causes this new script execution + * @param pEventHandler - script's event handler to be executed + */ + void ProcessEventByScript(AbstractEngineChannel* pChannel, RTList<Event>::Iterator& itEvent, VMEventHandler* pEventHandler) { + const int key = itEvent->Param.Note.Key; // even if this is not a note on/off event, accessing it does not mean any harm + // check if polyphonic data is passed from "note" to "release" + // script event handlers + if (pEventHandler == pChannel->pScript->handlerRelease && + pChannel->pScript->handlerNote && + pChannel->pScript->handlerNote->isPolyphonic() && + pChannel->pScript->handlerRelease->isPolyphonic() && + !pChannel->pScript->pKeyEventskey->isEmpty()) + { + // polyphonic variable data is used/passed from "note" to + // "release" script callback, so we have to recycle the + // original "note on" script event(s) + RTList<ScriptEvent>::Iterator it = pChannel->pScript->pKeyEventskey->first(); + RTList<ScriptEvent>::Iterator end = pChannel->pScript->pKeyEventskey->end(); + for (; it != end; ++it) { + ProcessScriptEvent( + pChannel, itEvent, pEventHandler, it + ); + } + } else { + // no polyphonic data is used/passed from "note" to + // "release" script callback, so just use a new fresh + // script event object + RTList<ScriptEvent>::Iterator itScriptEvent = + pChannel->pScript->pEvents->allocAppend(); + ProcessScriptEvent( + pChannel, itEvent, pEventHandler, itScriptEvent + ); + } + } + + void ProcessScriptEvent(AbstractEngineChannel* pChannel, RTList<Event>::Iterator& itEvent, VMEventHandler* pEventHandler, RTList<ScriptEvent>::Iterator& itScriptEvent) { + if (!itScriptEvent) return; // not a valid script event (i.e. because no free script event was left in the script event pool) + + // fill the list of script handlers to be executed by this event + int i = 0; + itScriptEvent->handlersi++ = pEventHandler; // actual event handler (i.e. note, controller) + itScriptEvent->handlersi = NULL; // NULL termination of list + + // initialize/reset other members + itScriptEvent->cause = *itEvent; + itScriptEvent->id = pEventPool->getID(itEvent); + itScriptEvent->currentHandler = 0; + itScriptEvent->executionSlices = 0; + + // run script handler(s) + VMExecStatus_t res = pScriptVM->exec( + pChannel->pScript->parserContext, &*itScriptEvent + ); + + // in case the script was suspended, keep it on the allocated + // ScriptEvent list to be continued on the next audio cycle + if (!(res & VM_EXEC_SUSPENDED)) { // script execution has finished without 'suspended' status ... + // if "polyphonic" variable data is passed from script's + // "note" event handler to its "release" event handler, then + // the script event must be kept and recycled for the later + // occuring "release" script event ... + if (pEventHandler == pChannel->pScript->handlerNote && + pChannel->pScript->handlerRelease && + pChannel->pScript->handlerNote->isPolyphonic() && + pChannel->pScript->handlerRelease->isPolyphonic()) + { + const int key = itEvent->Param.Note.Key; + itScriptEvent.moveToEndOf(pChannel->pScript->pKeyEventskey & 127); + } else { + // ... otherwise if no polyphonic data is passed and + // script's execution has finished without suspension + // status, then free the script event for a new future + // script event to be triggered from start + pChannel->pScript->pEvents->free(itScriptEvent); + } + } + } + + /** @brief Resume execution of instrument script. + * + * Will be called to resume execution of a real-time instrument + * script event which has been suspended in a previous audio + * fragment cycle. + * + * Script execution might be suspended for various reasons. Usually + * a script will be suspended if the script called the built-in + * "wait()" function, but it might also be suspended automatically + * if the script took too much execution time in an audio fragment + * cycle. So in the latter case automatic suspension is performed in + * order to avoid harm for the sampler's overall real-time + * requirements. + * + * @param pChannel - engine channel this script is running for + * @param itScriptEvent - script execution that shall be resumed + */ + void ResumeScriptEvent(AbstractEngineChannel* pChannel, RTList<ScriptEvent>::Iterator& itScriptEvent) { + VMEventHandler* handler = itScriptEvent->handlersitScriptEvent->currentHandler; + + // run script + VMExecStatus_t res = pScriptVM->exec( + pChannel->pScript->parserContext, &*itScriptEvent + ); + + // in case the script was suspended, keep it on the allocated + // ScriptEvent list to be continued on the next audio cycle + if (!(res & VM_EXEC_SUSPENDED)) { // script execution has finished without 'suspended' status ... + // if "polyphonic" variable data is passed from script's + // "note" event handler to its "release" event handler, then + // the script event must be kept and recycled for the later + // occuring "release" script event ... + if (handler && handler == pChannel->pScript->handlerNote && + pChannel->pScript->handlerRelease && + pChannel->pScript->handlerNote->isPolyphonic() && + pChannel->pScript->handlerRelease->isPolyphonic()) + { + const int key = itScriptEvent->cause.Param.Note.Key; + itScriptEvent.moveToEndOf(pChannel->pScript->pKeyEventskey & 127); + } else { + // ... otherwise if no polyphonic data is passed and + // script's execution has finished without suspension + // status, then free the script event for a new future + // script event to be triggered from start + pChannel->pScript->pEvents->free(itScriptEvent); + } + } + } + /** * Will be called by LaunchVoice() method in case there are no free * voices left. This method will select and kill one old voice for @@ -783,9 +978,28 @@ dmsg(5,("Engine: instrument change command received\n")); cmd.bChangeInstrument = false; pEngineChannel->pInstrument = cmd.pInstrument; + pEngineChannel->pScript = + cmd.pScript->bHasValidScript ? cmd.pScript : NULL; instrumentChanged = true; pEngineChannel->MarkAllActiveVoicesAsOrphans(); + + // the script's "init" event handler is only executed + // once (when the script is loaded or reloaded) + if (pEngineChannel->pScript && pEngineChannel->pScript->handlerInit) { + RTList<ScriptEvent>::Iterator itScriptEvent = + pEngineChannel->pScript->pEvents->allocAppend(); + + itScriptEvent->cause.pEngineChannel = pEngineChannel; + itScriptEvent->handlers0 = pEngineChannel->pScript->handlerInit; + itScriptEvent->handlers1 = NULL; + + VMExecStatus_t res = pScriptVM->exec( + pEngineChannel->pScript->parserContext, &*itScriptEvent + ); + + pEngineChannel->pScript->pEvents->free(itScriptEvent); + } } } @@ -968,8 +1182,6 @@ } case 10: { // panpot //TODO: not sample accurate yet - pChannel->GlobalPanLeft = PanCurve128 - itControlChangeEvent->Param.CC.Value; - pChannel->GlobalPanRight = PanCurveitControlChangeEvent->Param.CC.Value; pChannel->iLastPanRequest = itControlChangeEvent->Param.CC.Value; break; } @@ -1165,6 +1377,7 @@ pChannel->ProcessKeySwitchChange(key); pKey->KeyPressed = true; // the MIDI key was now pressed down + pChannel->KeyDownkey = true; // just used as built-in %KEY_DOWN script variable pKey->Velocity = itNoteOnEventOnKeyList->Param.Note.Velocity; pKey->NoteOnTime = FrameTime + itNoteOnEventOnKeyList->FragmentPos(); // will be used to calculate note length @@ -1241,6 +1454,7 @@ #endif pKey->KeyPressed = false; // the MIDI key was now released + pChannel->KeyDowniKey = false; // just used as built-in %KEY_DOWN script variable // move event to the key's own event list RTList<Event>::Iterator itNoteOffEventOnKeyList = itNoteOffEvent.moveToEndOf(pKey->pEvents); @@ -1320,7 +1534,7 @@ * control and status variables. This method is protected by a mutex. */ virtual void ResetInternal() { - ResetInternalMutex.Lock(); + LockGuard lock(ResetInternalMutex); // make sure that the engine does not get any sysex messages // while it's reseting @@ -1349,7 +1563,6 @@ pEventQueue->init(); pSysexBuffer->init(); if (sysexDisabled) MidiInputPort::AddSysexListener(this); - ResetInternalMutex.Unlock(); } /** @@ -1445,6 +1658,22 @@ return -1; } + + /** + * Checks whether scale tuning setting has been changed since last + * time this method was called, if yes, it recalculates the pitch + * for all active voices. + */ + void ProcessScaleTuningChange() { + const bool changed = ScaleTuningChanged.readAndReset(); + if (!changed) return; + + for (int i = 0; i < engineChannels.size(); i++) { + EngineChannelBase<V, R, I>* channel = + static_cast<EngineChannelBase<V, R, I>*>(engineChannelsi); + channel->OnScaleTuningChanged(); + } + } private: Pool<V>* pVoicePool; ///< Contains all voices that can be activated.
View file
linuxsampler-2342.tar.bz2/src/engines/EngineChannel.h -> linuxsampler-2718.tar.bz2/src/engines/EngineChannel.h
Changed
@@ -1,6 +1,6 @@ /*************************************************************************** * * - * Copyright (C) 2005 - 2012 Christian Schoenebeck * + * Copyright (C) 2005 - 2014 Christian Schoenebeck * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -58,8 +58,7 @@ // abstract methods // (these have to be implemented by the descendant) - virtual void PrepareLoadInstrument(const char* FileName, uint Instrument) = 0; - virtual void LoadInstrument() = 0; + // general sampler part management virtual void Reset() = 0; virtual void SendNoteOn(uint8_t Key, uint8_t Velocity, uint8_t MidiChannel) = 0; virtual void SendNoteOn(uint8_t Key, uint8_t Velocity, uint8_t MidiChannel, int32_t FragmentPos) = 0; @@ -70,34 +69,59 @@ virtual void SendControlChange(uint8_t Controller, uint8_t Value, uint8_t MidiChannel) = 0; virtual void SendControlChange(uint8_t Controller, uint8_t Value, uint8_t MidiChannel, int32_t FragmentPos) = 0; virtual void SendProgramChange(uint8_t Program) = 0; + virtual void SendChannelPressure(uint8_t Value, uint8_t MidiChannel) = 0; + virtual void SendChannelPressure(uint8_t Value, uint8_t MidiChannel, int32_t FragmentPos) = 0; + virtual void SendPolyphonicKeyPressure(uint8_t Key, uint8_t Value, uint8_t MidiChannel) = 0; + virtual void SendPolyphonicKeyPressure(uint8_t Key, uint8_t Value, uint8_t MidiChannel, int32_t FragmentPos) = 0; virtual bool StatusChanged(bool bNewStatus = false) = 0; virtual float Volume() = 0; virtual void Volume(float f) = 0; virtual float Pan() = 0; virtual void Pan(float f) = 0; virtual uint Channels() = 0; + + // audio driver management virtual void Connect(AudioOutputDevice* pAudioOut) = 0; virtual void DisconnectAudioOutputDevice() = 0; virtual AudioOutputDevice* GetAudioOutputDevice() = 0; virtual void SetOutputChannel(uint EngineAudioChannel, uint AudioDeviceChannel) = 0; virtual int OutputChannel(uint EngineAudioChannel) = 0; - virtual void Connect(MidiInputPort* pMidiPort, midi_chan_t MidiChannel) = 0; - virtual void DisconnectMidiInputPort() = 0; - virtual MidiInputPort* GetMidiInputPort() = 0; + + // MIDI driver management + virtual void Connect(MidiInputPort* pMidiPort) = 0; + virtual void Disconnect(MidiInputPort* pMidiPort) = 0; + virtual void DisconnectAllMidiInputPorts() = 0; + virtual uint GetMidiInputPortCount() = 0; + virtual MidiInputPort* GetMidiInputPort(uint index) = 0; virtual midi_chan_t MidiChannel() = 0; + virtual void SetMidiChannel(midi_chan_t MidiChannel) = 0; + // (deprecated MIDI driver management methods) + virtual void Connect(MidiInputPort* pMidiPort, midi_chan_t MidiChannel) DEPRECATED_API = 0; + virtual void DisconnectMidiInputPort() DEPRECATED_API = 0; + virtual MidiInputPort* GetMidiInputPort() DEPRECATED_API = 0; + + // virtual MIDI driver management (i.e. virtual on-screen MIDI keyboards) + virtual void Connect(VirtualMidiDevice* pDevice) = 0; + virtual void Disconnect(VirtualMidiDevice* pDevice) = 0; + + // instrument (sound file) management + virtual void PrepareLoadInstrument(const char* FileName, uint Instrument) = 0; + virtual void LoadInstrument() = 0; virtual String InstrumentFileName() = 0; ///< Returns the file name of the currently loaded instrument. Equivalent as calling InstrumentFileName(0). virtual String InstrumentFileName(int index); virtual String InstrumentName() = 0; virtual int InstrumentIndex() = 0; virtual int InstrumentStatus() = 0; + + // sampler format / sampler engine implementation details virtual Engine* GetEngine() = 0; virtual String EngineName() = 0; + + // effect routing virtual FxSend* AddFxSend(uint8_t MidiCtrl, String Name = "") throw (Exception) = 0; virtual FxSend* GetFxSend(uint FxSendIndex) = 0; virtual uint GetFxSendCount() = 0; virtual void RemoveFxSend(FxSend* pFxSend) = 0; - virtual void Connect(VirtualMidiDevice* pDevice) = 0; - virtual void Disconnect(VirtualMidiDevice* pDevice) = 0; /////////////////////////////////////////////////////////////////
View file
linuxsampler-2342.tar.bz2/src/engines/EngineChannelBase.h -> linuxsampler-2718.tar.bz2/src/engines/EngineChannelBase.h
Changed
@@ -4,7 +4,7 @@ * * * Copyright (C) 2003,2004 by Benno Senoner and Christian Schoenebeck * * Copyright (C) 2005-2008 Christian Schoenebeck * - * Copyright (C) 2009-2012 Christian Schoenebeck and Grigor Iliev * + * Copyright (C) 2009-2013 Christian Schoenebeck and Grigor Iliev * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -39,6 +39,7 @@ bool bChangeInstrument; ///< Set to true by the loader when the channel should change instrument. I* pInstrument; ///< The new instrument. Also used by the loader to read the previously loaded instrument. RTList<R*>* pRegionsInUse; ///< List of dimension regions in use by the currently loaded instrument. Continuously updated by the audio thread. + InstrumentScript* pScript; ///< Instrument script to be executed for this instrument. This is never NULL, it is always a valid InstrumentScript pointer. Use InstrumentScript::bHasValidScript whether it reflects a valid instrument script to be executed. }; template<class R> @@ -59,6 +60,10 @@ typedef typename RTList<R*>::Iterator RTListRegionIterator; typedef typename MidiKeyboardManager<V>::MidiKey MidiKey; + virtual MidiKeyboardManagerBase* GetMidiKeyboardManager() OVERRIDE { + return this; + } + virtual void HandBack(I* Instrument) { ResourceManager<InstrumentManager::instrument_id_t, I>* mgr = dynamic_cast<ResourceManager<InstrumentManager::instrument_id_t, I>*>(pEngine->GetInstrumentManager()); @@ -84,9 +89,11 @@ } virtual void DeleteRegionsInUse() { + RTList<R*>* previous = NULL; // prevent double free { InstrumentChangeCmd<R, I>& cmd = InstrumentChangeCommand.GetConfigForUpdate(); if (cmd.pRegionsInUse) { + previous = cmd.pRegionsInUse; delete cmd.pRegionsInUse; cmd.pRegionsInUse = NULL; } @@ -95,7 +102,8 @@ { InstrumentChangeCmd<R, I>& cmd = InstrumentChangeCommand.SwitchConfig(); if (cmd.pRegionsInUse) { - delete cmd.pRegionsInUse; + if (cmd.pRegionsInUse != previous) + delete cmd.pRegionsInUse; cmd.pRegionsInUse = NULL; } cmd.bChangeInstrument = false; @@ -121,9 +129,10 @@ DisconnectAudioOutputDevice(); } AbstractEngine* newEngine = AbstractEngine::AcquireEngine(this, pAudioOut); - EngineMutex.Lock(); - pEngine = newEngine; - EngineMutex.Unlock(); + { + LockGuard lock(EngineMutex); + pEngine = newEngine; + } ResetInternal(); pEvents = new RTList<Event>(pEngine->pEventPool); @@ -178,6 +187,7 @@ ResetInternal(); DeleteRegionsInUse(); + UnloadScriptInUse(); InstrumentChangeCmd<R, I>& cmd = InstrumentChangeCommand.GetConfigForUpdate(); if (cmd.pInstrument) { @@ -195,9 +205,10 @@ DeleteGroupEventLists(); AudioOutputDevice* oldAudioDevice = pEngine->pAudioOutputDevice; - EngineMutex.Lock(); - pEngine = NULL; - EngineMutex.Unlock(); + { + LockGuard lock(EngineMutex); + pEngine = NULL; + } AbstractEngine::FreeEngine(this, oldAudioDevice); AudioDeviceChannelLeft = -1; AudioDeviceChannelRight = -1; @@ -222,7 +233,10 @@ this->ProcessActiveVoices(&handler); // empty exclusive group specific event lists - ClearGroupEventLists(); + // (pInstrument == 0 could mean that LoadInstrument is + // building new group event lists, so we must check + // for that) + if (pInstrument) ClearGroupEventLists(); } // implementation of abstract methods derived from interface class 'InstrumentConsumer' @@ -232,7 +246,7 @@ * we are currently using on this EngineChannel is going to be updated, * so we can stop playback before that happens. */ - virtual void ResourceToBeUpdated(I* pResource, void*& pUpdateArg) { + virtual void ResourceToBeUpdated(I* pResource, void*& pUpdateArg) OVERRIDE { dmsg(3,("EngineChannelBase: Received instrument update message.\n")); if (pEngine) pEngine->DisableAndLock(); ResetInternal(); @@ -243,7 +257,7 @@ * Will be called by the InstrumentResourceManager when the instrument * update process was completed, so we can continue with playback. */ - virtual void ResourceUpdated(I* pOldResource, I* pNewResource, void* pUpdateArg) { + virtual void ResourceUpdated(I* pOldResource, I* pNewResource, void* pUpdateArg) OVERRIDE { this->pInstrument = pNewResource; //TODO: there are couple of engine parameters we should update here as well if the instrument was updated (see LoadInstrument()) if (pEngine) pEngine->Enable(); bStatusChanged = true; // status of engine has changed, so set notify flag @@ -255,7 +269,7 @@ * * @param fProgress - current progress as value between 0.0 and 1.0 */ - virtual void OnResourceProgress(float fProgress) { + virtual void OnResourceProgress(float fProgress) OVERRIDE { this->InstrumentStat = int(fProgress * 100.0f); dmsg(7,("EngineChannelBase: progress %d%", InstrumentStat)); bStatusChanged = true; // status of engine has changed, so set notify flag @@ -275,7 +289,10 @@ template<class TV, class TRR, class TR, class TD, class TIM, class TI> friend class EngineBase; protected: - EngineChannelBase() : InstrumentChangeCommandReader(InstrumentChangeCommand) { + EngineChannelBase() : + MidiKeyboardManager<V>(this), + InstrumentChangeCommandReader(InstrumentChangeCommand) + { pInstrument = NULL; // reset the instrument change command struct (need to be done @@ -284,17 +301,37 @@ InstrumentChangeCmd<R, I>& cmd = InstrumentChangeCommand.GetConfigForUpdate(); cmd.pRegionsInUse = NULL; cmd.pInstrument = NULL; + cmd.pScript = new InstrumentScript(this); cmd.bChangeInstrument = false; } { InstrumentChangeCmd<R, I>& cmd = InstrumentChangeCommand.SwitchConfig(); cmd.pRegionsInUse = NULL; cmd.pInstrument = NULL; + cmd.pScript = new InstrumentScript(this); cmd.bChangeInstrument = false; } } - virtual ~EngineChannelBase() { } + virtual ~EngineChannelBase() { + InstrumentScript* previous = NULL; // prevent double free + { + InstrumentChangeCmd<R, I>& cmd = InstrumentChangeCommand.GetConfigForUpdate(); + if (cmd.pScript) { + previous = cmd.pScript; + delete cmd.pScript; + cmd.pScript = NULL; + } + } + { + InstrumentChangeCmd<R, I>& cmd = InstrumentChangeCommand.SwitchConfig(); + if (cmd.pScript) { + if (previous != cmd.pScript) + delete cmd.pScript; + cmd.pScript = NULL; + } + } + } typedef typename RTList<V>::Iterator RTListVoiceIterator; @@ -347,6 +384,35 @@ } /** + * Unload the currently used and loaded real-time instrument script. + * The source code of the script is retained, so that it can still + * be reloaded. + */ + void UnloadScriptInUse() { + { + InstrumentChangeCmd<R, I>& cmd = InstrumentChangeCommand.GetConfigForUpdate(); + if (cmd.pScript) cmd.pScript->unload(); + } + { + InstrumentChangeCmd<R, I>& cmd = InstrumentChangeCommand.SwitchConfig(); + if (cmd.pScript) cmd.pScript->unload(); + } + InstrumentChangeCommand.SwitchConfig(); // switch back to original one + } + + /** + * Load real-time instrument script and all its resources required + * for the upcoming instrument change. + * + * @param text - source code of script + */ + void LoadInstrumentScript(const String& text) { + InstrumentChangeCmd<R, I>& cmd = InstrumentChangeCommand.GetConfigForUpdate(); + // load the new script + cmd.pScript->load(text); + } + + /** * Changes the instrument for an engine channel. * * @param pInstrument - new instrument
View file
linuxsampler-2342.tar.bz2/src/engines/EngineChannelFactory.cpp -> linuxsampler-2718.tar.bz2/src/engines/EngineChannelFactory.cpp
Changed
@@ -1,6 +1,6 @@ /*************************************************************************** * * - * Copyright (C) 2005 - 2012 Christian Schoenebeck * + * Copyright (C) 2005 - 2013 Christian Schoenebeck * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -92,15 +92,16 @@ Mutex EngineChannelFactory::EngineChannelsMutex; void EngineChannelFactory::SetDeleteEnabled(const EngineChannel* pEngineChannel, bool enable) { - LockedChannelsMutex.Lock(); if (!enable) { + LockGuard lock(LockedChannelsMutex); if (!lockedChannels.Contains(pEngineChannel)) lockedChannels.Add(pEngineChannel); - LockedChannelsMutex.Unlock(); } else { - bool b = lockedChannels.IsDestroyed(pEngineChannel); - lockedChannels.Remove(pEngineChannel); - LockedChannelsMutex.Unlock(); - + bool b; + { + LockGuard lock(LockedChannelsMutex); + b = lockedChannels.IsDestroyed(pEngineChannel); + lockedChannels.Remove(pEngineChannel); + } if (b) delete pEngineChannel; } } @@ -123,27 +124,27 @@ } else { throw Exception("Unknown engine type"); } - EngineChannelsMutex.Lock(); + LockGuard lock(EngineChannelsMutex); engineChannels.insert(pEngineChannel); - EngineChannelsMutex.Unlock(); return pEngineChannel; } void EngineChannelFactory::Destroy(LinuxSampler::EngineChannel* pEngineChannel) { pEngineChannel->RemoveAllFxSendCountListeners(); - EngineChannelsMutex.Lock(); - engineChannels.erase(pEngineChannel); - EngineChannelsMutex.Unlock(); + { + LockGuard lock(EngineChannelsMutex); + engineChannels.erase(pEngineChannel); + } // Postpone the deletion of the specified EngineChannel if needed (bug #113) - LockedChannelsMutex.Lock(); - if (lockedChannels.Contains(pEngineChannel)) { - lockedChannels.SetDestroyed(pEngineChannel); - pEngineChannel->SetSamplerChannel(NULL); - LockedChannelsMutex.Unlock(); - return; + { + LockGuard lock(LockedChannelsMutex); + if (lockedChannels.Contains(pEngineChannel)) { + lockedChannels.SetDestroyed(pEngineChannel); + pEngineChannel->SetSamplerChannel(NULL); + return; + } } - LockedChannelsMutex.Unlock(); /////// delete pEngineChannel;
View file
linuxsampler-2342.tar.bz2/src/engines/InstrumentManager.cpp -> linuxsampler-2718.tar.bz2/src/engines/InstrumentManager.cpp
Changed
@@ -3,7 +3,7 @@ * LinuxSampler - modular, streaming capable sampler * * * * Copyright (C) 2003, 2004 by Benno Senoner and Christian Schoenebeck * - * Copyright (C) 2005 - 2009 Christian Schoenebeck * + * Copyright (C) 2005 - 2013 Christian Schoenebeck * * * * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -35,15 +35,13 @@ Mutex loaderMutex; void InstrumentManager::LoadInstrumentInBackground(InstrumentManager::instrument_id_t ID, EngineChannel* pEngineChannel) { - loaderMutex.Lock(); + LockGuard lock(loaderMutex); thread.StartNewLoad(ID.FileName, ID.Index, pEngineChannel); - loaderMutex.Unlock(); } void InstrumentManager::SetModeInBackground(const instrument_id_t& ID, mode_t Mode) { - loaderMutex.Lock(); + LockGuard lock(loaderMutex); thread.StartSettingMode(this, ID, Mode); - loaderMutex.Unlock(); } void InstrumentManager::StopBackgroundThread() {
View file
linuxsampler-2342.tar.bz2/src/engines/InstrumentManager.h -> linuxsampler-2718.tar.bz2/src/engines/InstrumentManager.h
Changed
@@ -3,7 +3,7 @@ * LinuxSampler - modular, streaming capable sampler * * * * Copyright (C) 2003, 2004 by Benno Senoner and Christian Schoenebeck * - * Copyright (C) 2005 - 2009 Christian Schoenebeck * + * Copyright (C) 2005 - 2015 Christian Schoenebeck * * * * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -176,6 +176,8 @@ * * This method has to be implemented by the descendant. * + * @param pEngineChannel - engine channel for which an instrument + * editor shall be launched for * @param ID - the instrument for which an editor should be * spawned for * @param pUserData - (optional) arbitrary 3rd party user data @@ -185,7 +187,7 @@ * @throws InstrumentManagerException - in case no compatible * instrument editor is registered to the sampler */ - virtual InstrumentEditor* LaunchInstrumentEditor(instrument_id_t ID, void* pUserData = NULL) throw (InstrumentManagerException) = 0; + virtual InstrumentEditor* LaunchInstrumentEditor(EngineChannel* pEngineChannel, instrument_id_t ID, void* pUserData = NULL) throw (InstrumentManagerException) = 0; /** * Returns a list of instrument IDs of the provided instrument
View file
linuxsampler-2342.tar.bz2/src/engines/InstrumentManagerBase.h -> linuxsampler-2718.tar.bz2/src/engines/InstrumentManagerBase.h
Changed
@@ -4,7 +4,7 @@ * * * Copyright (C) 2003, 2004 by Benno Senoner and Christian Schoenebeck * * Copyright (C) 2005 - 2008 Christian Schoenebeck * - * Copyright (C) 2009 - 2012 Christian Schoenebeck and Grigor Iliev * + * Copyright (C) 2009 - 2013 Christian Schoenebeck and Grigor Iliev * * * * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -25,12 +25,10 @@ #ifndef __LS_INSTRUMENTMANAGERBASE_H__ #define __LS_INSTRUMENTMANAGERBASE_H__ -#include "InstrumentManager.h" +#include "common/AbstractInstrumentManager.h" +#include "../drivers/audio/AudioOutputDeviceFactory.h" #include "AbstractEngine.h" #include "AbstractEngineChannel.h" -#include "../common/ResourceManager.h" -#include "../common/global_private.h" -#include "../drivers/audio/AudioOutputDeviceFactory.h" // We need to know the maximum number of sample points which are going to // be processed for each render cycle of the audio output driver, to know @@ -42,7 +40,7 @@ namespace LinuxSampler { template <class F /* Instrument File */, class I /* Instrument */, class R /* Regions */, class S /*Sample */> - class InstrumentManagerBase : public InstrumentManager, public ResourceManager<InstrumentManager::instrument_id_t, I> { + class InstrumentManagerBase : public AbstractInstrumentManager, public ResourceManager<InstrumentManager::instrument_id_t, I> { public: struct region_info_t { int refCount; @@ -56,20 +54,20 @@ typedef ResourceConsumer<I> InstrumentConsumer; - InstrumentManagerBase() { } + InstrumentManagerBase() : AbstractInstrumentManager() { } virtual ~InstrumentManagerBase() { } - virtual InstrumentEditor* LaunchInstrumentEditor(instrument_id_t ID, void* pUserData = NULL) throw (InstrumentManagerException) { + virtual InstrumentEditor* LaunchInstrumentEditor(EngineChannel* pEngineChannel, instrument_id_t ID, void* pUserData = NULL) throw (InstrumentManagerException) OVERRIDE { throw InstrumentManagerException( "Instrument editing is not supported for this instrument format" ); } - virtual String GetInstrumentDataStructureName(instrument_id_t ID) { + virtual String GetInstrumentDataStructureName(instrument_id_t ID) OVERRIDE { throw InstrumentManagerException("Not implemented"); } - virtual String GetInstrumentDataStructureVersion(instrument_id_t ID) { + virtual String GetInstrumentDataStructureVersion(instrument_id_t ID) OVERRIDE { throw InstrumentManagerException("Not implemented"); } @@ -85,13 +83,12 @@ InstrumentConsumer* pConsumer, RTList<R*>* pRegionsInUse ) { - RegionInfoMutex.Lock(); + LockGuard lock(RegionInfoMutex); for (typename RTList<R*>::Iterator i = pRegionsInUse->first() ; i != pRegionsInUse->end() ; i++) { RegionInfo*i.refCount++; SampleRefCount(*i)->pSample++; } this->HandBack(pResource, pConsumer, true); - RegionInfoMutex.Unlock(); } /** @@ -99,7 +96,7 @@ * was previously handed back. */ virtual void HandBackRegion(R* pRegion) { - RegionInfoMutex.Lock(); + LockGuard lock(RegionInfoMutex); if (RegionInfo.find(pRegion) == RegionInfo.end()) { std::cerr << "Handing back unknown region. This is a BUG!!!" << std::endl; } @@ -117,14 +114,13 @@ } RegionInfo.erase(pRegion); } - RegionInfoMutex.Unlock(); } - virtual InstrumentManager::mode_t GetMode(const InstrumentManager::instrument_id_t& ID) { + virtual InstrumentManager::mode_t GetMode(const InstrumentManager::instrument_id_t& ID) OVERRIDE { return static_cast<InstrumentManager::mode_t>(ResourceManager<instrument_id_t, I>::AvailabilityMode(ID)); } - virtual void SetMode(const InstrumentManager::instrument_id_t& ID, InstrumentManager::mode_t Mode) { + virtual void SetMode(const InstrumentManager::instrument_id_t& ID, InstrumentManager::mode_t Mode) OVERRIDE { dmsg(2,("InstrumentManagerBase: setting mode for %s (Index=%d) to %d\n",ID.FileName.c_str(),ID.Index,Mode)); this->SetAvailabilityMode(ID, static_cast<typename ResourceManager<instrument_id_t, I>::mode_t>(Mode)); }
View file
linuxsampler-2342.tar.bz2/src/engines/InstrumentManagerThread.cpp -> linuxsampler-2718.tar.bz2/src/engines/InstrumentManagerThread.cpp
Changed
@@ -1,6 +1,6 @@ /*************************************************************************** * * - * Copyright (C) 2005 - 2012 Christian Schoenebeck * + * Copyright (C) 2005 - 2013 Christian Schoenebeck * * * * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -58,9 +58,10 @@ cmd.instrumentId.Index = uiInstrumentIndex; cmd.instrumentId.FileName = Filename; - mutex.Lock(); - queue.push_back(cmd); - mutex.Unlock(); + { + LockGuard lock(mutex); + queue.push_back(cmd); + } StartThread(); // ensure thread is running conditionJobsLeft.Set(true); // wake up thread @@ -85,9 +86,10 @@ cmd.instrumentId = ID; cmd.mode = Mode; - mutex.Lock(); - queue.push_back(cmd); - mutex.Unlock(); + { + LockGuard lock(mutex); + queue.push_back(cmd); + } StartThread(); // ensure thread is running conditionJobsLeft.Set(true); // wake up thread @@ -105,9 +107,10 @@ command_t cmd; // grab a new command from the queue - mutex.Lock(); - bool empty = queue.empty(); - if (!empty) { + { + LockGuard lock(mutex); + if (queue.empty()) break; + cmd = queue.front(); queue.pop_front(); @@ -115,8 +118,6 @@ EngineChannelFactory::SetDeleteEnabled(cmd.pEngineChannel, false); } } - mutex.Unlock(); - if (empty) break; try { switch (cmd.type) { @@ -159,7 +160,7 @@ Removing from the queue an eventual scheduled loading of an instrument to a sampler channel which is going to be removed. */ - pThread->mutex.Lock(); + LockGuard lock(pThread->mutex); std::list<command_t>::iterator it; for (it = pThread->queue.begin(); it != pThread->queue.end();){ if ((*it).type != command_t::DIRECT_LOAD) { ++it; continue; } @@ -171,13 +172,12 @@ ++it; } } - pThread->mutex.Unlock(); } -#ifdef __APPLE__ +#if defined(__APPLE__) && !defined(__x86_64__) int InstrumentManagerThread::StopThread() { - // This is a fix for Mac OS X, where SignalStopThread doesn't - // wake up a thread waiting for a condition variable. + // This is a fix for Mac OS X 32 bit, where SignalStopThread + // doesn't wake up a thread waiting for a condition variable. SignalStopThread(); // send stop signal, but don't wait conditionJobsLeft.Set(true); // wake thread return Thread::StopThread(); // then wait for it to cancel
View file
linuxsampler-2342.tar.bz2/src/engines/InstrumentManagerThread.h -> linuxsampler-2718.tar.bz2/src/engines/InstrumentManagerThread.h
Changed
@@ -48,7 +48,7 @@ void StartNewLoad(String Filename, uint uiInstrumentIndex, EngineChannel* pEngineChannel); void StartSettingMode(InstrumentManager* pManager, const InstrumentManager::instrument_id_t& ID, InstrumentManager::mode_t Mode); virtual ~InstrumentManagerThread(); -#if defined(__APPLE__) || defined(WIN32) +#if (defined(__APPLE__) && !defined(__x86_64__)) || defined(WIN32) int StopThread(); #endif protected:
View file
linuxsampler-2342.tar.bz2/src/engines/Makefile.am -> linuxsampler-2718.tar.bz2/src/engines/Makefile.am
Changed
@@ -1,4 +1,4 @@ -INCLUDES = $(all_includes) $(GIG_CFLAGS) $(LIB_SF2) +AM_CPPFLAGS = $(all_includes) $(GIG_CFLAGS) $(SNDFILE_CFLAGS) $(LIB_SF2) AM_CXXFLAGS = -Wreturn-type -ffast-math $(CXX_CPU_SWITCH) -DCONFIG_PLUGIN_DIR=\"$(config_plugin_dir)\" METASOURCES = AUTO SUBDIRS = gig sf2 sfz common
View file
linuxsampler-2718.tar.bz2/src/engines/common/AbstractInstrumentManager.cpp
Added
@@ -0,0 +1,25 @@ +/* + * Copyright (c) 2014 Christian Schoenebeck + * + * http://www.linuxsampler.org + * + * This file is part of LinuxSampler and released under the same terms. + * See README file for details. + */ + +#include "AbstractInstrumentManager.h" +#include "../AbstractEngine.h" +#include "../AbstractEngineChannel.h" + +namespace LinuxSampler { + + VMParserContext* AbstractInstrumentManager::ScriptResourceManager::Create(String Key, InstrumentScriptConsumer* pConsumer, void*& pArg) { + AbstractEngineChannel* pEngineChannel = dynamic_cast<AbstractEngineChannel*>(pConsumer); + return pEngineChannel->pEngine->pScriptVM->loadScript(Key); + } + + void AbstractInstrumentManager::ScriptResourceManager::Destroy(VMParserContext* pResource, void* pArg) { + delete pResource; + } + +} // namespace LinuxSampler
View file
linuxsampler-2718.tar.bz2/src/engines/common/AbstractInstrumentManager.h
Added
@@ -0,0 +1,41 @@ +/* + * Copyright (c) 2014 Christian Schoenebeck + * + * http://www.linuxsampler.org + * + * This file is part of LinuxSampler and released under the same terms. + * See README file for details. + */ + +#ifndef LS_ABSTRACTINSTRUMENTMANAGER_H +#define LS_ABSTRACTINSTRUMENTMANAGER_H + +#include "../InstrumentManager.h" +#include "../../common/ResourceManager.h" +#include "../../common/global_private.h" +#include "InstrumentScriptVM.h" + +namespace LinuxSampler { + + typedef ResourceConsumer<VMParserContext> InstrumentScriptConsumer; + + class AbstractInstrumentManager : public InstrumentManager { + public: + AbstractInstrumentManager() { } + virtual ~AbstractInstrumentManager() { } + + class ScriptResourceManager : public ResourceManager<String, VMParserContext> { + public: + ScriptResourceManager() {} + virtual ~ScriptResourceManager() {} + protected: + // implementation of derived abstract methods from 'ResourceManager' + virtual VMParserContext* Create(String Key, InstrumentScriptConsumer* pConsumer, void*& pArg); + virtual void Destroy(VMParserContext* pResource, void* pArg); + virtual void OnBorrow(VMParserContext* pResource, InstrumentScriptConsumer* pConsumer, void*& pArg) {} // ignore + } scripts; + }; + +} // namespace LinuxSampler + +#endif // LS_ABSTRACTINSTRUMENTMANAGER_H
View file
linuxsampler-2342.tar.bz2/src/engines/common/AbstractVoice.cpp -> linuxsampler-2718.tar.bz2/src/engines/common/AbstractVoice.cpp
Changed
@@ -112,8 +112,6 @@ Type = VoiceType; MIDIKey = itNoteOnEvent->Param.Note.Key; MIDIVelocity = itNoteOnEvent->Param.Note.Velocity; - MIDIPan = pEngineChannel->ControllerTable10; - if (MIDIPan == 0 && pEngineChannel->GlobalPanRight == 1) MIDIPan = 64; // workaround used to determine whether the MIDI pan has not been set PlaybackState = playback_state_init; // mark voice as triggered, but no audio rendered yet Delay = itNoteOnEvent->FragmentPos(); itTriggerEvent = itNoteOnEvent; @@ -126,6 +124,8 @@ RgnInfo = GetRegionInfo(); InstrInfo = GetInstrumentInfo(); + MIDIPan = CalculatePan(pEngineChannel->iLastPanRequest); + AboutToTrigger(); // calculate volume @@ -141,14 +141,12 @@ // get starting crossfade volume level float crossfadeVolume = CalculateCrossfadeVolume(itNoteOnEvent->Param.Note.Velocity); - VolumeLeft = volume * pKeyInfo->PanLeft * AbstractEngine::PanCurve64 - RgnInfo.Pan; - VolumeRight = volume * pKeyInfo->PanRight * AbstractEngine::PanCurve64 + RgnInfo.Pan; + VolumeLeft = volume * pKeyInfo->PanLeft; + VolumeRight = volume * pKeyInfo->PanRight; float subfragmentRate = GetEngine()->SampleRate / CONFIG_DEFAULT_SUBFRAGMENT_SIZE; CrossfadeSmoother.trigger(crossfadeVolume, subfragmentRate); VolumeSmoother.trigger(pEngineChannel->MidiVolume, subfragmentRate); - PanLeftSmoother.trigger(pEngineChannel->GlobalPanLeft, subfragmentRate); - PanRightSmoother.trigger(pEngineChannel->GlobalPanRight, subfragmentRate); // Check if the sample needs disk streaming or is too short for that long cachedsamples = GetSampleCacheSize() / SmplInfo.FrameSize; @@ -204,6 +202,11 @@ pSignalUnitRack->Trigger(); } + uint8_t pan = MIDIPan; + if (pSignalUnitRack) pan = pSignalUnitRack->GetEndpointUnit()->CalculatePan(MIDIPan); + PanLeftSmoother.trigger(AbstractEngine::PanCurve128 - pan, subfragmentRate); + PanRightSmoother.trigger(AbstractEngine::PanCurvepan, subfragmentRate); + #ifdef CONFIG_INTERPOLATE_VOLUME // setup initial volume in synthesis parameters #ifdef CONFIG_PROCESS_MUTED_CHANNELS @@ -221,8 +224,8 @@ finalVolume = pEngineChannel->MidiVolume * crossfadeVolume * pSignalUnitRack->GetEndpointUnit()->GetVolume(); } - finalSynthesisParameters.fFinalVolumeLeft = finalVolume * VolumeLeft * pEngineChannel->GlobalPanLeft; - finalSynthesisParameters.fFinalVolumeRight = finalVolume * VolumeRight * pEngineChannel->GlobalPanRight; + finalSynthesisParameters.fFinalVolumeLeft = finalVolume * VolumeLeft * PanLeftSmoother.render(); + finalSynthesisParameters.fFinalVolumeRight = finalVolume * VolumeRight * PanRightSmoother.render(); } #endif #endif @@ -379,7 +382,7 @@ GetFirstEventOnKey(MIDIKey, itNoteEvent); RTList<Event>::Iterator itGroupEvent; - if (pGroupEvents) itGroupEvent = pGroupEvents->first(); + if (pGroupEvents && !Orphan) itGroupEvent = pGroupEvents->first(); if (itTriggerEvent) { // skip events that happened before this voice was triggered while (itCCEvent && itCCEvent->FragmentPos() <= Skip) ++itCCEvent; @@ -424,10 +427,10 @@ fFinalCutoff = VCFCutoffCtrl.fvalue; fFinalResonance = VCFResonanceCtrl.fvalue; - // process MIDI control change and pitchbend events for this subfragment + // process MIDI control change, aftertouch and pitchbend events for this subfragment processCCEvents(itCCEvent, iSubFragmentEnd); uint8_t pan = MIDIPan; - if (pSignalUnitRack != NULL) pan = pSignalUnitRack->GetEndpointUnit()->CaluclatePan(pan); + if (pSignalUnitRack != NULL) pan = pSignalUnitRack->GetEndpointUnit()->CalculatePan(MIDIPan); PanLeftSmoother.update(AbstractEngine::PanCurve128 - pan); PanRightSmoother.update(AbstractEngine::PanCurvepan); @@ -485,7 +488,7 @@ // process low frequency oscillators if (bLFO1Enabled) fFinalVolume *= (1.0f - pLFO1->render()); - if (bLFO2Enabled) fFinalCutoff *= pLFO2->render(); + if (bLFO2Enabled) fFinalCutoff *= (1.0f - pLFO2->render()); if (bLFO3Enabled) finalSynthesisParameters.fFinalPitch *= RTMath::CentsToFreqRatio(pLFO3->render()); } else { // if the voice was killed in this subfragment, enter fade out stage @@ -608,8 +611,8 @@ } /** - * Process given list of MIDI control change and pitch bend events for - * the given time. + * Process given list of MIDI control change, aftertouch and pitch bend + * events for the given time. * * @param itEvent - iterator pointing to the next event to be processed * @param End - youngest time stamp where processing should be stopped @@ -637,10 +640,14 @@ if (itEvent->Param.CC.Controller == 7) { // volume VolumeSmoother.update(AbstractEngine::VolumeCurveitEvent->Param.CC.Value); } else if (itEvent->Param.CC.Controller == 10) { // panpot - MIDIPan = itEvent->Param.CC.Value; + MIDIPan = CalculatePan(itEvent->Param.CC.Value); } } else if (itEvent->Type == Event::type_pitchbend) { // if pitch bend event processPitchEvent(itEvent); + } else if (itEvent->Type == Event::type_channel_pressure) { + ProcessChannelPressureEvent(itEvent); + } else if (itEvent->Type == Event::type_note_pressure) { + ProcessPolyphonicKeyPressureEvent(itEvent); } ProcessCCEvent(itEvent); @@ -752,6 +759,19 @@ return pitch; } + + void AbstractVoice::onScaleTuningChanged() { + PitchInfo pitch = this->Pitch; + double pitchbasecents = InstrInfo.FineTune + RgnInfo.FineTune + GetEngine()->ScaleTuningMIDIKey % 12; + + // GSt behaviour: maximum transpose up is 40 semitones. If + // MIDI key is more than 40 semitones above unity note, + // the transpose is not done. + if (!SmplInfo.Unpitched && (MIDIKey - (int) RgnInfo.UnityNote) < 40) pitchbasecents += (MIDIKey - (int) RgnInfo.UnityNote) * 100; + + pitch.PitchBase = RTMath::CentsToFreqRatioUnlimited(pitchbasecents) * (double(SmplInfo.SampleRate) / double(GetEngine()->SampleRate)); + this->Pitch = pitch; + } double AbstractVoice::CalculateVolume(double velocityAttenuation) { // For 16 bit samples, we downscale by 32768 to convert from
View file
linuxsampler-2342.tar.bz2/src/engines/common/AbstractVoice.h -> linuxsampler-2718.tar.bz2/src/engines/common/AbstractVoice.h
Changed
@@ -4,7 +4,7 @@ * * * Copyright (C) 2003,2004 by Benno Senoner and Christian Schoenebeck * * Copyright (C) 2005-2008 Christian Schoenebeck * - * Copyright (C) 2009-2011 Christian Schoenebeck and Grigor Iliev * + * Copyright (C) 2009-2012 Christian Schoenebeck and Grigor Iliev * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -84,7 +84,7 @@ type_t Type; ///< Voice Type (bit field, a voice may have several types) int MIDIKey; ///< MIDI key number of the key that triggered the voice uint8_t MIDIVelocity; ///< MIDI velocity of the key that triggered the voice - uint8_t MIDIPan; ///< the current MIDI pan value + int MIDIPan; ///< the current MIDI pan value plus the value from RegionInfo SignalUnitRack* const pSignalUnitRack; @@ -123,6 +123,7 @@ void UpdatePortamentoPos(Pool<Event>::Iterator& itNoteOffEvent); void Kill(Pool<Event>::Iterator& itKillEvent); void CreateEq(); + void onScaleTuningChanged(); bool Orphan; ///< true if this voice is playing a sample from an instrument that is unloaded. When the voice dies, the sample (and dimension region) will be handed back to the instrument resource manager. playback_state_t PlaybackState; ///< When a sample will be triggered, it will be first played from RAM cache and after a couple of sample points it will switch to disk streaming and at the end of a disk stream we have to add null samples, so the interpolator can do it's work correctly @@ -293,6 +294,8 @@ virtual void GetFirstEventOnKey(uint8_t MIDIKey, RTList<Event>::Iterator& itEvent) = 0; virtual void ProcessCCEvent(RTList<Event>::Iterator& itEvent) = 0; + virtual void ProcessChannelPressureEvent(RTList<Event>::Iterator& itEvent) = 0; + virtual void ProcessPolyphonicKeyPressureEvent(RTList<Event>::Iterator& itEvent) = 0; virtual void ProcessCutoffEvent(RTList<Event>::Iterator& itEvent) = 0; virtual double GetVelocityRelease(uint8_t MIDIKeyVelocity) = 0; @@ -300,6 +303,8 @@ virtual void ProcessGroupEvent(RTList<Event>::Iterator& itEvent) = 0; void EnterReleaseStage(); + + virtual int CalculatePan(uint8_t pan) = 0; }; } // namespace LinuxSampler
View file
linuxsampler-2342.tar.bz2/src/engines/common/BiquadFilter.h -> linuxsampler-2718.tar.bz2/src/engines/common/BiquadFilter.h
Changed
@@ -3,7 +3,7 @@ * LinuxSampler - modular, streaming capable sampler * * * * Copyright (C) 2003, 2004 by Benno Senoner and Christian Schoenebeck * - * Copyright (C) 2005 - 2007 Christian Schoenebeck * + * Copyright (C) 2005 - 2012 Christian Schoenebeck * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -120,7 +120,7 @@ return y; } - inline bq_t Apply(biquad_param_t* param, const bq_t x) { + inline bq_t Apply(const biquad_param_t* __restrict param, const bq_t x) { bq_t y; y = param->b0 * x + param->b1 * this->x1 + param->b2 * this->x2 + @@ -238,7 +238,7 @@ return y; } - inline bq_t ApplyFB(biquad_param_t* param, bq_t x, const bq_t fb) { + inline bq_t ApplyFB(const biquad_param_t* __restrict param, bq_t x, const bq_t fb) { bq_t y; x += this->y1 * fb * 0.98; @@ -474,7 +474,7 @@ this->a2 = a0r * (alpha - 1.0); } - inline void SetParameters(biquad_param_t* param, bq_t fc, bq_t bw, bq_t fs) { + inline void SetParameters(biquad_param_t* __restrict param, bq_t fc, bq_t bw, bq_t fs) { bq_t omega = 2.0 * M_PI * fc / fs; bq_t sn = sin(omega); bq_t cs = cos(omega); @@ -511,7 +511,7 @@ this->a2 = a0r * (alpha - 1.0); } - inline void SetParameters(biquad_param_t* param, bq_t fc, bq_t bw, bq_t fs) { + inline void SetParameters(biquad_param_t* __restrict param, bq_t fc, bq_t bw, bq_t fs) { bq_t omega = 2.0 * M_PI * fc / fs; bq_t sn = sin(omega); bq_t cs = cos(omega); @@ -548,7 +548,7 @@ this->a2 = a0r * (alpha - 1.0); } - inline void SetParameters(biquad_param_t* param, bq_t fc, bq_t bw, bq_t fs) { + inline void SetParameters(biquad_param_t* __restrict param, bq_t fc, bq_t bw, bq_t fs) { bq_t omega = 2.0 * M_PI * fc / fs; bq_t sn = sin(omega); bq_t cs = cos(omega);
View file
linuxsampler-2342.tar.bz2/src/engines/common/DiskThreadBase.h -> linuxsampler-2718.tar.bz2/src/engines/common/DiskThreadBase.h
Changed
@@ -4,7 +4,8 @@ * * * Copyright (C) 2003, 2004 by Benno Senoner and Christian Schoenebeck * * Copyright (C) 2005 - 2008 Christian Schoenebeck * - * Copyright (C) 2009 - 2011 Christian Schoenebeck and Grigor Iliev * + * Copyright (C) 2009 - 2012 Christian Schoenebeck and Grigor Iliev * + * Copyright (C) 2013 - 2014 Christian Schoenebeck and Andreas Persson * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -25,6 +26,8 @@ #ifndef __LS_DISKTHREADBASE_H__ #define __LS_DISKTHREADBASE_H__ +#include <map> + #include "StreamBase.h" #include "../EngineChannel.h" #include "../InstrumentManagerBase.h" @@ -503,11 +506,24 @@ } // perform MIDI program change commands - while (ProgramChangeQueue.read_space() > 0) { + if (ProgramChangeQueue.read_space() > 0) { program_change_command_t cmd; - ProgramChangeQueue.pop(&cmd); - cmd.pEngineChannel->ExecuteProgramChange(cmd.Program); + std::map<EngineChannel*,uint32_t> cmds; + // skip all old ones, only process the latest program + // change command on each engine channel + do { + ProgramChangeQueue.pop(&cmd); + cmdscmd.pEngineChannel = cmd.Program; + } while (ProgramChangeQueue.read_space() > 0); + // now execute those latest program change commands on + // their respective engine channel + for (std::map<EngineChannel*,uint32_t>::const_iterator it = cmds.begin(); + it != cmds.end(); ++it) + { + it->first->ExecuteProgramChange(it->second); + } } + RefillStreams(); // refill the most empty streams // if nothing was done during this iteration (eg no streambuffer
View file
linuxsampler-2342.tar.bz2/src/engines/common/Event.h -> linuxsampler-2718.tar.bz2/src/engines/common/Event.h
Changed
@@ -3,7 +3,7 @@ * LinuxSampler - modular, streaming capable sampler * * * * Copyright (C) 2003, 2004 by Benno Senoner and Christian Schoenebeck * - * Copyright (C) 2005 - 2012 Christian Schoenebeck * + * Copyright (C) 2005 - 2014 Christian Schoenebeck * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -64,7 +64,7 @@ * controller like LFO or EG. An event should only be created by an * EventGenerator! * - * @see EventGenerator + * @see EventGenerator, ScriptEvent */ class Event { public: @@ -76,34 +76,56 @@ type_control_change, type_sysex, ///< MIDI system exclusive message type_cancel_release, ///< transformed either from a note-on or sustain-pedal-down event - type_release ///< transformed either from a note-off or sustain-pedal-up event + type_release, ///< transformed either from a note-off or sustain-pedal-up event + type_channel_pressure, ///< a.k.a. aftertouch + type_note_pressure, ///< polyphonic key pressure (aftertouch) } Type; union { /// Note-on and note-off event specifics struct _Note { + uint8_t Channel; ///< MIDI channel (0..15) uint8_t Key; ///< MIDI key number of note-on / note-off event. uint8_t Velocity; ///< Trigger or release velocity of note-on / note-off event. - uint8_t Channel; ///< MIDI channel (0..15) int8_t Layer; ///< Layer index (usually only used if a note-on event has to be postponed, e.g. due to shortage of free voices). int8_t ReleaseTrigger; ///< If new voice should be a release triggered voice (actually boolean field and usually only used if a note-on event has to be postponed, e.g. due to shortage of free voices). void* pRegion; ///< Engine specific pointer to instrument region } Note; /// Control change event specifics struct _CC { + uint8_t Channel; ///< MIDI channel (0..15) uint8_t Controller; ///< MIDI controller number of control change event. uint8_t Value; ///< Controller Value of control change event. - uint8_t Channel; ///< MIDI channel (0..15) } CC; /// Pitchbend event specifics struct _Pitch { - int16_t Pitch; ///< Pitch value of pitchbend event. uint8_t Channel; ///< MIDI channel (0..15) + int16_t Pitch; ///< Pitch value of pitchbend event. } Pitch; /// MIDI system exclusive event specifics struct _Sysex { uint Size; ///< Data length (in bytes) of MIDI system exclusive message. } Sysex; + /// Channel Pressure (aftertouch) event specifics + struct _ChannelPressure { + uint8_t Channel; ///< MIDI channel (0..15) + uint8_t Controller; ///< Should always be assigned to CTRL_TABLE_IDX_AFTERTOUCH. + uint8_t Value; ///< New aftertouch / pressure value for keys on that channel. + } ChannelPressure; + /// Polyphonic Note Pressure (aftertouch) event specifics + struct _NotePressure { + uint8_t Channel; ///< MIDI channel (0..15) + uint8_t Key; ///< MIDI note number where key pressure (polyphonic aftertouch) changed. + uint8_t Value; ///< New pressure value for note. + } NotePressure; } Param; + /// Sampler format specific informations and variables. + union { + /// Gigasampler/GigaStudio format specifics. + struct _Gig { + uint8_t DimMask; ///< May be used to override the Dimension zone to be selected for a new voice: each 1 bit means that respective bit shall be overridden by taking the respective bit from DimBits instead. + uint8_t DimBits; ///< Used only in conjunction with DimMask: Dimension bits that shall be selected. + } Gig; + } Format; EngineChannel* pEngineChannel; ///< Pointer to the EngineChannel where this event occured on, NULL means Engine global event (e.g. SysEx message). MidiInputPort* pMidiInputPort; ///< Pointer to the MIDI input port on which this event occured (NOTE: currently only for global events, that is SysEx messages) @@ -127,6 +149,24 @@ int32_t iFragmentPos; ///< Position in the current fragment this event refers to. }; + class VMEventHandler; + class VMExecContext; + + /** @brief Real-time instrument script event. + * + * Encapsulates one execution instance of a real-time instrument script for + * exactly one script event handler (script event callback). + */ + class ScriptEvent { + public: + Event cause; ///< Original external event that triggered this script event (i.e. MIDI note on event, MIDI CC event, etc.). + int id; ///< Unique ID of the external event that triggered this cript event. + VMEventHandler** handlers; ///< The script's event handlers (callbacks) to be processed (NULL terminated list). + VMExecContext* execCtx; ///< Script's current execution state (polyphonic variables and execution stack). + int currentHandler; ///< Current index in 'handlers' list above. + int executionSlices; ///< Amount of times this script event has been executed by the ScriptVM runner class. + }; + } // namespace LinuxSampler #endif // __LS_EVENT_H__
View file
linuxsampler-2718.tar.bz2/src/engines/common/InstrumentScriptVM.cpp
Added
@@ -0,0 +1,312 @@ +/* + * Copyright (c) 2014 Christian Schoenebeck + * + * http://www.linuxsampler.org + * + * This file is part of LinuxSampler and released under the same terms. + * See README file for details. + */ + +#include "InstrumentScriptVM.h" +#include "../AbstractEngineChannel.h" +#include "../../common/global_private.h" +#include "AbstractInstrumentManager.h" +#include "MidiKeyboardManager.h" + +namespace LinuxSampler { + + /////////////////////////////////////////////////////////////////////// + // class 'EventGroup' + + void EventGroup::insert(int eventID) { + if (contains(eventID)) return; + + AbstractEngine* pEngine = m_script->pEngineChannel->pEngine; + + // before adding the new event ID, check if there are any dead events + // and remove them in that case, before otherwise we might run in danger + // to run out of free space on this group for event IDs if a lot of + // events die before being removed explicitly from the group by script + // + // NOTE: or should we do this "dead ones" check only once in a while? + int firstDead = -1; + for (int i = 0; i < size(); ++i) { + if (firstDead >= 0) { + if (pEngine->EventByID(eventID)) { + remove(firstDead, i - firstDead); + firstDead = -1; + } + } else { + if (!pEngine->EventByID(eventID)) firstDead = i; + } + } + + append(eventID); + } + + void EventGroup::erase(int eventID) { + int index = find(eventID); + remove(index); + } + + /////////////////////////////////////////////////////////////////////// + // class 'InstrumentScript' + + InstrumentScript::InstrumentScript(AbstractEngineChannel* pEngineChannel) { + parserContext = NULL; + bHasValidScript = false; + handlerInit = NULL; + handlerNote = NULL; + handlerRelease = NULL; + handlerController = NULL; + pEvents = NULL; + for (int i = 0; i < 128; ++i) + pKeyEventsi = NULL; + this->pEngineChannel = pEngineChannel; + for (int i = 0; i < INSTR_SCRIPT_EVENT_GROUPS; ++i) + eventGroupsi.setScript(this); + } + + InstrumentScript::~InstrumentScript() { + resetAll(); + if (pEvents) { + for (int i = 0; i < 128; ++i) delete pKeyEventsi; + delete pEvents; + } + } + + /** @brief Load real-time instrument script. + * + * Loads the real-time instrument script given by @a text on the engine + * channel this InstrumentScript object belongs to (defined by + * pEngineChannel member variable). The sampler engine's resource manager is + * used to allocate and share equivalent scripts on multiple engine + * channels. + * + * @param text - source code of script + */ + void InstrumentScript::load(const String& text) { + dmsg(1,("Loading real-time instrument script ... ")); + + // hand back old script reference and VM execution contexts + // (if not done already) + unload(); + + code = text; + + AbstractInstrumentManager* pManager = + dynamic_cast<AbstractInstrumentManager*>(pEngineChannel->pEngine->GetInstrumentManager()); + + // get new script reference + parserContext = pManager->scripts.Borrow(text, pEngineChannel); + if (!parserContext->errors().empty()) { + std::vector<ParserIssue> errors = parserContext->errors(); + std::cerr << "ScriptVM Could not load instrument script, there were " + << errors.size() << " parser errors:\n"; + for (int i = 0; i < errors.size(); ++i) + errorsi.dump(); + return; // stop here if there were any parser errors + } + + handlerInit = parserContext->eventHandlerByName("init"); + handlerNote = parserContext->eventHandlerByName("note"); + handlerRelease = parserContext->eventHandlerByName("release"); + handlerController = parserContext->eventHandlerByName("controller"); + bHasValidScript = + handlerInit || handlerNote || handlerRelease || handlerController; + + // amount of script handlers each script event has to execute + int handlerExecCount = 0; + if (handlerNote || handlerRelease || handlerController) // only one of these are executed after "init" handler + handlerExecCount++; + + // create script event pool (if it doesn't exist already) + if (!pEvents) { + pEvents = new Pool<ScriptEvent>(CONFIG_MAX_EVENTS_PER_FRAGMENT); + for (int i = 0; i < 128; ++i) + pKeyEventsi = new RTList<ScriptEvent>(pEvents); + } + + // create new VM execution contexts for new script + while (!pEvents->poolIsEmpty()) { + RTList<ScriptEvent>::Iterator it = pEvents->allocAppend(); + it->execCtx = pEngineChannel->pEngine->pScriptVM->createExecContext( + parserContext + ); + it->handlers = new VMEventHandler*handlerExecCount+1; + } + pEvents->clear(); + + dmsg(1,("Done\n")); + } + + /** @brief Unload real-time instrument script. + * + * Unloads the currently used real-time instrument script and frees all + * resources allocated for that script. The sampler engine's resource manager + * is used to share equivalent scripts among multiple sampler channels, and + * to deallocate the parsed script once not used on any engine channel + * anymore. + * + * Calling this method will however not clear the @c code member variable. + * Thus, the script can be parsed again afterwards. + */ + void InstrumentScript::unload() { + //dmsg(1,("InstrumentScript::unload(this=0x%llx)\n", this)); + + if (parserContext) + dmsg(1,("Unloading current instrument script.\n")); + + // free allocated VM execution contexts + if (pEvents) { + pEvents->clear(); + while (!pEvents->poolIsEmpty()) { + RTList<ScriptEvent>::Iterator it = pEvents->allocAppend(); + if (it->execCtx) { + // free VM execution context object + delete it->execCtx; + it->execCtx = NULL; + // free C array of handler pointers + delete it->handlers; + } + } + pEvents->clear(); + } + // hand back VM representation of script + if (parserContext) { + AbstractInstrumentManager* pManager = + dynamic_cast<AbstractInstrumentManager*>(pEngineChannel->pEngine->GetInstrumentManager()); + + pManager->scripts.HandBack(parserContext, pEngineChannel); + parserContext = NULL; + handlerInit = NULL; + handlerNote = NULL; + handlerRelease = NULL; + handlerController = NULL; + } + bHasValidScript = false; + } + + /** + * Same as unload(), but this one also empties the @c code member variable + * to an empty string. + */ + void InstrumentScript::resetAll() { + unload(); + code.clear(); + } + + /////////////////////////////////////////////////////////////////////// + // class 'InstrumentScriptVM' + + InstrumentScriptVM::InstrumentScriptVM() : + m_event(NULL), m_fnPlayNote(this), m_fnSetController(this), + m_fnIgnoreEvent(this), m_fnIgnoreController(this), m_fnNoteOff(this), + m_fnSetEventMark(this), m_fnDeleteEventMark(this), m_fnByMarks(this) + { + m_CC.size = _MEMBER_SIZEOF(AbstractEngineChannel, ControllerTable); + m_CC_NUM = DECLARE_VMINT(m_event, class ScriptEvent, cause.Param.CC.Controller); + m_EVENT_ID = DECLARE_VMINT(m_event, class ScriptEvent, id); + m_EVENT_NOTE = DECLARE_VMINT(m_event, class ScriptEvent, cause.Param.Note.Key); + m_EVENT_VELOCITY = DECLARE_VMINT(m_event, class ScriptEvent, cause.Param.Note.Velocity); + m_KEY_DOWN.size = 128; + } + + VMExecStatus_t InstrumentScriptVM::exec(VMParserContext* parserCtx, ScriptEvent* event) { + AbstractEngineChannel* pEngineChannel = + static_cast<AbstractEngineChannel*>(event->cause.pEngineChannel); + + // prepare built-in script variables for script execution + m_event = event; + m_CC.data = (int8_t*) &pEngineChannel->ControllerTable0; + m_KEY_DOWN.data = &pEngineChannel->GetMidiKeyboardManager()->KeyDown0; + + // if script is in start condition, then do mandatory MIDI event + // preprocessing tasks, which essentially means updating i.e. controller + // table with new CC value in case of a controller event, because the + // script might access the new CC value + if (!event->executionSlices) { + switch (event->cause.Type) { + case Event::type_control_change: + pEngineChannel->ControllerTableevent->cause.Param.CC.Controller = + event->cause.Param.CC.Value; + break; + case Event::type_channel_pressure: + pEngineChannel->ControllerTableCTRL_TABLE_IDX_AFTERTOUCH = + event->cause.Param.ChannelPressure.Value; + break; + case Event::type_pitchbend: + pEngineChannel->ControllerTableCTRL_TABLE_IDX_PITCHBEND = + event->cause.Param.Pitch.Pitch; + break; + } + } + + // run the script handler(s) + VMExecStatus_t res = VM_EXEC_NOT_RUNNING; + for ( ; event->handlersevent->currentHandler; event->currentHandler++) { + res = ScriptVM::exec( + parserCtx, event->execCtx, event->handlersevent->currentHandler + ); + event->executionSlices++; + if (res & VM_EXEC_SUSPENDED || res & VM_EXEC_ERROR) return res; + } + + return res; + } + + std::map<String,VMIntRelPtr*> InstrumentScriptVM::builtInIntVariables() { + // first get buil-in integer variables of derived VM class + std::map<String,VMIntRelPtr*> m = ScriptVM::builtInIntVariables(); + + // now add own built-in variables + m"$CC_NUM" = &m_CC_NUM; + m"$EVENT_ID" = &m_EVENT_ID; + m"$EVENT_NOTE" = &m_EVENT_NOTE; + m"$EVENT_VELOCITY" = &m_EVENT_VELOCITY; +// m"$POLY_AT_NUM" = &m_POLY_AT_NUM; + + return m; + } + + std::map<String,VMInt8Array*> InstrumentScriptVM::builtInIntArrayVariables() { + // first get buil-in integer array variables of derived VM class + std::map<String,VMInt8Array*> m = ScriptVM::builtInIntArrayVariables(); + + // now add own built-in variables + m"%CC" = &m_CC; + m"%KEY_DOWN" = &m_KEY_DOWN; + //m"%POLY_AT" = &m_POLY_AT; + + return m; + } + + std::map<String,int> InstrumentScriptVM::builtInConstIntVariables() { + // first get buil-in integer variables of derived VM class + std::map<String,int> m = ScriptVM::builtInConstIntVariables(); + + m"$VCC_MONO_AT" = CTRL_TABLE_IDX_AFTERTOUCH; + m"$VCC_PITCH_BEND" = CTRL_TABLE_IDX_PITCHBEND; + for (int i = 0; i < INSTR_SCRIPT_EVENT_GROUPS; ++i) { + m"$MARK_" + ToString(i+1) = i; + } + + return m; + } + + VMFunction* InstrumentScriptVM::functionByName(const String& name) { + // built-in script functions of this class + if (name == "play_note") return &m_fnPlayNote; + else if (name == "set_controller") return &m_fnSetController; + else if (name == "ignore_event") return &m_fnIgnoreEvent; + else if (name == "ignore_controller") return &m_fnIgnoreController; + else if (name == "note_off") return &m_fnNoteOff; + else if (name == "set_event_mark") return &m_fnSetEventMark; + else if (name == "delete_event_mark") return &m_fnDeleteEventMark; + else if (name == "by_marks") return &m_fnByMarks; + + // built-in script functions of derived VM class + return ScriptVM::functionByName(name); + } + +} // namespace LinuxSampler
View file
linuxsampler-2718.tar.bz2/src/engines/common/InstrumentScriptVM.h
Added
@@ -0,0 +1,131 @@ +/* + * Copyright (c) 2014 Christian Schoenebeck + * + * http://www.linuxsampler.org + * + * This file is part of LinuxSampler and released under the same terms. + * See README file for details. + */ + +#ifndef LS_INSTRUMENT_SCRIPT_VM_H +#define LS_INSTRUMENT_SCRIPT_VM_H + +#include "../../common/global.h" +#include "../../common/ConstCapacityArray.h" +#include "../../scriptvm/ScriptVM.h" +#include "Event.h" +#include "../../common/Pool.h" +#include "InstrumentScriptVMFunctions.h" + +#define INSTR_SCRIPT_EVENT_GROUPS 28 + +namespace LinuxSampler { + + class AbstractEngineChannel; + class InstrumentScript; + + /** @brief List of Event IDs. + * + * Used for built-in script functions: + * by_marks(), set_event_mark(), delete_event_mark() + */ + class EventGroup : protected ConstCapacityArray<int> { + public: + EventGroup() : ConstCapacityArray<int>(CONFIG_MAX_EVENTS_PER_FRAGMENT), m_script(NULL) {} + void insert(int eventID); + void erase(int eventID); + void setScript(InstrumentScript* pScript) { m_script = pScript; } + inline int size() const { return ConstCapacityArray<int>::size(); } + inline int& operator(uint index) { return ConstCapacityArray<int>::operator(index); } + inline const int& operator(uint index) const { return ConstCapacityArray<int>::operator(index); } + protected: + InstrumentScript* m_script; + }; + + /** @brief Real-time instrument script VM representation. + * + * Holds the VM representation of all event handlers of the currently loaded + * script, ready to be executed by the sampler engine. + * + * Even thought scripts (or to be more specific their event handler objects) + * are shared between sampler engine channels, the InstrumentScript struct + * instances though are not shared. Each sampler engine channel has its own + * instance of a InstrumentScript struct. That's important, because this + * struct also holds engine channel local informations, for example the + * events that occured on the respective engine channel. + */ + struct InstrumentScript { + VMParserContext* parserContext; ///< VM represenation of the currently loaded script or NULL if not script was loaded. Note that it is also not NULL if parser errors occurred! + bool bHasValidScript; ///< True in case there is a valid script currently loaded, false if script processing shall be skipped. + VMEventHandler* handlerInit; ///< VM representation of script's initilization callback or NULL if current script did not define such an init handler. + VMEventHandler* handlerNote; ///< VM representation of script's MIDI note on callback or NULL if current script did not define such an event handler. + VMEventHandler* handlerRelease; ///< VM representation of script's MIDI note off callback or NULL if current script did not define such an event handler. + VMEventHandler* handlerController; ///< VM representation of script's MIDI controller callback or NULL if current script did not define such an event handler. + Pool<ScriptEvent>* pEvents; ///< Pool of all available script execution instances. ScriptEvents available to be allocated from the Pool are currently unused / not executiong, whereas the ScriptEvents allocated on the list are currently suspended / have not finished execution yet (@see pKeyEvents). + RTList<ScriptEvent>* pKeyEvents128; ///< Stores previously finished executed "note on" script events for the respective active note/key as long as the key/note is active. This is however only done if there is a "note" script event handler and a "release" script event handler defined in the script and both handlers use (reference) polyphonic variables. If that is not the case, then this list is not used at all. So the purpose of pKeyEvents is only to implement preserving/passing polyphonic variable data from "on note .. end on" script block to the respective "on release .. end on" script block. + AbstractEngineChannel* pEngineChannel; + String code; ///< Source code of the instrument script. Used in case the sampler engine is changed, in that case a new ScriptVM object is created for the engine and VMParserContext object for this script needs to be recreated as well. Thus the script is then parsed again by passing the source code to recreate the parser context. + EventGroup eventGroupsINSTR_SCRIPT_EVENT_GROUPS; ///< Used for built-in script functions: by_event_marks(), set_event_mark(), delete_event_mark(). + + InstrumentScript(AbstractEngineChannel* pEngineChannel); + ~InstrumentScript(); + + void load(const String& text); + void unload(); + void resetAll(); + }; + + /** @brief Real-time instrument script virtual machine. + * + * Extends the core ScriptVM implementation with MIDI specific built-in + * script functions and MIDI specific built-in script variables required + * for MIDI processing by instrument script for all sampler engine + * implementations (sampler formats) of this sampler. + * + * Note that this class is currently re-entrant safe, but @b not thread + * safe! See also comments of base class ScriptVM regarding this issue. + */ + class InstrumentScriptVM : public ScriptVM { + public: + InstrumentScriptVM(); + VMExecStatus_t exec(VMParserContext* parserCtx, ScriptEvent* event); + VMFunction* functionByName(const String& name) OVERRIDE; + std::map<String,VMIntRelPtr*> builtInIntVariables() OVERRIDE; + std::map<String,VMInt8Array*> builtInIntArrayVariables() OVERRIDE; + std::map<String,int> builtInConstIntVariables() OVERRIDE; + protected: + ScriptEvent* m_event; ///< The event currently executed by exec(). + + // buil-in script variables + VMInt8Array m_CC; + VMInt8RelPtr m_CC_NUM; + VMIntRelPtr m_EVENT_ID; + VMInt8RelPtr m_EVENT_NOTE; + VMInt8RelPtr m_EVENT_VELOCITY; + VMInt8Array m_KEY_DOWN; + //VMIntArray m_POLY_AT; //TODO: ... + //int m_POLY_AT_NUM; //TODO: ... + + // buil-in script functions + InstrumentScriptVMFunction_play_note m_fnPlayNote; + InstrumentScriptVMFunction_set_controller m_fnSetController; + InstrumentScriptVMFunction_ignore_event m_fnIgnoreEvent; + InstrumentScriptVMFunction_ignore_controller m_fnIgnoreController; + InstrumentScriptVMFunction_note_off m_fnNoteOff; + InstrumentScriptVMFunction_set_event_mark m_fnSetEventMark; + InstrumentScriptVMFunction_delete_event_mark m_fnDeleteEventMark; + InstrumentScriptVMFunction_by_marks m_fnByMarks; + + friend class InstrumentScriptVMFunction_play_note; + friend class InstrumentScriptVMFunction_set_controller; + friend class InstrumentScriptVMFunction_ignore_event; + friend class InstrumentScriptVMFunction_ignore_controller; + friend class InstrumentScriptVMFunction_note_off; + friend class InstrumentScriptVMFunction_set_event_mark; + friend class InstrumentScriptVMFunction_delete_event_mark; + friend class InstrumentScriptVMFunction_by_marks; + }; + +} // namespace LinuxSampler + +#endif // LS_INSTRUMENT_SCRIPT_VM_H
View file
linuxsampler-2718.tar.bz2/src/engines/common/InstrumentScriptVMFunctions.cpp
Added
@@ -0,0 +1,293 @@ +/* + * Copyright (c) 2014 Christian Schoenebeck + * + * http://www.linuxsampler.org + * + * This file is part of LinuxSampler and released under the same terms. + * See README file for details. + */ + +#include "InstrumentScriptVMFunctions.h" +#include "InstrumentScriptVM.h" +#include "../AbstractEngineChannel.h" + +namespace LinuxSampler { + + InstrumentScriptVMFunction_play_note::InstrumentScriptVMFunction_play_note(InstrumentScriptVM* parent) + : m_vm(parent) + { + } + + VMFnResult* InstrumentScriptVMFunction_play_note::exec(VMFnArgs* args) { + int note = args->arg(0)->asInt()->evalInt(); + int velocity = (args->argsCount() >= 2) ? args->arg(1)->asInt()->evalInt() : 127; + int sampleoffset = (args->argsCount() >= 3) ? args->arg(2)->asInt()->evalInt() : 0; + int duration = (args->argsCount() >= 4) ? args->arg(3)->asInt()->evalInt() : 0; //TODO: once -1 is implemented, it might be a better default value instead of 0 + + if (note < 0 || note > 127) { + errMsg("play_note(): argument 1 is an invalid note number"); + return errorResult(-1); + } + + if (velocity < 0 || velocity > 127) { + errMsg("play_note(): argument 2 is an invalid velocity value"); + return errorResult(-1); + } + + if (sampleoffset < 0) { + errMsg("play_note(): argument 3 may not be a negative sample offset"); + return errorResult(-1); + } else if (sampleoffset != 0) { + wrnMsg("play_note(): argument 3 does not support a sample offset other than 0 yet"); + } + + if (duration < -1) { + errMsg("play_note(): argument 4 must be a duration value of at least -1 or higher"); + return errorResult(-1); + } else if (duration == -1) { + wrnMsg("play_note(): argument 4 does not support special value -1 as duration yet"); + } else if (duration != 0) { + wrnMsg("play_note(): argument 4 does not support any other value as 0 as duration yet"); + } + + AbstractEngineChannel* pEngineChannel = + static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel); + + Event e = m_vm->m_event->cause; + e.Type = Event::type_note_on; + e.Param.Note.Key = note; + e.Param.Note.Velocity = velocity; + memset(&e.Format, 0, sizeof(e.Format)); // init format speific stuff with zero + + int id = pEngineChannel->ScheduleEvent(&e, duration); + + return successResult(id); + } + + InstrumentScriptVMFunction_set_controller::InstrumentScriptVMFunction_set_controller(InstrumentScriptVM* parent) + : m_vm(parent) + { + } + + VMFnResult* InstrumentScriptVMFunction_set_controller::exec(VMFnArgs* args) { + int controller = args->arg(0)->asInt()->evalInt(); + int value = args->arg(1)->asInt()->evalInt(); + + AbstractEngineChannel* pEngineChannel = + static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel); + + Event e = m_vm->m_event->cause; + memset(&e.Format, 0, sizeof(e.Format)); // init format speific stuff with zero + if (controller == CTRL_TABLE_IDX_AFTERTOUCH) { + e.Type = Event::type_channel_pressure; + e.Param.ChannelPressure.Value = value & 127; + } else if (controller == CTRL_TABLE_IDX_PITCHBEND) { + e.Type = Event::type_pitchbend; + e.Param.Pitch.Pitch = value; + } else if (controller >= 0 && controller <= 127) { + e.Type = Event::type_control_change; + e.Param.CC.Controller = controller; + e.Param.CC.Value = value; + } else { + errMsg("set_controller(): argument 1 is an invalid controller"); + return errorResult(); + } + + int id = pEngineChannel->ScheduleEvent(&e, 0); + + return successResult(id); + } + + InstrumentScriptVMFunction_ignore_event::InstrumentScriptVMFunction_ignore_event(InstrumentScriptVM* parent) + : m_vm(parent) + { + } + + bool InstrumentScriptVMFunction_ignore_event::acceptsArgType(int iArg, ExprType_t type) const { + return type == INT_EXPR || type == INT_ARR_EXPR; + } + + VMFnResult* InstrumentScriptVMFunction_ignore_event::exec(VMFnArgs* args) { + AbstractEngineChannel* pEngineChannel = + static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel); + + if (args->arg(0)->exprType() == INT_EXPR) { + int id = args->arg(0)->asInt()->evalInt(); + if (id < 0) { + wrnMsg("ignore_event(): argument may not be a negative event ID"); + return successResult(); + } + pEngineChannel->IgnoreEvent(id); + } else if (args->arg(0)->exprType() == INT_ARR_EXPR) { + VMIntArrayExpr* ids = args->arg(0)->asIntArray(); + for (int i = 0; i < ids->arraySize(); ++i) { + int id = ids->evalIntElement(i); + pEngineChannel->IgnoreEvent(id); + } + } + + return successResult(); + } + + InstrumentScriptVMFunction_ignore_controller::InstrumentScriptVMFunction_ignore_controller(InstrumentScriptVM* parent) + : m_vm(parent) + { + } + + VMFnResult* InstrumentScriptVMFunction_ignore_controller::exec(VMFnArgs* args) { + int id = (args->argsCount() >= 1) ? args->arg(0)->asInt()->evalInt() : m_vm->m_event->id; + if (id < 0) { + wrnMsg("ignore_controller(): argument may not be a negative event ID"); + return successResult(); + } + + AbstractEngineChannel* pEngineChannel = + static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel); + + pEngineChannel->IgnoreEvent(id); + + return successResult(); + } + + InstrumentScriptVMFunction_note_off::InstrumentScriptVMFunction_note_off(InstrumentScriptVM* parent) + : m_vm(parent) + { + } + + bool InstrumentScriptVMFunction_note_off::acceptsArgType(int iArg, ExprType_t type) const { + return type == INT_EXPR || type == INT_ARR_EXPR; + } + + VMFnResult* InstrumentScriptVMFunction_note_off::exec(VMFnArgs* args) { + AbstractEngineChannel* pEngineChannel = + static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel); + + int velocity = (args->argsCount() >= 2) ? args->arg(1)->asInt()->evalInt() : 127; + if (velocity < 0 || velocity > 127) { + errMsg("note_off(): argument 2 is an invalid velocity value"); + return errorResult(); + } + + if (args->arg(0)->exprType() == INT_EXPR) { + int id = args->arg(0)->asInt()->evalInt(); + if (id < 0) { + wrnMsg("note_off(): argument 1 may not be a negative event ID"); + return successResult(); + } + + RTList<Event>::Iterator itEvent = pEngineChannel->pEngine->EventByID(id); + if (!itEvent) return successResult(); + + Event e = *itEvent; + e.Type = Event::type_note_off; + e.Param.Note.Velocity = velocity; + memset(&e.Format, 0, sizeof(e.Format)); // init format speific stuff with zero + + int releaseEventID = pEngineChannel->ScheduleEvent(&e, 0); + } else if (args->arg(0)->exprType() == INT_ARR_EXPR) { + VMIntArrayExpr* ids = args->arg(0)->asIntArray(); + for (int i = 0; i < ids->arraySize(); ++i) { + int id = ids->evalIntElement(i); + + RTList<Event>::Iterator itEvent = pEngineChannel->pEngine->EventByID(id); + if (!itEvent) continue; + + Event e = *itEvent; + e.Type = Event::type_note_off; + e.Param.Note.Velocity = velocity; + memset(&e.Format, 0, sizeof(e.Format)); // init format speific stuff with zero + + int releaseEventID = pEngineChannel->ScheduleEvent(&e, 0); + } + } + + return successResult(); + } + + InstrumentScriptVMFunction_set_event_mark::InstrumentScriptVMFunction_set_event_mark(InstrumentScriptVM* parent) + : m_vm(parent) + { + } + + VMFnResult* InstrumentScriptVMFunction_set_event_mark::exec(VMFnArgs* args) { + int eventID = args->arg(0)->asInt()->evalInt(); + int groupID = args->arg(1)->asInt()->evalInt(); + + if (groupID < 0 || groupID >= INSTR_SCRIPT_EVENT_GROUPS) { + errMsg("set_event_mark(): argument 2 is an invalid group id"); + return errorResult(); + } + + AbstractEngineChannel* pEngineChannel = + static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel); + + RTList<Event>::Iterator itEvent = pEngineChannel->pEngine->EventByID(eventID); + if (!itEvent) return successResult(); + + pEngineChannel->pScript->eventGroupsgroupID.insert(eventID); + + return successResult(); + } + + InstrumentScriptVMFunction_delete_event_mark::InstrumentScriptVMFunction_delete_event_mark(InstrumentScriptVM* parent) + : m_vm(parent) + { + } + + VMFnResult* InstrumentScriptVMFunction_delete_event_mark::exec(VMFnArgs* args) { + int eventID = args->arg(0)->asInt()->evalInt(); + int groupID = args->arg(1)->asInt()->evalInt(); + + if (groupID < 0 || groupID >= INSTR_SCRIPT_EVENT_GROUPS) { + errMsg("delete_event_mark(): argument 2 is an invalid group id"); + return errorResult(); + } + + AbstractEngineChannel* pEngineChannel = + static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel); + + pEngineChannel->pScript->eventGroupsgroupID.erase(eventID); + + return successResult(); + } + + InstrumentScriptVMFunction_by_marks::InstrumentScriptVMFunction_by_marks(InstrumentScriptVM* parent) + : m_vm(parent) + { + } + + int InstrumentScriptVMFunction_by_marks::Result::arraySize() const { + return eventGroup->size(); + } + + int InstrumentScriptVMFunction_by_marks::Result::evalIntElement(uint i) { + return (*eventGroup)i; + } + + VMFnResult* InstrumentScriptVMFunction_by_marks::errorResult() { + m_result.eventGroup = NULL; + m_result.flags = StmtFlags_t(STMT_ABORT_SIGNALLED | STMT_ERROR_OCCURRED); + return &m_result; + } + + VMFnResult* InstrumentScriptVMFunction_by_marks::successResult(EventGroup* eventGroup) { + m_result.eventGroup = eventGroup; + m_result.flags = STMT_SUCCESS; + return &m_result; + } + + VMFnResult* InstrumentScriptVMFunction_by_marks::exec(VMFnArgs* args) { + int groupID = args->arg(0)->asInt()->evalInt(); + + if (groupID < 0 || groupID >= INSTR_SCRIPT_EVENT_GROUPS) { + errMsg("by_marks(): argument is an invalid group id"); + return errorResult(); + } + + AbstractEngineChannel* pEngineChannel = + static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel); + + return successResult( &pEngineChannel->pScript->eventGroupsgroupID ); + } + +} // namespace LinuxSampler
View file
linuxsampler-2718.tar.bz2/src/engines/common/InstrumentScriptVMFunctions.h
Added
@@ -0,0 +1,134 @@ +/* + * Copyright (c) 2014 Christian Schoenebeck + * + * http://www.linuxsampler.org + * + * This file is part of LinuxSampler and released under the same terms. + * See README file for details. + */ + +#ifndef LS_INSTRSCRIPTVMFUNCTIONS_H +#define LS_INSTRSCRIPTVMFUNCTIONS_H + +#include "../../common/global.h" +#include "../../scriptvm/CoreVMFunctions.h" + +namespace LinuxSampler { + + class EventGroup; + class InstrumentScriptVM; + + class InstrumentScriptVMFunction_play_note : public VMIntResultFunction { + public: + InstrumentScriptVMFunction_play_note(InstrumentScriptVM* parent); + int minRequiredArgs() const { return 1; } + int maxAllowedArgs() const { return 4; } + bool acceptsArgType(int iArg, ExprType_t type) const { return type == INT_EXPR;} + ExprType_t argType(int iArg) const { return INT_EXPR; } + VMFnResult* exec(VMFnArgs* args); + protected: + InstrumentScriptVM* m_vm; + }; + + class InstrumentScriptVMFunction_set_controller : public VMIntResultFunction { + public: + InstrumentScriptVMFunction_set_controller(InstrumentScriptVM* parent); + int minRequiredArgs() const { return 2; } + int maxAllowedArgs() const { return 2; } + bool acceptsArgType(int iArg, ExprType_t type) const { return type == INT_EXPR;} + ExprType_t argType(int iArg) const { return INT_EXPR; } + VMFnResult* exec(VMFnArgs* args); + protected: + InstrumentScriptVM* m_vm; + }; + + class InstrumentScriptVMFunction_ignore_event : public VMEmptyResultFunction { + public: + InstrumentScriptVMFunction_ignore_event(InstrumentScriptVM* parent); + int minRequiredArgs() const { return 1; } + int maxAllowedArgs() const { return 1; } + bool acceptsArgType(int iArg, ExprType_t type) const; + ExprType_t argType(int iArg) const { return INT_EXPR; } + VMFnResult* exec(VMFnArgs* args); + protected: + InstrumentScriptVM* m_vm; + }; + + class InstrumentScriptVMFunction_ignore_controller : public VMEmptyResultFunction { + public: + InstrumentScriptVMFunction_ignore_controller(InstrumentScriptVM* parent); + int minRequiredArgs() const { return 0; } + int maxAllowedArgs() const { return 1; } + bool acceptsArgType(int iArg, ExprType_t type) const { return type == INT_EXPR;} + ExprType_t argType(int iArg) const { return INT_EXPR; } + VMFnResult* exec(VMFnArgs* args); + protected: + InstrumentScriptVM* m_vm; + }; + + class InstrumentScriptVMFunction_note_off : public VMEmptyResultFunction { + public: + InstrumentScriptVMFunction_note_off(InstrumentScriptVM* parent); + int minRequiredArgs() const { return 1; } + int maxAllowedArgs() const { return 2; } + bool acceptsArgType(int iArg, ExprType_t type) const; + ExprType_t argType(int iArg) const { return INT_EXPR; } + VMFnResult* exec(VMFnArgs* args); + protected: + InstrumentScriptVM* m_vm; + }; + + class InstrumentScriptVMFunction_set_event_mark : public VMEmptyResultFunction { + public: + InstrumentScriptVMFunction_set_event_mark(InstrumentScriptVM* parent); + int minRequiredArgs() const { return 2; } + int maxAllowedArgs() const { return 2; } + bool acceptsArgType(int iArg, ExprType_t type) const { return type == INT_EXPR;} + ExprType_t argType(int iArg) const { return INT_EXPR; } + VMFnResult* exec(VMFnArgs* args); + protected: + InstrumentScriptVM* m_vm; + }; + + class InstrumentScriptVMFunction_delete_event_mark : public VMEmptyResultFunction { + public: + InstrumentScriptVMFunction_delete_event_mark(InstrumentScriptVM* parent); + int minRequiredArgs() const { return 2; } + int maxAllowedArgs() const { return 2; } + bool acceptsArgType(int iArg, ExprType_t type) const { return type == INT_EXPR;} + ExprType_t argType(int iArg) const { return INT_EXPR; } + VMFnResult* exec(VMFnArgs* args); + protected: + InstrumentScriptVM* m_vm; + }; + + class InstrumentScriptVMFunction_by_marks : public VMFunction { + public: + InstrumentScriptVMFunction_by_marks(InstrumentScriptVM* parent); + int minRequiredArgs() const { return 1; } + int maxAllowedArgs() const { return 1; } + bool acceptsArgType(int iArg, ExprType_t type) const { return type == INT_EXPR;} + ExprType_t argType(int iArg) const { return INT_EXPR; } + ExprType_t returnType() { return INT_ARR_EXPR; } + VMFnResult* exec(VMFnArgs* args); + protected: + InstrumentScriptVM* m_vm; + class Result : public VMFnResult, public VMIntArrayExpr { + public: + StmtFlags_t flags; + EventGroup* eventGroup; + + int arraySize() const OVERRIDE; + int evalIntElement(uint i) OVERRIDE; + void assignIntElement(uint i, int value) OVERRIDE {} // ignore assignment + VMExpr* resultValue() OVERRIDE { return this; } + StmtFlags_t resultFlags() { return flags; } + } m_result; + + VMFnResult* errorResult(); + VMFnResult* successResult(EventGroup* eventGroup); + }; + +} // namespace LinuxSampler + +#endif // LS_INSTRSCRIPTVMFUNCTIONS_H
View file
linuxsampler-2342.tar.bz2/src/engines/common/Makefile.am -> linuxsampler-2718.tar.bz2/src/engines/common/Makefile.am
Changed
@@ -1,4 +1,4 @@ -INCLUDES = $(all_includes) $(SNDFILE_CFLAGS) $(GIG_CFLAGS) +AM_CPPFLAGS = $(all_includes) $(SNDFILE_CFLAGS) $(GIG_CFLAGS) METASOURCES = AUTO AM_CXXFLAGS = -Wreturn-type -ffast-math $(CXX_CPU_SWITCH) @@ -19,6 +19,9 @@ LFOTriangleIntMath.h \ SineLFO.h PulseLFO.h SawLFO.h \ Resampler.h \ + AbstractInstrumentManager.h AbstractInstrumentManager.cpp \ + InstrumentScriptVM.h InstrumentScriptVM.cpp \ + InstrumentScriptVMFunctions.h InstrumentScriptVMFunctions.cpp \ EG.h EG.cpp liblinuxsamplercommonengine_la_LIBADD = $(SNDFILE_LIBS) liblinuxsamplercommonengine_la_LDFLAGS = $(SNDFILE_CFLAGS)
View file
linuxsampler-2342.tar.bz2/src/engines/common/MidiKeyboardManager.h -> linuxsampler-2718.tar.bz2/src/engines/common/MidiKeyboardManager.h
Changed
@@ -96,12 +96,21 @@ virtual void PreProcessSostenutoPedalDown() { } virtual void PostProcessSostenutoPedalDown() { } }; - + /** * This is the base class for class MidiKeyboardManager::MidiKey. It is * not intended to be instantiated directly. Instead it just defines * the part of class MidiKey which is not dependant on a C++ template * parameter. + * + * There are also ScriptEvent lists maintained for each key, which are not + * stored here though, but on the InstrumentScript structure. Simply because + * RTLists are tied to one Pool instance, and it would be error prone to + * maintain @c Pool<ScriptEvent> and @c RTList<ScriptEvent> separately, + * since one would need to be very careful to reallocate the lists when the + * script was changed or when the Engine instance changed, etc. + * + * @see InstrumentScript::pKeyEvents */ class MidiKeyBase { public: @@ -121,8 +130,21 @@ optional<float> ChorusSend; ///< Optional individual chorus send level for this MIDI key (usually not set, unless Roland GS NRPN 0x1Enn was received, nn reflecting the note number, see EngineBase::ProcessHardcodedControllers()) }; + class MidiKeyboardManagerBase { + public: + Pool<uint>* pActiveKeys; ///< Holds all keys in it's allocation list with active voices. + bool SoloMode; ///< in Solo Mode we only play one voice (group) at a time + int SoloKey; ///< Currently 'active' solo key, that is the key to which the currently sounding voice belongs to (only if SoloMode is enabled) + bool SustainPedal; ///< true if sustain pedal is down + bool SostenutoPedal; ///< true if sostenuto pedal is down + int SostenutoKeys128; + int SostenutoKeyCount; + uint32_t RoundRobinIndexes128; + int8_t KeyDown128; ///< True if the respective key is currently pressed down. Currently only used as built-in instrument script array variable %KEY_DOWN. It is currently not used by the sampler for any other purpose. + }; + template <class V> - class MidiKeyboardManager { + class MidiKeyboardManager : public MidiKeyboardManagerBase { public: /** @brief Voice Stealing Algorithms * @@ -204,16 +226,8 @@ }; MidiKey* pMIDIKeyInfo; ///< Contains all active voices sorted by MIDI key number and other informations to the respective MIDI key - Pool<uint>* pActiveKeys; ///< Holds all keys in it's allocation list with active voices. - bool SoloMode; ///< in Solo Mode we only play one voice (group) at a time - int SoloKey; ///< Currently 'active' solo key, that is the key to which the currently sounding voice belongs to (only if SoloMode is enabled) - bool SustainPedal; ///< true if sustain pedal is down - bool SostenutoPedal; ///< true if sostenuto pedal is down - int SostenutoKeys128; - int SostenutoKeyCount; - uint32_t RoundRobinIndexes128; - - MidiKeyboardManager() { + + MidiKeyboardManager(AbstractEngineChannel* pEngineChannel) { pMIDIKeyInfo = new MidiKey128; pActiveKeys = new Pool<uint>(128); SoloMode = false; @@ -221,12 +235,14 @@ SostenutoPedal = false; for (int i = 0 ; i < 128 ; i++) { RoundRobinIndexesi = 0; + KeyDowni = false; // by default use one counter for each key (the // gig engine will change this to one counter per // region) pMIDIKeyInfoi.pRoundRobinIndex = &RoundRobinIndexesi; } + m_engineChannel = pEngineChannel; } virtual ~MidiKeyboardManager() { @@ -239,7 +255,12 @@ SoloKey = -1; // no solo key active yet // reset key info - for (uint i = 0; i < 128; i++) pMIDIKeyInfoi.Reset(); + for (uint i = 0; i < 128; i++) { + pMIDIKeyInfoi.Reset(); + KeyDowni = false; + if (m_engineChannel->pScript) + m_engineChannel->pScript->pKeyEventsi->clear(); + } // free all active keys pActiveKeys->clear(); @@ -279,13 +300,13 @@ } } - void ClearAllActiveKeyEvents() { + /*void ClearAllActiveKeyEvents() { RTList<uint>::Iterator iuiKey = pActiveKeys->first(); RTList<uint>::Iterator end = pActiveKeys->end(); for(; iuiKey != end; ++iuiKey) { pMIDIKeyInfo*iuiKey.pEvents->clear(); // free all events on the key } - } + }*/ /** * Removes the given voice from the MIDI key's list of active voices. @@ -323,6 +344,8 @@ */ void FreeKey(MidiKey* pKey) { if (pKey->pActiveVoices->isEmpty()) { + if (m_engineChannel->pScript) + m_engineChannel->pScript->pKeyEventspKey->itSelf->clear(); pKey->Active = false; pActiveKeys->free(pKey->itSelf); // remove key from list of active keys pKey->itSelf = RTList<uint>::Iterator(); @@ -340,7 +363,7 @@ RTList<uint>::Iterator iuiKey = pActiveKeys->first(); RTList<uint>::Iterator end = pActiveKeys->end(); while (iuiKey != end) { // iterate through all active keys - MidiKey* pKey = &pMIDIKeyInfo*iuiKey; + MidiKey* pKey = &pMIDIKeyInfo*iuiKey; ++iuiKey; if (pKey->pActiveVoices->isEmpty()) FreeKey(pKey); #if CONFIG_DEVMODE @@ -543,7 +566,21 @@ } } } - + + /** + * Recalculate the pitch of all active voices. + */ + void OnScaleTuningChanged() { + RTList<uint>::Iterator iuiKey = pActiveKeys->first(); + for (; iuiKey; ++iuiKey) { + MidiKey* pKey = &pMIDIKeyInfo*iuiKey; + RTListVoiceIterator itVoice = pKey->pActiveVoices->first(); + for (; itVoice; ++itVoice) { + itVoice->onScaleTuningChanged(); + } + } + } + void ProcessSustainPedalDown(Pool<Event>::Iterator& itEvent) { // Cancel release process of all voices RTList<uint>::Iterator iuiKey = pActiveKeys->first(); @@ -624,6 +661,8 @@ void RemoveMidiKeyboardListener(MidiKeyboardListener* l) { listeners.RemoveListener(l); } protected: + AbstractEngineChannel* m_engineChannel; + class Listeners : public MidiKeyboardListener, public ListenerList<MidiKeyboardListener*> { public: REGISTER_FIRE_EVENT_METHOD_ARG2(PreProcessNoteOn, uint8_t, uint8_t)
View file
linuxsampler-2342.tar.bz2/src/engines/common/Resampler.h -> linuxsampler-2718.tar.bz2/src/engines/common/Resampler.h
Changed
@@ -3,7 +3,7 @@ * LinuxSampler - modular, streaming capable sampler * * * * Copyright (C) 2003, 2004 by Benno Senoner and Christian Schoenebeck * - * Copyright (C) 2005 - 2007 Christian Schoenebeck * + * Copyright (C) 2005 - 2012 Christian Schoenebeck * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -55,7 +55,7 @@ template<bool INTERPOLATE,bool BITDEPTH24> class Resampler { public: - inline static float GetNextSampleMonoCPP(sample_t* pSrc, double* Pos, float& Pitch) { + inline static float GetNextSampleMonoCPP(sample_t* __restrict pSrc, double* __restrict Pos, float& Pitch) { if (INTERPOLATE) return Interpolate1StepMonoCPP(pSrc, Pos, Pitch); else { // no pitch, so no interpolation necessary int pos_int = (int) *Pos; @@ -64,7 +64,7 @@ } } - inline static stereo_sample_t GetNextSampleStereoCPP(sample_t* pSrc, double* Pos, float& Pitch) { + inline static stereo_sample_t GetNextSampleStereoCPP(sample_t* __restrict pSrc, double* __restrict Pos, float& Pitch) { if (INTERPOLATE) return Interpolate1StepStereoCPP(pSrc, Pos, Pitch); else { // no pitch, so no interpolation necessary int pos_int = (int) *Pos; @@ -149,7 +149,7 @@ protected: - inline static int32_t getSample(sample_t* src, int pos) { + inline static int32_t getSample(sample_t* __restrict src, int pos) { if (BITDEPTH24) { pos *= 3; #if WORDS_BIGENDIAN @@ -165,7 +165,7 @@ } } - inline static float Interpolate1StepMonoCPP(sample_t* pSrc, double* Pos, float& Pitch) { + inline static float Interpolate1StepMonoCPP(sample_t* __restrict pSrc, double* __restrict Pos, float& Pitch) { int pos_int = (int) *Pos; // integer position float pos_fract = *Pos - pos_int; // fractional part of position @@ -188,7 +188,7 @@ return samplePoint; } - inline static stereo_sample_t Interpolate1StepStereoCPP(sample_t* pSrc, double* Pos, float& Pitch) { + inline static stereo_sample_t Interpolate1StepStereoCPP(sample_t* __restrict pSrc, double* __restrict Pos, float& Pitch) { int pos_int = (int) *Pos; // integer position float pos_fract = *Pos - pos_int; // fractional part of position pos_int <<= 1;
View file
linuxsampler-2342.tar.bz2/src/engines/common/SampleFile.cpp -> linuxsampler-2718.tar.bz2/src/engines/common/SampleFile.cpp
Changed
@@ -3,7 +3,7 @@ * LinuxSampler - modular, streaming capable sampler * * * * Copyright (C) 2003 - 2009 Christian Schoenebeck * - * Copyright (C) 2009 - 2011 Grigor Iliev * + * Copyright (C) 2009 - 2014 Grigor Iliev * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -27,6 +27,8 @@ #include <cstring> +#define CONVERT_BUFFER_SIZE 4096 + namespace LinuxSampler { #if CONFIG_DEVMODE int SampleFile_OpenFilesCount = 0; @@ -35,6 +37,7 @@ SampleFile::SampleFile(String File, bool DontClose) { this->File = File; this->pSndFile = NULL; + pConvertBuffer = NULL; SF_INFO sfInfo; sfInfo.format = 0; @@ -59,6 +62,8 @@ break; case SF_FORMAT_PCM_24: case SF_FORMAT_DWVW_24: + case SF_FORMAT_PCM_32: + case SF_FORMAT_FLOAT: FrameSize = 3 * ChannelCount; break; default: @@ -82,11 +87,21 @@ #endif } if(!DontClose) Close(); + + if (FrameSize == 3 * ChannelCount && ( +#if HAVE_DECL_SF_FORMAT_FLAC + (Format & SF_FORMAT_TYPEMASK) == SF_FORMAT_FLAC || +#endif + (Format & SF_FORMAT_SUBMASK) == SF_FORMAT_FLOAT || + (Format & SF_FORMAT_SUBMASK) == SF_FORMAT_PCM_32)) { + pConvertBuffer = new intCONVERT_BUFFER_SIZE; + } } SampleFile::~SampleFile() { Close(); ReleaseSampleData(); + delete pConvertBuffer; } void SampleFile::Open() { @@ -170,20 +185,54 @@ if (GetPos() + FrameCount > GetTotalFrameCount()) FrameCount = GetTotalFrameCount() - GetPos(); // For the cases where a different sample end is specified (not the end of the file) - // ogg files must be read with sf_readf, not sf_read_raw. On - // big endian machines, sf_readf_short is also used for 16 bit - // wav files, to get automatic endian conversion (for 24 bit - // samples this is handled in Synthesize::GetSample instead). + // ogg and flac files must be read with sf_readf, not + // sf_read_raw. On big endian machines, sf_readf_short is also + // used for 16 bit wav files, to get automatic endian + // conversion (for 24 bit samples this is handled in + // Synthesize::GetSample instead). -#if WORDS_BIGENDIAN || HAVE_DECL_SF_FORMAT_VORBIS +#if WORDS_BIGENDIAN || HAVE_DECL_SF_FORMAT_VORBIS || HAVE_DECL_SF_FORMAT_FLAC if ( #if WORDS_BIGENDIAN FrameSize == 2 * ChannelCount #else - (Format & SF_FORMAT_SUBMASK) == SF_FORMAT_VORBIS +#if HAVE_DECL_SF_FORMAT_VORBIS + ((Format & SF_FORMAT_SUBMASK) == SF_FORMAT_VORBIS) +#if HAVE_DECL_SF_FORMAT_FLAC + || +#endif +#endif +#if HAVE_DECL_SF_FORMAT_FLAC + (FrameSize == 2 * ChannelCount && + (Format & SF_FORMAT_TYPEMASK) == SF_FORMAT_FLAC) +#endif #endif ) { return sf_readf_short(pSndFile, static_cast<short*>(pBuffer), FrameCount); + } else if (FrameSize == 3 * ChannelCount && ( +#if HAVE_DECL_SF_FORMAT_FLAC + (Format & SF_FORMAT_TYPEMASK) == SF_FORMAT_FLAC || +#endif + (Format & SF_FORMAT_SUBMASK) == SF_FORMAT_FLOAT || + (Format & SF_FORMAT_SUBMASK) == SF_FORMAT_PCM_32)) { + // 24 bit flac needs to be converted from the 32 bit + // integers returned by libsndfile. Float and 32 bit pcm + // are treated in the same way. + int j = 0; + sf_count_t count = FrameCount; + const sf_count_t bufsize = CONVERT_BUFFER_SIZE / ChannelCount; + unsigned char* const dst = static_cast<unsigned char*>(pBuffer); + while (count > 0) { + int n = sf_readf_int(pSndFile, pConvertBuffer, std::min(count, bufsize)); + if (n <= 0) break; + for (int i = 0 ; i < n * ChannelCount ; i++) { + dstj++ = pConvertBufferi >> 8; + dstj++ = pConvertBufferi >> 16; + dstj++ = pConvertBufferi >> 24; + } + count -= n; + } + return FrameCount - count; } else #endif {
View file
linuxsampler-2342.tar.bz2/src/engines/common/SampleFile.h -> linuxsampler-2718.tar.bz2/src/engines/common/SampleFile.h
Changed
@@ -3,7 +3,7 @@ * LinuxSampler - modular, streaming capable sampler * * * * Copyright (C) 2003 - 2009 Christian Schoenebeck * - * Copyright (C) 2009 - 2011 Grigor Iliev * + * Copyright (C) 2009 - 2013 Grigor Iliev * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -80,6 +80,9 @@ SNDFILE* pSndFile; buffer_t RAMCache; ///< Buffers samples (already uncompressed) in RAM. + + int* pConvertBuffer; + long SetPos(unsigned long FrameCount, int Whence); };
View file
linuxsampler-2342.tar.bz2/src/engines/common/SignalUnit.h -> linuxsampler-2718.tar.bz2/src/engines/common/SignalUnit.h
Changed
@@ -2,7 +2,7 @@ * * * LinuxSampler - modular, streaming capable sampler * * * - * Copyright (C) 2011 Grigor Iliev * + * Copyright (C) 2011 - 2012 Grigor Iliev * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -316,7 +316,7 @@ } /** Should return value in the range 0, 127 (L <-> R) */ - virtual uint8_t CaluclatePan(uint8_t pan) { + virtual uint8_t CalculatePan(int pan) { int p = pan + GetPan() * 0.63; if (p < 0) return 0; if (p > 127) return 127;
View file
linuxsampler-2342.tar.bz2/src/engines/common/VoiceBase.h -> linuxsampler-2718.tar.bz2/src/engines/common/VoiceBase.h
Changed
@@ -138,7 +138,7 @@ // check if the disk thread created our ordered disk stream in the meantime DiskStreamRef.pStream = pDiskThread->AskForCreatedStream(DiskStreamRef.OrderID); if (!DiskStreamRef.pStream) { - std::cout << stderr << "Disk stream not available in time!" << std::endl << std::flush; + std::cerr << "Disk stream not available in time!\n" << std::flush; KillImmediately(); return; }
View file
linuxsampler-2342.tar.bz2/src/engines/gig/EGADSR.cpp -> linuxsampler-2718.tar.bz2/src/engines/gig/EGADSR.cpp
Changed
@@ -3,7 +3,7 @@ * LinuxSampler - modular, streaming capable sampler * * * * Copyright (C) 2003, 2004 by Benno Senoner and Christian Schoenebeck * - * Copyright (C) 2005 - 2010 Christian Schoenebeck * + * Copyright (C) 2005 - 2013 Christian Schoenebeck * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -172,11 +172,11 @@ Stage = stage_attack; Segment = segment_lin; - if (AttackTime >= 0.0005f) { + if (AttackTime >= 1e-8) { // Measurements of GSt output shows that the real attack time // is about 65.5% of the value specified in the gig file. - // The minimum attack value used is 0.032. - StepsLeft = int(0.655f * RTMath::Max(AttackTime, 0.032f) * SampleRate); + // The minimum attack value used is 0.0316. + StepsLeft = int(0.655f * RTMath::Max(AttackTime, 0.0316f) * SampleRate); Level = (float) PreAttack / 1000.0; Coeff = 0.896f * (1.0f - Level) / StepsLeft; // max level is a bit lower if attack != 0 } else { // attack is zero - immediately jump to the next stage
View file
linuxsampler-2342.tar.bz2/src/engines/gig/Engine.cpp -> linuxsampler-2718.tar.bz2/src/engines/gig/Engine.cpp
Changed
@@ -24,10 +24,17 @@ #include "Engine.h" #include "EngineChannel.h" +#include "InstrumentScriptVM.h" namespace LinuxSampler { namespace gig { Engine::Format Engine::GetEngineFormat() { return GIG; } + void Engine::CreateInstrumentScriptVM() { + dmsg(2,("gig::Engine created Giga format scriptvm\n")); + if (pScriptVM) return; + pScriptVM = new InstrumentScriptVM; // gig format specific extended script runner + } + /** * Reacts on supported control change commands (e.g. pitch bend wheel, * modulation wheel, aftertouch). @@ -109,6 +116,14 @@ ProcessFxSendControllers(pChannel, itControlChangeEvent); } + void Engine::ProcessChannelPressure(LinuxSampler::EngineChannel* pEngineChannel, Pool<Event>::Iterator& itChannelPressureEvent) { + // if required: engine global aftertouch handling (apart from the per voice handling) + } + + void Engine::ProcessPolyphonicKeyPressure(LinuxSampler::EngineChannel* pEngineChannel, Pool<Event>::Iterator& itNotePressureEvent) { + // if required: engine global aftertouch handling (apart from the per voice handling) + } + DiskThread* Engine::CreateDiskThread() { return new DiskThread ( iMaxDiskStreams, @@ -210,6 +225,9 @@ case ::gig::dimension_random: DimValuesi = uint(Random() * pRegion->pDimensionDefinitionsi.zones); break; + case ::gig::dimension_smartmidi: + DimValuesi = 0; + break; case ::gig::dimension_modwheel: DimValuesi = pChannel->ControllerTable1; break; @@ -292,7 +310,18 @@ // change has occured between note on and off) if (ReleaseTriggerVoice && !(VoiceType & Voice::type_release_trigger)) return Pool<Voice>::Iterator(); - ::gig::DimensionRegion* pDimRgn = pRegion->GetDimensionRegionByValue(DimValues); + + ::gig::DimensionRegion* pDimRgn; + if (!itNoteOnEvent->Format.Gig.DimMask) { // normal case ... + pDimRgn = pRegion->GetDimensionRegionByValue(DimValues); + } else { // some dimension zones were overridden (i.e. by instrument script) ... + dmsg(3,("trigger with dim mask=%d val=%d\n", itNoteOnEvent->Format.Gig.DimMask, itNoteOnEvent->Format.Gig.DimBits)); + int index = pRegion->GetDimensionRegionIndexByValue(DimValues); + index &= ~itNoteOnEvent->Format.Gig.DimMask; + index |= itNoteOnEvent->Format.Gig.DimBits & itNoteOnEvent->Format.Gig.DimMask; + pDimRgn = pRegion->pDimensionRegionsindex & 255; + } + if (!pDimRgn) return Pool<Voice>::Iterator(); // error (could not resolve dimension region) // no need to continue if sample is silent if (!pDimRgn->pSample || !pDimRgn->pSample->SamplesTotal) return Pool<Voice>::Iterator(); @@ -318,7 +347,7 @@ } String Engine::Version() { - String s = "$Revision: 1.110 $"; + String s = "$Revision: 2611 $"; return s.substr(11, s.size() - 13); // cut dollar signs, spaces and CVS macro keyword }
View file
linuxsampler-2342.tar.bz2/src/engines/gig/Engine.h -> linuxsampler-2718.tar.bz2/src/engines/gig/Engine.h
Changed
@@ -46,8 +46,10 @@ virtual void ProcessControlChange ( LinuxSampler::EngineChannel* pEngineChannel, Pool<Event>::Iterator& itControlChangeEvent - ); - + ) OVERRIDE; + virtual void ProcessChannelPressure(LinuxSampler::EngineChannel* pEngineChannel, Pool<Event>::Iterator& itChannelPressureEvent) OVERRIDE; + virtual void ProcessPolyphonicKeyPressure(LinuxSampler::EngineChannel* pEngineChannel, Pool<Event>::Iterator& itNotePressureEvent) OVERRIDE; + virtual void CreateInstrumentScriptVM() OVERRIDE; friend class Voice; protected:
View file
linuxsampler-2342.tar.bz2/src/engines/gig/EngineChannel.cpp -> linuxsampler-2718.tar.bz2/src/engines/gig/EngineChannel.cpp
Changed
@@ -89,6 +89,10 @@ // keep the dimension regions and samples that are in use pInstrumentManager->HandBackInstrument(cmd.pInstrument, this, cmd.pRegionsInUse); } + if (cmd.pScript) { + // give old instrument script back to instrument resource manager + cmd.pScript->resetAll(); + } cmd.pRegionsInUse->clear(); // delete all key groups @@ -105,6 +109,15 @@ if (!newInstrument) { throw InstrumentManagerException("resource was not created"); } + + if (newInstrument->ScriptSlotCount() > 1) { + std::cerr << "WARNING: Executing more than one real-time instrument script slot is not implemented yet!\n"; + } + ::gig::Script* script = newInstrument->GetScriptOfSlot(0); + if (script) { + String sourceCode = script->GetScriptAsText(); + LoadInstrumentScript(sourceCode); + } } catch (RIFF::Exception e) { InstrumentStat = -2; @@ -144,7 +157,14 @@ InstrumentIdxName = newInstrument->pInfo->Name; InstrumentStat = 100; - ChangeInstrument(newInstrument); + { + InstrumentChangeCmd< ::gig::DimensionRegion, ::gig::Instrument>& cmd = + ChangeInstrument(newInstrument); + if (cmd.pScript) { + // give old instrument script back to instrument resource manager + cmd.pScript->resetAll(); + } + } StatusChanged(true); }
View file
linuxsampler-2342.tar.bz2/src/engines/gig/EngineChannel.h -> linuxsampler-2718.tar.bz2/src/engines/gig/EngineChannel.h
Changed
@@ -2,9 +2,9 @@ * * * LinuxSampler - modular, streaming capable sampler * * * - * Copyright (C) 2003,2004 by Benno Senoner and Christian Schoenebeck * - * Copyright (C) 2005-2012 Christian Schoenebeck * - * Copyright (C) 2009 Grigor Iliev * + * Copyright (C) 2003, 2004 by Benno Senoner and Christian Schoenebeck * + * Copyright (C) 2005 - 2008 Christian Schoenebeck * + * Copyright (C) 2009 - 2013 Christian Schoenebeck and Grigor Iliev * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -32,6 +32,8 @@ #include <gig.h> namespace LinuxSampler { namespace gig { + class Voice; + class EngineChannel: public LinuxSampler::EngineChannelBase<Voice, ::gig::DimensionRegion, ::gig::Instrument> { public: virtual void SendProgramChange(uint8_t Program);
View file
linuxsampler-2342.tar.bz2/src/engines/gig/InstrumentResourceManager.cpp -> linuxsampler-2718.tar.bz2/src/engines/gig/InstrumentResourceManager.cpp
Changed
@@ -3,7 +3,7 @@ * LinuxSampler - modular, streaming capable sampler * * * * Copyright (C) 2003, 2004 by Benno Senoner and Christian Schoenebeck * - * Copyright (C) 2005 - 2012 Christian Schoenebeck * + * Copyright (C) 2005 - 2013 Christian Schoenebeck * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -208,7 +208,7 @@ } } - InstrumentEditor* InstrumentResourceManager::LaunchInstrumentEditor(instrument_id_t ID, void* pUserData) throw (InstrumentManagerException) { + InstrumentEditor* InstrumentResourceManager::LaunchInstrumentEditor(LinuxSampler::EngineChannel* pEngineChannel, instrument_id_t ID, void* pUserData) throw (InstrumentManagerException) { const String sDataType = GetInstrumentDataStructureName(ID); const String sDataVersion = GetInstrumentDataStructureVersion(ID); // find instrument editors capable to handle given instrument @@ -235,7 +235,7 @@ InstrumentEditorProxies.add(pProxy); InstrumentEditorProxiesMutex.Unlock(); // launch the instrument editor for the given instrument - pEditor->Launch(pInstrument, sDataType, sDataVersion, pUserData); + pEditor->Launch(pEngineChannel, pInstrument, sDataType, sDataVersion, pUserData); // register the instrument editor as virtual MIDI device as well ... VirtualMidiDevice* pVirtualMidiDevice = @@ -271,19 +271,20 @@ int iProxyIndex = -1; // first find the editor proxy entry for this editor - InstrumentEditorProxiesMutex.Lock(); - for (int i = 0; i < InstrumentEditorProxies.size(); i++) { - InstrumentEditorProxy* pCurProxy = - dynamic_cast<InstrumentEditorProxy*>( - InstrumentEditorProxiesi - ); - if (pCurProxy->pEditor == pSender) { - pProxy = pCurProxy; - iProxyIndex = i; - pInstrument = pCurProxy->pInstrument; + { + LockGuard lock(InstrumentEditorProxiesMutex); + for (int i = 0; i < InstrumentEditorProxies.size(); i++) { + InstrumentEditorProxy* pCurProxy = + dynamic_cast<InstrumentEditorProxy*>( + InstrumentEditorProxiesi + ); + if (pCurProxy->pEditor == pSender) { + pProxy = pCurProxy; + iProxyIndex = i; + pInstrument = pCurProxy->pInstrument; + } } } - InstrumentEditorProxiesMutex.Unlock(); if (!pProxy) { std::cerr << "Eeeek, could not find instrument editor proxy, " @@ -311,9 +312,10 @@ // finally delete proxy entry and hand back instrument if (pInstrument) { - InstrumentEditorProxiesMutex.Lock(); - InstrumentEditorProxies.remove(iProxyIndex); - InstrumentEditorProxiesMutex.Unlock(); + { + LockGuard lock(InstrumentEditorProxiesMutex); + InstrumentEditorProxies.remove(iProxyIndex); + } HandBack(pInstrument, pProxy); delete pProxy;
View file
linuxsampler-2342.tar.bz2/src/engines/gig/InstrumentResourceManager.h -> linuxsampler-2718.tar.bz2/src/engines/gig/InstrumentResourceManager.h
Changed
@@ -46,6 +46,7 @@ #include "../../plugins/InstrumentEditor.h" namespace LinuxSampler { namespace gig { + class Engine; class EngineChannel; @@ -70,7 +71,7 @@ virtual String GetInstrumentName(instrument_id_t ID); virtual String GetInstrumentDataStructureName(instrument_id_t ID); virtual String GetInstrumentDataStructureVersion(instrument_id_t ID); - virtual InstrumentEditor* LaunchInstrumentEditor(instrument_id_t ID, void* pUserData = NULL) throw (InstrumentManagerException); + virtual InstrumentEditor* LaunchInstrumentEditor(LinuxSampler::EngineChannel* pEngineChannel, instrument_id_t ID, void* pUserData = NULL) throw (InstrumentManagerException) OVERRIDE; virtual std::vector<instrument_id_t> GetInstrumentFileContent(String File) throw (InstrumentManagerException); virtual instrument_info_t GetInstrumentInfo(instrument_id_t ID) throw (InstrumentManagerException);
View file
linuxsampler-2718.tar.bz2/src/engines/gig/InstrumentScriptVM.cpp
Added
@@ -0,0 +1,72 @@ +/* + * Copyright (c) 2014 Christian Schoenebeck + * + * http://www.linuxsampler.org + * + * This file is part of LinuxSampler and released under the same terms. + * See README file for details. + */ + +#include "InstrumentScriptVM.h" +#include "../../common/global_private.h" +#include "EngineChannel.h" + +namespace LinuxSampler { namespace gig { + + InstrumentScriptVM::InstrumentScriptVM() : + LinuxSampler::InstrumentScriptVM(), m_fnGigSetDimZone(this) + { + } + + std::map<String,int> InstrumentScriptVM::builtInConstIntVariables() { + // first get buil-in integer variables of derived VM class + std::map<String,int> m = + ::LinuxSampler::InstrumentScriptVM::builtInConstIntVariables(); + + // add own built-in script constants + m"$GIG_DIM_CHANNEL" = ::gig::dimension_samplechannel; + m"$GIG_DIM_LAYER" = ::gig::dimension_layer; + m"$GIG_DIM_VELOCITY" = ::gig::dimension_velocity; + m"$GIG_DIM_AFTERTOUCH" = ::gig::dimension_channelaftertouch; + m"$GIG_DIM_RELEASE" = ::gig::dimension_releasetrigger; + m"$GIG_DIM_KEYBOARD" = ::gig::dimension_keyboard; + m"$GIG_DIM_ROUNDROBIN" = ::gig::dimension_roundrobin; + m"$GIG_DIM_RANDOM" = ::gig::dimension_random; + m"$GIG_DIM_SMARTMIDI" = ::gig::dimension_smartmidi; + m"$GIG_DIM_ROUNDROBINKEY" = ::gig::dimension_roundrobinkeyboard; + m"$GIG_DIM_MODWHEEL" = ::gig::dimension_modwheel; + m"$GIG_DIM_BREATH" = ::gig::dimension_breath; + m"$GIG_DIM_FOOT" = ::gig::dimension_foot; + m"$GIG_DIM_PORTAMENTOTIME" = ::gig::dimension_portamentotime; + m"$GIG_DIM_EFFECT1" = ::gig::dimension_effect1; + m"$GIG_DIM_EFFECT2" = ::gig::dimension_effect2; + m"$GIG_DIM_GENPURPOSE1" = ::gig::dimension_genpurpose1; + m"$GIG_DIM_GENPURPOSE2" = ::gig::dimension_genpurpose2; + m"$GIG_DIM_GENPURPOSE3" = ::gig::dimension_genpurpose3; + m"$GIG_DIM_GENPURPOSE4" = ::gig::dimension_genpurpose4; + m"$GIG_DIM_SUSTAIN" = ::gig::dimension_sustainpedal; + m"$GIG_DIM_PORTAMENTO" = ::gig::dimension_portamento; + m"$GIG_DIM_SOSTENUTO" = ::gig::dimension_sostenutopedal; + m"$GIG_DIM_SOFT" = ::gig::dimension_softpedal; + m"$GIG_DIM_GENPURPOSE5" = ::gig::dimension_genpurpose5; + m"$GIG_DIM_GENPURPOSE6" = ::gig::dimension_genpurpose6; + m"$GIG_DIM_GENPURPOSE7" = ::gig::dimension_genpurpose7; + m"$GIG_DIM_GENPURPOSE8" = ::gig::dimension_genpurpose8; + m"$GIG_DIM_EFFECT1DEPTH" = ::gig::dimension_effect1depth; + m"$GIG_DIM_EFFECT2DEPTH" = ::gig::dimension_effect2depth; + m"$GIG_DIM_EFFECT3DEPTH" = ::gig::dimension_effect3depth; + m"$GIG_DIM_EFFECT4DEPTH" = ::gig::dimension_effect4depth; + m"$GIG_DIM_EFFECT5DEPTH" = ::gig::dimension_effect5depth; + + return m; + } + + VMFunction* InstrumentScriptVM::functionByName(const String& name) { + // built-in script functions of this class + if (name == "gig_set_dim_zone") return &m_fnGigSetDimZone; + + // built-in script functions of derived VM class + return ::LinuxSampler::InstrumentScriptVM::functionByName(name); + } + +}} // namespace LinuxSampler::gig
View file
linuxsampler-2718.tar.bz2/src/engines/gig/InstrumentScriptVM.h
Added
@@ -0,0 +1,41 @@ +/* + * Copyright (c) 2014 Christian Schoenebeck + * + * http://www.linuxsampler.org + * + * This file is part of LinuxSampler and released under the same terms. + * See README file for details. + */ + +#ifndef LS_GIG_INSTRUMENT_SCRIPT_VM_H +#define LS_GIG_INSTRUMENT_SCRIPT_VM_H + +#include "../common/InstrumentScriptVM.h" +#include "InstrumentScriptVMFunctions.h" + +namespace LinuxSampler { namespace gig { + + /** @brief Real-time instrument script virtual machine (Giga format). + * + * Extends the common sampler format independent InstrumentScriptVM with + * Giga format specific built-in script variables and functions. + * + * Note that this class is currently re-entrant safe, but @b not thread + * safe! See also comments of base class ScriptVM regarding this issue. + */ + class InstrumentScriptVM : public LinuxSampler::InstrumentScriptVM { + public: + InstrumentScriptVM(); + VMFunction* functionByName(const String& name) OVERRIDE; + //std::map<String,VMIntRelPtr*> builtInIntVariables() OVERRIDE; + //std::map<String,VMInt8Array*> builtInIntArrayVariables() OVERRIDE; + std::map<String,int> builtInConstIntVariables() OVERRIDE; + protected: + InstrumentScriptVMFunction_gig_set_dim_zone m_fnGigSetDimZone; + + friend class InstrumentScriptVMFunction_gig_set_dim_zone; + }; + +}} // namespace LinuxSampler::gig + +#endif // LS_GIG_INSTRUMENT_SCRIPT_VM_H
View file
linuxsampler-2718.tar.bz2/src/engines/gig/InstrumentScriptVMFunctions.cpp
Added
@@ -0,0 +1,116 @@ +/* + * Copyright (c) 2014 Christian Schoenebeck + * + * http://www.linuxsampler.org + * + * This file is part of LinuxSampler and released under the same terms. + * See README file for details. + */ + +#include "InstrumentScriptVMFunctions.h" +#include "InstrumentScriptVM.h" +#include "EngineChannel.h" + +namespace LinuxSampler { namespace gig { + + ///////////////////////////////////////////////////////////////////////// + // Function: + // gig_set_dim_zone(event_id, dimension, zone) + + InstrumentScriptVMFunction_gig_set_dim_zone::InstrumentScriptVMFunction_gig_set_dim_zone(InstrumentScriptVM* parent) + : m_vm(parent) + { + } + + bool InstrumentScriptVMFunction_gig_set_dim_zone::acceptsArgType(int iArg, ExprType_t type) const { + return type == INT_EXPR || type == INT_ARR_EXPR; + } + + VMFnResult* InstrumentScriptVMFunction_gig_set_dim_zone::exec(VMFnArgs* args) { + EngineChannel* pEngineChannel = + static_cast<EngineChannel*>(m_vm->m_event->cause.pEngineChannel); + + int dim = args->arg(1)->asInt()->evalInt(); + int zone = args->arg(2)->asInt()->evalInt(); + + if (args->arg(0)->exprType() == INT_EXPR) { + int id = args->arg(0)->asInt()->evalInt(); + if (id < 0) { + wrnMsg("gig_set_dim_zone(): argument 1 may not be a negative event ID"); + return successResult(); + } + + RTList<Event>::Iterator itEvent = pEngineChannel->pEngine->EventByID(id); + if (!itEvent) { + dmsg(2,("gig_set_dim_zone(): no active event with ID %d\n", id)); + return successResult(); + } + + int note = itEvent->Param.Note.Key; + + ::gig::Region* pRegion = pEngineChannel->pInstrument->GetRegion(note); + if (!pRegion) { + dmsg(2,("gig_set_dim_zone(): no region for key %d\n", note)); + return successResult(); + } + + int idx = -1, baseBits = 0; + for (int i = 0; i < pRegion->Dimensions; ++i) { + if (pRegion->pDimensionDefinitionsi.dimension == dim) { + idx = i; + break; + } + baseBits += pRegion->pDimensionDefinitionsi.bits; + } + if (idx < 0) { + dmsg(2,("gig_set_dim_zone(): no such gig dimension %d\n", dim)); + return successResult(); // no such dimension found + } + + int bits = pRegion->pDimensionDefinitionsidx.bits; + int mask = 0; + for (int i = 0; i < bits; ++i) mask |= (1 << (baseBits + i)); + + itEvent->Format.Gig.DimMask |= mask; + itEvent->Format.Gig.DimBits |= (zone << baseBits) & mask; + + dmsg(3,("gig_set_dim_zone(): success, mask=%d bits=%d\n", itEvent->Format.Gig.DimMask, itEvent->Format.Gig.DimBits)); + } else if (args->arg(0)->exprType() == INT_ARR_EXPR) { + VMIntArrayExpr* ids = args->arg(0)->asIntArray(); + for (int i = 0; i < ids->arraySize(); ++i) { + int id = ids->evalIntElement(i); + if (id < 0) continue; + + RTList<Event>::Iterator itEvent = pEngineChannel->pEngine->EventByID(id); + if (!itEvent) continue; + + int note = itEvent->Param.Note.Key; + + ::gig::Region* pRegion = pEngineChannel->pInstrument->GetRegion(note); + if (!pRegion) continue; + + int idx = -1, baseBits = 0; + for (int i = 0; i < pRegion->Dimensions; ++i) { + if (pRegion->pDimensionDefinitionsi.dimension == dim) { + idx = i; + break; + } + baseBits += pRegion->pDimensionDefinitionsi.bits; + } + if (idx < 0) continue; + + int bits = pRegion->pDimensionDefinitionsidx.bits; + int mask = 0; + for (int i = 0; i < bits; ++i) mask |= (1 << (baseBits + i)); + + itEvent->Format.Gig.DimMask |= mask; + itEvent->Format.Gig.DimBits |= (zone << baseBits) & mask; + + dmsg(3,("gig_set_dim_zone(): success, mask=%d bits=%d\n", itEvent->Format.Gig.DimMask, itEvent->Format.Gig.DimBits)); + } + } + + return successResult(); + } + +}} // namespace LinuxSampler::gig
View file
linuxsampler-2718.tar.bz2/src/engines/gig/InstrumentScriptVMFunctions.h
Added
@@ -0,0 +1,38 @@ +/* + * Copyright (c) 2014 Christian Schoenebeck + * + * http://www.linuxsampler.org + * + * This file is part of LinuxSampler and released under the same terms. + * See README file for details. + */ + +#ifndef LS_GIG_INSTRSCRIPTVMFUNCTIONS_H +#define LS_GIG_INSTRSCRIPTVMFUNCTIONS_H + +#include "../common/InstrumentScriptVMFunctions.h" + +namespace LinuxSampler { namespace gig { + + class InstrumentScriptVM; + + /** + * Built-in script function: + * + * gig_set_dim_zone(event_id, dimension, zone) + */ + class InstrumentScriptVMFunction_gig_set_dim_zone : public VMEmptyResultFunction { + public: + InstrumentScriptVMFunction_gig_set_dim_zone(InstrumentScriptVM* parent); + int minRequiredArgs() const { return 3; } + int maxAllowedArgs() const { return 3; } + bool acceptsArgType(int iArg, ExprType_t type) const; + ExprType_t argType(int iArg) const { return INT_EXPR; } + VMFnResult* exec(VMFnArgs* args); + protected: + InstrumentScriptVM* m_vm; + }; + +}} // namespace LinuxSampler::gig + +#endif // LS_GIG_INSTRSCRIPTVMFUNCTIONS_H
View file
linuxsampler-2342.tar.bz2/src/engines/gig/Makefile.am -> linuxsampler-2718.tar.bz2/src/engines/gig/Makefile.am
Changed
@@ -1,4 +1,4 @@ -INCLUDES = $(all_includes) $(GIG_CFLAGS) +AM_CPPFLAGS = $(all_includes) $(GIG_CFLAGS) METASOURCES = AUTO AM_CXXFLAGS = -Wreturn-type -ffast-math @@ -11,6 +11,8 @@ EGDecay.cpp EGDecay.h \ Engine.cpp Engine.h \ Filter.h Filter.cpp \ + InstrumentScriptVM.h InstrumentScriptVM.cpp \ + InstrumentScriptVMFunctions.h InstrumentScriptVMFunctions.cpp \ InstrumentResourceManager.cpp InstrumentResourceManager.h \ Stream.cpp Stream.h \ Voice.cpp Voice.h \
View file
linuxsampler-2342.tar.bz2/src/engines/gig/Synthesizer.cpp -> linuxsampler-2718.tar.bz2/src/engines/gig/Synthesizer.cpp
Changed
@@ -3,7 +3,7 @@ * LinuxSampler - modular, streaming capable sampler * * * * Copyright (C) 2003, 2004 by Benno Senoner and Christian Schoenebeck * - * Copyright (C) 2005 Christian Schoenebeck * + * Copyright (C) 2005 - 2012 Christian Schoenebeck * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -197,7 +197,7 @@ case 0x1e: return (void*) SynthesizeFragment_mode1e; case 0x1f: return (void*) SynthesizeFragment_mode1f; default: { - printf("gig::Synthesizer: Invalid Synthesis Mode: %d\n", SynthesisMode); + std::cerr << "gig::Synthesizer: Invalid Synthesis Mode: " << SynthesisMode << std::endl << std::flush; exit(-1); } }
View file
linuxsampler-2342.tar.bz2/src/engines/gig/Voice.cpp -> linuxsampler-2718.tar.bz2/src/engines/gig/Voice.cpp
Changed
@@ -4,7 +4,7 @@ * * * Copyright (C) 2003, 2004 by Benno Senoner and Christian Schoenebeck * * Copyright (C) 2005 - 2008 Christian Schoenebeck * - * Copyright (C) 2009 - 2012 Christian Schoenebeck and Grigor Iliev * + * Copyright (C) 2009 - 2013 Christian Schoenebeck and Grigor Iliev * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -124,6 +124,18 @@ } } + void Voice::ProcessChannelPressureEvent(RTList<Event>::Iterator& itEvent) { + if (itEvent->Type == Event::type_channel_pressure) { // if (valid) MIDI channel pressure (aftertouch) event + if (pRegion->AttenuationController.type == ::gig::attenuation_ctrl_t::type_channelaftertouch) { + CrossfadeSmoother.update(AbstractEngine::CrossfadeCurveCrossfadeAttenuation(itEvent->Param.ChannelPressure.Value)); + } + } + } + + void Voice::ProcessPolyphonicKeyPressureEvent(RTList<Event>::Iterator& itEvent) { + // Not used so far + } + void Voice::ProcessCutoffEvent(RTList<Event>::Iterator& itEvent) { int ccvalue = itEvent->Param.CC.Value; if (VCFCutoffCtrl.value == ccvalue) return; @@ -181,9 +193,15 @@ Voice::EGInfo Voice::CalculateEG1ControllerInfluence(double eg1ControllerValue) { EGInfo eg; // (eg1attack is different from the others) - eg.Attack = (pRegion->EG1ControllerAttackInfluence) ? - 1 + 0.031 * (double) (pRegion->EG1ControllerAttackInfluence == 1 ? - 1 : 1 << pRegion->EG1ControllerAttackInfluence) * eg1ControllerValue : 1.0; + if (pRegion->EG1Attack < 1e-8 && // attack in gig == 0 + (pRegion->EG1ControllerAttackInfluence == 0 || + eg1ControllerValue <= 10)) { // strange GSt special case + eg.Attack = 0; // this will force the attack to be 0 in the call to EG1.trigger + } else { + eg.Attack = (pRegion->EG1ControllerAttackInfluence) ? + 1 + 0.031 * (double) (pRegion->EG1ControllerAttackInfluence == 1 ? + 1 : 1 << pRegion->EG1ControllerAttackInfluence) * eg1ControllerValue : 1.0; + } eg.Decay = (pRegion->EG1ControllerDecayInfluence) ? 1 + 0.00775 * (double) (1 << pRegion->EG1ControllerDecayInfluence) * eg1ControllerValue : 1.0; eg.Release = (pRegion->EG1ControllerReleaseInfluence) ? 1 + 0.00775 * (double) (1 << pRegion->EG1ControllerReleaseInfluence) * eg1ControllerValue : 1.0; @@ -444,13 +462,13 @@ void Voice::TriggerEG1(const EGInfo& egInfo, double velrelease, double velocityAttenuation, uint sampleRate, uint8_t velocity) { EG1.trigger(pRegion->EG1PreAttack, - pRegion->EG1Attack * egInfo.Attack, + RTMath::Max(pRegion->EG1Attack, 0.0316) * egInfo.Attack, pRegion->EG1Hold, pRegion->EG1Decay1 * egInfo.Decay * velrelease, pRegion->EG1Decay2 * egInfo.Decay * velrelease, pRegion->EG1InfiniteSustain, pRegion->EG1Sustain, - pRegion->EG1Release * egInfo.Release * velrelease, + RTMath::Max(pRegion->EG1Release * velrelease, 0.014) * egInfo.Release, velocityAttenuation, sampleRate / CONFIG_DEFAULT_SUBFRAGMENT_SIZE); } @@ -474,8 +492,14 @@ // TODO: The SustainPedal condition could be wrong, maybe the // check should be if this Voice is in release stage or is a // release sample instead. Need to test this in GSt. - if (itEvent->Param.Note.Key != MIDIKey || - !GetGigEngineChannel()->SustainPedal) { + // -- Andreas + // + // Commented sustain pedal check out. I don't think voices of the same + // note should be stopped at all, because it doesn't sound naturally + // with a drumkit. + // -- Christian, 2013-01-08 + if (itEvent->Param.Note.Key != MIDIKey /*|| + !GetGigEngineChannel()->SustainPedal*/) { dmsg(4,("Voice %x - kill", this)); // kill the voice fast @@ -487,4 +511,16 @@ EG1.CalculateFadeOutCoeff(FadeOutTime, SampleRate); } + int Voice::CalculatePan(uint8_t pan) { + int p; + // Gst behaviour: -64 and 63 are special cases + if (RgnInfo.Pan == -64) p = pan * 2 - 127; + else if (RgnInfo.Pan == 63) p = pan * 2; + else p = pan + RgnInfo.Pan; + + if (p < 0) return 0; + if (p > 127) return 127; + return p; + } + }} // namespace LinuxSampler::gig
View file
linuxsampler-2342.tar.bz2/src/engines/gig/Voice.h -> linuxsampler-2718.tar.bz2/src/engines/gig/Voice.h
Changed
@@ -4,7 +4,7 @@ * * * Copyright (C) 2003, 2004 by Benno Senoner and Christian Schoenebeck * * Copyright (C) 2005 - 2008 Christian Schoenebeck * - * Copyright (C) 2009 - 2012 Christian Schoenebeck and Grigor Iliev * + * Copyright (C) 2009 - 2013 Christian Schoenebeck and Grigor Iliev * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -40,6 +40,7 @@ #include "../common/VoiceBase.h" #include "SynthesisParam.h" #include "SmoothVolume.h" +#include "EngineChannel.h" namespace LinuxSampler { namespace gig { class Engine; @@ -76,12 +77,15 @@ virtual float CalculateFinalCutoff(float cutoffBase); virtual uint8_t GetVCFCutoffCtrl(); virtual uint8_t GetVCFResonanceCtrl(); - virtual void ProcessCCEvent(RTList<Event>::Iterator& itEvent); + virtual void ProcessCCEvent(RTList<Event>::Iterator& itEvent) OVERRIDE; + virtual void ProcessChannelPressureEvent(RTList<Event>::Iterator& itEvent) OVERRIDE; + virtual void ProcessPolyphonicKeyPressureEvent(RTList<Event>::Iterator& itEvent) OVERRIDE; virtual void ProcessCutoffEvent(RTList<Event>::Iterator& itEvent); virtual double GetVelocityAttenuation(uint8_t MIDIKeyVelocity); virtual double GetVelocityRelease(uint8_t MIDIKeyVelocity); virtual double GetSampleAttenuation(); virtual void ProcessGroupEvent(RTList<Event>::Iterator& itEvent); + virtual int CalculatePan(uint8_t pan); private: EGADSR EG1;
View file
linuxsampler-2342.tar.bz2/src/engines/sf2/Engine.cpp -> linuxsampler-2718.tar.bz2/src/engines/sf2/Engine.cpp
Changed
@@ -58,6 +58,14 @@ ProcessFxSendControllers(pChannel, itControlChangeEvent); } + void Engine::ProcessChannelPressure(LinuxSampler::EngineChannel* pEngineChannel, Pool<Event>::Iterator& itChannelPressureEvent) { + // if required: engine global aftertouch handling (apart from the per voice handling) + } + + void Engine::ProcessPolyphonicKeyPressure(LinuxSampler::EngineChannel* pEngineChannel, Pool<Event>::Iterator& itNotePressureEvent) { + // if required: engine global aftertouch handling (apart from the per voice handling) + } + DiskThread* Engine::CreateDiskThread() { return new DiskThread ( iMaxDiskStreams, @@ -157,7 +165,7 @@ } String Engine::Version() { - String s = "$Revision: 1.4 $"; + String s = "$Revision: 2559 $"; return s.substr(11, s.size() - 13); // cut dollar signs, spaces and CVS macro keyword }
View file
linuxsampler-2342.tar.bz2/src/engines/sf2/Engine.h -> linuxsampler-2718.tar.bz2/src/engines/sf2/Engine.h
Changed
@@ -46,7 +46,9 @@ virtual void ProcessControlChange ( LinuxSampler::EngineChannel* pEngineChannel, Pool<Event>::Iterator& itControlChangeEvent - ); + ) OVERRIDE; + virtual void ProcessChannelPressure(LinuxSampler::EngineChannel* pEngineChannel, Pool<Event>::Iterator& itChannelPressureEvent) OVERRIDE; + virtual void ProcessPolyphonicKeyPressure(LinuxSampler::EngineChannel* pEngineChannel, Pool<Event>::Iterator& itNotePressureEvent) OVERRIDE; friend class Voice;
View file
linuxsampler-2342.tar.bz2/src/engines/sf2/EngineChannel.h -> linuxsampler-2718.tar.bz2/src/engines/sf2/EngineChannel.h
Changed
@@ -2,9 +2,9 @@ * * * LinuxSampler - modular, streaming capable sampler * * * - * Copyright (C) 2003,2004 by Benno Senoner and Christian Schoenebeck * - * Copyright (C) 2005-2009 Christian Schoenebeck * - * Copyright (C) 2009 Grigor Iliev * + * Copyright (C) 2003, 2004 by Benno Senoner and Christian Schoenebeck * + * Copyright (C) 2005 - 2008 Christian Schoenebeck * + * Copyright (C) 2009 - 2013 Christian Schoenebeck and Grigor Iliev * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -32,6 +32,8 @@ #include <SF.h> namespace LinuxSampler { namespace sf2 { + class Voice; + class EngineChannel: public LinuxSampler::EngineChannelBase<Voice, ::sf2::Region, ::sf2::Preset> { public: virtual void SendProgramChange(uint8_t Program);
View file
linuxsampler-2342.tar.bz2/src/engines/sf2/Makefile.am -> linuxsampler-2718.tar.bz2/src/engines/sf2/Makefile.am
Changed
@@ -1,5 +1,5 @@ if HAVE_SF2 -INCLUDES = $(all_includes) $(GIG_CFLAGS) +AM_CPPFLAGS = $(all_includes) $(GIG_CFLAGS) METASOURCES = AUTO AM_CXXFLAGS = -Wreturn-type -ffast-math
View file
linuxsampler-2342.tar.bz2/src/engines/sf2/SF2SignalUnitRack.h -> linuxsampler-2718.tar.bz2/src/engines/sf2/SF2SignalUnitRack.h
Changed
@@ -103,7 +103,7 @@ virtual float GetPitch(); virtual float GetResonance(); virtual float GetPan() { return 0; } - virtual uint8_t CaluclatePan(uint8_t pan) { return pan; } + virtual uint8_t CalculatePan(uint8_t pan) { return pan; } }; class SF2SignalUnitRack : public SignalUnitRack {
View file
linuxsampler-2342.tar.bz2/src/engines/sf2/Voice.cpp -> linuxsampler-2718.tar.bz2/src/engines/sf2/Voice.cpp
Changed
@@ -127,6 +127,14 @@ } }*/ // TODO: ^^^ } + + void Voice::ProcessChannelPressureEvent(RTList<Event>::Iterator& itEvent) { + //TODO: ... + } + + void Voice::ProcessPolyphonicKeyPressureEvent(RTList<Event>::Iterator& itEvent) { + //TODO: ... + } void Voice::ProcessCutoffEvent(RTList<Event>::Iterator& itEvent) { /*int ccvalue = itEvent->Param.CC.Value; @@ -344,4 +352,12 @@ SignalRack.CalculateFadeOutCoeff(FadeOutTime, SampleRate); } + int Voice::CalculatePan(uint8_t pan) { + int p = pan + RgnInfo.Pan; + + if (p < 0) return 0; + if (p > 127) return 127; + return p; + } + }} // namespace LinuxSampler::sf2
View file
linuxsampler-2342.tar.bz2/src/engines/sf2/Voice.h -> linuxsampler-2718.tar.bz2/src/engines/sf2/Voice.h
Changed
@@ -4,7 +4,7 @@ * * * Copyright (C) 2003, 2004 by Benno Senoner and Christian Schoenebeck * * Copyright (C) 2005 - 2008 Christian Schoenebeck * - * Copyright (C) 2009 - 2012 Christian Schoenebeck and Grigor Iliev * + * Copyright (C) 2009 - 2013 Christian Schoenebeck and Grigor Iliev * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -38,6 +38,7 @@ #include "../gig/SynthesisParam.h" #include "../sfz/EGADSR.h" #include "SF2SignalUnitRack.h" +#include "EngineChannel.h" namespace LinuxSampler { namespace sf2 { class Engine; @@ -74,13 +75,16 @@ virtual float CalculateFinalCutoff(float cutoffBase); virtual uint8_t GetVCFCutoffCtrl(); virtual uint8_t GetVCFResonanceCtrl(); - virtual void ProcessCCEvent(RTList<Event>::Iterator& itEvent); + virtual void ProcessCCEvent(RTList<Event>::Iterator& itEvent) OVERRIDE; + virtual void ProcessChannelPressureEvent(RTList<Event>::Iterator& itEvent) OVERRIDE; + virtual void ProcessPolyphonicKeyPressureEvent(RTList<Event>::Iterator& itEvent) OVERRIDE; virtual void ProcessCutoffEvent(RTList<Event>::Iterator& itEvent); virtual double GetVelocityAttenuation(uint8_t MIDIKeyVelocity); virtual double GetVelocityRelease(uint8_t MIDIKeyVelocity); virtual double GetSampleAttenuation(); virtual void ProcessGroupEvent(RTList<Event>::Iterator& itEvent); virtual void AboutToTrigger(); + virtual int CalculatePan(uint8_t pan); private: ::sf2::Region* pPresetRegion;
View file
linuxsampler-2342.tar.bz2/src/engines/sfz/Engine.cpp -> linuxsampler-2718.tar.bz2/src/engines/sfz/Engine.cpp
Changed
@@ -119,6 +119,14 @@ } } + void Engine::ProcessChannelPressure(LinuxSampler::EngineChannel* pEngineChannel, Pool<Event>::Iterator& itChannelPressureEvent) { + // if required: engine global aftertouch handling (apart from the per voice handling) + } + + void Engine::ProcessPolyphonicKeyPressure(LinuxSampler::EngineChannel* pEngineChannel, Pool<Event>::Iterator& itNotePressureEvent) { + // if required: engine global aftertouch handling (apart from the per voice handling) + } + DiskThread* Engine::CreateDiskThread() { return new DiskThread ( iMaxDiskStreams, @@ -255,7 +263,7 @@ } String Engine::Version() { - String s = "$Revision: 1.11 $"; + String s = "$Revision: 2559 $"; return s.substr(11, s.size() - 13); // cut dollar signs, spaces and CVS macro keyword }
View file
linuxsampler-2342.tar.bz2/src/engines/sfz/Engine.h -> linuxsampler-2718.tar.bz2/src/engines/sfz/Engine.h
Changed
@@ -29,6 +29,7 @@ #include "../EngineBase.h" #include "Voice.h" #include "sfz.h" +#include "EngineChannel.h" // prevents compile errors with clang 2.x namespace LinuxSampler { namespace sfz { const int MaxCCPerVoice = 128; // FIXME: too much? @@ -47,7 +48,9 @@ virtual void ProcessControlChange ( LinuxSampler::EngineChannel* pEngineChannel, Pool<Event>::Iterator& itControlChangeEvent - ); + ) OVERRIDE; + virtual void ProcessChannelPressure(LinuxSampler::EngineChannel* pEngineChannel, Pool<Event>::Iterator& itChannelPressureEvent) OVERRIDE; + virtual void ProcessPolyphonicKeyPressure(LinuxSampler::EngineChannel* pEngineChannel, Pool<Event>::Iterator& itNotePressureEvent) OVERRIDE; virtual void PostSetMaxVoices(int iVoices);
View file
linuxsampler-2342.tar.bz2/src/engines/sfz/LookupTable.cpp -> linuxsampler-2718.tar.bz2/src/engines/sfz/LookupTable.cpp
Changed
@@ -2,7 +2,7 @@ * * * LinuxSampler - modular, streaming capable sampler * * * - * Copyright (C) 2010 Andreas Persson * + * Copyright (C) 2010 - 2012 Andreas Persson * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -279,7 +279,9 @@ if (nbDimensions) { int c = lennbDimensions - 1; for (dim = nbDimensions - 2 ; dim >= 0 ; dim--) { - for (int i = 0 ; i < 128 ; i++) mapArrdimi *= c; + const DimDef& dimDef = dimDefsdimsdim; + int max = dimDef.max == -1 ? 127 : dimDef.max; + for (int i = dimDef.min ; i <= max ; i++) mapArrdimi *= c; c *= lendim; } }
View file
linuxsampler-2342.tar.bz2/src/engines/sfz/Makefile.am -> linuxsampler-2718.tar.bz2/src/engines/sfz/Makefile.am
Changed
@@ -1,4 +1,4 @@ -INCLUDES = $(all_includes) $(GIG_CFLAGS) +AM_CPPFLAGS = $(all_includes) $(SNDFILE_CFLAGS) $(GIG_CFLAGS) METASOURCES = AUTO AM_CXXFLAGS = -Wreturn-type -ffast-math
View file
linuxsampler-2342.tar.bz2/src/engines/sfz/SfzSignalUnitRack.cpp -> linuxsampler-2718.tar.bz2/src/engines/sfz/SfzSignalUnitRack.cpp
Changed
@@ -771,9 +771,6 @@ pan += lfo->GetLevel() * (lfo->pLfoInfo->pan + f); } - if(pan < -100) return -100; - if(pan > 100) return 100; - return pan; }
View file
linuxsampler-2342.tar.bz2/src/engines/sfz/Voice.cpp -> linuxsampler-2718.tar.bz2/src/engines/sfz/Voice.cpp
Changed
@@ -4,7 +4,7 @@ * * * Copyright (C) 2003, 2004 by Benno Senoner and Christian Schoenebeck * * Copyright (C) 2005 - 2008 Christian Schoenebeck * - * Copyright (C) 2009 - 2012 Christian Schoenebeck and Grigor Iliev * + * Copyright (C) 2009 - 2013 Christian Schoenebeck and Grigor Iliev * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -134,7 +134,7 @@ } double Voice::GetSampleAttenuation() { - return exp(LN_10_DIV_20 * pRegion->volume); + return exp(LN_10_DIV_20 * pRegion->volume) * pRegion->amplitude / 100; } double Voice::GetVelocityAttenuation(uint8_t MIDIKeyVelocity) { @@ -156,6 +156,14 @@ }*/ // TODO: ^^^ } + void Voice::ProcessChannelPressureEvent(RTList<Event>::Iterator& itEvent) { + //TODO: ... + } + + void Voice::ProcessPolyphonicKeyPressureEvent(RTList<Event>::Iterator& itEvent) { + //TODO: ... + } + double Voice::CalculateCrossfadeVolume(uint8_t MIDIKeyVelocity) { /*float crossfadeVolume; switch (pRegion->AttenuationController.type) { @@ -302,4 +310,10 @@ SignalRack.CalculateFadeOutCoeff(FadeOutTime, SampleRate); } + int Voice::CalculatePan(uint8_t pan) { + // the value isn't limited to 0, 127 here, as this is done + // later in SignalUnit.CalculatePan + return pan + RgnInfo.Pan; + } + }} // namespace LinuxSampler::sfz
View file
linuxsampler-2342.tar.bz2/src/engines/sfz/Voice.h -> linuxsampler-2718.tar.bz2/src/engines/sfz/Voice.h
Changed
@@ -76,7 +76,9 @@ virtual float CalculateFinalCutoff(float cutoffBase); virtual uint8_t GetVCFCutoffCtrl() { return 0; } virtual uint8_t GetVCFResonanceCtrl() { return 0; } - virtual void ProcessCCEvent(RTList<Event>::Iterator& itEvent); + virtual void ProcessCCEvent(RTList<Event>::Iterator& itEvent) OVERRIDE; + virtual void ProcessChannelPressureEvent(RTList<Event>::Iterator& itEvent) OVERRIDE; + virtual void ProcessPolyphonicKeyPressureEvent(RTList<Event>::Iterator& itEvent) OVERRIDE; virtual void ProcessCutoffEvent(RTList<Event>::Iterator& itEvent) { } virtual double GetVelocityAttenuation(uint8_t MIDIKeyVelocity); virtual double GetVelocityRelease(uint8_t MIDIKeyVelocity); @@ -84,6 +86,7 @@ virtual void ProcessGroupEvent(RTList<Event>::Iterator& itEvent); virtual void SetSampleStartOffset(); virtual int GetRAMCacheOffset() { return pRegion->pSample->RAMCacheOffset; } + virtual int CalculatePan(uint8_t pan); private: SfzSignalUnitRack SignalRack;
View file
linuxsampler-2342.tar.bz2/src/engines/sfz/sfz.cpp -> linuxsampler-2718.tar.bz2/src/engines/sfz/sfz.cpp
Changed
@@ -3,7 +3,7 @@ * LinuxSampler - modular, streaming capable sampler * * * * Copyright (C) 2008 Anders Dahnielson <anders@dahnielson.com> * - * Copyright (C) 2009 - 2012 Anders Dahnielson and Grigor Iliev * + * Copyright (C) 2009 - 2013 Anders Dahnielson and Grigor Iliev * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -322,6 +322,7 @@ volume_curvecc.clear(); volume_smoothcc.clear(); volume_stepcc.clear(); + amplitude = 100; pan = 0; pan_oncc.clear(); pan_curvecc.clear(); @@ -638,6 +639,7 @@ region->volume_curvecc = volume_curvecc; region->volume_smoothcc = volume_smoothcc; region->volume_stepcc = volume_stepcc; + region->amplitude = amplitude; region->pan = pan; region->pan_oncc = pan_oncc; region->pan_curvecc = pan_curvecc; @@ -872,96 +874,8 @@ _instrument = new Instrument(LinuxSampler::Path::getBaseName(file), pSampleManager); _current_group = new Group(); pCurDef = _current_group; - enum token_type_t { HEADER, OPCODE }; - token_type_t token_type; - std::string token_string; - std::ifstream fs(file.c_str()); - currentDir = LinuxSampler::Path::stripLastName(file); - std::string token; - std::string line; - currentLine = 0; - - while (std::getline(fs, line)) - { - currentLine++; - // COMMENT - std::string::size_type slash_index = line.find("//"); - if (slash_index != std::string::npos) - line.resize(slash_index); - - // DEFINITION - std::stringstream linestream(line); - linestream >> std::noskipws; - int spaces = 0; - while (linestream >> token) - { - if (token0 == '<' && tokentoken.size()-1 == '>') - { - // HEAD - if (!token_string.empty()) - { - switch (token_type) - { - case HEADER: - push_header(token_string); - break; - case OPCODE: - push_opcode(token_string); - break; - } - token_string.erase(); - } - token_string.append(token); - token_type = HEADER; - } - else if (token.find('=') != std::string::npos) - { - // HEAD - if (!token_string.empty()) - { - switch (token_type) - { - case HEADER: - push_header(token_string); - break; - case OPCODE: - push_opcode(token_string); - break; - } - token_string.erase(); - } - token_string.append(token); - token_type = OPCODE; - } - else - { - // TAIL - token_string.append(spaces, ' '); - token_string.append(token); - } - spaces = 0; - while (isspace(linestream.peek())) { - linestream.ignore(); - spaces++; - } - } - - // EOL - if (!token_string.empty()) - { - switch (token_type) - { - case HEADER: - push_header(token_string); - break; - case OPCODE: - push_opcode(token_string); - break; - } - token_string.erase(); - } - } + parseFile(file,pSampleManager); std::set<float*> velcurves; for (int i = 0; i < _instrument->regions.size(); i++) { @@ -1145,6 +1059,120 @@ } } + void File::parseFile(std::string file, SampleManager* pSampleManager){ + enum token_type_t { HEADER, OPCODE }; + token_type_t token_type; + std::string token_string; + + std::ifstream fs(file.c_str()); + currentDir = LinuxSampler::Path::stripLastName(file); + std::string token; + std::string line; + currentLine = 0; + + while (std::getline(fs, line)) + { + currentLine++; + // COMMENT + std::string::size_type slash_index = line.find("//"); + if (slash_index != std::string::npos) + line.resize(slash_index); + + // #include + if (line.find("#include ") == 0) { + size_t fname_start = line.find("\""); + if (fname_start == std::string::npos) continue; + + size_t fname_end = line.find("\"", fname_start + 1); + if (fname_end == std::string::npos || fname_start == fname_end) + continue; + std::string fname = line.substr(fname_start + 1, fname_end - fname_start - 1); + + if (!currentDir.empty() && !LinuxSampler::Path(fname).isAbsolute()) + fname = currentDir + LinuxSampler::File::DirSeparator + fname; + + std::string cd = currentDir; // backup current dir + int cl = currentLine; + parseFile(fname, pSampleManager); + currentDir = cd; // restore currentDir (since altered by parsefile()) + currentLine = cl; + continue; + } + + // DEFINITION + std::stringstream linestream(line); + int spaces = 0; + while (linestream >> token) + { + linestream >> std::noskipws; + if (token0 == '<' && tokentoken.size()-1 == '>') + { + // HEAD + if (!token_string.empty()) + { + switch (token_type) + { + case HEADER: + push_header(token_string); + break; + case OPCODE: + push_opcode(token_string); + break; + } + token_string.erase(); + } + token_string.append(token); + token_type = HEADER; + } + else if (token.find('=') != std::string::npos) + { + // HEAD + if (!token_string.empty()) + { + switch (token_type) + { + case HEADER: + push_header(token_string); + break; + case OPCODE: + push_opcode(token_string); + break; + } + token_string.erase(); + } + token_string.append(token); + token_type = OPCODE; + } + else + { + // TAIL + token_string.append(spaces, ' '); + token_string.append(token); + } + spaces = 0; + while (isspace(linestream.peek())) { + linestream.ignore(); + spaces++; + } + } + + // EOL + if (!token_string.empty()) + { + switch (token_type) + { + case HEADER: + push_header(token_string); + break; + case OPCODE: + push_opcode(token_string); + break; + } + token_string.erase(); + } + } + } + File::~File() { delete _current_group; @@ -1395,6 +1423,7 @@ // amplifier else if ("volume" == key) pCurDef->volume = ToFloat(value); + else if ("amplitude" == key) pCurDef->amplitude = ToFloat(value); else if ("pan" == key) pCurDef->pan = ToFloat(value); else if ("width" == key) pCurDef->width = ToFloat(value); else if ("position" == key) pCurDef->position = ToFloat(value); @@ -1813,7 +1842,7 @@ else if ("volume_curve" == key_cc) pCurDef->volume_curvecc.add( CC(num_cc, 0, check(key, 0, 30000, ToInt(value))) ); else if ("volume_smooth" == key_cc) pCurDef->volume_smoothcc.add( CC(num_cc, 0, -1, check(key, 0.0f, 100000.0f /* max? */, ToFloat(value))) ); else if ("volume_step" == key_cc) pCurDef->volume_stepcc.add( CC(num_cc, 0, -1, 0, check(key, -20.0f, 20.0f, ToFloat(value))) ); - else if ("pan" == key_cc) pCurDef->pan_oncc.add( CC(num_cc, check(key, -100.0f, 100.0f, ToFloat(value))) ); + else if ("pan" == key_cc) pCurDef->pan_oncc.add( CC(num_cc, check(key, -200.0f, 200.0f, ToFloat(value))) ); else if ("pan_curve" == key_cc) pCurDef->pan_curvecc.add( CC(num_cc, 0, check(key, 0, 30000, ToInt(value))) ); else if ("pan_smooth" == key_cc) pCurDef->pan_smoothcc.add( CC(num_cc, 0, -1, check(key, 0.0f, 100000.0f /* max? */, ToFloat(value))) ); else if ("pan_step" == key_cc) pCurDef->pan_stepcc.add( CC(num_cc, 0, -1, 0, check(key, -100.0f, 100.0f, ToFloat(value))) );
View file
linuxsampler-2342.tar.bz2/src/engines/sfz/sfz.h -> linuxsampler-2718.tar.bz2/src/engines/sfz/sfz.h
Changed
@@ -3,7 +3,7 @@ * LinuxSampler - modular, streaming capable sampler * * * * Copyright (C) 2008 Anders Dahnielson <anders@dahnielson.com> * - * Copyright (C) 2009 - 2012 Anders Dahnielson and Grigor Iliev * + * Copyright (C) 2009 - 2013 Anders Dahnielson and Grigor Iliev * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -516,6 +516,7 @@ // amplifier float volume; + float amplitude; float pan; float width; float position; @@ -760,6 +761,7 @@ /// Load an existing SFZ file File(std::string file, SampleManager* pSampleManager = NULL); + void parseFile(std::string file, SampleManager* pSampleManager); virtual ~File(); /// Returns a pointer to the instrument object
View file
linuxsampler-2342.tar.bz2/src/hostplugins/au/Makefile.am -> linuxsampler-2718.tar.bz2/src/hostplugins/au/Makefile.am
Changed
@@ -4,7 +4,7 @@ examples_dir = $(DEVELOPER_EXTRAS_DIR) au_public_dir = $(examples_dir)/CoreAudio/AudioUnits/AUPublic ca_public_dir = $(examples_dir)/CoreAudio/PublicUtility -INCLUDES = -I$(au_public_dir)/AUBase -I$(au_public_dir)/OtherBases \ +LinuxSamplerAU_CPPFLAGS = -I$(au_public_dir)/AUBase -I$(au_public_dir)/OtherBases \ -I$(au_public_dir)/Utility -I$(ca_public_dir) $(GIG_CFLAGS) $(SQLITE3_CFLAGS) $(SNDFILE_CFLAGS) noinst_PROGRAMS = LinuxSamplerAU @@ -12,48 +12,7 @@ # Uncomment for debug messages. #debug_messages = -DAU_DEBUG_DISPATCHER=1 -nodist_LinuxSamplerAU_SOURCES = $(ca_public_dir)/CAHostTimeBase.cpp \ - $(ca_public_dir)/CAHostTimeBase.h \ - $(ca_public_dir)/CAAUMIDIMap.cpp \ - $(ca_public_dir)/CAAUMIDIMap.h \ - $(ca_public_dir)/CAAUMIDIMapManager.cpp \ - $(ca_public_dir)/CAAUMIDIMapManager.h \ - $(ca_public_dir)/CAAUParameter.cpp \ - $(ca_public_dir)/CAAUParameter.h \ - $(ca_public_dir)/CAAudioChannelLayout.cpp \ - $(ca_public_dir)/CAAudioChannelLayout.h \ - $(ca_public_dir)/CAMutex.cpp \ - $(ca_public_dir)/CAMutex.h \ - $(ca_public_dir)/CAStreamBasicDescription.cpp \ - $(ca_public_dir)/CAStreamBasicDescription.h \ - $(ca_public_dir)/CAVectorUnitTypes.h \ - $(ca_public_dir)/CAVectorUnit.cpp \ - $(ca_public_dir)/CAVectorUnit.h \ - $(au_public_dir)/Utility/AUBuffer.cpp \ - $(au_public_dir)/Utility/AUBuffer.h \ - $(au_public_dir)/Utility/AUDebugDispatcher.cpp \ - $(au_public_dir)/Utility/AUDebugDispatcher.h \ - $(au_public_dir)/Utility/AUInputFormatConverter.h \ - $(au_public_dir)/Utility/AUSilentTimeout.h \ - $(au_public_dir)/Utility/AUTimestampGenerator.h \ - $(au_public_dir)/AUBase/AUBase.cpp \ - $(au_public_dir)/AUBase/AUBase.h \ - $(au_public_dir)/AUBase/AUDispatch.cpp \ - $(au_public_dir)/AUBase/AUDispatch.h \ - $(au_public_dir)/AUBase/AUInputElement.cpp \ - $(au_public_dir)/AUBase/AUInputElement.h \ - $(au_public_dir)/AUBase/AUOutputElement.cpp \ - $(au_public_dir)/AUBase/AUOutputElement.h \ - $(au_public_dir)/AUBase/AUScopeElement.cpp \ - $(au_public_dir)/AUBase/AUScopeElement.h \ - $(au_public_dir)/AUBase/ComponentBase.cpp \ - $(au_public_dir)/AUBase/ComponentBase.h \ - $(au_public_dir)/OtherBases/AUMIDIBase.cpp \ - $(au_public_dir)/OtherBases/AUMIDIBase.h \ - $(au_public_dir)/OtherBases/MusicDeviceBase.cpp \ - $(au_public_dir)/OtherBases/MusicDeviceBase.h - -LinuxSamplerAU_SOURCES = PluginAU.cpp PluginAU.h PluginAUVersion.h +LinuxSamplerAU_SOURCES = PluginAU.cpp PluginAU.h PluginAUVersion.h ausdk.cpp LinuxSamplerAU_LDADD = $(top_builddir)/src/liblinuxsampler.la @@ -62,7 +21,7 @@ AUFLAGS = $(GIG_LIBS) $(JACK_LIBS) $(SQLITE3_LIBS) $(SNDFILE_LIBS) endif -LinuxSamplerAU_CPPFLAGS = $(debug_messages) +LinuxSamplerAU_CPPFLAGS += $(debug_messages) LinuxSamplerAU_LDFLAGS = -bundle $(AUFLAGS) \ -exported_symbols_list $(srcdir)/PluginAU.exp \ -framework CoreServices -framework CoreMIDI -framework CoreAudio \
View file
linuxsampler-2718.tar.bz2/src/hostplugins/au/ausdk.cpp
Added
@@ -0,0 +1,18 @@ +#include "CAHostTimeBase.cpp" +#include "CAAUMIDIMap.cpp" +#include "CAAUMIDIMapManager.cpp" +#include "CAAUParameter.cpp" +#include "CAAudioChannelLayout.cpp" +#include "CAMutex.cpp" +#include "CAStreamBasicDescription.cpp" +#include "CAVectorUnit.cpp" +#include "AUBuffer.cpp" +#include "AUDebugDispatcher.cpp" +#include "AUBase.cpp" +#include "AUDispatch.cpp" +#include "AUInputElement.cpp" +#include "AUOutputElement.cpp" +#include "AUScopeElement.cpp" +#include "ComponentBase.cpp" +#include "AUMIDIBase.cpp" +#include "MusicDeviceBase.cpp"
View file
linuxsampler-2342.tar.bz2/src/hostplugins/dssi/Makefile.am -> linuxsampler-2718.tar.bz2/src/hostplugins/dssi/Makefile.am
Changed
@@ -1,5 +1,5 @@ if HAVE_DSSI -INCLUDES = $(GIG_CFLAGS) $(SQLITE3_CFLAGS) +AM_CPPFLAGS = $(GIG_CFLAGS) $(SQLITE3_CFLAGS) plugindir = $(libdir)/dssi plugin_LTLIBRARIES = linuxsampler.la linuxsampler_la_SOURCES = PluginDssi.cpp PluginDssi.h
View file
linuxsampler-2342.tar.bz2/src/hostplugins/dssi/PluginDssi.cpp -> linuxsampler-2718.tar.bz2/src/hostplugins/dssi/PluginDssi.cpp
Changed
@@ -1,6 +1,6 @@ /*************************************************************************** * * - * Copyright (C) 2008 - 2011 Andreas Persson * + * Copyright (C) 2008 - 2013 Andreas Persson * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -27,6 +27,7 @@ #include "PluginDssi.h" #include "../../engines/AbstractEngineChannel.h" +#include "../../engines/EngineChannelFactory.h" namespace { @@ -47,8 +48,6 @@ Out0 = 0; Out1 = 0; - uint outputChannel = 0; - uint midiPort = 0; if (!plugin) { plugin = new PluginDssi(SampleRate); } @@ -57,31 +56,52 @@ pChannel = plugin->global->pSampler->AddSamplerChannel(); pChannel->SetEngineType("gig"); pChannel->SetAudioOutputDevice(plugin->pAudioDevice); - pPort = plugin->pMidiDevice->CreateMidiPort(); - pPort->Connect(pChannel->GetEngineChannel(), LinuxSampler::midi_chan_all); + if (plugin->RefCount > 1) { + plugin->pMidiDevice->AddMidiPort(); + plugin->pAudioDevice->AddChannels(2); + } + + int i = plugin->RefCount - 1; + + pChannel->SetMidiInput(plugin->pMidiDevice, i, LinuxSampler::midi_chan_all); LinuxSampler::AbstractEngineChannel* engineChannel = static_cast<LinuxSampler::AbstractEngineChannel*>(pChannel->GetEngineChannel()); - // TODO: pChannelLeft and pChannelRight are meant to be - // protected - engineChannel->pChannelLeft = new LinuxSampler::AudioChannel(0, 0, 0); - engineChannel->pChannelRight = new LinuxSampler::AudioChannel(1, 0, 0); + engineChannel->SetOutputChannel(0, i * 2); + engineChannel->SetOutputChannel(1, i * 2 + 1); + + pPort = plugin->pMidiDevice->GetPort(i); + pChannelLeft = plugin->pAudioDevice->Channel(i * 2); + pChannelRight = plugin->pAudioDevice->Channel(i * 2 + 1); } PluginInstance::~PluginInstance() { - LinuxSampler::AbstractEngineChannel* engineChannel = - static_cast<LinuxSampler::AbstractEngineChannel*>(pChannel->GetEngineChannel()); - delete engineChannel->pChannelLeft; - delete engineChannel->pChannelRight; - if (--plugin->RefCount == 0) { delete plugin; plugin = 0; } else { + LinuxSampler::AbstractEngineChannel* engineChannel = + static_cast<LinuxSampler::AbstractEngineChannel*>(pChannel->GetEngineChannel()); + int oldChannelNumber = engineChannel->OutputChannel(0); + plugin->global->pSampler->RemoveSamplerChannel(pChannel); + plugin->pMidiDevice->RemoveMidiPort(pPort); + plugin->pAudioDevice->RemoveChannel(pChannelLeft); + plugin->pAudioDevice->RemoveChannel(pChannelRight); + + const std::set<LinuxSampler::EngineChannel*>& engineChannels = + LinuxSampler::EngineChannelFactory::EngineChannelInstances(); + for (std::set<LinuxSampler::EngineChannel*>::iterator i = engineChannels.begin(); + i != engineChannels.end() ; ++i) { + if ((*i)->GetAudioOutputDevice() == plugin->pAudioDevice) { + int channelNumber = (*i)->OutputChannel(0); + if (channelNumber > oldChannelNumber) { + (*i)->SetOutputChannel(0, channelNumber - 2); + (*i)->SetOutputChannel(1, channelNumber - 1); + } + } + } } - - LinuxSampler::MidiInputDevicePlugin::DeleteMidiPort(pPort); } void PluginInstance::ConnectPort(unsigned long Port, LADSPA_Data* DataLocation) { @@ -135,9 +155,7 @@ for (unsigned long i = 0 ; i < InstanceCount ; i++) { PluginInstance* instance = static_cast<PluginInstance*>(Instancesi); - LinuxSampler::EngineChannel* engineChannel = - instance->pChannel->GetEngineChannel(); - LinuxSampler::MidiInputPort* port = engineChannel->GetMidiInputPort(); + LinuxSampler::MidiInputPort* port = instance->pPort; snd_seq_event_t* events = Eventsi; unsigned& eventPos = eventPosArri; @@ -181,19 +199,8 @@ } } - LinuxSampler::AbstractEngineChannel* abstractEngineChannel = - static_cast<LinuxSampler::AbstractEngineChannel*>(engineChannel); - abstractEngineChannel->pChannelLeft->SetBuffer(instance->Out0 + samplePos); - abstractEngineChannel->pChannelRight->SetBuffer(instance->Out1 + samplePos); - if (i) { - abstractEngineChannel->pChannelLeft->Clear(samples); - abstractEngineChannel->pChannelRight->Clear(samples); - } else { - // the buffer set in the audio device is cleared - // by Render - audioDevice->Channel(0)->SetBuffer(instance->Out0 + samplePos); - audioDevice->Channel(1)->SetBuffer(instance->Out1 + samplePos); - } + instance->pChannelLeft->SetBuffer(instance->Out0 + samplePos); + instance->pChannelRight->SetBuffer(instance->Out1 + samplePos); } audioDevice->Render(samples); @@ -205,7 +212,7 @@ void PluginInstance::Activate() { dmsg(2, ("linuxsampler: activate instance=%p\n", static_cast<void*>(this))); - pChannel->GetEngineChannel()->GetMidiInputPort()->DispatchControlChange(123, 0, 0, 0); // all sound off + pPort->DispatchControlChange(123, 0, 0, 0); // all sound off } @@ -266,7 +273,7 @@ Ladspa.Name = "LinuxSampler"; Ladspa.Maker = "linuxsampler.org"; Ladspa.Copyright = "(C) 2003,2004 Benno Senoner and Christian Schoenebeck, " - "2005-2012 Christian Schoenebeck"; + "2005-2013 Christian Schoenebeck"; Ladspa.PortCount = 2; Ladspa.ImplementationData = 0; Ladspa.PortDescriptors = PortDescriptors;
View file
linuxsampler-2342.tar.bz2/src/hostplugins/dssi/PluginDssi.h -> linuxsampler-2718.tar.bz2/src/hostplugins/dssi/PluginDssi.h
Changed
@@ -1,6 +1,6 @@ /*************************************************************************** * * - * Copyright (C) 2008 - 2009 Andreas Persson * + * Copyright (C) 2008 - 2012 Andreas Persson * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -52,6 +52,9 @@ static PluginDssi* plugin; LinuxSampler::SamplerChannel* pChannel; LinuxSampler::MidiInputPort* pPort; + LinuxSampler::AudioChannel* pChannelLeft; + LinuxSampler::AudioChannel* pChannelRight; + LADSPA_Data* Out2; };
View file
linuxsampler-2342.tar.bz2/src/hostplugins/lv2/Makefile.am -> linuxsampler-2718.tar.bz2/src/hostplugins/lv2/Makefile.am
Changed
@@ -1,13 +1,12 @@ if HAVE_LV2 -INCLUDES = $(LV2_CFLAGS) $(GIG_CFLAGS) $(SQLITE3_CFLAGS) +AM_CPPFLAGS = $(LV2_CFLAGS) $(GIG_CFLAGS) $(SQLITE3_CFLAGS) plugindir = $(libdir)/lv2/linuxsampler.lv2 plugin_LTLIBRARIES = linuxsampler.la plugin_DATA = manifest.ttl linuxsampler.ttl -linuxsampler_la_SOURCES = PluginLv2.cpp PluginLv2.h \ - lv2_event.h lv2_state.h lv2_uri_map.h lv2_files.h -linuxsampler_la_LDFLAGS = -module -avoid-version +linuxsampler_la_SOURCES = PluginLv2.cpp PluginLv2.h +linuxsampler_la_LDFLAGS = -module -avoid-version -no-undefined linuxsampler_la_LIBADD = $(top_builddir)/src/liblinuxsampler.la endif -EXTRA_DIST = $(plugin_DATA) +EXTRA_DIST = manifest.ttl.in linuxsampler.ttl
View file
linuxsampler-2342.tar.bz2/src/hostplugins/lv2/PluginLv2.cpp -> linuxsampler-2718.tar.bz2/src/hostplugins/lv2/PluginLv2.cpp
Changed
@@ -18,6 +18,8 @@ * MA 02110-1301 USA * ***************************************************************************/ +#define _BSD_SOURCE 1 /* for realpath() */ + #include <algorithm> #include <cassert> #include <cstdio> @@ -28,23 +30,29 @@ #include "PluginLv2.h" -#define NS_ATOM "http://lv2plug.in/ns/ext/atom#" +#include <lv2/lv2plug.in/ns/ext/atom/util.h> +#include <lv2/lv2plug.in/ns/ext/midi/midi.h> + #define NS_LS "http://linuxsampler.org/schema#" +#define CHANNELS 32 + namespace { PluginLv2::PluginLv2(const LV2_Descriptor* Descriptor, double SampleRate, const char* BundlePath, const LV2_Feature* const* Features) { - Out0 = 0; - Out1 = 0; + Out = new float*CHANNELS; + for (int i = 0 ; i < CHANNELS ; i++) { + Outi = 0; + } UriMap = 0; MapPath = 0; MakePath = 0; for (int i = 0 ; Featuresi ; i++) { dmsg(2, ("linuxsampler: init feature: %s\n", Featuresi->URI)); - if (!strcmp(Featuresi->URI, LV2_URI_MAP_URI)) { - UriMap = (LV2_URI_Map_Feature*)Featuresi->data; + if (!strcmp(Featuresi->URI, LV2_URID__map)) { + UriMap = (LV2_URID_Map*)Featuresi->data; } else if (!strcmp(Featuresi->URI, LV2_STATE__mapPath)) { MapPath = (LV2_State_Map_Path*)Featuresi->data; } else if (!strcmp(Featuresi->URI, LV2_STATE__makePath)) { @@ -52,18 +60,24 @@ } } - Init(SampleRate, 128); + MidiEventType = uri_to_id(LV2_MIDI__MidiEvent); + + Init(SampleRate, 128, CHANNELS); InitState(); DefaultState = GetState(); } + PluginLv2::~PluginLv2() { + delete Out; + } + void PluginLv2::ConnectPort(uint32_t Port, void* DataLocation) { - if (Port == 2) { - MidiBuf = static_cast<LV2_Event_Buffer*>(DataLocation); - } else if (Port < 2) { - OutPort = static_cast<float*>(DataLocation); + if (Port == 0) { + MidiBuf = static_cast<LV2_Atom_Sequence*>(DataLocation); + } else if (Port < CHANNELS + 1) { + OutPort - 1 = static_cast<float*>(DataLocation); } } @@ -73,24 +87,28 @@ void PluginLv2::Run(uint32_t SampleCount) { int samplePos = 0; - uint8_t* events = MidiBuf->data; - int eventCount = MidiBuf->event_count; + + LV2_Atom_Event* ev = lv2_atom_sequence_begin(&MidiBuf->body); + while (SampleCount) { int samples = std::min(SampleCount, 128U); - for ( ; eventCount ; eventCount--) { - LV2_Event* event = reinterpret_cast<LV2_Event*>(events); + for ( ; !lv2_atom_sequence_is_end(&MidiBuf->body, + MidiBuf->atom.size, ev) ; + ev = lv2_atom_sequence_next(ev)) { + if (ev->body.type == MidiEventType) { - int time = event->frames - samplePos; - if (time >= samples) break; + int time = ev->time.frames - samplePos; + if (time >= samples) break; - uint8_t* data = events + sizeof(LV2_Event); - events += (sizeof(LV2_Event) + event->size + 7) & ~7; + uint8_t* data = reinterpret_cast<uint8_t*>(ev + 1); - pMidiDevice->Port()->DispatchRaw(data, time); + pMidiDevice->Port()->DispatchRaw(data, time); + } + } + for (int i = 0 ; i < CHANNELS ; i++) { + pAudioDevice->Channel(i)->SetBuffer(Outi + samplePos); } - pAudioDevice->Channel(0)->SetBuffer(Out0 + samplePos); - pAudioDevice->Channel(1)->SetBuffer(Out1 + samplePos); pAudioDevice->Render(samples); samplePos += samples; @@ -102,6 +120,23 @@ dmsg(2, ("linuxsampler: Deactivate\n")); } + static String RealPath(const String& path) + { + String out = path; + char* cpath = NULL; +#ifdef _WIN32 + cpath = (char*)malloc(MAX_PATH); + GetFullPathName(path.c_str(), MAX_PATH, cpath, NULL); +#else + cpath = realpath(path.c_str(), NULL); +#endif + if (cpath) { + out = cpath; + free(cpath); + } + return out; + } + String PluginLv2::PathToState(const String& path) { if (MapPath) { char* cstr = MapPath->abstract_path(MapPath->handle, path.c_str()); @@ -115,9 +150,10 @@ String PluginLv2::PathFromState(const String& path) { if (MapPath) { char* cstr = MapPath->absolute_path(MapPath->handle, path.c_str()); - const String abstract_path(cstr); + // Resolve symbolic links so SFZ sample paths load correctly + const String absolute_path(RealPath(cstr)); free(cstr); - return abstract_path; + return absolute_path; } return path; } @@ -135,8 +171,8 @@ } LV2_State_Status PluginLv2::Save( - LV2_State_Store_Function store, LV2_State_Handle handle, - uint32_t flags, const LV2_Feature* const* features) + LV2_State_Store_Function store, LV2_State_Handle handle, + uint32_t flags, const LV2_Feature* const* features) { LV2_State_Map_Path* OldMapPath = MapPath; LV2_State_Make_Path* OldMakePath = MakePath; @@ -152,10 +188,10 @@ char* path = MapPath->abstract_path(MapPath->handle, abs_path); store(handle, - uri_to_id(NULL, NS_LS "state-file"), + uri_to_id(NS_LS "state-file"), path, strlen(path) + 1, - uri_to_id(NULL, NS_ATOM "Path"), + uri_to_id(LV2_ATOM__Path), LV2_STATE_IS_PORTABLE); free(path); @@ -167,10 +203,10 @@ out << GetState(); store(handle, - uri_to_id(NULL, NS_LS "state-string"), + uri_to_id(NS_LS "state-string"), out.str().c_str(), out.str().length() + 1, - uri_to_id(NULL, NS_ATOM "String"), + uri_to_id(LV2_ATOM__String), LV2_STATE_IS_POD | LV2_STATE_IS_PORTABLE); } dmsg(2, ("saving done\n")); @@ -182,8 +218,8 @@ } LV2_State_Status PluginLv2::Restore( - LV2_State_Retrieve_Function retrieve, LV2_State_Handle handle, - uint32_t rflags, const LV2_Feature* const* features) + LV2_State_Retrieve_Function retrieve, LV2_State_Handle handle, + uint32_t rflags, const LV2_Feature* const* features) { LV2_State_Map_Path* OldMapPath = MapPath; LV2_State_Make_Path* OldMakePath = MakePath; @@ -195,11 +231,11 @@ const void* value = retrieve( handle, - uri_to_id(NULL, NS_LS "state-file"), + uri_to_id(NS_LS "state-file"), &size, &type, &flags); if (value) { // Restore from state-file - assert(type == uri_to_id(NULL, NS_ATOM "Path")); + assert(type == uri_to_id(LV2_ATOM__Path)); const String path((const char*)value); dmsg(2, ("linuxsampler: restoring from file %s\n", path.c_str())); std::ifstream in(path.c_str()); @@ -207,11 +243,11 @@ std::getline(in, state, '\0'); SetState(state); } else if ((value = retrieve(handle, - uri_to_id(NULL, NS_LS "state-string"), + uri_to_id(NS_LS "state-string"), &size, &type, &flags))) { // Restore from state-string dmsg(2, ("linuxsampler: restoring from string\n")); - assert(type == uri_to_id(NULL, NS_ATOM "String")); + assert(type == uri_to_id(LV2_ATOM__String)); String state((const char*)value); SetState(state); } else {
View file
linuxsampler-2342.tar.bz2/src/hostplugins/lv2/PluginLv2.h -> linuxsampler-2718.tar.bz2/src/hostplugins/lv2/PluginLv2.h
Changed
@@ -1,6 +1,6 @@ /*************************************************************************** * * - * Copyright (C) 2008 Andreas Persson * + * Copyright (C) 2008 - 2012 Andreas Persson * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -21,12 +21,13 @@ #ifndef LS_PLUGINLV2_H #define LS_PLUGINLV2_H -#include <lv2.h> -#include "lv2_event.h" -#include "lv2_state.h" -#include "lv2_uri_map.h" #include "../../drivers/Plugin.h" +#include <lv2/lv2plug.in/ns/lv2core/lv2.h> +#include <lv2/lv2plug.in/ns/ext/atom/atom.h> +#include <lv2/lv2plug.in/ns/ext/state/state.h> +#include <lv2/lv2plug.in/ns/ext/urid/urid.h> + namespace { class PluginLv2 : public LinuxSampler::Plugin { @@ -34,6 +35,7 @@ PluginLv2(const LV2_Descriptor* Descriptor, double SampleRate, const char* BundlePath, const LV2_Feature* const* Features); + ~PluginLv2(); void ConnectPort(uint32_t Port, void* DataLocation); void Activate(); void Run(uint32_t SampleCount); @@ -48,15 +50,16 @@ virtual String PathFromState(const String& string); private: - uint32_t uri_to_id(const char* map, const char* uri) { - return UriMap->uri_to_id(UriMap->callback_data, map, uri); + LV2_URID uri_to_id(const char* uri) { + return UriMap->map(UriMap->handle, uri); } void SetStateFeatures(const LV2_Feature* const* Features); - float* Out2; - LV2_Event_Buffer* MidiBuf; - LV2_URI_Map_Feature* UriMap; + float** Out; + LV2_Atom_Sequence* MidiBuf; + LV2_URID_Map* UriMap; + LV2_URID MidiEventType; LV2_State_Map_Path* MapPath; LV2_State_Make_Path* MakePath; @@ -91,13 +94,15 @@ static void cleanup(LV2_Handle instance); static const void* extension_data(const char* uri); - static LV2_State_Status save(LV2_Handle handle, + static LV2_State_Status save(LV2_Handle handle, LV2_State_Store_Function store, - void* data); + LV2_State_Handle state, uint32_t flags, + const LV2_Feature* const* features); - static LV2_State_Status restore(LV2_Handle handle, + static LV2_State_Status restore(LV2_Handle handle, LV2_State_Retrieve_Function retrieve, - void* data); + LV2_State_Handle state, uint32_t flags, + const LV2_Feature* const* features); } }
View file
linuxsampler-2342.tar.bz2/src/hostplugins/lv2/linuxsampler.ttl -> linuxsampler-2718.tar.bz2/src/hostplugins/lv2/linuxsampler.ttl
Changed
@@ -1,30 +1,331 @@ -@prefix lv2: <http://lv2plug.in/ns/lv2core#> . +@prefix atom: <http://lv2plug.in/ns/ext/atom#> . @prefix doap: <http://usefulinc.com/ns/doap#> . -@prefix ev: <http://lv2plug.in/ns/ext/event#> . +@prefix foaf: <http://xmlns.com/foaf/0.1/> . +@prefix ls: <http://linuxsampler.org/plugins/linuxsampler#> . +@prefix lv2: <http://lv2plug.in/ns/lv2core#> . +@prefix pg: <http://lv2plug.in/ns/ext/port-groups#> . <http://linuxsampler.org/plugins/linuxsampler> - a lv2:InstrumentPlugin ; + a lv2:InstrumentPlugin, doap:Project ; doap:name "LinuxSampler" ; + doap:maintainer + foaf:name "linuxsampler.org" + ; doap:license <http://linuxsampler.org/downloads.html#exception> ; lv2:optionalFeature lv2:hardRTCapable ; lv2:extensionData <http://lv2plug.in/ns/ext/state#interface> ; lv2:optionalFeature <http://lv2plug.in/ns/ext/state#mapPath> ; lv2:optionalFeature <http://lv2plug.in/ns/ext/state#makePath> ; - lv2:optionalFeature <http://lv2plug.in/ns/ext/uri-map> ; + lv2:optionalFeature <http://lv2plug.in/ns/ext/urid#map> ; lv2:port - a lv2:AudioPort , lv2:OutputPort ; - lv2:index 0 ; - lv2:symbol "out_left" ; - lv2:name "Output Left" + a atom:AtomPort , lv2:InputPort ; + atom:bufferType atom:Sequence ; + atom:supports <http://lv2plug.in/ns/ext/midi#MidiEvent> ; + lv2:index 0 ; + lv2:symbol "midi" ; + lv2:name "MIDI Input" , - a lv2:AudioPort , lv2:OutputPort ; - lv2:index 1 ; - lv2:symbol "out_right" ; - lv2:name "Output Right" + a lv2:AudioPort , lv2:OutputPort ; + lv2:index 1 ; + lv2:symbol "out_left" ; + lv2:name "Output Left" ; + lv2:designation pg:left ; + pg:group ls:Out1 , - a ev:EventPort , lv2:InputPort ; + a lv2:AudioPort , lv2:OutputPort ; lv2:index 2 ; - ev:supportsEvent <http://lv2plug.in/ns/ext/midi#MidiEvent> ; - lv2:symbol "midi" ; - lv2:name "MIDI Input" + lv2:symbol "out_right" ; + lv2:name "Output Right" ; + lv2:designation pg:right ; + pg:group ls:Out1 + , + a lv2:AudioPort , lv2:OutputPort ; + lv2:index 3 ; + lv2:symbol "out__2_left" ; + lv2:name "Output 2 Left" ; + lv2:designation pg:left ; + pg:group ls:Out2 + , + a lv2:AudioPort , lv2:OutputPort ; + lv2:index 4 ; + lv2:symbol "out__2_right" ; + lv2:name "Output 2 Right" ; + lv2:designation pg:right ; + pg:group ls:Out2 + , + a lv2:AudioPort , lv2:OutputPort ; + lv2:index 5 ; + lv2:symbol "out__3_left" ; + lv2:name "Output 3 Left" ; + lv2:designation pg:left ; + pg:group ls:Out3 + , + a lv2:AudioPort , lv2:OutputPort ; + lv2:index 6 ; + lv2:symbol "out__3_right" ; + lv2:name "Output 3 Right" ; + lv2:designation pg:right ; + pg:group ls:Out3 + , + a lv2:AudioPort , lv2:OutputPort ; + lv2:index 7 ; + lv2:symbol "out__4_left" ; + lv2:name "Output 4 Left" ; + lv2:designation pg:left ; + pg:group ls:Out4 + , + a lv2:AudioPort , lv2:OutputPort ; + lv2:index 8 ; + lv2:symbol "out__4_right" ; + lv2:name "Output 4 Right" ; + lv2:designation pg:right ; + pg:group ls:Out4 + , + a lv2:AudioPort , lv2:OutputPort ; + lv2:index 9 ; + lv2:symbol "out__5_left" ; + lv2:name "Output 5 Left" ; + lv2:designation pg:left ; + pg:group ls:Out5 + , + a lv2:AudioPort , lv2:OutputPort ; + lv2:index 10 ; + lv2:symbol "out__5_right" ; + lv2:name "Output 5 Right" ; + lv2:designation pg:right ; + pg:group ls:Out5 + , + a lv2:AudioPort , lv2:OutputPort ; + lv2:index 11 ; + lv2:symbol "out__6_left" ; + lv2:name "Output 6 Left" ; + lv2:designation pg:left ; + pg:group ls:Out6 + , + a lv2:AudioPort , lv2:OutputPort ; + lv2:index 12 ; + lv2:symbol "out__6_right" ; + lv2:name "Output 6 Right" ; + lv2:designation pg:right ; + pg:group ls:Out6 + , + a lv2:AudioPort , lv2:OutputPort ; + lv2:index 13 ; + lv2:symbol "out__7_left" ; + lv2:name "Output 7 Left" ; + lv2:designation pg:left ; + pg:group ls:Out7 + , + a lv2:AudioPort , lv2:OutputPort ; + lv2:index 14 ; + lv2:symbol "out__7_right" ; + lv2:name "Output 7 Right" ; + lv2:designation pg:right ; + pg:group ls:Out7 + , + a lv2:AudioPort , lv2:OutputPort ; + lv2:index 15 ; + lv2:symbol "out__8_left" ; + lv2:name "Output 8 Left" ; + lv2:designation pg:left ; + pg:group ls:Out8 + , + a lv2:AudioPort , lv2:OutputPort ; + lv2:index 16 ; + lv2:symbol "out__8_right" ; + lv2:name "Output 8 Right" ; + lv2:designation pg:right ; + pg:group ls:Out8 + , + a lv2:AudioPort , lv2:OutputPort ; + lv2:index 17 ; + lv2:symbol "out__9_left" ; + lv2:name "Output 9 Left" ; + lv2:designation pg:left ; + pg:group ls:Out9 + , + a lv2:AudioPort , lv2:OutputPort ; + lv2:index 18 ; + lv2:symbol "out__9_right" ; + lv2:name "Output 9 Right" ; + lv2:designation pg:right ; + pg:group ls:Out9 + , + a lv2:AudioPort , lv2:OutputPort ; + lv2:index 19 ; + lv2:symbol "out__10_left" ; + lv2:name "Output 10 Left" ; + lv2:designation pg:left ; + pg:group ls:Out10 + , + a lv2:AudioPort , lv2:OutputPort ; + lv2:index 20 ; + lv2:symbol "out__10_right" ; + lv2:name "Output 10 Right" ; + lv2:designation pg:right ; + pg:group ls:Out10 + , + a lv2:AudioPort , lv2:OutputPort ; + lv2:index 21 ; + lv2:symbol "out__11_left" ; + lv2:name "Output 11 Left" ; + lv2:designation pg:left ; + pg:group ls:Out11 + , + a lv2:AudioPort , lv2:OutputPort ; + lv2:index 22 ; + lv2:symbol "out__11_right" ; + lv2:name "Output 11 Right" ; + lv2:designation pg:right ; + pg:group ls:Out11 + , + a lv2:AudioPort , lv2:OutputPort ; + lv2:index 23 ; + lv2:symbol "out__12_left" ; + lv2:name "Output 12 Left" ; + lv2:designation pg:left ; + pg:group ls:Out12 + , + a lv2:AudioPort , lv2:OutputPort ; + lv2:index 24 ; + lv2:symbol "out__12_right" ; + lv2:name "Output 12 Right" ; + lv2:designation pg:right ; + pg:group ls:Out12 + , + a lv2:AudioPort , lv2:OutputPort ; + lv2:index 25 ; + lv2:symbol "out__13_left" ; + lv2:name "Output 13 Left" ; + lv2:designation pg:left ; + pg:group ls:Out13 + , + a lv2:AudioPort , lv2:OutputPort ; + lv2:index 26 ; + lv2:symbol "out__13_right" ; + lv2:name "Output 13 Right" ; + lv2:designation pg:right ; + pg:group ls:Out13 + , + a lv2:AudioPort , lv2:OutputPort ; + lv2:index 27 ; + lv2:symbol "out__14_left" ; + lv2:name "Output 14 Left" ; + lv2:designation pg:left ; + pg:group ls:Out14 + , + a lv2:AudioPort , lv2:OutputPort ; + lv2:index 28 ; + lv2:symbol "out__14_right" ; + lv2:name "Output 14 Right" ; + lv2:designation pg:right ; + pg:group ls:Out14 + , + a lv2:AudioPort , lv2:OutputPort ; + lv2:index 29 ; + lv2:symbol "out__15_left" ; + lv2:name "Output 15 Left" ; + lv2:designation pg:left ; + pg:group ls:Out15 + , + a lv2:AudioPort , lv2:OutputPort ; + lv2:index 30 ; + lv2:symbol "out__15_right" ; + lv2:name "Output 15 Right" ; + lv2:designation pg:right ; + pg:group ls:Out15 + , + a lv2:AudioPort , lv2:OutputPort ; + lv2:index 31 ; + lv2:symbol "out__16_left" ; + lv2:name "Output 16 Left" ; + lv2:designation pg:left ; + pg:group ls:Out16 + , + a lv2:AudioPort , lv2:OutputPort ; + lv2:index 32 ; + lv2:symbol "out__16_right" ; + lv2:name "Output 16 Right" ; + lv2:designation pg:right ; + pg:group ls:Out16 . + +ls:Out1 + a pg:StereoGroup, pg:OutputGroup ; + lv2:name "Out 1" ; + lv2:symbol "out1" . + +ls:Out2 + a pg:StereoGroup, pg:OutputGroup ; + lv2:name "Out 2" ; + lv2:symbol "out2" . + +ls:Out3 + a pg:StereoGroup, pg:OutputGroup ; + lv2:name "Out 3" ; + lv2:symbol "out3" . + +ls:Out4 + a pg:StereoGroup, pg:OutputGroup ; + lv2:name "Out 4" ; + lv2:symbol "out4" . + +ls:Out5 + a pg:StereoGroup, pg:OutputGroup ; + lv2:name "Out 5" ; + lv2:symbol "out5" . + +ls:Out6 + a pg:StereoGroup, pg:OutputGroup ; + lv2:name "Out 6" ; + lv2:symbol "out6" . + +ls:Out7 + a pg:StereoGroup, pg:OutputGroup ; + lv2:name "Out 7" ; + lv2:symbol "out7" . + +ls:Out8 + a pg:StereoGroup, pg:OutputGroup ; + lv2:name "Out 8" ; + lv2:symbol "out8" . + +ls:Out9 + a pg:StereoGroup, pg:OutputGroup ; + lv2:name "Out 9" ; + lv2:symbol "out9" . + +ls:Out10 + a pg:StereoGroup, pg:OutputGroup ; + lv2:name "Out 10" ; + lv2:symbol "out10" . + +ls:Out11 + a pg:StereoGroup, pg:OutputGroup ; + lv2:name "Out 11" ; + lv2:symbol "out11" . + +ls:Out12 + a pg:StereoGroup, pg:OutputGroup ; + lv2:name "Out 12" ; + lv2:symbol "out12" . + +ls:Out13 + a pg:StereoGroup, pg:OutputGroup ; + lv2:name "Out 13" ; + lv2:symbol "out13" . + +ls:Out14 + a pg:StereoGroup, pg:OutputGroup ; + lv2:name "Out 14" ; + lv2:symbol "out14" . + +ls:Out15 + a pg:StereoGroup, pg:OutputGroup ; + lv2:name "Out 15" ; + lv2:symbol "out15" . + +ls:Out16 + a pg:StereoGroup, pg:OutputGroup ; + lv2:name "Out 16" ; + lv2:symbol "out16" .
View file
linuxsampler-2718.tar.bz2/src/hostplugins/lv2/manifest.ttl.in
Added
@@ -0,0 +1,7 @@ +@prefix lv2: <http://lv2plug.in/ns/lv2core#> . +@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> . + +<http://linuxsampler.org/plugins/linuxsampler> + a lv2:Plugin ; + lv2:binary <linuxsampler@LIB_EXT@> ; + rdfs:seeAlso <linuxsampler.ttl> .
View file
linuxsampler-2342.tar.bz2/src/hostplugins/vst/Makefile.am -> linuxsampler-2718.tar.bz2/src/hostplugins/vst/Makefile.am
Changed
@@ -2,14 +2,11 @@ plugindir = $(libdir)/vst plugin_LTLIBRARIES = LinuxSampler.la -INCLUDES = -I$(VSTSDK_DIR) -I$(VSTSDK_DIR)/public.sdk/source/vst2.x \ +LinuxSampler_la_CPPFLAGS = -I"$(VSTSDK_DIR)" \ + -I"$(VSTSDK_DIR)/public.sdk/source/vst2.x" \ $(GIG_CFLAGS) $(SQLITE3_CFLAGS) -LinuxSampler_la_SOURCES = PluginVst.cpp PluginVst.h -nodist_LinuxSampler_la_SOURCES = \ - $(VSTSDK_DIR)/public.sdk/source/vst2.x/audioeffectx.cpp \ - $(VSTSDK_DIR)/public.sdk/source/vst2.x/audioeffect.cpp \ - $(VSTSDK_DIR)/public.sdk/source/vst2.x/vstplugmain.cpp +LinuxSampler_la_SOURCES = PluginVst.cpp PluginVst.h vstsdk.cpp LinuxSampler_la_LDFLAGS = -module -avoid-version -no-undefined LinuxSampler_la_LIBADD = $(top_builddir)/src/liblinuxsampler.la @@ -18,7 +15,7 @@ LinuxSampler_la_LDFLAGS += -Wl,$(srcdir)/PluginVst.def -Wl,-s LinuxSampler_la_LIBADD += -lws2_32 else -LinuxSampler_la_CPPFLAGS = -D__cdecl= +LinuxSampler_la_CPPFLAGS += -D__cdecl= -DDATADIR=\""$(datadir)"\" if LINUX LinuxSampler_la_LDFLAGS += -Wl,--defsym -Wl,main=VSTPluginMain -Wl,-s endif @@ -34,7 +31,9 @@ if test -f .libs/LinuxSampler.so ; then cp .libs/LinuxSampler.so LinuxSampler.vst/Contents/MacOS/LinuxSampler ; fi @touch $@ -clean-local: +CLEAN_MAC = clean-bundle + +clean-bundle: rm -rf LinuxSampler.vst install-exec-hook:
View file
linuxsampler-2342.tar.bz2/src/hostplugins/vst/PluginVst.cpp -> linuxsampler-2718.tar.bz2/src/hostplugins/vst/PluginVst.cpp
Changed
@@ -1,6 +1,6 @@ /*************************************************************************** * * - * Copyright (C) 2008 - 2011 Andreas Persson * + * Copyright (C) 2008 - 2013 Andreas Persson * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -24,14 +24,30 @@ #ifdef WIN32 #include <tchar.h> +#else +#include <cerrno> +#include <signal.h> +#include <sys/wait.h> +#include <unistd.h> +#include <dirent.h> +#include <sys/stat.h> #endif #include "PluginVst.h" +#include "../../drivers/midi/MidiInstrumentMapper.h" #ifndef CHANNELS #define CHANNELS 32 #endif +// the sampler can actually hold up a very huge amount of programs +// (2^32 maps * 2^16 banks MSB + LSB * 2^8 programs per bank = 2^56 total programs ) +// however I am not sure if we might crash some VST host with a number +// here that is too huge, however this should be enough in most cases ... +#define MAX_VST_PROGRAMS (128*128) + +using namespace LinuxSampler; + namespace { #if defined(WIN32) && CONFIG_DEBUG_LEVEL >= 2 @@ -42,28 +58,19 @@ LinuxSampler::Mutex logmutex; void log(const char* fmt, ...) { - logmutex.Lock(); + LockGuard lock(logmutex); FILE* f = fopen((String(getenv("TEMP")) + "\\linuxsamplervst.log").c_str(), "a"); va_list ap; va_start(ap, fmt); vfprintf(f, fmt, ap); va_end(ap); fclose(f); - logmutex.Unlock(); } #undef dmsg #define dmsg(debuglevel,x) log x; #endif -// *************** LinuxSamplerVstProgram *************** -// * - - LinuxSamplerVstProgram::LinuxSamplerVstProgram() { - vst_strncpy(name, "Basic", kVstMaxProgNameLen); - } - - // *************** LinuxSamplerEditor *************** // * @@ -72,6 +79,8 @@ dmsg(2, ("-->LinuxSamplerEditor constructor\n")); #ifdef WIN32 ProcessHandle = INVALID_HANDLE_VALUE; +#else + pid = 0; #endif } @@ -79,13 +88,62 @@ close(); } + String LinuxSamplerEditor::FindFantasia() { + String fantasia; +#ifdef WIN32 + // assume Fantasia is in the same directory or one directory above + // the liblinuxsampler dll + String lspath = LinuxSampler::Sampler::GetInstallDir(); + if (!lspath.empty()) { + lspath += "\\"; + WIN32_FIND_DATA fd; + HANDLE hFind = + FindFirstFile((lspath + "Fantasia*.jar").c_str(), &fd); + if (hFind == INVALID_HANDLE_VALUE) { + lspath += "..\\"; + hFind = FindFirstFile((lspath + "Fantasia*.jar").c_str(), &fd); + } + if (hFind != INVALID_HANDLE_VALUE) { + fantasia = fd.cFileName; + FindClose(hFind); + return lspath + fantasia; + } + } +#elif defined(__APPLE__) + // look for the java application wrapper + const char* cmd = + "/Applications/LinuxSampler/Fantasia.app" + "/Contents/MacOS/JavaApplicationStub"; + fantasia = String(getenv("HOME")) + cmd; + struct stat buf; + if (stat(fantasia.c_str(), &buf) != 0) { + fantasia = stat(cmd, &buf) == 0 ? cmd : ""; + } +#else + // search in <datadir>/java (default: /usr/local/share/java) + String path(DATADIR); + path += *(path.end() - 1) == '/' ? "java" : "/java"; + DIR* dir = opendir(path.c_str()); + if (dir) { + while (dirent* d = readdir(dir)) { + const char* name = d->d_name; + if (strncmp("Fantasia", name, 8) == 0 && + strcmp(name + strlen(name) - 4, ".jar") == 0) { + fantasia = path + "/" + name; + } + } + closedir(dir); + } +#endif + return fantasia; + } + bool LinuxSamplerEditor::open(void* ptr) { dmsg(2, ("-->LinuxSamplerEditor::open\n")); AEffEditor::open(ptr); + // try to start the JSampler Fantasia GUI as a separate process #ifdef WIN32 - // try to start the JSample Fantasia GUI as a separate process - // first check if it's already running if (ProcessHandle != INVALID_HANDLE_VALUE) { DWORD exitCode; @@ -97,55 +155,68 @@ ProcessHandle = INVALID_HANDLE_VALUE; } - // assume Fantasia is in the same directory or one directory above - // the liblinuxsampler dll - String lspath = LinuxSampler::Sampler::GetInstallDir(); - if (!lspath.empty()) { - lspath += "\\"; - WIN32_FIND_DATA fd; - HANDLE hFind = FindFirstFile((lspath + "Fantasia*.jar").c_str(), &fd); - if (hFind == INVALID_HANDLE_VALUE) { - lspath += "..\\"; - hFind = FindFirstFile((lspath + "Fantasia*.jar").c_str(), &fd); - } - if (hFind != INVALID_HANDLE_VALUE) { - String fantasia(fd.cFileName); - FindClose(hFind); - - // start a java process - String path; // look in PATH first - for (int i = 0 ; i < 2 ; i++) { // two tries - STARTUPINFO si; - PROCESS_INFORMATION pi; - ZeroMemory(&si, sizeof(si)); - si.cb = sizeof(si); - ZeroMemory(&pi, sizeof(pi)); - - Command = _tcsdup(TEXT((String("\"") + path + "javaw\" -jar \"" + - lspath + fantasia + "\"").c_str())); - if (CreateProcess(NULL, Command, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)) { - ProcessHandle = pi.hProcess; - CloseHandle(pi.hThread); - break; - } else { - free(Command); - if (path.empty()) { - // java wasn't found in PATH, try again - // with an alternative directory - char* windir = getenv("windir"); - if (!windir) break; + String fantasia = FindFantasia(); + if (!fantasia.empty()) { + // start a java process + String path; // look in PATH first + for (int i = 0 ; i < 2 ; i++) { // two tries + STARTUPINFO si; + PROCESS_INFORMATION pi; + ZeroMemory(&si, sizeof(si)); + si.cb = sizeof(si); + ZeroMemory(&pi, sizeof(pi)); + + Command = _tcsdup(TEXT((String("\"") + path + + "javaw\" -jar \"" + fantasia + + "\"").c_str())); + if (CreateProcess(NULL, Command, NULL, NULL, FALSE, 0, NULL, + NULL, &si, &pi)) { + ProcessHandle = pi.hProcess; + CloseHandle(pi.hThread); + break; + } else { + free(Command); + if (path.empty()) { + // java wasn't found in PATH, try again + // with an alternative directory + char* windir = getenv("windir"); + if (!windir) break; #ifdef _WIN64 - // LS plugin is 64 bit - look for 32 bit java - path = String(windir) + "\\SysWOW64\\"; + // LS plugin is 64 bit - look for 32 bit java + path = String(windir) + "\\SysWOW64\\"; #else - // LS plugin is 32 bit - look for 64 bit java - path = String(windir) + "\\Sysnative\\"; + // LS plugin is 32 bit - look for 64 bit java + path = String(windir) + "\\Sysnative\\"; #endif - } } } } } +#else + // first check if it's already running + if (pid && waitpid(pid, 0, WNOHANG) == 0) return true; + + String fantasia = FindFantasia(); + if (!fantasia.empty()) + { + // start a java process + pid = fork(); + if (pid == -1) { + dmsg(1, ("fork failed %d %s\n", errno, strerror(errno))); + pid = 0; + } else if (pid == 0) { +#ifdef __APPLE__ + execl(fantasia.c_str(), fantasia.c_str(), (char*)0); +#else + execlp("java", "java", "-jar", fantasia.c_str(), (char*)0); +#endif + dmsg(1, ("exec failed %d %s\n", errno, strerror(errno))); + + // make sure something is executed, so the static + // destructors copied from the parent don't run + execl("/usr/bin/true", "/usr/bin/true", (char*)0); + } + } #endif dmsg(2, ("<--LinuxSamplerEditor::open\n")); return true; @@ -160,6 +231,12 @@ CloseHandle(ProcessHandle); ProcessHandle = INVALID_HANDLE_VALUE; } +#else + if (pid) { + kill(pid, SIGTERM); + waitpid(pid, 0, 0); + pid = 0; + } #endif } @@ -167,7 +244,7 @@ ERect* r = new ERect(); r->top = 0; r->left = 0; - r->bottom = 0; + r->bottom = 1; // 0 makes EnergyXT and Ardour on Linux crash r->right = 400; *rect = r; return true; @@ -178,12 +255,11 @@ // * LinuxSamplerVst::LinuxSamplerVst(audioMasterCallback audioMaster) : - AudioEffectX(audioMaster, NbPrograms, 0), + AudioEffectX(audioMaster, MAX_VST_PROGRAMS, 0), StateBuf(0) { dmsg(2, ("-->constructor\n")); - Programs = new LinuxSamplerVstProgramNbPrograms; setProgram(0); setNumInputs(0); setNumOutputs(CHANNELS); @@ -200,17 +276,22 @@ void LinuxSamplerVst::resume() { dmsg(2, ("-->resume\n")); - if (!pAudioDevice) { - Init(int(sampleRate), blockSize, CHANNELS); - if (!SavedChunk.empty()) { - SetState(SavedChunk); - SavedChunk.clear(); + // Ardour initially sets blockSize to zero - we postpone the + // initialization until we have a blockSize + if (blockSize != 0) { + if (!pAudioDevice) { + Init(int(sampleRate), blockSize, CHANNELS); + + if (!SavedChunk.empty()) { + SetState(SavedChunk); + SavedChunk.clear(); + } else { + InitState(); + } } else { - InitState(); + Init(int(sampleRate), blockSize, CHANNELS); } - } else { - Init(int(sampleRate), blockSize, CHANNELS); } AudioEffectX::resume(); dmsg(2, ("<--resume\n")); @@ -218,26 +299,95 @@ LinuxSamplerVst::~LinuxSamplerVst() { dmsg(2, ("-->destructor\n")); - delete Programs; if (StateBuf) free(StateBuf); dmsg(2, ("<--destructor\n")); } void LinuxSamplerVst::setProgram(VstInt32 program) { - if (program < 0 || program >= NbPrograms) return; - - curProgram = program; + if (program < 0 || program >= MAX_VST_PROGRAMS || + !pMidiDevice || !pMidiDevice->Port()) return; + + int i = 0; + const std::vector<int> maps = MidiInstrumentMapper::Maps(); + for (int iMap = 0; iMap < maps.size(); ++iMap) { + const int mapID = mapsiMap; + const std::map<midi_prog_index_t, MidiInstrumentMapper::entry_t> mappings = + MidiInstrumentMapper::Entries(mapsmapID); + for (std::map<midi_prog_index_t, MidiInstrumentMapper::entry_t>::const_iterator iter = mappings.begin(); + iter != mappings.end(); ++iter, ++i) + { + if (i == program) { + //TODO: switch MIDI instrument map before sending bank select and program change + + const uint iMIDIChannel = 0; + pMidiDevice->Port()->DispatchBankSelectMsb(iter->first.midi_bank_msb, iMIDIChannel); + pMidiDevice->Port()->DispatchBankSelectLsb(iter->first.midi_bank_lsb, iMIDIChannel); + pMidiDevice->Port()->DispatchProgramChange(iter->first.midi_prog, iMIDIChannel); + + curProgram = program; + + return; + } + } + } } - void LinuxSamplerVst::setProgramName(char* name) { - vst_strncpy(ProgramscurProgram.name, name, kVstMaxProgNameLen); + int i = 0; + const std::vector<int> maps = MidiInstrumentMapper::Maps(); + for (int iMap = 0; iMap < maps.size(); ++iMap) { + const int mapID = mapsiMap; + const std::map<midi_prog_index_t, MidiInstrumentMapper::entry_t> mappings = + MidiInstrumentMapper::Entries(mapsmapID); + for (std::map<midi_prog_index_t, MidiInstrumentMapper::entry_t>::const_iterator iter = mappings.begin(); + iter != mappings.end(); ++iter, ++i) + { + if (i == curProgram) { + char bufkVstMaxProgNameLen + 1 = {}; + vst_strncpy(buf, name, kVstMaxProgNameLen); + MidiInstrumentMapper::entry_t entry = iter->second; + entry.Name = buf; + try { + MidiInstrumentMapper::AddOrReplaceEntry( + mapID, iter->first, entry, false/*bInBackground*/ + ); + } catch (Exception e) { + e.PrintMessage(); + } + return; + } + } + } } - void LinuxSamplerVst::getProgramName(char* name) { - vst_strncpy(name, ProgramscurProgram.name, kVstMaxProgNameLen); + if (!getProgramNameIndexed(0 /*dont care*/, curProgram, name)) { + vst_strncpy(name, "unknown", kVstMaxProgNameLen); + } + } + + bool LinuxSamplerVst::getProgramNameIndexed(VstInt32 /*category*/, VstInt32 index, + char* text) + { + //NOTE: parameter 'category' is unused in VST 2.4 + + int i = 0; + const std::vector<int> maps = MidiInstrumentMapper::Maps(); + for (int iMap = 0; iMap < maps.size(); ++iMap) { + const int mapID = mapsiMap; + const std::map<midi_prog_index_t, MidiInstrumentMapper::entry_t> mappings = + MidiInstrumentMapper::Entries(mapsmapID); + for (std::map<midi_prog_index_t, MidiInstrumentMapper::entry_t>::const_iterator iter = mappings.begin(); + iter != mappings.end(); ++iter, ++i) + { + if (i == index) { + vst_strncpy(text, iter->second.Name.c_str(), kVstMaxProgNameLen); + return true; + } + } + } + return false; } @@ -252,16 +402,6 @@ } - bool LinuxSamplerVst::getProgramNameIndexed(VstInt32 category, VstInt32 index, - char* text) { - if (index < NbPrograms) { - vst_strncpy(text, Programsindex.name, kVstMaxProgNameLen); - return true; - } - return false; - } - - bool LinuxSamplerVst::getEffectName(char* name) { vst_strncpy(name, "LinuxSampler", kVstMaxEffectNameLen); return true; @@ -287,9 +427,20 @@ VstInt32 LinuxSamplerVst::canDo(char* text) { dmsg(2, ("canDo %s\n", text)); + + // supported features if (strcmp(text, "receiveVstEvents") == 0 || - strcmp(text, "receiveVstMidiEvent") == 0) return 1; - return -1; + strcmp(text, "receiveVstMidiEvent") == 0 || + strcmp(text, "midiProgramNames") == 0) return 1; + + // not supported features + if (strcmp(text, "sendVstEvents") == 0 || + strcmp(text, "sendVstMidiEvent") == 0 || + strcmp(text, "receiveVstTimeInfo") == 0 || + strcmp(text, "offline") == 0 || + strcmp(text, "bypass") == 0) return -1; + + return 0; // "don't know", never heard of this feature } void LinuxSamplerVst::setSampleRate(float sampleRate) {
View file
linuxsampler-2342.tar.bz2/src/hostplugins/vst/PluginVst.h -> linuxsampler-2718.tar.bz2/src/hostplugins/vst/PluginVst.h
Changed
@@ -1,6 +1,6 @@ /*************************************************************************** * * - * Copyright (C) 2008 Andreas Persson * + * Copyright (C) 2008 - 2012 Andreas Persson * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -29,13 +29,6 @@ namespace { - class LinuxSamplerVstProgram { - public: - LinuxSamplerVstProgram(); - char namekVstMaxProgNameLen + 1; - }; - - class LinuxSamplerEditor : public AEffEditor { public: LinuxSamplerEditor(AudioEffect* effect); @@ -47,7 +40,10 @@ #ifdef WIN32 HANDLE ProcessHandle; LPTSTR Command; +#else + pid_t pid; #endif + static String FindFantasia(); }; @@ -80,10 +76,6 @@ VstInt32 setChunk(void* data, VstInt32 byteSize, bool isPreset); private: - static const int NbPrograms = 128; - - LinuxSamplerVstProgram* Programs; - char* StateBuf; String SavedChunk; };
View file
linuxsampler-2718.tar.bz2/src/hostplugins/vst/vstsdk.cpp
Added
@@ -0,0 +1,3 @@ +#include "audioeffectx.cpp" +#include "audioeffect.cpp" +#include "vstplugmain.cpp"
View file
linuxsampler-2342.tar.bz2/src/linuxsampler.cpp -> linuxsampler-2718.tar.bz2/src/linuxsampler.cpp
Changed
@@ -3,7 +3,7 @@ * LinuxSampler - modular, streaming capable sampler * * * * Copyright (C) 2003-2004 by Benno Senoner and Christian Schoenebeck * - * Copyright (C) 2005-2012 Christian Schoenebeck * + * Copyright (C) 2005-2015 Christian Schoenebeck * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -119,7 +119,7 @@ dmsg(1,("LinuxSampler %s\n", VERSION)); dmsg(1,("Copyright (C) 2003,2004 by Benno Senoner and Christian Schoenebeck\n")); - dmsg(1,("Copyright (C) 2005-2012 Christian Schoenebeck\n")); + dmsg(1,("Copyright (C) 2005-2015 Christian Schoenebeck\n")); #if defined(WIN32) #if 0 @@ -220,6 +220,11 @@ } } +//TODO: (hopefully) just a temporary nasty hack for launching gigedit on the main thread on Mac (see comments in gigedit.cpp for details) +#if defined(__APPLE__) + g_mainThreadCallbackSupported = true; +#endif + while (atomic_read(&running)) { if (bPrintStatistics) { const std::set<Engine*>& engines = EngineFactory::EngineInstances(); @@ -247,7 +252,22 @@ } pSampler->fireStatistics(); + + //TODO: (hopefully) just a temporary nasty hack for launching gigedit on the main thread on Mac (see comments in gigedit.cpp for details) + #if defined(__APPLE__) + if (g_fireMainThreadCallback && g_mainThreadCallback) { + void (*fn)(void* info) = g_mainThreadCallback; + void* info = g_mainThreadCallbackInfo; + g_mainThreadCallbackInfo = NULL; + g_mainThreadCallback = NULL; + g_fireMainThreadCallback = false; + printf("Received main thread callback, calling now ...\n"); fflush(stdout); + (*fn)(info); + printf("Main thread callback executed.\n"); fflush(stdout); + } + #endif } +//#endif if (pLSCPServer) pLSCPServer->StopThread(); // the delete order here is important: the Sampler // destructor sends notifications to the lscpserver
View file
linuxsampler-2718.tar.bz2/src/ls_instr_script.cpp
Added
@@ -0,0 +1,150 @@ +/* + * Copyright (c) 2014 Christian Schoenebeck + * + * http://www.linuxsampler.org + * + * This program is part of LinuxSampler and released under the same terms. + * See README file for details. + */ + +#include "common/global.h" +#include "scriptvm/ScriptVM.h" +#include "shell/CFmt.h" +#include "engines/common/InstrumentScriptVM.h" +#include "engines/gig/InstrumentScriptVM.h" +#include <iostream> + +/* + This command line tool is currently merely for development and testing + purposes, regarding the real-time instrument script feature of the sampler. + You can use this command line application like this: + + ls_instr_script core < src/scriptvm/examples/helloworld.txt + + Which will peform 3 things: + + 1. Parses the given instrument script and prints any parser errors or + warnings. + 2. It dumps the parsed VM tree (only interesting for LS developers). + 3. If there were not parser errors, it will run each event handler defined in + the script. + */ + +using namespace LinuxSampler; +using namespace std; + +static void printUsage() { + cout << "ls_instr_script - Parse real-time instrument script from stdin." << endl; + cout << endl; + cout << "Usage: ls_instr_script ENGINE" << endl; + cout << endl; + cout << " ENGINE\n"; + cout << " Either \"core\", \"gig\", \"sf2\" or \"sfz\"." << endl; + cout << endl; + cout << "If you pass \"core\" as argument, only the core language built-in" << endl; + cout << "variables and functions are available. However in this particular" << endl; + cout << "mode the program will not just parse the given script, but also" << endl; + cout << "execute the event handlers. All other arguments for ENGINE provide" << endl; + cout << "the sampler engine / sampler format specific additional built-in" << endl; + cout << "variables and functions, however they wil not be executed by this" << endl; + cout << "program." << endl; + cout << endl; +} + +int main(int argc, char *argv) { + if (argc < 2) { + printUsage(); + return -1; + } + String engine = argv1; + bool runScript = false; + + ScriptVM* vm; + if (engine == "core") { + vm = new ScriptVM; + runScript = true; + } else if (engine == "sf2" || engine == "sfz") { + vm = new InstrumentScriptVM; + } else if (engine == "gig") { + vm = new gig::InstrumentScriptVM; + } else { + std::cerr << "Unknown ENGINE '" << engine << "'\n\n"; + printUsage(); + return -1; + } + + VMParserContext* parserContext = vm->loadScript(&std::cin); + + std::vector<ParserIssue> errors = parserContext->errors(); + std::vector<ParserIssue> warnings = parserContext->warnings(); + std::vector<ParserIssue> issues = parserContext->issues(); + if (warnings.empty() && errors.empty()) { + CFmt fmt; fmt.green(); + printf("EOF. Script parse completed successfully (no errors, no warnings).\n"); + } else if (!errors.empty()) { + CFmt fmt; fmt.red(); + printf("EOF. Script parse completed with issues (%d errors, %d warnings):\n", + errors.size(), warnings.size()); + } else { + CFmt fmt; fmt.yellow(); + printf("EOF. Script parse completed with issues (%d errors, %d warnings):\n", + errors.size(), warnings.size()); + } + for (int i = 0; i < issues.size(); ++i) { + CFmt fmt; + if (issuesi.isWrn()) fmt.yellow(); + else if (issuesi.isErr()) fmt.red(); + issuesi.dump(); + } + + printf("Dumping parsed VM tree\n"); + vm->dumpParsedScript(parserContext); + printf("End of parsed VM tree\n"); + + if (!errors.empty()) { + if (parserContext) delete parserContext; + return -1; + } + + if (!runScript) { + return 0; + } + + if (!parserContext->eventHandler(0)) { + printf("No event handler exists. So nothing to execute.\n"); + if (parserContext) delete parserContext; + return 0; + } + + printf("Preparing execution of script.\n"); + VMExecContext* execContext = vm->createExecContext(parserContext); + for (int i = 0; parserContext->eventHandler(i); ++i) { + VMEventHandler* handler = parserContext->eventHandler(i); + printf("Running event handler '%s'\n", handler->eventHandlerName().c_str()); + VMExecStatus_t result = vm->exec(parserContext, execContext, handler); + CFmt fmt; + if (result & VM_EXEC_ERROR) { + fmt.red(); + printf("Event handler '%s' finished with ERROR status\n", handler->eventHandlerName().c_str()); + } else if (result & VM_EXEC_SUSPENDED) { + fmt.yellow(); + printf("Event handler '%s' returned with SUSPENDED status: %d microseconds\n", + handler->eventHandlerName().c_str(), execContext->suspensionTimeMicroseconds()); + } else if (!(result & VM_EXEC_RUNNING)) { + fmt.green(); + printf("Event handler '%s' finished with SUCCESS status\n", handler->eventHandlerName().c_str()); + } else if (result & VM_EXEC_RUNNING) { + fmt.cyan(); + printf("Event handler '%s' finished with RUNNING status\n", handler->eventHandlerName().c_str()); + } else { + fmt.red(); + printf("Event handler '%s' finished with UNKNOWN status\n", handler->eventHandlerName().c_str()); + } + } + + if (parserContext) delete parserContext; + if (execContext) delete execContext; + if (vm) delete vm; + + return 0; +}
View file
linuxsampler-2342.tar.bz2/src/network/Makefile.am -> linuxsampler-2718.tar.bz2/src/network/Makefile.am
Changed
@@ -1,4 +1,4 @@ -INCLUDES = $(all_includes) $(GIG_CFLAGS) $(SQLITE3_CFLAGS) +AM_CPPFLAGS = $(all_includes) $(GIG_CFLAGS) $(SQLITE3_CFLAGS) METASOURCES = AUTO AM_CXXFLAGS = -Wreturn-type -ffast-math $(CXX_CPU_SWITCH) @@ -16,6 +16,7 @@ lscpserver.cpp lscpserver.h \ lscpsymbols.h \ lscpresultset.cpp lscpresultset.h \ + lscp_shell_reference.cpp lscp_shell_reference.h \ lscpevent.cpp lscpevent.h liblinuxsamplernetwork_la_LIBADD = $(winsocket_ldflags) @@ -26,16 +27,20 @@ # automatically (re)generate lscpsymbols.h with bison / yacc if the # yacc source file(s) have been changed lscpsymbols.h: $(yacc_sources) - $(top_srcdir)/scripts/generate_parser.sh + $(top_srcdir)/scripts/generate_lscp_parser.sh # automatically (re)generate lscpparser.cpp with bison / yacc if the # yacc source file(s) have been changed lscpparser.cpp: $(yacc_sources) - $(top_srcdir)/scripts/generate_parser.sh + $(top_srcdir)/scripts/generate_lscp_parser.sh +# automatically (re)generate lscp_shell_reference.cpp if the +# yacc source file or lscp.xml source have been changed +lscp_shell_reference.cpp: $(yacc_sources) ../../Documentation/lscp.xml + $(top_srcdir)/scripts/generate_lscp_parser.sh .PHONY: parser # "make parser" was explicitly requested parser: - $(top_builddir)/scripts/generate_parser.sh + $(top_builddir)/scripts/generate_lscp_parser.sh
View file
linuxsampler-2342.tar.bz2/src/network/lscp.h -> linuxsampler-2718.tar.bz2/src/network/lscp.h
Changed
@@ -3,6 +3,7 @@ * LinuxSampler - modular, streaming capable sampler * * * * Copyright (C) 2003, 2004 by Benno Senoner and Christian Schoenebeck * + * Copyright (C) 2014 Christian Schoenebeck * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -35,4 +36,21 @@ #define LSCP_WRN_UNKNOWN 0 ///< unknown warning type +// LSCP Shell Codes + +#define LSCP_SHU_COMPLETE 0 +#define LSCP_SHU_SYNTAX_ERR 1 +#define LSCP_SHU_INCOMPLETE 2 + +#define LSCP_SHD_NO_MATCH 0 +#define LSCP_SHD_MATCH 1 + +// LSCP Shell Keywords + +#define LSCP_SHK_GOOD_FRONT "{{GF}}" +#define LSCP_SHK_CURSOR "{{CU}}" +#define LSCP_SHK_SUGGEST_BACK "{{SB}}" +#define LSCP_SHK_POSSIBILITIES_BACK "{{PB}}" +#define LSCP_SHK_EXPECT_MULTI_LINE "SHE:MLINE" + #endif // __LSCP_H__
View file
linuxsampler-2342.tar.bz2/src/network/lscp.y -> linuxsampler-2718.tar.bz2/src/network/lscp.y
Changed
@@ -3,7 +3,7 @@ * LinuxSampler - modular, streaming capable sampler * * * * Copyright (C) 2003, 2004 by Benno Senoner and Christian Schoenebeck * - * Copyright (C) 2005 - 2010 Christian Schoenebeck * + * Copyright (C) 2005 - 2014 Christian Schoenebeck * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -35,13 +35,15 @@ #include "lscpserver.h" #include "lscpevent.h" #include "lscpsymbols.h" +#include <algorithm> +#include "lscp.h" namespace LinuxSampler { // to save us typing work in the rules action definitions #define LSCPSERVER ((yyparse_param_t*) yyparse_param)->pServer #define SESSION_PARAM ((yyparse_param_t*) yyparse_param) -#define INCREMENT_LINE { SESSION_PARAM->iLine++; SESSION_PARAM->iColumn = 0; } +#define INCREMENT_LINE { SESSION_PARAM->onNextLine(); sParsed.clear(); } // clears input buffer void restart(yyparse_param_t* pparam, int& yychar); @@ -51,6 +53,7 @@ static int bytes = 0; // current number of characters in the input buffer static int ptr = 0; // current position in the input buffer static String sLastError; // error message of the last error occured +static String sParsed; ///< Characters of current line which have already been shifted (consumed/parsed) by the parser. // external reference to the function which actually reads from the socket extern int GetLSCPCommand( void *buf, int max_size); @@ -64,6 +67,16 @@ return (c < 0); } +// returns true if the given character is between between a to z. +inline bool isLowerCaseAlphaChar(const char c) { + return c >= 'a' && c <= 'z'; +} + +// converts the given (expected) lower case character to upper case +inline char alphaCharToUpperCase(const char c) { + return (c - 'a') + 'A'; +} + // custom scanner function which reads from the socket // (bison expects it to return the numerical ID of the next // "recognized token" from the input stream) @@ -81,6 +94,7 @@ const char c = bufptr++; // increment current reading position (just for verbosity / messages) GetCurrentYaccSession()->iColumn++; + sParsed += c; // we have to handle "normal" and "extended" ASCII characters separately if (isExtendedAsciiChar(c)) { // workaround for characters with ASCII code higher than 127 @@ -103,15 +117,76 @@ } -// we provide our own version of yyerror() so we don't have to link against the yacc library -void yyerror(const char* s); - using namespace LinuxSampler; +static std::set<String> yyExpectedSymbols(); + +/** + * Will be called when an error occured (usually syntax error). + * + * We provide our own version of yyerror() so we a) don't have to link against + * the yacc library and b) can render more helpful syntax error messages. + */ +void yyerror(void* x, const char* s) { + yyparse_param_t* param = GetCurrentYaccSession(); + + // get the text part already parsed (of current line) + const bool bContainsLineFeed = + sParsed.find('\r') != std::string::npos || + sParsed.find('\n') != std::string::npos; + // remove potential line feed characters + if (bContainsLineFeed) { + for (size_t p = sParsed.find('\r'); p != std::string::npos; + p = sParsed.find('\r')) sParsed.erase(p); + for (size_t p = sParsed.find('\n'); p != std::string::npos; + p = sParsed.find('\n')) sParsed.erase(p); + } + + // start assembling the error message with Bison's own message + String txt = s; + + // append exact position info of syntax error + txt += (" (line:" + ToString(param->iLine+1)) + + (",column:" + ToString(param->iColumn)) + ")"; + + // append the part of the lined that has already been parsed + txt += ". Context: \"" + sParsed; + if (txt.empty() || bContainsLineFeed) + txt += "^"; + else + txt.insert(txt.size() - 1, "^"); + txt += "...\""; + + // append the non-terminal symbols expected now/next + std::set<String> expectedSymbols = yyExpectedSymbols(); + for (std::set<String>::const_iterator it = expectedSymbols.begin(); + it != expectedSymbols.end(); ++it) + { + if (it == expectedSymbols.begin()) + txt += " -> Should be: " + *it; + else + txt += " | " + *it; + } + + dmsg(2,("LSCPParser: %s\n", txt.c_str())); + sLastError = txt; +} + %} // reentrant parser -%pure_parser +%pure-parser + +%parse-param {void* yyparse_param} + +// After entering the yyparse() function, store references to the parser's +// symbol stack, so that we can create more helpful syntax error messages than +// Bison (2.x) could do. +%initial-action { + yyparse_param_t* p = (yyparse_param_t*) yyparse_param; + p->ppStackBottom = &yyss; + p->ppStackTop = &yyssp; +} // tell bison to spit out verbose syntax error messages %error-verbose @@ -121,7 +196,7 @@ %type <Char> char char_base alpha_char digit digit_oct digit_hex escape_seq escape_seq_octal escape_seq_hex %type <Dotnum> real dotnum volume_value boolean control_value %type <Number> number sampler_channel instrument_index fx_send_id audio_channel_index device_index effect_index effect_instance effect_chain chain_pos input_control midi_input_channel_index midi_input_port_index midi_map midi_bank midi_prog midi_ctrl -%type <String> string string_escaped text text_escaped text_escaped_base stringval stringval_escaped digits param_val_list param_val query_val filename module effect_system db_path map_name entry_name fx_send_name effect_name engine_name command add_instruction create_instruction destroy_instruction get_instruction list_instruction load_instruction send_instruction set_chan_instruction load_instr_args load_engine_args audio_output_type_name midi_input_type_name remove_instruction unmap_instruction set_instruction subscribe_event unsubscribe_event map_instruction reset_instruction clear_instruction find_instruction move_instruction copy_instruction scan_mode edit_instruction format_instruction append_instruction insert_instruction +%type <String> string string_escaped text text_escaped text_escaped_base stringval stringval_escaped digits param_val_list param_val query_val filename module effect_system db_path map_name entry_name fx_send_name effect_name engine_name line statement command add_instruction create_instruction destroy_instruction get_instruction list_instruction load_instruction send_instruction set_chan_instruction load_instr_args load_engine_args audio_output_type_name midi_input_type_name remove_instruction unmap_instruction set_instruction subscribe_event unsubscribe_event map_instruction reset_instruction clear_instruction find_instruction move_instruction copy_instruction scan_mode edit_instruction format_instruction append_instruction insert_instruction %type <FillResponse> buffer_size_type %type <KeyValList> key_val_list query_val_list %type <LoadMode> instr_load_mode @@ -143,14 +218,17 @@ // GRAMMAR_BNF_BEGIN - do NOT delete or modify this line !!! -input : line LF - | line CR LF +input : line { INCREMENT_LINE; if (!$1.empty()) LSCPSERVER->AnswerClient($1); return LSCP_DONE; } + | error { INCREMENT_LINE; LSCPSERVER->AnswerClient("ERR:0:" + sLastError + "\r\n"); RESTART; return LSCP_SYNTAX_ERROR; } ; -line : /* epsilon (empty line ignored) */ { INCREMENT_LINE; return LSCP_DONE; } - | comment { INCREMENT_LINE; return LSCP_DONE; } - | command { INCREMENT_LINE; LSCPSERVER->AnswerClient($1); return LSCP_DONE; } - | error { INCREMENT_LINE; LSCPSERVER->AnswerClient("ERR:0:" + sLastError + "\r\n"); RESTART; return LSCP_SYNTAX_ERROR; } +line : statement LF { $$ = $1; } + | statement CR LF { $$ = $1; } + ; + +statement : /* epsilon (empty statement/line ignored) */ { $$ = ""; } + | comment { $$ = ""; } + | command ; comment : '#' @@ -187,6 +265,8 @@ ; add_instruction : CHANNEL { $$ = LSCPSERVER->AddChannel(); } + | CHANNEL SP MIDI_INPUT SP sampler_channel SP device_index { $$ = LSCPSERVER->AddChannelMidiInput($5,$7); } + | CHANNEL SP MIDI_INPUT SP sampler_channel SP device_index SP midi_input_port_index { $$ = LSCPSERVER->AddChannelMidiInput($5,$7,$9); } | DB_INSTRUMENT_DIRECTORY SP db_path { $$ = LSCPSERVER->AddDbInstrumentDirectory($3); } | DB_INSTRUMENTS SP NON_MODAL SP scan_mode SP db_path SP filename { $$ = LSCPSERVER->AddDbInstruments($5,$7,$9, true); } | DB_INSTRUMENTS SP NON_MODAL SP scan_mode SP FILE_AS_DIR SP db_path SP filename { $$ = LSCPSERVER->AddDbInstruments($5,$9,$11, true, true); } @@ -275,6 +355,9 @@ ; remove_instruction : CHANNEL SP sampler_channel { $$ = LSCPSERVER->RemoveChannel($3); } + | CHANNEL SP MIDI_INPUT SP sampler_channel { $$ = LSCPSERVER->RemoveChannelMidiInput($5); } + | CHANNEL SP MIDI_INPUT SP sampler_channel SP device_index { $$ = LSCPSERVER->RemoveChannelMidiInput($5,$7); } + | CHANNEL SP MIDI_INPUT SP sampler_channel SP device_index SP midi_input_port_index { $$ = LSCPSERVER->RemoveChannelMidiInput($5,$7,$9); } | MIDI_INSTRUMENT_MAP SP midi_map { $$ = LSCPSERVER->RemoveMidiInstrumentMap($3); } | MIDI_INSTRUMENT_MAP SP ALL { $$ = LSCPSERVER->RemoveAllMidiInstrumentMaps(); } | SEND_EFFECT_CHAIN SP device_index SP effect_chain { $$ = LSCPSERVER->RemoveSendEffectChain($3,$5); } @@ -359,6 +442,9 @@ | DB_INSTRUMENT SP DESCRIPTION SP db_path SP stringval_escaped { $$ = LSCPSERVER->SetDbInstrumentDescription($5,$7); } | DB_INSTRUMENT SP FILE_PATH SP filename SP filename { $$ = LSCPSERVER->SetDbInstrumentFilePath($5,$7); } | ECHO SP boolean { $$ = LSCPSERVER->SetEcho((yyparse_param_t*) yyparse_param, $3); } + | SHELL SP INTERACT SP boolean { $$ = LSCPSERVER->SetShellInteract((yyparse_param_t*) yyparse_param, $5); } + | SHELL SP AUTO_CORRECT SP boolean { $$ = LSCPSERVER->SetShellAutoCorrect((yyparse_param_t*) yyparse_param, $5); } + | SHELL SP DOC SP boolean { $$ = LSCPSERVER->SetShellDoc((yyparse_param_t*) yyparse_param, $5); } | VOLUME SP volume_value { $$ = LSCPSERVER->SetGlobalVolume($3); } | VOICES SP number { $$ = LSCPSERVER->SetGlobalMaxVoices($3); } | STREAMS SP number { $$ = LSCPSERVER->SetGlobalMaxStreams($3); } @@ -449,6 +535,7 @@ list_instruction : AUDIO_OUTPUT_DEVICES { $$ = LSCPSERVER->GetAudioOutputDevices(); } | MIDI_INPUT_DEVICES { $$ = LSCPSERVER->GetMidiInputDevices(); } | CHANNELS { $$ = LSCPSERVER->ListChannels(); } + | CHANNEL SP MIDI_INPUTS SP sampler_channel { $$ = LSCPSERVER->ListChannelMidiInputs($5); } | AVAILABLE_ENGINES { $$ = LSCPSERVER->ListAvailableEngines(); } | AVAILABLE_EFFECTS { $$ = LSCPSERVER->ListAvailableEffects(); } | EFFECT_INSTANCES { $$ = LSCPSERVER->ListEffectInstances(); } @@ -850,6 +937,15 @@ SET : 'S''E''T' ; +SHELL : 'S''H''E''L''L' + ; + +INTERACT : 'I''N''T''E''R''A''C''T' + ; + +AUTO_CORRECT : 'A''U''T''O''_''C''O''R''R''E''C''T' + ; + APPEND : 'A''P''P''E''N''D' ; @@ -1078,6 +1174,9 @@ MIDI_INPUT : 'M''I''D''I''_''I''N''P''U''T' ; +MIDI_INPUTS : 'M''I''D''I''_''I''N''P''U''T''S' + ; + MIDI_CONTROLLER : 'M''I''D''I''_''C''O''N''T''R''O''L''L''E''R' ; @@ -1183,26 +1282,851 @@ ECHO : 'E''C''H''O' ; +DOC : 'D''O''C' + ; + QUIT : 'Q''U''I''T' ; %% +// TODO: actually would be fine to have the following bunch of source code in a separate file, however those functions are a) accessing private Bison tables like yytable and b) including the functions from another file here would make the line numbers incorrect on compile errors in auto generated lscpparser.cpp + /** - * Will be called when an error occured (usually syntax error). + * Additional informations of a grammar symbol. + */ +struct BisonSymbolInfo { + bool isTerminalSymbol; ///< Whether the symbol is a terminal or non-termianl symbol. NOTE: Read comment regarding this in _isRuleTerminalSymbol() !! + String nextExpectedChars; ///< According to current parser position: sequence of characters expected next for satisfying this grammar symbol. +}; + +#if HAVE_BISON_MAJ >= 3 // Bison 3.x or younger ... + +/** + * Must ONLY be called just before a so called "reduce" parser action: + * Returns true if the grammar rule, which is just about to be "reduced", is a + * terminal symbol (in *our* terms). + * + * Please note that the term "terminal symbol" is a bit confusingly used in + * this source code here around. In Bison's terms, "terminal symbols" are (more + * or less) just the numbers returned by the YYLEX function. Since we decided + * though to use a convenient solution without a separate lexer, and all its + * caveats, all numbers by the yylex() function here are just the ASCII + * numbers of the individual characters received. Based on that however, one + * single character is not what one would intuitively expect of being a + * "terminal symbol", because it is simply too primitive. + * + * So in this LSCP parser source code a "terminal symbol" rather means a + * keyword like "CREATE" or "GET". In the grammal definition above, those are + * however defined as grammar rules (non-terminals in Bison's terms). So this + * function decides like this: if the given grammar rule just contains + * individual characters on the right side of its grammar rule, then it is a + * "terminal symbol" in *our* terms. + * + * @param rule - Bison grammar rule number + * @param stack - reflecting current Bison parser state */ -void yyerror(const char* s) { +inline static bool _isRuleTerminalSymbol(int rule, const std::vector<YYTYPE_INT16>& stack) { + int nrhs = yyr2rule; + for (int i = 0; i < nrhs; ++i) + if (yystos*(stack.end() - nrhs + i) >= YYNTOKENS) return false; + return true; +} + +/** + * Must ONLY be called just before a so called "reduce" parser action: Returns + * additional informations to the given grammar rule that is about to be + * "reduced". + * + * @param rule - Bison grammar rule number + * @param stack - reflecting current Bison parser state + * @param nextExpectedChars - must already be filled with the characters + * expected to be coming next + */ +inline static BisonSymbolInfo _symbolInfoForRule(int rule, const std::vector<YYTYPE_INT16>& stack, const String& nextExpectedChars) { + BisonSymbolInfo info; + info.isTerminalSymbol = _isRuleTerminalSymbol(rule, stack); + if (info.isTerminalSymbol) info.nextExpectedChars = nextExpectedChars; + return info; +} + +#else // Bison 2.x or older ... + +//TODO: The Bison 2.x code below can probably soon just be deleted. Most Bisonx 2.x versions should be able to compile successfully with the Bison 3.x code above as well (just requires the existence of table yystos in the auto generated lscpparser.cpp). + +/** + * Returns true if the given grammar @a rule is a terminal symbol (in *our* + * terms). + * + * Please note that the term "terminal symbol" is a bit confusingly used in + * this source code here around. In Bison's terms, "terminal symbols" are (more + * or less) just the numbers returned by the YYLEX function. Since we decided + * though to use a convenient solution without a separate lexer, and all its + * caveats, all numbers by the yylex() function here are just the ASCII + * numbers of the individual characters received. Based on that however, one + * single character is not what one would intuitively expect of being a + * "terminal symbol", because it is simply too primitive. + * + * So in this LSCP parser source code a "terminal symbol" rather means a + * keyword like "CREATE" or "GET". In the grammal definition above, those are + * however defined as grammar rules (non-terminals in Bison's terms). So this + * function decides like this: if the given grammar rule just contains + * individual characters on the right side of its grammar rule, then it is a + * "terminal symbol" in *our* terms. + * + * @param rule - Bison grammar rule number + */ +inline static bool _isRuleTerminalSymbol(int rule) { + for (int i = yyprhsrule; yyrhsi != -1; ++i) + if (yyrhsi >= YYNTOKENS) return false; + return true; +} + +/** + * Returns additional informations to the given grammar @a rule. + * + * @param rule - grammar rule index to retrieve informations about + * @param nextExpectedChars - must already be filled with the characters + * expected to be coming next + */ +inline static BisonSymbolInfo _symbolInfoForRule(int rule, const String& nextExpectedChars) { + BisonSymbolInfo info; + info.isTerminalSymbol = _isRuleTerminalSymbol(rule); + if (info.isTerminalSymbol) info.nextExpectedChars = nextExpectedChars; + return info; +} + +#endif // HAVE_BISON_MAJ >= 3 + +/** + * Returns the human readable name of the given @a token. + */ +inline static String _tokenName(int token) { + String s = yytnametoken; + // remove leading and trailing apostrophes that Bison usually adds to + // ASCII characters used directly in grammar rules + if (s.empty()) return s; + if (s0 == '\'') s.erase(0, 1); + if (s.empty()) return s; + if (ss.size() - 1 == '\'') s.erase(s.size() - 1); + return s; +} + +/** + * Assumes the given @a token is exactly one character and returns that + * character. This must be changed in future, i.e. in case Unicode characters + * will be introduced in the LSCP grammar one day. + */ +inline static char _tokenChar(int token) { + String s = _tokenName(token); + if (s == "\\n") return '\n'; + if (s == "\\r") return '\r'; + return _tokenName(token)0; +} + +/** + * Implements Bison's so called "reduce" action, according to Bison's LALR(1) + * parser algorithm. + */ +inline static int _yyReduce(std::vector<YYTYPE_INT16>& stack, const int& rule) { + if (stack.empty()) throw 1; // severe error + const int len = yyr2rule; + stack.resize(stack.size() - len); + YYTYPE_INT16 newState = yypgotoyyr1rule - YYNTOKENS + stack.back(); + if (0 <= newState && newState <= YYLAST && yychecknewState == stack.back()) + newState = yytablenewState; + else + newState = yydefgotoyyr1rule - YYNTOKENS; + stack.push_back(newState); + return newState; +} + +/** + * Implements Bison's so called "default reduce" action, according to Bison's + * LALR(1) parser algorithm. + */ +inline static int _yyDefaultReduce(std::vector<YYTYPE_INT16>& stack) { + if (stack.empty()) throw 2; // severe error + int rule = yydefactstack.back(); + if (rule <= 0 || rule >= YYNRULES) throw 3; // no rule, something is wrong + return _yyReduce(stack, rule); +} + +static bool yyValid(std::vector<YYTYPE_INT16>& stack, char ch); + +/** + * A set of parser symbol stacks. This type is used for the recursive algorithms + * in a) yyAutoComplete() and b) walkAndFillExpectedSymbols() for detecting + * endless recursions. + * + * This unique container is used to keep track of all previous parser states + * (stacks), for detecting a parser symbol stack that has already been + * encountered before. Because if yyAutoComplete() or + * walkAndFillExpectedSymbols() reach the exactly same parser symbol stack + * again, that means there is an endless recursion in that part of the grammar + * tree branch and shall not be evaluated any further, since it would end up in + * an endless loop of the algorithm otherwise. + * + * This solution consumes a lot of memory, but unfortunately there is no other + * easy way to solve it. With our grammar and today's usual memory heap size & + * memory stack size in modern devices, it should be fine though. + */ +typedef std::set< std::vector<YYTYPE_INT16> > YYStackHistory; + +#define DEBUG_BISON_SYNTAX_ERROR_WALKER 0 + +/** + * Tries to find the next expected grammar symbols according to the given + * precise parse position & state represented by @a stack, according to Bison's + * LALR(1) parser algorithm. + * + * This function is given a Bison parser symbol stack, reflecting the parser's + * entire state at a certain point, i.e. when a syntax error occured. This + * function will then walk ahead the potential parse tree starting from the + * current head of the given symbol stack. This function will call itself + * recursively to scan the individual parse tree branches. As soon as it hits + * on the next non-terminal grammar symbol in one parse tree branch, it adds the + * found non-terminal symbol to @a expectedSymbols and aborts scanning the + * respective tree branch further. If any local parser state is reached a second + * time, the respective parse tree is aborted to avoid any endless recursion. + * + * @param stack - current Bison (yacc) symbol stack to be examined + * @param expectedSymbols - will be filled with next expected grammar symbols + * @param nextExpectedChars - just for internal purpose, due to the recursive + * implementation of this function, do supply an + * empty string for this argument + * @param history - only for internal purpose, keeps a history of all previous + * parser symbol stacks (just for avoiding endless recursion in + * this recursive algorithm), do supply an empty history + * @param depth - just for internal debugging purposes, do not supply it + */ +static void walkAndFillExpectedSymbols( + std::vector<YYTYPE_INT16>& stack, + std::map<String,BisonSymbolInfo>& expectedSymbols, + String& nextExpectedChars, YYStackHistory& history, int depth = 0) +{ +#if DEBUG_BISON_SYNTAX_ERROR_WALKER + printf("\n"); + for (int i = 0; i < depth; ++i) printf("\t"); + printf("Symbol stack:"); + for (int i = 0; i < stack.size(); ++i) { + printf(" %d", stacki); + } + printf("\n"); +#endif + startLabel: + + // detect endless recursion + if (history.count(stack)) return; + history.insert(stack); + + if (stack.empty()) { +#if DEBUG_BISON_SYNTAX_ERROR_WALKER + for (int i = 0; i < depth; ++i) printf("\t"); + printf("(EMPTY STACK)\n"); +#endif + return; + } + + int state = stackstack.size() - 1; + int n = yypactstate; + if (n == YYPACT_NINF) { // default reduction required ... + // get default reduction rule for this state + n = yydefactstate; + if (n <= 0 || n >= YYNRULES) { +#if DEBUG_BISON_SYNTAX_ERROR_WALKER + for (int i = 0; i < depth; ++i) printf("\t"); + printf("(EMPTY RULE)\n"); +#endif + return; // no rule, something is wrong + } +#if DEBUG_BISON_SYNTAX_ERROR_WALKER + for (int i = 0; i < depth; ++i) printf("\t"); + printf("(default reduction)\n"); +#endif + #if HAVE_BISON_MAJ >= 3 + if (!nextExpectedChars.empty() || !_isRuleTerminalSymbol(n, stack)) { + #else + if (!nextExpectedChars.empty() || !_isRuleTerminalSymbol(n)) { + #endif + // Return the new resolved expected symbol (left-hand symbol of grammar + // rule), then we're done in this state. (If the same symbol can be + // matched on different ways, then it is non-terminal symbol.) + bool ambigious = + expectedSymbols.count(yytnameyyr1n) && + expectedSymbolsyytnameyyr1n.nextExpectedChars != nextExpectedChars; + #if HAVE_BISON_MAJ >= 3 + expectedSymbolsyytnameyyr1n = _symbolInfoForRule(n, stack, nextExpectedChars); + #else + expectedSymbolsyytnameyyr1n = _symbolInfoForRule(n, nextExpectedChars); + #endif + if (ambigious) + expectedSymbolsyytnameyyr1n.isTerminalSymbol = false; +#if DEBUG_BISON_SYNTAX_ERROR_WALKER + for (int i = 0; i < depth; ++i) printf("\t"); + printf("(empty expectedChars. sym = %s)\n", yytnameyyr1n); +#endif + return; + } + _yyReduce(stack, n); + goto startLabel; + } + if (!(YYPACT_NINF < n && n <= YYLAST)) { +#if DEBUG_BISON_SYNTAX_ERROR_WALKER + for (int i = 0; i < depth; ++i) printf("\t"); + printf("(invalid action B)\n"); +#endif + return; + } + + // Check for duplicate states, if duplicates exist return + // (this check is necessary since the introduction of the yyValid() call + // below, which does not care about duplicates). + for (int i = 0; i < stack.size(); ++i) + for (int k = i + 1; k < stack.size(); ++k) + if (stacki == stackk) + return; + +#if DEBUG_BISON_SYNTAX_ERROR_WALKER + for (int i = 0; i < depth; ++i) printf("\t"); + printf("Expected tokens:"); +#endif + int begin = n < 0 ? -n : 0; + //int checklim = YYLAST - n + 1; + int end = YYNTOKENS;//checklim < YYNTOKENS ? checklim : YYNTOKENS; + int rule, action, stackSize, nextExpectedCharsLen; + for (int token = begin; token < end; ++token) { + if (token <= YYTERROR) continue; + if (yytnametoken == String("$undefined")) continue; + if (yytnametoken == String("EXT_ASCII_CHAR")) continue; + //if (yycheckn + token != token) goto default_reduction; + if (yycheckn + token != token) { // default reduction suggested ... + // If we are here, it means the current token in the loop would not + // cause a "shift", however we don't already know whether this token + // is valid or not. Because there might be several reductions + // involved until one can determine whether the token causes an + // error or is valid. So we use this heavy check instead: + std::vector<YYTYPE_INT16> stackCopy = stack; // copy required, since reduction will take place + if (!yyValid(stackCopy, _tokenChar(token))) continue; // invalid token +#if DEBUG_BISON_SYNTAX_ERROR_WALKER + printf(" ETdr(%s)", yytnametoken); +#endif + // the token is valid, "stackCopy" has been reduced accordingly + // and now do recurse ... + nextExpectedChars += _tokenName(token); + nextExpectedCharsLen = nextExpectedChars.size(); + walkAndFillExpectedSymbols( //FIXME: could cause stack overflow (should be a loop instead), is probably fine with our current grammar though + stackCopy, expectedSymbols, nextExpectedChars, history, depth + 1 + ); + nextExpectedChars.resize(nextExpectedCharsLen); // restore 'nextExpectedChars' + continue; + } +#if DEBUG_BISON_SYNTAX_ERROR_WALKER + printf(" ET(%s)", yytnametoken); +#endif + + action = yytablen + token; + if (action == 0 || action == YYTABLE_NINF) { +#if DEBUG_BISON_SYNTAX_ERROR_WALKER + printf(" (invalid action A) "); fflush(stdout); +#endif + continue; // error, ignore + } + if (action < 0) { // reduction with rule -action required ... +#if DEBUG_BISON_SYNTAX_ERROR_WALKER + printf(" (reduction) "); fflush(stdout); +#endif + rule = -action; + goto reduce; + } + if (action == YYFINAL) { +#if DEBUG_BISON_SYNTAX_ERROR_WALKER + printf(" (ACCEPT) "); fflush(stdout); +#endif + continue; // "accept" state, we don't care about it here + } + + // "shift" required ... + + if (std::find(stack.begin(), stack.end(), action) != stack.end()) { +#if DEBUG_BISON_SYNTAX_ERROR_WALKER + printf(" (duplicate state %d) ", action); fflush(stdout); +#endif + continue; // duplicate state, ignore it to avoid endless recursions + } + + // "shift" / push the new state on the symbol stack and call this + // function recursively, and restore the stack after the recurse return + stackSize = stack.size(); + nextExpectedCharsLen = nextExpectedChars.size(); + stack.push_back(action); + nextExpectedChars += _tokenName(token); + walkAndFillExpectedSymbols( //FIXME: could cause stack overflow (should be a loop instead), is probably fine with our current grammar though + stack, expectedSymbols, nextExpectedChars, history, depth + 1 + ); + stack.resize(stackSize); // restore stack + nextExpectedChars.resize(nextExpectedCharsLen); // restore 'nextExpectedChars' + continue; + + //default_reduction: // resolve default reduction for this state + // printf(" (default red.) "); fflush(stdout); + // rule = yydefactstate; + + reduce: // "reduce" required +#if DEBUG_BISON_SYNTAX_ERROR_WALKER + printf(" (reduce by %d) ", rule); fflush(stdout); +#endif + if (rule == 0 || rule >= YYNRULES) { +#if DEBUG_BISON_SYNTAX_ERROR_WALKER + printf(" (invalid rule) "); fflush(stdout); +#endif + continue; // invalid rule, something is wrong + } + // Store the left-hand symbol of the grammar rule. (If the same symbol + // can be matched on different ways, then it is non-terminal symbol.) + bool ambigious = + expectedSymbols.count(yytnameyyr1rule) && + expectedSymbolsyytnameyyr1rule.nextExpectedChars != nextExpectedChars; + #if HAVE_BISON_MAJ >= 3 + expectedSymbolsyytnameyyr1rule = _symbolInfoForRule(rule, stack, nextExpectedChars); + #else + expectedSymbolsyytnameyyr1rule = _symbolInfoForRule(rule, nextExpectedChars); + #endif + if (ambigious) + expectedSymbolsyytnameyyr1n.isTerminalSymbol = false; +#if DEBUG_BISON_SYNTAX_ERROR_WALKER + printf(" (SYM %s) ", yytnameyyr1rule); fflush(stdout); +#endif + } +#if DEBUG_BISON_SYNTAX_ERROR_WALKER + printf("\n"); +#endif +} + +/** + * Just a convenience wrapper on top of the actual walkAndFillExpectedSymbols() + * implementation above, which can be called with less parameters than the + * implementing function above actually requires. + */ +static void walkAndFillExpectedSymbols( + std::vector<YYTYPE_INT16>& stack, + std::map<String,BisonSymbolInfo>& expectedSymbols) +{ + String nextExpectedChars; + YYStackHistory history; + + walkAndFillExpectedSymbols( + stack, expectedSymbols, nextExpectedChars, history + ); +} + +#define DEBUG_PUSH_PARSE 0 + +/** + * Implements parsing exactly one character (given by @a ch), continueing at the + * parser position reflected by @a stack. The @a stack will hold the new parser + * state after this call. + * + * This function is implemented according to Bison's LALR(1) parser algorithm. + */ +static bool yyPushParse(std::vector<YYTYPE_INT16>& stack, char ch) { + startLabel: + +#if DEBUG_PUSH_PARSE + //printf("\n"); + //for (int i = 0; i < depth; ++i) printf("\t"); + printf("Symbol stack:"); + for (int i = 0; i < stack.size(); ++i) { + printf(" %d", stacki); + } + printf(" char='%c'(%d)\n", ch, (int)ch); +#endif + + if (stack.empty()) return false; + + int state = stack.back(); + int n = yypactstate; + if (n == YYPACT_NINF) { // default reduction required ... +#if DEBUG_PUSH_PARSE + printf("(def reduce 1)\n"); +#endif + state = _yyDefaultReduce(stack); + goto startLabel; + } + if (!(YYPACT_NINF < n && n <= YYLAST)) return false; + + YYTYPE_INT16 token = (ch == YYEOF) ? YYEOF : yytranslatech; + n += token; + if (n < 0 || YYLAST < n || yycheckn != token) { +#if DEBUG_PUSH_PARSE + printf("(def reduce 2) n=%d token=%d\n", n, token); +#endif + state = _yyDefaultReduce(stack); + goto startLabel; + } + int action = yytablen; // yytableyypactstate + token + if (action == 0 || action == YYTABLE_NINF) throw 4; + if (action < 0) { +#if DEBUG_PUSH_PARSE + printf("(reduce)\n"); +#endif + int rule = -action; + state = _yyReduce(stack, rule); + goto startLabel; + } + if (action == YYFINAL) return true; // final state reached + +#if DEBUG_PUSH_PARSE + printf("(push)\n"); +#endif + // push new state + state = action; + stack.push_back(state); + return true; +} + +/** + * Returns true if parsing ahead with given character @a ch is syntactically + * valid according to the LSCP grammar, it returns false if it would create a + * parse error. + * + * The @a stack will reflect the new parser state after this call. + * + * This is just a wrapper ontop of yyPushParse() which converts parser + * exceptions thrown by yyPushParse() into @c false return value. + */ +static bool yyValid(std::vector<YYTYPE_INT16>& stack, char ch) { + try { + return yyPushParse(stack, ch); + } catch (int i) { +#if DEBUG_PUSH_PARSE + printf("exception %d\n", i); +#endif + return false; + } catch (...) { + return false; + } +} + +/** + * Returns the amount of correct characters of given @a line from the left, + * according to the LSCP grammar. + * + * @param stack - a Bison symbol stack to work with + * @param line - the input line to check + * @param bAutoCorrect - if true: try to correct obvious, trivial syntax errors + */ +static int yyValidCharacters(std::vector<YYTYPE_INT16>& stack, String& line, bool bAutoCorrect) { + int i; + for (i = 0; i < line.size(); ++i) { + // since we might check the same parser state twice against the current + // char here below, and since the symbol stack might be altered + // (i.e. shifted or reduced) on syntax errors, we have to backup the + // current symbol stack and restore it on syntax errors below + std::vector<YYTYPE_INT16> stackCopy = stack; + if (yyValid(stackCopy, linei)) { + stack = stackCopy; + continue; + } + if (bAutoCorrect) { + // try trivial corrections, i.e. upper case character instead of + // lower case, subline instead of space and vice versa + char c; + if (linei == ' ') c = '_'; + else if (linei == '_') c = ' '; + else if (isLowerCaseAlphaChar(linei)) + c = alphaCharToUpperCase(linei); + else return i; + if (yyValid(stack, c)) { + linei = c; + continue; + } + } + return i; + } + return i; +} + +/** + * Should only be called on syntax errors: returns a set of non-terminal + * symbols expected to appear now/next, just at the point where the syntax + * error appeared. + * + * @returns names of the non-terminal symbols expected at this parse position + */ +static std::set<String> yyExpectedSymbols() { + std::map<String,BisonSymbolInfo> expectedSymbols; yyparse_param_t* param = GetCurrentYaccSession(); - String msg = s - + (" (line:" + ToString(param->iLine+1)) - + ( ",column:" + ToString(param->iColumn)) - + ")"; - dmsg(2,("LSCPParser: %s\n", msg.c_str())); - sLastError = msg; + YYTYPE_INT16* ss = (*param->ppStackBottom); + YYTYPE_INT16* sp = (*param->ppStackTop); + int iStackSize = sp - ss + 1; + // copy and wrap parser's symbol stack into a convenient STL container + std::vector<YYTYPE_INT16> stack; + for (int i = 0; i < iStackSize; ++i) { + stack.push_back(ssi); + } + // do the actual parser work + walkAndFillExpectedSymbols(stack, expectedSymbols); + + // convert expectedSymbols to the result set + std::set<String> result; + for (std::map<String,BisonSymbolInfo>::const_iterator it = expectedSymbols.begin(); + it != expectedSymbols.end(); ++it) result.insert(it->first); + return result; +} + +#define DEBUG_YY_AUTO_COMPLETE 0 + +/** + * Generates and returns an auto completion string for the current parser + * state given by @a stack. That means, this function will return the longest + * sequence of characters that is uniqueley expected to be sent next by the LSCP + * client. Or in other words, if the LSCP client would send any other + * character(s) than returned here, it would result in a syntax error. + * + * This function takes a Bison symbol @a stack as argument, reflecting the + * current Bison parser state, and evaluates the individual grammar tree + * branches starting from that particular position. It walks along the grammar + * tree as long as there is only one possible tree branch and assembles a string + * of input characters that would lead to that walk through the grammar tree. As + * soon as a position in the grammar tree is reached where there are multiple + * possible tree branches, this algorithm will stop, since the user could have + * multiple possible valid characters he could type at that point, thus auto + * completion would no longer be unique at that point. + * + * Regarding @a history argument: read the description on YYStackHistory for the + * purpose behind this argument. + * + * @param stack - current Bison (yacc) symbol stack to create auto completion for + * @param history - only for internal purpose, keeps a history of all previous + * parser symbol stacks (just for avoiding endless recursion in + * this auto completion algorithm), do supply an empty history + * @param depth - just for internal debugging purposes, do not supply anything + * @returns auto completion for current, given parser state + */ +static String yyAutoComplete(std::vector<YYTYPE_INT16>& stack, YYStackHistory& history, int depth = 0) { + std::map<String,BisonSymbolInfo> expectedSymbols; + walkAndFillExpectedSymbols(stack, expectedSymbols); + if (expectedSymbols.size() == 1) { + String name = expectedSymbols.begin()->first; + BisonSymbolInfo info = expectedSymbols.begin()->second; +#if DEBUG_YY_AUTO_COMPLETE + for (int q = 0; q < depth; ++q) printf(" "); + printf("(%d) Suggested Sub Completion (sz=%d): type=%s %s -> '%s'\n", depth, expectedSymbols.size(), (info.isTerminalSymbol) ? "T" : "NT", name.c_str(), info.nextExpectedChars.c_str()); +#endif + if (info.nextExpectedChars.empty() || !info.isTerminalSymbol) return ""; + // parse forward with the suggested auto completion + std::vector<YYTYPE_INT16> stackCopy = stack; + yyValidCharacters(stackCopy, info.nextExpectedChars, false); + // detect endless recursion + if (history.count(stackCopy)) return ""; + history.insert(stackCopy); + // recurse and return the expanded auto completion with maximum length + return info.nextExpectedChars + yyAutoComplete(stackCopy, history, depth + 1); + } else if (expectedSymbols.size() == 0) { +#if DEBUG_YY_AUTO_COMPLETE + for (int q = 0; q < depth; ++q) printf(" "); + printf("(%d) No sub suggestion.\n", depth); +#endif + return ""; + } else if (expectedSymbols.size() > 1) { +#if DEBUG_YY_AUTO_COMPLETE + for (int q = 0; q < depth; ++q) printf(" "); + printf("(%d) Multiple sub possibilities (before expansion):", depth); + for (std::map<String,BisonSymbolInfo>::const_iterator it = expectedSymbols.begin(); + it != expectedSymbols.end(); ++it) + { + printf(" %s (..%s)", it->first.c_str(), it->second.nextExpectedChars.c_str()); + } + printf("\n"); +#endif + // check if any of the possibilites is a non-terminal symbol, if so, we + // have no way for auto completion at this point + for (std::map<String,BisonSymbolInfo>::const_iterator it = expectedSymbols.begin(); + it != expectedSymbols.end(); ++it) + { + if (!it->second.isTerminalSymbol) { +#if DEBUG_YY_AUTO_COMPLETE + for (int q = 0; q < depth; ++q) printf(" "); + printf("(%d) Non-terminal exists. Stop.", depth); +#endif + return ""; + } + } +#if 0 // commented out for now, since practically irrelevant and VERY slow ... + // all possibilities are terminal symbols, so expand all possiblities to + // maximum length with a recursive call for each possibility + for (std::map<String,BisonSymbolInfo>::iterator it = expectedSymbols.begin(); + it != expectedSymbols.end(); ++it) + { + if (it->second.nextExpectedChars.empty() || !it->second.isTerminalSymbol) continue; + // parse forward with this particular suggested auto completion + std::vector<YYTYPE_INT16> stackCopy = stack; + yyValidCharacters(stackCopy, it->second.nextExpectedChars, false); + // detect endless recursion + if (history.count(stackCopy)) continue; + history.insert(stackCopy); + // recurse and return the total possible auto completion for this + // grammar tree branch + it->second.nextExpectedChars += yyAutoComplete(stackCopy, history, depth + 1); + } +#endif + // try to find the longest common string all possibilities start with + // (from the left) + String sCommon; + for (int i = 0; true; ++i) { + char c; + for (std::map<String,BisonSymbolInfo>::const_iterator it = expectedSymbols.begin(); + it != expectedSymbols.end(); ++it) + { + if (i >= it->second.nextExpectedChars.size()) + goto commonSearchEndLabel; + if (it == expectedSymbols.begin()) + c = it->second.nextExpectedCharsi; + if (c != it->second.nextExpectedCharsi) + goto commonSearchEndLabel; + if (it == --expectedSymbols.end()) + sCommon += c; + } + } + commonSearchEndLabel: +#if DEBUG_YY_AUTO_COMPLETE + for (int q = 0; q < depth; ++q) printf(" "); + printf("(%d) Multiple sub possibilities (after expansion):", depth); + for (std::map<String,BisonSymbolInfo>::const_iterator it = expectedSymbols.begin(); + it != expectedSymbols.end(); ++it) + { + printf(" %s (..%s)", it->first.c_str(), it->second.nextExpectedChars.c_str()); + } + printf("\n"); + for (int q = 0; q < depth; ++q) printf(" "); + printf("(%d) Common sub possibility: '%s'\n", depth, sCommon.c_str()); +#endif + return sCommon; + } + return ""; // just pro forma, should never happen though +} + +/** + * Just a convenience wrapper on top of the actual yyAutoComplete() + * implementation. See its description above for details. + */ +static String yyAutoComplete(std::vector<YYTYPE_INT16>& stack) { + YYStackHistory history; + return yyAutoComplete(stack, history); } namespace LinuxSampler { +#define DEBUG_SHELL_INTERACTION 0 + +/** + * If LSCP shell mode is enabled for the respective LSCP client connection, then + * this function is called on every new byte received from that client. It will + * check the current total input line and reply to the LSCP shell with a + * specially crafted string, which allows the shell to provide colored syntax + * highlighting and potential auto completion in the shell. + * + * It also performs auto correction of obvious & trivial syntax mistakes if + * requested. + * + * The return value of this function will be sent to the client. It contains one + * line specially formatted for the LSCP shell application, which can easily be + * processed by the client/shell for extracting its necessary informations like + * which part of the current command line is syntactically correct, which part + * is incorrect, what could be auto completed right now, etc. So all the heavy + * grammar evaluation tasks are peformed by the LSCP server for the LSCP shell + * application (which is desgined as a thin client), so the LSCP shell + * application will only have to show the results of the LSCP server's + * evaluation to the user on the screen. + * + * @param line - the current command line to be evaluated by LSCP parser + * @param param = reentrant parser session parameters + * @param possibilities - whether all possibilities shall be shown + * @returns LSCP shell response line to be returned to the client + */ +String lscpParserProcessShellInteraction(String& line, yyparse_param_t* param, bool possibilities) { + // first, determine how many characters (starting from the left) of the + // given input line are already syntactically correct + std::vector<YYTYPE_INT16> stack; + stack.push_back(0); // every Bison symbol stack starts with state zero + String l = line + '\n'; // '\n' to pretend ENTER as if the line was now complete + int n = yyValidCharacters(stack, l, param->bShellAutoCorrect); + + // if auto correction is enabled, apply the auto corrected string to + // intput/output string 'line' + if (param->bShellAutoCorrect) { + int nMin = (n < line.length()) ? n : line.length(); + line.replace(0, nMin, l.substr(0, nMin)); + } + + size_t cursorPos = line.size() + param->iCursorOffset; + if (cursorPos < 0) cursorPos = 0; + + // generate an info string that will be sent to the LSCP shell for letting + // it know which part is correct, which one is wrong, where is the cursor, etc. + String result = line; + result.insert(n <= result.length() ? n : result.length(), LSCP_SHK_GOOD_FRONT); + result.insert(cursorPos <= n ? cursorPos : cursorPos + String(LSCP_SHK_GOOD_FRONT).length(), LSCP_SHK_CURSOR); + int code = (n > line.length()) ? LSCP_SHU_COMPLETE : (n < line.length()) ? + LSCP_SHU_SYNTAX_ERR : LSCP_SHU_INCOMPLETE; + result = "SHU:" + ToString(code) + ":" + result; + //if (n > line.length()) result += " OK"; + + // get a clean parser stack to the last valid parse position + // (due to the appended '\n' character above, and on syntax errors, the + // symbol stack might be in undesired, i.e. reduced state) + stack.clear(); + stack.push_back(0); // every Bison symbol stack starts with state zero + l = line.substr(0, n); + if (!l.empty()) yyValidCharacters(stack, l, param->bShellAutoCorrect); + + // generate auto completion suggestion (based on the current parser stack) + std::vector<YYTYPE_INT16> stackCopy = stack; // make a copy, since yyAutoComplete() might alter the stack + String sSuggestion = yyAutoComplete(stackCopy); + if (!sSuggestion.empty()) result += LSCP_SHK_SUGGEST_BACK + sSuggestion; + + if (possibilities) { + // append all possible terminals and non-terminals according to + // current parser state + std::map<String,BisonSymbolInfo> expectedSymbols; + walkAndFillExpectedSymbols(stack, expectedSymbols); + + // pretend to LSCP shell that the following terminal symbols were + // non-terminal symbols (since they are not human visible for auto + // completion on the shell's screen) + std::set<String> specialNonTerminals; + specialNonTerminals.insert("SP"); + specialNonTerminals.insert("CR"); + specialNonTerminals.insert("LF"); + + String sPossibilities; + int iNonTerminals = 0; + int iTerminals = 0; + for (std::map<String,BisonSymbolInfo>::const_iterator it = expectedSymbols.begin(); + it != expectedSymbols.end(); ++it) + { + if (!sPossibilities.empty()) sPossibilities += " | "; + if (it->second.isTerminalSymbol && !specialNonTerminals.count(it->first)) { + sPossibilities += it->first; + iTerminals++; + } else { + sPossibilities += "<" + it->first + ">"; + iNonTerminals++; + } + } + if (!sPossibilities.empty() && (iNonTerminals || iTerminals > 1)) { + result += LSCP_SHK_POSSIBILITIES_BACK + sPossibilities; + } + } + +#if DEBUG_SHELL_INTERACTION + printf("%s\n", result.c_str()); +#endif + + return result; +} + /** * Clears input buffer. */ @@ -1210,6 +2134,7 @@ bytes = 0; ptr = 0; sLastError = ""; + sParsed = ""; } }
View file
linuxsampler-2718.tar.bz2/src/network/lscp_shell_reference.h
Added
@@ -0,0 +1,34 @@ +/***************************************************************************** + * * + * LSCP documentation reference built into LSCP shell. * + * * + * Copyright (c) 2014 Christian Schoenebeck * + * * + * This program is part of LinuxSampler and released under the same terms. * + * * + *****************************************************************************/ + +#ifndef LSCP_SHELL_REFERENCE_H +#define LSCP_SHELL_REFERENCE_H + +/** + * Encapsulates the reference documentation for exactly one LSCP command. + */ +struct lscp_ref_entry_t { + const char* name; ///< shortened name of the LSCP command, acting as unique key (i.e. "CREATE AUDIO_OUTPUT_DEVICE"). + const char* section; ///< Multi-line text block, being the actual documentation section of that LSCP command. +}; + +/** + * Returns the LSCP documentation reference section for the LSCP command given + * by @a cmd. A non complete LSCP command may be passed to this function. In the + * latter case this function will try to "guess" the LSCP command. If there is + * no unique match for the given LSCP command, then NULL is returned. + * + * @param cmd - LSCP command + * @returns LSCP reference section for given LSCP command or NULL if there is + * no unique match + */ +lscp_ref_entry_t* lscp_reference_for_command(const char* cmd); + +#endif // LSCP_SHELL_REFERENCE_H
View file
linuxsampler-2342.tar.bz2/src/network/lscpparser.h -> linuxsampler-2718.tar.bz2/src/network/lscpparser.h
Changed
@@ -3,7 +3,7 @@ * LinuxSampler - modular, streaming capable sampler * * * * Copyright (C) 2003, 2004 by Benno Senoner and Christian Schoenebeck * - * Copyright (C) 2005 - 2008 Christian Schoenebeck * + * Copyright (C) 2005 - 2014 Christian Schoenebeck * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -35,12 +35,15 @@ #include <iostream> #include <sstream> #include <string> +#include <stdint.h> +#include <set> #include "../common/global_private.h" #include "../common/Path.h" #include "lscpevent.h" #include "../Sampler.h" #include "../drivers/midi/MidiInstrumentMapper.h" +#include "lscp_shell_reference.h" namespace LinuxSampler { @@ -85,6 +88,8 @@ #define YYSTYPE _YYSTYPE #define yystype YYSTYPE ///< For backward compatibility. #define YYSTYPE_IS_DECLARED ///< We tell the lexer / parser that we use our own data structure as defined above. +#define YYTYPE_INT16 int16_t +#define YYDEBUG 1 /** * Parameters given to the parser on every yyparse() call. @@ -93,14 +98,30 @@ LSCPServer* pServer; int hSession; bool bVerbose; ///< if true then all commands will immediately sent back (echo) + bool bShellInteract; ///< if true: then client is the LSCP shell + bool bShellAutoCorrect; ///< if true: try to automatically correct obvious syntax mistakes + bool bShellSendLSCPDoc; ///< if true: send the relevant LSCP documentation reference section for the current command. int iLine; ///< Current line (just for verbosity / messages) - int iColumn; ///< Current column (just for verbosity / messages) + int iColumn; ///< End of current line (just for verbosity / messages) + int iCursorOffset; ///< Column of cursor position in current line (reflected as offset to iColumn, range -n .. 0) + YYTYPE_INT16** ppStackBottom; ///< Bottom end of the Bison parser's state stack. + YYTYPE_INT16** ppStackTop; ///< Current position (heap) of the Bison parser's state stack. + lscp_ref_entry_t* pLSCPDocRef; ///< only if bShellSendLSCPDoc=true: points to the current LSCP doc reference, for being able to detect if another LSCP doc page has to be sent to the LSCP shell (client). yyparse_param_t() { pServer = NULL; hSession = -1; bVerbose = false; - iLine = iColumn = 0; + bShellInteract = bShellAutoCorrect = bShellSendLSCPDoc = false; + iCursorOffset = iLine = iColumn = 0; + ppStackBottom = ppStackTop = NULL; + pLSCPDocRef = NULL; + } + + void onNextLine() { + iLine++; + iColumn = 0; + iCursorOffset = 0; } }; #define YYPARSE_PARAM yyparse_param
View file
linuxsampler-2342.tar.bz2/src/network/lscpserver.cpp -> linuxsampler-2718.tar.bz2/src/network/lscpserver.cpp
Changed
@@ -3,7 +3,7 @@ * LinuxSampler - modular, streaming capable sampler * * * * Copyright (C) 2003, 2004 by Benno Senoner and Christian Schoenebeck * - * Copyright (C) 2005 - 2012 Christian Schoenebeck * + * Copyright (C) 2005 - 2015 Christian Schoenebeck * * * * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -47,6 +47,8 @@ namespace LinuxSampler { +String lscpParserProcessShellInteraction(String& line, yyparse_param_t* param, bool possibilities); + /** * Returns a copy of the given string where all special characters are * replaced by LSCP escape sequences ("\xHH"). This function shall be used @@ -97,15 +99,15 @@ */ fd_set LSCPServer::fdSet; int LSCPServer::currentSocket = -1; -std::vector<yyparse_param_t> LSCPServer::Sessions = std::vector<yyparse_param_t>(); -std::vector<yyparse_param_t>::iterator itCurrentSession = std::vector<yyparse_param_t>::iterator(); -std::map<int,String> LSCPServer::bufferedNotifies = std::map<int,String>(); -std::map<int,String> LSCPServer::bufferedCommands = std::map<int,String>(); -std::map< LSCPEvent::event_t, std::list<int> > LSCPServer::eventSubscriptions = std::map< LSCPEvent::event_t, std::list<int> >(); -Mutex LSCPServer::NotifyMutex = Mutex(); -Mutex LSCPServer::NotifyBufferMutex = Mutex(); -Mutex LSCPServer::SubscriptionMutex = Mutex(); -Mutex LSCPServer::RTNotifyMutex = Mutex(); +std::vector<yyparse_param_t> LSCPServer::Sessions; +std::vector<yyparse_param_t>::iterator itCurrentSession; +std::map<int,String> LSCPServer::bufferedNotifies; +std::map<int,String> LSCPServer::bufferedCommands; +std::map< LSCPEvent::event_t, std::list<int> > LSCPServer::eventSubscriptions; +Mutex LSCPServer::NotifyMutex; +Mutex LSCPServer::NotifyBufferMutex; +Mutex LSCPServer::SubscriptionMutex; +Mutex LSCPServer::RTNotifyMutex; LSCPServer::LSCPServer(Sampler* pSampler, long int addr, short int port) : Thread(true, false, 0, -4), eventHandler(this) { SocketAddress.sin_family = AF_INET; @@ -447,7 +449,7 @@ #endif // check if some engine channel's parameter / status changed, if so notify the respective LSCP event subscribers { - EngineChannelFactory::EngineChannelsMutex.Lock(); + LockGuard lock(EngineChannelFactory::EngineChannelsMutex); std::set<EngineChannel*> engineChannels = EngineChannelFactory::EngineChannelInstances(); std::set<EngineChannel*>::iterator itEngineChannel = engineChannels.begin(); std::set<EngineChannel*>::iterator itEnd = engineChannels.end(); @@ -465,7 +467,6 @@ } } } - EngineChannelFactory::EngineChannelsMutex.Unlock(); } // check if MIDI data arrived on some engine channel @@ -517,17 +518,18 @@ } } - //Now let's deliver late notifies (if any) - NotifyBufferMutex.Lock(); - for (std::map<int,String>::iterator iterNotify = bufferedNotifies.begin(); iterNotify != bufferedNotifies.end(); iterNotify++) { + //Now let's deliver late notifies (if any) + { + LockGuard lock(NotifyBufferMutex); + for (std::map<int,String>::iterator iterNotify = bufferedNotifies.begin(); iterNotify != bufferedNotifies.end(); iterNotify++) { #ifdef MSG_NOSIGNAL - send(iterNotify->first, iterNotify->second.c_str(), iterNotify->second.size(), MSG_NOSIGNAL); + send(iterNotify->first, iterNotify->second.c_str(), iterNotify->second.size(), MSG_NOSIGNAL); #else - send(iterNotify->first, iterNotify->second.c_str(), iterNotify->second.size(), 0); + send(iterNotify->first, iterNotify->second.c_str(), iterNotify->second.size(), 0); #endif - } - bufferedNotifies.clear(); - NotifyBufferMutex.Unlock(); + } + bufferedNotifies.clear(); + } fd_set selectSet = fdSet; timeout.tv_sec = 0; @@ -592,11 +594,11 @@ //Something was selected and it was not the hSocket, so it must be some command(s) coming. for (std::vector<yyparse_param_t>::iterator iter = Sessions.begin(); iter != Sessions.end(); iter++) { if (FD_ISSET((*iter).hSession, &selectSet)) { //Was it this socket? + currentSocket = (*iter).hSession; //a hack if (GetLSCPCommand(iter)) { //Have we read the entire command? dmsg(3,("LSCPServer: Got command on socket %d, calling parser.\n", currentSocket)); int dummy; // just a temporary hack to fulfill the restart() function prototype restart(NULL, dummy); // restart the 'scanner' - currentSocket = (*iter).hSession; //a hack itCurrentSession = iter; // another hack dmsg(2,("LSCPServer: %s\n",bufferedCommandscurrentSocket.c_str())); if ((*iter).bVerbose) { // if echo mode enabled @@ -610,6 +612,7 @@ CloseConnection(iter); } } + currentSocket = -1; //continuation of a hack //socket may have been closed, iter may be invalid, get out of the loop for now. //we'll be back if there is data. break; @@ -624,12 +627,14 @@ LSCPServer::SendLSCPNotify(LSCPEvent(LSCPEvent::event_misc, "Client connection terminated on socket", socket)); Sessions.erase(iter); FD_CLR(socket, &fdSet); - SubscriptionMutex.Lock(); //Must unsubscribe this socket from all events (if any) - for (std::map< LSCPEvent::event_t, std::list<int> >::iterator iter = eventSubscriptions.begin(); iter != eventSubscriptions.end(); iter++) { - iter->second.remove(socket); - } - SubscriptionMutex.Unlock(); - NotifyMutex.Lock(); + { + LockGuard lock(SubscriptionMutex); + // Must unsubscribe this socket from all events (if any) + for (std::map< LSCPEvent::event_t, std::list<int> >::iterator iter = eventSubscriptions.begin(); iter != eventSubscriptions.end(); iter++) { + iter->second.remove(socket); + } + } + LockGuard lock(NotifyMutex); bufferedCommands.erase(socket); bufferedNotifies.erase(socket); #if defined(WIN32) @@ -637,7 +642,6 @@ #else close(socket); #endif - NotifyMutex.Unlock(); } void LSCPServer::CloseAllConnections() { @@ -648,30 +652,21 @@ } } -void LSCPServer::LockRTNotify() { - RTNotifyMutex.Lock(); -} - -void LSCPServer::UnlockRTNotify() { - RTNotifyMutex.Unlock(); -} - int LSCPServer::EventSubscribers( std::list<LSCPEvent::event_t> events ) { int subs = 0; - SubscriptionMutex.Lock(); + LockGuard lock(SubscriptionMutex); for( std::list<LSCPEvent::event_t>::iterator iter = events.begin(); iter != events.end(); iter++) { subs += eventSubscriptions.count(*iter); } - SubscriptionMutex.Unlock(); return subs; } void LSCPServer::SendLSCPNotify( LSCPEvent event ) { - SubscriptionMutex.Lock(); + LockGuard lock(SubscriptionMutex); if (eventSubscriptions.count(event.GetType()) == 0) { - SubscriptionMutex.Unlock(); //Nobody is subscribed to this event + // Nobody is subscribed to this event return; } std::list<int>::iterator iter = eventSubscriptionsevent.GetType().begin(); @@ -697,7 +692,6 @@ } } } - SubscriptionMutex.Unlock(); } extern int GetLSCPCommand( void *buf, int max_size ) { @@ -722,84 +716,178 @@ } /** + * Generate the relevant LSCP documentation reference section if necessary. + * The documentation section for the currently active command on the LSCP + * shell's command line will be encoded in a special format, specifically for + * the LSCP shell application. + * + * @param line - current LSCP command line + * @param param - reentrant Bison parser parameters + * + * @return encoded reference string or empty string if nothing shall be sent + * to LSCP shell (client) at this point + */ +String LSCPServer::generateLSCPDocReply(const String& line, yyparse_param_t* param) { + String result; + lscp_ref_entry_t* ref = lscp_reference_for_command(line.c_str()); + // Pointer comparison works here, since the function above always + // returns the same constant pointer for the respective LSCP + // command ... Only send the LSCP reference section to the client if + // another LSCP reference section became relevant now: + if (ref != param->pLSCPDocRef) { + param->pLSCPDocRef = ref; + if (ref) { // send a new LSCP doc section to client ... + result += "SHD:" + ToString(LSCP_SHD_MATCH) + ":" + String(ref->name) + "\n"; + result += String(ref->section) + "\n"; + result += "."; // dot line marks the end of the text for client + } else { // inform client that no LSCP doc section matches right now ... + result = "SHD:" + ToString(LSCP_SHD_NO_MATCH); + } + } + dmsg(4,("LSCP doc reply -> '%s'\n", result.c_str())); + return result; +} + +/** * Will be called to try to read the command from the socket * If command is read, it will return true. Otherwise false is returned. * In any case the received portion (complete or incomplete) is saved into bufferedCommand map. */ bool LSCPServer::GetLSCPCommand( std::vector<yyparse_param_t>::iterator iter ) { int socket = (*iter).hSession; + int result; char c; - int i = 0; + std::vector<char> input; + + // first get as many character as possible and add it to the 'input' buffer while (true) { #if defined(WIN32) - int result = recv(socket, (char *)&c, 1, 0); //Read one character at a time for now + result = recv(socket, (char *)&c, 1, 0); //Read one character at a time for now #else - int result = recv(socket, (void *)&c, 1, 0); //Read one character at a time for now + result = recv(socket, (void *)&c, 1, 0); //Read one character at a time for now #endif - if (result == 0) { //socket was selected, so 0 here means client has closed the connection - CloseConnection(iter); - break; + if (result == 1) input.push_back(c); + else break; // end of input or some error + if (c == '\n') break; // process line by line + } + + // process input buffer + for (int i = 0; i < input.size(); ++i) { + c = inputi; + if (c == '\r') continue; //Ignore CR + if (c == '\n') { + // only if the other side is the LSCP shell application: + // check the current (incomplete) command line for syntax errors, + // possible completions and report everything back to the shell + if ((*iter).bShellInteract || (*iter).bShellAutoCorrect) { + String s = lscpParserProcessShellInteraction(bufferedCommandssocket, &(*iter), false); + if (!s.empty() && (*iter).bShellInteract) AnswerClient(s + "\n"); + } + // if other side is LSCP shell application, send the relevant LSCP + // documentation section of the current command line (if necessary) + if ((*iter).bShellSendLSCPDoc && (*iter).bShellInteract) { + String s = generateLSCPDocReply(bufferedCommandssocket, &(*iter)); + if (!s.empty()) AnswerClient(s + "\n"); + } + LSCPServer::SendLSCPNotify(LSCPEvent(LSCPEvent::event_misc, "Received \'" + bufferedCommandssocket + "\' on socket", socket)); + bufferedCommandssocket += "\r\n"; + return true; //Complete command was read + } else if (c == 2) { // custom ASCII code usage for moving cursor left (LSCP shell) + if (iter->iCursorOffset + bufferedCommandssocket.size() > 0) + iter->iCursorOffset--; + } else if (c == 3) { // custom ASCII code usage for moving cursor right (LSCP shell) + if (iter->iCursorOffset < 0) iter->iCursorOffset++; + } else { + size_t cursorPos = bufferedCommandssocket.size() + iter->iCursorOffset; + // backspace character - should only happen with shell + if (c == '\b') { + if (!bufferedCommandssocket.empty() && cursorPos > 0) + bufferedCommandssocket.erase(cursorPos - 1, 1); + } else { // append (or insert) new character (at current cursor position) ... + if (cursorPos >= 0) + bufferedCommandssocket.insert(cursorPos, String(1,c)); // insert + else + bufferedCommandssocket += c; // append + } } - if (result == 1) { - if (c == '\r') - continue; //Ignore CR - if (c == '\n') { - LSCPServer::SendLSCPNotify(LSCPEvent(LSCPEvent::event_misc, "Received \'" + bufferedCommandssocket + "\' on socket", socket)); - bufferedCommandssocket += "\r\n"; - return true; //Complete command was read + // Only if the other side (client) is the LSCP shell application: + // The following block takes care about automatic correction, auto + // completion (and suggestions), LSCP reference documentation, etc. + // The "if" statement here is for optimization reasons, so that the + // heavy LSCP grammar evaluation algorithm is only executed once for an + // entire command line received. + if (i == input.size() - 1) { + // check the current (incomplete) command line for syntax errors, + // possible completions and report everything back to the shell + if ((*iter).bShellInteract || (*iter).bShellAutoCorrect) { + String s = lscpParserProcessShellInteraction(bufferedCommandssocket, &(*iter), true); + if (!s.empty() && (*iter).bShellInteract && i == input.size() - 1) + AnswerClient(s + "\n"); + } + // if other side is LSCP shell application, send the relevant LSCP + // documentation section of the current command line (if necessary) + if ((*iter).bShellSendLSCPDoc && (*iter).bShellInteract) { + String s = generateLSCPDocReply(bufferedCommandssocket, &(*iter)); + if (!s.empty()) AnswerClient(s + "\n"); } - bufferedCommandssocket += c; } - #if defined(WIN32) - if (result == SOCKET_ERROR) { - int wsa_lasterror = WSAGetLastError(); - if (wsa_lasterror == WSAEWOULDBLOCK) //Would block, try again later. + } + + // handle network errors ... + if (result == 0) { //socket was selected, so 0 here means client has closed the connection + CloseConnection(iter); + return false; + } + #if defined(WIN32) + if (result == SOCKET_ERROR) { + int wsa_lasterror = WSAGetLastError(); + if (wsa_lasterror == WSAEWOULDBLOCK) //Would block, try again later. + return false; + dmsg(2,("LSCPScanner: Socket error after recv() Error %d.\n", wsa_lasterror)); + CloseConnection(iter); + return false; + } + #else + if (result == -1) { + if (errno == EAGAIN) //Would block, try again later. + return false; + switch(errno) { + case EBADF: + dmsg(2,("LSCPScanner: The argument s is an invalid descriptor.\n")); return false; - dmsg(2,("LSCPScanner: Socket error after recv() Error %d.\n", wsa_lasterror)); - CloseConnection(iter); - break; - } - #else - if (result == -1) { - if (errno == EAGAIN) //Would block, try again later. + case ECONNREFUSED: + dmsg(2,("LSCPScanner: A remote host refused to allow the network connection (typically because it is not running the requested service).\n")); + return false; + case ENOTCONN: + dmsg(2,("LSCPScanner: The socket is associated with a connection-oriented protocol and has not been connected (see connect(2) and accept(2)).\n")); + return false; + case ENOTSOCK: + dmsg(2,("LSCPScanner: The argument s does not refer to a socket.\n")); + return false; + case EAGAIN: + dmsg(2,("LSCPScanner: The socket is marked non-blocking and the receive operation would block, or a receive timeout had been set and the timeout expired before data was received.\n")); + return false; + case EINTR: + dmsg(2,("LSCPScanner: The receive was interrupted by delivery of a signal before any data were available.\n")); + return false; + case EFAULT: + dmsg(2,("LSCPScanner: The receive buffer pointer(s) point outside the process's address space.\n")); + return false; + case EINVAL: + dmsg(2,("LSCPScanner: Invalid argument passed.\n")); + return false; + case ENOMEM: + dmsg(2,("LSCPScanner: Could not allocate memory for recvmsg.\n")); + return false; + default: + dmsg(2,("LSCPScanner: Unknown recv() error.\n")); return false; - switch(errno) { - case EBADF: - dmsg(2,("LSCPScanner: The argument s is an invalid descriptor.\n")); - break; - case ECONNREFUSED: - dmsg(2,("LSCPScanner: A remote host refused to allow the network connection (typically because it is not running the requested service).\n")); - break; - case ENOTCONN: - dmsg(2,("LSCPScanner: The socket is associated with a connection-oriented protocol and has not been connected (see connect(2) and accept(2)).\n")); - break; - case ENOTSOCK: - dmsg(2,("LSCPScanner: The argument s does not refer to a socket.\n")); - break; - case EAGAIN: - dmsg(2,("LSCPScanner: The socket is marked non-blocking and the receive operation would block, or a receive timeout had been set and the timeout expired before data was received.\n")); - break; - case EINTR: - dmsg(2,("LSCPScanner: The receive was interrupted by delivery of a signal before any data were available.\n")); - break; - case EFAULT: - dmsg(2,("LSCPScanner: The receive buffer pointer(s) point outside the process's address space.\n")); - break; - case EINVAL: - dmsg(2,("LSCPScanner: Invalid argument passed.\n")); - break; - case ENOMEM: - dmsg(2,("LSCPScanner: Could not allocate memory for recvmsg.\n")); - break; - default: - dmsg(2,("LSCPScanner: Unknown recv() error.\n")); - break; - } - CloseConnection(iter); - break; } - #endif + CloseConnection(iter); + return false; } + #endif + return false; } @@ -810,15 +898,34 @@ * @param ReturnMessage - message that will be send to the client */ void LSCPServer::AnswerClient(String ReturnMessage) { - dmsg(2,("LSCPServer::AnswerClient(ReturnMessage=%s)", ReturnMessage.c_str())); + dmsg(2,("LSCPServer::AnswerClient(ReturnMessage='%s')", ReturnMessage.c_str())); if (currentSocket != -1) { - NotifyMutex.Lock(); + LockGuard lock(NotifyMutex); + + // just if other side is LSCP shell: in case respose is a multi-line + // one, then inform client about it before sending the actual mult-line + // response + if (GetCurrentYaccSession()->bShellInteract) { + // check if this is a multi-line response + int n = 0; + for (int i = 0; i < ReturnMessage.size(); ++i) + if (ReturnMessagei == '\n') ++n; + if (n >= 2) { + dmsg(2,("LSCP Shell <- expect mult-line response\n")); + String s = LSCP_SHK_EXPECT_MULTI_LINE "\r\n"; +#ifdef MSG_NOSIGNAL + send(currentSocket, s.c_str(), s.size(), MSG_NOSIGNAL); +#else + send(currentSocket, s.c_str(), s.size(), 0); +#endif + } + } + #ifdef MSG_NOSIGNAL send(currentSocket, ReturnMessage.c_str(), ReturnMessage.size(), MSG_NOSIGNAL); #else send(currentSocket, ReturnMessage.c_str(), ReturnMessage.size(), 0); #endif - NotifyMutex.Unlock(); } } @@ -968,10 +1075,9 @@ try { SamplerChannel* pSamplerChannel = pSampler->GetSamplerChannel(uiSamplerChannel); if (!pSamplerChannel) throw Exception("Invalid sampler channel number " + ToString(uiSamplerChannel)); - LockRTNotify(); + LockGuard lock(RTNotifyMutex); pSamplerChannel->SetEngineType(EngineName); if(HasSoloChannel()) pSamplerChannel->GetEngineChannel()->SetMute(-1); - UnlockRTNotify(); } catch (Exception e) { result.Error(e); @@ -1011,9 +1117,11 @@ */ String LSCPServer::AddChannel() { dmsg(2,("LSCPServer: AddChannel()\n")); - LockRTNotify(); - SamplerChannel* pSamplerChannel = pSampler->AddSamplerChannel(); - UnlockRTNotify(); + SamplerChannel* pSamplerChannel; + { + LockGuard lock(RTNotifyMutex); + pSamplerChannel = pSampler->AddSamplerChannel(); + } LSCPResultSet result(pSamplerChannel->Index()); return result.Produce(); } @@ -1024,9 +1132,10 @@ String LSCPServer::RemoveChannel(uint uiSamplerChannel) { dmsg(2,("LSCPServer: RemoveChannel(SamplerChannel=%d)\n", uiSamplerChannel)); LSCPResultSet result; - LockRTNotify(); - pSampler->RemoveSamplerChannel(uiSamplerChannel); - UnlockRTNotify(); + { + LockGuard lock(RTNotifyMutex); + pSampler->RemoveSamplerChannel(uiSamplerChannel); + } return result.Produce(); } @@ -1069,17 +1178,18 @@ String LSCPServer::GetEngineInfo(String EngineName) { dmsg(2,("LSCPServer: GetEngineInfo(EngineName=%s)\n", EngineName.c_str())); LSCPResultSet result; - LockRTNotify(); - try { - Engine* pEngine = EngineFactory::Create(EngineName); - result.Add("DESCRIPTION", _escapeLscpResponse(pEngine->Description())); - result.Add("VERSION", pEngine->Version()); - EngineFactory::Destroy(pEngine); - } - catch (Exception e) { - result.Error(e); + { + LockGuard lock(RTNotifyMutex); + try { + Engine* pEngine = EngineFactory::Create(EngineName); + result.Add("DESCRIPTION", _escapeLscpResponse(pEngine->Description())); + result.Add("VERSION", pEngine->Version()); + EngineFactory::Destroy(pEngine); + } + catch (Exception e) { + result.Error(e); + } } - UnlockRTNotify(); return result.Produce(); } @@ -1728,58 +1838,160 @@ String LSCPServer::SetAudioOutputDevice(uint AudioDeviceId, uint uiSamplerChannel) { dmsg(2,("LSCPServer: SetAudiotOutputDevice(AudioDeviceId=%d, SamplerChannel=%d)\n",AudioDeviceId,uiSamplerChannel)); LSCPResultSet result; - LockRTNotify(); + { + LockGuard lock(RTNotifyMutex); + try { + SamplerChannel* pSamplerChannel = pSampler->GetSamplerChannel(uiSamplerChannel); + if (!pSamplerChannel) throw Exception("Invalid sampler channel number " + ToString(uiSamplerChannel)); + std::map<uint, AudioOutputDevice*> devices = pSampler->GetAudioOutputDevices(); + if (!devices.count(AudioDeviceId)) throw Exception("There is no audio output device with index " + ToString(AudioDeviceId)); + AudioOutputDevice* pDevice = devicesAudioDeviceId; + pSamplerChannel->SetAudioOutputDevice(pDevice); + } + catch (Exception e) { + result.Error(e); + } + } + return result.Produce(); +} + +String LSCPServer::SetAudioOutputType(String AudioOutputDriver, uint uiSamplerChannel) { + dmsg(2,("LSCPServer: SetAudioOutputType(String AudioOutputDriver=%s, SamplerChannel=%d)\n",AudioOutputDriver.c_str(),uiSamplerChannel)); + LSCPResultSet result; + { + LockGuard lock(RTNotifyMutex); + try { + SamplerChannel* pSamplerChannel = pSampler->GetSamplerChannel(uiSamplerChannel); + if (!pSamplerChannel) throw Exception("Invalid sampler channel number " + ToString(uiSamplerChannel)); + // Driver type name aliasing... + if (AudioOutputDriver == "Alsa") AudioOutputDriver = "ALSA"; + if (AudioOutputDriver == "Jack") AudioOutputDriver = "JACK"; + // Check if there's one audio output device already created + // for the intended audio driver type (AudioOutputDriver)... + AudioOutputDevice *pDevice = NULL; + std::map<uint, AudioOutputDevice*> devices = pSampler->GetAudioOutputDevices(); + std::map<uint, AudioOutputDevice*>::iterator iter = devices.begin(); + for (; iter != devices.end(); iter++) { + if ((iter->second)->Driver() == AudioOutputDriver) { + pDevice = iter->second; + break; + } + } + // If it doesn't exist, create a new one with default parameters... + if (pDevice == NULL) { + std::map<String,String> params; + pDevice = pSampler->CreateAudioOutputDevice(AudioOutputDriver, params); + } + // Must have a device... + if (pDevice == NULL) + throw Exception("Internal error: could not create audio output device."); + // Set it as the current channel device... + pSamplerChannel->SetAudioOutputDevice(pDevice); + } + catch (Exception e) { + result.Error(e); + } + } + return result.Produce(); +} + +String LSCPServer::AddChannelMidiInput(uint uiSamplerChannel, uint MIDIDeviceId, uint MIDIPort) { + dmsg(2,("LSCPServer: AddChannelMidiInput(uiSamplerChannel=%d, MIDIDeviceId=%d, MIDIPort=%d)\n",uiSamplerChannel,MIDIDeviceId,MIDIPort)); + LSCPResultSet result; try { SamplerChannel* pSamplerChannel = pSampler->GetSamplerChannel(uiSamplerChannel); if (!pSamplerChannel) throw Exception("Invalid sampler channel number " + ToString(uiSamplerChannel)); - std::map<uint, AudioOutputDevice*> devices = pSampler->GetAudioOutputDevices(); - if (!devices.count(AudioDeviceId)) throw Exception("There is no audio output device with index " + ToString(AudioDeviceId)); - AudioOutputDevice* pDevice = devicesAudioDeviceId; - pSamplerChannel->SetAudioOutputDevice(pDevice); + + std::map<uint, MidiInputDevice*> devices = pSampler->GetMidiInputDevices(); + if (!devices.count(MIDIDeviceId)) throw Exception("There is no MIDI input device with index " + ToString(MIDIDeviceId)); + MidiInputDevice* pDevice = devicesMIDIDeviceId; + + MidiInputPort* pPort = pDevice->GetPort(MIDIPort); + if (!pPort) throw Exception("There is no MIDI input port with index " + ToString(MIDIPort) + " on MIDI input device with index " + ToString(MIDIDeviceId)); + + pSamplerChannel->Connect(pPort); + } catch (Exception e) { + result.Error(e); } - catch (Exception e) { - result.Error(e); + return result.Produce(); +} + +String LSCPServer::RemoveChannelMidiInput(uint uiSamplerChannel) { + dmsg(2,("LSCPServer: RemoveChannelMidiInput(uiSamplerChannel=%d)\n",uiSamplerChannel)); + LSCPResultSet result; + try { + SamplerChannel* pSamplerChannel = pSampler->GetSamplerChannel(uiSamplerChannel); + if (!pSamplerChannel) throw Exception("Invalid sampler channel number " + ToString(uiSamplerChannel)); + pSamplerChannel->DisconnectAllMidiInputPorts(); + } catch (Exception e) { + result.Error(e); } - UnlockRTNotify(); return result.Produce(); } -String LSCPServer::SetAudioOutputType(String AudioOutputDriver, uint uiSamplerChannel) { - dmsg(2,("LSCPServer: SetAudioOutputType(String AudioOutputDriver=%s, SamplerChannel=%d)\n",AudioOutputDriver.c_str(),uiSamplerChannel)); +String LSCPServer::RemoveChannelMidiInput(uint uiSamplerChannel, uint MIDIDeviceId) { + dmsg(2,("LSCPServer: RemoveChannelMidiInput(uiSamplerChannel=%d, MIDIDeviceId=%d)\n",uiSamplerChannel,MIDIDeviceId)); LSCPResultSet result; - LockRTNotify(); try { SamplerChannel* pSamplerChannel = pSampler->GetSamplerChannel(uiSamplerChannel); if (!pSamplerChannel) throw Exception("Invalid sampler channel number " + ToString(uiSamplerChannel)); - // Driver type name aliasing... - if (AudioOutputDriver == "Alsa") AudioOutputDriver = "ALSA"; - if (AudioOutputDriver == "Jack") AudioOutputDriver = "JACK"; - // Check if there's one audio output device already created - // for the intended audio driver type (AudioOutputDriver)... - AudioOutputDevice *pDevice = NULL; - std::map<uint, AudioOutputDevice*> devices = pSampler->GetAudioOutputDevices(); - std::map<uint, AudioOutputDevice*>::iterator iter = devices.begin(); - for (; iter != devices.end(); iter++) { - if ((iter->second)->Driver() == AudioOutputDriver) { - pDevice = iter->second; - break; - } - } - // If it doesn't exist, create a new one with default parameters... - if (pDevice == NULL) { - std::map<String,String> params; - pDevice = pSampler->CreateAudioOutputDevice(AudioOutputDriver, params); - } - // Must have a device... - if (pDevice == NULL) - throw Exception("Internal error: could not create audio output device."); - // Set it as the current channel device... - pSamplerChannel->SetAudioOutputDevice(pDevice); + + std::map<uint, MidiInputDevice*> devices = pSampler->GetMidiInputDevices(); + if (!devices.count(MIDIDeviceId)) throw Exception("There is no MIDI input device with index " + ToString(MIDIDeviceId)); + MidiInputDevice* pDevice = devicesMIDIDeviceId; + + std::vector<MidiInputPort*> vPorts = pSamplerChannel->GetMidiInputPorts(); + for (int i = 0; i < vPorts.size(); ++i) + if (vPortsi->GetDevice() == pDevice) + pSamplerChannel->Disconnect(vPortsi); + + } catch (Exception e) { + result.Error(e); } - catch (Exception e) { - result.Error(e); + return result.Produce(); +} + +String LSCPServer::RemoveChannelMidiInput(uint uiSamplerChannel, uint MIDIDeviceId, uint MIDIPort) { + dmsg(2,("LSCPServer: RemoveChannelMidiInput(uiSamplerChannel=%d, MIDIDeviceId=%d, MIDIPort=%d)\n",uiSamplerChannel,MIDIDeviceId,MIDIPort)); + LSCPResultSet result; + try { + SamplerChannel* pSamplerChannel = pSampler->GetSamplerChannel(uiSamplerChannel); + if (!pSamplerChannel) throw Exception("Invalid sampler channel number " + ToString(uiSamplerChannel)); + + std::map<uint, MidiInputDevice*> devices = pSampler->GetMidiInputDevices(); + if (!devices.count(MIDIDeviceId)) throw Exception("There is no MIDI input device with index " + ToString(MIDIDeviceId)); + MidiInputDevice* pDevice = devicesMIDIDeviceId; + + MidiInputPort* pPort = pDevice->GetPort(MIDIPort); + if (!pPort) throw Exception("There is no MIDI input port with index " + ToString(MIDIPort) + " on MIDI input device with index " + ToString(MIDIDeviceId)); + + pSamplerChannel->Disconnect(pPort); + } catch (Exception e) { + result.Error(e); + } + return result.Produce(); +} + +String LSCPServer::ListChannelMidiInputs(uint uiSamplerChannel) { + dmsg(2,("LSCPServer: ListChannelMidiInputs(uiSamplerChannel=%d)\n",uiSamplerChannel)); + LSCPResultSet result; + try { + SamplerChannel* pSamplerChannel = pSampler->GetSamplerChannel(uiSamplerChannel); + if (!pSamplerChannel) throw Exception("Invalid sampler channel number " + ToString(uiSamplerChannel)); + std::vector<MidiInputPort*> vPorts = pSamplerChannel->GetMidiInputPorts(); + + String s; + for (int i = 0; i < vPorts.size(); ++i) { + const int iDeviceID = vPortsi->GetDevice()->MidiInputDeviceID(); + const int iPortNr = vPortsi->GetPortNumber(); + if (s.size()) s += ","; + s += "{" + ToString(iDeviceID) + "," + + ToString(iPortNr) + "}"; + } + result.Add(s); + } catch (Exception e) { + result.Error(e); } - UnlockRTNotify(); return result.Produce(); } @@ -2941,7 +3153,7 @@ InstrumentManager::instrument_id_t instrumentID; instrumentID.FileName = pEngineChannel->InstrumentFileName(); instrumentID.Index = pEngineChannel->InstrumentIndex(); - pInstrumentManager->LaunchInstrumentEditor(instrumentID); + pInstrumentManager->LaunchInstrumentEditor(pEngineChannel, instrumentID); } catch (Exception e) { result.Error(e); } @@ -3063,7 +3275,7 @@ String LSCPServer::GetTotalVoiceCountMax() { dmsg(2,("LSCPServer: GetTotalVoiceCountMax()\n")); LSCPResultSet result; - result.Add(EngineFactory::EngineInstances().size() * GLOBAL_MAX_VOICES); + result.Add(EngineFactory::EngineInstances().size() * pSampler->GetGlobalMaxVoices()); return result.Produce(); } @@ -3074,7 +3286,7 @@ String LSCPServer::GetGlobalMaxVoices() { dmsg(2,("LSCPServer: GetGlobalMaxVoices()\n")); LSCPResultSet result; - result.Add(GLOBAL_MAX_VOICES); + result.Add(pSampler->GetGlobalMaxVoices()); return result.Produce(); } @@ -3086,17 +3298,10 @@ dmsg(2,("LSCPServer: SetGlobalMaxVoices(%d)\n", iVoices)); LSCPResultSet result; try { - if (iVoices < 1) throw Exception("Maximum voices may not be less than 1"); - GLOBAL_MAX_VOICES = iVoices; // see common/global_private.cpp - const std::set<Engine*>& engines = EngineFactory::EngineInstances(); - if (engines.size() > 0) { - std::set<Engine*>::iterator iter = engines.begin(); - std::set<Engine*>::iterator end = engines.end(); - for (; iter != end; ++iter) { - (*iter)->SetMaxVoices(iVoices); - } - } - LSCPServer::SendLSCPNotify(LSCPEvent(LSCPEvent::event_global_info, "VOICES", GLOBAL_MAX_VOICES)); + pSampler->SetGlobalMaxVoices(iVoices); + LSCPServer::SendLSCPNotify( + LSCPEvent(LSCPEvent::event_global_info, "VOICES", pSampler->GetGlobalMaxVoices()) + ); } catch (Exception e) { result.Error(e); } @@ -3110,7 +3315,7 @@ String LSCPServer::GetGlobalMaxStreams() { dmsg(2,("LSCPServer: GetGlobalMaxStreams()\n")); LSCPResultSet result; - result.Add(GLOBAL_MAX_STREAMS); + result.Add(pSampler->GetGlobalMaxStreams()); return result.Produce(); } @@ -3122,17 +3327,10 @@ dmsg(2,("LSCPServer: SetGlobalMaxStreams(%d)\n", iStreams)); LSCPResultSet result; try { - if (iStreams < 0) throw Exception("Maximum disk streams may not be negative"); - GLOBAL_MAX_STREAMS = iStreams; // see common/global_private.cpp - const std::set<Engine*>& engines = EngineFactory::EngineInstances(); - if (engines.size() > 0) { - std::set<Engine*>::iterator iter = engines.begin(); - std::set<Engine*>::iterator end = engines.end(); - for (; iter != end; ++iter) { - (*iter)->SetMaxDiskStreams(iStreams); - } - } - LSCPServer::SendLSCPNotify(LSCPEvent(LSCPEvent::event_global_info, "STREAMS", GLOBAL_MAX_STREAMS)); + pSampler->SetGlobalMaxStreams(iStreams); + LSCPServer::SendLSCPNotify( + LSCPEvent(LSCPEvent::event_global_info, "STREAMS", pSampler->GetGlobalMaxStreams()) + ); } catch (Exception e) { result.Error(e); } @@ -3336,9 +3534,10 @@ String LSCPServer::SubscribeNotification(LSCPEvent::event_t type) { dmsg(2,("LSCPServer: SubscribeNotification(Event=%s)\n", LSCPEvent::Name(type).c_str())); LSCPResultSet result; - SubscriptionMutex.Lock(); - eventSubscriptionstype.push_back(currentSocket); - SubscriptionMutex.Unlock(); + { + LockGuard lock(SubscriptionMutex); + eventSubscriptionstype.push_back(currentSocket); + } return result.Produce(); } @@ -3349,9 +3548,10 @@ String LSCPServer::UnsubscribeNotification(LSCPEvent::event_t type) { dmsg(2,("LSCPServer: UnsubscribeNotification(Event=%s)\n", LSCPEvent::Name(type).c_str())); LSCPResultSet result; - SubscriptionMutex.Lock(); - eventSubscriptionstype.remove(currentSocket); - SubscriptionMutex.Unlock(); + { + LockGuard lock(SubscriptionMutex); + eventSubscriptionstype.remove(currentSocket); + } return result.Produce(); } @@ -3876,5 +4076,44 @@ } return result.Produce(); } + +String LSCPServer::SetShellInteract(yyparse_param_t* pSession, double boolean_value) { + dmsg(2,("LSCPServer: SetShellInteract(val=%f)\n", boolean_value)); + LSCPResultSet result; + try { + if (boolean_value == 0) pSession->bShellInteract = false; + else if (boolean_value == 1) pSession->bShellInteract = true; + else throw Exception("Not a boolean value, must either be 0 or 1"); + } catch (Exception e) { + result.Error(e); + } + return result.Produce(); +} + +String LSCPServer::SetShellAutoCorrect(yyparse_param_t* pSession, double boolean_value) { + dmsg(2,("LSCPServer: SetShellAutoCorrect(val=%f)\n", boolean_value)); + LSCPResultSet result; + try { + if (boolean_value == 0) pSession->bShellAutoCorrect = false; + else if (boolean_value == 1) pSession->bShellAutoCorrect = true; + else throw Exception("Not a boolean value, must either be 0 or 1"); + } catch (Exception e) { + result.Error(e); + } + return result.Produce(); +} + +String LSCPServer::SetShellDoc(yyparse_param_t* pSession, double boolean_value) { + dmsg(2,("LSCPServer: SetShellDoc(val=%f)\n", boolean_value)); + LSCPResultSet result; + try { + if (boolean_value == 0) pSession->bShellSendLSCPDoc = false; + else if (boolean_value == 1) pSession->bShellSendLSCPDoc = true; + else throw Exception("Not a boolean value, must either be 0 or 1"); + } catch (Exception e) { + result.Error(e); + } + return result.Produce(); +} }
View file
linuxsampler-2342.tar.bz2/src/network/lscpserver.h -> linuxsampler-2718.tar.bz2/src/network/lscpserver.h
Changed
@@ -3,7 +3,7 @@ * LinuxSampler - modular, streaming capable sampler * * * * Copyright (C) 2003, 2004 by Benno Senoner and Christian Schoenebeck * - * Copyright (C) 2005 - 2008 Christian Schoenebeck * + * Copyright (C) 2005 - 2014 Christian Schoenebeck * * * * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -132,6 +132,11 @@ String SetAudioOutputChannel(uint ChannelAudioOutputChannel, uint AudioOutputDeviceInputChannel, uint uiSamplerChannel); String SetAudioOutputDevice(uint AudioDeviceId, uint SamplerChannel); String SetAudioOutputType(String AudioOutputDriver, uint uiSamplerChannel); + String AddChannelMidiInput(uint uiSamplerChannel, uint MIDIDeviceId, uint MIDIPort = 0); + String RemoveChannelMidiInput(uint uiSamplerChannel); + String RemoveChannelMidiInput(uint uiSamplerChannel, uint MIDIDeviceId); + String RemoveChannelMidiInput(uint uiSamplerChannel, uint MIDIDeviceId, uint MIDIPort); + String ListChannelMidiInputs(uint uiSamplerChannel); String SetMIDIInputPort(uint MIDIPort, uint uiSamplerChannel); String SetMIDIInputChannel(uint MIDIChannel, uint uiSamplerChannel); String SetMIDIInputDevice(uint MIDIDeviceId, uint uiSamplerChannel); @@ -234,6 +239,9 @@ String SubscribeNotification(LSCPEvent::event_t); String UnsubscribeNotification(LSCPEvent::event_t); String SetEcho(yyparse_param_t* pSession, double boolean_value); + String SetShellInteract(yyparse_param_t* pSession, double boolean_value); + String SetShellDoc(yyparse_param_t* pSession, double boolean_value); + String SetShellAutoCorrect(yyparse_param_t* pSession, double boolean_value); void AnswerClient(String ReturnMessage); void CloseAllConnections(); @@ -242,9 +250,12 @@ static void SendLSCPNotify( LSCPEvent Event ); static int EventSubscribers( std::list<LSCPEvent::event_t> events ); - static void LockRTNotify(); - static void UnlockRTNotify(); - static String FilterEndlines(String s); + static String FilterEndlines(String s); + + //Protect main thread that generates real time notify messages + //like voice count, stream count and buffer fill + //from LSCP server removing engines and channels from underneath + static Mutex RTNotifyMutex; protected: int hSocket; @@ -286,18 +297,14 @@ static std::map<int,String> bufferedNotifies; static Mutex NotifyMutex; static Mutex NotifyBufferMutex; - static bool GetLSCPCommand( std::vector<yyparse_param_t>::iterator iter ); + String generateLSCPDocReply(const String& line, yyparse_param_t* param); + bool GetLSCPCommand( std::vector<yyparse_param_t>::iterator iter ); static void CloseConnection( std::vector<yyparse_param_t>::iterator iter ); static std::vector<yyparse_param_t> Sessions; static Mutex SubscriptionMutex; static std::map< LSCPEvent::event_t, std::list<int> > eventSubscriptions; static fd_set fdSet; - //Protect main thread that generates real time notify messages - //like voice count, stream count and buffer fill - //from LSCP server removing engines and channels from underneath - static Mutex RTNotifyMutex; - class EventHandler : public ChannelCountListener, public AudioDeviceCountListener, public MidiDeviceCountListener, public MidiInstrumentCountListener, public MidiInstrumentInfoListener, public MidiInstrumentMapCountListener,
View file
linuxsampler-2342.tar.bz2/src/plugins/InstrumentEditor.cpp -> linuxsampler-2718.tar.bz2/src/plugins/InstrumentEditor.cpp
Changed
@@ -1,6 +1,6 @@ /*************************************************************************** * * - * Copyright (C) 2007 - 2009 Christian Schoenebeck * + * Copyright (C) 2007 - 2015 Christian Schoenebeck * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -21,7 +21,6 @@ #include "InstrumentEditor.h" #include "../common/global_private.h" -#include "../common/atomic.h" #include <algorithm> #include <functional> @@ -31,18 +30,20 @@ InstrumentEditor::InstrumentEditor() : Thread(false, false, -1, 0) { pInstrument = NULL; pUserData = NULL; + pEngineChannel = NULL; } InstrumentEditor::~InstrumentEditor() { } - void InstrumentEditor::Launch(void* pInstrument, String sTypeName, String sTypeVersion, void* pUserData) { + void InstrumentEditor::Launch(EngineChannel* pEngineChannel, void* pInstrument, String sTypeName, String sTypeVersion, void* pUserData) { dmsg(1,("InstrumentEditor::Launch(instr=%x,type=%s,version=%s)\n", pInstrument, sTypeName.c_str(), sTypeVersion.c_str())); // prepare the editor's mandatory parameters this->pInstrument = pInstrument; this->sTypeName = sTypeName; this->sTypeVersion = sTypeVersion; this->pUserData = pUserData; + this->pEngineChannel = pEngineChannel; // start the editor in its own thread StartThread(); } @@ -66,6 +67,10 @@ return iResult; } + EngineChannel* InstrumentEditor::GetEngineChannel() { + return pEngineChannel; + } + void InstrumentEditor::AddListener(InstrumentEditorListener* pListener) { listeners.insert(pListener); }
View file
linuxsampler-2342.tar.bz2/src/plugins/InstrumentEditor.h -> linuxsampler-2718.tar.bz2/src/plugins/InstrumentEditor.h
Changed
@@ -1,6 +1,6 @@ /*************************************************************************** * * - * Copyright (C) 2007 - 2009 Christian Schoenebeck * + * Copyright (C) 2007 - 2015 Christian Schoenebeck * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -31,6 +31,7 @@ // just symbol prototyping class InstrumentEditorListener; + class EngineChannel; /** @brief Instrument Editor Interface (external plugin) * @@ -194,11 +195,26 @@ * editor will be spawned in its own thread and this method will * return as soon as the editor's thread actually started. * + * @param pEngineChannel - the engine channel on which @a pInstrument is + * currently used on and for which the instrument + * editor shall be spawned for editing + * @param pInstrument - pointer to the respective instrument object + * @param sTypeName - format of the instrument data structure + * (i.e. @c "libgig" ) + * @param sTypeVersion - version of the instrument data structure + * (i.e. @c "3.0.1" ). * @param pUserData - (optional) arbitrary 3rd party data that might * e.g. been passed by * InstrumentManager::LaunchInstrumentEditor() */ - void Launch(void* pInstrument, String sTypeName, String sTypeVersion, void* pUserData = NULL); + void Launch(EngineChannel* pEngineChannel, void* pInstrument, String sTypeName, String sTypeVersion, void* pUserData = NULL); + + /** + * Returns the EngineChannel for which this instrument editor was + * spawned for, for editing the respective instrument loaded on that + * EngineChannel. + */ + EngineChannel* GetEngineChannel(); /** * Registers object that wants to be notified on events. @@ -230,6 +246,7 @@ String sTypeName; String sTypeVersion; void* pUserData; + EngineChannel* pEngineChannel; }; /** @brief Instrument Editor Notifications
View file
linuxsampler-2342.tar.bz2/src/plugins/InstrumentEditorFactory.h -> linuxsampler-2718.tar.bz2/src/plugins/InstrumentEditorFactory.h
Changed
@@ -1,6 +1,6 @@ /*************************************************************************** * * - * Copyright (C) 2007 - 2009 Christian Schoenebeck * + * Copyright (C) 2007 - 2014 Christian Schoenebeck * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -46,6 +46,7 @@ public: class InnerFactory { public: + virtual ~InnerFactory() {} virtual InstrumentEditor* Create() = 0; virtual void Destroy(InstrumentEditor* pEditor) = 0; };
View file
linuxsampler-2342.tar.bz2/src/plugins/Makefile.am -> linuxsampler-2718.tar.bz2/src/plugins/Makefile.am
Changed
@@ -1,4 +1,4 @@ -INCLUDES = $(all_includes) +AM_CPPFLAGS = $(all_includes) AM_CXXFLAGS = -Wreturn-type -ffast-math $(CXX_CPU_SWITCH) -DCONFIG_PLUGIN_DIR=\"$(config_plugin_dir)\" METASOURCES = AUTO
View file
linuxsampler-2718.tar.bz2/src/scriptvm
Added
+(directory)
View file
linuxsampler-2718.tar.bz2/src/scriptvm/CoreVMFunctions.cpp
Added
@@ -0,0 +1,118 @@ +/* + * Copyright (c) 2014 Christian Schoenebeck + * + * http://www.linuxsampler.org + * + * This file is part of LinuxSampler and released under the same terms. + * See README file for details. + */ + +#include "CoreVMFunctions.h" + +#include <iostream> +#include <math.h> +#include <stdlib.h> +#include "tree.h" +#include "ScriptVM.h" + +namespace LinuxSampler { + +VMFnResult* VMEmptyResultFunction::errorResult() { + result.flags = StmtFlags_t(STMT_ABORT_SIGNALLED | STMT_ERROR_OCCURRED); + return &result; +} + +VMFnResult* VMEmptyResultFunction::successResult() { + result.flags = STMT_SUCCESS; + return &result; +} + +VMFnResult* VMIntResultFunction::errorResult(int i) { + result.flags = StmtFlags_t(STMT_ABORT_SIGNALLED | STMT_ERROR_OCCURRED); + result.value = i; + return &result; +} + +VMFnResult* VMIntResultFunction::successResult(int i) { + result.flags = STMT_SUCCESS; + result.value = i; + return &result; +} + +VMFnResult* VMStringResultFunction::errorResult(const String& s) { + result.flags = StmtFlags_t(STMT_ABORT_SIGNALLED | STMT_ERROR_OCCURRED); + result.value = s; + return &result; +} + +VMFnResult* VMStringResultFunction::successResult(const String& s) { + result.flags = STMT_SUCCESS; + result.value = s; + return &result; +} + +bool CoreVMFunction_message::acceptsArgType(int iArg, ExprType_t type) const { + return type == INT_EXPR || type == STRING_EXPR; +} + +VMFnResult* CoreVMFunction_message::exec(VMFnArgs* args) { + if (!args->argsCount()) return errorResult(); + + VMStringExpr* strExpr = dynamic_cast<VMStringExpr*>(args->arg(0)); + if (strExpr) { + std::cout << "ScriptVM " << strExpr->evalStr() << "\n"; + return successResult(); + } + + VMIntExpr* intExpr = dynamic_cast<VMIntExpr*>(args->arg(0)); + if (intExpr) { + std::cout << "ScriptVM " << intExpr->evalInt() << "\n"; + return successResult(); + } + + return errorResult(); +} + +VMFnResult* CoreVMFunction_exit::exec(VMFnArgs* args) { + this->result.flags = STMT_ABORT_SIGNALLED; + return &result; +} + +VMFnResult* CoreVMFunction_wait::exec(VMFnArgs* args) { + ExecContext* ctx = dynamic_cast<ExecContext*>(vm->currentVMExecContext()); + VMIntExpr* expr = dynamic_cast<VMIntExpr*>(args->arg(0)); + ctx->suspendMicroseconds = expr->evalInt(); + this->result.flags = STMT_SUSPEND_SIGNALLED; + return &result; +} + +bool CoreVMFunction_abs::acceptsArgType(int iArg, ExprType_t type) const { + return type == INT_EXPR; +} + +VMFnResult* CoreVMFunction_abs::exec(VMFnArgs* args) { + return successResult( ::abs(args->arg(0)->asInt()->evalInt()) ); +} + +bool CoreVMFunction_random::acceptsArgType(int iArg, ExprType_t type) const { + return type == INT_EXPR; +} + +VMFnResult* CoreVMFunction_random::exec(VMFnArgs* args) { + int iMin = args->arg(0)->asInt()->evalInt(); + int iMax = args->arg(1)->asInt()->evalInt(); + float f = float(::rand()) / float(RAND_MAX); + return successResult( + iMin + roundf( f * float(iMax - iMin) ) + ); +} + +bool CoreVMFunction_num_elements::acceptsArgType(int iArg, ExprType_t type) const { + return type == INT_ARR_EXPR; +} + +VMFnResult* CoreVMFunction_num_elements::exec(VMFnArgs* args) { + return successResult( args->arg(0)->asIntArray()->arraySize() ); +} + +} // namespace LinuxSampler
View file
linuxsampler-2718.tar.bz2/src/scriptvm/CoreVMFunctions.h
Added
@@ -0,0 +1,138 @@ +/* + * Copyright (c) 2014 Christian Schoenebeck + * + * http://www.linuxsampler.org + * + * This file is part of LinuxSampler and released under the same terms. + * See README file for details. + */ + +#ifndef LS_COREVMFUNCTIONS_H +#define LS_COREVMFUNCTIONS_H + +#include "../common/global.h" +#include "common.h" + +namespace LinuxSampler { + +class ScriptVM; + +class VMEmptyResult : public VMFnResult, public VMExpr { +public: + StmtFlags_t flags; + + VMEmptyResult() : flags(STMT_SUCCESS) {} + ExprType_t exprType() const { return EMPTY_EXPR; } + VMExpr* resultValue() { return this; } + StmtFlags_t resultFlags() { return flags; } +}; + +class VMIntResult : public VMFnResult, public VMIntExpr { +public: + StmtFlags_t flags; + int value; + + VMIntResult() : flags(STMT_SUCCESS) {} + int evalInt() { return value; } + VMExpr* resultValue() { return this; } + StmtFlags_t resultFlags() { return flags; } +}; + +class VMStringResult : public VMFnResult, public VMStringExpr { +public: + StmtFlags_t flags; + String value; + + VMStringResult() : flags(STMT_SUCCESS) {} + String evalStr() { return value; } + VMExpr* resultValue() { return this; } + StmtFlags_t resultFlags() { return flags; } +}; + +class VMEmptyResultFunction : public VMFunction { +protected: + ExprType_t returnType() { return EMPTY_EXPR; } + VMFnResult* errorResult(); + VMFnResult* successResult(); +protected: + VMEmptyResult result; +}; + +class VMIntResultFunction : public VMFunction { +protected: + ExprType_t returnType() { return INT_EXPR; } + VMFnResult* errorResult(int i = 0); + VMFnResult* successResult(int i = 0); +protected: + VMIntResult result; +}; + +class VMStringResultFunction : public VMFunction { +protected: + ExprType_t returnType() { return STRING_EXPR; } + VMFnResult* errorResult(const String& s = ""); + VMFnResult* successResult(const String& s = ""); +protected: + VMStringResult result; +}; + +class CoreVMFunction_message : public VMEmptyResultFunction { +public: + int minRequiredArgs() const { return 1; } + int maxAllowedArgs() const { return 1; } + bool acceptsArgType(int iArg, ExprType_t type) const; + ExprType_t argType(int iArg) const { return STRING_EXPR; } + VMFnResult* exec(VMFnArgs* args); +}; + +class CoreVMFunction_exit : public VMEmptyResultFunction { +public: + int minRequiredArgs() const { return 0; } + int maxAllowedArgs() const { return 0; } + bool acceptsArgType(int iArg, ExprType_t type) const { return false; } + ExprType_t argType(int iArg) const { return INT_EXPR; /*whatever*/ } + VMFnResult* exec(VMFnArgs* args); +}; + +class CoreVMFunction_wait : public VMEmptyResultFunction { +public: + CoreVMFunction_wait(ScriptVM* vm) : vm(vm) {} + int minRequiredArgs() const { return 1; } + int maxAllowedArgs() const { return 1; } + bool acceptsArgType(int iArg, ExprType_t type) const { return type == INT_EXPR; } + ExprType_t argType(int iArg) const { return INT_EXPR; } + VMFnResult* exec(VMFnArgs* args); +protected: + ScriptVM* vm; +}; + +class CoreVMFunction_abs : public VMIntResultFunction { +public: + int minRequiredArgs() const { return 1; } + int maxAllowedArgs() const { return 1; } + bool acceptsArgType(int iArg, ExprType_t type) const; + ExprType_t argType(int iArg) const { return INT_EXPR; } + VMFnResult* exec(VMFnArgs* args); +}; + +class CoreVMFunction_random : public VMIntResultFunction { +public: + int minRequiredArgs() const { return 2; } + int maxAllowedArgs() const { return 2; } + bool acceptsArgType(int iArg, ExprType_t type) const; + ExprType_t argType(int iArg) const { return INT_EXPR; } + VMFnResult* exec(VMFnArgs* args); +}; + +class CoreVMFunction_num_elements : public VMIntResultFunction { +public: + int minRequiredArgs() const { return 1; } + int maxAllowedArgs() const { return 1; } + bool acceptsArgType(int iArg, ExprType_t type) const; + ExprType_t argType(int iArg) const { return INT_ARR_EXPR; } + VMFnResult* exec(VMFnArgs* args); +}; + +} // namespace LinuxSampler + +#endif // LS_COREVMFUNCTIONS_H
View file
linuxsampler-2718.tar.bz2/src/scriptvm/Makefile.am
Added
@@ -0,0 +1,36 @@ +AM_CPPFLAGS = $(all_includes) +METASOURCES = AUTO + +AM_CXXFLAGS = -Wreturn-type -ffast-math $(CXX_CPU_SWITCH) + +AM_YFLAGS = -d +BUILT_SOURCES = parser.h + +noinst_LTLIBRARIES = liblinuxsamplerscriptvm.la +liblinuxsamplerscriptvm_la_SOURCES = \ + common.h common.cpp \ + scanner.cpp \ + parser.h parser.cpp \ + tree.h tree.cpp \ + CoreVMFunctions.h CoreVMFunctions.cpp \ + ScriptVM.h ScriptVM.cpp + +yacc_sources = parser.y + +EXTRA_DIST = $(yacc_sources) scanner.l + +# automatically (re)generate scanner.cpp with flex / lex if the +# lex source file(s) have been changed +scanner.cpp: scanner.l tree.h parser_shared.h + $(top_srcdir)/scripts/generate_instrument_script_parser.sh + +# automatically (re)generate parser.cpp with bison / yacc if the +# yacc source file(s) have been changed +parser.cpp: $(yacc_sources) scanner.l tree.h parser_shared.h + $(top_srcdir)/scripts/generate_instrument_script_parser.sh + +.PHONY: parser + +# "make parser" was explicitly requested +parser: + $(top_srcdir)/scripts/generate_instrument_script_parser.sh
View file
linuxsampler-2718.tar.bz2/src/scriptvm/ScriptVM.cpp
Added
@@ -0,0 +1,315 @@ +/* + * Copyright (c) 2014 Christian Schoenebeck + * + * http://www.linuxsampler.org + * + * This file is part of LinuxSampler and released under the same terms. + * See README file for details. + */ + +#include "ScriptVM.h" + +#include <string.h> +#include <assert.h> +#include "../common/global_private.h" +#include "tree.h" + +#define DEBUG_SCRIPTVM_CORE 0 + +int InstrScript_parse(LinuxSampler::ParserContext*); + +namespace LinuxSampler { + + static void _printIndents(int n) { + for (int i = 0; i < n; ++i) printf(" "); + fflush(stdout); + } + + static int _requiredMaxStackSizeFor(Statement* statement, int depth = 0) { + if (!statement) return 1; + + switch (statement->statementType()) { + case STMT_LEAF: + #if DEBUG_SCRIPTVM_CORE + _printIndents(depth); + printf("-> STMT_LEAF\n"); + #endif + return 1; + + case STMT_LIST: { + #if DEBUG_SCRIPTVM_CORE + _printIndents(depth); + printf("-> STMT_LIST\n"); + #endif + Statements* stmts = (Statements*) statement; + int max = 0; + for (int i = 0; stmts->statement(i); ++i) { + int size = _requiredMaxStackSizeFor( stmts->statement(i), depth+1 ); + if (max < size) max = size; + } + return max + 1; + } + + case STMT_BRANCH: { + #if DEBUG_SCRIPTVM_CORE + _printIndents(depth); + printf("-> STMT_BRANCH\n"); + #endif + BranchStatement* branchStmt = (BranchStatement*) statement; + int max = 0; + for (int i = 0; branchStmt->branch(i); ++i) { + int size = _requiredMaxStackSizeFor( branchStmt->branch(i), depth+1 ); + if (max < size) max = size; + } + return max + 1; + } + + case STMT_LOOP: { + #if DEBUG_SCRIPTVM_CORE + _printIndents(depth); + printf("-> STMT_LOOP\n"); + #endif + While* whileStmt = (While*) statement; + if (whileStmt->statements()) + return _requiredMaxStackSizeFor( whileStmt->statements() ) + 1; + else + return 1; + } + } + + return 1; // actually just to avoid compiler warning + } + + static int _requiredMaxStackSizeFor(EventHandlers* handlers) { + int max = 1; + for (int i = 0; i < handlers->size(); ++i) { + int size = _requiredMaxStackSizeFor(handlers->eventHandler(i)); + if (max < size) max = size; + } + return max; + } + + ScriptVM::ScriptVM() : m_parserContext(NULL), fnWait(this) { + } + + ScriptVM::~ScriptVM() { + } + + VMParserContext* ScriptVM::loadScript(const String& s) { + std::istringstream iss(s); + return loadScript(&iss); + } + + VMParserContext* ScriptVM::loadScript(std::istream* is) { + ParserContext* context = new ParserContext(this); + //printf("parserCtx=0x%lx\n", (uint64_t)context); + + context->registerBuiltInConstIntVariables( builtInConstIntVariables() ); + context->registerBuiltInIntVariables( builtInIntVariables() ); + context->registerBuiltInIntArrayVariables( builtInIntArrayVariables() ); + + context->createScanner(is); + + InstrScript_parse(context); + dmsg(2,("Allocating %d bytes of global int VM memory.\n", context->globalIntVarCount * sizeof(int))); + dmsg(2,("Allocating %d of global VM string variables.\n", context->globalStrVarCount)); + if (!context->globalIntMemory) + context->globalIntMemory = new ArrayList<int>(); + if (!context->globalStrMemory) + context->globalStrMemory = new ArrayList<String>(); + context->globalIntMemory->resize(context->globalIntVarCount); + memset(&((*context->globalIntMemory)0), 0, context->globalIntVarCount * sizeof(int)); + + context->globalStrMemory->resize(context->globalStrVarCount); + + context->destroyScanner(); + + return context; + } + + void ScriptVM::dumpParsedScript(VMParserContext* context) { + ParserContext* ctx = dynamic_cast<ParserContext*>(context); + if (!ctx) { + std::cerr << "No VM context. So nothing to dump.\n"; + return; + } + if (!ctx->handlers) { + std::cerr << "No event handlers defined in script. So nothing to dump.\n"; + return; + } + if (!ctx->globalIntMemory) { + std::cerr << "Internal error: no global memory assigend to script VM.\n"; + return; + } + ctx->handlers->dump(); + } + + VMExecContext* ScriptVM::createExecContext(VMParserContext* parserContext) { + ParserContext* parserCtx = dynamic_cast<ParserContext*>(parserContext); + ExecContext* execCtx = new ExecContext(); + + if (parserCtx->requiredMaxStackSize < 0) { + parserCtx->requiredMaxStackSize = + _requiredMaxStackSizeFor(&*parserCtx->handlers); + } + execCtx->stack.resize(parserCtx->requiredMaxStackSize); + dmsg(2,("Created VM exec context with %d bytes VM stack size.\n", + parserCtx->requiredMaxStackSize * sizeof(ExecContext::StackFrame))); + //printf("execCtx=0x%lx\n", (uint64_t)execCtx); + const int polySize = parserCtx->polyphonicIntVarCount; + execCtx->polyphonicIntMemory.resize(polySize); + memset(&execCtx->polyphonicIntMemory0, 0, polySize * sizeof(int)); + + dmsg(2,("Allocated %d bytes polyphonic memory.\n", polySize * sizeof(int))); + return execCtx; + } + + VMFunction* ScriptVM::functionByName(const String& name) { + if (name == "message") return &fnMessage; + else if (name == "exit") return &fnExit; + else if (name == "wait") return &fnWait; + else if (name == "abs") return &fnAbs; + else if (name == "random") return &fnRandom; + else if (name == "num_elements") return &fnNumElements; + return NULL; + } + + std::map<String,VMIntRelPtr*> ScriptVM::builtInIntVariables() { + return std::map<String,VMIntRelPtr*>(); + } + + std::map<String,VMInt8Array*> ScriptVM::builtInIntArrayVariables() { + return std::map<String,VMInt8Array*>(); + } + + std::map<String,int> ScriptVM::builtInConstIntVariables() { + return std::map<String,int>(); + } + + VMParserContext* ScriptVM::currentVMParserContext() { + return m_parserContext; + } + + VMExecContext* ScriptVM::currentVMExecContext() { + if (!m_parserContext) return NULL; + return m_parserContext->execContext; + } + + VMExecStatus_t ScriptVM::exec(VMParserContext* parserContext, VMExecContext* execContex, VMEventHandler* handler) { + m_parserContext = dynamic_cast<ParserContext*>(parserContext); + if (!m_parserContext) { + std::cerr << "No VM parser context provided. Did you load a script?.\n"; + return VMExecStatus_t(VM_EXEC_NOT_RUNNING | VM_EXEC_ERROR); + } + + // a ParserContext object is always tied to exactly one ScriptVM object + assert(m_parserContext->functionProvider == this); + + ExecContext* ctx = dynamic_cast<ExecContext*>(execContex); + if (!ctx) { + std::cerr << "Invalid VM exec context.\n"; + return VMExecStatus_t(VM_EXEC_NOT_RUNNING | VM_EXEC_ERROR); + } + EventHandler* h = dynamic_cast<EventHandler*>(handler); + if (!h) return VM_EXEC_NOT_RUNNING; + + m_parserContext->execContext = ctx; + + ctx->status = VM_EXEC_RUNNING; + StmtFlags_t flags = STMT_SUCCESS; + + int& frameIdx = ctx->stackFrame; + if (frameIdx < 0) { // start condition ... + frameIdx = -1; + ctx->pushStack(h); + } + + while (flags == STMT_SUCCESS && frameIdx >= 0) { + if (frameIdx >= ctx->stack.size()) { // should never happen, otherwise it's a bug ... + std::cerr << "CRITICAL: VM stack overflow! (" << frameIdx << ")\n"; + flags = StmtFlags_t(STMT_ABORT_SIGNALLED | STMT_ERROR_OCCURRED); + break; + } + + ExecContext::StackFrame& frame = ctx->stackframeIdx; + switch (frame.statement->statementType()) { + case STMT_LEAF: { + #if DEBUG_SCRIPTVM_CORE + _printIndents(frameIdx); + printf("-> STMT_LEAF\n"); + #endif + LeafStatement* leaf = (LeafStatement*) frame.statement; + flags = leaf->exec(); + ctx->popStack(); + break; + } + + case STMT_LIST: { + #if DEBUG_SCRIPTVM_CORE + _printIndents(frameIdx); + printf("-> STMT_LIST subidx=%d\n", frame.subindex); + #endif + Statements* stmts = (Statements*) frame.statement; + if (stmts->statement(frame.subindex)) { + ctx->pushStack( + stmts->statement(frame.subindex++) + ); + } else { + #if DEBUG_SCRIPTVM_CORE + _printIndents(frameIdx); + printf("END OF LIST subidx=%d\n", frame.subindex); + #endif + ctx->popStack(); + } + break; + } + + case STMT_BRANCH: { + #if DEBUG_SCRIPTVM_CORE + _printIndents(frameIdx); + printf("-> STMT_BRANCH\n"); + #endif + if (frame.subindex < 0) ctx->popStack(); + else { + BranchStatement* branchStmt = (BranchStatement*) frame.statement; + frame.subindex = branchStmt->evalBranch(); + if (frame.subindex >= 0) { + ctx->pushStack( + branchStmt->branch(frame.subindex) + ); + frame.subindex = -1; + } else ctx->popStack(); + } + break; + } + + case STMT_LOOP: { + #if DEBUG_SCRIPTVM_CORE + _printIndents(frameIdx); + printf("-> STMT_LOOP\n"); + #endif + While* whileStmt = (While*) frame.statement; + if (whileStmt->evalLoopStartCondition() && whileStmt->statements()) { + ctx->pushStack( + whileStmt->statements() + ); + } else ctx->popStack(); + } + } + } + + if (flags & STMT_SUSPEND_SIGNALLED) { + ctx->status = VM_EXEC_SUSPENDED; + } else { + ctx->status = VM_EXEC_NOT_RUNNING; + if (flags & STMT_ERROR_OCCURRED) + ctx->status = VM_EXEC_ERROR; + ctx->reset(); + } + + m_parserContext->execContext = NULL; + m_parserContext = NULL; + return ctx->status; + } + +} // namespace LinuxSampler
View file
linuxsampler-2718.tar.bz2/src/scriptvm/ScriptVM.h
Added
@@ -0,0 +1,75 @@ +/* + * Copyright (c) 2014 Christian Schoenebeck + * + * http://www.linuxsampler.org + * + * This file is part of LinuxSampler and released under the same terms. + * See README file for details. + */ + +#ifndef LS_SCRIPTVM_H +#define LS_SCRIPTVM_H + +#include <iostream> +#include <vector> + +#include "../common/global.h" +#include "common.h" +#include "CoreVMFunctions.h" + +namespace LinuxSampler { + + class ParserContext; + class ExecContext; + + /** @brief Core virtual machine for real-time instrument scripts. + * + * This is the core of the virtual machine, used for running real-time + * instrument scripts. The core encompasses the instrument script parser, + * generalized virtual machine and very generic built-in script functions. + * Thus this class only provides functionalities which are yet independent + * of the actual purpose the virtual machine is going to be used for. + * + * The actual use case specific functionalites (i.e. MIDI processing) is + * then implemented by VM classes which are derived from this generalized + * ScriptVM class. + * + * This class is re-entrant safe, but not thread safe. So you can share one + * instance of this class between multiple (native) threads, but you @b must + * @b not execute methods of the same class instance simultaniously from + * different (native) threads. If you want to execute scripts simultaniously + * multi threaded, then create a separate ScriptVM instance for each + * (native) thread. Also note that one VMParserContext instance is tied to + * exactly one ScriptVM instance. So you @b must @b not create a + * VMParserContext with one ScriptVM instance and run it with a different + * ScriptVM instance! + */ + class ScriptVM : public VMFunctionProvider { + public: + ScriptVM(); + virtual ~ScriptVM(); + VMParserContext* loadScript(const String& s); + VMParserContext* loadScript(std::istream* is); + void dumpParsedScript(VMParserContext* context); + VMExecContext* createExecContext(VMParserContext* parserContext); + VMExecStatus_t exec(VMParserContext* parserContext, VMExecContext* execContex, VMEventHandler* handler); + VMFunction* functionByName(const String& name) OVERRIDE; + std::map<String,VMIntRelPtr*> builtInIntVariables() OVERRIDE; + std::map<String,VMInt8Array*> builtInIntArrayVariables() OVERRIDE; + std::map<String,int> builtInConstIntVariables() OVERRIDE; + + VMParserContext* currentVMParserContext(); //TODO: should be protected (only usable during exec() calls, intended only for VMFunctions) + VMExecContext* currentVMExecContext(); //TODO: should be protected (only usable during exec() calls, intended only for VMFunctions) + protected: + ParserContext* m_parserContext; + CoreVMFunction_message fnMessage; + CoreVMFunction_exit fnExit; + CoreVMFunction_wait fnWait; + CoreVMFunction_abs fnAbs; + CoreVMFunction_random fnRandom; + CoreVMFunction_num_elements fnNumElements; + }; + +} // namespace LinuxSampler + +#endif // LS_INSTRUMENTSCRIPTVM_H
View file
linuxsampler-2718.tar.bz2/src/scriptvm/common.cpp
Added
@@ -0,0 +1,35 @@ +/* + * Copyright (c) 2014 Christian Schoenebeck + * + * http://www.linuxsampler.org + * + * This file is part of LinuxSampler and released under the same terms. + * See README file for details. + */ + +#include "common.h" +#include <iostream> + +namespace LinuxSampler { + + VMIntExpr* VMExpr::asInt() const { + return const_cast<VMIntExpr*>( dynamic_cast<const VMIntExpr*>(this) ); + } + + VMStringExpr* VMExpr::asString() const { + return const_cast<VMStringExpr*>( dynamic_cast<const VMStringExpr*>(this) ); + } + + VMIntArrayExpr* VMExpr::asIntArray() const { + return const_cast<VMIntArrayExpr*>( dynamic_cast<const VMIntArrayExpr*>(this) ); + } + + void VMFunction::wrnMsg(const String& txt) { + std::cout << "ScriptVM " << txt << std::endl; + } + + void VMFunction::errMsg(const String& txt) { + std::cerr << "ScriptVM " << txt << std::endl; + } + +} // namespace LinuxSampler
View file
linuxsampler-2718.tar.bz2/src/scriptvm/common.h
Added
@@ -0,0 +1,430 @@ +/* + * Copyright (c) 2014 Christian Schoenebeck + * + * http://www.linuxsampler.org + * + * This file is part of LinuxSampler and released under the same terms. + * See README file for details. + */ + +// This header defines data types shared between the VM core implementation +// (inside the current source directory) and other parts of the sampler +// (located at other source directories). + +#ifndef LS_INSTR_SCRIPT_PARSER_COMMON_H +#define LS_INSTR_SCRIPT_PARSER_COMMON_H + +#include "../common/global.h" +#include <vector> +#include <map> +#include <stddef.h> // offsetof() + +namespace LinuxSampler { + + enum ParserIssueType_t { + PARSER_ERROR, + PARSER_WARNING + }; + + enum ExprType_t { + EMPTY_EXPR, ///< i.e. on invalid expressions or i.e. a function call that does not return a result value + INT_EXPR, + INT_ARR_EXPR, + STRING_EXPR, + STRING_ARR_EXPR, + }; + + enum StmtFlags_t { + STMT_SUCCESS = 0, ///< Function / statement was executed successfully, no error occurred. + STMT_ABORT_SIGNALLED = 1, ///< VM should stop the current callback execution (usually because of an error, but might also be without an error reason). + STMT_SUSPEND_SIGNALLED = (1<<1), + STMT_ERROR_OCCURRED = (1<<2), + }; + + enum VMExecStatus_t { + VM_EXEC_NOT_RUNNING = 0, + VM_EXEC_RUNNING = 1, + VM_EXEC_SUSPENDED = (1<<1), + VM_EXEC_ERROR = (1<<2), + }; + + class VMIntExpr; + class VMStringExpr; + class VMIntArrayExpr; + class VMStringArrayExpr; + + class VMExpr { + public: + virtual ExprType_t exprType() const = 0; + VMIntExpr* asInt() const; + VMStringExpr* asString() const; + VMIntArrayExpr* asIntArray() const; + }; + + class VMIntExpr : virtual public VMExpr { + public: + virtual int evalInt() = 0; + ExprType_t exprType() const { return INT_EXPR; } + }; + + class VMStringExpr : virtual public VMExpr { + public: + virtual String evalStr() = 0; + ExprType_t exprType() const { return STRING_EXPR; } + }; + + class VMArrayExpr : virtual public VMExpr { + public: + virtual int arraySize() const = 0; + }; + + class VMIntArrayExpr : virtual public VMArrayExpr { + public: + virtual int evalIntElement(uint i) = 0; + virtual void assignIntElement(uint i, int value) = 0; + ExprType_t exprType() const { return INT_ARR_EXPR; } + }; + + class VMFnArgs { + public: + virtual int argsCount() const = 0; + virtual VMExpr* arg(int i) = 0; + }; + + class VMFnResult { + public: + virtual VMExpr* resultValue() = 0; + virtual StmtFlags_t resultFlags() { return STMT_SUCCESS; } + }; + + /** @brief VM built-in function. + * + * Abstract base class for built-in script functions, defining the interface + * for all built-in script function implementations. + */ + class VMFunction { + public: + /** + * Script data type of the function's return value. If the function does + * not return any value, then it returns EMPTY_EXPR here. + */ + virtual ExprType_t returnType() = 0; + + /** + * Minimum amount of function arguments this function accepts. If a + * script is calling this function with less arguments, the script + * parser will throw a parser error. + */ + virtual int minRequiredArgs() const = 0; + + /** + * Maximum amount of function arguments this functions accepts. If a + * script is calling this function with more arguments, the script + * parser will throw a parser error. + */ + virtual int maxAllowedArgs() const = 0; + + /** + * Script data type of the function's @c iArg 'th function argument. + * The information provided here is less strong than acceptsArgType(). + * The parser will compare argument data types provided in scripts by + * calling cceptsArgType(). The return value of argType() is used by the + * parser instead to show an appropriate parser error which data type + * this function usually expects as "default" data type. Reason: a + * function may accept multiple data types for a certain function + * argument and would automatically cast the passed argument value in + * that case to the type it actually needs. + * + * @param iArg - index of the function argument in question + */ + virtual ExprType_t argType(int iArg) const = 0; + + /** + * This function is called by the parser to check whether arguments + * passed in scripts to this function are accepted by this function. If + * a script calls this function with an argument's data type not + * accepted by this function, the parser will throw a parser error. + * + * @param iArg - index of the function argument in question + * @param type - script data type used for this function argument by + * currently parsed script + */ + virtual bool acceptsArgType(int iArg, ExprType_t type) const = 0; + + /** + * Implements the actual function execution. This function is called by + * the VM when this function shall be executed at script runtime. + * + * @param args - function arguments for executing this built-in function + */ + virtual VMFnResult* exec(VMFnArgs* args) = 0; + + /** + * Concenience method for function implementations to show warning + * messages. + * + * @param txt - warning text + */ + void wrnMsg(const String& txt); + + /** + * Concenience method for function implementations to show error + * messages. + * + * @param txt - error text + */ + void errMsg(const String& txt); + }; + + /** + * POD base of VMIntRelPtr and VMInt8RelPtr structures. Not intended to be + * used directly. Use VMIntRelPtr or VMInt8RelPtr instead. + */ + struct VMRelPtr { + void** base; ///< Base pointer. + int offset; ///< Offset (in bytes) to base pointer. + }; + + /** @brief Pointer to built-in VM integer variable (of C/C++ type int). + * + * Used for defining built-in integer script variables. + * + * @b CAUTION: You may only use this class for pointing to C/C++ variables + * of type "int" (which on most systems is 32 bit in size). If the C/C++ int + * variable you want to reference is only 8 bit in size, then you @b must + * use VMInt8RelPtr instead! + * + * For efficiency reasons the actual native C/C++ int variable is referenced + * by two components here. The actual native int C/C++ variable in memory + * is dereferenced at VM run-time by taking the @c base pointer dereference + * and adding @c offset bytes. This has the advantage that for a large + * number of built-in int variables, only one (or few) base pointer need + * to be re-assigned before running a script, instead of updating each + * built-in variable each time before a script is executed. + * + * Refer to DECLARE_VMINT() for example code. + * + * @see VMInt8RelPtr, DECLARE_VMINT() + */ + struct VMIntRelPtr : VMRelPtr { + VMIntRelPtr() { + base = NULL; + offset = 0; + } + VMIntRelPtr(const VMRelPtr& data) { + base = data.base; + offset = data.offset; + } + virtual int evalInt() { return *(int*)&(*(uint8_t**)base)offset; } + virtual void assign(int i) { *(int*)&(*(uint8_t**)base)offset = i; } + }; + + /** @brief Pointer to built-in VM integer variable (of C/C++ type int8_t). + * + * Used for defining built-in integer script variables. + * + * @b CAUTION: You may only use this class for pointing to C/C++ variables + * of type "int8_t" (8 bit integer). If the C/C++ int variable you want to + * reference is an "int" type (which is 32 bit on most systems), then you + * @b must use VMIntRelPtr instead! + * + * For efficiency reasons the actual native C/C++ int variable is referenced + * by two components here. The actual native int C/C++ variable in memory + * is dereferenced at VM run-time by taking the @c base pointer dereference + * and adding @c offset bytes. This has the advantage that for a large + * number of built-in int variables, only one (or few) base pointer need + * to be re-assigned before running a script, instead of updating each + * built-in variable each time before a script is executed. + * + * Refer to DECLARE_VMINT() for example code. + * + * @see VMIntRelPtr, DECLARE_VMINT() + */ + struct VMInt8RelPtr : VMIntRelPtr { + VMInt8RelPtr() : VMIntRelPtr() {} + VMInt8RelPtr(const VMRelPtr& data) : VMIntRelPtr(data) {} + virtual int evalInt() OVERRIDE { + return *(uint8_t*)&(*(uint8_t**)base)offset; + } + virtual void assign(int i) OVERRIDE { + *(uint8_t*)&(*(uint8_t**)base)offset = i; + } + }; + + /** + * Convenience macro for initializing VMIntRelPtr and VMInt8RelPtr + * structures. Example: + * @code + * struct Foo { + * uint8_t a; + * int b; + * }; + * + * Foo foo1 = (Foo) { 1, 3000 }; + * Foo foo2 = (Foo) { 2, 4000 }; + * + * Foo* pFoo; + * + * VMInt8RelPtr var1 = DECLARE_VMINT(pFoo, class Foo, a); + * VMIntRelPtr var2 = DECLARE_VMINT(pFoo, class Foo, b); + * + * pFoo = &foo1; + * printf("%d\n", var1->evalInt()); // will print 1 + * printf("%d\n", var2->evalInt()); // will print 3000 + * + * pFoo = &foo2; + * printf("%d\n", var1->evalInt()); // will print 2 + * printf("%d\n", var2->evalInt()); // will print 4000 + * @endcode + */ + #define DECLARE_VMINT(basePtr, T_struct, T_member) ( \ + (VMRelPtr) { \ + (void**) &basePtr, \ + offsetof(T_struct, T_member) \ + } \ + ) \ + + /** @brief Built-in VM 8 bit integer array variable. + * + * Used for defining built-in integer array script variables. + */ + struct VMInt8Array { + int8_t* data; + int size; + + VMInt8Array() : data(NULL), size(0) {} + }; + + /** @brief Provider for built-in script functions and variables. + * + * Abstract base class defining the interface for all classes which add and + * implement built-in script functions and built-in script variables. + */ + class VMFunctionProvider { + public: + /** + * Returns pointer to the built-in function with the given function + * name, or NULL if there is no built-in function with that name. + * + * @param name - function name + */ + virtual VMFunction* functionByName(const String& name) = 0; + + /** + * Returns a variable name indexed map of all built-in script variables + * which point to native "int" (usually 32 bit) variables. + */ + virtual std::map<String,VMIntRelPtr*> builtInIntVariables() = 0; + + /** + * Returns a variable name indexed map of all built-in script variables + * which point to native "int8_t" (8 bit) variables. + */ + virtual std::map<String,VMInt8Array*> builtInIntArrayVariables() = 0; + + /** + * Returns a variable name indexed map of all built-in constant script + * variables, which never change their value at runtime. + */ + virtual std::map<String,int> builtInConstIntVariables() = 0; + }; + + /** @brief Execution state of a virtual machine. + * + * An instance of this abstract base class represents exactly one execution + * state of a virtual machine. This encompasses most notably the VM + * execution stack, and VM polyphonic variables. It does not contain global + * variable. Global variables are contained in the VMParserContext object. + * You might see a VMExecContext object as one virtual thread of the virtual + * machine. + * + * In contrast to a VMParserContext, a VMExecContext is not tied to a + * ScriptVM instance. Thus you can use a VMExecContext with different + * ScriptVM instances, however not concurrently at the same time. + * + * @see VMParserContext + */ + class VMExecContext { + public: + virtual ~VMExecContext() {} + virtual int suspensionTimeMicroseconds() const = 0; + }; + + /** @brief Script callback for a certain event. + * + * Represents a script callback for a certain event, i.e. + * "on note ... end on". + */ + class VMEventHandler { + public: + /** + * Name of the event handler which identifies its purpose. For example + * for a "on note ... end on" script callback block, the name "note" + * would be returned here. + */ + virtual String eventHandlerName() const = 0; + + /** + * Whether or not the event handler makes any use of so called + * "polyphonic" variables. + */ + virtual bool isPolyphonic() const = 0; + }; + + struct ParserIssue { + String txt; + int line; + ParserIssueType_t type; + + inline void dump() { + switch (type) { + case PARSER_ERROR: + printf("ERROR line %d: %s\n", line, txt.c_str()); + break; + case PARSER_WARNING: + printf("Warning line %d: %s\n", line, txt.c_str()); + break; + } + } + + inline bool isErr() const { return type == PARSER_ERROR; } + inline bool isWrn() const { return type == PARSER_WARNING; } + }; + + inline String typeStr(const ExprType_t& type) { + switch (type) { + case EMPTY_EXPR: return "empty"; + case INT_EXPR: return "integer"; + case INT_ARR_EXPR: return "integer array"; + case STRING_EXPR: return "string"; + case STRING_ARR_EXPR: return "string array"; + } + return "invalid"; + } + + /** @brief Virtual machine representation of a script. + * + * An instance of this abstract base class represents a parsed script, + * translated into a virtual machine. You should first check if there were + * any parser errors. If there were any parser errors, you should refrain + * from executing the virtual machine. Otherwise if there were no parser + * errors (i.e. only warnings), then you might access one of the script's + * event handlers by i.e. calling eventHandlerByName() and pass the + * respective event handler to the ScriptVM class (or to one of its + * descendants) for execution. + * + * @see VMExecContext + */ + class VMParserContext { + public: + virtual ~VMParserContext() {} + virtual std::vector<ParserIssue> issues() const = 0; + virtual std::vector<ParserIssue> errors() const = 0; + virtual std::vector<ParserIssue> warnings() const = 0; + virtual VMEventHandler* eventHandler(uint index) = 0; + virtual VMEventHandler* eventHandlerByName(const String& name) = 0; + }; + +} // namespace LinuxSampler + +#endif // LS_INSTR_SCRIPT_PARSER_COMMON_H
View file
linuxsampler-2718.tar.bz2/src/scriptvm/examples
Added
+(directory)
View file
linuxsampler-2718.tar.bz2/src/scriptvm/examples/array.txt
Added
@@ -0,0 +1,35 @@ +{ Array variables are always global variables, with constant size defined at + parse time. Trying to declare an array size with a runtime expression (i.e. a + non const variable), will cause a parser error. } + +on init + declare const $SIZE := 5 + declare %a$SIZE := ( 0,1,2,3,4 ) + declare $i + + message("num_elements(%a) = " & num_elements(%a)) + + message("Initial values of array:") + + $i := 0 + while ($i < $SIZE) + message("array" & $i & " = " & %a$i) + $i := $i + 1 + end while + + %a0 := 2 + %a1 := 3 + %a2 := 5 + %a3 := 7 + %a4 := 11 + + message("") + message("New values of array:") + + $i := 0 + while ($i < $SIZE) + message("array" & $i & " = " & %a$i) + $i := $i + 1 + end while + +end on
View file
linuxsampler-2718.tar.bz2/src/scriptvm/examples/exit.txt
Added
@@ -0,0 +1,14 @@ +{ The exit function can be used to stop current execution of the current event + handler. It does not stop overall execution of scripts. } + +on init + declare $foo := 1 + + message("if this is the last message you see, then exit works") + + if ($foo) + exit() + end if + + message("exit does not work!") +end on
View file
linuxsampler-2718.tar.bz2/src/scriptvm/examples/expressions.txt
Added
@@ -0,0 +1,33 @@ +on init + declare @s := "foo" & " bar" + message(@s) + + message("4 + 3 is " & 4 + 3) + message("10 mod 8 -> " & 10 mod 8) + + message("Relation 3 < 4 -> " & 3 < 4) + message("Relation 3 > 4 -> " & 3 > 4) + message("Relation 3 <= 4 -> " & 3 <= 4) + message("Relation 3 >= 4 -> " & 3 >= 4) + message("Relation 3 # 4 -> " & 3 # 4) + message("Relation 3 = 4 -> " & 3 = 4) + + message("Bit operation 1 and 2 = " & 1 and 2) + message("Bit operation 1 and 3 = " & 1 and 3) + message("Bit operation 1 and 0 = " & 1 and 0) + message("Bit operation 1 or 2 = " & 1 or 2) + message("Bit operation 1 or 1 = " & 1 or 1) + message("Bit operation 1 or 0 = " & 1 or 0) + message("Bit operation 0 or 0 = " & 0 or 0) + + message("Bit operation not 1 = " & not 1) + message("Bit operation not 2 = " & not 2) + message("Bit operation not 0 = " & not 0) + + message("abs(-3) = " & abs(-3)) + + message("random(-5,5) = " & random(-5,5)) + message("random(-5,5) = " & random(-5,5)) + message("random(-5,5) = " & random(-5,5)) + +end on
View file
linuxsampler-2718.tar.bz2/src/scriptvm/examples/helloworld.txt
Added
@@ -0,0 +1,9 @@ +on init + { this will throw a warning: int variable, but string expression assignment } + declare $a := "bla" + + declare @h := "Hello " + declare const @w := "world!" + message(@h) + message(@w) +end on
View file
linuxsampler-2718.tar.bz2/src/scriptvm/examples/if.txt
Added
@@ -0,0 +1,27 @@ +on init + declare $foo := 1 + + if ($foo) + message("if works.") + end if + + $foo := 0 + + if ($foo) + message("if does not work!") + end if + + if ($foo) + message("if else does not work!") + else + message("if else works.") + end if + + $foo := 1 + + if ($foo) + message("if else works.") + else + message("if else does not work!") + end if +end on
View file
linuxsampler-2718.tar.bz2/src/scriptvm/examples/polyphonic.txt
Added
@@ -0,0 +1,19 @@ +{ Polyhonic variables are, in constrast to all other variable types, not global + variables, but instead they exist separately in each event. Which circumvents + the problem that (global) variables may be altered undesiredly if event + handlers are called in "parallel". Downside: polyphonic variables need to be + allocated as many times as events exist in the system. Thus: one single + polyphonic variable takes a lot of memory! Due to this reason, only integer + variables are allowed to be declared as polyphonics ATM. } + +on init + declare polyphonic $p +end on + +on note + message($p) + $p := $p + 1 + message($p) + $p := $p + 1 + message($p) +end on
View file
linuxsampler-2718.tar.bz2/src/scriptvm/examples/preprocessor.txt
Added
@@ -0,0 +1,57 @@ +on init + + { These are preprocessor statements, which are not executed at runtime. + They are instead processed at early parser stage. You can set a + preprocessor condition with: + + SET_CONDITION(<condition-name>) + + Then you can use such preprocessor conditions to conditionally let the + script parser know which code parts shall be used and which code parts + shall be ignored. The ignored code parts will never be executed and thus + will not waste any resources at runtime. + + There are also built-in coditions, already defined by the sampler, which + allows you to execude script parts depending on certain circumstances. + } + + SET_CONDITION(foo) + + { This will raise a warning, because it has already been set before. } + SET_CONDITION(foo) + + USE_CODE_IF(foo) + message("SET_CONDITION works.") + END_USE_CODE + + USE_CODE_IF_NOT(foo) + message("SET_CONDITION does not work!") + END_USE_CODE + + RESET_CONDITION(foo) + + USE_CODE_IF_NOT(foo) + message("RESET_CONDITION works.") + END_USE_CODE + + USE_CODE_IF(foo) + message("RESET_CONDITION does not work!") + END_USE_CODE + + + SET_CONDITION(bar) + + USE_CODE_IF(bar) + SET_CONDITION(bla) + USE_CODE_IF(bla) + message("Nested conditions work.") + END_USE_CODE + USE_CODE_IF_NOT(bla) + message("Nested conditions do not work!") + END_USE_CODE + END_USE_CODE + + { If you uncomment the following, it will throw an error. } + { RESET_CONDITION(somethingthatdoesnotexist) } + +end on
View file
linuxsampler-2718.tar.bz2/src/scriptvm/examples/selectcase.txt
Added
@@ -0,0 +1,24 @@ +on init + declare $foo := 7 + + { you can also use parentheses, i.e.: select ($foo) } + select $foo + case 0 + message("unary select case does not work!") + case 6 + message("unary select case does not work!") + case 7 + message("unary select case works.") + case 8 + message("unary select case does not work!") + end select + + select $foo + case 0 to 4 + message("range select case does not work!") + case 10 to 12 + message("range select case does not work!") + case 5 to 9 + message("range select case works.") + end select +end on
View file
linuxsampler-2718.tar.bz2/src/scriptvm/examples/simple.txt
Added
@@ -0,0 +1,8 @@ +on init + declare $g1 := 3 + + declare const $c1 := 5 + { declare const $c2 := $g1 } + declare const $c3 := $c1 + +end on
View file
linuxsampler-2718.tar.bz2/src/scriptvm/examples/wait.txt
Added
@@ -0,0 +1,19 @@ +{ The wait function can be used to suspend execution of the current event + handler for the given amount of microseconds. Note that execution of an event + handler might also be automatically suspended by the sampler in case its + execution exceeded at certain limit in time. + + If you script might get suspended for the reasons mentioned above, you should + be aware that all variables are global variables by default. So after a + suspended event handler execution is woken up again, the respective global + variables might not reflect what you wanted them to be, because in the + meantime other execution instances of your script might have altered them + already. In case this is an undesired behavior for you, then you should use + the "polyphonic" variable type for the respective variable instead. +} + +on note + message("function should suspend now") + wait(1000000) + message("this would be after suspension of 1000000 microseconds") +end on
View file
linuxsampler-2718.tar.bz2/src/scriptvm/examples/while.txt
Added
@@ -0,0 +1,8 @@ +on init + declare $i := 3 + + while ($i) + message("Print this three times.") + $i := $i - 1 + end while +end on
View file
linuxsampler-2718.tar.bz2/src/scriptvm/parser.y
Added
@@ -0,0 +1,669 @@ +/* + * Copyright (c) 2014 Christian Schoenebeck and Andreas Persson + * + * http://www.linuxsampler.org + * + * This file is part of LinuxSampler and released under the same terms. + * See README file for details. + */ + +%{ + #define YYERROR_VERBOSE 1 + #include "parser_shared.h" + #include <string> + #include <map> + using namespace LinuxSampler; + + void InstrScript_error(YYLTYPE* locp, LinuxSampler::ParserContext* context, const char* err); + void InstrScript_warning(YYLTYPE* locp, LinuxSampler::ParserContext* context, const char* txt); + int InstrScript_lex(YYSTYPE* lvalp, YYLTYPE* llocp, void* scanner); + #define scanner context->scanner + #define PARSE_ERR(txt) yyerror(&yylloc, context, txt) + #define PARSE_WRN(txt) InstrScript_warning(&yylloc, context, txt) +%} + +// generate reentrant safe parser +%pure-parser +%parse-param { LinuxSampler::ParserContext* context } +%lex-param { void* scanner } +// avoid symbol collision with other (i.e. future) auto generated (f)lex scanners +%name-prefix "InstrScript_" +%locations +%defines +%error-verbose + +%token <iValue> INTEGER +%token <sValue> STRING +%token <sValue> IDENTIFIER +%token <sValue> VARIABLE +%token ON END INIT NOTE DECLARE ASSIGNMENT WHILE IF OR RELEASE AND ELSE +%token CONTROLLER SELECT CASE TO NOT CONST_ POLYPHONIC MOD +%token LE GE + +%type <nEventHandlers> script eventhandlers +%type <nEventHandler> eventhandler +%type <nStatements> statements +%type <nStatement> statement assignment +%type <nFunctionCall> functioncall +%type <nArgs> args +%type <nExpression> arg expr or_expr and_expr rel_expr add_expr mul_expr unary_expr concat_expr +%type <nCaseBranch> caseclause +%type <nCaseBranches> caseclauses + +%start script + +%% + +script: + eventhandlers { + $$ = context->handlers = $1; + } + +eventhandlers: + eventhandler { + $$ = new EventHandlers(); + $$->add($1); + } + | eventhandlers eventhandler { + $$ = $1; + $$->add($2); + } + +eventhandler: + ON NOTE statements END ON { + if (context->onNote) + PARSE_ERR("Redeclaration of 'note' event handler."); + context->onNote = new OnNote($3); + $$ = context->onNote; + } + | ON INIT statements END ON { + if (context->onInit) + PARSE_ERR("Redeclaration of 'init' event handler."); + context->onInit = new OnInit($3); + $$ = context->onInit; + } + | ON RELEASE statements END ON { + if (context->onRelease) + PARSE_ERR("Redeclaration of 'release' event handler."); + context->onRelease = new OnRelease($3); + $$ = context->onRelease; + } + | ON CONTROLLER statements END ON { + if (context->onController) + PARSE_ERR("Redeclaration of 'controller' event handler."); + context->onController = new OnController($3); + $$ = context->onController; + } + +statements: + statement { + $$ = new Statements(); + if ($1) { + if (!isNoOperation($1)) $$->add($1); // filter out NoOperation statements + } else + PARSE_WRN("Not a statement."); + } + | statements statement { + $$ = $1; + if ($2) { + if (!isNoOperation($2)) $$->add($2); // filter out NoOperation statements + } else + PARSE_WRN("Not a statement."); + } + +statement: + functioncall { + $$ = $1; + } + | DECLARE VARIABLE { + const char* name = $2; + //printf("declared var '%s'\n", name); + if (context->variableByName(name)) + PARSE_ERR((String("Redeclaration of variable '") + name + "'.").c_str()); + if (name0 == '@') { + context->vartablename = new StringVariable(context); + $$ = new NoOperation; + } else { + context->vartablename = new IntVariable(context); + $$ = new NoOperation; + } + } + | DECLARE POLYPHONIC VARIABLE { + const char* name = $3; + //printf("declared polyphonic var '%s'\n", name); + if (context->variableByName(name)) + PARSE_ERR((String("Redeclaration of variable '") + name + "'.").c_str()); + if (name0 != '$') { + PARSE_ERR("Polyphonic variables may only be declared as integers."); + $$ = new FunctionCall("nothing", new Args, NULL); // whatever + } else { + context->vartablename = new PolyphonicIntVariable(context); + $$ = new NoOperation; + } + } + | DECLARE VARIABLE ASSIGNMENT expr { + const char* name = $2; + //printf("declared assign var '%s'\n", name); + if (context->variableByName(name)) + PARSE_ERR((String("Redeclaration of variable '") + name + "'.").c_str()); + if ($4->exprType() == STRING_EXPR) { + if (name0 == '$') + PARSE_WRN((String("Variable '") + name + "' declared as integer, string expression assigned though.").c_str()); + StringExprRef expr = $4; + if (expr->isConstExpr()) { + const String s = expr->evalStr(); + StringVariableRef var = new StringVariable(context); + context->vartablename = var; + $$ = new Assignment(var, new StringLiteral(s)); + } else { + StringVariableRef var = new StringVariable(context); + context->vartablename = var; + $$ = new Assignment(var, expr); + } + } else { + if (name0 == '@') + PARSE_WRN((String("Variable '") + name + "' declared as string, integer expression assigned though.").c_str()); + IntExprRef expr = $4; + if (expr->isConstExpr()) { + const int i = expr->evalInt(); + IntVariableRef var = new IntVariable(context); + context->vartablename = var; + $$ = new Assignment(var, new IntLiteral(i)); + } else { + IntVariableRef var = new IntVariable(context); + context->vartablename = var; + $$ = new Assignment(var, expr); + } + } + } + | DECLARE VARIABLE '' expr '' { + //printf("declare array without args\n"); + const char* name = $2; + if (!$4->isConstExpr()) { + PARSE_ERR((String("Array variable '") + name + "' must be declared with constant array size.").c_str()); + $$ = new FunctionCall("nothing", new Args, NULL); // whatever + } else if ($4->exprType() != INT_EXPR) { + PARSE_ERR((String("Size of array variable '") + name + "' declared with non integer expression.").c_str()); + $$ = new FunctionCall("nothing", new Args, NULL); // whatever + } else if (context->variableByName(name)) { + PARSE_ERR((String("Redeclaration of variable '") + name + "'.").c_str()); + $$ = new FunctionCall("nothing", new Args, NULL); // whatever + } else { + IntExprRef expr = $4; + int size = expr->evalInt(); + if (size <= 0) { + PARSE_ERR((String("Array variable '") + name + "' declared with array size " + ToString(size) + ".").c_str()); + $$ = new FunctionCall("nothing", new Args, NULL); // whatever + } else { + context->vartablename = new IntArrayVariable(context, size); + $$ = new NoOperation; + } + } + } + | DECLARE VARIABLE '' expr '' ASSIGNMENT '(' args ')' { + const char* name = $2; + if (!$4->isConstExpr()) { + PARSE_ERR((String("Array variable '") + name + "' must be declared with constant array size.").c_str()); + $$ = new FunctionCall("nothing", new Args, NULL); // whatever + } else if ($4->exprType() != INT_EXPR) { + PARSE_ERR((String("Size of array variable '") + name + "' declared with non integer expression.").c_str()); + $$ = new FunctionCall("nothing", new Args, NULL); // whatever + } else if (context->variableByName(name)) { + PARSE_ERR((String("Redeclaration of variable '") + name + "'.").c_str()); + $$ = new FunctionCall("nothing", new Args, NULL); // whatever + } else { + IntExprRef sizeExpr = $4; + ArgsRef args = $8; + int size = sizeExpr->evalInt(); + if (size <= 0) { + PARSE_ERR((String("Array variable '") + name + "' must be declared with positive array size.").c_str()); + $$ = new FunctionCall("nothing", new Args, NULL); // whatever + } else if (args->argsCount() > size) { + PARSE_ERR((String("Variable '") + name + + "' was declared with size " + ToString(size) + + " but " + ToString(args->argsCount()) + + " values were assigned." ).c_str()); + $$ = new FunctionCall("nothing", new Args, NULL); // whatever + } else { + bool argsOK = true; + for (int i = 0; i < args->argsCount(); ++i) { + if (args->arg(i)->exprType() != INT_EXPR) { + PARSE_ERR( + (String("Array variable '") + name + + "' declared with invalid assignment values. Assigned element " + + ToString(i+1) + " is not an integer expression.").c_str() + ); + argsOK = false; + break; + } + } + if (argsOK) + $$ = context->vartablename = new IntArrayVariable(context, size, args); + else + $$ = new FunctionCall("nothing", new Args, NULL); // whatever + } + } + } + | DECLARE CONST_ VARIABLE ASSIGNMENT expr { + const char* name = $3; + if ($5->exprType() == STRING_EXPR) { + if (name0 == '$') + PARSE_WRN("Variable declared as integer, string expression assigned though."); + String s; + StringExprRef expr = $5; + if (expr->isConstExpr()) + s = expr->evalStr(); + else + PARSE_ERR((String("Assignment to const string variable '") + name + "' requires const expression.").c_str()); + ConstStringVariableRef var = new ConstStringVariable(context, s); + context->vartablename = var; + //$$ = new Assignment(var, new StringLiteral(s)); + $$ = new NoOperation(); + } else { + if (name0 == '@') + PARSE_WRN("Variable declared as string, integer expression assigned though."); + int i = 0; + IntExprRef expr = $5; + if (expr->isConstExpr()) + i = expr->evalInt(); + else + PARSE_ERR((String("Assignment to const integer variable '") + name + "' requires const expression.").c_str()); + ConstIntVariableRef var = new ConstIntVariable(i); + context->vartablename = var; + //$$ = new Assignment(var, new IntLiteral(i)); + $$ = new NoOperation(); + } + } + | assignment { + $$ = $1; + } + | WHILE '(' expr ')' statements END WHILE { + if ($3->exprType() == INT_EXPR) { + $$ = new While($3, $5); + } else { + PARSE_ERR("Condition for 'while' loops must be integer expression."); + $$ = new While(new IntLiteral(0), $5); + } + } + | IF '(' expr ')' statements ELSE statements END IF { + $$ = new If($3, $5, $7); + } + | IF '(' expr ')' statements END IF { + $$ = new If($3, $5); + } + | SELECT expr caseclauses END SELECT { + if ($2->exprType() == INT_EXPR) { + $$ = new SelectCase($2, $3); + } else { + PARSE_ERR("Statement 'select' can only by applied to integer expressions."); + $$ = new SelectCase(new IntLiteral(0), $3); + } + } + +caseclauses: + caseclause { + $$ = CaseBranches(); + $$.push_back($1); + } + | caseclauses caseclause { + $$ = $1; + $$.push_back($2); + } + +caseclause: + CASE INTEGER statements { + $$ = CaseBranch(); + $$.from = new IntLiteral($2); + $$.statements = $3; + } + | CASE INTEGER TO INTEGER statements { + $$ = CaseBranch(); + $$.from = new IntLiteral($2); + $$.to = new IntLiteral($4); + $$.statements = $5; + } + +functioncall: + IDENTIFIER '(' args ')' { + const char* name = $1; + //printf("function call of '%s' with args\n", name); + ArgsRef args = $3; + VMFunction* fn = context->functionProvider->functionByName(name); + if (!fn) { + PARSE_ERR((String("No built-in function with name '") + name + "'.").c_str()); + $$ = new FunctionCall(name, args, NULL); + } else if (args->argsCount() < fn->minRequiredArgs()) { + PARSE_ERR((String("Built-in function '") + name + "' requires at least " + ToString(fn->minRequiredArgs()) + " arguments.").c_str()); + $$ = new FunctionCall(name, args, NULL); + } else if (args->argsCount() > fn->maxAllowedArgs()) { + PARSE_ERR((String("Built-in function '") + name + "' accepts max. " + ToString(fn->maxAllowedArgs()) + " arguments.").c_str()); + $$ = new FunctionCall(name, args, NULL); + } else { + bool argsOK = true; + for (int i = 0; i < args->argsCount(); ++i) { + if (args->arg(i)->exprType() != fn->argType(i) && !fn->acceptsArgType(i, args->arg(i)->exprType())) { + PARSE_ERR((String("Argument ") + ToString(i+1) + " of built-in function '" + name + "' expects " + typeStr(fn->argType(i)) + " type, but type " + typeStr(args->arg(i)->exprType()) + " was given instead.").c_str()); + argsOK = false; + break; + } + } + $$ = new FunctionCall(name, args, argsOK ? fn : NULL); + } + } + | IDENTIFIER '(' ')' { + const char* name = $1; + //printf("function call of '%s' (with empty args)\n", name); + ArgsRef args = new Args; + VMFunction* fn = context->functionProvider->functionByName(name); + if (!fn) { + PARSE_ERR((String("No built-in function with name '") + name + "'.").c_str()); + $$ = new FunctionCall(name, args, NULL); + } else if (fn->minRequiredArgs() > 0) { + PARSE_ERR((String("Built-in function '") + name + "' requires at least " + ToString(fn->minRequiredArgs()) + " arguments.").c_str()); + $$ = new FunctionCall(name, args, NULL); + } else { + $$ = new FunctionCall(name, args, fn); + } + } + | IDENTIFIER { + const char* name = $1; + //printf("function call of '%s' (without args)\n", name); + ArgsRef args = new Args; + VMFunction* fn = context->functionProvider->functionByName(name); + if (!fn) { + PARSE_ERR((String("No built-in function with name '") + name + "'.").c_str()); + $$ = new FunctionCall(name, args, NULL); + } else if (fn->minRequiredArgs() > 0) { + PARSE_ERR((String("Built-in function '") + name + "' requires at least " + ToString(fn->minRequiredArgs()) + " arguments.").c_str()); + $$ = new FunctionCall(name, args, NULL); + } else { + $$ = new FunctionCall(name, args, fn); + } + } + +args: + arg { + $$ = new Args(); + $$->add($1); + } + | args ',' arg { + $$ = $1; + $$->add($3); + } + +arg: + expr + +assignment: + VARIABLE ASSIGNMENT expr { + //printf("variable lookup with name '%s' as assignment expr\n", $1); + const char* name = $1; + VariableRef var = context->variableByName(name); + if (!var) + PARSE_ERR((String("Variable assignment: No variable declared with name '") + name + "'.").c_str()); + else if (var->isConstExpr()) + PARSE_ERR((String("Variable assignment: Cannot modify const variable '") + name + "'.").c_str()); + else if (var->exprType() != $3->exprType()) + PARSE_ERR((String("Variable assignment: Variable '") + name + "' is of type " + typeStr(var->exprType()) + ", assignment is of type " + typeStr($3->exprType()) + " though.").c_str()); + $$ = new Assignment(var, $3); + } + | VARIABLE '' expr '' ASSIGNMENT expr { + const char* name = $1; + VariableRef var = context->variableByName(name); + if (!var) + PARSE_ERR((String("No variable declared with name '") + name + "'.").c_str()); + else if (var->exprType() != INT_ARR_EXPR) + PARSE_ERR((String("Variable '") + name + "' is not an array variable.").c_str()); + else if ($3->exprType() != INT_EXPR) + PARSE_ERR((String("Array variable '") + name + "' accessed with non integer expression.").c_str()); + else if ($6->exprType() != INT_EXPR) + PARSE_ERR((String("Value assigned to array variable '") + name + "' must be an integer expression.").c_str()); + IntArrayElementRef element = new IntArrayElement(var, $3); + $$ = new Assignment(element, $6); + } + +unary_expr: + INTEGER { + $$ = new IntLiteral($1); + } + | STRING { + $$ = new StringLiteral($1); + } + | VARIABLE { + //printf("variable lookup with name '%s' as unary expr\n", $1); + VariableRef var = context->variableByName($1); + if (var) + $$ = var; + else { + PARSE_ERR((String("No variable declared with name '") + $1 + "'.").c_str()); + $$ = new IntLiteral(0); + } + } + | VARIABLE '' expr '' { + const char* name = $1; + VariableRef var = context->variableByName(name); + if (!var) { + PARSE_ERR((String("No variable declared with name '") + name + "'.").c_str()); + $$ = new IntLiteral(0); + } else if (var->exprType() != INT_ARR_EXPR) { + PARSE_ERR((String("Variable '") + name + "' is not an array variable.").c_str()); + $$ = new IntLiteral(0); + } else if ($3->exprType() != INT_EXPR) { + PARSE_ERR((String("Array variable '") + name + "' accessed with non integer expression.").c_str()); + $$ = new IntLiteral(0); + } else { + $$ = new IntArrayElement(var, $3); + } + } + | '(' expr ')' { + $$ = $2; + } + | functioncall { + $$ = $1; + } + | '-' unary_expr { + $$ = new Neg($2); + } + | NOT unary_expr { + if ($2->exprType() != INT_EXPR) { + PARSE_ERR((String("Right operand of operator 'not' must be an integer expression, is ") + typeStr($2->exprType()) + " though.").c_str()); + $$ = new IntLiteral(0); + } else { + $$ = new Not($2); + } + } + +expr: + concat_expr + +concat_expr: + or_expr + | concat_expr '&' or_expr { + ExpressionRef lhs = $1; + ExpressionRef rhs = $3; + if (lhs->isConstExpr() && rhs->isConstExpr()) { + $$ = new StringLiteral( + lhs->evalCastToStr() + rhs->evalCastToStr() + ); + } else { + $$ = new ConcatString(lhs, rhs); + } + } + +or_expr: + and_expr + | or_expr OR and_expr { + ExpressionRef lhs = $1; + ExpressionRef rhs = $3; + if (lhs->exprType() != INT_EXPR) { + PARSE_ERR((String("Left operand of operator 'or' must be an integer expression, is ") + typeStr(lhs->exprType()) + " though.").c_str()); + $$ = new IntLiteral(0); + } else if (rhs->exprType() != INT_EXPR) { + PARSE_ERR((String("Right operand of operator 'or' must be an integer expression, is ") + typeStr(rhs->exprType()) + " though.").c_str()); + $$ = new IntLiteral(0); + } else { + $$ = new Or(lhs, rhs); + } + } + +and_expr: + rel_expr { + $$ = $1; + } + | and_expr AND rel_expr { + ExpressionRef lhs = $1; + ExpressionRef rhs = $3; + if (lhs->exprType() != INT_EXPR) { + PARSE_ERR((String("Left operand of operator 'and' must be an integer expression, is ") + typeStr(lhs->exprType()) + " though.").c_str()); + $$ = new IntLiteral(0); + } else if (rhs->exprType() != INT_EXPR) { + PARSE_ERR((String("Right operand of operator 'and' must be an integer expression, is ") + typeStr(rhs->exprType()) + " though.").c_str()); + $$ = new IntLiteral(0); + } else { + $$ = new And(lhs, rhs); + } + } + +rel_expr: + add_expr + | rel_expr '<' add_expr { + ExpressionRef lhs = $1; + ExpressionRef rhs = $3; + if (lhs->exprType() != INT_EXPR) { + PARSE_ERR((String("Left operand of operator '<' must be an integer expression, is ") + typeStr(lhs->exprType()) + " though.").c_str()); + $$ = new IntLiteral(0); + } else if (rhs->exprType() != INT_EXPR) { + PARSE_ERR((String("Right operand of operator '<' must be an integer expression, is ") + typeStr(rhs->exprType()) + " though.").c_str()); + $$ = new IntLiteral(0); + } else { + $$ = new Relation(lhs, Relation::LESS_THAN, rhs); + } + } + | rel_expr '>' add_expr { + ExpressionRef lhs = $1; + ExpressionRef rhs = $3; + if (lhs->exprType() != INT_EXPR) { + PARSE_ERR((String("Left operand of operator '>' must be an integer expression, is ") + typeStr(lhs->exprType()) + " though.").c_str()); + $$ = new IntLiteral(0); + } else if (rhs->exprType() != INT_EXPR) { + PARSE_ERR((String("Right operand of operator '>' must be an integer expression, is ") + typeStr(rhs->exprType()) + " though.").c_str()); + $$ = new IntLiteral(0); + } else { + $$ = new Relation(lhs, Relation::GREATER_THAN, rhs); + } + } + | rel_expr LE add_expr { + ExpressionRef lhs = $1; + ExpressionRef rhs = $3; + if (lhs->exprType() != INT_EXPR) { + PARSE_ERR((String("Left operand of operator '<=' must be an integer expression, is ") + typeStr(lhs->exprType()) + " though.").c_str()); + $$ = new IntLiteral(0); + } else if (rhs->exprType() != INT_EXPR) { + PARSE_ERR((String("Right operand of operator '<=' must be an integer expression, is ") + typeStr(rhs->exprType()) + " though.").c_str()); + $$ = new IntLiteral(0); + } else { + $$ = new Relation(lhs, Relation::LESS_OR_EQUAL, rhs); + } + } + | rel_expr GE add_expr { + ExpressionRef lhs = $1; + ExpressionRef rhs = $3; + if (lhs->exprType() != INT_EXPR) { + PARSE_ERR((String("Left operand of operator '>=' must be an integer expression, is ") + typeStr(lhs->exprType()) + " though.").c_str()); + $$ = new IntLiteral(0); + } else if (rhs->exprType() != INT_EXPR) { + PARSE_ERR((String("Right operand of operator '>=' must be an integer expression, is ") + typeStr(rhs->exprType()) + " though.").c_str()); + $$ = new IntLiteral(0); + } else { + $$ = new Relation(lhs, Relation::GREATER_OR_EQUAL, rhs); + } + } + | rel_expr '=' add_expr { + $$ = new Relation($1, Relation::EQUAL, $3); + } + | rel_expr '#' add_expr { + $$ = new Relation($1, Relation::NOT_EQUAL, $3); + } + +add_expr: + mul_expr + | add_expr '+' mul_expr { + ExpressionRef lhs = $1; + ExpressionRef rhs = $3; + if (lhs->exprType() != INT_EXPR) { + PARSE_ERR((String("Left operand of operator '+' must be an integer expression, is ") + typeStr(lhs->exprType()) + " though.").c_str()); + $$ = new IntLiteral(0); + } else if (rhs->exprType() != INT_EXPR) { + PARSE_ERR((String("Right operand of operator '+' must be an integer expression, is ") + typeStr(rhs->exprType()) + " though.").c_str()); + $$ = new IntLiteral(0); + } else { + $$ = new Add(lhs,rhs); + } + } + | add_expr '-' mul_expr { + ExpressionRef lhs = $1; + ExpressionRef rhs = $3; + if (lhs->exprType() != INT_EXPR) { + PARSE_ERR((String("Left operand of operator '-' must be an integer expression, is ") + typeStr(lhs->exprType()) + " though.").c_str()); + $$ = new IntLiteral(0); + } else if (rhs->exprType() != INT_EXPR) { + PARSE_ERR((String("Right operand of operator '-' must be an integer expression, is ") + typeStr(rhs->exprType()) + " though.").c_str()); + $$ = new IntLiteral(0); + } else { + $$ = new Sub(lhs,rhs); + } + } + +mul_expr: + unary_expr + | mul_expr '*' unary_expr { + ExpressionRef lhs = $1; + ExpressionRef rhs = $3; + if (lhs->exprType() != INT_EXPR) { + PARSE_ERR((String("Left operand of operator '*' must be an integer expression, is ") + typeStr(lhs->exprType()) + " though.").c_str()); + $$ = new IntLiteral(0); + } else if (rhs->exprType() != INT_EXPR) { + PARSE_ERR((String("Right operand of operator '*' must be an integer expression, is ") + typeStr(rhs->exprType()) + " though.").c_str()); + $$ = new IntLiteral(0); + } else { + $$ = new Mul(lhs,rhs); + } + } + | mul_expr '/' unary_expr { + ExpressionRef lhs = $1; + ExpressionRef rhs = $3; + if (lhs->exprType() != INT_EXPR) { + PARSE_ERR((String("Left operand of operator '/' must be an integer expression, is ") + typeStr(lhs->exprType()) + " though.").c_str()); + $$ = new IntLiteral(0); + } else if (rhs->exprType() != INT_EXPR) { + PARSE_ERR((String("Right operand of operator '/' must be an integer expression, is ") + typeStr(rhs->exprType()) + " though.").c_str()); + $$ = new IntLiteral(0); + } else { + $$ = new Div(lhs,rhs); + } + } + | mul_expr MOD unary_expr { + ExpressionRef lhs = $1; + ExpressionRef rhs = $3; + if (lhs->exprType() != INT_EXPR) { + PARSE_ERR((String("Left operand of modulo operator must be an integer expression, is ") + typeStr(lhs->exprType()) + " though.").c_str()); + $$ = new IntLiteral(0); + } else if (rhs->exprType() != INT_EXPR) { + PARSE_ERR((String("Right operand of modulo operator must be an integer expression, is ") + typeStr(rhs->exprType()) + " though.").c_str()); + $$ = new IntLiteral(0); + } else { + $$ = new Mod(lhs,rhs); + } + } + +%% + +void InstrScript_error(YYLTYPE* locp, LinuxSampler::ParserContext* context, const char* err) { + //fprintf(stderr, "%d: %s\n", locp->first_line, err); + context->addErr(locp->first_line, err); +} + +void InstrScript_warning(YYLTYPE* locp, LinuxSampler::ParserContext* context, const char* txt) { + //fprintf(stderr, "WRN %d: %s\n", locp->first_line, txt); + context->addWrn(locp->first_line, txt); +}
View file
linuxsampler-2718.tar.bz2/src/scriptvm/parser_shared.h
Added
@@ -0,0 +1,39 @@ +/* + * Copyright (c) 2014 Christian Schoenebeck + * + * http://www.linuxsampler.org + * + * This file is part of LinuxSampler and released under the same terms. + * See README file for details. + */ + +// types shared between auto generated lexer and parser ... + +#ifndef LS_INSTRSCRIPTSPARSER_SHARED_H +#define LS_INSTRSCRIPTSPARSER_SHARED_H + +#include <stdio.h> +#include "tree.h" +#include "parser.h" +#include "../common/global_private.h" + +struct _YYSTYPE { + union { + int iValue; + char* sValue; + }; + LinuxSampler::EventHandlersRef nEventHandlers; + LinuxSampler::EventHandlerRef nEventHandler; + LinuxSampler::StatementsRef nStatements; + LinuxSampler::StatementRef nStatement; + LinuxSampler::FunctionCallRef nFunctionCall; + LinuxSampler::ArgsRef nArgs; + LinuxSampler::ExpressionRef nExpression; + LinuxSampler::CaseBranch nCaseBranch; + LinuxSampler::CaseBranches nCaseBranches; +}; +#define YYSTYPE _YYSTYPE +#define yystype YYSTYPE ///< For backward compatibility. +#define YYSTYPE_IS_DECLARED ///< We tell the lexer / parser that we use our own data structure as defined above. + +#endif // LS_INSTRSCRIPTSPARSER_SHARED_H
View file
linuxsampler-2718.tar.bz2/src/scriptvm/scanner.l
Added
@@ -0,0 +1,272 @@ +/* + * Copyright (c) 2014 Christian Schoenebeck and Andreas Persson + * + * http://www.linuxsampler.org + * + * This file is part of LinuxSampler and released under the same terms. + * See README file for details. + */ + +/* Token scanner for instrument script language. */ + +/* FIXME: line numbers (i.e. on error/warning messages) are incorrect, because + the current grammar does not process new line characters. \n is currently + filtered out in this lexer. Due to this, the parser will sometimes read + several lines before it matches the next complete grammar rule, causing the + incorrect lines in the error/warning messages. */ + +%{ + +#include "parser_shared.h" +#include <math.h> +// reentrant scanner data context +#define YY_EXTRA_TYPE ParserContext* +// set line number each time a token is recognized +#define YY_USER_ACTION \ +{ \ + yylloc->first_line = yylineno; \ + yylloc->last_line = yylineno; \ + /* first_column = TODO */ \ + /* last_column = TODO */ \ + /*printf("lex: line '%s'\n", yytext);*/ \ +} +// custom (f)lex input for reading from std::istream object +#define YY_INPUT(buf,result,max_size) \ +{ \ + char c = yyextra->is->get(); \ + if (yyextra->is->eof()) \ + result = YY_NULL; \ + else { \ + buf0 = c; \ + result = 1; \ + } \ +} + +static void scanner_error(YYLTYPE* locp, LinuxSampler::ParserContext* context, const char* err) { + context->addErr(locp->first_line, err); +} + +static void scanner_warning(YYLTYPE* locp, LinuxSampler::ParserContext* context, const char* txt) { + context->addWrn(locp->first_line, txt); +} + +#define SCANNER_ERR(txt) scanner_error(yylloc, yyextra, txt) +#define SCANNER_WRN(txt) scanner_warning(yylloc, yyextra, txt) + +using namespace LinuxSampler; + +%} + +/* use Flex's built-in support for line numbers */ +%option yylineno +/* generate a reentrant safe scanner */ +%option reentrant +/* avoid symbol collision with other (i.e. future) scanner symbols */ +%option prefix="InstrScript_" +/* bison-bridge adds an argument yylval to yylex, and bison-locations adds an + argument code yylloc for location tracking. */ +%option bison-bridge +%option bison-locations +/* yywrap() would be called at EOF, we don't need it */ +%option noyywrap +/* enable functions yy_push_state(), yy_pop_state(), yy_top_state() */ +%option stack + +/* inclusive scanner conditions */ +%s PREPROC_BODY_USE +/* exclusive scanner conditions */ +%x PREPROC_SET_COND PREPROC_RESET_COND PREPROC_IF PREPROC_IF_NOT PREPROC_BODY_EAT PREPROC_PRE_BODY_USE PREPROC_PRE_BODY_EAT + +DIGIT 0-9 +ID a-zA-Z0-9_* + +%% + +\"^"*\" { + yylval->sValue = strdup(yytext + 1); + yylval->sValuestrlen(yylval->sValue) - 1 = '\0'; + return STRING; +} + +{DIGIT}+ { + yylval->iValue = atoi(yytext); + return INTEGER; +} + +{DIGIT}+"."{DIGIT}* { + printf("A float: %s (%g)\n", yytext, atof(yytext)); +} + + + /* Preprocessor statement: SET_CONDITION(name) */ + +<*>"SET_CONDITION" \t*"(" { + //printf("SET_CONDITION\n"); + yy_push_state(PREPROC_SET_COND, yyscanner); +} +<PREPROC_SET_COND>{ID} { + //printf("preproc set condition '%s'\n", yytext); + bool success = yyextra->setPreprocessorCondition(yytext); + if (!success) { + SCANNER_WRN((String("Preprocessor: Condition '") + + yytext + "' is already set.").c_str()); + } +} +<PREPROC_SET_COND> \t*")" { + //printf("End of PREPROC_SET_COND\n"); + yy_pop_state(yyscanner); +} + + + /* Preprocessor statement: RESET_CONDITION(name) */ + +<*>"RESET_CONDITION" \t*"(" { + //printf("RESET_CONDITION\n"); + yy_push_state(PREPROC_RESET_COND, yyscanner); +} +<PREPROC_RESET_COND>{ID} { + //printf("preproc reset condition '%s'\n", yytext); + bool success = yyextra->resetPreprocessorCondition(yytext); + if (!success) { + SCANNER_ERR((String("Preprocessor: could not reset condition '") + + yytext + "' (either not set or a built-in condition).").c_str()); + } +} +<PREPROC_RESET_COND> \t*")" { + //printf("End of RESET_CONDITION\n"); + yy_pop_state(yyscanner); +} + + + /* Preprocessor conditional statements: + + USE_CODE_IF(name) + ... + END_USE_CODE + + and: + + USE_CODE_IF_NOT(name) + ... + END_USE_CODE + */ + +<*>"USE_CODE_IF" \t*"(" { + //printf("USE_CODE_IF\n"); + yy_push_state(PREPROC_IF, yyscanner); +} +<*>"USE_CODE_IF_NOT" \t*"(" { + //printf("USE_CODE_IF_NOT\n"); + yy_push_state(PREPROC_IF_NOT, yyscanner); +} +<PREPROC_IF>{ID} { + //printf("preproc use code if '%s'\n", yytext); + yy_pop_state(yyscanner); + if (yyextra->isPreprocessorConditionSet(yytext)) + yy_push_state(PREPROC_PRE_BODY_USE, yyscanner); + else + yy_push_state(PREPROC_PRE_BODY_EAT, yyscanner); +} +<PREPROC_IF_NOT>{ID} { + //printf("preproc use code if not '%s'\n", yytext); + yy_pop_state(yyscanner); + if (!yyextra->isPreprocessorConditionSet(yytext)) + yy_push_state(PREPROC_PRE_BODY_USE, yyscanner); + else + yy_push_state(PREPROC_PRE_BODY_EAT, yyscanner); +} +<PREPROC_PRE_BODY_USE> \t*")" { + yy_pop_state(yyscanner); + yy_push_state(PREPROC_BODY_USE, yyscanner); +} +<PREPROC_PRE_BODY_EAT> \t*")" { + //printf("PREPROCESSOR EAT : \n"); + yy_pop_state(yyscanner); + yy_push_state(PREPROC_BODY_EAT, yyscanner); +} +<*>.*"END_USE_CODE" { + //printf("-->END_USE_CODE\n"); + yy_pop_state(yyscanner); +} +<PREPROC_BODY_EAT> \t\r\n* /* eat up code block filtered out by preprocessor */ +<PREPROC_BODY_EAT>.* /* eat up code block filtered out by preprocessor */ + + + /* Language keywords */ + +"on" return ON; +"end" return END; +"note" return NOTE; +"init" return INIT; +"declare" return DECLARE; +"while" return WHILE; +"if" return IF; +"or" return OR; +"release" return RELEASE; +"and" return AND; +"not" return NOT; +"else" return ELSE; +"controller" return CONTROLLER; +"case" return CASE; +"select" return SELECT; +"to" return TO; +"<=" return LE; +">=" return GE; +"const" return CONST_; // note: "CONST" is already defined for C/C++ compilers on Windows by default +"polyphonic" return POLYPHONIC; +"mod" return MOD; + +on|end|note|init|declare|if|then|begin|end|procedure|function { + printf("A keyword: %s\n", yytext); +} + +&,()\<>=*+#/- { return *yytext; } + +("$"|"@"){ID} { + yylval->sValue = strdup(yytext); + return VARIABLE; +} + +"%"{ID} { + yylval->sValue = strdup(yytext); + return VARIABLE; +} + +{ID} { + yylval->sValue = strdup(yytext); + return IDENTIFIER; +} + +":=" return ASSIGNMENT; + +\n+ { + //printf("lex: new line %d\n", yylineno, yytext); + //return LF; +} + +"{"^}*"}" /* eat up comments */ + + \t\r+ /* eat up whitespace */ + +"..." /* eat up */ + +. printf( "Unrecognized character: %s\n", yytext ); + +%% + +namespace LinuxSampler { + +void ParserContext::createScanner(std::istream* is) { + if (scanner) destroyScanner(); + this->is = is; + yylex_init(&scanner); + yyset_extra(this, scanner); +} + +void ParserContext::destroyScanner() { + if (!scanner) return; + yylex_destroy(scanner); + scanner = NULL; +} + +} // namespace LinuxSampler
View file
linuxsampler-2718.tar.bz2/src/scriptvm/tree.cpp
Added
@@ -0,0 +1,917 @@ +/* + * Copyright (c) 2014 Christian Schoenebeck and Andreas Persson + * + * http://www.linuxsampler.org + * + * This file is part of LinuxSampler and released under the same terms. + * See README file for details. + */ + +#include <cstdio> +#include <string.h> +#include "tree.h" +#include "../common/global_private.h" +#include <assert.h> + +namespace LinuxSampler { + +bool isNoOperation(StatementRef statement) { + NoOperation* noOp = dynamic_cast<NoOperation*>(&*statement); + return noOp; +} + +Node::Node() { +} + +Node::~Node() { +} + +void Node::printIndents(int n) { + for (int i = 0; i < n; ++i) printf(" "); + fflush(stdout); +} + +String IntExpr::evalCastToStr() { + return ToString(evalInt()); +} + +int IntLiteral::evalInt() { + return value; +} + +void IntLiteral::dump(int level) { + printIndents(level); + printf("IntLiteral %d\n", value); +} + +void StringLiteral::dump(int level) { + printIndents(level); + printf("StringLiteral: '%s'\n", value.c_str()); +} + +int Add::evalInt() { + IntExpr* pLHS = dynamic_cast<IntExpr*>(&*lhs); + IntExpr* pRHS = dynamic_cast<IntExpr*>(&*rhs);; + return (pLHS && pRHS) ? pLHS->evalInt() + pRHS->evalInt() : 0; +} + +void Add::dump(int level) { + printIndents(level); + printf("Add(\n"); + lhs->dump(level+1); + printIndents(level); + printf(",\n"); + rhs->dump(level+1); + printIndents(level); + printf(")\n"); +} + +int Sub::evalInt() { + IntExpr* pLHS = dynamic_cast<IntExpr*>(&*lhs); + IntExpr* pRHS = dynamic_cast<IntExpr*>(&*rhs);; + return (pLHS && pRHS) ? pLHS->evalInt() - pRHS->evalInt() : 0; +} + +void Sub::dump(int level) { + printIndents(level); + printf("Sub(\n"); + lhs->dump(level+1); + printIndents(level); + printf(",\n"); + rhs->dump(level+1); + printIndents(level); + printf(")\n"); +} + +int Mul::evalInt() { + IntExpr* pLHS = dynamic_cast<IntExpr*>(&*lhs); + IntExpr* pRHS = dynamic_cast<IntExpr*>(&*rhs);; + return (pLHS && pRHS) ? pLHS->evalInt() * pRHS->evalInt() : 0; +} + +void Mul::dump(int level) { + printIndents(level); + printf("Mul(\n"); + lhs->dump(level+1); + printIndents(level); + printf(",\n"); + rhs->dump(level+1); + printIndents(level); + printf(")\n"); +} + +int Div::evalInt() { + IntExpr* pLHS = dynamic_cast<IntExpr*>(&*lhs); + IntExpr* pRHS = dynamic_cast<IntExpr*>(&*rhs);; + return (pLHS && pRHS) ? pRHS->evalInt() == 0 ? 0 : pLHS->evalInt() / pRHS->evalInt() : 0; +} + +void Div::dump(int level) { + printIndents(level); + printf("Div(\n"); + lhs->dump(level+1); + printIndents(level); + printf(",\n"); + rhs->dump(level+1); + printIndents(level); + printf(")\n"); +} + +int Mod::evalInt() { + IntExpr* pLHS = dynamic_cast<IntExpr*>(&*lhs); + IntExpr* pRHS = dynamic_cast<IntExpr*>(&*rhs);; + return (pLHS && pRHS) ? pLHS->evalInt() % pRHS->evalInt() : 0; +} + +void Mod::dump(int level) { + printIndents(level); + printf("Mod(\n"); + lhs->dump(level+1); + printIndents(level); + printf(",\n"); + rhs->dump(level+1); + printIndents(level); + printf(")\n"); +} + +void Args::dump(int level) { + printIndents(level); + printf("Args(\n"); + for (std::vector<ExpressionRef>::iterator it = args.begin() ; it != args.end() ; ++it) { + (*it)->dump(level+1); + } + printIndents(level); + printf(")\n"); +} + +bool Args::isPolyphonic() const { + for (int i = 0; i < args.size(); ++i) + if (argsi->isPolyphonic()) + return true; + return false; +} + +EventHandlers::EventHandlers() { + //printf("EventHandlers::Constructor 0x%lx\n", (long long)this); +} + +EventHandlers::~EventHandlers() { +} + +void EventHandlers::add(EventHandlerRef arg) { + args.push_back(arg); +} + +void EventHandlers::dump(int level) { + printIndents(level); + printf("EventHandlers {\n"); + for (std::vector<EventHandlerRef>::iterator it = args.begin() ; it != args.end() ; ++it) { + (*it)->dump(level+1); + } + printIndents(level); + printf("}\n"); +} + +EventHandler* EventHandlers::eventHandlerByName(const String& name) const { + for (int i = 0; i < args.size(); ++i) + if (args.at(i)->eventHandlerName() == name) + return const_cast<EventHandler*>(&*args.at(i)); + return NULL; +} + +EventHandler* EventHandlers::eventHandler(uint index) const { + if (index >= args.size()) return NULL; + return const_cast<EventHandler*>(&*args.at(index)); +} + +bool EventHandlers::isPolyphonic() const { + for (int i = 0; i < args.size(); ++i) + if (argsi->isPolyphonic()) + return true; + return false; +} + +Assignment::Assignment(VariableRef variable, ExpressionRef value) + : variable(variable), value(value) +{ +} + +void Assignment::dump(int level) { + printIndents(level); + printf("Assignment\n"); +} + +StmtFlags_t Assignment::exec() { + if (!variable) + return StmtFlags_t(STMT_ABORT_SIGNALLED | STMT_ERROR_OCCURRED); + variable->assign(&*value); + return STMT_SUCCESS; +} + +EventHandler::EventHandler(StatementsRef statements) { + this->statements = statements; + usingPolyphonics = statements->isPolyphonic(); +} + +void EventHandler::dump(int level) { + printIndents(level); + printf("EventHandler {\n"); + statements->dump(level+1); + printIndents(level); + printf("}\n"); +} + +void Statements::dump(int level) { + printIndents(level); + printf("Statements {\n"); + for (std::vector<StatementRef>::iterator it = args.begin() ; it != args.end() ; ++it) { + (*it)->dump(level+1); + } + printIndents(level); + printf("}\n"); +} + +Statement* Statements::statement(uint i) { + if (i >= args.size()) return NULL; + return &*args.at(i); +} + +bool Statements::isPolyphonic() const { + for (int i = 0; i < args.size(); ++i) + if (argsi->isPolyphonic()) + return true; + return false; +} + +void FunctionCall::dump(int level) { + printIndents(level); + printf("FunctionCall '%s' args={\n", functionName.c_str()); + args->dump(level+1); + printIndents(level); + printf("}\n"); +} + +ExprType_t FunctionCall::exprType() const { + if (!fn) return EMPTY_EXPR; + return fn->returnType(); +} + +VMFnResult* FunctionCall::execVMFn() { + if (!fn) return NULL; + // assuming here that all argument checks (amount and types) have been made + // at parse time, to avoid time intensive checks on each function call + return fn->exec(dynamic_cast<VMFnArgs*>(&*args)); +} + +StmtFlags_t FunctionCall::exec() { + VMFnResult* result = execVMFn(); + if (!result) + return StmtFlags_t(STMT_ABORT_SIGNALLED | STMT_ERROR_OCCURRED); + return result->resultFlags(); +} + +int FunctionCall::evalInt() { + VMFnResult* result = execVMFn(); + if (!result) return 0; + VMIntExpr* intExpr = dynamic_cast<VMIntExpr*>(result->resultValue()); + if (!intExpr) return 0; + return intExpr->evalInt(); +} + +String FunctionCall::evalStr() { + VMFnResult* result = execVMFn(); + if (!result) return ""; + VMStringExpr* strExpr = dynamic_cast<VMStringExpr*>(result->resultValue()); + if (!strExpr) return ""; + return strExpr->evalStr(); +} + +String FunctionCall::evalCastToStr() { + VMFnResult* result = execVMFn(); + if (!result) return ""; + if (result->resultValue()->exprType() == STRING_EXPR) { + VMStringExpr* strExpr = dynamic_cast<VMStringExpr*>(result->resultValue()); + return strExpr ? strExpr->evalStr() : ""; + } else { + VMIntExpr* intExpr = dynamic_cast<VMIntExpr*>(result->resultValue()); + return intExpr ? ToString(intExpr->evalInt()) : ""; + } +} + +IntVariable::IntVariable(ParserContext* ctx) + : Variable(ctx, ctx ? ctx->globalIntVarCount++ : 0, false), polyphonic(false) +{ + //printf("globalIntVar parserctx=0x%lx memPOS=%d\n", ctx, memPos); + assert(ctx); +} + +inline static int postfixInc(int& object, int incBy) { + const int i = object; + object += incBy; + return i; +} + +IntVariable::IntVariable(ParserContext* ctx, bool polyphonic, bool bConst, int size) + : Variable(ctx, !ctx ? 0 : polyphonic ? postfixInc(ctx->polyphonicIntVarCount, size) : postfixInc(ctx->globalIntVarCount, size), bConst), + polyphonic(polyphonic) +{ + //printf("InvVar size=%d parserCtx=0x%lx\n", size, (uint64_t)ctx); + if (polyphonic) { + //printf("polyIntVar memPOS=%d\n", memPos); + assert(ctx); + } +} + +void IntVariable::assign(Expression* expr) { + IntExpr* intExpr = dynamic_cast<IntExpr*>(expr); + if (intExpr) + if (polyphonic) + context->execContext->polyphonicIntMemorymemPos = intExpr->evalInt(); + else + (*context->globalIntMemory)memPos = intExpr->evalInt(); +} + +int IntVariable::evalInt() { + //printf("IntVariable::eval pos=%d\n", memPos); + if (polyphonic) { + //printf("evalInt() poly memPos=%d execCtx=0x%lx\n", memPos, (uint64_t)context->execContext); + return context->execContext->polyphonicIntMemorymemPos; + } + return (*context->globalIntMemory)memPos; +} + +void IntVariable::dump(int level) { + printIndents(level); + //printf("IntVariable memPos=%d\n", memPos); +} + +//ConstIntVariable::ConstIntVariable(ParserContext* ctx, int value) +ConstIntVariable::ConstIntVariable(int value) + : IntVariable(NULL,false,true), value(value) +{ +} + +void ConstIntVariable::assign(Expression* expr) { + // ignore assignment +/* + printf("ConstIntVariable::assign()\n"); + IntExpr* intExpr = dynamic_cast<IntExpr*>(expr); + if (intExpr) { + value = intExpr->evalInt(); + } +*/ +} + +int ConstIntVariable::evalInt() { + return value; +} + +void ConstIntVariable::dump(int level) { + printIndents(level); + printf("ConstIntVariable val=%d\n", value); +} + +BuiltInIntVariable::BuiltInIntVariable(const String& name, VMIntRelPtr* ptr) + : IntVariable(NULL,false,false), name(name), ptr(ptr) +{ +} + +void BuiltInIntVariable::assign(Expression* expr) { + IntExpr* valueExpr = dynamic_cast<IntExpr*>(expr); + if (!valueExpr) return; + ptr->assign(valueExpr->evalInt()); +} + +int BuiltInIntVariable::evalInt() { + return ptr->evalInt(); +} + +void BuiltInIntVariable::dump(int level) { + printIndents(level); + printf("Built-in IntVar '%s'\n", name.c_str()); +} + +PolyphonicIntVariable::PolyphonicIntVariable(ParserContext* ctx) + : IntVariable(ctx,true,false) +{ +} + +void PolyphonicIntVariable::dump(int level) { + printIndents(level); + printf("PolyphonicIntVariable\n"); +} + +IntArrayVariable::IntArrayVariable(ParserContext* ctx, int size) + : Variable(ctx, 0, false) +{ + values.resize(size); + memset(&values0, 0, size * sizeof(int)); +} + +IntArrayVariable::IntArrayVariable(ParserContext* ctx, int size, ArgsRef values) + : Variable(ctx, 0, false) +{ + this->values.resize(size); + for (int i = 0; i < values->argsCount(); ++i) { + VMIntExpr* expr = dynamic_cast<VMIntExpr*>(values->arg(i)); + if (expr) this->valuesi = expr->evalInt(); + } +} + +IntArrayVariable::IntArrayVariable(ParserContext* ctx, bool bConst) + : Variable(ctx, 0, bConst) +{ +} + +int IntArrayVariable::evalIntElement(uint i) { + if (i >= values.size()) return 0; + return valuesi; +} + +void IntArrayVariable::assignIntElement(uint i, int value) { + if (i >= values.size()) return; + valuesi = value; +} + +void IntArrayVariable::dump(int level) { + printIndents(level); + printf("IntArray("); + for (int i = 0; i < values.size(); ++i) { + if (i % 12 == 0) { + printf("\n"); + printIndents(level+1); + } + printf("%d, ", valuesi); + } + printIndents(level); + printf(")\n"); +} + +BuiltInIntArrayVariable::BuiltInIntArrayVariable(const String& name, VMInt8Array* array) + : IntArrayVariable(NULL, false), name(name), array(array) +{ +} + +int BuiltInIntArrayVariable::evalIntElement(uint i) { + return i >= array->size ? 0 : array->datai; +} + +void BuiltInIntArrayVariable::assignIntElement(uint i, int value) { + if (i >= array->size) return; + array->datai = value; +} + +void BuiltInIntArrayVariable::dump(int level) { + printIndents(level); + printf("Built-In Int Array Variable '%s'\n", name.c_str()); +} + +IntArrayElement::IntArrayElement(IntArrayVariableRef array, IntExprRef arrayIndex) + : IntVariable(NULL, false, false, 0), array(array), index(arrayIndex) +{ +} + +void IntArrayElement::assign(Expression* expr) { + IntExpr* valueExpr = dynamic_cast<IntExpr*>(expr); + if (!valueExpr) return; + int value = valueExpr->evalInt(); + + if (!index) return; + int idx = index->evalInt(); + if (idx < 0 || idx >= array->arraySize()) return; + + array->assignIntElement(idx, value); +} + +int IntArrayElement::evalInt() { + if (!index) return 0; + int idx = index->evalInt(); + if (idx < 0 || idx >= array->arraySize()) return 0; + + return array->evalIntElement(idx); +} + +void IntArrayElement::dump(int level) { + printIndents(level); + printf("IntArrayElement\n"); +} + +StringVariable::StringVariable(ParserContext* ctx) + : Variable(ctx,ctx->globalStrVarCount++,false) +{ +} + +StringVariable::StringVariable(ParserContext* ctx, bool bConst) + : Variable(ctx,0,bConst) +{ +} + +void StringVariable::assign(Expression* expr) { + StringExpr* strExpr = dynamic_cast<StringExpr*>(expr); + (*context->globalStrMemory)memPos = strExpr->evalStr(); +} + +String StringVariable::evalStr() { + //printf("StringVariable::eval pos=%d\n", memPos); + return (*context->globalStrMemory)memPos; +} + +void StringVariable::dump(int level) { + printIndents(level); + printf("StringVariable memPos=%d\n", memPos); +} + +ConstStringVariable::ConstStringVariable(ParserContext* ctx, String _value) + : StringVariable(ctx,true), value(_value) +{ +} + +void ConstStringVariable::assign(Expression* expr) { + // ignore assignment +// StringExpr* strExpr = dynamic_cast<StringExpr*>(expr); +// if (strExpr) value = strExpr->evalStr(); +} + +String ConstStringVariable::evalStr() { + return value; +} + +void ConstStringVariable::dump(int level) { + printIndents(level); + printf("ConstStringVariable val='%s'\n", value.c_str()); +} + +void If::dump(int level) { + printIndents(level); + if (ifStatements && elseStatements) + printf("if cond stmts1 else stmts2 end if\n"); + else if (ifStatements) + printf("if cond statements end if\n"); + else + printf("if INVALID\n"); +} + +int If::evalBranch() { + if (condition->evalInt()) return 0; + if (elseStatements) return 1; + return -1; +} + +Statements* If::branch(uint i) const { + if (i == 0) return (Statements*) &*ifStatements; + if (i == 1) return (elseStatements) ? (Statements*) &*elseStatements : NULL; + return NULL; +} + +bool If::isPolyphonic() const { + if (condition->isPolyphonic() || ifStatements->isPolyphonic()) + return true; + return elseStatements ? elseStatements->isPolyphonic() : false; +} + +void SelectCase::dump(int level) { + printIndents(level); + if (select) + if (select->isConstExpr()) + printf("Case select %d\n", select->evalInt()); + else + printf("Case select runtime expr\n"); + else + printf("Case select NULL\n"); + for (int i = 0; i < branches.size(); ++i) { + printIndents(level+1); + CaseBranch& branch = branchesi; + if (branch.from && branch.to) + if (branch.from->isConstExpr() && branch.to->isConstExpr()) + printf("case %d to %d\n", branch.from->evalInt(), branch.to->evalInt()); + else if (branch.from->isConstExpr() && !branch.to->isConstExpr()) + printf("case %d to runtime expr\n", branch.from->evalInt()); + else if (!branch.from->isConstExpr() && branch.to->isConstExpr()) + printf("case runtime expr to %d\n", branch.to->evalInt()); + else + printf("case runtime expr to runtime expr\n"); + else if (branch.from) + if (branch.from->isConstExpr()) + printf("case %d\n", branch.from->evalInt()); + else + printf("case runtime expr\n"); + else + printf("case NULL\n"); + } +} + +int SelectCase::evalBranch() { + int value = select->evalInt(); + for (int i = 0; i < branches.size(); ++i) { + if (branches.at(i).from && branches.at(i).to) { // i.e. "case 4 to 7" ... + if (branches.at(i).from->evalInt() <= value && + branches.at(i).to->evalInt() >= value) return i; + } else { // i.e. "case 5" ... + if (branches.at(i).from->evalInt() == value) return i; + } + } + return -1; +} + +Statements* SelectCase::branch(uint i) const { + if (i < branches.size()) + return const_cast<Statements*>( &*branchesi.statements ); + return NULL; +} + +bool SelectCase::isPolyphonic() const { + if (select->isPolyphonic()) return true; + for (int i = 0; i < branches.size(); ++i) + if (branchesi.statements->isPolyphonic()) + return true; + return false; +} + +// void Case::addBranch(IntExprRef condition, StatementsRef statements) { +// CaseBranchRef b = new CaseBranchRef; +// b->from = condition; +// b->statements = statements; +// branches.push_back(b); +// } +// +// void Case::addBranch(IntExprRef from, IntExprRef to, StatementsRef statements) { +// CaseBranchRef b = new CaseBranchRef; +// b->from = from; +// b->to = to; +// b->statements = statements; +// branches.push_back(b); +// } +// +// void Case::addBranch(CaseBranchRef branch) { +// branches.push_back(branch); +// } + +void While::dump(int level) { + printIndents(level); + if (m_condition) + if (m_condition->isConstExpr()) + printf("while (%d) {\n", m_condition->evalInt()); + else + printf("while (runtime expr) {\n"); + else + printf("while (INVALID) {\n"); + m_statements->dump(level+1); + printIndents(level); + printf("}\n"); +} + +Statements* While::statements() const { + return (m_statements) ? const_cast<Statements*>( &*m_statements ) : NULL; +} + +bool While::evalLoopStartCondition() { + if (!m_condition) return false; + return m_condition->evalInt(); +} + +void Neg::dump(int level) { + printIndents(level); + printf("Negative Expr\n"); +} + +String ConcatString::evalStr() { + return lhs->evalCastToStr() + rhs->evalCastToStr(); +} + +void ConcatString::dump(int level) { + printIndents(level); + printf("ConcatString(\n"); + lhs->dump(level+1); + printIndents(level); + printf(",\n"); + rhs->dump(level+1); + printIndents(level); + printf(")"); +} + +bool ConcatString::isConstExpr() const { + return lhs->isConstExpr() && rhs->isConstExpr(); +} + +int Relation::evalInt() { + switch (type) { + case LESS_THAN: + return lhs->evalInt() < rhs->evalInt(); + case GREATER_THAN: + return lhs->evalInt() > rhs->evalInt(); + case LESS_OR_EQUAL: + return lhs->evalInt() <= rhs->evalInt(); + case GREATER_OR_EQUAL: + return lhs->evalInt() >= rhs->evalInt(); + case EQUAL: + if (lhs->exprType() == STRING_EXPR || rhs->exprType() == STRING_EXPR) + return lhs->evalCastToStr() == rhs->evalCastToStr(); + else + return lhs->evalInt() == rhs->evalInt(); + case NOT_EQUAL: + if (lhs->exprType() == STRING_EXPR || rhs->exprType() == STRING_EXPR) + return lhs->evalCastToStr() != rhs->evalCastToStr(); + else + return lhs->evalInt() != rhs->evalInt(); + } + return 0; +} + +void Relation::dump(int level) { + printIndents(level); + printf("Relation(\n"); + lhs->dump(level+1); + printIndents(level); + switch (type) { + case LESS_THAN: + printf("LESS_THAN\n"); + break; + case GREATER_THAN: + printf("GREATER_THAN\n"); + break; + case LESS_OR_EQUAL: + printf("LESS_OR_EQUAL\n"); + break; + case GREATER_OR_EQUAL: + printf("GREATER_OR_EQUAL\n"); + break; + case EQUAL: + printf("EQUAL\n"); + break; + case NOT_EQUAL: + printf("NOT_EQUAL\n"); + break; + } + rhs->dump(level+1); + printIndents(level); + printf(")\n"); +} + +bool Relation::isConstExpr() const { + return lhs->isConstExpr() && rhs->isConstExpr(); +} + +int Or::evalInt() { + IntExpr* pLHS = dynamic_cast<IntExpr*>(&*lhs); + if (pLHS->evalInt()) return 1; + IntExpr* pRHS = dynamic_cast<IntExpr*>(&*rhs);; + return (pRHS->evalInt()) ? 1 : 0; +} + +void Or::dump(int level) { + printIndents(level); + printf("Or(\n"); + lhs->dump(level+1); + printIndents(level); + printf(",\n"); + rhs->dump(level+1); + printIndents(level); + printf(")\n"); +} + +int And::evalInt() { + IntExpr* pLHS = dynamic_cast<IntExpr*>(&*lhs); + if (!pLHS->evalInt()) return 0; + IntExpr* pRHS = dynamic_cast<IntExpr*>(&*rhs); + return (pRHS->evalInt()) ? 1 : 0; +} + +void And::dump(int level) { + printIndents(level); + printf("And(\n"); + lhs->dump(level+1); + printIndents(level); + printf(",\n"); + rhs->dump(level+1); + printIndents(level); + printf(")\n"); +} + +void Not::dump(int level) { + printIndents(level); + printf("Not(\n"); + expr->dump(level+1); + printIndents(level); + printf(")\n"); +} + +VariableRef ParserContext::variableByName(const String& name) { + if (!vartable.count(name)) { + return VariableRef(); + } + return vartable.find(name)->second; +} + +VariableRef ParserContext::globalVar(const String& name) { + if (!vartable.count(name)) { + //printf("No global var '%s'\n", name.c_str()); + //for (std::map<String,VariableRef>::const_iterator it = vartable.begin(); it != vartable.end(); ++it) + // printf("-> var '%s'\n", it->first.c_str()); + return VariableRef(); + } + return vartable.find(name)->second; +} + +IntVariableRef ParserContext::globalIntVar(const String& name) { + return globalVar(name); +} + +StringVariableRef ParserContext::globalStrVar(const String& name) { + return globalVar(name); +} + +ParserContext::~ParserContext() { + destroyScanner(); + if (globalIntMemory) { + delete globalIntMemory; + globalIntMemory = NULL; + } +} + +void ParserContext::addErr(int line, const char* txt) { + ParserIssue e; + e.type = PARSER_ERROR; + e.txt = txt; + e.line = line; + vErrors.push_back(e); + vIssues.push_back(e); +} + +void ParserContext::addWrn(int line, const char* txt) { + ParserIssue w; + w.type = PARSER_WARNING; + w.txt = txt; + w.line = line; + vWarnings.push_back(w); + vIssues.push_back(w); +} + +bool ParserContext::setPreprocessorCondition(const char* name) { + if (builtinPreprocessorConditions.count(name)) return false; + if (userPreprocessorConditions.count(name)) return false; + userPreprocessorConditions.insert(name); + return true; +} + +bool ParserContext::resetPreprocessorCondition(const char* name) { + if (builtinPreprocessorConditions.count(name)) return false; + if (!userPreprocessorConditions.count(name)) return false; + userPreprocessorConditions.erase(name); + return true; +} + +bool ParserContext::isPreprocessorConditionSet(const char* name) { + if (builtinPreprocessorConditions.count(name)) return true; + return userPreprocessorConditions.count(name); +} + +std::vector<ParserIssue> ParserContext::issues() const { + return vIssues; +} + +std::vector<ParserIssue> ParserContext::errors() const { + return vErrors; +} + +std::vector<ParserIssue> ParserContext::warnings() const { + return vWarnings; +} + +VMEventHandler* ParserContext::eventHandler(uint index) { + if (!handlers) return NULL; + return handlers->eventHandler(index); +} + +VMEventHandler* ParserContext::eventHandlerByName(const String& name) { + if (!handlers) return NULL; + return handlers->eventHandlerByName(name); +} + +void ParserContext::registerBuiltInConstIntVariables(const std::map<String,int>& vars) { + for (std::map<String,int>::const_iterator it = vars.begin(); + it != vars.end(); ++it) + { + ConstIntVariableRef ref = new ConstIntVariable(it->second); + vartableit->first = ref; + } +} + +void ParserContext::registerBuiltInIntVariables(const std::map<String,VMIntRelPtr*>& vars) { + for (std::map<String,VMIntRelPtr*>::const_iterator it = vars.begin(); + it != vars.end(); ++it) + { + BuiltInIntVariableRef ref = new BuiltInIntVariable(it->first, it->second); + vartableit->first = ref; + } +} + +void ParserContext::registerBuiltInIntArrayVariables(const std::map<String,VMInt8Array*>& vars) { + for (std::map<String,VMInt8Array*>::const_iterator it = vars.begin(); + it != vars.end(); ++it) + { + BuiltInIntArrayVariableRef ref = new BuiltInIntArrayVariable(it->first, it->second); + vartableit->first = ref; + } +} + +} // namespace LinuxSampler
View file
linuxsampler-2718.tar.bz2/src/scriptvm/tree.h
Added
@@ -0,0 +1,653 @@ +/* -*- c++ -*- + * + * Copyright (c) 2014 Christian Schoenebeck and Andreas Persson + * + * http://www.linuxsampler.org + * + * This file is part of LinuxSampler and released under the same terms. + * See README file for details. + */ + +// This header defines VM core implementation internal data types only used +// inside the parser and core VM implementation of this source directory. Not +// intended to be used in other source code parts (other source code +// directories) of the sampler. + +#ifndef LS_INSTRPARSERTREE_H +#define LS_INSTRPARSERTREE_H + +#include <vector> +#include <iostream> +#include <map> +#include <set> +#include "../common/global.h" +#include "../common/Ref.h" +#include "../common/ArrayList.h" +#include "common.h" + +namespace LinuxSampler { + +class ParserContext; +class ExecContext; + +enum StmtType_t { + STMT_LEAF, + STMT_LIST, + STMT_BRANCH, + STMT_LOOP, +}; + +class Node { +public: + Node(); + virtual ~Node(); + virtual void dump(int level = 0) = 0; + virtual bool isPolyphonic() const = 0; + void printIndents(int n); +}; +typedef Ref<Node> NodeRef; + +class Expression : virtual public VMExpr, virtual public Node { +public: + virtual ExprType_t exprType() const = 0; + virtual bool isConstExpr() const = 0; + virtual String evalCastToStr() = 0; +}; +typedef Ref<Expression,Node> ExpressionRef; + +class IntExpr : virtual public VMIntExpr, virtual public Expression { +public: + ExprType_t exprType() const { return INT_EXPR; } + virtual int evalInt() = 0; + String evalCastToStr(); +}; +typedef Ref<IntExpr,Node> IntExprRef; + +class StringExpr : virtual public VMStringExpr, virtual public Expression { +public: + ExprType_t exprType() const { return STRING_EXPR; } + virtual String evalStr() = 0; + String evalCastToStr() { return evalStr(); } +}; +typedef Ref<StringExpr,Node> StringExprRef; + +class IntLiteral : virtual public IntExpr { + int value; +public: + IntLiteral(int value) : value(value) { } + int evalInt(); + void dump(int level = 0); + bool isConstExpr() const { return true; } + bool isPolyphonic() const { return false; } +}; +typedef Ref<IntLiteral,Node> IntLiteralRef; + +class StringLiteral : virtual public StringExpr { +public: + String value; + StringLiteral(const String& value) : value(value) { } + bool isConstExpr() const { return true; } + void dump(int level = 0); + String evalStr() { return value; } + bool isPolyphonic() const { return false; } +}; +typedef Ref<StringLiteral,Node> StringLiteralRef; + +class Args : virtual public VMFnArgs, virtual public Node { +public: + std::vector<ExpressionRef> args; + void add(ExpressionRef arg) { args.push_back(arg); } + void dump(int level = 0); + int argsCount() const { return args.size(); } + VMExpr* arg(int i) { return (i >= 0 && i < argsCount()) ? &*args.at(i) : NULL; } + bool isPolyphonic() const; +}; +typedef Ref<Args,Node> ArgsRef; + +class Variable : virtual public Expression { +public: + virtual bool isConstExpr() const { return bConst; } + virtual void assign(Expression* expr) = 0; +protected: + Variable(ParserContext* ctx, int _memPos, bool _bConst) + : context(ctx), memPos(_memPos), bConst(_bConst) {} + + ParserContext* context; + int memPos; + bool bConst; +}; +typedef Ref<Variable,Node> VariableRef; + +class IntVariable : public Variable, virtual public IntExpr { + bool polyphonic; +public: + IntVariable(ParserContext* ctx); + void assign(Expression* expr); + int evalInt(); + void dump(int level = 0); + bool isPolyphonic() const { return polyphonic; } +protected: + IntVariable(ParserContext* ctx, bool polyphonic, bool bConst, int size = 1); +}; +typedef Ref<IntVariable,Node> IntVariableRef; + +class ConstIntVariable : public IntVariable { +public: + int value; + + ConstIntVariable(int value); + //ConstIntVariable(ParserContext* ctx, int value = 0); + void assign(Expression* expr); + int evalInt(); + void dump(int level = 0); +}; +typedef Ref<ConstIntVariable,Node> ConstIntVariableRef; + +class BuiltInIntVariable : public IntVariable { + String name; + VMIntRelPtr* ptr; +public: + BuiltInIntVariable(const String& name, VMIntRelPtr* ptr); + void assign(Expression* expr); + int evalInt(); + void dump(int level = 0); +}; +typedef Ref<BuiltInIntVariable,Node> BuiltInIntVariableRef; + +class PolyphonicIntVariable : public IntVariable { +public: + PolyphonicIntVariable(ParserContext* ctx); + void dump(int level = 0); +}; +typedef Ref<PolyphonicIntVariable,Node> PolyphonicIntVariableRef; + +class IntArrayVariable : public Variable, virtual public VMIntArrayExpr { + ArrayList<int> values; +public: + IntArrayVariable(ParserContext* ctx, int size); + IntArrayVariable(ParserContext* ctx, int size, ArgsRef values); + void assign(Expression* expr) {} // ignore scalar assignment + String evalCastToStr() { return ""; } // ignore scalar cast to string + ExprType_t exprType() const { return INT_ARR_EXPR; } + virtual int arraySize() const { return values.size(); } + virtual int evalIntElement(uint i); + virtual void assignIntElement(uint i, int value); + void dump(int level = 0); + bool isPolyphonic() const { return false; } +protected: + IntArrayVariable(ParserContext* ctx, bool bConst); +}; +typedef Ref<IntArrayVariable,Node> IntArrayVariableRef; + +class BuiltInIntArrayVariable : public IntArrayVariable { + String name; + VMInt8Array* array; +public: + BuiltInIntArrayVariable(const String& name, VMInt8Array* array); + int arraySize() const { return array->size; } + int evalIntElement(uint i); + void assignIntElement(uint i, int value); + void dump(int level = 0); +}; +typedef Ref<BuiltInIntArrayVariable,Node> BuiltInIntArrayVariableRef; + +class IntArrayElement : public IntVariable { + IntArrayVariableRef array; + IntExprRef index; +public: + IntArrayElement(IntArrayVariableRef array, IntExprRef arrayIndex); + void assign(Expression* expr); + int evalInt(); + void dump(int level = 0); +}; +typedef Ref<IntArrayElement,Node> IntArrayElementRef; + +class StringVariable : public Variable, virtual public StringExpr { +public: + StringVariable(ParserContext* ctx); + void assign(Expression* expr); + String evalStr(); + void dump(int level = 0); + bool isPolyphonic() const { return false; } +protected: + StringVariable(ParserContext* ctx, bool bConst); +}; +typedef Ref<StringVariable,Node> StringVariableRef; + +class ConstStringVariable : public StringVariable { +public: + String value; + + ConstStringVariable(ParserContext* ctx, String value = ""); + void assign(Expression* expr); + String evalStr(); + void dump(int level = 0); +}; +typedef Ref<ConstStringVariable,Node> ConstStringVariableRef; + +class BinaryOp : virtual public Expression { +protected: + ExpressionRef lhs; + ExpressionRef rhs; +public: + BinaryOp(ExpressionRef lhs, ExpressionRef rhs) : lhs(lhs), rhs(rhs) { } + bool isConstExpr() const { return lhs->isConstExpr() && rhs->isConstExpr(); } + bool isPolyphonic() const { return lhs->isPolyphonic() || rhs->isPolyphonic(); } +}; +typedef Ref<BinaryOp,Node> BinaryOpRef; + +class Add : virtual public BinaryOp, virtual public IntExpr { +public: + Add(IntExprRef lhs, IntExprRef rhs) : BinaryOp(lhs, rhs) { } + int evalInt(); + void dump(int level = 0); +}; +typedef Ref<Add,Node> AddRef; + +class Sub : virtual public BinaryOp, virtual public IntExpr { +public: + Sub(IntExprRef lhs, IntExprRef rhs) : BinaryOp(lhs, rhs) { } + int evalInt(); + void dump(int level = 0); +}; +typedef Ref<Sub,Node> SubRef; + +class Mul : virtual public BinaryOp, virtual public IntExpr { +public: + Mul(IntExprRef lhs, IntExprRef rhs) : BinaryOp(lhs, rhs) { } + int evalInt(); + void dump(int level = 0); +}; +typedef Ref<Mul,Node> MulRef; + +class Div : virtual public BinaryOp, virtual public IntExpr { +public: + Div(IntExprRef lhs, IntExprRef rhs) : BinaryOp(lhs, rhs) { } + int evalInt(); + void dump(int level = 0); +}; +typedef Ref<Div,Node> DivRef; + +class Mod : virtual public BinaryOp, virtual public IntExpr { +public: + Mod(IntExprRef lhs, IntExprRef rhs) : BinaryOp(lhs, rhs) { } + int evalInt(); + void dump(int level = 0); +}; +typedef Ref<Mod,Node> ModRef; + +class Statement : virtual public Node { +public: + virtual StmtType_t statementType() const = 0; +}; +typedef Ref<Statement,Node> StatementRef; + +// Just used by parser to avoid "not a statement" parser warning, will be +// filtered out by parser. So it will not be part of the VM tree after parsing. +class NoOperation : public Statement { +public: + NoOperation() : Statement() {} + StmtType_t statementType() const { return STMT_LEAF; } + void dump(int level = 0) {} + bool isPolyphonic() const { return false; } +}; +typedef Ref<NoOperation,Node> NoOperationRef; + +bool isNoOperation(StatementRef statement); + +class LeafStatement : public Statement { +public: + virtual StmtFlags_t exec() = 0; + virtual StmtType_t statementType() const { return STMT_LEAF; } +}; +typedef Ref<LeafStatement,Node> LeafStatementRef; + +class Statements : public Statement { + std::vector<StatementRef> args; +public: + void add(StatementRef arg) { args.push_back(arg); } + void dump(int level = 0); + StmtType_t statementType() const { return STMT_LIST; } + virtual Statement* statement(uint i); + bool isPolyphonic() const; +}; +typedef Ref<Statements,Node> StatementsRef; + +class BranchStatement : public Statement { +public: + StmtType_t statementType() const { return STMT_BRANCH; } + virtual int evalBranch() = 0; + virtual Statements* branch(uint i) const = 0; +}; + +class FunctionCall : virtual public LeafStatement, virtual public IntExpr, virtual public StringExpr { + String functionName; + ArgsRef args; + VMFunction* fn; +public: + FunctionCall(const char* function, ArgsRef args, VMFunction* fn) : + functionName(function), args(args), fn(fn) { } + void dump(int level = 0); + StmtFlags_t exec(); + int evalInt(); + String evalStr(); + bool isConstExpr() const { return false; } + ExprType_t exprType() const; + String evalCastToStr(); + bool isPolyphonic() const { return args->isPolyphonic(); } +protected: + VMFnResult* execVMFn(); +}; +typedef Ref<FunctionCall,Node> FunctionCallRef; + +class EventHandler : virtual public Statements, virtual public VMEventHandler { + StatementsRef statements; + bool usingPolyphonics; +public: + void dump(int level = 0); + StmtFlags_t exec(); + EventHandler(StatementsRef statements); + Statement* statement(uint i) { return statements->statement(i); } + bool isPolyphonic() const { return usingPolyphonics; } +}; +typedef Ref<EventHandler,Node> EventHandlerRef; + +class OnNote : public EventHandler { +public: + OnNote(StatementsRef statements) : EventHandler(statements) {} + String eventHandlerName() const { return "note"; } +}; +typedef Ref<OnNote,Node> OnNoteRef; + +class OnInit : public EventHandler { +public: + OnInit(StatementsRef statements) : EventHandler(statements) {} + String eventHandlerName() const { return "init"; } +}; +typedef Ref<OnInit,Node> OnInitRef; + +class OnRelease : public EventHandler { +public: + OnRelease(StatementsRef statements) : EventHandler(statements) {} + String eventHandlerName() const { return "release"; } +}; +typedef Ref<OnRelease,Node> OnReleaseRef; + +class OnController : public EventHandler { +public: + OnController(StatementsRef statements) : EventHandler(statements) {} + String eventHandlerName() const { return "controller"; } +}; +typedef Ref<OnController,Node> OnControllerRef; + +class EventHandlers : virtual public Node { + std::vector<EventHandlerRef> args; +public: + EventHandlers(); + ~EventHandlers(); + void add(EventHandlerRef arg); + void dump(int level = 0); + int evalInt() { return 0; } + EventHandler* eventHandlerByName(const String& name) const; + EventHandler* eventHandler(uint index) const; + inline uint size() const { return args.size(); } + bool isPolyphonic() const; +}; +typedef Ref<EventHandlers,Node> EventHandlersRef; + +class Assignment : public LeafStatement { +protected: + VariableRef variable; + ExpressionRef value; +public: + Assignment(VariableRef variable, ExpressionRef value); + void dump(int level = 0); + StmtFlags_t exec(); + bool isPolyphonic() const { return variable->isPolyphonic() || value->isPolyphonic(); } +}; +typedef Ref<Assignment,Node> AssignmentRef; + +class If : public BranchStatement { + IntExprRef condition; + StatementsRef ifStatements; + StatementsRef elseStatements; +public: + If(IntExprRef condition, StatementsRef ifStatements, StatementsRef elseStatements) : + condition(condition), ifStatements(ifStatements), elseStatements(elseStatements) { } + If(IntExprRef condition, StatementsRef statements) : + condition(condition), ifStatements(statements) { } + void dump(int level = 0); + int evalBranch(); + Statements* branch(uint i) const; + bool isPolyphonic() const; +}; +typedef Ref<If,Node> IfRef; + +struct CaseBranch { + IntExprRef from; + IntExprRef to; + StatementsRef statements; +}; + +typedef std::vector<CaseBranch> CaseBranches; + +class SelectCase : public BranchStatement { + IntExprRef select; + CaseBranches branches; +public: + SelectCase(IntExprRef select, const CaseBranches& branches) : select(select), branches(branches) { } + void dump(int level = 0); + int evalBranch(); + Statements* branch(uint i) const; + //void addBranch(IntExprRef condition, StatementsRef statements); + //void addBranch(IntExprRef from, IntExprRef to, StatementsRef statements); + //void addBranch(CaseBranchRef branch); + //void addBranches(CaseBranchesRef branches); + bool isPolyphonic() const; +}; +typedef Ref<SelectCase,Node> SelectCaseRef; + +class While : public Statement { + IntExprRef m_condition; + StatementsRef m_statements; +public: + While(IntExprRef condition, StatementsRef statements) : + m_condition(condition), m_statements(statements) {} + StmtType_t statementType() const { return STMT_LOOP; } + void dump(int level = 0); + bool evalLoopStartCondition(); + Statements* statements() const; + bool isPolyphonic() const { return m_condition->isPolyphonic() || m_statements->isPolyphonic(); } +}; + +class Neg : public IntExpr { + IntExprRef expr; +public: + Neg(IntExprRef expr) : expr(expr) { } + int evalInt() { return (expr) ? -expr->evalInt() : 0; } + void dump(int level = 0); + bool isConstExpr() const { return expr->isConstExpr(); } + bool isPolyphonic() const { return expr->isPolyphonic(); } +}; +typedef Ref<Neg,Node> NegRef; + +class ConcatString : public StringExpr { + ExpressionRef lhs; + ExpressionRef rhs; +public: + ConcatString(ExpressionRef lhs, ExpressionRef rhs) : lhs(lhs), rhs(rhs) {} + String evalStr(); + void dump(int level = 0); + bool isConstExpr() const; + bool isPolyphonic() const { return lhs->isPolyphonic() || rhs->isPolyphonic(); } +}; +typedef Ref<ConcatString,Node> ConcatStringRef; + +class Relation : public IntExpr { +public: + enum Type { + LESS_THAN, + GREATER_THAN, + LESS_OR_EQUAL, + GREATER_OR_EQUAL, + EQUAL, + NOT_EQUAL + }; + Relation(IntExprRef lhs, Type type, IntExprRef rhs) : + lhs(lhs), rhs(rhs), type(type) {} + int evalInt(); + void dump(int level = 0); + bool isConstExpr() const; + bool isPolyphonic() const { return lhs->isPolyphonic() || rhs->isPolyphonic(); } +private: + IntExprRef lhs; + IntExprRef rhs; + Type type; +}; +typedef Ref<Relation,Node> RelationRef; + +class Or : virtual public BinaryOp, virtual public IntExpr { +public: + Or(IntExprRef lhs, IntExprRef rhs) : BinaryOp(lhs,rhs) {} + int evalInt(); + void dump(int level = 0); +}; +typedef Ref<Or,Node> OrRef; + +class And : virtual public BinaryOp, virtual public IntExpr { +public: + And(IntExprRef lhs, IntExprRef rhs) : BinaryOp(lhs,rhs) {} + int evalInt(); + void dump(int level = 0); +}; +typedef Ref<And,Node> AndRef; + +class Not : virtual public IntExpr { + IntExprRef expr; +public: + Not(IntExprRef expr) : expr(expr) {} + int evalInt() { return !expr->evalInt(); } + void dump(int level = 0); + bool isConstExpr() const { return expr->isConstExpr(); } + bool isPolyphonic() const { return expr->isPolyphonic(); } +}; +typedef Ref<Not,Node> NotRef; + +class ParserContext : public VMParserContext { +public: + struct Error { + String txt; + int line; + }; + typedef Error Warning; + + void* scanner; + std::istream* is; + std::vector<ParserIssue> vErrors; + std::vector<ParserIssue> vWarnings; + std::vector<ParserIssue> vIssues; + + std::set<String> builtinPreprocessorConditions; + std::set<String> userPreprocessorConditions; + + std::map<String,VariableRef> vartable; + int globalIntVarCount; + int globalStrVarCount; + int polyphonicIntVarCount; + + EventHandlersRef handlers; + + OnInitRef onInit; + OnNoteRef onNote; + OnReleaseRef onRelease; + OnControllerRef onController; + + ArrayList<int>* globalIntMemory; + ArrayList<String>* globalStrMemory; + int requiredMaxStackSize; + + VMFunctionProvider* functionProvider; + + ExecContext* execContext; + + ParserContext(VMFunctionProvider* parent) : + scanner(NULL), is(NULL), + globalIntVarCount(0), globalStrVarCount(0), polyphonicIntVarCount(0), + globalIntMemory(NULL), globalStrMemory(NULL), requiredMaxStackSize(-1), + functionProvider(parent), execContext(NULL) + { + } + virtual ~ParserContext(); + VariableRef globalVar(const String& name); + IntVariableRef globalIntVar(const String& name); + StringVariableRef globalStrVar(const String& name); + VariableRef variableByName(const String& name); + void addErr(int line, const char* txt); + void addWrn(int line, const char* txt); + void createScanner(std::istream* is); + void destroyScanner(); + bool setPreprocessorCondition(const char* name); + bool resetPreprocessorCondition(const char* name); + bool isPreprocessorConditionSet(const char* name); + std::vector<ParserIssue> issues() const OVERRIDE; + std::vector<ParserIssue> errors() const OVERRIDE; + std::vector<ParserIssue> warnings() const OVERRIDE; + VMEventHandler* eventHandler(uint index) OVERRIDE; + VMEventHandler* eventHandlerByName(const String& name) OVERRIDE; + void registerBuiltInConstIntVariables(const std::map<String,int>& vars); + void registerBuiltInIntVariables(const std::map<String,VMIntRelPtr*>& vars); + void registerBuiltInIntArrayVariables(const std::map<String,VMInt8Array*>& vars); +}; + +class ExecContext : public VMExecContext { +public: + struct StackFrame { + Statement* statement; + int subindex; + + StackFrame() { + statement = NULL; + subindex = -1; + } + }; + + ArrayList<int> polyphonicIntMemory; + VMExecStatus_t status; + ArrayList<StackFrame> stack; + int stackFrame; + int suspendMicroseconds; + + ExecContext() : + status(VM_EXEC_NOT_RUNNING), stackFrame(-1), suspendMicroseconds(0) {} + + virtual ~ExecContext() {} + + inline void pushStack(Statement* stmt) { + stackFrame++; + //printf("pushStack() -> %d\n", stackFrame); + if (stackFrame >= stack.size()) return; + stackstackFrame.statement = stmt; + stackstackFrame.subindex = 0; + } + + inline void popStack() { + stackstackFrame.statement = NULL; + stackstackFrame.subindex = -1; + stackFrame--; + //printf("popStack() -> %d\n", stackFrame); + } + + inline void reset() { + stack0.statement = NULL; + stack0.subindex = -1; + stackFrame = -1; + } + + int suspensionTimeMicroseconds() const OVERRIDE { + return suspendMicroseconds; + } +}; + +} // namespace LinuxSampler + +#endif // LS_INSTRPARSERTREE_H
View file
linuxsampler-2718.tar.bz2/src/shell
Added
+(directory)
View file
linuxsampler-2718.tar.bz2/src/shell/CCursor.h
Added
@@ -0,0 +1,251 @@ +/* + * LSCP Shell + * + * Copyright (c) 2014 Christian Schoenebeck + * + * This program is part of LinuxSampler and released under the same terms. + */ + +#ifndef CCURSOR_H +#define CCURSOR_H + +#include <stdio.h> +#include <stdlib.h> +#include <iostream> +#include <sstream> +#include <string> + +#include "TerminalCtrl.h" +#include "KeyboardReader.h" + +/** + * Writes (and reads) "ANSI escape codes" to/from stdout/stdin to control the + * position and appearance of the text input cursor on comand line terminals. + * + * This is a lite-weight class to prevent a dependency to i.e. libncurses. + */ +class CCursor { +public: + CCursor() : m_row(-1), m_col(-1) {} + virtual ~CCursor() {} + + /** + * Returns a CCursor storing the current position of the cursor on screen. + * That position can be altered with CCursors methods or restored at any + * time by calling restore(). + */ + static CCursor now() { + CCursor cursor; + TerminalSetting setting = TerminalCtrl::now(); + TerminalCtrl::echoInput(false); + + // Workaround: obviously only one thread can read from stdin, so if + // there is already 'KeyboardReader' listening to stdin, then use its + // read buffers, otherwise read "directly" from stdin with + // 'TerminalCtrl' class. + KeyboardReader* reader = KeyboardReader::singleton(); + if (reader) reader->callback(false); // disable its callback for a moment (so it won't deliver the input to somewhere) + setCode("6n"); + std::string s = (reader) ? reader->popStringToDelimiterSync('R') : TerminalCtrl::getStringToDelimiter('R'); + if (reader) reader->callback(true); // re-enable its callback + + TerminalCtrl::restore(setting); + char c; + int row, col; + int res = sscanf(s.c_str(), "%c%d;%d", &c, &row, &col); + if (res == 3) { + cursor.m_row = row - 1; + cursor.m_col = col - 1; + } + return cursor; + } + + /** + * Move the cursor on the terminal screen to the location stored in this + * CCcursor object. + */ + const CCursor& restore() const { + if (!valid()) return *this; + std::stringstream ss; + ss << m_row+1 << ";" << m_col+1 << "f"; + setCode(ss.str()); + return *this; + } + + /// Returns false if this object stores an invalid screen position. + bool valid() const { + return m_row >= 0 && m_col >= 0; + } + + /** + * Move the cursor on the terminal screen directly to the requested position + * given by @a row and @a col. + * + * @returns CCursor object with new position + */ + CCursor& moveTo(int row, int col) { + if (row < 0 || col < 0) return *this; + m_row = row; + m_col = col; + return const_cast<CCursor&>(restore()); + } + + CCursor& toColumn(int n) { + if (n < 0) return *this; + m_col = n; + return const_cast<CCursor&>(restore()); + } + + CCursor& toRow(int n) { + if (n < 0) return *this; + m_row = n; + return const_cast<CCursor&>(restore()); + } + + CCursor& up(int n = 1) { + if (n < 1) return *this; + std::stringstream ss; + ss << n << "A"; + setCode(ss.str()); + *this = now(); + return *this; + } + + CCursor& down(int n = 1) { + if (n < 1) return *this; + std::stringstream ss; + ss << n << "B"; + setCode(ss.str()); + *this = now(); + return *this; + } + + CCursor& right(int n = 1) { + if (n < 1) return *this; + std::stringstream ss; + ss << n << "C"; + setCode(ss.str()); + *this = now(); + return *this; + } + + CCursor& left(int n = 1) { + if (n < 1) return *this; + std::stringstream ss; + ss << n << "D"; + setCode(ss.str()); + *this = now(); + return *this; + } + + int column() const { + return m_col; + } + + int row() const { + return m_row; + } + + CCursor& hide() { + setCode("?25l"); + return *this; + } + + CCursor& show() { + setCode("?25h"); + return *this; + } + + // NOTE: not ANSI.SYS! + CCursor& lineDown(int n = 1) { + if (n < 1) return *this; + std::stringstream ss; + ss << n << "E"; + setCode(ss.str()); + *this = now(); + return *this; + } + + // NOTE: not ANSI.SYS! + CCursor& lineUp(int n = 1) { + if (n < 1) return *this; + std::stringstream ss; + ss << n << "F"; + setCode(ss.str()); + *this = now(); + return *this; + } + + // NOTE: not ANSI.SYS! + CCursor& scrollUp(int nLines = 1) { + if (nLines < 1) return *this; + std::stringstream ss; + ss << nLines << "S"; + setCode(ss.str()); + *this = now(); + return *this; + } + + // NOTE: not ANSI.SYS! + CCursor& scrollDown(int nLines = 1) { + if (nLines < 1) return *this; + std::stringstream ss; + ss << nLines << "T"; + setCode(ss.str()); + *this = now(); + return *this; + } + + CCursor& clearScreen() { + restore(); + setCode("2J"); + restore(); // allegedly cursor moves to beginning of screen on some systems, so restore + return *this; + } + + CCursor& clearVerticalToTop() { + restore(); + setCode("1J"); + return *this; + } + + CCursor& clearVerticalToBottom() { + restore(); + setCode("0J"); + return *this; + } + + CCursor& clearLineToRight() { + restore(); + setCode("0K"); + return *this; + } + + CCursor& clearLineToLeft() { + restore(); + setCode("1K"); + return *this; + } + + CCursor& clearLine() { + restore(); + setCode("2K"); + return *this; + } + + //static void save() { setCode("s"); } // barely supported by any terminal + //static void restore() { setCode("u"); } // barely supported by any terminal +protected: + static void setCode(std::string sCode) { + std::cout << modStr(sCode) << std::flush; + } + static std::string modStr(std::string code) { + return "\033" + code; + } + +private: + int m_row; + int m_col; +}; + +#endif // CCURSOR_H
View file
linuxsampler-2718.tar.bz2/src/shell/CFmt.h
Added
@@ -0,0 +1,76 @@ +/* + * LSCP Shell + * + * Copyright (c) 2014 Christian Schoenebeck + * + * This program is part of LinuxSampler and released under the same terms. + */ + +#ifndef CFMT_H +#define CFMT_H + +#include <stdio.h> +#include <stdlib.h> +#include <iostream> +#include <string> + +/** + * Writes "ANSI escape codes" to stdout to control color, font weight and + * brightness of text written to the command line terminal. + * + * This is a lite-weight class to prevent a dependency to i.e. libncurses. + * + * This class is not designed as singleton, because the idea was to reset the + * terminals output format as soon as the respective CFmt object is destructed. + * This way a developer is not forces to reset the output format each time he + * used something special. + */ +class CFmt { +public: + CFmt() : m_bright(false) {} + virtual ~CFmt() { reset(); } + + CFmt& black() { setColorFg("0"); return *this; } + CFmt& red() { setColorFg("1"); return *this; } + CFmt& green() { setColorFg("2"); return *this; } + CFmt& yellow() { setColorFg("3"); return *this; } + CFmt& blue() { setColorFg("4"); return *this; } + CFmt& magenta() { setColorFg("5"); return *this; } + CFmt& cyan() { setColorFg("6"); return *this; } + CFmt& white() { setColorFg("7"); return *this; } + + CFmt& bold() { setCode("1"); return *this; } + //CFmt& faint() { setCode("2"); return *this; } // barely supported by any terminal + //CFmt& italic() { setCode("3"); return *this; } // barely supported by any terminal + CFmt& underline() { setCode("4"); return *this; } + //CFmt& frame() { setCode("51"); return *this; } // barely supported by any terminal + //CFmt& encircle() { setCode("52"); return *this; } // barely supported by any terminal + //CFmt& overline() { setCode("53"); return *this; } // barely supported by any terminal + + CFmt& bright(bool b = true) { m_bright = b; return *this; } + + CFmt& reset() { + setCode("0"); + m_bright = false; + return *this; + } + +protected: + CFmt& setColorFg(std::string colorCode) { + setCode((m_bright ? "9" : "3") + colorCode); + return *this; + } + + static void setCode(std::string sCode) { + std::cout << modStr(sCode) << std::flush; + } + + static std::string modStr(std::string code) { + return "\033" + code + "m"; + } + +private: + bool m_bright; +}; + +#endif // CFMT_H
View file
linuxsampler-2718.tar.bz2/src/shell/KeyboardReader.cpp
Added
@@ -0,0 +1,112 @@ +/* + * LSCP Shell + * + * Copyright (c) 2014 Christian Schoenebeck + * + * This program is part of LinuxSampler and released under the same terms. + */ + +#include "KeyboardReader.h" + +static KeyboardReader* g_singleton = NULL; +static int g_instanceCount = 0; + +KeyboardReader::KeyboardReader() : Thread(false, false, 1, -1), + m_originalTerminalSetting(TerminalCtrl::now()), + m_callback(NULL), m_doCallback(true) +{ + if (!g_instanceCount) g_singleton = this; + g_instanceCount++; + TerminalCtrl::echoInput(false); +} + +KeyboardReader::~KeyboardReader() { + TerminalCtrl::restore(m_originalTerminalSetting); + // ensures that no temporary copy objects delete the global reference + g_instanceCount--; + if (!g_instanceCount) g_singleton = NULL; +} + +void KeyboardReader::setCallback(Callback_t fn) { + m_callback = fn; +} + +void KeyboardReader::callback(bool b) { + m_doCallback = b; +} + +int KeyboardReader::Main() { + while (true) { + std::vector<char> v = TerminalCtrl::getChars(1, 1); + if (!v.empty()) { + m_fifoMutex.Lock(); + m_fifo.push_back(v0); + if (m_sync.GetUnsafe()) { + bool delimiterReceived = false; + for (std::list<char>::iterator it = m_fifo.begin(); it != m_fifo.end(); ++it) { + if ((*it) == m_syncDelimiter) { + delimiterReceived = true; + break; + } + } + m_fifoMutex.Unlock(); + if (delimiterReceived) m_sync.Set(false); + } else { + m_fifoMutex.Unlock(); + if (m_callback && m_doCallback) (*m_callback)(this); + } + } + TestCancel(); + } + return 0; // just to avoid a warning with some old compilers +} + +bool KeyboardReader::charAvailable() const { + return !m_fifo.empty(); // is thread safe +} + +char KeyboardReader::popChar() { + LockGuard lock(m_fifoMutex); + if (m_fifo.empty()) return 0; + char c = m_fifo.front(); + m_fifo.pop_front(); + return c; +} + +std::string KeyboardReader::popStringToDelimiterSync(char delimiter, bool includeDelimiter) { + m_syncDelimiter = delimiter; + + bool alreadyReceived = false; + m_fifoMutex.Lock(); + for (std::list<char>::iterator it = m_fifo.begin(); it != m_fifo.end(); ++it) { + if ((*it)== delimiter) { + alreadyReceived = true; + break; + } + } + if (!alreadyReceived) m_sync.Set(true); + m_fifoMutex.Unlock(); + if (!alreadyReceived) m_sync.WaitAndUnlockIf(true); + + // if we are here, then there is now a string with the requested delimiter + // in the FIFO + std::string s; + while (charAvailable()) { + char c = popChar(); + if (includeDelimiter || c != delimiter) s += c; + if (c == delimiter) return s; + } + return s; +} + +void KeyboardReader::startReading() { + StartThread(); +} + +void KeyboardReader::stopReading() { + StopThread(); +} + +KeyboardReader* KeyboardReader::singleton() { + return g_singleton; +}
View file
linuxsampler-2718.tar.bz2/src/shell/KeyboardReader.h
Added
@@ -0,0 +1,58 @@ +/* + * LSCP Shell + * + * Copyright (c) 2014 Christian Schoenebeck + * + * This program is part of LinuxSampler and released under the same terms. + */ + +#ifndef KEYBOARDREADER_H +#define KEYBOARDREADER_H + +#include <list> +#include <string> + +#include "../common/global.h" +#include "../common/Thread.h" +#include "../common/Condition.h" +#include "TerminalCtrl.h" + +#define KBD_BACKSPACE 127 +#define KBD_ESCAPE 27 + +using namespace LinuxSampler; + +/** + * Implements a FIFO object that is constantly reading from stdin on a separate + * thread. The reader thread can call a callback function as soon as new data + * arrived from stdin. The reader thread will react on every single key stroke, + * that is it will already fire before line end (RETURN / ENTER) was received. + */ +class KeyboardReader : protected Thread { +public: + typedef void(*Callback_t)(KeyboardReader*); + + KeyboardReader(); + virtual ~KeyboardReader(); + void setCallback(Callback_t fn); + void callback(bool b); + bool charAvailable() const; + char popChar(); + void startReading(); + void stopReading(); + std::string popStringToDelimiterSync(char delimiter, bool includeDelimiter = false); + + static KeyboardReader* singleton(); +protected: + int Main() OVERRIDE; +private: + TerminalSetting m_originalTerminalSetting; + Callback_t m_callback; + std::list<char> m_fifo; + Mutex m_fifoMutex; + bool m_doCallback; + Condition m_sync; + char m_syncDelimiter; +}; + +#endif // KEYBOARDREADER_H
View file
linuxsampler-2718.tar.bz2/src/shell/LSCPClient.cpp
Added
@@ -0,0 +1,354 @@ +/* + * LSCP Shell + * + * Copyright (c) 2014 Christian Schoenebeck + * + * This program is part of LinuxSampler and released under the same terms. + */ + +#include "LSCPClient.h" +#include "lscp.h" +#include <strings.h> + +/** + * This function is called by the LSCPClient receiving thread to identify + * whether the received line is a) just a single line response or b) the first + * line of a multi line response. LSCPClient will provide this information via + * its multiLine() and messageComplete() methods, which are accessed by the + * application's main thread to find out whether it should wait until all lines + * of the multi line message were completely received. This strategy (to let + * the main app thread wait until a multi line message is really entirely + * received) is preferable since it would otherwise require a very complex code + * with a bunch of state variables in the main apps loop, which would also make + * it very error prone. + * + * @param line - input: the input line to check + * @param skipLine - output: whether @a line shall be silently ignored + * @returns true if @a line indicates a multi line response + */ +static bool _lscpLineIndicatesMultiLineResponse(const String& line, bool& skipLine) { + static const String multiLineKey = LSCP_SHK_EXPECT_MULTI_LINE; + static const String lscpRefDocKey = "SHD:1"; + if (line.substr(0, multiLineKey.length()) == multiLineKey) { + skipLine = true; + return true; + } + if (line.substr(0, lscpRefDocKey.length()) == lscpRefDocKey) { + skipLine = false; + return true; + } + skipLine = false; + return false; +} + +/** @brief Default constructor. + * + * Initializes a yet unconnected LSCP client. You need to call connect() + * afterwards to actually let it connect to some LSCP server for being able to + * use the client for something useful. + */ +LSCPClient::LSCPClient() : + Thread(false, false, 1, -1), + hSocket(-1), m_callback(NULL), m_errorCallback(NULL), + m_multiLineExpected(false) +{ +} + +/** @brief Destructor. + * + * Disconnects this client automatically if it is currently still connected to + * some LSCP server. + */ +LSCPClient::~LSCPClient() { + disconnect(); +} + +/** + * Let this client connect to a (already running) LSCP server listening on + * host name @a host and TCP port number @a port. If a connection was + * successfully established, this method will also start a thread which will + * constantly wait for receiving new data sent by the LSCP server. This thread + * will store all received data in an internal buffer which can then be pulled + * out with popLine(). + * + * @param host - host name of LSCP server + * @param port - TCP port number of LSCP server + * @returns true if connection was successful, false on errors. + */ +bool LSCPClient::connect(String host, int port) { + m_lineBuffer.clear(); + m_lines.clear(); + // resolve given host name + hostent* server = ::gethostbyname(host.c_str()); + if (!server) { + std::cerr << "Error: Could not resolve host \"" << host << "\".\n"; + return false; + } + // create local TCP socket + hSocket = ::socket(AF_INET, SOCK_STREAM, 0); + if (hSocket < 0) { + std::cerr << "Error: Could not create local socket.\n"; + return false; + } + // TCP connect to server + sockaddr_in addr; + bzero((char*)&addr, sizeof(addr)); + addr.sin_family = AF_INET; + bcopy((char*)server->h_addr, (char*)&addr.sin_addr.s_addr, server->h_length); + addr.sin_port = htons(port); + if (::connect(hSocket, (sockaddr*)&addr, sizeof(addr)) < 0) { + std::cerr << "Error: Could not connect to host \"" << host << "\".\n"; + std::cerr << "Is linuxsampler running and listening on port " << port << " ?\n"; + disconnect(); + return false; + } + StartThread(); + return true; // success +} + +/** + * Disconnect this client from the currently connected LSCP server. If there + * is no connection, then this method does nothing. + */ +void LSCPClient::disconnect() { + if (hSocket >= 0) { + StopThread(); + ::close(hSocket); + hSocket = -1; + } +} + +/** + * Returns true if this LSCP client is currently connected to a LSCP server. + */ +bool LSCPClient::isConnected() const { + return hSocket >= 0; +} + +/** + * Send one byte given by @a c to the connected LSCP server asynchronously. + * + * @param c - byte (character) to be sent + */ +bool LSCPClient::send(char c) { + String s; + s += c; + return send(s); +} + +/** + * Send a sequence of bytes given by @a s to the connected LSCP server + * asynchronously. + * + * @param s - sequence of bytes (string) to be sent + */ +bool LSCPClient::send(String s) { + //lscpLog("send '%s'\n", s.c_str()); + if (!isConnected()) return false; + int n = ::write(hSocket, &s0, s.size()); + return n == s.size(); +} + +/** + * Send a sequence of bytes given by @a s to the connected LSCP server + * synchronously. This method will then block until a response line was + * received from the LSCP server and will return that response line as result + * to this method. Since this currently assumes a single line response, it + * should just be used for sending LSCP commands which will exepct a single + * line response from the LSCP server. + * + * @param s - sequence of bytes (string) to be sent + * @returns response of LSCP server + */ +String LSCPClient::sendCommandSync(String s) { + m_linesMutex.Lock(); + m_lines.clear(); + m_linesMutex.Unlock(); + + m_sync.Set(true); + if (!send(s + "\n")) return ""; + m_sync.WaitIf(true); + + m_linesMutex.Lock(); + String sResponse = m_lines.back().data; + m_lines.clear(); + m_linesMutex.Unlock(); + + return sResponse; +} + +/** + * Pulls out and returns the next line received from the LSCP server. If the + * return value of this method evaluates to false, then there is currently no + * new line received. The line returned will be removed from the internal + * receive buffer. If that's not what you want, then use lookAheadLine() + * instead. + */ +optional<String> LSCPClient::popLine() { + String s; + LockGuard guard(m_linesMutex); + if (m_lines.empty()) return optional<String>::nothing; + s = m_lines.front().data; + m_lines.pop_front(); + return s; +} + +/** + * Use this method instead of popLine() in case you want to access received + * line(s) without removing them from the internal buffer. + * + * @param index - offset of line to be accessed (0 being the oldest line + * received) + */ +optional<String> LSCPClient::lookAheadLine(int index) { + LockGuard guard(m_linesMutex); + if (index < 0 || index >= m_lines.size()) + return optional<String>::nothing; + std::list<Line>::iterator it = m_lines.begin(); + for (int i = 0; i < index; ++i) ++it; + String s = (*it).data; + return s; +} + +/** + * Returns true if the received line(s), waiting to be pulled out with + * popLine(), is/are completely received. So if the next line waiting to be + * pulled out with popLine() is a single line response, then this function + * returns true. If the next line waiting to be pulled out with popLine() is + * considered a part of multi line response though, this function only returns + * true if all lines of that expected multi line response were entirely + * received already. + * + * @see multiLine() + */ +bool LSCPClient::messageComplete() { + LockGuard guard(m_linesMutex); + if (m_lines.empty()) { + //lscpLog("\tmessageComplete false - buffer empty\n"); + return false; + } + if (!m_lines.front().isMultiLine) { + //lscpLog("\tmessageComplete true - single line\n"); + return true; + } + +// // just for debugging purposes ... +// int k = 0; +// for (std::list<Line>::const_iterator it = m_lines.begin(); +// it != m_lines.end(); ++it, ++k) +// { +// lscpLog("\tmessageComplete k=%d : mul=%d cmpl=%d data='%s'\n", k, (*it).isMultiLine, (*it).isMultiLineComplete, (*it).data.c_str()); +// } + + // so next line is part of a multi line response, check if it's complete ... + for (std::list<Line>::const_iterator it = m_lines.begin(); + it != m_lines.end(); ++it) + { + if ((*it).isMultiLineComplete) return true; + } + return false; +} + +/** + * Returns true if the next line to be pulled out with popLine() is considered + * a single line response, it returns false if the next line is a part of a + * multi line response instead. In the latter case you should call multiLine() + * to check whether all lines of that multi line response were already received + * before pulling them out with popLine(). + * + * @see messageComplete() + */ +bool LSCPClient::multiLine() { + LockGuard guard(m_linesMutex); + if (m_lines.empty()) return false; + bool bIsMultiLine = m_lines.front().isMultiLine; + return bIsMultiLine; +} + +/** + * Returns true if new line(s) were received from LSCP server, waiting to be + * pulled out with popLine(). + */ +bool LSCPClient::lineAvailable() const { + return !m_lines.empty(); // is thread safe +} + +/** + * You may call this method to register a callback function which shall be + * notified if new data was received from the connected LSCP server. + */ +void LSCPClient::setCallback(Callback_t fn) { + m_callback = fn; +} + +/** + * You may call this method to register a callback function which shall be + * notified if some kind of network error occurred. + */ +void LSCPClient::setErrorCallback(Callback_t fn) { + m_errorCallback = fn; +} + +/** + * This method is only called by the internal client thread: a call to this + * method blocks until a complete new line was received from the LSCP server. + */ +optional<String> LSCPClient::receiveLine() { + if (!isConnected()) return optional<String>::nothing; + for (char c; true; ) { + int n = ::read(hSocket, &c, 1); + if (n < 1) return optional<String>::nothing; + if (c == '\r') continue; + if (c == '\n') { + String s = m_lineBuffer; + m_lineBuffer.clear(); + return s; + } + //printf("->%c\n", c); + m_lineBuffer += c; + } + return optional<String>::nothing; +} + +/** + * Client's internal receiving thread's loop. It does nothing else than waiting + * for new data sent by LSCP server and puts the received lines into an + * internal FIFO buffer. + */ +int LSCPClient::Main() { + while (true) { + optional<String> pLine = receiveLine(); + if (pLine) { // if there was a line received ... + //lscpLog("client receiveLine '%s'\n", pLine->c_str()); + String s = *pLine; + + // check whether this is a multi line data ... + bool bSkipLine = false; + bool bMultiLineComplete = false; + if (!m_multiLineExpected && _lscpLineIndicatesMultiLineResponse(s, bSkipLine)) { + m_multiLineExpected = true; + } else if (m_multiLineExpected && s.substr(0, 1) == ".") { + bMultiLineComplete = true; + } + + // store received line in internal FIFO buffer + if (!bSkipLine) { + Line l = { s, m_multiLineExpected, bMultiLineComplete }; + m_linesMutex.Lock(); + m_lines.push_back(l); + m_linesMutex.Unlock(); + } + + // if this was the last line of a multi line response, reset + // for next run + if (m_multiLineExpected && bMultiLineComplete) { + m_multiLineExpected = false; + } + + if (m_sync.GetUnsafe()) m_sync.Set(false); + else if (m_callback) (*m_callback)(this); + } else if (m_errorCallback) (*m_errorCallback)(this); + TestCancel(); + } + return 0; // just to avoid a warning with some old compilers +}
View file
linuxsampler-2718.tar.bz2/src/shell/LSCPClient.h
Added
@@ -0,0 +1,85 @@ +/* + * LSCP Shell + * + * Copyright (c) 2014 Christian Schoenebeck + * + * This program is part of LinuxSampler and released under the same terms. + */ + +#ifndef LSCPCLIENT_H +#define LSCPCLIENT_H + +#include <stdio.h> +#include <stdlib.h> +#include <string> +#include <list> + +#if defined(WIN32) +#include <windows.h> +typedef int socklen_t; +#else +#include <unistd.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/select.h> +#include <sys/time.h> +#include <netinet/in.h> +#include <netinet/tcp.h> +#include <arpa/inet.h> +#include <netdb.h> +#endif + +#include "../common/global.h" +#include "../common/Thread.h" +#include "../common/Mutex.h" +#include "../common/Condition.h" +#include "../common/optional.h" +#include "../network/lscp.h" + +using namespace LinuxSampler; + +/** + * Implements network communication with LinuxSampler's LSCP server. A separate + * thread is spawned which will constantly read on new incoming data coming from + * the LSCP server, it can call a callback function whenever new response data + * arrived. + */ +class LSCPClient : protected Thread { +public: + typedef void(*Callback_t)(LSCPClient*); + + LSCPClient(); + virtual ~LSCPClient(); + bool connect(String host, int port); + void disconnect(); + bool isConnected() const; + bool send(char c); + bool send(String s); + String sendCommandSync(String s); + bool lineAvailable() const; + bool messageComplete(); + bool multiLine(); + optional<String> lookAheadLine(int index); + optional<String> popLine(); + void setCallback(Callback_t fn); + void setErrorCallback(Callback_t fn); +protected: + int Main() OVERRIDE; + optional<String> receiveLine(); +private: + struct Line { + String data; + bool isMultiLine; + bool isMultiLineComplete; + }; + int hSocket; + String m_lineBuffer; + std::list<Line> m_lines; + Mutex m_linesMutex; + Callback_t m_callback; + Callback_t m_errorCallback; + Condition m_sync; + bool m_multiLineExpected; +}; + +#endif // LSCPCLIENT_H
View file
linuxsampler-2718.tar.bz2/src/shell/Makefile.am
Added
@@ -0,0 +1,8 @@ +# set the include path found by configure +AM_CPPFLAGS= $(all_includes) + +AM_CXXFLAGS = -Wreturn-type $(CXX_CPU_SWITCH) + +bin_PROGRAMS = lscp +lscp_SOURCES = lscp.cpp TerminalCtrl.cpp LSCPClient.cpp KeyboardReader.cpp TerminalPrinter.cpp +lscp_LDADD = $(top_builddir)/src/liblinuxsampler.la
View file
linuxsampler-2718.tar.bz2/src/shell/TerminalCtrl.cpp
Added
@@ -0,0 +1,191 @@ +/* + * LSCP Shell + * + * Copyright (c) 2014 Christian Schoenebeck + * + * This program is part of LinuxSampler and released under the same terms. + */ + +#include "TerminalCtrl.h" + +#include <unistd.h> +#include <termios.h> +#include <string.h> +#include <map> +#include <assert.h> +#include <sys/ioctl.h> +#include <stdio.h> +#include "../common/Mutex.h" + +using namespace LinuxSampler; + +/////////////////////////////////////////////////////////////////////////// +// class 'TerminalSetting' + +static Mutex g_mutexReferences; +static std::map<void*,int> g_terminalSettingReferences; + +static termios* _newTermios() { + termios* p = new termios; + LockGuard lock(g_mutexReferences); + g_terminalSettingReferencesp = 1; + return p; +} + +static void _releaseTermiosRef(void* p) { + if (!p) return; + LockGuard lock(g_mutexReferences); + std::map<void*,int>::iterator it = g_terminalSettingReferences.find(p); + assert(it != g_terminalSettingReferences.end()); + assert(it->second > 0); + it->second--; + if (!it->second) { + g_terminalSettingReferences.erase(it); + delete (termios*) p; + } +} + +static termios* _bumpTermiosRef(void* p) { + if (!p) return NULL; + LockGuard lock(g_mutexReferences); + std::map<void*,int>::iterator it = g_terminalSettingReferences.find(p); + assert(it != g_terminalSettingReferences.end()); + assert(it->second > 0); + it->second++; + return (termios*) p; +} + +TerminalSetting::TerminalSetting() : p(NULL) { +} + +TerminalSetting::TerminalSetting(const TerminalSetting& ts) : p(_bumpTermiosRef(ts.p)) { +} + +TerminalSetting::~TerminalSetting() { + _releaseTermiosRef(p); +} + +bool TerminalSetting::valid() const { + return p != NULL; +} + +/////////////////////////////////////////////////////////////////////////// +// class 'TerminalCtrl' + +static termios g_defaults; + +static void _saveDefaults() { + static bool done = false; + if (done) return; + if (tcgetattr(0, &g_defaults) < 0) return; + done = true; +} + +int TerminalCtrl::reset() { + _saveDefaults(); + if (tcsetattr(0, TCSANOW, &g_defaults) < 0) return -1; + return 0; // success +} + +int TerminalCtrl::restore(const TerminalSetting& setting) { + _saveDefaults(); + termios* s = (termios*) setting.p; + if (tcsetattr(0, TCSADRAIN, s) < 0) return -1; + return 0; // success +} + +TerminalSetting TerminalCtrl::now() { + _saveDefaults(); + TerminalSetting setting; + if (!setting.p) setting.p = _newTermios(); + if (tcgetattr(0, (termios*) setting.p) < 0) + return TerminalSetting(); // error, return invalid setting + return setting; // success +} + +int TerminalCtrl::echoInput(bool b) { + _saveDefaults(); + termios settings = {}; + if (tcgetattr(0, &settings) < 0) return -1; + if (b) { + settings.c_lflag |= ECHO; + if (tcsetattr(0, TCSADRAIN, &settings) < 0) return -1; + } else { + settings.c_lflag &= ~ECHO; + if (tcsetattr(0, TCSANOW, &settings) < 0) return -1; + } + return 0; // success +} + +int TerminalCtrl::immediateInput(bool b) { + _saveDefaults(); + termios settings = {}; + if (tcgetattr(0, &settings) < 0) return -1; + if (b) { + settings.c_lflag &= ~ICANON; + settings.c_ccVMIN = 0; + settings.c_ccVTIME = 0; + if (tcsetattr(0, TCSANOW, &settings) < 0) return -1; + } else { + settings.c_lflag |= ICANON; + if (tcsetattr(0, TCSADRAIN, &settings) < 0) return -1; + } + return 0; // success +} + +std::vector<char> TerminalCtrl::getChars(int max, int blockUntilMin, int burstDeciSeconds) { + _saveDefaults(); + std::vector<char> v; + + TerminalSetting original = now(); + if (!original.valid()) return v; + + termios setting = *(termios*)original.p; // copy + setting.c_lflag &= ~ICANON; + setting.c_ccVMIN = blockUntilMin; + setting.c_ccVTIME = burstDeciSeconds; + if (tcsetattr(0, TCSANOW, &setting) < 0) return v; + v.resize(max); + const int n = read(0, &v0, max); + if (n != max) v.resize(n < 0 ? 0 : n); + + restore(original); + return v; +} + +std::vector<char> TerminalCtrl::getCharsToDelimiter(char delimiter, bool includeDelimiter) { + _saveDefaults(); + std::vector<char> result; + while (true) { + std::vector<char> v = getChars(1, 1); + if (v.empty()) break; + if (v0 == delimiter && !includeDelimiter) break; + result.push_back(v0); + if (v0 == delimiter) break; + } + return result; +} + +std::string TerminalCtrl::getStringToDelimiter(char delimiter, bool includeDelimiter) { + _saveDefaults(); + std::string s; + std::vector<char> v = getCharsToDelimiter(delimiter, includeDelimiter); + if (v.empty()) return s; + v.push_back(0); + int n = strlen(&v0); + s.resize(n); + memcpy(&s0, &v0, n); + return s; +} + +int TerminalCtrl::columns() { + struct winsize w; + ioctl(0, TIOCGWINSZ, &w); + return w.ws_col; +} + +int TerminalCtrl::rows() { + struct winsize w; + ioctl(0, TIOCGWINSZ, &w); + return w.ws_row; +}
View file
linuxsampler-2718.tar.bz2/src/shell/TerminalCtrl.h
Added
@@ -0,0 +1,54 @@ +/* + * LSCP Shell + * + * Copyright (c) 2014 Christian Schoenebeck + * + * This program is part of LinuxSampler and released under the same terms. + */ + +#ifndef TERMINALCTRL_H +#define TERMINALCTRL_H + +#include <vector> +#include <string> + +class TerminalCtrl; + +/** + * Represents a state of the command line terminal for being restored later on + * at arbitrary time. + */ +class TerminalSetting { +public: + TerminalSetting(); + TerminalSetting(const TerminalSetting& ts); + virtual ~TerminalSetting(); + bool valid() const; +protected: + void* p; + friend class TerminalCtrl; +}; + +/** + * OS dependent implementation for controlling keyboard input behavior and + * retrieving terminal screen size (rows and columns). + * + * Current implementation assumes a POSIX compatible system. + * + * This is a lite-weight class to prevent a dependency to i.e. libncurses. + */ +class TerminalCtrl { +public: + static std::vector<char> getChars(int max, int blockUntilMin, int burstDeciSeconds = 0); + static std::vector<char> getCharsToDelimiter(char delimiter, bool includeDelimiter = false); + static std::string getStringToDelimiter(char delimiter, bool includeDelimiter = false); + static int echoInput(bool b); + static int immediateInput(bool b); + static int reset(); + static int restore(const TerminalSetting& setting); + static TerminalSetting now(); + static int columns(); + static int rows(); +}; + +#endif // TERMINALCTRL_H
View file
linuxsampler-2718.tar.bz2/src/shell/TerminalPrinter.cpp
Added
@@ -0,0 +1,49 @@ +/* + * LSCP Shell + * + * Copyright (c) 2014 Christian Schoenebeck + * + * This program is part of LinuxSampler and released under the same terms. + */ + +#include "TerminalPrinter.h" +#include <iostream> +#include "CCursor.h" + +TerminalPrinter::TerminalPrinter() : m_lines(0) { + m_col = CCursor::now().column(); + m_screenWidth = TerminalCtrl::columns(); +} + +TerminalPrinter::~TerminalPrinter() { +} + +TerminalPrinter& TerminalPrinter::operator<< (std::string s) { + for (int i = 0; i < s.size(); ++i) + printChar(si); + std::cout << std::flush; + return *this; +} + +void TerminalPrinter::printChar(char c) { + std::cout << c << std::flush; + switch (c) { + case '\r': + m_col = 0; + break; + case '\n': + m_col = 0; + m_lines++; + break; + default: + m_col++; + if (m_col >= m_screenWidth) { + m_col = 0; + m_lines++; + } + } +} + +int TerminalPrinter::linesAdvanced() const { + return m_lines; +}
View file
linuxsampler-2718.tar.bz2/src/shell/TerminalPrinter.h
Added
@@ -0,0 +1,34 @@ +/* + * LSCP Shell + * + * Copyright (c) 2014 Christian Schoenebeck + * + * This program is part of LinuxSampler and released under the same terms. + */ + +#ifndef TERMINALPRINTER_H +#define TERMINALPRINTER_H + +#include <string> + +/** + * Simple helper class that remembers the amount of lines advanced on the + * terminal (since this object was constructed) by printing out text to the + * terminal with this class. This way the content been printed on the terminal + * can completely be erased afterwards once necessary. + */ +class TerminalPrinter { +public: + TerminalPrinter(); + virtual ~TerminalPrinter(); + int linesAdvanced() const; + TerminalPrinter& operator<< (const std::string s); +protected: + void printChar(char c); +private: + int m_lines; + int m_col; + int m_screenWidth; // in characters +}; + +#endif // TERMINALPRINTER_H
View file
linuxsampler-2718.tar.bz2/src/shell/lscp.cpp
Added
@@ -0,0 +1,597 @@ +/* + * LSCP Shell + * + * Copyright (c) 2014 Christian Schoenebeck + * + * This program is part of LinuxSampler and released under the same terms. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <iostream> +#include <sstream> +#include <string.h> + +#include "lscp.h" +#include "LSCPClient.h" +#include "KeyboardReader.h" +#include "TerminalCtrl.h" +#include "CFmt.h" +#include "CCursor.h" +#include "TerminalPrinter.h" +#if DEBUG_LSCP_SHELL +# include <stdarg.h> +# include <sys/timeb.h> +#endif // DEBUG_LSCP_SHELL +#include "../common/global.h" +#include "../common/global_private.h" +#include "../common/Condition.h" + +#define LSCP_DEFAULT_HOST "localhost" +#define LSCP_DEFAULT_PORT 8888 + +using namespace std; +using namespace LinuxSampler; + +static LSCPClient* g_client = NULL; +static KeyboardReader* g_keyboardReader = NULL; +static Condition g_todo; +static String g_goodPortion; +static String g_badPortion; +static String g_suggestedPortion; +static int g_linesActive = 0; +static const String g_prompt = "lscp=# "; +static std::vector<String> g_commandHistory; +static int g_commandHistoryIndex = -1; +static String g_doc; +static bool g_quitAppRequested = false; +static int g_exitCode = 0; +#if DEBUG_LSCP_SHELL +static FILE* g_df; ///< handle to output log file (just for debugging) +#endif + +/** + * Log the given debug message to a log file. This works only if + * DEBUG_LSCP_SHELL was turned on in lscp.h. Otherwise this function does + * nothing. Usage of this function is equivalent to printf(). + */ +void lscpLog(const char* format, ...) { + #if DEBUG_LSCP_SHELL + // assemble variable argument list given to this function call + va_list arg; + va_start(arg, format); + // write current timestamp to log file + struct timeb tp; + ftime(&tp); + fprintf(g_df, "%d.%03d ", tp.time, tp.millitm); + // write actual debug message to log file + vfprintf(g_df, format, arg); + fflush(g_df); + va_end(arg); + #endif // DEBUG_LSCP_SHELL +} + +static void printUsage() { + cout << "lscp - The LinuxSampler Control Protocol (LSCP) Shell." << endl; + cout << endl; + cout << "Usage: lscp -h HOSTNAME -p PORT" << endl; + cout << endl; + cout << " -h Host name of LSCP server (default \"" << LSCP_DEFAULT_HOST << "\")." << endl; + cout << endl; + cout << " -p TCP port number of LSCP server (default " << LSCP_DEFAULT_PORT << ")." << endl; + cout << endl; + cout << " --no-auto-correct Don't perform auto correction of obvious syntax errors." << endl; + cout << endl; + cout << " --no-doc Don't show LSCP reference documentation on screen." << endl; + cout << endl; +} + +static void printWelcome() { + cout << "Welcome to lscp " << VERSION << ", the LinuxSampler Control Protocol (LSCP) shell." << endl; + cout << endl; +} + +static void printPrompt() { + cout << g_prompt << flush; +} + +static int promptOffset() { + return g_prompt.size(); +} + +static void quitApp(int code = 0) { + //lscpLog("quit app\n"); + g_exitCode = code; + g_quitAppRequested = true; +} + +// Called by the network reading thread, whenever new data arrived from the +// network connection. +static void onLSCPClientNewInputAvailable(LSCPClient* client) { + g_todo.Set(true); +} + +// Called by the keyboard reading thread, whenever a key stroke was received. +static void onNewKeyboardInputAvailable(KeyboardReader* reader) { + g_todo.Set(true); +} + +// Called on network error or when server side closed the TCP connection. +static void onLSCPClientErrorOccured(LSCPClient* client) { + //lscpLog("client error callback\n"); + quitApp(); +} + +/// Will be called when the user hits tab key to trigger auto completion. +static void autoComplete() { + if (g_suggestedPortion.empty()) return; + String s; + // let the server delete mistaken characters first + for (int i = 0; i < g_badPortion.size(); ++i) s += '\b'; + // now add the suggested, correct characters + s += g_suggestedPortion; + g_suggestedPortion.clear(); + g_client->send(s); +} + +static void commandFromHistory(int offset) { + if (g_commandHistoryIndex + offset < 0 || + g_commandHistoryIndex + offset >= g_commandHistory.size()) return; + g_commandHistoryIndex += offset; + int len = g_goodPortion.size() + g_badPortion.size(); + String command; + // erase current active line + for (int i = 0; i < len; ++i) command += '\b'; + // transmit new/old line to LSCP server + command += g_commandHistoryg_commandHistory.size() - g_commandHistoryIndex - 1; + g_client->send(command); +} + +/// Will be called when the user hits arrow up key, to iterate to an older command line. +static void previousCommand() { + commandFromHistory(1); +} + +/// Will be called when the user hits arrow down key, to iterate to a more recent command line. +static void nextCommand() { + commandFromHistory(-1); +} + +/// Will be called whenever the user hits ENTER, to store the line in the command history. +static void storeCommandInHistory(const String& sCommand) { + g_commandHistoryIndex = -1; // reset history index + // don't save the command if the previous one was the same + if (g_commandHistory.empty() || g_commandHistory.back() != sCommand) + g_commandHistory.push_back(sCommand); +} + +/// Splits the given string into individual lines for the given screen resolution. +static std::vector<String> splitForScreen(const String& s, int cols, int rows) { + std::vector<String> lines; + if (rows <= 0 || cols <= 0) return lines; + String line; + for (int i = 0; i < s.size(); ++i) { + char c = si; + if (c == '\r') continue; + if (c == '\n') { + lines.push_back(line); + if (lines.size() >= rows) return lines; + line.clear(); + continue; + } + line += c; + if (line.size() >= cols) { + lines.push_back(line); + if (lines.size() >= rows) return lines; + line.clear(); + } + } + return lines; +} + +/** + * Will be called whenever the LSCP documentation reference to be shown, has + * been changed. This call will accordingly update the screen with the new + * documentation text received from LSCP server. + */ +static void updateDoc() { + const int vOffset = 2; + + CCursor originalCursor = CCursor::now(); + CCursor cursor = originalCursor; + cursor.toColumn(0).down(vOffset); + + // wipe out current documentation off screen + cursor.clearVerticalToBottom(); + + if (g_doc.empty()) { + // restore original cursor position + cursor.up(vOffset).toColumn(originalCursor.column()); + return; + } + + // get screen size (in characters) + const int cols = TerminalCtrl::columns(); + const int rows = TerminalCtrl::rows(); + + // convert the string block into individual lines according to screen resolution + std::vector<String> lines = splitForScreen(g_doc, cols - 1, rows); + + // print lines onto screen + for (int row = 0; row < lines.size(); ++row) + std::cout << linesrow << std::endl; + + // restore original cursor position + cursor.up(vOffset + lines.size()).toColumn(originalCursor.column()); +} + +/** + * This LSCP shell application is designed as thin client. That means the heavy + * LSCP grammar evaluation tasks are peformed by the LSCP server and the shell + * application's task are simply limited to forward individual characters typed + * by the user to the LSCP server and showing the result of the LSCP server's + * evaluation to the user on the screen. This has the big advantage that the + * shell works perfectly with any machine running (some minimum recent version + * of) LinuxSampler, no matter which precise LSCP version the server side + * is using. Which reduces the maintenance efforts for the shell application + * development tremendously. + * + * As soon as this application established a TCP connection to a LSCP server, it + * sends this command to the LSCP server: + * @code + * SET SHELL INTERACT 1 + * @endcode + * which will inform the LSCP server that this LSCP client is actually a LSCP + * shell application. The shell will then simply forward every single character + * typed by the user immediately to the LSCP server. The LSCP server in turn + * will evaluate every single character received and will return immediately a + * specially formatted string to the shell application like (assuming the user's + * current command line was "CREATE AUasdf"): + * @code + * SHU:1:CREATE AU{{GF}}asdf{{CU}}{{SB}}DIO_OUTPUT_DEVICE + * @endcode + * which informs this shell application about the result of the LSCP grammar + * evaluation and allows the shell to easily show that result of the evaluation + * to the user on the screen. In the example reply above, the prefix "SHU:" just + * indicates to the shell application that this response line is the result + * of the latest grammar evaluation, the number followed (here 1) indicates the + * semantic status of the current command line: + * + * - 0: Command line is complete, thus ENTER key may be hit by the user now. + * - 1: Current command line contains syntax error(s). + * - 2: Command line is incomplete, but contains no syntax errors so far. + * + * Then the actual current command line follows, with special markers: + * + * - Left of "{{GF}}" the command line is syntactically correct, right of that + * marker the command line is syntactically wrong. + * + * - Marker "{{CU}}" indicates the current cursor position of the command line. + * + * - Right of "{{SB}}" follows the current auto completion suggestion, so that + * string portion was not typed by the user yet, but is expected to be typed + * by him next, to retain syntax correctness. The auto completion portion is + * added by the LSCP server only if there is one unique way to add characters + * to the current command line. If there are multiple possibilities, than this + * portion is missing due to ambiguity. + * + * - Optionally there might also be a "{{PB}" marker on right hand side of the + * line. The text right to that marker reflects all possibilities at the + * user's current input position (which cannot be auto completed) due to + * ambiguity, including abstract (a.k.a. "non-terminal") symbols like: + * @code + * <digit>, <text>, <number>, etc. + * @endcode + * This portion is added by the LSCP server only if there is not a unique way + * to add characters to the current command line. + */ +int main(int argc, char *argv) { + #if DEBUG_LSCP_SHELL + g_df = fopen("lscp.log", "w"); + if (!g_df) { + std::cerr << "Could not open lscp.log for writing!\n"; + exit(-1); + } + #endif // DEBUG_LSCP_SHELL + + String host = LSCP_DEFAULT_HOST; + int port = LSCP_DEFAULT_PORT; + bool autoCorrect = true; + bool showDoc = true; + + // parse command line arguments + for (int i = 0; i < argc; ++i) { + String s = argvi; + if (s == "-h" || s == "--host") { + if (++i >= argc) { + printUsage(); + return -1; + } + host = argvi; + } else if (s == "-p" || s == "--port") { + if (++i >= argc) { + printUsage(); + return -1; + } + port = atoi(argvi); + if (port <= 0) { + cerr << "Error: invalid port argument \"" << argvi << "\"\n"; + return -1; + } + } else if (s == "--no-auto-correct") { + autoCorrect = false; + } else if (s == "--no-doc") { + showDoc = false; + } else if (s0 == '-') { // invalid / unknown command line argument ... + printUsage(); + return -1; + } + } + + // try to connect to the sampler's LSCP server and start a thread for + // receiving incoming network data from the sampler's LSCP server + g_client = new LSCPClient; + g_client->setErrorCallback(onLSCPClientErrorOccured); + if (!g_client->connect(host, port)) return -1; + g_client->sendCommandSync( + (showDoc) ? "SET SHELL DOC 1" : "SET SHELL DOC 0" + ); + String sResponse = g_client->sendCommandSync( + (autoCorrect) ? "SET SHELL AUTO_CORRECT 1" : "SET SHELL AUTO_CORRECT 0" + ); + sResponse = g_client->sendCommandSync("SET SHELL INTERACT 1"); + if (sResponse.substr(0, 2) != "OK") { + cerr << "Error: sampler too old, it does not support shell instructions\n"; + return -1; + } + g_client->setCallback(onLSCPClientNewInputAvailable); + + printWelcome(); + printPrompt(); + + // start a thread for reading from the local text input keyboard + // (keyboard echo will be disabled as well to have a clean control on what + // is appearing on the screen) + g_keyboardReader = new KeyboardReader; + g_keyboardReader->setCallback(onNewKeyboardInputAvailable); + g_keyboardReader->startReading(); + + int iKbdEscapeCharsExpected = 0; + char kbdPrevEscapeChar; + + // main thread's loop + // + // This application runs 3 threads: + // + // - Keyboard thread: reads constantly on stdin for new characters (which + // will block this keyboard thread until new character(s) were typed by + // the user) and pushes the typed characters into a FIFO buffer. + // + // - Network thread: reads constantly on the TCP connection for new bytes + // being sent by the LSCP server (which will block this network thread + // until new bytes were received) and pushes the received bytes into a + // FIFO buffer. + // + // - Main thread: this thread runs in the loop below. The main thread sleeps + // (by using the "g_todo" condition variable) until either new keys on the + // keyboard were stroke by the user or until new bytes were received from + // the LSCP server. The main thread will then accordingly send the typed + // characters to the LSCP server and/or show the result of the LSCP + // server's latest evaluation to the user on the screen (by pulling those + // data from the other two thread's FIFO buffers). + while (!g_quitAppRequested) { + // sleep until either new data from the network or from keyboard arrived + g_todo.WaitIf(false); + // immediately unset the condition variable and unlock it + g_todo.Set(false); + g_todo.Unlock(); + + // did network data arrive? + while (g_client->messageComplete()) { + String line = *g_client->popLine(); + //lscpLog("client '%s'\n", line.c_str()); + if (line.substr(0,4) == "SHU:") { + int code = 0, n = 0; + int res = sscanf(line.c_str(), "SHU:%d:%n", &code, &n); + if (res >= 1) { + String s = line.substr(n); + + // extract portion that is already syntactically correct + size_t iGood = s.find(LSCP_SHK_GOOD_FRONT); + String sGood = s.substr(0, iGood); + if (sGood.find(LSCP_SHK_CURSOR) != string::npos) + sGood.erase(sGood.find(LSCP_SHK_CURSOR), strlen(LSCP_SHK_CURSOR)); // erase cursor marker + + // extract portion that was written syntactically incorrect + String sBad = s.substr(iGood + strlen(LSCP_SHK_GOOD_FRONT)); + if (sBad.find(LSCP_SHK_CURSOR) != string::npos) + sBad.erase(sBad.find(LSCP_SHK_CURSOR), strlen(LSCP_SHK_CURSOR)); // erase cursor marker + if (sBad.find(LSCP_SHK_SUGGEST_BACK) != string::npos) + sBad.erase(sBad.find(LSCP_SHK_SUGGEST_BACK)); // erase auto suggestion portion + if (sBad.find(LSCP_SHK_POSSIBILITIES_BACK) != string::npos) + sBad.erase(sBad.find(LSCP_SHK_POSSIBILITIES_BACK)); // erase possibilities portion + + // extract portion that is suggested for auto completion + String sSuggest; + if (s.find(LSCP_SHK_SUGGEST_BACK) != string::npos) { + sSuggest = s.substr(s.find(LSCP_SHK_SUGGEST_BACK) + strlen(LSCP_SHK_SUGGEST_BACK)); + if (sSuggest.find(LSCP_SHK_CURSOR) != string::npos) + sSuggest.erase(sSuggest.find(LSCP_SHK_CURSOR), strlen(LSCP_SHK_CURSOR)); // erase cursor marker + if (sSuggest.find(LSCP_SHK_POSSIBILITIES_BACK) != string::npos) + sSuggest.erase(sSuggest.find(LSCP_SHK_POSSIBILITIES_BACK)); // erase possibilities portion + } + + // extract portion that provides all current possibilities + // (that is all branches in the current grammar tree) + String sPossibilities; + if (s.find(LSCP_SHK_POSSIBILITIES_BACK) != string::npos) { + sPossibilities = s.substr(s.find(LSCP_SHK_POSSIBILITIES_BACK) + strlen(LSCP_SHK_POSSIBILITIES_BACK)); + } + + // extract current cursor position + int cursorColumn = sGood.size(); + String sCursor = s; + if (sCursor.find(LSCP_SHK_GOOD_FRONT) != string::npos) + sCursor.erase(sCursor.find(LSCP_SHK_GOOD_FRONT), strlen(LSCP_SHK_GOOD_FRONT)); // erase good/bad marker + if (sCursor.find(LSCP_SHK_SUGGEST_BACK) != string::npos) + sCursor.erase(sCursor.find(LSCP_SHK_SUGGEST_BACK), strlen(LSCP_SHK_SUGGEST_BACK)); // erase suggestion marker + if (sCursor.find(LSCP_SHK_CURSOR) != string::npos) + cursorColumn = sCursor.find(LSCP_SHK_CURSOR); + + // store those informations globally for the auto-completion + // feature + g_goodPortion = sGood; + g_badPortion = sBad; + g_suggestedPortion = sSuggest; + + //printf("line '%s' good='%s' bad='%s' suggested='%s' cursor=%d\n", line.c_str(), sGood.c_str(), sBad.c_str(), sSuggest.c_str(), cursorColumn); + + // clear current command line on screen + // (which may have been printed over several lines) + CCursor cursor = CCursor::now().toColumn(0).clearLine(); + for (int i = 0; i < g_linesActive; ++i) + cursor = cursor.down().clearLine(); + if (g_linesActive) cursor = cursor.up(g_linesActive).toColumn(0); + printPrompt(); + + // print out the gathered informations on the screen + TerminalPrinter p; + CFmt cfmt; + if (code == LSCP_SHU_COMPLETE) cfmt.bold().green(); + else cfmt.bold().white(); + p << sGood; + cfmt.reset().red(); + p << sBad; + cfmt.bold().yellow(); + p << sSuggest; + if (!sPossibilities.empty()) + p << " <- " << sPossibilities; + + // move cursor back to the appropriate input position in + // the command line (which may be several lines above) + g_linesActive = p.linesAdvanced(); + if (p.linesAdvanced()) cursor.up(p.linesAdvanced()); + cursor.toColumn(cursorColumn + promptOffset()); + } + } else if (line.substr(0,4) == "SHD:") { // new LSCP doc reference section received ... + int code = LSCP_SHD_NO_MATCH; + int res = sscanf(line.c_str(), "SHD:%d", &code); + g_doc.clear(); + if (code == LSCP_SHD_MATCH) { + while (true) { // multi-line response expected (terminated by dot line) ... + if (line.substr(0, 1) == ".") break; + if (!g_client->lineAvailable()) break; + line = *g_client->popLine(); + g_doc += line; + } + } + updateDoc(); + } else if (line.substr(0,2) == "OK") { // single-line response expected ... + cout << endl << flush; + + // wipe out potential current documentation off screen + CCursor cursor = CCursor::now(); + cursor.clearVerticalToBottom(); + + CFmt cfmt; + cfmt.green(); + cout << line.substr(0,2) << flush; + cfmt.reset(); + cout << line.substr(2) << endl << flush; + printPrompt(); + } else if (line.substr(0,3) == "WRN") { // single-line response expected ... + cout << endl << flush; + + // wipe out potential current documentation off screen + CCursor cursor = CCursor::now(); + cursor.clearVerticalToBottom(); + + CFmt cfmt; + cfmt.yellow(); + cout << line.substr(0,3) << flush; + cfmt.reset(); + cout << line.substr(3) << endl << flush; + printPrompt(); + } else if (line.substr(0,3) == "ERR") { // single-line response expected ... + cout << endl << flush; + + // wipe out potential current documentation off screen + CCursor cursor = CCursor::now(); + cursor.clearVerticalToBottom(); + + CFmt cfmt; + cfmt.bold().red(); + cout << line.substr(0,3) << flush; + cfmt.reset(); + cout << line.substr(3) << endl << flush; + printPrompt(); + } else if (g_client->multiLine()) { // multi-line response expected ... + cout << endl << flush; + + // wipe out potential current documentation off screen + CCursor cursor = CCursor::now(); + cursor.clearVerticalToBottom(); + + while (true) { + cout << line << endl << flush; + if (line.substr(0, 1) == ".") break; + if (!g_client->lineAvailable()) break; + line = *g_client->popLine(); + } + printPrompt(); + } else { + cout << endl << flush; + + // wipe out potential current documentation off screen + CCursor cursor = CCursor::now(); + cursor.clearVerticalToBottom(); + + cout << line << endl << flush; + printPrompt(); + } + } + + // did keyboard input arrive? + while (g_keyboardReader->charAvailable()) { + char c = g_keyboardReader->popChar(); + //lscpLog("keyboard '%c' (dec %d%s)'\n", c, (int)c, iKbdEscapeCharsExpected ? " ESC SEQ" : ""); + + //std::cout << c << "(" << int(c) << ")" << std::endl << std::flush; + if (iKbdEscapeCharsExpected) { // escape sequence (still) expected now ... + iKbdEscapeCharsExpected--; + if (iKbdEscapeCharsExpected) kbdPrevEscapeChar = c; + else { // escape sequence is complete ... + if (kbdPrevEscapeChar == 91 && c == 65) // up key + previousCommand(); + else if (kbdPrevEscapeChar == 91 && c == 66) // down key + nextCommand(); + else if (kbdPrevEscapeChar == 91 && c == 68) // left key + g_client->send(2); // custom usage of this ASCII code + else if (kbdPrevEscapeChar == 91 && c == 67) // right key + g_client->send(3); // custom usage of this ASCII code + } + continue; // don't send this escape sequence character to LSCP server + } else if (c == KBD_ESCAPE) { // escape sequence for special keys expected next ... + iKbdEscapeCharsExpected = 2; + continue; // don't send ESC character to LSCP server + } else if (c == KBD_BACKSPACE) { + c = '\b'; + } else if (c == '\t') { // auto completion ... + autoComplete(); + continue; // don't send tab character to LSCP server + } else if (c == '\n') { + storeCommandInHistory(g_goodPortion + g_badPortion); + } + + g_client->send(c); + } + } + + // Application is going to exit (due to user request or server + // disconnection). Clean up everything ... + std::cout << std::endl << std::flush; + if (g_client) delete g_client; + if (g_keyboardReader) delete g_keyboardReader; + + return g_exitCode; +}
View file
linuxsampler-2718.tar.bz2/src/shell/lscp.h
Added
@@ -0,0 +1,20 @@ +/* + * LSCP Shell + * + * Copyright (c) 2014 Christian Schoenebeck + * + * This program is part of LinuxSampler and released under the same terms. + */ + +#ifndef LSCP_SHELL_H +#define LSCP_SHELL_H + +// Turn this on if you need to debug something in the LSCP shell. It will cause +// a debug log file "lscp.log" to be created and written to on calls to +// lscpLog(). Note: the log file will be created / opened in the current working +// directory! +#define DEBUG_LSCP_SHELL 0 + +void lscpLog(const char* format, ...); + +#endif // LSCP_SHELL_H
View file
linuxsampler-2342.tar.bz2/src/testcases/Makefile.am -> linuxsampler-2718.tar.bz2/src/testcases/Makefile.am
Changed
@@ -1,4 +1,4 @@ -INCLUDES = $(all_includes) +AM_CPPFLAGS = $(all_includes) METASOURCES = AUTO if HAVE_COREMIDI
Locations
Projects
Search
Status Monitor
Help
Open Build Service
OBS Manuals
API Documentation
OBS Portal
Reporting a Bug
Contact
Mailing List
Forums
Chat (IRC)
Twitter
Open Build Service (OBS)
is an
openSUSE project
.