Browse Source

Update zynaddsubfx to latest version, working without UI for now

tags/1.9.7
falkTX 10 years ago
parent
commit
c7cddfe5b0
100 changed files with 9349 additions and 2244 deletions
  1. +8
    -0
      Makefile
  2. +4
    -1
      source/Makefile.mk
  3. +4
    -0
      source/backend/Makefile
  4. +4
    -0
      source/bridges-plugin/Makefile
  5. +61
    -0
      source/modules/rtosc/Makefile
  6. +277
    -0
      source/modules/rtosc/miditable.h
  7. +371
    -0
      source/modules/rtosc/port-sugar.h
  8. +219
    -0
      source/modules/rtosc/ports.h
  9. +238
    -0
      source/modules/rtosc/rtosc.h
  10. +533
    -0
      source/modules/rtosc/src/cpp/midimapper.cpp
  11. +267
    -0
      source/modules/rtosc/src/cpp/miditable.cpp
  12. +828
    -0
      source/modules/rtosc/src/cpp/ports.cpp
  13. +144
    -0
      source/modules/rtosc/src/cpp/subtree-serialize.cpp
  14. +180
    -0
      source/modules/rtosc/src/cpp/thread-link.cpp
  15. +174
    -0
      source/modules/rtosc/src/cpp/undo-history.cpp
  16. +76
    -0
      source/modules/rtosc/src/dispatch.c
  17. +673
    -0
      source/modules/rtosc/src/rtosc.c
  18. +9
    -0
      source/modules/rtosc/subtree-serialize.h
  19. +97
    -0
      source/modules/rtosc/thread-link.h
  20. +116
    -0
      source/modules/rtosc/typed-message.h
  21. +34
    -0
      source/modules/rtosc/undo-history.h
  22. +6
    -7
      source/native-plugins/Makefile
  23. +66
    -94
      source/native-plugins/zynaddsubfx-fx.cpp
  24. +28
    -15
      source/native-plugins/zynaddsubfx-src.cpp
  25. +115
    -210
      source/native-plugins/zynaddsubfx-synth.cpp
  26. +19
    -19
      source/native-plugins/zynaddsubfx-ui.cpp
  27. +0
    -384
      source/native-plugins/zynaddsubfx/CMakeLists.txt
  28. +0
    -2
      source/native-plugins/zynaddsubfx/DSP/AnalogFilter.cpp
  29. +6
    -4
      source/native-plugins/zynaddsubfx/DSP/AnalogFilter.h
  30. +17
    -0
      source/native-plugins/zynaddsubfx/DSP/FFTwrapper.cpp
  31. +20
    -2
      source/native-plugins/zynaddsubfx/DSP/FFTwrapper.h
  32. +11
    -10
      source/native-plugins/zynaddsubfx/DSP/Filter.cpp
  33. +2
    -1
      source/native-plugins/zynaddsubfx/DSP/Filter.h
  34. +5
    -4
      source/native-plugins/zynaddsubfx/DSP/FormantFilter.cpp
  35. +3
    -1
      source/native-plugins/zynaddsubfx/DSP/FormantFilter.h
  36. +0
    -2
      source/native-plugins/zynaddsubfx/DSP/SVFilter.cpp
  37. +9
    -10
      source/native-plugins/zynaddsubfx/DSP/Unison.cpp
  38. +3
    -1
      source/native-plugins/zynaddsubfx/DSP/Unison.h
  39. +12
    -13
      source/native-plugins/zynaddsubfx/Effects/Alienwah.cpp
  40. +4
    -16
      source/native-plugins/zynaddsubfx/Effects/Alienwah.h
  41. +7
    -6
      source/native-plugins/zynaddsubfx/Effects/Chorus.cpp
  42. +1
    -1
      source/native-plugins/zynaddsubfx/Effects/Chorus.h
  43. +13
    -12
      source/native-plugins/zynaddsubfx/Effects/Distorsion.cpp
  44. +1
    -1
      source/native-plugins/zynaddsubfx/Effects/Distorsion.h
  45. +12
    -10
      source/native-plugins/zynaddsubfx/Effects/DynamicFilter.cpp
  46. +1
    -1
      source/native-plugins/zynaddsubfx/Effects/DynamicFilter.h
  47. +51
    -4
      source/native-plugins/zynaddsubfx/Effects/EQ.cpp
  48. +11
    -3
      source/native-plugins/zynaddsubfx/Effects/EQ.h
  49. +8
    -6
      source/native-plugins/zynaddsubfx/Effects/Echo.cpp
  50. +1
    -1
      source/native-plugins/zynaddsubfx/Effects/Echo.h
  51. +14
    -10
      source/native-plugins/zynaddsubfx/Effects/Effect.cpp
  52. +30
    -11
      source/native-plugins/zynaddsubfx/Effects/Effect.h
  53. +171
    -53
      source/native-plugins/zynaddsubfx/Effects/EffectMgr.cpp
  54. +25
    -12
      source/native-plugins/zynaddsubfx/Effects/EffectMgr.h
  55. +21
    -33
      source/native-plugins/zynaddsubfx/Effects/Phaser.cpp
  56. +1
    -1
      source/native-plugins/zynaddsubfx/Effects/Phaser.h
  57. +41
    -45
      source/native-plugins/zynaddsubfx/Effects/Reverb.cpp
  58. +1
    -1
      source/native-plugins/zynaddsubfx/Effects/Reverb.h
  59. +192
    -0
      source/native-plugins/zynaddsubfx/Misc/Allocator.cpp
  60. +113
    -0
      source/native-plugins/zynaddsubfx/Misc/Allocator.h
  61. +70
    -61
      source/native-plugins/zynaddsubfx/Misc/Bank.cpp
  62. +18
    -20
      source/native-plugins/zynaddsubfx/Misc/Bank.h
  63. +3
    -1
      source/native-plugins/zynaddsubfx/Misc/CMakeLists.txt
  64. +96
    -23
      source/native-plugins/zynaddsubfx/Misc/Config.cpp
  65. +1
    -2
      source/native-plugins/zynaddsubfx/Misc/Config.h
  66. +0
    -121
      source/native-plugins/zynaddsubfx/Misc/Dump.cpp
  67. +0
    -63
      source/native-plugins/zynaddsubfx/Misc/Dump.h
  68. +442
    -126
      source/native-plugins/zynaddsubfx/Misc/Master.cpp
  69. +31
    -31
      source/native-plugins/zynaddsubfx/Misc/Master.h
  70. +83
    -66
      source/native-plugins/zynaddsubfx/Misc/Microtonal.cpp
  71. +8
    -6
      source/native-plugins/zynaddsubfx/Misc/Microtonal.h
  72. +1296
    -0
      source/native-plugins/zynaddsubfx/Misc/MiddleWare.cpp
  73. +40
    -0
      source/native-plugins/zynaddsubfx/Misc/MiddleWare.h
  74. +491
    -442
      source/native-plugins/zynaddsubfx/Misc/Part.cpp
  75. +52
    -51
      source/native-plugins/zynaddsubfx/Misc/Part.h
  76. +479
    -0
      source/native-plugins/zynaddsubfx/Misc/PresetExtractor.cpp
  77. +22
    -0
      source/native-plugins/zynaddsubfx/Misc/PresetExtractor.h
  78. +4
    -3
      source/native-plugins/zynaddsubfx/Misc/Recorder.cpp
  79. +3
    -2
      source/native-plugins/zynaddsubfx/Misc/Recorder.h
  80. +41
    -5
      source/native-plugins/zynaddsubfx/Misc/Util.cpp
  81. +72
    -2
      source/native-plugins/zynaddsubfx/Misc/Util.h
  82. +4
    -3
      source/native-plugins/zynaddsubfx/Misc/XMLwrapper.cpp
  83. +20
    -19
      source/native-plugins/zynaddsubfx/Nio/AlsaEngine.cpp
  84. +15
    -14
      source/native-plugins/zynaddsubfx/Nio/AlsaEngine.h
  85. +2
    -2
      source/native-plugins/zynaddsubfx/Nio/AudioOut.cpp
  86. +2
    -1
      source/native-plugins/zynaddsubfx/Nio/AudioOut.h
  87. +9
    -4
      source/native-plugins/zynaddsubfx/Nio/CMakeLists.txt
  88. +14
    -8
      source/native-plugins/zynaddsubfx/Nio/EngineMgr.cpp
  89. +3
    -2
      source/native-plugins/zynaddsubfx/Nio/EngineMgr.h
  90. +23
    -10
      source/native-plugins/zynaddsubfx/Nio/InMgr.cpp
  91. +3
    -1
      source/native-plugins/zynaddsubfx/Nio/InMgr.h
  92. +58
    -17
      source/native-plugins/zynaddsubfx/Nio/JackEngine.cpp
  93. +18
    -13
      source/native-plugins/zynaddsubfx/Nio/JackEngine.h
  94. +176
    -0
      source/native-plugins/zynaddsubfx/Nio/JackMultiEngine.cpp
  95. +46
    -0
      source/native-plugins/zynaddsubfx/Nio/JackMultiEngine.h
  96. +31
    -8
      source/native-plugins/zynaddsubfx/Nio/Nio.cpp
  97. +6
    -1
      source/native-plugins/zynaddsubfx/Nio/Nio.h
  98. +4
    -4
      source/native-plugins/zynaddsubfx/Nio/NulEngine.cpp
  99. +1
    -1
      source/native-plugins/zynaddsubfx/Nio/NulEngine.h
  100. +304
    -104
      source/native-plugins/zynaddsubfx/Nio/OssEngine.cpp

+ 8
- 0
Makefile View File

@@ -73,6 +73,10 @@ ALL_LIBS += $(MODULEDIR)/rtaudio.a
ALL_LIBS += $(MODULEDIR)/rtmidi.a
endif

ifeq ($(HAVE_ZYN_DEPS),true)
ALL_LIBS += $(MODULEDIR)/rtosc.a
endif

ifeq ($(HAVE_QT4),true)
ALL_LIBS += $(MODULEDIR)/theme.qt4.a
endif
@@ -534,9 +538,13 @@ endif
bin/resources/nekofilter/*.png \
$(DESTDIR)$(PREFIX)/share/carla/resources/nekofilter/

ifeq ($(HAVE_ZYN_DEPS),true)
ifeq ($(HAVE_ZYN_UI_DEPS),true)
install -m 644 \
bin/resources/zynaddsubfx/*.png \
$(DESTDIR)$(PREFIX)/share/carla/resources/zynaddsubfx/
endif
endif

# Install resources (re-use python files)
$(LINK) $(PREFIX)/share/carla/carla_app.py $(DESTDIR)$(PREFIX)/share/carla/resources/


+ 4
- 1
source/Makefile.mk View File

@@ -419,6 +419,9 @@ ifeq ($(HAVE_NTK),true)
HAVE_ZYN_UI_DEPS = true
endif

# TESTING
HAVE_ZYN_UI_DEPS = false

ifeq ($(HAVE_DGL),true)
NATIVE_PLUGINS_LIBS += $(DGL_LIBS)
ifeq ($(HAVE_PROJECTM),true)
@@ -429,7 +432,7 @@ endif
ifeq ($(EXPERIMENTAL_PLUGINS),true)
BASE_FLAGS += -DHAVE_EXPERIMENTAL_PLUGINS
NATIVE_PLUGINS_LIBS += -lclxclient -lclthreads -lzita-convolver -lzita-resampler
NATIVE_PLUGINS_LIBS += $(shell pkg-config --libs cairo libpng12 fftw3f x11 xft)
NATIVE_PLUGINS_LIBS += $(shell pkg-config --libs cairo libpng12 fftw3f x11 xft zlib)
endif

ifeq ($(HAVE_ZYN_DEPS),true)


+ 4
- 0
source/backend/Makefile View File

@@ -52,6 +52,10 @@ STANDALONE_LIBS += $(MODULEDIR)/rtaudio.a
STANDALONE_LIBS += $(MODULEDIR)/rtmidi.a
endif

ifeq ($(HAVE_ZYN_DEPS),true)
STANDALONE_LIBS += $(MODULEDIR)/rtosc.a
endif

UTILS_LIBS = $(MODULEDIR)/juce_audio_basics.a
UTILS_LIBS += $(MODULEDIR)/juce_audio_formats.a
UTILS_LIBS += $(MODULEDIR)/juce_core.a


+ 4
- 0
source/bridges-plugin/Makefile View File

@@ -114,6 +114,10 @@ LINK_FLAGS += $(JUCE_GUI_EXTRA_LIBS)
endif
endif

ifeq ($(HAVE_ZYN_DEPS),true)
LIBS_native += $(MODULEDIR)/rtosc.a
endif

LINK_FLAGS += $(LIBLO_LIBS)

ifeq ($(HAVE_X11),true)


+ 61
- 0
source/modules/rtosc/Makefile View File

@@ -0,0 +1,61 @@
#!/usr/bin/make -f
# Makefile for rtosc #
# ------------------ #
# Created by falkTX
#

CWD=../..
MODULENAME=rtosc
include ../Makefile.mk

# ----------------------------------------------------------------------------------------------------------------------------

BUILD_CXX_FLAGS += -I.

# ----------------------------------------------------------------------------------------------------------------------------

OBJS = \
$(OBJDIR)/dispatch.c.o \
$(OBJDIR)/rtosc.c.o \
$(OBJDIR)/midimapper.cpp.o \
$(OBJDIR)/miditable.cpp.o \
$(OBJDIR)/ports.cpp.o \
$(OBJDIR)/subtree-serialize.cpp.o \
$(OBJDIR)/thread-link.cpp.o \
$(OBJDIR)/undo-history.cpp.o

# ----------------------------------------------------------------------------------------------------------------------------

all: $(MODULEDIR)/$(MODULENAME).a

# ----------------------------------------------------------------------------------------------------------------------------

clean:
rm -f $(OBJDIR)/*.o $(MODULEDIR)/$(MODULENAME)*.a

debug:
$(MAKE) DEBUG=true

# ----------------------------------------------------------------------------------------------------------------------------

$(MODULEDIR)/$(MODULENAME).a: $(OBJS)
-@mkdir -p $(MODULEDIR)
@echo "Creating $(MODULENAME).a"
@rm -f $@
@$(AR) crs $@ $^

# ----------------------------------------------------------------------------------------------------------------------------

$(OBJDIR)/%.c.o: src/%.c
-@mkdir -p $(OBJDIR)
@echo "Compiling $<"
@$(CC) $< $(BUILD_C_FLAGS) -c -o $@

$(OBJDIR)/%.cpp.o: src/cpp/%.cpp
-@mkdir -p $(OBJDIR)
@echo "Compiling $<"
@$(CXX) $< $(BUILD_CXX_FLAGS) -c -o $@

-include $(OBJS:%.o=%.d)

# ----------------------------------------------------------------------------------------------------------------------------

+ 277
- 0
source/modules/rtosc/miditable.h View File

@@ -0,0 +1,277 @@
/*
* Copyright (c) 2012 Mark McCurry
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/

#include "ports.h"
#include <string.h>
#include <algorithm>
#include <map>
#include <sstream>
#include <deque>
#include <utility>
#include <cassert>

namespace rtosc {
/**
* Module Overview
*
* Actions:
* - Add a mapping {coarse/fine} [nRT]
* - Delete a mapping {coarse/fine} [nRT]
* - Transform mapping value based on passive observation [nRT]
* - Find unused CC numbers [RT]
* - Transform CC into event {coarse/fine} [RT]
*/

class MidiMapperStorage
{
public:
//Almost immutable short vector class
template<class T>
class TinyVector {
int n;
T *t;
public:
TinyVector(void):n(0),t(0){}
TinyVector(int i):n(i),t(new T[i]){}
T&operator[](int i) {assert(i>=0 && i<n);return t[i];}
T operator[](int i) const {assert(i>=0 && i<n);return t[i];}

TinyVector insert(const T &t_)
{TinyVector next(n+1); for(int i=0;i<n; ++i) next.t[i]=t[i]; next.t[n] = t_;return std::move(next);}
TinyVector one_larger(void)
{TinyVector next(n+1); for(int i=0;i<n + 1; ++i) next.t[i]=0; return std::move(next);}
TinyVector sized_clone(void)
{TinyVector next(n); for(int i=0;i<n; ++i) next.t[i]=0; return std::move(next);}
TinyVector clone(void)
{TinyVector next(n); for(int i=0;i<n; ++i) next.t[i]=t[i]; return std::move(next);}
int size(void) const{return n;}
};

typedef std::function<void(const char*)> write_cb;
typedef std::function<void(int16_t,write_cb)> callback_t;
//RT Read Only
TinyVector<std::tuple<int, bool, int>> mapping;//CC->{coarse, val-cb offset}
TinyVector<callback_t> callbacks;
//RT RW
TinyVector<int> values;

bool handleCC(int ID, int val, write_cb write);

//TODO try to change O(n^2) algorithm to O(n)
void cloneValues(const MidiMapperStorage &storage);

MidiMapperStorage *clone(void);
};

struct MidiBijection
{
int mode;//0:linear,1:log
float min;
float max;
int operator()(float x) const;
float operator()(int x) const;
};

#include <cassert>
class MidiMappernRT
{
public:
MidiMappernRT(void);
void map(const char *addr, bool coarse = true);

MidiMapperStorage *generateNewBijection(const Port &port, std::string);

void addNewMapper(int ID, const Port &port, std::string addr);

void addFineMapper(int ID, const Port &port, std::string addr);

void useFreeID(int ID);

void unMap(const char *addr, bool coarse);

void delMapping(int ID, bool coarse, const char *addr);
void replaceMapping(int, bool, const char *);

std::map<std::string, std::string> getMidiMappingStrings(void);

//unclear if this should be be here as a helper or not
std::string getMappedString(std::string addr);

MidiBijection getBijection(std::string s);
void snoop(const char *msg);

void apply_high(int v, int ID);
void apply_low(int v, int ID);
void apply_midi(int val, int ID);

void setBounds(const char *str, float low, float high);

std::tuple<float,float,float,float> getBounds(const char *str);

bool has(std::string addr);
bool hasPending(std::string addr);
bool hasCoarse(std::string addr);
bool hasFine(std::string addr);
bool hasCoarsePending(std::string addr);
bool hasFinePending(std::string addr);
int getCoarse(std::string addr);
int getFine(std::string addr);

//(Location, Coarse, Fine, Bijection)
std::map<std::string, std::tuple<int, int, int, MidiBijection>> inv_map;
std::deque<std::pair<std::string,bool>> learnQueue;
std::function<void(const char *)> rt_cb;
MidiMapperStorage *storage;
const Ports *base_ports;
};

class MidiMapperRT
{
public:
MidiMapperRT(void);
void setBackendCb(std::function<void(const char*)> cb);
void setFrontendCb(std::function<void(const char*)> cb);
void handleCC(int ID, int val);
void addWatch(void);
void remWatch(void);
Port addWatchPort(void);
Port removeWatchPort(void);
Port bindPort(void);

//Fixed upper bounded size set of integer IDs
class PendingQueue
{
public:
PendingQueue()
:pos_r(0), pos_w(0), size(0)
{
for(int i=0; i<32; ++i)
vals[i] = -1;
}
void insert(int x)
{
if(has(x) || size > 31)
return;
vals[pos_w] = x;
size++;
pos_w = (pos_w+1)%32;
}
void pop(void)
{
if(size == 0)
return;
size--;
vals[pos_r] = -1;
pos_r = (1+pos_r)%32;
}
bool has(int x)
{
for(int i=0; i<32; ++i)
if(vals[i] == x)
return true;
return false;
}
int vals[32];
int pos_r;
int pos_w;
int size;

};


/***************
* Member Data *
***************/
PendingQueue pending;
MidiMapperStorage *storage;
unsigned watchSize;
std::function<void(const char*)> backend;
std::function<void(const char*)> frontend;
};

struct MidiAddr
{
//The midi values that map to the specified action
uint8_t ch, ctl;

//The type of the event 'f', 'i', 'T', 'c'
char type;
//The path of the event
char *path;
//The conversion function for 'f' types
const char *conversion;
};


/**
* Table of midi mappings - Deprecated
*
*/
class MidiTable
{
public:

const Ports &dispatch_root;
short unhandled_ch;
short unhandled_ctl;
char *unhandled_path;

void (*error_cb)(const char *, const char *);
void (*event_cb)(const char *);
void (*modify_cb)(const char *, const char *, const char *, int, int);

MidiTable(const Ports &_dispatch_root);
~MidiTable();

bool has(uint8_t ch, uint8_t ctl) const;

MidiAddr *get(uint8_t ch, uint8_t ctl);

const MidiAddr *get(uint8_t ch, uint8_t ctl) const;

bool mash_port(MidiAddr &e, const Port &port);

void addElm(uint8_t ch, uint8_t ctl, const char *path);

void check_learn(void);

void learn(const char *s);

void clear_entry(const char *s);

void process(uint8_t ch, uint8_t ctl, uint8_t val);

Port learnPort(void);
Port unlearnPort(void);
Port registerPort(void);

//TODO generalize to an addScalingFunction() system
static float translate(uint8_t val, const char *meta);

private:
class MidiTable_Impl *impl;
};

};

+ 371
- 0
source/modules/rtosc/port-sugar.h View File

@@ -0,0 +1,371 @@
/*
* Copyright (c) 2013 Mark McCurry
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/

#ifndef RTOSC_PORT_SUGAR
#define RTOSC_PORT_SUGAR

//Hack to workaround old incomplete decltype implementations
#ifdef __GNUC__
#if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ <= 7)
template<typename T>
struct rtosc_hack_decltype_t
{
typedef T type;
};

#define decltype(expr) rtosc_hack_decltype_t<decltype(expr)>::type
#endif
#endif

//General macro utilities
#define STRINGIFY2(a) #a
#define STRINGIFY(a) STRINGIFY2(a)

//Helper for documenting varargs
#define IMPL(_1,_2,_3,_4,_5,_6,_7,_8,_9, N, ...) N
#define LAST_IMP(...) IMPL(__VA_ARGS__,9,8,7,6,5,4,3,2,1,0,0,0,0)
#define DOC_IMP9(a,b,c,d,e,f,g,h,i) a b c d e f g h rDoc(i)
#define DOC_IMP8(a,b,c,d,e,f,g,h) a b c d e f g rDoc(h)
#define DOC_IMP7(a,b,c,d,e,f,g) a b c d e f rDoc(g)
#define DOC_IMP6(a,b,c,d,e,f) a b c d e rDoc(f)
#define DOC_IMP5(a,b,c,d,e) a b c d rDoc(e)
#define DOC_IMP4(a,b,c,d) a b c rDoc(d)
#define DOC_IMP3(a,b,c) a b rDoc(c)
#define DOC_IMP2(a,b) a rDoc(b)
#define DOC_IMP1(a) rDoc(a)
#define DOC_IMP0() YOU_MUST_DOCUMENT_YOUR_PORTS
#define DOC_IMP(count, ...) DOC_IMP ##count(__VA_ARGS__)
#define DOC_I(count, ...) DOC_IMP(count,__VA_ARGS__)
#define DOC(...) DOC_I(LAST_IMP(__VA_ARGS__), __VA_ARGS__)

//XXX Currently unused macro
#define MAC_EACH_0(mac, x, ...) INSUFFICIENT_ARGUMENTS_PROVIDED_TO_MAC_EACH
#define MAC_EACH_1(mac, x, ...) mac(x)
#define MAC_EACH_2(mac, x, ...) mac(x) MAC_EACH_1(mac, __VA_ARGS__)
#define MAC_EACH_3(mac, x, ...) mac(x) MAC_EACH_2(mac, __VA_ARGS__)
#define MAC_EACH_4(mac, x, ...) mac(x) MAC_EACH_3(mac, __VA_ARGS__)
#define MAC_EACH_5(mac, x, ...) mac(x) MAC_EACH_4(mac, __VA_ARGS__)
#define MAC_EACH_6(mac, x, ...) mac(x) MAC_EACH_5(mac, __VA_ARGS__)
#define MAC_EACH_7(mac, x, ...) mac(x) MAC_EACH_6(mac, __VA_ARGS__)
#define MAC_EACH_8(mac, x, ...) mac(x) MAC_EACH_7(mac, __VA_ARGS__)
#define MAC_EACH_9(mac, x, ...) mac(x) MAC_EACH_8(mac, __VA_ARGS__)

#define MAC_EACH_IMP(mac, count, ...) MAC_EACH_ ##count(mac,__VA_ARGS__)
#define MAC_EACH_I(mac, count, ...) MAC_EACH_IMP(mac, count, __VA_ARGS__)
#define MAC_EACH(mac, ...) MAC_EACH_I(mac, LAST_IMP(__VA_ARGS__), __VA_ARGS__)

#define OPTIONS_IMP9(a,b,c,d,e,f,g,h,i) \
rOpt(0,a) rOpt(1,b) rOpt(2,c) rOpt(3,d) rOpt(4,e) rOpt(5,f) rOpt(6,g) \
rOpt(7,h) rOpt(8,i)
#define OPTIONS_IMP8(a,b,c,d,e,f,g,h) \
rOpt(0,a) rOpt(1,b) rOpt(2,c) rOpt(3,d) rOpt(4,e) rOpt(5,f) rOpt(6,g) \
rOpt(7,h)
#define OPTIONS_IMP7(a,b,c,d,e,f,g) \
rOpt(0,a) rOpt(1,b) rOpt(2,c) rOpt(3,d) rOpt(4,e) rOpt(5,f) rOpt(6,g)
#define OPTIONS_IMP6(a,b,c,d,e,f) \
rOpt(0,a) rOpt(1,b) rOpt(2,c) rOpt(3,d) rOpt(4,e) rOpt(5,f)
#define OPTIONS_IMP5(a,b,c,d,e) \
rOpt(0,a) rOpt(1,b) rOpt(2,c) rOpt(3,d) rOpt(4,e)
#define OPTIONS_IMP4(a,b,c,d) \
rOpt(0,a) rOpt(1,b) rOpt(2,c) rOpt(3,d)
#define OPTIONS_IMP3(a,b,c) \
rOpt(0,a) rOpt(1,b) rOpt(2,c)
#define OPTIONS_IMP2(a,b) \
rOpt(0,a) rOpt(1,b)
#define OPTIONS_IMP1(a) \
rOpt(0,a)
#define OPTIONS_IMP0() YOU_MUST_PROVIDE_OPTIONS
#define OPTIONS_IMP(count, ...) OPTIONS_IMP ##count(__VA_ARGS__)
#define OPTIONS_I(count, ...) OPTIONS_IMP(count, __VA_ARGS__)
#define OPTIONS(...) OPTIONS_I(LAST_IMP(__VA_ARGS__), __VA_ARGS__)

//Additional Change Callback (after parameters have been changed)
//This can be used to queue up interpolation or parameter regen
#define rChangeCb

//Normal parameters
#define rParam(name, ...) \
{STRINGIFY(name) "::c", rProp(parameter) rMap(min, 0) rMap(max, 127) DOC(__VA_ARGS__), NULL, rParamCb(name)}
#define rParamF(name, ...) \
{STRINGIFY(name) "::f", rProp(parameter) DOC(__VA_ARGS__), NULL, rParamFCb(name)}
#define rParamI(name, ...) \
{STRINGIFY(name) "::i", rProp(parameter) DOC(__VA_ARGS__), NULL, rParamICb(name)}
#define rToggle(name, ...) \
{STRINGIFY(name) "::T:F",rProp(parameter) DOC(__VA_ARGS__), NULL, rToggleCb(name)}
#define rOption(name, ...) \
{STRINGIFY(name) "::i:c",rProp(parameter) DOC(__VA_ARGS__), NULL, rOptionCb(name)}

//Array operators
#define rArrayF(name, length, ...) \
{STRINGIFY(name) "#" STRINGIFY(length) "::f", rProp(parameter) DOC(__VA_ARGS__), NULL, rArrayFCb(name)}
#define rArray(name, length, ...) \
{STRINGIFY(name) "#" STRINGIFY(length) "::c", rProp(parameter) DOC(__VA_ARGS__), NULL, rArrayCb(name)}
#define rArrayT(name, length, ...) \
{STRINGIFY(name) "#" STRINGIFY(length) "::T:F", rProp(parameter) DOC(__VA_ARGS__), NULL, rArrayTCb(name)}
#define rArrayI(name, length, ...) \
{STRINGIFY(name) "#" STRINGIFY(length) "::i", rProp(parameter) DOC(__VA_ARGS__), NULL, rArrayICb(name)}


//Method callback Actions
#define rAction(name, ...) \
{STRINGIFY(name) ":", DOC(__VA_ARGS__), NULL, rActionCb(name)}
#define rActioni(name, ...) \
{STRINGIFY(name) ":i", DOC(__VA_ARGS__), NULL, rActioniCb(name)}


//Alias operators
#define rParams(name, length, ...) \
rArray(name, length, __VA_ARGS__), \
{STRINGIFY(name) ":", rProp(alias), NULL, rParamsCb(name, length)}


template<class T> constexpr T spice(T*t) {return *t;}

//Recursion [two ports in one for pointer manipulation]
#define rRecur(name, ...) \
{STRINGIFY(name) "/", DOC(__VA_ARGS__), &decltype(rObject::name)::ports, rRecurCb(name)}, \
{STRINGIFY(name) ":", rProp(internal), NULL, rRecurPtrCb(name)}

#define rRecurp(name, ...) \
{STRINGIFY(name) "/", DOC(__VA_ARGS__), \
&decltype(spice(rObject::name))::ports, \
rRecurpCb(name)}

#define rRecurs(name, length, ...) \
{STRINGIFY(name) "#" STRINGIFY(length)"/", DOC(__VA_ARGS__), \
&decltype(spice(&rObject::name[0]))::ports, \
rRecursCb(name, length)}

//Technically this is a pointer pointer method...
#define rRecursp(name, length, ...) \
{STRINGIFY(name)"#" STRINGIFY(length) "/", DOC(__VA_ARGS__), \
&decltype(spice(rObject::name[0]))::ports, \
rRecurspCb(name)}

//{STRINGIFY(name) ":", rProp(internal), NULL, rRecurPtrCb(name)}

//Misc
#define rDummy(name, ...) {STRINIFY(name), rProp(dummy), NULL, [](msg_t, rtosc::RtData &){}}
#define rString(name, len, ...) \
{STRINGIFY(name) "::s", rMap(length, len) DOC(__VA_ARGS__), NULL, rStringCb(name,len)}

//General property operators
#define rMap(name, value) ":" STRINGIFY(name) "\0=" STRINGIFY(value) "\0"
#define rProp(name) ":" STRINGIFY(name) "\0"

//Scaling property
#define rLinear(min_, max_) rMap(min, min_) rMap(max, max_) rMap(scale, linear)
#define rLog(min_, max_) rMap(min, min_) rMap(max, max_) rMap(scale, logarithmic)

//Special values
#define rSpecial(doc) ":special\0" STRINGIFY(doc) "\0"
#define rCentered ":centered\0"

//Misc properties
#define rDoc(doc) ":documentation\0=" doc "\0"
#define rOpt(numeric,symbolic) rMap(map numeric, symbolic)
#define rOptions(...) OPTIONS(__VA_ARGS__)


//Callback Implementations
#define rBOIL_BEGIN [](const char *msg, rtosc::RtData &data) { \
(void) msg; (void) data; \
rObject *obj = (rObject*) data.obj;(void) obj; \
const char *args = rtosc_argument_string(msg); (void) args;\
const char *loc = data.loc; (void) loc;\
auto prop = data.port->meta(); (void) prop;

#define rBOIL_END }

#define rLIMIT(var, convert) \
if(prop["min"] && var < (decltype(var))convert(prop["min"])) \
var = convert(prop["min"]);\
if(prop["max"] && var > (decltype(var))convert(prop["max"])) \
var = convert(prop["max"]);

#define rTYPE(n) decltype(obj->n)

#define rAPPLY(n,t) if(obj->n != var) data.reply("undo_change", "s" #t #t, data.loc, obj->n, var); obj->n = var;

#define rParamCb(name) rBOIL_BEGIN \
if(!strcmp("", args)) {\
data.reply(loc, "c", obj->name); \
} else { \
rTYPE(name) var = rtosc_argument(msg, 0).i; \
rLIMIT(var, atoi) \
rAPPLY(name, c) \
data.broadcast(loc, "c", obj->name);\
rChangeCb \
} rBOIL_END

#define rParamFCb(name) rBOIL_BEGIN \
if(!strcmp("", args)) {\
data.reply(loc, "f", obj->name); \
} else { \
rTYPE(name) var = rtosc_argument(msg, 0).f; \
rLIMIT(var, atof) \
rAPPLY(name, f) \
data.broadcast(loc, "f", obj->name);\
rChangeCb \
} rBOIL_END

#define rParamICb(name) rBOIL_BEGIN \
if(!strcmp("", args)) {\
data.reply(loc, "i", obj->name); \
} else { \
rTYPE(name) var = rtosc_argument(msg, 0).i; \
rLIMIT(var, atoi) \
rAPPLY(name, i) \
data.broadcast(loc, "i", obj->name);\
rChangeCb \
} rBOIL_END

//TODO finish me (include string mapper action?)
#define rOptionCb(name) rBOIL_BEGIN \
if(!strcmp("", args)) {\
data.reply(loc, "i", obj->name); \
} else { \
rTYPE(name) var = rtosc_argument(msg, 0).i; \
rLIMIT(var, atoi) \
rAPPLY(name, i) \
data.broadcast(loc, rtosc_argument_string(msg), obj->name);\
rChangeCb \
} rBOIL_END

#define rToggleCb(name) rBOIL_BEGIN \
if(!strcmp("", args)) {\
data.reply(loc, obj->name ? "T" : "F"); \
} else { \
if(obj->name != rtosc_argument(msg, 0).T) { \
data.broadcast(loc, args);\
rChangeCb \
} \
obj->name = rtosc_argument(msg, 0).T; \
} rBOIL_END

#define SNIP \
while(*msg && *msg!='/') ++msg; \
msg = *msg ? msg+1 : msg;

#define rRecurCb(name) rBOIL_BEGIN \
data.obj = &obj->name; \
SNIP \
decltype(obj->name)::ports.dispatch(msg, data); \
rBOIL_END

#define rRecurPtrCb(name) rBOIL_BEGIN \
void *ptr = &obj->name; \
data.reply(loc, "b", sizeof(void*), &ptr); \
rBOIL_END

#define rRecurpCb(name) rBOIL_BEGIN \
if(obj->name == NULL) return; \
data.obj = obj->name; \
SNIP \
decltype(spice(rObject::name))::ports.dispatch(msg, data); \
rBOIL_END

#define rRecursCb(name, length) rBOILS_BEGIN \
data.obj = &obj->name[idx]; \
SNIP \
decltype(spice(rObject::name))::ports.dispatch(msg, data); \
rBOILS_END

#define rRecurspCb(name) rBOILS_BEGIN \
data.obj = obj->name[idx]; \
SNIP \
decltype(spice(rObject::name[0]))::ports.dispatch(msg, data); \
rBOILS_END

#define rActionCb(name) rBOIL_BEGIN obj->name(); rBOIL_END
#define rActioniCb(name) rBOIL_BEGIN \
obj->name(rtosc_argument(msg,0).i); rBOIL_END

//Array ops

#define rBOILS_BEGIN rBOIL_BEGIN \
const char *mm = msg; \
while(*mm && !isdigit(*mm)) ++mm; \
unsigned idx = atoi(mm);

#define rBOILS_END rBOIL_END


#define rArrayCb(name) rBOILS_BEGIN \
if(!strcmp("", args)) {\
data.reply(loc, "c", obj->name[idx]); \
} else { \
char var = rtosc_argument(msg, 0).i; \
rLIMIT(var, atoi) \
rAPPLY(name[idx], c) \
data.broadcast(loc, "c", obj->name[idx]);\
rChangeCb \
} rBOILS_END

#define rArrayFCb(name) rBOILS_BEGIN \
if(!strcmp("", args)) {\
data.reply(loc, "f", obj->name[idx]); \
} else { \
float var = rtosc_argument(msg, 0).f; \
rLIMIT(var, atof) \
rAPPLY(name[idx], f) \
data.broadcast(loc, "f", obj->name[idx]);\
} rBOILS_END

#define rArrayTCb(name) rBOILS_BEGIN \
if(!strcmp("", args)) {\
data.reply(loc, obj->name[idx] ? "T" : "F"); \
} else { \
if(obj->name[idx] != rtosc_argument(msg, 0).T) { \
data.broadcast(loc, args);\
rChangeCb \
} \
obj->name[idx] = rtosc_argument(msg, 0).T; \
} rBOILS_END

#define rArrayICb(name) rBOILS_BEGIN \
if(!strcmp("", args)) {\
data.reply(loc, "i", obj->name[idx]); \
} else { \
char var = rtosc_argument(msg, 0).i; \
rLIMIT(var, atoi) \
rAPPLY(name[idx], i) \
data.broadcast(loc, "i", obj->name[idx]);\
rChangeCb \
} rBOILS_END

#define rParamsCb(name, length) rBOIL_BEGIN \
data.reply(loc, "b", length, obj->name); rBOIL_END

#define rStringCb(name, length) rBOIL_BEGIN \
if(!strcmp("", args)) {\
data.reply(loc, "s", obj->name); \
} else { \
strncpy(obj->name, rtosc_argument(msg, 0).s, length); \
data.broadcast(loc, "s", obj->name);\
rChangeCb \
} rBOIL_END


#endif

+ 219
- 0
source/modules/rtosc/ports.h View File

@@ -0,0 +1,219 @@
/*
* Copyright (c) 2012 Mark McCurry
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/

#ifndef RTOSC_PORTS
#define RTOSC_PORTS

#include <vector>
#include <functional>
#include <initializer_list>
#include "rtosc.h"
#include <cstring>
#include <cctype>
#include <cstdlib>
#include <cstdio>
#include <string>

namespace rtosc {

//First define all types
typedef const char *msg_t;

struct Port;
struct Ports;

struct RtData
{
RtData(void);

char *loc;
size_t loc_size;
void *obj;
int matches;
const Port *port;

virtual void reply(const char *path, const char *args, ...);
virtual void reply(const char *msg);
virtual void broadcast(const char *path, const char *args, ...);
virtual void broadcast(const char *msg);
};


/**
* Port in rtosc dispatching hierarchy
*/
struct Port {
const char *name; //< Pattern for messages to match
const char *metadata;//< Statically accessable data about port
const Ports *ports; //< Pointer to further ports
std::function<void(msg_t, RtData&)> cb;//< Callback for matching functions

class MetaIterator
{
public:
MetaIterator(const char *str);

//A bit odd to return yourself, but it seems to work for this
//context
const MetaIterator& operator*(void) const {return *this;}
const MetaIterator* operator->(void) const {return this;}
bool operator==(MetaIterator a) {return title == a.title;}
bool operator!=(MetaIterator a) {return title != a.title;}
MetaIterator& operator++(void);

const char *title;
const char *value;
};

class MetaContainer
{
public:
MetaContainer(const char *str_);

MetaIterator begin(void) const;
MetaIterator end(void) const;

MetaIterator find(const char *str) const;
size_t length(void) const;
const char *operator[](const char *str) const;

const char *str_ptr;
};

MetaContainer meta(void) const
{
if(metadata && *metadata == ':')
return MetaContainer(metadata+1);
else
return MetaContainer(metadata);
}
};

/**
* Ports - a dispatchable collection of Port entries
*
* This structure makes it somewhat easier to perform actions on collections of
* port entries and it is responsible for the dispatching of OSC messages to
* their respective ports.
* That said, it is a very simple structure, which uses a stl container to store
* all data in a simple dispatch table.
* All methods post-initialization are RT safe (assuming callbacks are RT safe)
*/
struct Ports
{
std::vector<Port> ports;


typedef std::vector<Port>::const_iterator itr_t;

/**Forwards to builtin container*/
itr_t begin() const {return ports.begin();}

/**Forwards to builtin container*/
itr_t end() const {return ports.end();}

/**Forwards to builtin container*/
size_t size() const {return ports.size();}

/**Forwards to builtin container*/
const Port &operator[](unsigned i) const {return ports[i];}

Ports(std::initializer_list<Port> l);
~Ports(void);

Ports(const Ports&) = delete;

/**
* Dispatches message to all matching ports.
* This uses simple pattern matching available in rtosc::match.
*
* @param m a valid OSC message
* @param d The RtData object shall contain a path buffer (or null), the length of
* the buffer, a pointer to data.
*/
void dispatch(const char *m, RtData &d) const;

/**
* Retrieve local port by name
* TODO implement full matching
*/
const Port *operator[](const char *name) const;


/**
* Find the best match for a given path
*
* @parameter path partial OSC path
* @returns first path prefixed by the argument
*
* Example usage:
* @code
* Ports p = {{"foo",0,0,dummy_method},
* {"flam",0,0,dummy_method},
* {"bar",0,0,dummy_method}};
* p.apropos("/b")->name;//bar
* p.apropos("/f")->name;//foo
* p.apropos("/fl")->name;//flam
* p.apropos("/gg");//NULL
* @endcode
*/
const Port *apropos(const char *path) const;

private:
//Performance hacks
class Port_Matcher *impl;
unsigned elms;
};


/*********************
* Port walking code *
*********************/
//typedef std::function<void(const Port*,const char*)> port_walker_t;
typedef void(*port_walker_t)(const Port*,const char*,void*);

void walk_ports(const Ports *base,
char *name_buffer,
size_t buffer_size,
void *data,
port_walker_t walker);

/*********************
* Port Dumping code *
*********************/

struct OscDocFormatter
{
const Ports *p;
std::string prog_name;
std::string uri;
std::string doc_origin;
std::string author_first;
std::string author_last;
//TODO extend this some more
};

std::ostream &operator<<(std::ostream &o, OscDocFormatter &formatter);
};
#endif

+ 238
- 0
source/modules/rtosc/rtosc.h View File

@@ -0,0 +1,238 @@
/*
* Copyright (c) 2012 Mark McCurry
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
* @file rtosc.h
*/
#ifndef RTOSC_H
#define RTOSC_H
#include <stdarg.h>
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>

#ifdef __cplusplus
extern "C" {
#endif

typedef struct {
int32_t len;
uint8_t *data;
} rtosc_blob_t;

typedef union {
int32_t i; //i,c,r
char T; //I,T,F,N
float f; //f
double d; //d
int64_t h; //h
uint64_t t; //t
uint8_t m[4];//m
const char *s; //s,S
rtosc_blob_t b; //b
} rtosc_arg_t;

/**
* Write OSC message to fixed length buffer
*
* On error, buffer will be zeroed.
* When buffer is NULL, the function returns the size of the buffer required to
* store the message
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* //Example messages
* char buffer[128];
* rtosc_message(buffer,128,"/path","TFI");
* rtosc_message(buffer,128,"/path","s","foobar");
* rtosc_message(buffer,128,"/path","i",128);
* rtosc_message(buffer,128,"/path","f",128.0);
* const char blob[4] = {'a','b','c','d'};
* rtosc_message(buffer,128,"/path","b",4,blob);
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* @param buffer Memory to write to
* @param len Length of buffer
* @param address OSC pattern to send message to
* @param arguments String consisting of the types of the following arguments
* @param ... OSC arguments to pass forward
* @returns length of resulting message or zero if bounds exceeded
*/
size_t rtosc_message(char *buffer,
size_t len,
const char *address,
const char *arguments,
...);

/**
* @see rtosc_message()
*/
size_t rtosc_vmessage(char *buffer,
size_t len,
const char *address,
const char *arguments,
va_list va);

/**
* @see rtosc_message()
*/
size_t rtosc_amessage(char *buffer,
size_t len,
const char *address,
const char *arguments,
const rtosc_arg_t *args);

/**
* Returns the number of arguments found in a given message
*
* @param msg well formed OSC message
* @returns number of arguments in message
*/
unsigned rtosc_narguments(const char *msg);

/**
* @param msg well formed OSC message
* @param i index of argument
* @returns the type of the ith argument in msg
*/
char rtosc_type(const char *msg, unsigned i);

/**
* Blob data may be safely written to
* @param msg OSC message
* @param i index of argument
* @returns an argument by value via the rtosc_arg_t union
*/
rtosc_arg_t rtosc_argument(const char *msg, unsigned i);

/**
* @param msg OSC message
* @param len Message length upper bound
* @returns the size of a message given a chunk of memory.
*/
size_t rtosc_message_length(const char *msg, size_t len);

typedef struct {
char *data;
size_t len;
} ring_t;

/**
* Finds the length of the next message inside a ringbuffer structure.
*
* @param ring The addresses and lengths of the split buffer, in a compatible
* format to jack's ringbuffer
* @returns size of message stored in ring datastructure
*/
size_t rtosc_message_ring_length(ring_t *ring);


/**
* Validate if an arbitrary byte sequence is an OSC message.
* @param msg pointer to memory buffer
* @param len length of buffer
*/
bool rtosc_valid_message_p(const char *msg, size_t len);

/**
* @param OSC message
* @returns the argument string of a given message
*/
const char *rtosc_argument_string(const char *msg);

/**
* Generate a bundle from sub-messages
*
* @param buffer Destination buffer
* @param len Length of buffer
* @param tt OSC time tag
* @param elms Number of sub messages
* @param ... Messages
* @returns legnth of generated bundle or zero on failure
*/
size_t rtosc_bundle(char *buffer, size_t len, uint64_t tt, int elms, ...);

/**
* Find the elements in a bundle
*
* @param msg OSC bundle
* @param len Upper bound on the length of the bundle
* @returns The number of messages contained within the bundle
*/
size_t rtosc_bundle_elements(const char *msg, size_t len);

/**
* Fetch a message within the bundle
*
* @param msg OSC bundle
* @param i index of sub-message
* @returns The ith message within the bundle
*/
const char *rtosc_bundle_fetch(const char *msg, unsigned i);

/**
* Get the size of a particular bundle element
*
* @param msg OSC bundle
* @param i Index of sub-message
* @returns The size of the ith sub-message in bytes
*/
size_t rtosc_bundle_size(const char *msg, unsigned i);

/**
* Test if the buffer contains a bundle
*
* @param msg OSC message
* @returns true if message is a bundle
*/
int rtosc_bundle_p(const char *msg);

/**
* @returns Time Tag for a bundle
*/
uint64_t rtosc_bundle_timetag(const char *msg);


/**
* This is a non-compliant pattern matcher for dispatching OSC messages
*
* Overall the pattern specification is
* (normal-path)(\#digit-specifier)?(/)?(:argument-restrictor)*
*
* @param pattern The pattern string stored in the Port
* @param msg The OSC message to be matched
* @returns true if a normal match and false if unmatched
*/
bool rtosc_match(const char *pattern, const char *msg);


/**
* Attempt to match a rtosc style path while ignoring arguments
*
* @param pattern rtosc pattern
* @param msg a normal C string or a rtosc message
*/
const char *rtosc_match_path(const char *pattern, const char *msg);

#ifdef __cplusplus
};
#endif
#endif

+ 533
- 0
source/modules/rtosc/src/cpp/midimapper.cpp View File

@@ -0,0 +1,533 @@

#include "ports.h"
#include <cstring>
#include <algorithm>
#include <map>
#include <sstream>
#include <deque>
#include <utility>

#include <cassert>
#include "miditable.h"

using namespace rtosc;
using std::string;
using std::get;
using std::tuple;
using std::make_tuple;

/********************
* Helper Templates *
********************/

template<class T, class U>
bool has_t(const T &t, const U&u)
{return t.find(u) != t.end();}

template<class T, class U>
bool has2(const T &t, const U&u) {
for(const U&uu:t)
if(uu==u)
return true;
return false;
}
template<class T,class U>
int getInd(const T&t,const U&u){
int i=0;
for(const U&uu:t) {
if(uu==u)
return i;
else
i++;
}
return -1;
}

/***********
* Storage *
***********/

bool MidiMapperStorage::handleCC(int ID, int val, write_cb write)
{
for(int i=0; i<mapping.size(); ++i)
{
if(std::get<0>(mapping[i]) == ID)
{
bool coarse = std::get<1>(mapping[i]);
int ind = std::get<2>(mapping[i]);
if(coarse)
values[ind] = (val<<7)|(values[ind]&0x7f);
else
values[ind] = val|(values[ind]&0x3f80);
callbacks[ind](values[ind],write);
return true;
}
}
return false;
}

//TODO try to change O(n^2) algorithm to O(n)
void MidiMapperStorage::cloneValues(const MidiMapperStorage &storage)
{
//XXX this method is SUPER error prone
for(int i=0; i<values.size(); ++i)
values[i] = 0;

for(int i=0; i<mapping.size(); ++i) {
for(int j=0; j<storage.mapping.size(); ++j) {
if(std::get<0>(mapping[i]) == std::get<0>(storage.mapping[j])) {
bool coarse_src = std::get<1>(storage.mapping[j]);
int ind_src = std::get<2>(storage.mapping[j]);

bool coarse_dest = std::get<1>(mapping[i]);
int ind_dest = std::get<2>(mapping[i]);

int val = 0;
//Extract
if(coarse_src)
val = storage.values[ind_src]>>7;
else
val = storage.values[ind_src]&0x7f;

//Blit
if(coarse_dest)
values[ind_dest] = (val<<7)|(values[ind_dest]&0x7f);
else
values[ind_dest] = val|(values[ind_dest]&0x3f80);
}
}
}
}

MidiMapperStorage *MidiMapperStorage::clone(void)
{
MidiMapperStorage *nstorage = new MidiMapperStorage();
nstorage->values = values.sized_clone();
nstorage->mapping = mapping.clone();
nstorage->callbacks = callbacks.clone();
return nstorage;
}

int MidiBijection::operator()(float x) const {
if(mode == 0)
return ((x-min)/(max-min))*(1<<14);
else
return 0;
}

float MidiBijection::operator()(int x) const {
if(mode == 0)
return x/((1<<14)*1.0)*(max-min)+min;
else
return 0;
}


/************************
* Non realtime portion *
************************/
MidiMappernRT::MidiMappernRT(void)
:storage(0),base_ports(0)
{}

void MidiMappernRT::map(const char *addr, bool coarse)
{
for(auto x:learnQueue)
if(x.first == addr && x.second == coarse)
return;

unMap(addr, coarse);
learnQueue.push_back(std::make_pair(addr,coarse));
char buf[1024];
rtosc_message(buf, 1024, "/midi-add-watch","");
rt_cb(buf);
}
MidiMapperStorage *MidiMappernRT::generateNewBijection(const Port &port, std::string addr)
{
MidiBijection bi;
bi.mode = 0;
bi.min = atof(port.meta()["min"]);
bi.max = atof(port.meta()["max"]);
auto tmp = [bi,addr](int16_t x, MidiMapperStorage::write_cb cb) {
float out = bi(x);
char buf[1024];
rtosc_message(buf, 1024, addr.c_str(), "f", out);
cb(buf);
};

MidiMapperStorage *nstorage = new MidiMapperStorage();
if(storage) {
//XXX not quite
nstorage->values = storage->values.one_larger();
nstorage->mapping = storage->mapping.clone();//insert(std::make_tuple(ID, true, storage->callbacks.size()));
nstorage->callbacks = storage->callbacks.insert(tmp);
} else {
nstorage->values = nstorage->values.insert(0);
nstorage->mapping = nstorage->mapping.clone();//insert(std::make_tuple(ID, true, 0));
nstorage->callbacks = nstorage->callbacks.insert(tmp);
}
inv_map[addr] = std::make_tuple(nstorage->callbacks.size()-1, -1,-1,bi);
return nstorage;
}

void MidiMappernRT::addNewMapper(int ID, const Port &port, std::string addr)
{
MidiBijection bi;
bi.mode = 0;
bi.min = atof(port.meta()["min"]);
bi.max = atof(port.meta()["max"]);
auto tmp = [bi,addr](int16_t x, MidiMapperStorage::write_cb cb) {
float out = bi(x);
char buf[1024];
rtosc_message(buf, 1024, addr.c_str(), "f", out);
cb(buf);
};

MidiMapperStorage *nstorage = new MidiMapperStorage();
if(storage) {
//XXX not quite
nstorage->values = storage->values.one_larger();
nstorage->mapping = storage->mapping.insert(std::make_tuple(ID, true, storage->callbacks.size()));
nstorage->callbacks = storage->callbacks.insert(tmp);
} else {
nstorage->values = nstorage->values.insert(0);
nstorage->mapping = nstorage->mapping.insert(std::make_tuple(ID, true, 0));
nstorage->callbacks = nstorage->callbacks.insert(tmp);
}
storage = nstorage;
inv_map[addr] = std::make_tuple(storage->callbacks.size()-1, ID,-1,bi);
}

void MidiMappernRT::addFineMapper(int ID, const Port &port, std::string addr)
{
(void) port;
//TODO asserts
//Bijection already created
//Coarse node already active
//Value already allocated

//Find mapping
int mapped_ID = std::get<0>(inv_map[addr]);
std::get<2>(inv_map[addr]) = ID;
MidiMapperStorage *nstorage = new MidiMapperStorage();
nstorage->values = storage->values.sized_clone();
nstorage->mapping = storage->mapping.insert(std::make_tuple(ID, false, mapped_ID));
nstorage->callbacks = storage->callbacks.insert(storage->callbacks[mapped_ID]);
storage = nstorage;
}

void killMap(int ID, MidiMapperStorage &m)
{
MidiMapperStorage::TinyVector<tuple<int, bool, int>> nmapping(m.mapping.size()-1);
int j=0;
for(int i=0; i<m.mapping.size(); i++)
if(get<0>(m.mapping[i]) != ID)
nmapping[j++] = m.mapping[i];
assert(j == nmapping.size());
m.mapping = nmapping;
}

void MidiMappernRT::useFreeID(int ID)
{
if(learnQueue.empty())
return;
std::string addr = std::get<0>(learnQueue.front());
bool coarse = std::get<1>(learnQueue.front());
learnQueue.pop_front();
assert(base_ports);
const rtosc::Port *p = base_ports->apropos(addr.c_str());
assert(p);

MidiMapperStorage *nstorage;
if(inv_map.find(addr) == inv_map.end())
nstorage = generateNewBijection(*p, addr);
else
nstorage = storage->clone();

auto imap = inv_map[addr];
int mapped_ID = std::get<0>(imap);
nstorage->mapping = nstorage->mapping.insert(make_tuple(ID, coarse, mapped_ID));

if(coarse) {
if(get<1>(imap) != -1)
killMap(get<1>(imap), *nstorage);
inv_map[addr] = make_tuple(get<0>(imap), ID, get<2>(imap), get<3>(imap));
} else {
if(get<2>(imap) != -1)
killMap(get<1>(imap), *nstorage);
inv_map[addr] = make_tuple(get<0>(imap), get<1>(imap), ID, get<3>(imap));
}
storage = nstorage;

//TODO clean up unused value and callback objects

char buf[1024];
rtosc_message(buf, 1024, "/midi-bind", "b", sizeof(storage), &storage);
rt_cb(buf);
};

void MidiMappernRT::unMap(const char *addr, bool coarse)
{
printf("Unmapping('%s',%d)\n",addr,coarse);
if(inv_map.find(addr) == inv_map.end())
return;
auto imap = inv_map[addr];

int kill_id = -1;
if(coarse) {
kill_id = get<1>(imap);
inv_map[addr] = make_tuple(get<0>(imap), -1, get<2>(imap), get<3>(imap));
} else {
kill_id = get<2>(imap);
inv_map[addr] = make_tuple(get<0>(imap), get<1>(imap), -1, get<3>(imap));
}

if(kill_id == -1)
return;



MidiMapperStorage *nstorage = storage->clone();
killMap(kill_id, *nstorage);
storage = nstorage;

//TODO clean up unused value and callback objects

char buf[1024];
rtosc_message(buf, 1024, "/midi-bind", "b", sizeof(storage), &storage);
rt_cb(buf);
}

void MidiMappernRT::delMapping(int ID, bool coarse, const char *addr){
(void) ID;
(void) coarse;
(void) addr;
};
void MidiMappernRT::replaceMapping(int, bool, const char *){};



std::map<std::string, std::string> MidiMappernRT::getMidiMappingStrings(void)
{
std::map<std::string, std::string> result;
for(auto s:inv_map)
result[s.first] = getMappedString(s.first);
char ID = 'A';
for(auto s:learnQueue)
{
if(s.second == false)
result[s.first] += std::string(":")+ID++;
else
result[s.first] = ID++;
}
return result;
}

//unclear if this should be be here as a helper or not
std::string MidiMappernRT::getMappedString(std::string addr)
{
std::stringstream out;
//find coarse
if(has_t(inv_map,addr)) {
if(std::get<1>(inv_map[addr]) != -1)
out << std::get<1>(inv_map[addr]);
}else if(has2(learnQueue, make_pair(addr,true)))
out << getInd(learnQueue,std::make_pair(addr,true));
//find Fine
if(has_t(inv_map,addr)) {
if(std::get<2>(inv_map[addr]) != -1)
out << ":" << std::get<2>(inv_map[addr]);
} else if(has2(learnQueue, make_pair(addr,false)))
out << getInd(learnQueue,std::make_pair(addr,false));

return out.str();
}

MidiBijection MidiMappernRT::getBijection(std::string s)
{
return std::get<3>(inv_map[s]);
}

void MidiMappernRT::snoop(const char *msg)
{
if(inv_map.find(msg) != inv_map.end())
{
auto apple = inv_map[msg];
MidiBijection bi = getBijection(msg);
float value = 0;
std::string args = rtosc_argument_string(msg);
if(args == "f")
value = rtosc_argument(msg, 0).f;
else if(args == "i")
value = rtosc_argument(msg, 0).i;
else if(args == "T")
value = 1.0;
else if(args == "F")
value = 0.0;
else
return;

int new_midi = bi(value);
//printf("--------------------------------------------\n");
//printf("msg = '%s'\n", msg);
//printf("--------------------------------------------\n");
//printf("new midi value: %f->'%x'\n", value, new_midi);
if(std::get<1>(apple) != -1)
apply_high(new_midi,std::get<1>(apple));
if(std::get<2>(apple) != -1)
apply_low(new_midi,std::get<2>(apple));
}
};

void MidiMappernRT::apply_high(int v, int ID) { apply_midi(v>>7,ID); }
void MidiMappernRT::apply_low(int v, int ID) { apply_midi(0x7f&v,ID);}
void MidiMappernRT::apply_midi(int val, int ID)
{
char buf[1024];
rtosc_message(buf,1024,"/virtual_midi_cc","ii",val,ID);
rt_cb(buf);
}

void MidiMappernRT::setBounds(const char *str, float low, float high)
{
if(inv_map.find(str) == inv_map.end())
return;
string addr = str;
auto imap = inv_map[str];
auto newBi = MidiBijection{0,low,high};
inv_map[str] = make_tuple(get<0>(imap),get<1>(imap),get<2>(imap),newBi);
MidiMapperStorage *nstorage = storage->clone();

nstorage->callbacks[get<0>(imap)] = [newBi,addr](int16_t x, MidiMapperStorage::write_cb cb) {
float out = newBi(x);
char buf[1024];
rtosc_message(buf, 1024, addr.c_str(), "f", out);
cb(buf);
};

storage = nstorage;
char buf[1024];
rtosc_message(buf, 1024, "/midi-bind", "b", sizeof(storage), &storage);
rt_cb(buf);
}

std::tuple<float,float,float,float> MidiMappernRT::getBounds(const char *str)
{
const rtosc::Port *p = base_ports->apropos(str);
assert(p);
float min_val = atof(p->meta()["min"]);
float max_val = atof(p->meta()["max"]);
if(inv_map.find(str) != inv_map.end()) {
auto elm = std::get<3>(inv_map[str]);
return std::make_tuple(min_val, max_val,elm.min,elm.max);
}
return std::make_tuple(min_val, max_val,-1.0f,-1.0f);
}

bool MidiMappernRT::has(std::string addr)
{
return inv_map.find(addr) != inv_map.end();
}

bool MidiMappernRT::hasPending(std::string addr)
{
for(auto s:learnQueue)
if(s.first == addr)
return true;
return false;
}

bool MidiMappernRT::hasCoarse(std::string addr)
{
if(!has(addr))
return false;
auto e = inv_map[addr];
return std::get<1>(e) != -1;
}

bool MidiMappernRT::hasFine(std::string addr)
{
if(!has(addr))
return false;
auto e = inv_map[addr];
return std::get<2>(e) != -1;
}

bool MidiMappernRT::hasCoarsePending(std::string addr)
{
for(auto s:learnQueue)
if(s.first == addr && s.second)
return true;
return false;
}

bool MidiMappernRT::hasFinePending(std::string addr)
{
for(auto s:learnQueue)
if(s.first == addr && !s.second)
return true;
return false;
}

int MidiMappernRT::getCoarse(std::string addr)
{
if(!has(addr))
return -1;
auto e = inv_map[addr];
return std::get<1>(e);
}

int MidiMappernRT::getFine(std::string addr)
{
if(!has(addr))
return -1;
auto e = inv_map[addr];
return std::get<2>(e);
}

/*****************
* Realtime code *
*****************/

MidiMapperRT::MidiMapperRT(void)
:storage(NULL), watchSize(0)
{}
void MidiMapperRT::setBackendCb(std::function<void(const char*)> cb) {backend = cb;}
void MidiMapperRT::setFrontendCb(std::function<void(const char*)> cb) {frontend = cb;}
void MidiMapperRT::handleCC(int ID, int val) {
if((!storage || !storage->handleCC(ID, val, backend)) && !pending.has(ID) && watchSize) {
watchSize--;
pending.insert(ID);
char msg[1024];
rtosc_message(msg, 1024, "/midi-use-CC", "i", ID);
frontend(msg);
}
}
void MidiMapperRT::addWatch(void) {watchSize++;}
void MidiMapperRT::remWatch(void) {if(watchSize) watchSize--;}
Port MidiMapperRT::addWatchPort(void) {
return Port{"midi-add-watch","",0, [this](msg_t, RtData&) {
this->addWatch();
}};
}
Port MidiMapperRT::removeWatchPort(void) {
return Port{"midi-remove-watch","",0, [this](msg_t, RtData&) {
this->remWatch();
}};
}
Port MidiMapperRT::bindPort(void) {
return Port{"midi-bind:b","",0, [this](msg_t msg, RtData&) {
pending.pop();
MidiMapperStorage *nstorage =
*(MidiMapperStorage**)rtosc_argument(msg,0).b.data;
if(storage) {
nstorage->cloneValues(*storage);
storage = nstorage;
} else
storage = nstorage;
//TODO memory deallocation
}};
}

+ 267
- 0
source/modules/rtosc/src/cpp/miditable.cpp View File

@@ -0,0 +1,267 @@
#include <math.h>
#include "miditable.h"

using namespace rtosc;

#define RTOSC_INVALID_MIDI 255
class rtosc::MidiTable_Impl
{
public:
MidiTable_Impl(unsigned len, unsigned elms)
:len(len), elms(elms)
{
table = new MidiAddr[elms];
for(unsigned i=0; i<elms; ++i) {
table[i].ch = RTOSC_INVALID_MIDI;
table[i].ctl = RTOSC_INVALID_MIDI;
table[i].path = new char[len];
table[i].conversion = NULL;
}
//TODO initialize all elms
}
~MidiTable_Impl()
{
for(unsigned i=0; i<elms; ++i) {
delete [] table[i].path;
}
delete [] table;
}

MidiAddr *begin(void) {return table;}
MidiAddr *end(void) {return table + elms;}

unsigned len;
unsigned elms;
MidiAddr *table;
};

//MidiAddr::MidiAddr(void)
// :ch(RTOSC_INVALID_MIDI),ctl(RTOSC_INVALID_MIDI)
//{}

static void black_hole3 (const char *, const char *, const char *, int, int)
{}
static void black_hole2(const char *a, const char *b)
{printf("'%s' and '%s'\n", a,b);}
static void black_hole1(const char *a)
{printf("'%s'\n", a);}

#define MAX_UNHANDLED_PATH 128

MidiTable::MidiTable(const Ports &_dispatch_root)
:dispatch_root(_dispatch_root), unhandled_ch(RTOSC_INVALID_MIDI), unhandled_ctl(RTOSC_INVALID_MIDI),
error_cb(black_hole2), event_cb(black_hole1), modify_cb(black_hole3)
{
impl = new MidiTable_Impl(128,128);
unhandled_path = new char[MAX_UNHANDLED_PATH];
memset(unhandled_path, 0, MAX_UNHANDLED_PATH);
}

MidiTable::~MidiTable()
{
delete impl;
delete [] unhandled_path;
}

bool MidiTable::has(uint8_t ch, uint8_t ctl) const
{
for(auto e: *impl) {
if(e.ch == ch && e.ctl == ctl)
return true;
}
return false;
}

MidiAddr *MidiTable::get(uint8_t ch, uint8_t ctl)
{
for(auto &e: *impl)
if(e.ch==ch && e.ctl == ctl)
return &e;
return NULL;
}

const MidiAddr *MidiTable::get(uint8_t ch, uint8_t ctl) const
{
for(auto &e:*impl)
if(e.ch==ch && e.ctl == ctl)
return &e;
return NULL;
}

bool MidiTable::mash_port(MidiAddr &e, const Port &port)
{
const char *args = strchr(port.name, ':');
if(!args)
return false;

//Consider a path to be typed based upon the argument restrictors
if(strchr(args, 'f')) {
e.type = 'f';
e.conversion = port.metadata;
} else if(strchr(args, 'i'))
e.type = 'i';
else if(strchr(args, 'T'))
e.type = 'T';
else if(strchr(args, 'c'))
e.type = 'c';
else
return false;
return true;
}

void MidiTable::addElm(uint8_t ch, uint8_t ctl, const char *path)
{
const Port *port = dispatch_root.apropos(path);

if(!port || port->ports) {//missing or directory node
error_cb("Bad path", path);
return;
}

if(MidiAddr *e = this->get(ch,ctl)) {
strncpy(e->path,path,impl->len);
if(!mash_port(*e, *port)) {
e->ch = RTOSC_INVALID_MIDI;
e->ctl = RTOSC_INVALID_MIDI;
error_cb("Failed to read metadata", path);
}
modify_cb("REPLACE", path, e->conversion, (int) ch, (int) ctl);
return;
}

for(MidiAddr &e:*impl) {
if(e.ch == RTOSC_INVALID_MIDI) {//free spot
e.ch = ch;
e.ctl = ctl;
strncpy(e.path,path,impl->len);
if(!mash_port(e, *port)) {
e.ch = RTOSC_INVALID_MIDI;
e.ctl = RTOSC_INVALID_MIDI;
error_cb("Failed to read metadata", path);
}
modify_cb("ADD", path, e.conversion, (int) ch, (int) ctl);
return;
}
}
}

void MidiTable::check_learn(void)
{
if(unhandled_ctl == RTOSC_INVALID_MIDI || unhandled_path[0] == '\0')
return;
addElm(unhandled_ch, unhandled_ctl, unhandled_path);
unhandled_ch = unhandled_ctl = RTOSC_INVALID_MIDI;
memset(unhandled_path, 0, MAX_UNHANDLED_PATH);
}

void MidiTable::learn(const char *s)
{
if(strlen(s) > impl->len) {
error_cb("String too long", s);
return;
}
clear_entry(s);
strncpy(unhandled_path, s, MAX_UNHANDLED_PATH);
unhandled_path[MAX_UNHANDLED_PATH-1] = '\0';
check_learn();
}

void MidiTable::clear_entry(const char *s)
{
for(unsigned i=0; i<impl->elms; ++i) {
if(!strcmp(impl->table[i].path, s)) {
//Invalidate
impl->table[i].ch = RTOSC_INVALID_MIDI;
impl->table[i].ctl = RTOSC_INVALID_MIDI;
modify_cb("DEL", s, "", -1, -1);
break;
}
}
}

void MidiTable::process(uint8_t ch, uint8_t ctl, uint8_t val)
{
const MidiAddr *addr = get(ch,ctl);
if(!addr) {
unhandled_ctl = ctl;
unhandled_ch = ch;
check_learn();
return;
}

char buffer[1024];
switch(addr->type)
{
case 'f':
rtosc_message(buffer, 1024, addr->path,
"f", translate(val,addr->conversion));
break;
case 'i':
rtosc_message(buffer, 1024, addr->path,
"i", val);
break;
case 'T':
rtosc_message(buffer, 1024, addr->path,
(val<64 ? "F" : "T"));
break;
case 'c':
rtosc_message(buffer, 1024, addr->path,
"c", val);
}

event_cb(buffer);
}

Port MidiTable::learnPort(void)
{
return Port{"learn:s", "", 0, [this](msg_t m, RtData&){
this->learn(rtosc_argument(m,0).s);
}};

}

Port MidiTable::unlearnPort(void)
{
return Port{"unlearn:s", "", 0, [this](msg_t m, RtData&){
this->clear_entry(rtosc_argument(m,0).s);
}};

}

Port MidiTable::registerPort(void)
{
return Port{"register:iis","", 0, [this](msg_t m,RtData&){
const char *pos = rtosc_argument(m,2).s;
while(*pos) putchar(*pos++);
this->addElm(rtosc_argument(m,0).i,rtosc_argument(m,1).i,rtosc_argument(m,2).s);}};
}

//TODO generalize to an addScalingFunction() system
float MidiTable::translate(uint8_t val, const char *meta_)
{
//Allow for middle value to be set
//TODO consider the centered trait for this op
float x = val!=64.0 ? val/127.0 : 0.5;

Port::MetaContainer meta(meta_);

if(!meta["min"] || !meta["max"] || !meta["scale"]) {
fprintf(stderr, "failed to get properties\n");
return 0.0f;
}

const float min = atof(meta["min"]);
const float max = atof(meta["max"]);
const char *scale = meta["scale"];

if(!strcmp(scale,"linear"))
return x*(max-min)+min;
else if(!strcmp(scale,"logarithmic")) {
const float b = log(min);
const float a = log(max)-b;
return expf(a*x+b);
}

return 0.0f;
}

+ 828
- 0
source/modules/rtosc/src/cpp/ports.cpp View File

@@ -0,0 +1,828 @@
#include "ports.h"
#include <ostream>
#include <cassert>
#include <climits>
#include <cstring>
#include <string>

using namespace rtosc;

static inline void scat(char *dest, const char *src)
{
while(*dest) dest++;
if(*dest) dest++;
while(*src && *src!=':') *dest++ = *src++;
*dest = 0;
}

RtData::RtData(void)
:loc(NULL), loc_size(0), obj(NULL), matches(0)
{}

void RtData::reply(const char *path, const char *args, ...)
{
va_list va;
va_start(va,args);
char buffer[1024];
rtosc_vmessage(buffer,1024,path,args,va);
reply(buffer);
va_end(va);
};
void RtData::reply(const char *msg)
{(void)msg;};
void RtData::broadcast(const char *path, const char *args, ...)
{
va_list va;
va_start(va,args);
char buffer[1024];
rtosc_vmessage(buffer,1024,path,args,va);
broadcast(buffer);
va_end(va);
}
void RtData::broadcast(const char *msg)
{reply(msg);};

void metaiterator_advance(const char *&title, const char *&value)
{
if(!title || !*title) {
value = NULL;
return;
}

//Try to find "\0=" after title string
value = title;
while(*value)
++value;
if(*++value != '=')
value = NULL;
else
value++;
}

Port::MetaIterator::MetaIterator(const char *str)
:title(str), value(NULL)
{
metaiterator_advance(title, value);
}

Port::MetaIterator& Port::MetaIterator::operator++(void)
{
if(!title || !*title) {
title = NULL;
return *this;
}
//search for next parameter start
//aka "\0:" unless "\0\0" is seen
char prev = 0;
while(prev || (*title && *title != ':'))
prev = *title++;

if(!*title)
title = NULL;
else
++title;

metaiterator_advance(title, value);
return *this;
}

Port::MetaContainer::MetaContainer(const char *str_)
:str_ptr(str_)
{}

Port::MetaIterator Port::MetaContainer::begin(void) const
{
if(str_ptr && *str_ptr == ':')
return Port::MetaIterator(str_ptr+1);
else
return Port::MetaIterator(str_ptr);
}

Port::MetaIterator Port::MetaContainer::end(void) const
{
return MetaIterator(NULL);
}

Port::MetaIterator Port::MetaContainer::find(const char *str) const
{
for(const auto x : *this)
if(!strcmp(x.title, str))
return x;
return NULL;
}

size_t Port::MetaContainer::length(void) const
{
if(!str_ptr || !*str_ptr)
return 0;
char prev = 0;
const char *itr = str_ptr;
while(prev || *itr)
prev = *itr++;
return 2+(itr-str_ptr);
}

const char *Port::MetaContainer::operator[](const char *str) const
{
for(const auto x : *this)
if(!strcmp(x.title, str))
return x.value;
return NULL;
}
//Match the arg string or fail
inline bool arg_matcher(const char *pattern, const char *args)
{
//match anything if now arg restriction is present (ie the ':')
if(*pattern++ != ':')
return true;

const char *arg_str = args;
bool arg_match = *pattern || *pattern == *arg_str;

while(*pattern && *pattern != ':')
arg_match &= (*pattern++==*arg_str++);

if(*pattern==':') {
if(arg_match && !*arg_str)
return true;
else
return arg_matcher(pattern, args); //retry
}

return arg_match;
}

inline bool scmp(const char *a, const char *b)
{
while(*a && *a == *b) a++, b++;
return a[0] == b[0];
}

typedef std::vector<std::string> words_t;
typedef std::vector<std::string> svec_t;
typedef std::vector<const char *> cvec_t;
typedef std::vector<int> ivec_t;
typedef std::vector<int> tuple_t;
typedef std::vector<tuple_t> tvec_t;

namespace rtosc{
class Port_Matcher
{
public:
bool *enump;
svec_t fixed;
cvec_t arg_spec;
ivec_t pos;
ivec_t assoc;
ivec_t remap;

bool rtosc_match_args(const char *pattern, const char *msg)
{
//match anything if now arg restriction is present
//(ie the ':')
if(*pattern++ != ':')
return true;

const char *arg_str = rtosc_argument_string(msg);
bool arg_match = *pattern || *pattern == *arg_str;

while(*pattern && *pattern != ':')
arg_match &= (*pattern++==*arg_str++);

if(*pattern==':') {
if(arg_match && !*arg_str)
return true;
else
return rtosc_match_args(pattern, msg); //retry
}

return arg_match;
}

bool hard_match(int i, const char *msg)
{
if(strncmp(msg, fixed[i].c_str(), fixed[i].length()))
return false;
if(arg_spec[i])
return rtosc_match_args(arg_spec[i], msg);
else
return true;
}
};
}


tvec_t do_hash(const words_t &strs, const ivec_t &pos)
{
tvec_t tvec;
for(auto &s:strs) {
tuple_t tuple;
tuple.push_back(s.length());
for(const auto &p:pos)
if(p < (int)s.size())
tuple.push_back(s[p]);
tvec.push_back(std::move(tuple));
}
return tvec;
}

template<class T>
int count_dups(std::vector<T> &t)
{
int dups = 0;
int N = t.size();
bool mark[t.size()];
memset(mark, 0, N);
for(int i=0; i<N; ++i) {
if(mark[i])
continue;
for(int j=i+1; j<N; ++j) {
if(t[i] == t[j]) {
dups++;
mark[j] = true;
}
}
}
return dups;
}

template<class T, class Z>
bool has(T &t, Z&z)
{
for(auto tt:t)
if(tt==z)
return true;
return false;
}

int max(int a, int b) { return a<b?b:a;}

ivec_t find_pos(words_t &strs)
{
ivec_t pos;
int current_dups = strs.size();
int N = 0;
for(auto w:strs)
N = max(N,w.length());

int pos_best = -1;
int pos_best_val = INT_MAX;
while(true)
{
for(int i=0; i<N; ++i) {
ivec_t npos = pos;
if(has(pos, i))
continue;
npos.push_back(i);
auto hashed = do_hash(strs, npos);
int d = count_dups(hashed);
if(d < pos_best_val) {
pos_best_val = d;
pos_best = i;
}
}
if(pos_best_val >= current_dups)
break;
current_dups = pos_best_val;
pos.push_back(pos_best);
}
auto hashed = do_hash(strs, pos);
int d = count_dups(hashed);
//printf("Total Dups: %d\n", d);
if(d != 0)
pos.clear();
return pos;
}

ivec_t do_hash(const words_t &strs, const ivec_t &pos, const ivec_t &assoc)
{
ivec_t ivec;
ivec.reserve(strs.size());
for(auto &s:strs) {
int t = s.length();
for(auto p:pos)
if(p < (int)s.size())
t += assoc[s[p]];
ivec.push_back(t);
}
return ivec;
}

ivec_t find_assoc(const words_t &strs, const ivec_t &pos)
{
ivec_t assoc;
int current_dups = strs.size();
int N = 127;
std::vector<char> useful_chars;
for(auto w:strs)
for(auto c:w)
if(!has(useful_chars, c))
useful_chars.push_back(c);

for(int i=0; i<N; ++i)
assoc.push_back(0);

int assoc_best = -1;
int assoc_best_val = INT_MAX;
for(int k=0; k<4; ++k)
{
for(int i:useful_chars) {
assoc_best_val = INT_MAX;
for(int j=0; j<100; ++j) {
//printf(".");
assoc[i] = j;
auto hashed = do_hash(strs, pos, assoc);
//for(int i=0; i<hashed.size(); ++i)
// printf("%d ", hashed[i]);
//printf("\n");
int d = count_dups(hashed);
//printf("dup %d\n",d);
if(d < assoc_best_val) {
assoc_best_val = d;
assoc_best = j;
}
}
assoc[i] = assoc_best;
}
if(assoc_best_val >= current_dups)
break;
current_dups = assoc_best_val;
}
auto hashed = do_hash(strs, pos, assoc);
//int d = count_dups(hashed);
//printf("Total Dups Assoc: %d\n", d);
return assoc;
}

ivec_t find_remap(words_t &strs, ivec_t &pos, ivec_t &assoc)
{
ivec_t remap;
auto hashed = do_hash(strs, pos, assoc);
//for(int i=0; i<strs.size(); ++i)
// printf("%d) '%s'\n", hashed[i], strs[i].c_str());
int N = 0;
for(auto h:hashed)
N = max(N,h+1);
for(int i=0; i<N; ++i)
remap.push_back(0);
for(int i=0; i<(int)hashed.size(); ++i)
remap[hashed[i]] = i;

return remap;
}

void generate_minimal_hash(std::vector<std::string> str, Port_Matcher &pm)
{
pm.pos = find_pos(str);
if(pm.pos.empty()) {
fprintf(stderr, "rtosc: Failed to generate minimal hash\n");
return;
}
pm.assoc = find_assoc(str, pm.pos);
pm.remap = find_remap(str, pm.pos, pm.assoc);
}

void generate_minimal_hash(Ports &p, Port_Matcher &pm)
{
svec_t keys;
cvec_t args;

bool enump = false;
for(unsigned i=0; i<p.ports.size(); ++i)
if(strchr(p.ports[i].name, '#'))
enump = true;
if(enump)
return;
for(unsigned i=0; i<p.ports.size(); ++i)
{
std::string tmp = p.ports[i].name;
const char *arg = NULL;
int idx = tmp.find(':');
if(idx > 0) {
arg = p.ports[i].name+idx;
tmp = tmp.substr(0,idx);
}
keys.push_back(tmp);
args.push_back(arg);

}
pm.fixed = keys;
pm.arg_spec = args;

generate_minimal_hash(keys, pm);
}

Ports::Ports(std::initializer_list<Port> l)
:ports(l), impl(new Port_Matcher)
{
generate_minimal_hash(*this, *impl);
impl->enump = new bool[ports.size()];
for(int i=0; i<(int)ports.size(); ++i)
impl->enump[i] = strchr(ports[i].name, '#');

elms = ports.size();
}

Ports::~Ports()
{
delete []impl->enump;
delete impl;
}

#if !defined(__GNUC__)
#define __builtin_expect(a,b) a
#endif

void Ports::dispatch(const char *m, rtosc::RtData &d) const
{
void *obj = d.obj;
//simple case
if(!d.loc || !d.loc_size) {
for(const Port &port: ports) {
if(rtosc_match(port.name,m))
d.port = &port, port.cb(m,d), d.obj = obj;
}
} else {

//TODO this function is certainly buggy at the moment, some tests
//are needed to make it clean
//XXX buffer_size is not properly handled yet
if(__builtin_expect(d.loc[0] == 0, 0)) {
memset(d.loc, 0, d.loc_size);
d.loc[0] = '/';
}

char *old_end = d.loc;
while(*old_end) ++old_end;

if(impl->pos.empty()) { //No perfect minimal hash function
for(unsigned i=0; i<elms; ++i) {
const Port &port = ports[i];
if(!rtosc_match(port.name, m))
continue;
if(!port.ports)
d.matches++;

//Append the path
if(strchr(port.name,'#')) {
const char *msg = m;
char *pos = old_end;
while(*msg && *msg != '/')
*pos++ = *msg++;
if(strchr(port.name, '/'))
*pos++ = '/';
*pos = '\0';
} else
scat(d.loc, port.name);

d.port = &port;

//Apply callback
port.cb(m,d), d.obj = obj;

//Remove the rest of the path
char *tmp = old_end;
while(*tmp) *tmp++=0;
}
} else {

//Define string to be hashed
unsigned len=0;
const char *tmp = m;

while(*tmp && *tmp != '/')
tmp++;
if(*tmp == '/')
tmp++;
len = tmp-m;

//Compute the hash
int t = len;
for(auto p:impl->pos)
if(p < (int)len)
t += impl->assoc[m[p]];
if(t >= (int)impl->remap.size())
return;
int port_num = impl->remap[t];

//Verify the chosen port is correct
if(__builtin_expect(impl->hard_match(port_num, m), 1)) {
const Port &port = ports[impl->remap[t]];
if(!port.ports)
d.matches++;

//Append the path
if(impl->enump[port_num]) {
const char *msg = m;
char *pos = old_end;
while(*msg && *msg != '/')
*pos++ = *msg++;
if(strchr(port.name, '/'))
*pos++ = '/';
*pos = '\0';
} else
memcpy(old_end, impl->fixed[port_num].c_str(),
impl->fixed[port_num].length()+1);

d.port = &port;

//Apply callback
port.cb(m,d), d.obj = obj;

//Remove the rest of the path
old_end[0] = '\0';
}
}
}
}

const Port *Ports::operator[](const char *name) const
{
for(const Port &port:ports) {
const char *_needle = name,
*_haystack = port.name;
while(*_needle && *_needle==*_haystack)_needle++,_haystack++;

if(*_needle == 0 && (*_haystack == ':' || *_haystack == '\0')) {
return &port;
}
}
return NULL;
}

static msg_t snip(msg_t m)
{
while(*m && *m != '/') ++m;
return m+1;
}

const Port *Ports::apropos(const char *path) const
{
if(path && path[0] == '/')
++path;

for(const Port &port: ports)
if(strchr(port.name,'/') && rtosc_match_path(port.name,path))
return (strchr(path,'/')[1]==0) ? &port :
port.ports->apropos(snip(path));

//This is the lowest level, now find the best port
for(const Port &port: ports)
if(*path && strstr(port.name, path)==port.name)
return &port;

return NULL;
}

void rtosc::walk_ports(const Ports *base,
char *name_buffer,
size_t buffer_size,
void *data,
port_walker_t walker)
{
assert(name_buffer);
//XXX buffer_size is not properly handled yet
if(name_buffer[0] == 0)
name_buffer[0] = '/';

char *old_end = name_buffer;
while(*old_end) ++old_end;

for(const Port &p: *base) {
if(strchr(p.name, '/')) {//it is another tree
if(strchr(p.name,'#')) {
const char *name = p.name;
char *pos = old_end;
while(*name != '#') *pos++ = *name++;
const unsigned max = atoi(name+1);

for(unsigned i=0; i<max; ++i)
{
sprintf(pos,"%d",i);

//Ensure the result is a path
if(strrchr(name_buffer, '/')[1] != '/')
strcat(name_buffer, "/");

//Recurse
rtosc::walk_ports(p.ports, name_buffer, buffer_size,
data, walker);
}
} else {
//Append the path
scat(name_buffer, p.name);

//Recurse
rtosc::walk_ports(p.ports, name_buffer, buffer_size,
data, walker);
}
} else {
if(strchr(p.name,'#')) {
const char *name = p.name;
char *pos = old_end;
while(*name != '#') *pos++ = *name++;
const unsigned max = atoi(name+1);

for(unsigned i=0; i<max; ++i)
{
sprintf(pos,"%d",i);

//Apply walker function
walker(&p, name_buffer, data);
}
} else {
//Append the path
scat(name_buffer, p.name);

//Apply walker function
walker(&p, name_buffer, data);
}
}

//Remove the rest of the path
char *tmp = old_end;
while(*tmp) *tmp++=0;
}
}

void walk_ports2(const rtosc::Ports *base,
char *name_buffer,
size_t buffer_size,
void *data,
rtosc::port_walker_t walker)
{
assert(name_buffer);
//XXX buffer_size is not properly handled yet
if(name_buffer[0] == 0)
name_buffer[0] = '/';

char *old_end = name_buffer;
while(*old_end) ++old_end;

for(const rtosc::Port &p: *base) {
if(strchr(p.name, '/')) {//it is another tree
if(strchr(p.name,'#')) {
const char *name = p.name;
char *pos = old_end;
while(*name != '#') *pos++ = *name++;
const unsigned max = atoi(name+1);

//for(unsigned i=0; i<max; ++i)
{
sprintf(pos,"[0,%d]",max);

//Ensure the result is a path
if(strrchr(name_buffer, '/')[1] != '/')
strcat(name_buffer, "/");

//Recurse
walk_ports2(p.ports, name_buffer, buffer_size,
data, walker);
}
} else {
//Append the path
scat(name_buffer, p.name);

//Recurse
walk_ports2(p.ports, name_buffer, buffer_size,
data, walker);
}
} else {
if(strchr(p.name,'#')) {
const char *name = p.name;
char *pos = old_end;
while(*name != '#') *pos++ = *name++;
const unsigned max = atoi(name+1);

//for(unsigned i=0; i<max; ++i)
{
sprintf(pos,"[0,%d]",max);

//Apply walker function
walker(&p, name_buffer, data);
}
} else {
//Append the path
scat(name_buffer, p.name);

//Apply walker function
walker(&p, name_buffer, data);
}
}

//Remove the rest of the path
char *tmp = old_end;
while(*tmp) *tmp++=0;
}
}

static void units(std::ostream &o, const char *u)
{
if(!u)
return;
o << " units=\"" << u << "\"";
}

void dump_ports_cb(const rtosc::Port *p, const char *name, void *v)
{
std::ostream &o = *(std::ostream*)v;
auto meta = p->meta();
if(meta.find("parameter") != p->meta().end()) {
char type = 0;
const char *foo = strchr(p->name, ':');
if(strchr(foo, 'f'))
type = 'f';
else if(strchr(foo, 'i'))
type = 'i';
else if(strchr(foo, 'c'))
type = 'c';
else if(strchr(foo, 'T'))
type = 't';
if(!type) {
fprintf(stderr, "rtosc port dumper: Cannot handle '%s'\n", p->name);
return;
}

if(type == 't')
{
o << " <message_in pattern=\"" << name << "\" typetag=\"T\">\n";
o << " <desc>Enable " << p->meta()["documentation"] << "</desc>\n";
o << " <param_T symbol=\"x\"/>\n";
o << " </message_in>\n";
o << " <message_in pattern=\"" << name << "\" typetag=\"F\">\n";
o << " <desc>Disable " << p->meta()["documentation"] << "</desc>\n";
o << " <param_F symbol=\"x\"/>\n";
o << " </message_in>\n";
o << " <message_in pattern=\"" << name << "\" typetag=\"\">\n";
o << " <desc>Get state of " << p->meta()["documentation"] << "</desc>\n";
o << " </message_in>\n";
o << " <message_out pattern=\"" << name << "\" typetag=\"T\">\n";
o << " <desc>Value of " << p->meta()["documentation"] << "</desc>\n";
o << " <param_T symbol=\"x\"/>";
o << " </message_out>\n";
o << " <message_out pattern=\"" << name << "\" typetag=\"F\">\n";
o << " <desc>Value of %s</desc>\n", p->meta()["documentation"];
o << " <param_F symbol=\"x\"/>";
o << " </message_out>\n";
return;
}

o << " <message_in pattern=\"" << name << "\" typetag=\"" << type << "\">\n";
o << " <desc>Set Value of " << p->meta()["documentation"] << "</desc>\n";
if(meta.find("min") != meta.end() && meta.find("max") != meta.end() && type != 'c')
{
o << " <param_" << type << " symbol=\"x\"";
units(o, meta["unit"]);
o << ">\n";
o << " <range_min_max " << (type == 'f' ? "lmin=\"[\" lmax=\"]\"" : "");
o << " min=\"" << meta["min"] << "\" max=\"" << meta["max"] << "\"/>\n";
o << " </param_" << type << ">";
} else {
o << " <param_" << type << " symbol=\"x\"";
units(o, meta["unit"]);
o << "/>\n";
}
o << " </message_in>\n";
o << " <message_in pattern=\"" << name << "\" typetag=\"\">\n";
o << " <desc>Get Value of " << p->meta()["documentation"] << "</desc>\n";
o << " </message_in>\n";
o << " <message_out pattern=\"" << name << "\" typetag=\"" << type << "\">\n";
o << " <desc>Value of " << p->meta()["documentation"] << "</desc>\n";
if(meta.find("min") != meta.end() && meta.find("max") != meta.end() && type != 'c')
{
o << " <param_" << type << " symbol=\"x\"";
units(o, meta["unit"]);
o << ">\n";
o << " <range_min_max " << (type == 'f' ? "lmin=\"[\" lmax=\"]\"" : "");
o << " min=\"" << meta["min"] << "\" max=\"" << meta["max"] << "\"/>\n";
o << " </param_" << type << ">\n";
} else {
o << " <param_" << type << " symbol=\"x\"";
units(o, meta["unit"]);
o << "/>\n";
}
o << " </message_out>\n";
}// else if(meta.find("documentation") != meta.end())
// fprintf(stderr, "Skipping \"%s\"\n", name);
//else
// fprintf(stderr, "Skipping [UNDOCUMENTED] \"%s\"\n", name);
}

std::ostream &rtosc::operator<<(std::ostream &o, rtosc::OscDocFormatter &formatter)
{
o << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
o << "<osc_unit format_version=\"1.0\">\n";
o << " <meta>\n";
o << " <name>" << formatter.prog_name << "</name>\n";
o << " <uri>" << formatter.uri << "</uri>\n";
o << " <doc_origin>" << formatter.doc_origin << "</doc_origin>\n";
o << " <author><firstname>" << formatter.author_first;
o << "</firstname><lastname>" << formatter.author_last << "</lastname></author>\n";
o << " </meta>\n";
char buffer[1024];
memset(buffer, 0, sizeof(buffer));
walk_ports2(formatter.p, buffer, 1024, &o, dump_ports_cb);
o << "</osc_unit>\n";
return o;
}


+ 144
- 0
source/modules/rtosc/src/cpp/subtree-serialize.cpp View File

@@ -0,0 +1,144 @@
#include "subtree-serialize.h"
#include "ports.h"
#include "rtosc.h"
#include <cstring>
#include <cassert>


using namespace rtosc;

/*
* Append another message onto a bundle if the space permits it.
* If insufficient space is available, then zero is returned and the buffer is
* untouched.
*
* If this is useful it may be generalized to rtosc_bundle_append()
*/
static size_t append_bundle(char *dst, const char *src,
size_t max_len, size_t dst_len, size_t src_len)
{
assert(rtosc_message_length(src,src_len) == src_len);

//Handle Edge case alignment
//if(rtosc_bundle_elements(dst, dst_len) == 0)
// dst_len -= 4;


if(max_len < dst_len + src_len + 4 || dst_len == 0 || src_len == 0)
return 0;
*(int32_t*)(dst+dst_len) = (int32_t)src_len;

memcpy(dst+dst_len+4, src, src_len);

return dst_len + src_len + 4;
}

//This object captures the output of any given port by calling it through a no
//argument message
//Assuming that the loc field is set correctly the message stored here will be
//able to be replayed to get an object to a previous state
class VarCapture : public RtData
{
public:
char buf[128];
char location[128];
char msg[128];
const char *dummy;
bool success;

VarCapture(void)
:dummy("/ser\0\0\0\0,\0\0\0")
{
memset(buf, 0, sizeof(buf));
memset(location, 0, sizeof(buf));
this->loc = location;
success = false;
}

const char *capture(const Ports *p, const char *path, void *obj_)
{
this->loc = location;
assert(this->loc == location);
this->obj = obj_;
location[0] = '/';
strcpy(location+1, path);
success = false;
size_t len = rtosc_message(msg, 128, path, "");
(void) len;
assert(len);
assert(!strchr(path, ':'));

p->dispatch(msg, *this);
return success ? buf : NULL;
}

virtual void reply(const char *path, const char *args, ...)
{
assert(!success);
assert(*path);
va_list va;
va_start(va, args);
size_t len = rtosc_vmessage(buf, 128, path, args, va);
(void) len;
assert(len != 0);
success = true;
va_end(va);
}
virtual void broadcast(const char *msg)
{
(void) msg;
}
};

struct subtree_args_t
{
VarCapture v, vv;
size_t len;
char *buffer;
size_t buffer_size;
void *object;
rtosc::Ports *ports;
};

size_t subtree_serialize(char *buffer, size_t buffer_size,
void *object, rtosc::Ports *ports)
{
(void) object;
assert(buffer);
assert(ports);

subtree_args_t args;
args.v.obj = object;
args.len = rtosc_bundle(buffer, buffer_size, 0xdeadbeef0a0b0c0dULL, 0);
args.buffer = buffer;
args.buffer_size = buffer_size;
args.object = object;
args.ports = ports;


//TODO FIXME this is not currently RT safe at the moment
walk_ports(ports, args.v.loc, 128, &args, [](const Port *p, const char *, void *dat) {
if(p->meta().find("internal") != p->meta().end())
return;

subtree_args_t *args = (subtree_args_t*) dat;

const char *buf = args->vv.capture(args->ports, args->v.loc+1, args->object);
if(buf)
args->len = append_bundle(args->buffer, buf, args->buffer_size, args->len,
rtosc_message_length(buf, 128));
});

return args.len;
}

void subtree_deserialize(char *buffer, size_t buffer_size,
void *object, rtosc::Ports *ports, RtData &d)
{
d.obj = object;
//simply replay all objects seen here
for(unsigned i=0; i<rtosc_bundle_elements(buffer, buffer_size); ++i) {
const char *msg = rtosc_bundle_fetch(buffer, i);
ports->dispatch(msg+1, d);
}
}

+ 180
- 0
source/modules/rtosc/src/cpp/thread-link.cpp View File

@@ -0,0 +1,180 @@
#include "thread-link.h"

namespace rtosc {
#ifdef off_t
#undef off_t
#endif
#define off_t signed long


//Ringbuffer internal structure
//XXX possible undefined behavior depending on future semantics of volatile
struct internal_ringbuffer_t {
char *buffer;
volatile off_t write;
volatile off_t read;
size_t size;
};

typedef internal_ringbuffer_t ringbuffer_t;
#define static

static size_t ring_read_size(ringbuffer_t *ring)
{
const size_t w = ring->write;
const size_t r = ring->read;

return (w-r+ring->size) % ring->size;
}
static size_t ring_write_size(ringbuffer_t *ring)
{
//leave one forbidden element
const size_t w = ring->write;
const size_t r = ring->read;
if(r == w)
return ring->size - 1;
return ((r - w + ring->size) % ring->size) - 1;
}
static void ring_write(ringbuffer_t *ring, const char *data, size_t len)
{
assert(ring_write_size(ring) >= len);
const off_t next_write = (ring->write + len)%ring->size;

//discontinuous write
if(next_write < ring->write) {
const size_t w1 = ring->size - ring->write - 1;
const size_t w2 = len - w1;
memcpy(ring->buffer+ring->write, data, w1);
memcpy(ring->buffer, data+w1, w2);
} else { //contiguous
memcpy(ring->buffer+ring->write, data, len);
}
ring->write = next_write;
}
static void ring_read(ringbuffer_t *ring, char *data, size_t len)
{
assert(ring_read_size(ring) >= len);
const off_t next_read = (ring->read + len)%ring->size;

//discontinuous read
if(next_read < ring->read) {
const size_t r1 = ring->size - ring->read - 1;
const size_t r2 = len - r1;
memcpy(data, ring->buffer+ring->read, r1);
memcpy(data+r1, ring->buffer, r2);
} else { //contiguous
memcpy(data, ring->buffer+ring->read, len);
}
ring->read = next_read;
}
static void ring_read_vector(ringbuffer_t *ring, ring_t *r)
{
assert(r);
size_t read_size = ring_read_size(ring);
off_t read = ring->read;
r[0].data = ring->buffer+ring->read;
if(read_size+read > ring->size) { //discontinuous
size_t r2 = (read_size+1+read)%ring->size;
size_t r1 = read_size - r2;
r[0].len = r1;
r[1].data = ring->buffer;
r[1].len = r2;
} else {
r[0].len = read_size;
r[1].data = NULL;
r[1].len = 0;
}
}

ThreadLink::ThreadLink(size_t max_message_length, size_t max_messages)
:MaxMsg(max_message_length),
BufferSize(MaxMsg*max_messages),
write_buffer(new char[MaxMsg]),
read_buffer(new char[MaxMsg]),
ring(new ringbuffer_t)
{
ring->buffer = new char[BufferSize];
ring->size = BufferSize;
ring->read = 0;
ring->write = 0;
memset(write_buffer, 0, MaxMsg);
memset(read_buffer, 0, MaxMsg);
}

ThreadLink::~ThreadLink(void)
{
delete[] ring->buffer;
delete ring;
delete[] write_buffer;
delete[] read_buffer;
}

void ThreadLink::write(const char *dest, const char *args, ...)
{
va_list va;
va_start(va,args);
const size_t len =
rtosc_vmessage(write_buffer,MaxMsg,dest,args,va);
va_end(va);
if(ring_write_size(ring) >= len)
ring_write(ring,write_buffer,len);
}

void ThreadLink::writeArray(const char *dest, const char *args, const rtosc_arg_t *aargs)
{
const size_t len =
rtosc_amessage(write_buffer, MaxMsg, dest, args, aargs);
if(ring_write_size(ring) >= len)
ring_write(ring,write_buffer,len);
}

/**
* Directly write message to ringbuffer
*/
void ThreadLink::raw_write(const char *msg)
{
const size_t len = rtosc_message_length(msg, -1);//assumed valid
if(ring_write_size(ring) >= len)
ring_write(ring,msg,len);
}

/**
* @returns true iff there is another message to be read in the buffer
*/
bool ThreadLink::hasNext(void) const
{
return ring_read_size(ring);
}

/**
* Read a new message from the ringbuffer
*/
msg_t ThreadLink::read(void) {
ring_t r[2];
ring_read_vector(ring,r);
const size_t len =
rtosc_message_ring_length(r);
assert(ring_read_size(ring) >= len);
assert(len <= MaxMsg);
ring_read(ring, read_buffer, len);
return read_buffer;
}

/**
* Peak at last message read without reading another
*/
msg_t ThreadLink::peak(void) const
{
return read_buffer;
}

/**
* Raw write buffer access for more complicated task
*/
char *ThreadLink::buffer(void) {return write_buffer;}
/**
* Access to write buffer length
*/
size_t ThreadLink::buffer_size(void) const {return BufferSize;}

};

+ 174
- 0
source/modules/rtosc/src/cpp/undo-history.cpp View File

@@ -0,0 +1,174 @@
#include <deque>
#include <cstring>
#include <cstdio>
#include <cassert>
#include <ctime>
#include "rtosc.h"
#include "undo-history.h"

using std::pair;
using std::make_pair;

namespace rtosc {
class UndoHistoryImpl
{
public:
UndoHistoryImpl(void)
:max_history_size(20)
{}
std::deque<pair<time_t, const char *>> history;
long history_pos;
unsigned max_history_size;//XXX Expose this via a public API
std::function<void(const char*)> cb;

void rewind(const char *msg);
void replay(const char *msg);
bool mergeEvent(time_t t, const char *msg, char *buf, size_t N);
};

UndoHistory::UndoHistory(void)
{
impl = new UndoHistoryImpl;
impl->history_pos = 0;
}

void UndoHistory::recordEvent(const char *msg)
{
//TODO Properly account for when you have traveled back in time.
//while this could result in another branch of history, the simple method
//would be to kill off any future redos when new history is recorded
if(impl->history.size() != (unsigned) impl->history_pos) {
impl->history.resize(impl->history_pos);
}

size_t len = rtosc_message_length(msg, -1);
char *data = new char[len];
time_t now = time(NULL);
//printf("now = '%ld'\n", now);
if(!impl->mergeEvent(now, msg, data, len)) {
memcpy(data, msg, len);
impl->history.push_back(make_pair(now, data));
impl->history_pos++;
if(impl->history.size() > impl->max_history_size)
{
delete[] impl->history[0].second;
impl->history.pop_front();
impl->history_pos--;
}
}

}

void UndoHistory::showHistory(void) const
{
int i = 0;
for(auto s : impl->history)
printf("#%d type: %s dest: %s arguments: %s\n", i++,
s.second, rtosc_argument(s.second, 0).s, rtosc_argument_string(s.second));
}

static char tmp[256];
void UndoHistoryImpl::rewind(const char *msg)
{
memset(tmp, 0, sizeof(tmp));
printf("rewind('%s')\n", msg);
rtosc_arg_t arg = rtosc_argument(msg,1);
rtosc_amessage(tmp, 256, rtosc_argument(msg,0).s,
rtosc_argument_string(msg)+2,
&arg);
cb(tmp);
}

void UndoHistoryImpl::replay(const char *msg)
{
printf("replay...'%s'\n", msg);
rtosc_arg_t arg = rtosc_argument(msg,2);
printf("replay address: '%s'\n", rtosc_argument(msg, 0).s);
int len = rtosc_amessage(tmp, 256, rtosc_argument(msg,0).s,
rtosc_argument_string(msg)+2,
&arg);
if(len)
cb(tmp);
}

const char *getUndoAddress(const char *msg)
{
return rtosc_argument(msg,0).s;
}

bool UndoHistoryImpl::mergeEvent(time_t now, const char *msg, char *buf, size_t N)
{
if(history_pos == 0)
return false;
for(int i=history_pos-1; i>=0; --i) {
if(difftime(now, history[i].first) > 2)
break;
if(!strcmp(getUndoAddress(msg),
getUndoAddress(history[i].second)))
{
//We can splice events together, merging them into one event
rtosc_arg_t args[3];
args[0] = rtosc_argument(msg, 0);
args[1] = rtosc_argument(history[i].second,1);
args[2] = rtosc_argument(msg, 2);

rtosc_amessage(buf, N, msg, rtosc_argument_string(msg), args);

delete [] history[i].second;
history[i].second = buf;
history[i].first = now;
return true;
}
}
return false;
}



void UndoHistory::seekHistory(int distance)
{
//TODO print out the events that would need to take place to get to the
//final destination
//TODO limit the distance to be to applicable sizes
//ie ones that do not exceed the known history/future
long dest = impl->history_pos + distance;
if(dest < 0)
distance -= dest;
if(dest > (long) impl->history.size())
distance = impl->history.size() - impl->history_pos;
if(!distance)
return;
printf("distance == '%d'\n", distance);
printf("history_pos == '%ld'\n", impl->history_pos);
//TODO account for traveling back in time
if(distance<0)
while(distance++)
impl->rewind(impl->history[--impl->history_pos].second);
else
while(distance--)
impl->replay(impl->history[impl->history_pos++].second);
}

unsigned UndoHistory::getPos(void) const
{
return impl->history_pos;
}

const char *UndoHistory::getHistory(int i) const
{
return impl->history[i].second;
}

size_t UndoHistory::size() const
{
return impl->history.size();
}

void UndoHistory::setCallback(std::function<void(const char*)> cb)
{
impl->cb = cb;
}
};

+ 76
- 0
source/modules/rtosc/src/dispatch.c View File

@@ -0,0 +1,76 @@
#include "rtosc.h"
#include <ctype.h>
#include <stdlib.h>

static bool rtosc_match_number(const char **pattern, const char **msg)
{
//Verify both hold digits
if(!isdigit(**pattern) || !isdigit(**msg))
return false;

//Read in both numeric values
unsigned max = atoi(*pattern);
unsigned val = atoi(*msg);

////Advance pointers
while(isdigit(**pattern))++*pattern;
while(isdigit(**msg))++*msg;

//Match iff msg number is strictly less than pattern
return val < max;
}

const char *rtosc_match_path(const char *pattern, const char *msg)
{
while(1) {
//Check for special characters
if(*pattern == ':' && !*msg)
return pattern;
else if(*pattern == '/' && *msg == '/')
return ++pattern;
else if(*pattern == '#') {
++pattern;
if(!rtosc_match_number(&pattern, &msg))
return NULL;
} else if((*pattern == *msg)) { //verbatim compare
if(*msg)
++pattern, ++msg;
else
return pattern;
} else
return NULL;
}
}

//Match the arg string or fail
static bool rtosc_match_args(const char *pattern, const char *msg)
{
//match anything if now arg restriction is present (ie the ':')
if(*pattern++ != ':')
return true;

const char *arg_str = rtosc_argument_string(msg);
bool arg_match = *pattern || *pattern == *arg_str;

while(*pattern && *pattern != ':')
arg_match &= (*pattern++==*arg_str++);

if(*pattern==':') {
if(arg_match && !*arg_str)
return true;
else
return rtosc_match_args(pattern, msg); //retry
}

return arg_match;
}

bool rtosc_match(const char *pattern, const char *msg)
{
const char *arg_pattern = rtosc_match_path(pattern, msg);
if(!arg_pattern)
return false;
else if(*arg_pattern == ':')
return rtosc_match_args(arg_pattern, msg);
return true;
}

+ 673
- 0
source/modules/rtosc/src/rtosc.c View File

@@ -0,0 +1,673 @@
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include <stdbool.h>
#include <ctype.h>
#include <assert.h>

#include "rtosc.h"

const char *rtosc_argument_string(const char *msg)
{
assert(msg && *msg);
while(*++msg); //skip pattern
while(!*++msg);//skip null
return msg+1; //skip comma
}

unsigned rtosc_narguments(const char *msg)
{
const char *args = rtosc_argument_string(msg);
int nargs = 0;
while(*args++)
nargs += (*args == ']' || *args == '[') ? 0 : 1;
return nargs;
}

static int has_reserved(char type)
{
switch(type)
{
case 'i'://official types
case 's':
case 'b':
case 'f':

case 'h'://unofficial
case 't':
case 'd':
case 'S':
case 'r':
case 'm':
case 'c':
return 1;
case 'T':
case 'F':
case 'N':
case 'I':
case '[':
case ']':
return 0;
}

//Should not happen
return 0;
}

static unsigned nreserved(const char *args)
{
unsigned res = 0;
for(;*args;++args)
res += has_reserved(*args);

return res;
}

char rtosc_type(const char *msg, unsigned nargument)
{
assert(nargument < rtosc_narguments(msg));
const char *arg = rtosc_argument_string(msg);
while(1) {
if(*arg == '[' || *arg == ']')
++arg;
else if(!nargument || !*arg)
return *arg;
else
++arg, --nargument;
}
}

static unsigned arg_off(const char *msg, unsigned idx)
{
if(!has_reserved(rtosc_type(msg,idx)))
return 0;

//Iterate to the right position
const uint8_t *args = (const uint8_t*) rtosc_argument_string(msg);
const uint8_t *aligned_ptr = args-1;
const uint8_t *arg_pos = args;

while(*++arg_pos);
//Alignment
arg_pos += 4-(arg_pos-((uint8_t*)aligned_ptr))%4;

//ignore any leading '[' or ']'
while(*args == '[' || *args == ']')
++args;

while(idx--) {
uint32_t bundle_length = 0;
switch(*args++)
{
case 'h':
case 't':
case 'd':
arg_pos +=8;
break;
case 'm':
case 'r':
case 'f':
case 'c':
case 'i':
arg_pos += 4;
break;
case 'S':
case 's':
while(*++arg_pos);
arg_pos += 4-(arg_pos-((uint8_t*)aligned_ptr))%4;
break;
case 'b':
bundle_length |= (*arg_pos++ << 24);
bundle_length |= (*arg_pos++ << 16);
bundle_length |= (*arg_pos++ << 8);
bundle_length |= (*arg_pos++);
if(bundle_length%4)
bundle_length += 4-bundle_length%4;
arg_pos += bundle_length;
break;
case '[':
case ']':
//completely ignore array chars
++idx;
break;
case 'T':
case 'F':
case 'I':
;
}
}
return arg_pos-(uint8_t*)msg;
}

size_t rtosc_message(char *buffer,
size_t len,
const char *address,
const char *arguments,
...)
{
va_list va;
va_start(va, arguments);
size_t result = rtosc_vmessage(buffer, len, address, arguments, va);
va_end(va);
return result;
}

//Calculate the size of the message without writing to a buffer
static size_t vsosc_null(const char *address,
const char *arguments,
const rtosc_arg_t *args)
{
unsigned pos = 0;
pos += strlen(address);
pos += 4-pos%4;//get 32 bit alignment
pos += 1+strlen(arguments);
pos += 4-pos%4;

unsigned toparse = nreserved(arguments);
unsigned arg_pos = 0;

//Take care of varargs
while(toparse)
{
char arg = *arguments++;
assert(arg);
int i;
const char *s;
switch(arg) {
case 'h':
case 't':
case 'd':
++arg_pos;
pos += 8;
--toparse;
break;
case 'm':
case 'r':
case 'c':
case 'f':
case 'i':
++arg_pos;
pos += 4;
--toparse;
break;
case 's':
case 'S':
s = args[arg_pos++].s;
assert(s && "Input strings CANNOT be NULL");
pos += strlen(s);
pos += 4-pos%4;
--toparse;
break;
case 'b':
i = args[arg_pos++].b.len;
pos += 4 + i;
if(pos%4)
pos += 4-pos%4;
--toparse;
break;
default:
;
}
}

return pos;
}
size_t rtosc_vmessage(char *buffer,
size_t len,
const char *address,
const char *arguments,
va_list ap)
{
const unsigned nargs = nreserved(arguments);
if(!nargs)
return rtosc_amessage(buffer,len,address,arguments,NULL);

rtosc_arg_t args[nargs];

unsigned arg_pos = 0;
const char *arg_str = arguments;
uint8_t *midi_tmp;
while(arg_pos < nargs)
{
switch(*arg_str++) {
case 'h':
case 't':
args[arg_pos++].h = va_arg(ap, int64_t);
break;
case 'd':
args[arg_pos++].d = va_arg(ap, double);
break;
case 'c':
case 'i':
case 'r':
args[arg_pos++].i = va_arg(ap, int);
break;
case 'm':
midi_tmp = va_arg(ap, uint8_t *);
args[arg_pos].m[0] = midi_tmp[0];
args[arg_pos].m[1] = midi_tmp[1];
args[arg_pos].m[2] = midi_tmp[2];
args[arg_pos++].m[3] = midi_tmp[3];
break;
case 'S':
case 's':
args[arg_pos++].s = va_arg(ap, const char *);
break;
case 'b':
args[arg_pos].b.len = va_arg(ap, int);
args[arg_pos].b.data = va_arg(ap, unsigned char *);
arg_pos++;
break;
case 'f':
args[arg_pos++].f = va_arg(ap, double);
break;
default:
;
}
}

return rtosc_amessage(buffer,len,address,arguments,args);
}

size_t rtosc_amessage(char *buffer,
size_t len,
const char *address,
const char *arguments,
const rtosc_arg_t *args)
{
const size_t total_len = vsosc_null(address, arguments, args);

if(!buffer)
return total_len;

//Abort if the message cannot fit
if(total_len>len) {
memset(buffer, 0, len);
return 0;
}

memset(buffer, 0, total_len);

unsigned pos = 0;
while(*address)
buffer[pos++] = *address++;

//get 32 bit alignment
pos += 4-pos%4;

buffer[pos++] = ',';

const char *arg_str = arguments;
while(*arg_str)
buffer[pos++] = *arg_str++;

pos += 4-pos%4;

unsigned toparse = nreserved(arguments);
unsigned arg_pos = 0;
while(toparse)
{
char arg = *arguments++;
assert(arg);
int32_t i;
int64_t d;
const uint8_t *m;
const char *s;
const unsigned char *u;
rtosc_blob_t b;
switch(arg) {
case 'h':
case 't':
case 'd':
d = args[arg_pos++].t;
buffer[pos++] = ((d>>56) & 0xff);
buffer[pos++] = ((d>>48) & 0xff);
buffer[pos++] = ((d>>40) & 0xff);
buffer[pos++] = ((d>>32) & 0xff);
buffer[pos++] = ((d>>24) & 0xff);
buffer[pos++] = ((d>>16) & 0xff);
buffer[pos++] = ((d>>8) & 0xff);
buffer[pos++] = (d & 0xff);
--toparse;
break;
case 'r':
case 'f':
case 'c':
case 'i':
i = args[arg_pos++].i;
buffer[pos++] = ((i>>24) & 0xff);
buffer[pos++] = ((i>>16) & 0xff);
buffer[pos++] = ((i>>8) & 0xff);
buffer[pos++] = (i & 0xff);
--toparse;
break;
case 'm':
//TODO verify ordering of spec
m = args[arg_pos++].m;
buffer[pos++] = m[0];
buffer[pos++] = m[1];
buffer[pos++] = m[2];
buffer[pos++] = m[3];
--toparse;
break;
case 'S':
case 's':
s = args[arg_pos++].s;
while(*s)
buffer[pos++] = *s++;
pos += 4-pos%4;
--toparse;
break;
case 'b':
b = args[arg_pos++].b;
i = b.len;
buffer[pos++] = ((i>>24) & 0xff);
buffer[pos++] = ((i>>16) & 0xff);
buffer[pos++] = ((i>>8) & 0xff);
buffer[pos++] = (i & 0xff);
u = b.data;
if(u) {
while(i--)
buffer[pos++] = *u++;
}
else
pos += i;
if(pos%4)
pos += 4-pos%4;
--toparse;
break;
default:
;
}
}

return pos;
}

rtosc_arg_t rtosc_argument(const char *msg, unsigned idx)
{
rtosc_arg_t result = {0};
char type = rtosc_type(msg, idx);
//trivial case
if(!has_reserved(type)) {
switch(type)
{
case 'T':
result.T = true;
break;
case 'F':
result.T = false;
break;
default:
;
}
} else {
const unsigned char *arg_pos = (const unsigned char*)msg+arg_off(msg,idx);
switch(type)
{
case 'h':
case 't':
case 'd':
result.t |= (((uint64_t)*arg_pos++) << 56);
result.t |= (((uint64_t)*arg_pos++) << 48);
result.t |= (((uint64_t)*arg_pos++) << 40);
result.t |= (((uint64_t)*arg_pos++) << 32);
result.t |= (((uint64_t)*arg_pos++) << 24);
result.t |= (((uint64_t)*arg_pos++) << 16);
result.t |= (((uint64_t)*arg_pos++) << 8);
result.t |= (((uint64_t)*arg_pos++));
break;
case 'r':
case 'f':
case 'c':
case 'i':
result.i |= (*arg_pos++ << 24);
result.i |= (*arg_pos++ << 16);
result.i |= (*arg_pos++ << 8);
result.i |= (*arg_pos++);
break;
case 'm':
result.m[0] = *arg_pos++;
result.m[1] = *arg_pos++;
result.m[2] = *arg_pos++;
result.m[3] = *arg_pos++;
break;
case 'b':
result.b.len |= (*arg_pos++ << 24);
result.b.len |= (*arg_pos++ << 16);
result.b.len |= (*arg_pos++ << 8);
result.b.len |= (*arg_pos++);
result.b.data = (unsigned char *)arg_pos;
break;
case 'S':
case 's':
result.s = (char *)arg_pos;
break;
}
}

return result;
}

static unsigned char deref(unsigned pos, ring_t *ring)
{
return pos<ring[0].len ? ring[0].data[pos] :
((pos-ring[0].len)<ring[1].len ? ring[1].data[pos-ring[0].len] : 0x00);
}

static size_t bundle_ring_length(ring_t *ring)
{
unsigned pos = 8+8;//goto first length field
uint32_t advance = 0;
do {
advance = deref(pos+0, ring) << (8*0) |
deref(pos+1, ring) << (8*1) |
deref(pos+2, ring) << (8*2) |
deref(pos+3, ring) << (8*3);
if(advance)
pos += 4+advance;
} while(advance);

return pos <= (ring[0].len+ring[1].len) ? pos : 0;
}

//Zero means no full message present
size_t rtosc_message_ring_length(ring_t *ring)
{
//Check if the message is a bundle
if(deref(0,ring) == '#' &&
deref(1,ring) == 'b' &&
deref(2,ring) == 'u' &&
deref(3,ring) == 'n' &&
deref(4,ring) == 'd' &&
deref(5,ring) == 'l' &&
deref(6,ring) == 'e' &&
deref(7,ring) == '\0')
return bundle_ring_length(ring);

//Proceed for normal messages
//Consume path
unsigned pos = 0;
while(deref(pos++,ring));
pos--;

//Travel through the null word end [1..4] bytes
for(int i=0; i<4; ++i)
if(deref(++pos, ring))
break;

if(deref(pos, ring) != ',')
return 0;

unsigned aligned_pos = pos;
int arguments = pos+1;
while(deref(++pos,ring));
pos += 4-(pos-aligned_pos)%4;

unsigned toparse = 0;
{
int arg = arguments-1;
while(deref(++arg,ring))
toparse += has_reserved(deref(arg,ring));
}

//Take care of varargs
while(toparse)
{
char arg = deref(arguments++,ring);
assert(arg);
uint32_t i;
switch(arg) {
case 'h':
case 't':
case 'd':
pos += 8;
--toparse;
break;
case 'm':
case 'r':
case 'c':
case 'f':
case 'i':
pos += 4;
--toparse;
break;
case 'S':
case 's':
while(deref(++pos,ring));
pos += 4-(pos-aligned_pos)%4;
--toparse;
break;
case 'b':
i = 0;
i |= (deref(pos++,ring) << 24);
i |= (deref(pos++,ring) << 16);
i |= (deref(pos++,ring) << 8);
i |= (deref(pos++,ring));
pos += i;
if((pos-aligned_pos)%4)
pos += 4-(pos-aligned_pos)%4;
--toparse;
break;
default:
;
}
}


return pos <= (ring[0].len+ring[1].len) ? pos : 0;
}

size_t rtosc_message_length(const char *msg, size_t len)
{
ring_t ring[2] = {{(char*)msg,len},{NULL,0}};
return rtosc_message_ring_length(ring);
}

bool rtosc_valid_message_p(const char *msg, size_t len)
{
//Validate Path Characters (assumes printable characters are sufficient)
if(*msg != '/')
return false;
const char *tmp = msg;
for(unsigned i=0; i<len; ++i) {
if(*tmp == 0)
break;
if(!isprint(*tmp))
return false;
tmp++;
}

//tmp is now either pointing to a null or the end of the string
const size_t offset1 = tmp-msg;
size_t offset2 = tmp-msg;
for(; offset2<len; offset2++) {
if(*tmp == ',')
break;
tmp++;
}

//Too many NULL bytes
if(offset2-offset1 > 4)
return false;

if((offset2 % 4) != 0)
return false;

size_t observed_length = rtosc_message_length(msg, len);
return observed_length == len;
}

size_t rtosc_bundle(char *buffer, size_t len, uint64_t tt, int elms, ...)
{
char *_buffer = buffer;
memset(buffer, 0, len);
strcpy(buffer, "#bundle");
buffer += 8;
(*(uint64_t*)buffer) = tt;
buffer +=8;
va_list va;
va_start(va, elms);
for(int i=0; i<elms; ++i) {
const char *msg = va_arg(va, const char*);
//It is assumed that any passed message/bundle is valid
size_t size = rtosc_message_length(msg, -1);
*(uint32_t*)buffer = size;
buffer += 4;
memcpy(buffer, msg, size);
buffer+=size;
}
va_end(va);

return buffer-_buffer;
}

#define POS ((size_t)(((const char *)lengths) - buffer))
size_t rtosc_bundle_elements(const char *buffer, size_t len)
{
const uint32_t *lengths = (const uint32_t*) (buffer+16);
size_t elms = 0;
//TODO
while(POS < len && *lengths) {
lengths += *lengths/4+1;

if(POS > len)
break;
++elms;
}
return elms;
}
#undef POS

const char *rtosc_bundle_fetch(const char *buffer, unsigned elm)
{
const uint32_t *lengths = (const uint32_t*) (buffer+16);
size_t elm_pos = 0;
while(elm_pos!=elm && *lengths) ++elm_pos, lengths+=*lengths/4+1;

return (const char*) (elm==elm_pos?lengths+1:NULL);
}

size_t rtosc_bundle_size(const char *buffer, unsigned elm)
{
const uint32_t *lengths = (const uint32_t*) (buffer+16);
size_t elm_pos = 0;
size_t last_len = 0;
while(elm_pos!=elm && *lengths) {
last_len = *lengths;
++elm_pos, lengths+=*lengths/4+1;
}

return last_len;
}

int rtosc_bundle_p(const char *msg)
{
return !strcmp(msg,"#bundle");
}

uint64_t rtosc_bundle_timetag(const char *msg)
{
return *(uint64_t*)(msg+8);
}

+ 9
- 0
source/modules/rtosc/subtree-serialize.h View File

@@ -0,0 +1,9 @@
#include <cstddef>

namespace rtosc{struct Ports; struct RtData;}

size_t subtree_serialize(char *buffer, size_t buffer_size,
void *object, rtosc::Ports *ports);

void subtree_deserialize(char *buffer, size_t buffer_size,
void *object, rtosc::Ports *ports, rtosc::RtData &d);

+ 97
- 0
source/modules/rtosc/thread-link.h View File

@@ -0,0 +1,97 @@
/*
* Copyright (c) 2012 Mark McCurry
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/

#ifndef RTOSC_THREAD_LINK
#define RTOSC_THREAD_LINK

#include <cstring>
#include <cassert>
#include <cstdio>
#include "rtosc.h"

namespace rtosc {
typedef const char *msg_t;

/**
* ThreadLink - A simple wrapper around jack's ringbuffers desinged to make
* sending messages via rt-osc trivial.
* This class provides the basics of reading and writing events via fixed sized
* buffers, which can be specified at compile time.
*/
class ThreadLink
{
public:
ThreadLink(size_t max_message_length, size_t max_messages);
~ThreadLink(void);

/**
* Write message to ringbuffer
* @see rtosc_message()
*/
void write(const char *dest, const char *args, ...);

/**
* Write an arary of arguments to ringbuffer
* @see rtosc_amessage()
*/
void writeArray(const char *dest, const char *args, const rtosc_arg_t *aargs);

/**
* Directly write message to ringbuffer
*/
void raw_write(const char *msg);

/**
* @returns true iff there is another message to be read in the buffer
*/
bool hasNext(void) const;

/**
* Read a new message from the ringbuffer
*/
msg_t read(void);

/**
* Peak at last message read without reading another
*/
msg_t peak(void) const;

/**
* Raw write buffer access for more complicated task
*/
char *buffer(void);
/**
* Access to write buffer length
*/
size_t buffer_size(void) const;
private:
const size_t MaxMsg;
const size_t BufferSize;
char *write_buffer;
char *read_buffer;

struct internal_ringbuffer_t *ring;
};
};
#endif

+ 116
- 0
source/modules/rtosc/typed-message.h View File

@@ -0,0 +1,116 @@
#include "rtosc.h"
#include <type_traits>
#include <stdexcept>

namespace rtosc
{

template<class... Types> class rtMsg;

// empty tuple
template<> class rtMsg<>
{
public:
rtMsg(const char *arg = NULL, const char *spec=NULL, bool _=false)
:msg(arg)
{
if(arg && spec && !rtosc_match_path(spec, arg))
msg = NULL;
(void)_;
}

operator bool(void){return this->msg;}

const char *msg;
};


template<class T>
bool valid_char(char) { return false;}

template<>
bool valid_char<const char*>(char c) { return c=='s' || c=='S'; };

template<>
bool valid_char<int32_t>(char c) { return c=='i'; };

template<>
bool valid_char<float>(char c) { return c=='f'; };

template<int i>
bool validate(const char *arg)
{
return rtosc_narguments(arg) == i;
}

template<int i, class This, class... Rest>
bool validate(const char *arg)
{
if(!valid_char<This>(rtosc_type(arg,i)))
return false;
else
return validate<i+1,Rest...>(arg);
}

//Tuple Like Template Class Definition
template<class This, class... Rest>
class rtMsg<This, Rest...>:public rtMsg<Rest...>
{
public:
typedef rtMsg<Rest...> T;
rtMsg(const char *arg = NULL, const char *spec=NULL)
:T(arg, spec, false)
{
if(this->msg && !validate<0,This,Rest...>(this->msg))
this->msg = NULL;
}

rtMsg(const char *arg, const char *spec, bool)
:T(arg, spec, false)
{}
};


// tuple_element
template<size_t Index, class Tuple> struct osc_element;

// select first element
template<class This, class... Rest>
struct osc_element<0, rtMsg<This, Rest...>>
{
typedef This type;
};

// recursive tuple_element definition
template <size_t Index, class This, class... Rest>
struct osc_element<Index, rtMsg<This, Rest...>>
: public osc_element<Index - 1, rtMsg<Rest...>>
{
};

template<class T>
T rt_get_impl(const char *msg, size_t i);

template<>
const char *rt_get_impl(const char *msg, size_t i)
{
return rtosc_argument(msg,i).s;
}

template<>
int32_t rt_get_impl(const char *msg, size_t i)
{
return rtosc_argument(msg,i).i;
}

// get reference to _Index element of tuple
template<size_t Index, class... Types> inline
typename osc_element<Index, rtMsg<Types...>>::type
get(rtMsg<Types...>& Tuple)
{
if(!Tuple.msg)
throw std::invalid_argument("Message Does Not Match Spec");
typedef typename std::remove_reference<typename osc_element<Index, rtMsg<Types...>>::type>::type T;
return rt_get_impl<T>(Tuple.msg, Index);
}
};

+ 34
- 0
source/modules/rtosc/undo-history.h View File

@@ -0,0 +1,34 @@
#pragma once
#include <functional>

namespace rtosc
{
/**
* Known event types:
* /undo_change /path/location old-data new-data
*/
class UndoHistory
{
//TODO think about the consequences of largish loads
public:
UndoHistory(void);

//Records any undoable event
void recordEvent(const char *msg);

//Prints out a history
void showHistory(void) const;

//Seek to another point in history relative to the current one
//Negative values mean undo, positive values mean redo
void seekHistory(int distance);

unsigned getPos(void) const;
const char *getHistory(int i) const;
size_t size(void) const;

void setCallback(std::function<void(const char*)> cb);
private:
class UndoHistoryImpl *impl;
};
};

+ 6
- 7
source/native-plugins/Makefile View File

@@ -19,21 +19,20 @@ BUILD_CXX_FLAGS += -I.. -isystem $(CWD)/modules -I$(CWD)/modules/distrho
ifeq ($(HAVE_ZYN_DEPS),true)
ZYN_CXX_FLAGS = $(BUILD_CXX_FLAGS) -isystem zynaddsubfx
ZYN_CXX_FLAGS += $(shell pkg-config --cflags fftw3 mxml zlib)
ifneq ($(MACOS),true)
ZYN_CXX_FLAGS += -DHAVE_SCHEDULER
endif
ifeq ($(HAVE_ZYN_UI_DEPS),true)
ifeq ($(HAVE_NTK),true)
FLUID = ntk-fluid
ZYN_CXX_FLAGS += -DNTK_GUI
ZYN_CXX_FLAGS += $(shell pkg-config --cflags ntk_images ntk)
else
else # HAVE_NTK
FLUID = fluid
ZYN_CXX_FLAGS += -DFLTK_GUI
ZYN_CXX_FLAGS += $(shell fltk-config --use-images --cxxflags)
endif
endif
endif
endif # HAVE_NTK
else # HAVE_ZYN_UI_DEPS
ZYN_CXX_FLAGS += -DNO_UI
endif # HAVE_ZYN_UI_DEPS
endif # HAVE_ZYN_DEPS

# ----------------------------------------------------------------------------------------------------------------------------
# Set objects


+ 66
- 94
source/native-plugins/zynaddsubfx-fx.cpp View File

@@ -1,6 +1,6 @@
/*
* Carla Native Plugins
* Copyright (C) 2012-2014 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2015 Filipe Coelho <falktx@falktx.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@@ -25,13 +25,16 @@
#include "Effects/Echo.h"
#include "Effects/Phaser.h"
#include "Effects/Reverb.h"
#include "Misc/Allocator.h"

#include "juce_audio_basics.h"
using juce::roundToIntAccurate;
using juce::FloatVectorOperations;
using juce::SharedResourcePointer;

// -----------------------------------------------------------------------

template<class ZynFX>
class FxAbstractPlugin : public NativePluginClass
{
protected:
@@ -39,18 +42,21 @@ protected:
: NativePluginClass(host),
fParamCount(paramCount-2), // volume and pan handled by host
fProgramCount(programCount),
fBufferSize(getBufferSize()),
fSampleRate(getSampleRate()),
fEffect(nullptr),
efxoutl(nullptr),
efxoutr(nullptr),
leakDetector_FxAbstractPlugin()
{
const int bufferSize(static_cast<int>(getBufferSize()));
const int ibufferSize(static_cast<int>(fBufferSize));

efxoutl = new float[bufferSize];
efxoutr = new float[bufferSize];
efxoutl = new float[fBufferSize];
efxoutr = new float[fBufferSize];
FloatVectorOperations::clear(efxoutl, ibufferSize);
FloatVectorOperations::clear(efxoutr, ibufferSize);

FloatVectorOperations::clear(efxoutl, bufferSize);
FloatVectorOperations::clear(efxoutr, bufferSize);
doReinit(true);
}

~FxAbstractPlugin() override
@@ -147,7 +153,11 @@ protected:

void bufferSizeChanged(const uint32_t bufferSize) final
{
const int ibufferSize(static_cast<int>(getBufferSize()));
if (fBufferSize == bufferSize)
return;

fBufferSize = bufferSize;
const int ibufferSize(static_cast<int>(fBufferSize));

delete[] efxoutl;
delete[] efxoutr;
@@ -156,52 +166,74 @@ protected:
FloatVectorOperations::clear(efxoutl, ibufferSize);
FloatVectorOperations::clear(efxoutr, ibufferSize);

doReinit(getSampleRate(), bufferSize);
doReinit(false);
}

void sampleRateChanged(const double sampleRate) final
{
doReinit(sampleRate, getBufferSize());
if (fSampleRate == sampleRate)
return;

fSampleRate = sampleRate;

doReinit(false);
}

void doReinit(const double sampleRate, const uint32_t bufferSize)
void doReinit(const bool firstInit)
{
uchar params[fParamCount];

for (int i=0, count=static_cast<int>(fParamCount); i<count; ++i)
params[i] = fEffect->getpar(i+2);
if (fEffect != nullptr)
{
for (int i=0, count=static_cast<int>(fParamCount); i<count; ++i)
params[i] = fEffect->getpar(i+2);

reinit(static_cast<uint>(sampleRate), static_cast<int>(bufferSize));
delete fEffect;
}

for (int i=0, count=static_cast<int>(fParamCount); i<count; ++i)
fEffect->changepar(i+2, params[i]);
}
EffectParams pars(fAllocator.getObject(), false, efxoutl, efxoutr, 0, static_cast<uint>(fSampleRate), static_cast<int>(fBufferSize));
fEffect = new ZynFX(pars);

if (firstInit)
{
fEffect->setpreset(0);
}
else
{
for (int i=0, count=static_cast<int>(fParamCount); i<count; ++i)
fEffect->changepar(i+2, params[i]);
}

virtual void reinit(const uint sampleRate, const int bufferSize) = 0;
// reset volume and pan
fEffect->changepar(0, 127);
fEffect->changepar(1, 64);
}

// -------------------------------------------------------------------

const uint32_t fParamCount;
const uint32_t fProgramCount;

uint32_t fBufferSize;
double fSampleRate;

Effect* fEffect;
float* efxoutl;
float* efxoutr;

SharedResourcePointer<Allocator> fAllocator;

CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(FxAbstractPlugin)
};

// -----------------------------------------------------------------------

class FxAlienWahPlugin : public FxAbstractPlugin
class FxAlienWahPlugin : public FxAbstractPlugin<Alienwah>
{
public:
FxAlienWahPlugin(const NativeHostDescriptor* const host)
: FxAbstractPlugin(host, 11, 4),
leakDetector_FxAlienWahPlugin()
{
fEffect = new Alienwah(false, efxoutl, efxoutr, static_cast<uint>(getSampleRate()), static_cast<int>(getBufferSize()));
}
leakDetector_FxAlienWahPlugin() {}

protected:
// -------------------------------------------------------------------
@@ -327,27 +359,18 @@ protected:

// -------------------------------------------------------------------

void reinit(const uint sampleRate, const int bufferSize) override
{
delete fEffect;
fEffect = new Alienwah(false, efxoutl, efxoutr, sampleRate, bufferSize);
}

PluginClassEND(FxAlienWahPlugin)
CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(FxAlienWahPlugin)
};

// -----------------------------------------------------------------------

class FxChorusPlugin : public FxAbstractPlugin
class FxChorusPlugin : public FxAbstractPlugin<Chorus>
{
public:
FxChorusPlugin(const NativeHostDescriptor* const host)
: FxAbstractPlugin(host, 12, 10),
leakDetector_FxChorusPlugin()
{
fEffect = new Chorus(false, efxoutl, efxoutr, static_cast<uint>(getSampleRate()), static_cast<int>(getBufferSize()));
}
leakDetector_FxChorusPlugin() {}

protected:
// -------------------------------------------------------------------
@@ -497,27 +520,18 @@ protected:

// -------------------------------------------------------------------

void reinit(const uint sampleRate, const int bufferSize) override
{
delete fEffect;
fEffect = new Chorus(false, efxoutl, efxoutr, sampleRate, bufferSize);
}

PluginClassEND(FxChorusPlugin)
CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(FxChorusPlugin)
};

// -----------------------------------------------------------------------

class FxDistortionPlugin : public FxAbstractPlugin
class FxDistortionPlugin : public FxAbstractPlugin<Distorsion>
{
public:
FxDistortionPlugin(const NativeHostDescriptor* const host)
: FxAbstractPlugin(host, 11, 6),
leakDetector_FxDistortionPlugin()
{
fEffect = new Distorsion(false, efxoutl, efxoutr, static_cast<uint>(getSampleRate()), static_cast<int>(getBufferSize()));
}
leakDetector_FxDistortionPlugin() {}

protected:
// -------------------------------------------------------------------
@@ -675,27 +689,18 @@ protected:

// -------------------------------------------------------------------

void reinit(const uint sampleRate, const int bufferSize) override
{
delete fEffect;
fEffect = new Distorsion(false, efxoutl, efxoutr, sampleRate, bufferSize);
}

PluginClassEND(FxDistortionPlugin)
CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(FxDistortionPlugin)
};

// -----------------------------------------------------------------------

class FxDynamicFilterPlugin : public FxAbstractPlugin
class FxDynamicFilterPlugin : public FxAbstractPlugin<DynamicFilter>
{
public:
FxDynamicFilterPlugin(const NativeHostDescriptor* const host)
: FxAbstractPlugin(host, 10, 5),
leakDetector_FxDynamicFilterPlugin()
{
fEffect = new DynamicFilter(false, efxoutl, efxoutr, static_cast<uint>(getSampleRate()), static_cast<int>(getBufferSize()));
}
leakDetector_FxDynamicFilterPlugin() {}

protected:
// -------------------------------------------------------------------
@@ -819,27 +824,18 @@ protected:

// -------------------------------------------------------------------

void reinit(const uint sampleRate, const int bufferSize) override
{
delete fEffect;
fEffect = new DynamicFilter(false, efxoutl, efxoutr, sampleRate, bufferSize);
}

PluginClassEND(FxDynamicFilterPlugin)
CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(FxDynamicFilterPlugin)
};

// -----------------------------------------------------------------------

class FxEchoPlugin : public FxAbstractPlugin
class FxEchoPlugin : public FxAbstractPlugin<Echo>
{
public:
FxEchoPlugin(const NativeHostDescriptor* const host)
: FxAbstractPlugin(host, 7, 9),
leakDetector_FxEchoPlugin()
{
fEffect = new Echo(false, efxoutl, efxoutr, static_cast<uint>(getSampleRate()), static_cast<int>(getBufferSize()));
}
leakDetector_FxEchoPlugin() {}

protected:
// -------------------------------------------------------------------
@@ -951,27 +947,18 @@ protected:

// -------------------------------------------------------------------

void reinit(const uint sampleRate, const int bufferSize) override
{
delete fEffect;
fEffect = new Echo(false, efxoutl, efxoutr, sampleRate, bufferSize);
}

PluginClassEND(FxEchoPlugin)
CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(FxEchoPlugin)
};

// -----------------------------------------------------------------------

class FxPhaserPlugin : public FxAbstractPlugin
class FxPhaserPlugin : public FxAbstractPlugin<Phaser>
{
public:
FxPhaserPlugin(const NativeHostDescriptor* const host)
: FxAbstractPlugin(host, 15, 12),
leakDetector_FxPhaserPlugin()
{
fEffect = new Phaser(false, efxoutl, efxoutr, static_cast<uint>(getSampleRate()), static_cast<int>(getBufferSize()));
}
leakDetector_FxPhaserPlugin() {}

protected:
// -------------------------------------------------------------------
@@ -1144,27 +1131,18 @@ protected:

// -------------------------------------------------------------------

void reinit(const uint sampleRate, const int bufferSize) override
{
delete fEffect;
fEffect = new Phaser(false, efxoutl, efxoutr, sampleRate, bufferSize);
}

PluginClassEND(FxPhaserPlugin)
CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(FxPhaserPlugin)
};

// -----------------------------------------------------------------------

class FxReverbPlugin : public FxAbstractPlugin
class FxReverbPlugin : public FxAbstractPlugin<Reverb>
{
public:
FxReverbPlugin(const NativeHostDescriptor* const host)
: FxAbstractPlugin(host, 13, 13),
leakDetector_FxReverbPlugin()
{
fEffect = new Reverb(false, efxoutl, efxoutr, static_cast<uint>(getSampleRate()), static_cast<int>(getBufferSize()));
}
leakDetector_FxReverbPlugin() {}

protected:
// -------------------------------------------------------------------
@@ -1323,12 +1301,6 @@ protected:

// -------------------------------------------------------------------

void reinit(const uint sampleRate, const int bufferSize) override
{
delete fEffect;
fEffect = new Reverb(false, efxoutl, efxoutr, sampleRate, bufferSize);
}

PluginClassEND(FxReverbPlugin)
CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(FxReverbPlugin)
};


+ 28
- 15
source/native-plugins/zynaddsubfx-src.cpp View File

@@ -1,6 +1,6 @@
/*
* Carla Native Plugins
* Copyright (C) 2012-2014 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2015 Filipe Coelho <falktx@falktx.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@@ -22,6 +22,13 @@
#define warnx(...)
#endif

#define PLUGINVERSION

#include "zynaddsubfx/tlsf/tlsf.h"
extern "C" {
#include "zynaddsubfx/tlsf/tlsf.c"
}

// zynaddsubfx includes
#include "zynaddsubfx/DSP/AnalogFilter.cpp"
#include "zynaddsubfx/DSP/FFTwrapper.cpp"
@@ -40,12 +47,14 @@
#include "zynaddsubfx/Effects/EQ.cpp"
#include "zynaddsubfx/Effects/Phaser.cpp"
#include "zynaddsubfx/Effects/Reverb.cpp"
#include "zynaddsubfx/Misc/Allocator.cpp"
#include "zynaddsubfx/Misc/Bank.cpp"
#include "zynaddsubfx/Misc/Config.cpp"
#include "zynaddsubfx/Misc/Dump.cpp"
#include "zynaddsubfx/Misc/Master.cpp"
#include "zynaddsubfx/Misc/Microtonal.cpp"
#include "zynaddsubfx/Misc/MiddleWare.cpp"
#include "zynaddsubfx/Misc/Part.cpp"
#include "zynaddsubfx/Misc/PresetExtractor.cpp"
#include "zynaddsubfx/Misc/Recorder.cpp"
//#include "zynaddsubfx/Misc/Stereo.cpp"
#include "zynaddsubfx/Misc/Util.cpp"
@@ -71,21 +80,25 @@
#include "zynaddsubfx/Synth/SUBnote.cpp"
#include "zynaddsubfx/Synth/SynthNote.cpp"

#ifdef NO_UI
#include "zynaddsubfx/UI/ConnectionDummy.cpp"
#endif

// Dummy variables and functions for linking purposes
const char* instance_name = nullptr;
SYNTH_T* synth = nullptr;
// const char* instance_name = nullptr;
// SYNTH_T* synth = nullptr;
class WavFile;
namespace Nio {
bool start(void){return 1;}
void stop(void){}
bool setSource(std::string){return true;}
bool setSink(std::string){return true;}
std::set<std::string> getSources(void){return std::set<std::string>();}
std::set<std::string> getSinks(void){return std::set<std::string>();}
std::string getSource(void){return "";}
std::string getSink(void){return "";}
// bool start(void){return 1;}
// void stop(void){}
// bool setSource(std::string){return true;}
// bool setSink(std::string){return true;}
// std::set<std::string> getSources(void){return std::set<std::string>();}
// std::set<std::string> getSinks(void){return std::set<std::string>();}
// std::string getSource(void){return "";}
// std::string getSink(void){return "";}
void waveNew(WavFile*){}
void waveStart(void){}
void waveStop(void){}
void waveEnd(void){}
void waveStart(){}
void waveStop(){}
// void waveEnd(void){}
}

+ 115
- 210
source/native-plugins/zynaddsubfx-synth.cpp View File

@@ -1,6 +1,6 @@
/*
* Carla Native Plugins
* Copyright (C) 2012-2014 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2015 Filipe Coelho <falktx@falktx.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@@ -111,12 +111,10 @@ public:
return;
fInitiated = true;

Master& master(Master::getInstance());

pthread_mutex_lock(&master.mutex);

fPrograms.append(new ProgramInfo(0, 0, "default"));

Master& master(getMasterInstance());

// refresh banks
master.bank.rescanforbanks();

@@ -137,48 +135,43 @@ public:
fPrograms.append(new ProgramInfo(i+1, instrument, insName.c_str()));
}
}

pthread_mutex_unlock(&master.mutex);
}

void load(Master* const master, const uint8_t channel, const uint32_t bank, const uint32_t program)
void load(Master* const master, CarlaMutex& mutex, const uint8_t channel, const uint32_t bank, const uint32_t program)
{
if (bank == 0)
{
pthread_mutex_lock(&master->mutex);
if (program != 0)
return;

const CarlaMutexLocker cml(mutex);

master->partonoff(channel, 1);
master->part[channel]->defaults();
master->part[channel]->applyparameters(false);

pthread_mutex_unlock(&master->mutex);

return;
}

const std::string& bankdir(master->bank.banks[bank-1].dir);

if (! bankdir.empty())
{
pthread_mutex_lock(&master->mutex);

master->partonoff(channel, 1);
const Master& gmaster(getMasterInstance());
const std::string& bankdir(gmaster.bank.banks[bank-1].dir);

master->bank.loadbank(bankdir);
master->bank.loadfromslot(program, master->part[channel]);
if (bankdir.empty())
return;

master->part[channel]->applyparameters(false);
const CarlaMutexLocker cml(mutex);

pthread_mutex_unlock(&master->mutex);
}
master->partonoff(channel, 1);
master->bank.loadbank(bankdir);
master->bank.loadfromslot(program, master->part[channel]);
master->part[channel]->applyparameters(false);
}

uint32_t count() const noexcept
uint32_t getNativeMidiProgramCount() const noexcept
{
return static_cast<uint32_t>(fPrograms.count());
}

const NativeMidiProgram* getInfo(const uint32_t index) const noexcept
const NativeMidiProgram* getNativeMidiProgramInfo(const uint32_t index) const noexcept
{
if (index >= fPrograms.count())
return nullptr;
@@ -193,6 +186,13 @@ public:
return &fRetProgram;
}

uint32_t getZynBankCount() const
{
const Master& master(getMasterInstance());

return master.bank.banks.size();
}

private:
struct ProgramInfo {
uint32_t bank;
@@ -226,140 +226,28 @@ private:
mutable NativeMidiProgram fRetProgram;
LinkedList<const ProgramInfo*> fPrograms;

CARLA_PREVENT_HEAP_ALLOCATION
CARLA_DECLARE_NON_COPY_CLASS(ZynAddSubFxPrograms)
};

static ZynAddSubFxPrograms sPrograms;

// -----------------------------------------------------------------------

class ZynAddSubFxInstanceCount
{
public:
ZynAddSubFxInstanceCount()
: fCount(0),
fMutex() {}

~ZynAddSubFxInstanceCount()
static Master& getMasterInstance()
{
CARLA_SAFE_ASSERT(fCount == 0);
}

void addOne(const NativeHostDescriptor* const host)
{
if (fCount++ != 0)
return;

const CarlaMutexLocker cml(fMutex);

CARLA_SAFE_ASSERT(synth == nullptr);
CARLA_SAFE_ASSERT(denormalkillbuf == nullptr);

reinit(host);

#ifdef WANT_ZYNADDSUBFX_UI
if (gPixmapPath.isEmpty())
{
gPixmapPath = host->resourceDir;
gPixmapPath += "/zynaddsubfx/";
gUiPixmapPath = gPixmapPath;
}
#endif
}

void removeOne()
{
if (--fCount != 0)
return;

const CarlaMutexLocker cml(fMutex);

CARLA_SAFE_ASSERT(synth != nullptr);
CARLA_SAFE_ASSERT(denormalkillbuf != nullptr);

Master::deleteInstance();

delete[] denormalkillbuf;
denormalkillbuf = nullptr;

delete synth;
synth = nullptr;
}

void maybeReinit(const NativeHostDescriptor* const host)
{
if (static_cast< int>(host->get_buffer_size(host->handle)) == synth->buffersize &&
static_cast<uint>(host->get_sample_rate(host->handle)) == synth->samplerate)
return;

const CarlaMutexLocker cml(fMutex);

reinit(host);
}

CarlaMutex& getLock() noexcept
{
return fMutex;
}

private:
int fCount;
CarlaMutex fMutex;

void reinit(const NativeHostDescriptor* const host)
{
Master::deleteInstance();

if (denormalkillbuf != nullptr)
{
delete[] denormalkillbuf;
denormalkillbuf = nullptr;
}

if (synth != nullptr)
{
delete synth;
synth = nullptr;
}

synth = new SYNTH_T();
synth->buffersize = static_cast<int>(host->get_buffer_size(host->handle));
synth->samplerate = static_cast<uint>(host->get_sample_rate(host->handle));

config.init();
config.cfg.SoundBufferSize = synth->buffersize;
config.cfg.SampleRate = static_cast<int>(synth->samplerate);
config.cfg.GzipCompression = 0;

sprng(static_cast<prng_t>(std::time(nullptr)));

denormalkillbuf = new float[synth->buffersize];
for (int i=0; i < synth->buffersize; ++i)
denormalkillbuf[i] = (RND - 0.5f) * 1e-16f;

if (synth->buffersize > 32)
synth->buffersize = 32;

synth->alias();

Master::getInstance();
static SYNTH_T synth;
static Master master(synth);
return master;
}

CARLA_PREVENT_HEAP_ALLOCATION
CARLA_DECLARE_NON_COPY_CLASS(ZynAddSubFxInstanceCount)
CARLA_DECLARE_NON_COPY_CLASS(ZynAddSubFxPrograms)
};

static ZynAddSubFxInstanceCount sInstanceCount;
static ZynAddSubFxPrograms sPrograms;

// -----------------------------------------------------------------------

class ZynAddSubFxThread : public CarlaThread
{
public:
ZynAddSubFxThread(Master* const master, const NativeHostDescriptor* const host)
ZynAddSubFxThread(Master* const master, CarlaMutex& mutex, const NativeHostDescriptor* const host)
: CarlaThread("ZynAddSubFxThread"),
fMaster(master),
fMutex(mutex),
kHost(host),
#ifdef WANT_ZYNADDSUBFX_UI
fUi(nullptr),
@@ -475,12 +363,11 @@ protected:
if (fUi == nullptr)
{
fUiClosed = 0;
fUi = new MasterUI(fMaster, &fUiClosed);
fUi = new MasterUI(&fUiClosed, &fOscIface);
fUi->masterwindow->label(kHost->uiName);
fUi->showUI();
}
else
fUi->showUI();
fUi->showUI(1);
}
else if (fNextUiAction == 0) // close
{
@@ -509,7 +396,7 @@ protected:
if (fChangeProgram)
{
fChangeProgram = false;
sPrograms.load(fMaster, fNextChannel, fNextBank, fNextProgram);
sPrograms.load(fMaster, fMutex, fNextChannel, fNextBank, fNextProgram);
fNextChannel = 0;
fNextBank = 0;
fNextProgram = 0;
@@ -545,12 +432,14 @@ protected:

private:
Master* fMaster;
CarlaMutex& fMutex;
const NativeHostDescriptor* const kHost;

#ifdef WANT_ZYNADDSUBFX_UI
MasterUI* fUi;
int fUiClosed;
volatile int fNextUiAction;
Fl_Osc_Interface fOscIface;
#endif

volatile bool fChangeProgram;
@@ -580,9 +469,10 @@ public:
ZynAddSubFxPlugin(const NativeHostDescriptor* const host)
: NativePluginClass(host),
fMaster(nullptr),
fSampleRate(static_cast<uint>(getSampleRate())),
fSynth(),
fIsActive(false),
fThread(nullptr, host),
fMutex(),
fThread(nullptr, fMutex, host),
leakDetector_ZynAddSubFxPlugin()
{
// init parameters to default
@@ -593,7 +483,19 @@ public:
fParameters[kParamResCenter] = 64.0f;
fParameters[kParamResBandwidth] = 64.0f;

fSynth.buffersize = static_cast<int>(getBufferSize());
fSynth.samplerate = static_cast<uint>(getSampleRate());

//if (fSynth.buffersize > 32)
// fSynth.buffersize = 32;

fSynth.alias();

// FIXME
fSynth.samplerate_f = getSampleRate();

_initMaster();

sPrograms.initIfNeeded();
}

@@ -668,14 +570,14 @@ protected:
// -------------------------------------------------------------------
// Plugin midi-program calls

uint32_t getMidiProgramCount() const override
uint32_t getMidiProgramCount() const noexcept override
{
return sPrograms.count();
return sPrograms.getNativeMidiProgramCount();
}

const NativeMidiProgram* getMidiProgramInfo(const uint32_t index) const override
const NativeMidiProgram* getMidiProgramInfo(const uint32_t index) const noexcept override
{
return sPrograms.getInfo(index);
return sPrograms.getNativeMidiProgramInfo(index);
}

// -------------------------------------------------------------------
@@ -699,14 +601,14 @@ protected:

void setMidiProgram(const uint8_t channel, const uint32_t bank, const uint32_t program) override
{
if (bank >= fMaster->bank.banks.size())
if (bank >= sPrograms.getZynBankCount())
return;
if (program >= BANK_SIZE)
return;

if (isOffline() || ! fIsActive)
{
sPrograms.load(fMaster, channel, bank, program);
sPrograms.load(fMaster, fMutex, channel, bank, program);
#ifdef WANT_ZYNADDSUBFX_UI
fThread.uiRepaint();
#endif
@@ -721,7 +623,7 @@ protected:
CARLA_SAFE_ASSERT_RETURN(key != nullptr,);
CARLA_SAFE_ASSERT_RETURN(value != nullptr,);

pthread_mutex_lock(&fMaster->mutex);
const CarlaMutexLocker cml(fMutex);

/**/ if (std::strcmp(key, "CarlaAlternateFile1") == 0) // xmz
{
@@ -734,9 +636,7 @@ protected:
fMaster->part[0]->loadXMLinstrument(value);
}

fMaster->applyparameters(false);

pthread_mutex_unlock(&fMaster->mutex);
fMaster->applyparameters();
}

// -------------------------------------------------------------------
@@ -754,13 +654,15 @@ protected:

void process(float**, float** const outBuffer, const uint32_t frames, const NativeMidiEvent* const midiEvents, const uint32_t midiEventCount) override
{
const CarlaMutexTryLocker cmtl(sInstanceCount.getLock());

if (cmtl.wasNotLocked() || pthread_mutex_trylock(&fMaster->mutex) != 0)
if (! fMutex.tryLock())
{
FloatVectorOperations::clear(outBuffer[0], static_cast<int>(frames));
FloatVectorOperations::clear(outBuffer[1], static_cast<int>(frames));
return;
if (! isOffline())
{
FloatVectorOperations::clear(outBuffer[0], static_cast<int>(frames));
FloatVectorOperations::clear(outBuffer[1], static_cast<int>(frames));
return;
}
fMutex.lock();
}

for (uint32_t i=0; i < midiEventCount; ++i)
@@ -811,9 +713,9 @@ protected:
}
}

fMaster->GetAudioOutSamples(frames, fSampleRate, outBuffer[0], outBuffer[1]);
fMaster->GetAudioOutSamples(frames, fSynth.samplerate, outBuffer[0], outBuffer[1]);

pthread_mutex_unlock(&fMaster->mutex);
fMutex.unlock();
}

#ifdef WANT_ZYNADDSUBFX_UI
@@ -834,8 +736,6 @@ protected:

char* getState() const override
{
config.save();

char* data = nullptr;
fMaster->getalldata(&data);
return data;
@@ -844,43 +744,53 @@ protected:
void setState(const char* const data) override
{
fThread.stopLoadProgramLater();

const CarlaMutexLocker cml(fMutex);

fMaster->putalldata(const_cast<char*>(data), 0);
fMaster->applyparameters(true);
fMaster->applyparameters();
}

// -------------------------------------------------------------------
// Plugin dispatcher

void bufferSizeChanged(const uint32_t) final
void bufferSizeChanged(const uint32_t bufferSize) final
{
char* const state(getState());

_deleteMaster();
sInstanceCount.maybeReinit(getHostHandle());

fSynth.buffersize = static_cast<int>(bufferSize);
fSynth.alias();

_initMaster();

if (state != nullptr)
{
fMaster->putalldata(state, 0);
fMaster->applyparameters(true);
fMaster->applyparameters();
std::free(state);
}
}

void sampleRateChanged(const double sampleRate) final
{
fSampleRate = static_cast<uint>(sampleRate);

char* const state(getState());

_deleteMaster();
sInstanceCount.maybeReinit(getHostHandle());

fSynth.samplerate = static_cast<uint>(sampleRate);
fSynth.alias();

// FIXME
fSynth.samplerate_f = sampleRate;

_initMaster();

if (state != nullptr)
{
fMaster->putalldata(state, 0);
fMaster->applyparameters(true);
fMaster->applyparameters();
std::free(state);
}
}
@@ -896,35 +806,13 @@ protected:

private:
Master* fMaster;
uint fSampleRate;
SYNTH_T fSynth;
bool fIsActive;
float fParameters[kParamCount];

CarlaMutex fMutex;
ZynAddSubFxThread fThread;

/*
static Parameters getParameterFromZynIndex(const MidiControllers index)
{
switch (index)
{
case C_filtercutoff:
return kParamFilterCutoff;
case C_filterq:
return kParamFilterQ;
case C_bandwidth:
return kParamBandwidth;
case C_fmamp:
return kParamModAmp;
case C_resonance_center:
return kParamResCenter;
case C_resonance_bandwidth:
return kParamResBandwidth;
default:
return kParamCount;
}
}
*/

static uint getZynParameterFromIndex(const uint index)
{
switch (index)
@@ -952,7 +840,7 @@ private:

void _initMaster()
{
fMaster = new Master();
fMaster = new Master(fSynth);
fThread.setMaster(fMaster);
fThread.startThread();

@@ -971,8 +859,6 @@ private:
void _deleteMaster()
{
//ensure that everything has stopped
pthread_mutex_lock(&fMaster->mutex);
pthread_mutex_unlock(&fMaster->mutex);
fThread.stopThread(-1);

delete fMaster;
@@ -984,14 +870,33 @@ private:
public:
static NativePluginHandle _instantiate(const NativeHostDescriptor* host)
{
sInstanceCount.addOne(host);
static bool needsInit = true;

if (needsInit)
{
needsInit = false;
config.init();

sprng(static_cast<prng_t>(std::time(nullptr)));

// FIXME - kill this
denormalkillbuf = new float[8192];
for (int i=0; i < 8192; ++i)
denormalkillbuf[i] = (RND - 0.5f) * 1e-16f;

#ifdef WANT_ZYNADDSUBFX_UI
gPixmapPath = host->resourceDir;
gPixmapPath += "/zynaddsubfx/";
gUiPixmapPath = gPixmapPath;
#endif
}

return new ZynAddSubFxPlugin(host);
}

static void _cleanup(NativePluginHandle handle)
{
delete (ZynAddSubFxPlugin*)handle;
sInstanceCount.removeOne();
}

CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(ZynAddSubFxPlugin)


+ 19
- 19
source/native-plugins/zynaddsubfx-ui.cpp View File

@@ -1,6 +1,6 @@
/*
* Carla Native Plugins
* Copyright (C) 2012-2014 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2015 Filipe Coelho <falktx@falktx.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@@ -24,21 +24,21 @@
CarlaString gUiPixmapPath;

// zynaddsubfx ui includes
#include "zynaddsubfx/UI/NioUI.cpp"
#include "zynaddsubfx/UI/WidgetPDial.cpp"
#include "zynaddsubfx/UI/ADnoteUI.cpp"
#include "zynaddsubfx/UI/BankUI.cpp"
#include "zynaddsubfx/UI/ConfigUI.cpp"
#include "zynaddsubfx/UI/EffUI.cpp"
#include "zynaddsubfx/UI/EnvelopeUI.cpp"
#include "zynaddsubfx/UI/FilterUI.cpp"
#include "zynaddsubfx/UI/LFOUI.cpp"
#include "zynaddsubfx/UI/MasterUI.cpp"
#include "zynaddsubfx/UI/MicrotonalUI.cpp"
#include "zynaddsubfx/UI/OscilGenUI.cpp"
#include "zynaddsubfx/UI/PADnoteUI.cpp"
#include "zynaddsubfx/UI/PartUI.cpp"
#include "zynaddsubfx/UI/PresetsUI.cpp"
#include "zynaddsubfx/UI/ResonanceUI.cpp"
#include "zynaddsubfx/UI/SUBnoteUI.cpp"
#include "zynaddsubfx/UI/VirKeyboard.cpp"
// #include "zynaddsubfx/UI/NioUI.cpp"
// #include "zynaddsubfx/UI/WidgetPDial.cpp"
// #include "zynaddsubfx/UI/ADnoteUI.cpp"
// #include "zynaddsubfx/UI/BankUI.cpp"
// #include "zynaddsubfx/UI/ConfigUI.cpp"
// #include "zynaddsubfx/UI/EffUI.cpp"
// #include "zynaddsubfx/UI/EnvelopeUI.cpp"
// #include "zynaddsubfx/UI/FilterUI.cpp"
// #include "zynaddsubfx/UI/LFOUI.cpp"
// #include "zynaddsubfx/UI/MasterUI.cpp"
// #include "zynaddsubfx/UI/MicrotonalUI.cpp"
// #include "zynaddsubfx/UI/OscilGenUI.cpp"
// #include "zynaddsubfx/UI/PADnoteUI.cpp"
// #include "zynaddsubfx/UI/PartUI.cpp"
// #include "zynaddsubfx/UI/PresetsUI.cpp"
// #include "zynaddsubfx/UI/ResonanceUI.cpp"
// #include "zynaddsubfx/UI/SUBnoteUI.cpp"
// #include "zynaddsubfx/UI/VirKeyboard.cpp"

+ 0
- 384
source/native-plugins/zynaddsubfx/CMakeLists.txt View File

@@ -1,384 +0,0 @@
#checking include/library paths
message(STATUS "Checking Include Path" $ENV{CMAKE_INCLUDE_PATH} ${CMAKE_INCLUDE_PATH})
message(STATUS "Checking Library Path" $ENV{CMAKE_LIBRARY_PATH} ${CMAKE_LIBRARY_PATH})

#Dependency check
find_package(PkgConfig REQUIRED)
find_package(zlib REQUIRED)
pkg_check_modules(FFTW REQUIRED fftw3)
pkg_check_modules(MXML REQUIRED mxml)
find_package(Threads REQUIRED)
find_package(OSS)
find_package(Alsa)
pkg_check_modules(JACK jack)
pkg_check_modules(PORTAUDIO portaudio-2.0>=19)
set(FLTK_SKIP_OPENGL true)
pkg_check_modules(NTK ntk)
pkg_check_modules(NTK_IMAGES ntk_images)
find_package(FLTK)
find_package(OpenGL) #for FLTK
find_package(CxxTest)
if(CXXTEST_FOUND)
set(CXXTEST_USE_PYTHON TRUE)
endif()
# lash
pkg_search_module(LASH lash-1.0)
mark_as_advanced(LASH_LIBRARIES)
pkg_search_module(DSSI dssi>=0.9.0)
mark_as_advanced(DSSI_LIBRARIES)
pkg_search_module(LIBLO liblo>=0.26)
mark_as_advanced(LIBLO_LIBRARIES)

CHECK_FUNCTION_EXISTS(sched_setscheduler HAVE_SCHEDULER)

execute_process(COMMAND echo fistpl 0
COMMAND as -
ERROR_VARIABLE AVOID_ASM)

######### Settings ###########
# NOTE: These cache variables should normally not be changed in this
# file, but either in in CMakeCache.txt before compile, or by passing
# parameters directly into cmake using the -D flag.
SET (GuiModule fltk CACHE STRING "GUI module, either fltk, ntk or off")
SET (CompileTests ${CXXTEST_FOUND} CACHE BOOL "whether tests should be compiled in or not")
SET (AlsaEnable ${ALSA_FOUND} CACHE BOOL
"Enable support for Advanced Linux Sound Architecture")
SET (JackEnable ${JACK_FOUND} CACHE BOOL
"Enable support for JACK Audio Connection toolKit")
SET (OssEnable ${OSS_FOUND} CACHE BOOL
"Enable support for Open Sound System")
SET (PaEnable ${PORTAUDIO_FOUND} CACHE BOOL
"Enable support for Port Audio System")
SET (LashEnable ${LASH_FOUND} CACHE BOOL
"Enable LASH Audio Session Handler")
SET (DssiEnable ${DSSI_FOUND} CACHE BOOL
"Enable DSSI Plugin compilation")
SET (LibloEnable ${LIBLO_FOUND} CACHE BOOL
"Enable Liblo")

# Now, handle the incoming settings and set define flags/variables based
# on this

# Add version information
add_definitions(-DVERSION="${VERSION}")

message(STATUS "Building on a '${CMAKE_SYSTEM_NAME}' System")

if(NOT "Darwin" STREQUAL ${CMAKE_SYSTEM_NAME})
# Add scheduler function existance info (OSX compatiability)
add_definitions(-DHAVE_SCHEDULER=${HAVE_SCHEDULER})
endif()


# Give a good guess on the best Input/Output default backends
if (JackEnable)
SET (DefaultOutput jack CACHE STRING
"Default Output module: [null, alsa, oss, jack, portaudio]")
# Override with perhaps more helpful midi backends
if (AlsaEnable)
SET (DefaultInput alsa CACHE STRING
"Default Input module: [null, alsa, oss, jack]")
elseif (OssEnable)
SET (DefaultInput oss CACHE STRING
"Default Input module: [null, alsa, oss, jack]")
else ()
SET (DefaultInput jack CACHE STRING
"Default Input module: [null, alsa, oss, jack]")
endif ()
elseif (AlsaEnable)
SET (DefaultOutput alsa CACHE STRING
"Default Output module: [null, alsa, oss, jack, portaudio]")
SET (DefaultInput alsa CACHE STRING
"Default Input module: [null, alsa, oss, jack]")
elseif (OssEnable)
SET (DefaultOutput oss CACHE STRING
"Default Output module: [null, alsa, oss, jack, portaudio]")
SET (DefaultInput oss CACHE STRING
"Default Input module: [null, alsa, oss, jack]")
else()
SET (DefaultOutput null CACHE STRING
"Default Output module: [null, alsa, oss, jack, portaudio]")
SET (DefaultInput null CACHE STRING
"Default Input module: [null, alsa, oss, jack]")
endif()



if (GuiModule STREQUAL qt AND QT_FOUND)
set (QtGui TRUE)
elseif(GuiModule STREQUAL ntk AND NTK_FOUND)
set (NtkGui TRUE)
elseif(GuiModule STREQUAL fltk AND FLTK_FOUND)
set (FltkGui TRUE)
elseif(GuiModule STREQUAL off)
add_definitions(-DDISABLE_GUI)
else ()
set (GuiModule off CACHE STRING "GUI module, either fltk, qt or off")
add_definitions(-DDISABLE_GUI)
message(STATUS "GUI module defaulting to off")
endif()


#Build Flags
option (BuildForAMD_X86_64 "Build for AMD x86_64 system" OFF)
option (BuildForCore2_X86_64 "Build for Intel Core2 x86_64 system" OFF)
option (BuildForDebug "Include gdb debugging support" OFF)

set(CMAKE_BUILD_TYPE "Release")

set (BuildOptions_x86_64AMD
"-O3 -march=athlon64 -m64 -Wall -ffast-math -fno-finite-math-only -fomit-frame-pointer"
CACHE STRING "X86_64 compiler options"
)

set (BuildOptions_X86_64Core2
"-O3 -march=core2 -m64 -Wall -ffast-math -fno-finite-math-only -fomit-frame-pointer"
CACHE STRING "X86_64 compiler options"
)

set (BuildOptionsBasic
"-O3 -msse -msse2 -mfpmath=sse -ffast-math -fomit-frame-pointer"
CACHE STRING "basic X86 complier options"
)

set (BuildOptionsDebug
"-O0 -g3 -ggdb -Wall -Wpointer-arith" CACHE STRING "Debug build flags")

########### Settings dependant code ###########
# From here on, the setting variables have been prepared so concentrate
# on the actual compiling.

if(AlsaEnable)
list(APPEND AUDIO_LIBRARIES ${ASOUND_LIBRARY})
list(APPEND AUDIO_LIBRARY_DIRS ${ASOUND_LIBRARY_DIRS})
add_definitions(-DALSA=1)
endif(AlsaEnable)

if(JackEnable)
list(APPEND AUDIO_LIBRARIES ${JACK_LIBRARIES})
list(APPEND AUDIO_LIBRARY_DIRS ${JACK_LIBRARY_DIRS})
add_definitions(-DJACK=1)
endif(JackEnable)

if(OssEnable)
add_definitions(-DOSS=1)
endif(OssEnable)

if(PaEnable)
include_directories(${PORTAUDIO_INCLUDE_DIR})
add_definitions(-DPORTAUDIO=1)
list(APPEND AUDIO_LIBRARIES ${PORTAUDIO_LIBRARIES})
list(APPEND AUDIO_LIBRARY_DIRS ${PORTAUDIO_LIBRARY_DIRS})
endif()

if (CompileTests)
ENABLE_TESTING()
endif()

if(LashEnable)
include_directories(${LASH_INCLUDE_DIRS})
add_definitions(-DLASH=1)
list(APPEND AUDIO_LIBRARIES ${LASH_LIBRARIES})
list(APPEND AUDIO_LIBRARY_DIRS ${LASH_LIBRARY_DIRS})
message(STATUS "Compiling with lash")
endif()
if(LibloEnable)
include_directories(${LIBLO_INCLUDE_DIRS})
add_definitions(-DUSE_NSM=1)
list(APPEND AUDIO_LIBRARIES ${LIBLO_LIBRARIES})
list(APPEND AUDIO_LIBRARY_DIRS ${LIBLO_LIBRARY_DIRS})
message(STATUS "Compiling with liblo")
endif()

# other include directories
include_directories(${ZLIB_INCLUDE_DIRS} ${MXML_INCLUDE_DIRS})

add_definitions(
-g #TODO #todo put in a better location
-Wall
-Wextra
)
if(NOT AVOID_ASM)
message(STATUS "Compiling with x86 opcode support")
add_definitions(-DASM_F2I_YES)
endif()

if (BuildForDebug)
set (CMAKE_BUILD_TYPE "Debug")
set (CMAKE_CXX_FLAGS_DEBUG ${BuildOptionsDebug})
message (STATUS "Building for ${CMAKE_BUILD_TYPE}, flags: ${CMAKE_CXX_FLAGS_DEBUG}")
else (BuildForDebug)
set (CMAKE_BUILD_TYPE "Release")
if (BuildForAMD_X86_64)
set (CMAKE_CXX_FLAGS_RELEASE ${BuildOptions_x86_64AMD})
else (BuildForAMD_X86_64)
if (BuildForCore2_X86_64)
set (CMAKE_CXX_FLAGS_RELEASE ${BuildOptions_X86_64Core2})
else (BuildForCore2_X86_64)
set (CMAKE_CXX_FLAGS_RELEASE ${BuildOptionsBasic})
endif (BuildForCore2_X86_64)
endif (BuildForAMD_X86_64)
message (STATUS "Building for ${CMAKE_BUILD_TYPE}, flags: ${CMAKE_CXX_FLAGS_RELEASE}")
endif (BuildForDebug)

add_definitions(-fPIC)

if(FLTK_FOUND)
mark_as_advanced(FORCE FLTK_BASE_LIBRARY)
mark_as_advanced(FORCE FLTK_CONFIG_SCRIPT)
mark_as_advanced(FORCE FLTK_DIR)
mark_as_advanced(FORCE FLTK_FLUID_EXECUTABLE)
mark_as_advanced(FORCE FLTK_FORMS_LIBRARY)
mark_as_advanced(FORCE FLTK_GL_LIBRARY)
mark_as_advanced(FORCE FLTK_IMAGES_LIBRARY)
mark_as_advanced(FORCE FLTK_INCLUDE_DIR)
mark_as_advanced(FORCE FLTK_MATH_LIBRARY)
endif(FLTK_FOUND)

if(NTK_FOUND)
mark_as_advanced(FORCE NTK_BASE_LIBRARY)
mark_as_advanced(FORCE NTK_CONFIG_SCRIPT)
mark_as_advanced(FORCE NTK_DIR)
mark_as_advanced(FORCE FLTK_FLUID_EXECUTABLE)
mark_as_advanced(FORCE NTK_FORMS_LIBRARY)
mark_as_advanced(FORCE NTK_GL_LIBRARY)
mark_as_advanced(FORCE NTK_IMAGES_LIBRARY)
mark_as_advanced(FORCE NTK_INCLUDE_DIR)
mark_as_advanced(FORCE NTK_MATH_LIBRARY)
endif(NTK_FOUND)

if(FltkGui)
#UGLY WORKAROUND
find_program (FLTK_CONFIG fltk-config)
if (FLTK_CONFIG)
execute_process (COMMAND ${FLTK_CONFIG} --use-images --ldflags OUTPUT_VARIABLE FLTK_LDFLAGS)
string(STRIP ${FLTK_LDFLAGS} FLTK_LIBRARIES)
endif()

message(STATUS ${FLTK_LDFLAGS})


set(GUI_LIBRARIES ${FLTK_LIBRARIES} ${FLTK_LIBRARIES} ${OPENGL_LIBRARIES} zynaddsubfx_gui)

add_definitions(-DFLTK_GUI)
message(STATUS "Will build FLTK gui")

include_directories(
${FLTK_INCLUDE_DIR}
"${CMAKE_CURRENT_SOURCE_DIR}/UI"
"${CMAKE_CURRENT_BINARY_DIR}/UI"
)

add_subdirectory(UI)
endif()

if(NtkGui)

find_program( FLTK_FLUID_EXECUTABLE ntk-fluid)
message(STATUS ${NTK_LDFLAGS} ${NTK_IMAGES_LDFLAGS})

set(GUI_LIBRARIES ${NTK_LIBRARIES} ${NTK_IMAGES_LIBRARIES} ${OPENGL_LIBRARIES} zynaddsubfx_gui)

add_definitions(-DNTK_GUI)

message(STATUS "Will build NTK gui")

include_directories(
${NTK_INCLUDE_DIRS}
"${CMAKE_CURRENT_SOURCE_DIR}/UI"
"${CMAKE_CURRENT_BINARY_DIR}/UI"
)

add_subdirectory(UI)
endif()

########### General section ##############
# Following this should be only general compilation code, and no mention
# of module-specific variables

link_directories(${AUDIO_LIBRARY_DIRS} ${ZLIB_LIBRARY_DIRS} ${FFTW_LIBRARY_DIRS} ${MXML_LIBRARY_DIRS} ${FLTK_LIBRARY_DIRS} ${NTK_LIBRARY_DIRS})

include_directories(
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_BINARY_DIR}
)



set(NONGUI_LIBRARIES
zynaddsubfx_misc
zynaddsubfx_synth
zynaddsubfx_effect
zynaddsubfx_params
zynaddsubfx_dsp
zynaddsubfx_nio
)

add_subdirectory(Misc)
add_subdirectory(Synth)
add_subdirectory(Effects)
add_subdirectory(Params)
add_subdirectory(DSP)
add_subdirectory(Nio)

add_library(zynaddsubfx_core STATIC
${zynaddsubfx_dsp_SRCS}
${zynaddsubfx_effect_SRCS}
${zynaddsubfx_misc_SRCS}
${zynaddsubfx_params_SRCS}
${zynaddsubfx_synth_SRCS}
)

target_link_libraries(zynaddsubfx_core
${ZLIB_LIBRARIES}
${FFTW_LIBRARIES}
${MXML_LIBRARIES}
${OS_LIBRARIES}
pthread)

if(CompileTests)
add_subdirectory(Tests)
endif(CompileTests)

message(STATUS "using link directories: ${AUDIO_LIBRARY_DIRS} ${ZLIB_LIBRARY_DIRS} ${FFTW_LIBRARY_DIRS} ${MXML_LIBRARY_DIRS} ${FLTK_LIBRARY_DIRS}")


add_executable(zynaddsubfx main.cpp)

target_link_libraries(zynaddsubfx
zynaddsubfx_core
zynaddsubfx_nio
${GUI_LIBRARIES}
${NIO_LIBRARIES}
${AUDIO_LIBRARIES}
)

if (DssiEnable)
add_library(zynaddsubfx_dssi SHARED
Output/DSSIaudiooutput.cpp
)

target_link_libraries(zynaddsubfx_dssi
zynaddsubfx_core
${OS_LIBRARIES}
)
if (${CMAKE_SIZEOF_VOID_P} EQUAL "8")
install(TARGETS zynaddsubfx_dssi LIBRARY DESTINATION lib64/dssi/)
else ()
install(TARGETS zynaddsubfx_dssi LIBRARY DESTINATION lib/dssi/)
endif ()
endif()

message(STATUS "Link libraries: ${ZLIB_LIBRARY} ${FFTW_LIBRARY} ${MXML_LIBRARIES} ${AUDIO_LIBRARIES} ${OS_LIBRARIES}")
install(TARGETS zynaddsubfx
RUNTIME DESTINATION bin
)

if(NtkGui)
install(DIRECTORY ../pixmaps DESTINATION share/zynaddsubfx)
add_definitions(-DPIXMAP_PATH="${CMAKE_INSTALL_PREFIX}/share/zynaddsubfx/pixmaps/")
add_definitions(-DSOURCE_DIR="${CMAKE_CURRENT_SOURCE_DIR}")
endif(NtkGui)

include(CTest)

+ 0
- 2
source/native-plugins/zynaddsubfx/DSP/AnalogFilter.cpp View File

@@ -213,7 +213,6 @@ void AnalogFilter::computefiltercoefs(void)
case 7: //Low Shelf - 2 poles
if(!zerocoefs) {
tmpq = sqrtf(tmpq);
alpha = sn / (2.0f * tmpq);
beta = sqrtf(tmpgain) / tmpq;
tmp = (tmpgain + 1.0f) + (tmpgain - 1.0f) * cs + beta * sn;

@@ -239,7 +238,6 @@ void AnalogFilter::computefiltercoefs(void)
case 8: //High Shelf - 2 poles
if(!zerocoefs) {
tmpq = sqrtf(tmpq);
alpha = sn / (2.0f * tmpq);
beta = sqrtf(tmpgain) / tmpq;
tmp = (tmpgain + 1.0f) - (tmpgain - 1.0f) * cs + beta * sn;



+ 6
- 4
source/native-plugins/zynaddsubfx/DSP/AnalogFilter.h View File

@@ -49,16 +49,18 @@ class AnalogFilter:public Filter

float H(float freq); //Obtains the response for a given frequency


struct Coeff {
float c[3], //Feed Forward
d[3]; //Feed Back
} coeff, oldCoeff;

private:
struct fstage {
float x1, x2; //Input History
float y1, y2; //Output History
} history[MAX_FILTER_STAGES + 1], oldHistory[MAX_FILTER_STAGES + 1];

struct Coeff {
float c[3], //Feed Forward
d[3]; //Feed Back
} coeff, oldCoeff;
//old coeffs are used for interpolation when paremeters change quickly

//Apply IIR filter to Samples, with coefficients, and past history


+ 17
- 0
source/native-plugins/zynaddsubfx/DSP/FFTwrapper.cpp View File

@@ -23,13 +23,24 @@
#include <cmath>
#include <cassert>
#include <cstring>
#include <pthread.h>
#include "FFTwrapper.h"

static pthread_mutex_t *mutex = NULL;

FFTwrapper::FFTwrapper(int fftsize_)
{
//first one will spawn the mutex (yeah this may be a race itself)
if(!mutex) {
mutex = new pthread_mutex_t;
pthread_mutex_init(mutex, NULL);
}


fftsize = fftsize_;
time = new fftw_real[fftsize];
fft = new fftw_complex[fftsize + 1];
pthread_mutex_lock(mutex);
planfftw = fftw_plan_dft_r2c_1d(fftsize,
time,
fft,
@@ -38,12 +49,15 @@ FFTwrapper::FFTwrapper(int fftsize_)
fft,
time,
FFTW_ESTIMATE);
pthread_mutex_unlock(mutex);
}

FFTwrapper::~FFTwrapper()
{
pthread_mutex_lock(mutex);
fftw_destroy_plan(planfftw);
fftw_destroy_plan(planfftw_inv);
pthread_mutex_unlock(mutex);

delete [] time;
delete [] fft;
@@ -82,4 +96,7 @@ void FFTwrapper::freqs2smps(const fft_t *freqs, float *smps)
void FFT_cleanup()
{
fftw_cleanup();
pthread_mutex_destroy(mutex);
delete mutex;
mutex = NULL;
}

+ 20
- 2
source/native-plugins/zynaddsubfx/DSP/FFTwrapper.h View File

@@ -24,8 +24,7 @@
#define FFT_WRAPPER_H
#include <fftw3.h>
#include <complex>
typedef double fftw_real;
typedef std::complex<fftw_real> fft_t;
#include "../globals.h"

/**A wrapper for the FFTW library (Fast Fourier Transforms)*/
class FFTwrapper
@@ -48,5 +47,24 @@ class FFTwrapper
fftw_plan planfftw, planfftw_inv;
};

/*
* The "std::polar" template has no clear definition for the range of
* the input parameters, and some C++ standard library implementations
* don't accept negative amplitude among others. Define our own
* FFTpolar template, which works like we expect it to.
*/
template<class _Tp>
std::complex<_Tp>
FFTpolar(const _Tp& __rho, const _Tp& __theta = _Tp(0))
{
_Tp __x = __rho * cos(__theta);
if (isnan(__x))
__x = 0;
_Tp __y = __rho * sin(__theta);
if (isnan(__y))
__y = 0;
return std::complex<_Tp>(__x, __y);
}

void FFT_cleanup();
#endif

+ 11
- 10
source/native-plugins/zynaddsubfx/DSP/Filter.cpp View File

@@ -20,14 +20,16 @@

*/

#include <math.h>
#include <stdio.h>
#include <cmath>
#include <cstdio>
#include <cassert>

#include "Filter.h"
#include "AnalogFilter.h"
#include "FormantFilter.h"
#include "SVFilter.h"
#include "../Params/FilterParams.h"
#include "../Misc/Allocator.h"

Filter::Filter(unsigned int srate, int bufsize)
: outgain(1.0f),
@@ -37,12 +39,11 @@ Filter::Filter(unsigned int srate, int bufsize)
alias();
}

Filter *Filter::generate(FilterParams *pars, unsigned int srate, int bufsize)
Filter *Filter::generate(Allocator &memory, FilterParams *pars,
unsigned int srate, int bufsize)
{
if (srate == 0)
srate = synth->samplerate;
if (bufsize == 0)
bufsize = synth->buffersize;
assert(srate != 0);
assert(bufsize != 0);

unsigned char Ftype = pars->Ptype;
unsigned char Fstages = pars->Pstages;
@@ -50,16 +51,16 @@ Filter *Filter::generate(FilterParams *pars, unsigned int srate, int bufsize)
Filter *filter;
switch(pars->Pcategory) {
case 1:
filter = new FormantFilter(pars, srate, bufsize);
filter = memory.alloc<FormantFilter>(pars, &memory, srate, bufsize);
break;
case 2:
filter = new SVFilter(Ftype, 1000.0f, pars->getq(), Fstages, srate, bufsize);
filter = memory.alloc<SVFilter>(Ftype, 1000.0f, pars->getq(), Fstages, srate, bufsize);
filter->outgain = dB2rap(pars->getgain());
if(filter->outgain > 1.0f)
filter->outgain = sqrt(filter->outgain);
break;
default:
filter = new AnalogFilter(Ftype, 1000.0f, pars->getq(), Fstages, srate, bufsize);
filter = memory.alloc<AnalogFilter>(Ftype, 1000.0f, pars->getq(), Fstages, srate, bufsize);
if((Ftype >= 6) && (Ftype <= 8))
filter->setgain(pars->getgain());
else


+ 2
- 1
source/native-plugins/zynaddsubfx/DSP/Filter.h View File

@@ -29,7 +29,8 @@ class Filter
{
public:
static float getrealfreq(float freqpitch);
static Filter *generate(class FilterParams * pars, unsigned int srate = 0, int bufsize = 0);
static Filter *generate(class Allocator &memory, class FilterParams *pars,
unsigned int srate, int bufsize);

Filter(unsigned int srate, int bufsize);
virtual ~Filter() {}


+ 5
- 4
source/native-plugins/zynaddsubfx/DSP/FormantFilter.cpp View File

@@ -23,16 +23,17 @@
#include <cmath>
#include <cstdio>
#include "../Misc/Util.h"
#include "../Misc/Allocator.h"
#include "FormantFilter.h"
#include "AnalogFilter.h"
#include "../Params/FilterParams.h"

FormantFilter::FormantFilter(FilterParams *pars, unsigned int srate, int bufsize)
: Filter(srate, bufsize)
FormantFilter::FormantFilter(FilterParams *pars, Allocator *alloc, unsigned int srate, int bufsize)
: Filter(srate, bufsize), memory(*alloc)
{
numformants = pars->Pnumformants;
for(int i = 0; i < numformants; ++i)
formant[i] = new AnalogFilter(4 /*BPF*/, 1000.0f, 10.0f, pars->Pstages, srate, bufsize);
formant[i] = memory.alloc<AnalogFilter>(4 /*BPF*/, 1000.0f, 10.0f, pars->Pstages, srate, bufsize);
cleanup();

for(int j = 0; j < FF_MAX_VOWELS; ++j)
@@ -78,7 +79,7 @@ FormantFilter::FormantFilter(FilterParams *pars, unsigned int srate, int bufsize
FormantFilter::~FormantFilter()
{
for(int i = 0; i < numformants; ++i)
delete (formant[i]);
memory.dealloc(formant[i]);
}

void FormantFilter::cleanup()


+ 3
- 1
source/native-plugins/zynaddsubfx/DSP/FormantFilter.h View File

@@ -27,10 +27,11 @@
#include "Filter.h"


class Allocator;
class FormantFilter:public Filter
{
public:
FormantFilter(class FilterParams *pars, unsigned int srate, int bufsize);
FormantFilter(class FilterParams *pars, Allocator *alloc, unsigned int srate, int bufsize);
~FormantFilter();
void filterout(float *smp);
void setfreq(float frequency);
@@ -61,6 +62,7 @@ class FormantFilter:public Filter
float oldinput, slowinput;
float Qfactor, formantslowness, oldQfactor;
float vowelclearness, sequencestretch;
Allocator &memory;
};

#endif

+ 0
- 2
source/native-plugins/zynaddsubfx/DSP/SVFilter.cpp View File

@@ -24,9 +24,7 @@
#include <cstdio>
#include <cstring>
#include <cassert>
#ifndef CARLA_OS_WIN
#include <err.h>
#endif
#include "../Misc/Util.h"
#include "SVFilter.h"



+ 9
- 10
source/native-plugins/zynaddsubfx/DSP/Unison.cpp View File

@@ -21,13 +21,12 @@

#include <cmath>
#include <cstring>
#ifndef CARLA_OS_WIN
#include <err.h>
#endif

#include "../Misc/Allocator.h"
#include "Unison.h"

Unison::Unison(int update_period_samples_, float max_delay_sec_, float srate_f)
Unison::Unison(Allocator *alloc_, int update_period_samples_, float max_delay_sec_, float srate_f)
:unison_size(0),
base_freq(1.0f),
uv(NULL),
@@ -39,18 +38,19 @@ Unison::Unison(int update_period_samples_, float max_delay_sec_, float srate_f)
delay_buffer(NULL),
unison_amplitude_samples(0.0f),
unison_bandwidth_cents(10.0f),
samplerate_f(srate_f)
samplerate_f(srate_f),
alloc(*alloc_)
{
if(max_delay < 10)
max_delay = 10;
delay_buffer = new float[max_delay];
delay_buffer = alloc.valloc<float>(max_delay);
memset(delay_buffer, 0, max_delay * sizeof(float));
setSize(1);
}

Unison::~Unison() {
delete [] delay_buffer;
delete [] uv;
alloc.devalloc(delay_buffer);
alloc.devalloc(uv);
}

void Unison::setSize(int new_size)
@@ -58,9 +58,8 @@ void Unison::setSize(int new_size)
if(new_size < 1)
new_size = 1;
unison_size = new_size;
if(uv)
delete [] uv;
uv = new UnisonVoice[unison_size];
alloc.devalloc(uv);
uv = alloc.valloc<UnisonVoice>(unison_size);
first_time = true;
updateParameters();
}


+ 3
- 1
source/native-plugins/zynaddsubfx/DSP/Unison.h View File

@@ -26,11 +26,12 @@

//how much the unison frequencies varies (always >= 1.0)
#define UNISON_FREQ_SPAN 2.0f
class Allocator;

class Unison
{
public:
Unison(int update_period_samples_, float max_delay_sec_, float srate_f);
Unison(Allocator *alloc_, int update_period_samples_, float max_delay_sec_, float srate_f);
~Unison();

void setSize(int new_size);
@@ -72,5 +73,6 @@ class Unison

// current setup
float samplerate_f;
Allocator &alloc;
};
#endif

+ 12
- 13
source/native-plugins/zynaddsubfx/Effects/Alienwah.cpp View File

@@ -21,11 +21,14 @@
*/

#include <cmath>
#include "../Misc/Allocator.h"
#include "Alienwah.h"

Alienwah::Alienwah(bool insertion_, float *efxoutl_, float *efxoutr_, unsigned int srate, int bufsize)
:Effect(insertion_, efxoutl_, efxoutr_, NULL, 0, srate, bufsize),
lfo(srate, bufsize),
using std::complex;

Alienwah::Alienwah(EffectParams pars)
:Effect(pars),
lfo(pars.srate, pars.bufsize),
oldl(NULL),
oldr(NULL)
{
@@ -37,10 +40,8 @@ Alienwah::Alienwah(bool insertion_, float *efxoutl_, float *efxoutr_, unsigned i

Alienwah::~Alienwah()
{
if(oldl != NULL)
delete [] oldl;
if(oldr != NULL)
delete [] oldr;
memory.devalloc(oldl);
memory.devalloc(oldr);
}


@@ -139,13 +140,11 @@ void Alienwah::setphase(unsigned char _Pphase)

void Alienwah::setdelay(unsigned char _Pdelay)
{
if(oldl != NULL)
delete [] oldl;
if(oldr != NULL)
delete [] oldr;
memory.devalloc(oldl);
memory.devalloc(oldr);
Pdelay = (_Pdelay >= MAX_ALIENWAH_DELAY) ? MAX_ALIENWAH_DELAY : _Pdelay;
oldl = new complex<float>[Pdelay];
oldr = new complex<float>[Pdelay];
oldl = memory.valloc<complex<float>>(Pdelay);
oldr = memory.valloc<complex<float>>(Pdelay);
cleanup();
}



+ 4
- 16
source/native-plugins/zynaddsubfx/Effects/Alienwah.h View File

@@ -23,11 +23,9 @@
#ifndef ALIENWAH_H
#define ALIENWAH_H

#include <complex>
#include "Effect.h"
#include "EffectLFO.h"

using namespace std;
#include <complex>

#define MAX_ALIENWAH_DELAY 100

@@ -35,17 +33,7 @@ using namespace std;
class Alienwah:public Effect
{
public:
/**
* Constructor
* @param insertion_ true for insertion Effect
* @param efxoutl_ Pointer to Alienwah's left channel output buffer
* @param efxoutr_ Pointer to Alienwah's left channel output buffer
* @return Initialized Alienwah
*/
Alienwah(bool insertion_,
float *const efxoutl_,
float *const efxoutr_,
unsigned int srate, int bufsize);
Alienwah(EffectParams pars);
~Alienwah();
void out(const Stereo<float *> &smp);

@@ -73,8 +61,8 @@ class Alienwah:public Effect

//Internal Values
float fb, depth, phase;
complex<float> *oldl, *oldr;
complex<float> oldclfol, oldclfor;
std::complex<float> *oldl, *oldr;
std::complex<float> oldclfol, oldclfor;
int oldk;
};



+ 7
- 6
source/native-plugins/zynaddsubfx/Effects/Chorus.cpp View File

@@ -21,16 +21,17 @@
*/

#include <cmath>
#include "../Misc/Allocator.h"
#include "Chorus.h"
#include <iostream>

using namespace std;

Chorus::Chorus(bool insertion_, float *const efxoutl_, float *efxoutr_, unsigned int srate, int bufsize)
:Effect(insertion_, efxoutl_, efxoutr_, NULL, 0, srate, bufsize),
lfo(srate, bufsize),
Chorus::Chorus(EffectParams pars)
:Effect(pars),
lfo(pars.srate, pars.bufsize),
maxdelay((int)(MAX_CHORUS_DELAY / 1000.0f * samplerate_f)),
delaySample(new float[maxdelay], new float[maxdelay])
delaySample(memory.valloc<float>(maxdelay), memory.valloc<float>(maxdelay))
{
dlk = 0;
drk = 0;
@@ -44,8 +45,8 @@ Chorus::Chorus(bool insertion_, float *const efxoutl_, float *efxoutr_, unsigned

Chorus::~Chorus()
{
delete [] delaySample.l;
delete [] delaySample.r;
memory.devalloc(delaySample.l);
memory.devalloc(delaySample.r);
}

//get the delay value in samples; xlfo is the current lfo value


+ 1
- 1
source/native-plugins/zynaddsubfx/Effects/Chorus.h View File

@@ -32,7 +32,7 @@
class Chorus:public Effect
{
public:
Chorus(bool insertion_, float *efxoutl_, float *efxoutr_, unsigned int srate, int bufsize);
Chorus(EffectParams pars);
/**Destructor*/
~Chorus();
void out(const Stereo<float *> &input);


+ 13
- 12
source/native-plugins/zynaddsubfx/Effects/Distorsion.cpp View File

@@ -23,10 +23,11 @@
#include "Distorsion.h"
#include "../DSP/AnalogFilter.h"
#include "../Misc/WaveShapeSmps.h"
#include "../Misc/Allocator.h"
#include <cmath>

Distorsion::Distorsion(bool insertion_, float *efxoutl_, float *efxoutr_, unsigned int srate, int bufsize)
:Effect(insertion_, efxoutl_, efxoutr_, NULL, 0, srate, bufsize),
Distorsion::Distorsion(EffectParams pars)
:Effect(pars),
Pvolume(50),
Pdrive(90),
Plevel(64),
@@ -37,20 +38,20 @@ Distorsion::Distorsion(bool insertion_, float *efxoutl_, float *efxoutr_, unsign
Pstereo(0),
Pprefiltering(0)
{
lpfl = new AnalogFilter(2, 22000, 1, 0, srate, bufsize);
lpfr = new AnalogFilter(2, 22000, 1, 0, srate, bufsize);
hpfl = new AnalogFilter(3, 20, 1, 0, srate, bufsize);
hpfr = new AnalogFilter(3, 20, 1, 0, srate, bufsize);
lpfl = memory.alloc<AnalogFilter>(2, 22000, 1, 0, pars.srate, pars.bufsize);
lpfr = memory.alloc<AnalogFilter>(2, 22000, 1, 0, pars.srate, pars.bufsize);
hpfl = memory.alloc<AnalogFilter>(3, 20, 1, 0, pars.srate, pars.bufsize);
hpfr = memory.alloc<AnalogFilter>(3, 20, 1, 0, pars.srate, pars.bufsize);
setpreset(Ppreset);
cleanup();
}

Distorsion::~Distorsion()
{
delete lpfl;
delete lpfr;
delete hpfl;
delete hpfr;
memory.dealloc(lpfl);
memory.dealloc(lpfr);
memory.dealloc(hpfl);
memory.dealloc(hpfr);
}

//Cleanup the effect
@@ -137,7 +138,7 @@ void Distorsion::setvolume(unsigned char _Pvolume)
void Distorsion::setlpf(unsigned char _Plpf)
{
Plpf = _Plpf;
float fr = expf(powf(Plpf / 127.0f, 0.5f) * logf(25000.0f)) + 40.0f;
float fr = expf(sqrtf(Plpf / 127.0f) * logf(25000.0f)) + 40.0f;
lpfl->setfreq(fr);
lpfr->setfreq(fr);
}
@@ -145,7 +146,7 @@ void Distorsion::setlpf(unsigned char _Plpf)
void Distorsion::sethpf(unsigned char _Phpf)
{
Phpf = _Phpf;
float fr = expf(powf(Phpf / 127.0f, 0.5f) * logf(25000.0f)) + 20.0f;
float fr = expf(sqrtf(Phpf / 127.0f) * logf(25000.0f)) + 20.0f;
hpfl->setfreq(fr);
hpfr->setfreq(fr);
}


+ 1
- 1
source/native-plugins/zynaddsubfx/Effects/Distorsion.h View File

@@ -29,7 +29,7 @@
class Distorsion:public Effect
{
public:
Distorsion(bool insertion, float *efxoutl_, float *efxoutr_, unsigned int srate, int bufsize);
Distorsion(EffectParams pars);
~Distorsion();
void out(const Stereo<float *> &smp);
void setpreset(unsigned char npreset);


+ 12
- 10
source/native-plugins/zynaddsubfx/Effects/DynamicFilter.cpp View File

@@ -23,10 +23,11 @@
#include <cmath>
#include "DynamicFilter.h"
#include "../DSP/Filter.h"
#include "../Misc/Allocator.h"

DynamicFilter::DynamicFilter(bool insertion_, float *efxoutl_, float *efxoutr_, unsigned int srate, int bufsize)
:Effect(insertion_, efxoutl_, efxoutr_, new FilterParams(0, 64, 64), 0, srate, bufsize),
lfo(srate, bufsize),
DynamicFilter::DynamicFilter(EffectParams pars)
:Effect(pars),
lfo(pars.srate, pars.bufsize),
Pvolume(110),
Pdepth(0),
Pampsns(90),
@@ -35,15 +36,16 @@ DynamicFilter::DynamicFilter(bool insertion_, float *efxoutl_, float *efxoutr_,
filterl(NULL),
filterr(NULL)
{
filterpars = memory.alloc<FilterParams>(0,0,0);
setpreset(Ppreset);
cleanup();
}

DynamicFilter::~DynamicFilter()
{
delete filterpars;
delete filterl;
delete filterr;
memory.dealloc(filterpars);
memory.dealloc(filterl);
memory.dealloc(filterr);
}


@@ -129,10 +131,10 @@ void DynamicFilter::setampsns(unsigned char _Pampsns)

void DynamicFilter::reinitfilter(void)
{
delete filterl;
delete filterr;
filterl = Filter::generate(filterpars, samplerate, buffersize);
filterr = Filter::generate(filterpars, samplerate, buffersize);
memory.dealloc(filterl);
memory.dealloc(filterr);
filterl = Filter::generate(memory, filterpars, samplerate, buffersize);
filterr = Filter::generate(memory, filterpars, samplerate, buffersize);
}

void DynamicFilter::setpreset(unsigned char npreset)


+ 1
- 1
source/native-plugins/zynaddsubfx/Effects/DynamicFilter.h View File

@@ -30,7 +30,7 @@
class DynamicFilter:public Effect
{
public:
DynamicFilter(bool insertion_, float *efxoutl_, float *efxoutr_, unsigned int srate, int bufsize);
DynamicFilter(EffectParams pars);
~DynamicFilter();
void out(const Stereo<float *> &smp);



+ 51
- 4
source/native-plugins/zynaddsubfx/Effects/EQ.cpp View File

@@ -23,9 +23,10 @@
#include <cmath>
#include "EQ.h"
#include "../DSP/AnalogFilter.h"
#include "../Misc/Allocator.h"

EQ::EQ(bool insertion_, float *efxoutl_, float *efxoutr_, unsigned int srate, int bufsize)
:Effect(insertion_, efxoutl_, efxoutr_, NULL, 0, srate, bufsize)
EQ::EQ(EffectParams pars)
:Effect(pars)
{
for(int i = 0; i < MAX_EQ_BANDS; ++i) {
filter[i].Ptype = 0;
@@ -33,8 +34,8 @@ EQ::EQ(bool insertion_, float *efxoutl_, float *efxoutr_, unsigned int srate, in
filter[i].Pgain = 64;
filter[i].Pq = 64;
filter[i].Pstages = 0;
filter[i].l = new AnalogFilter(6, 1000.0f, 1.0f, 0, srate, bufsize);
filter[i].r = new AnalogFilter(6, 1000.0f, 1.0f, 0, srate, bufsize);
filter[i].l = memory.alloc<AnalogFilter>(6, 1000.0f, 1.0f, 0, pars.srate, pars.bufsize);
filter[i].r = memory.alloc<AnalogFilter>(6, 1000.0f, 1.0f, 0, pars.srate, pars.bufsize);
}
//default values
Pvolume = 50;
@@ -43,6 +44,13 @@ EQ::EQ(bool insertion_, float *efxoutl_, float *efxoutr_, unsigned int srate, in
cleanup();
}

EQ::~EQ()
{
for(int i = 0; i < MAX_EQ_BANDS; ++i) {
memory.dealloc(filter[i].l);
memory.dealloc(filter[i].r);
}
}

// Cleanup the effect
void EQ::cleanup(void)
@@ -196,3 +204,42 @@ float EQ::getfreqresponse(float freq)
}
return rap2dB(resp * outvolume);
}

//full taps & three filter taps
static void convolve(float *a, const float *filter)
{
float tmp[MAX_EQ_BANDS*2+1];

for(int i=0; i<MAX_EQ_BANDS*2+1; ++i)
tmp[i] = a[i];

//Base Case
a[0] = tmp[0]*filter[0];
a[1] = tmp[0]*filter[1] + tmp[1]*filter[0];

for(int i=2; i<MAX_EQ_BANDS+1; ++i) {
a[i] = filter[0]*tmp[i] +
filter[1]*tmp[i-1] +
filter[2]*tmp[i-2];
}
}

//Not exactly the most efficient manner to derive the total taps, but it should
//be fast enough in practice
void EQ::getFilter(float *a, float *b) const
{
a[0] = 1;
b[0] = 1;
for(int i = 0; i < MAX_EQ_BANDS; ++i) {
auto &F = filter[i];
if(F.Ptype == 0)
continue;
float Fb[3] = {F.l->coeff.c[0], F.l->coeff.c[1], F.l->coeff.c[2]};
float Fa[3] = {1.0f, -F.l->coeff.d[1], -F.l->coeff.d[2]};

for(int j=0; j<F.Pstages+1; ++j) {
convolve(b, Fb);
convolve(a, Fa);
}
}
}

+ 11
- 3
source/native-plugins/zynaddsubfx/Effects/EQ.h View File

@@ -29,8 +29,8 @@
class EQ:public Effect
{
public:
EQ(bool insertion_, float *efxoutl_, float *efxoutr_, unsigned int srate, int bufsize);
~EQ() {}
EQ(EffectParams pars);
~EQ();
void out(const Stereo<float *> &smp);
void setpreset(unsigned char npreset);
void changepar(int npar, unsigned char value);
@@ -38,6 +38,8 @@ class EQ:public Effect
void cleanup(void);
float getfreqresponse(float freq);

void getFilter(float *a/*[MAX_EQ_BANDS*MAX_FILTER_STAGES*2+1]*/,
float *b/*[MAX_EQ_BANDS*MAX_FILTER_STAGES*2+1]*/) const;
private:
//Parameters
unsigned char Pvolume;
@@ -48,7 +50,13 @@ class EQ:public Effect
//parameters
unsigned char Ptype, Pfreq, Pgain, Pq, Pstages;
//internal values
class AnalogFilter * l, *r;
/* TODO
* The analog filters here really ought to be dumbed down some as
* you are just looking to do a batch convolution in the end
* Perhaps some static functions to do the filter design?
*/
class AnalogFilter *l, *r;
} filter[MAX_EQ_BANDS];
};



+ 8
- 6
source/native-plugins/zynaddsubfx/Effects/Echo.cpp View File

@@ -23,12 +23,13 @@
*/

#include <cmath>
#include "../Misc/Allocator.h"
#include "Echo.h"

#define MAX_DELAY 2

Echo::Echo(bool insertion_, float *efxoutl_, float *efxoutr_, unsigned int srate, int bufsize)
:Effect(insertion_, efxoutl_, efxoutr_, NULL, 0, srate, bufsize),
Echo::Echo(EffectParams pars)
:Effect(pars),
Pvolume(50),
Pdelay(60),
Plrdelay(100),
@@ -37,8 +38,8 @@ Echo::Echo(bool insertion_, float *efxoutl_, float *efxoutr_, unsigned int srate
delayTime(1),
lrdelay(0),
avgDelay(0),
delay(new float[(int)(MAX_DELAY * srate)],
new float[(int)(MAX_DELAY * srate)]),
delay(memory.valloc<float>(MAX_DELAY * pars.srate),
memory.valloc<float>(MAX_DELAY * pars.srate)),
old(0.0f),
pos(0),
delta(1),
@@ -50,8 +51,8 @@ Echo::Echo(bool insertion_, float *efxoutl_, float *efxoutr_, unsigned int srate

Echo::~Echo()
{
delete[] delay.l;
delete[] delay.r;
memory.devalloc(delay.l);
memory.devalloc(delay.r);
}

//Cleanup the effect
@@ -79,6 +80,7 @@ void Echo::initdelays(void)

ndelta.l = max(1, (int) (dl * samplerate));
ndelta.r = max(1, (int) (dr * samplerate));
delta = ndelta;
}

//Effect output


+ 1
- 1
source/native-plugins/zynaddsubfx/Effects/Echo.h View File

@@ -30,7 +30,7 @@
class Echo:public Effect
{
public:
Echo(bool insertion_, float *efxoutl_, float *efxoutr_, unsigned int srate, int bufsize);
Echo(EffectParams pars);
~Echo();

void out(const Stereo<float *> &input);


+ 14
- 10
source/native-plugins/zynaddsubfx/Effects/Effect.cpp View File

@@ -25,16 +25,20 @@
#include "../Params/FilterParams.h"
#include <cmath>

Effect::Effect(bool insertion_, float *efxoutl_, float *efxoutr_,
FilterParams *filterpars_, unsigned char Ppreset_,
unsigned int srate, int bufsize)
:Ppreset(Ppreset_),
efxoutl(efxoutl_),
efxoutr(efxoutr_),
filterpars(filterpars_),
insertion(insertion_),
samplerate(srate),
buffersize(bufsize)
EffectParams::EffectParams(Allocator &alloc_, bool insertion_, float *efxoutl_, float *efxoutr_,
unsigned char Ppreset_, unsigned int srate_, int bufsize_, FilterParams *filterpars_)
:alloc(alloc_), insertion(insertion_), efxoutl(efxoutl_), efxoutr(efxoutr_),
Ppreset(Ppreset_), srate(srate_), bufsize(bufsize_), filterpars(filterpars_)
{}
Effect::Effect(EffectParams pars)
:Ppreset(pars.Ppreset),
efxoutl(pars.efxoutl),
efxoutr(pars.efxoutr),
filterpars(pars.filterpars),
insertion(pars.insertion),
memory(pars.alloc),
samplerate(pars.srate),
buffersize(pars.bufsize)
{
alias();
}


+ 30
- 11
source/native-plugins/zynaddsubfx/Effects/Effect.h View File

@@ -29,22 +29,38 @@
#include "../Misc/Stereo.h"

class FilterParams;
class Allocator;

struct EffectParams
{
/**
* Effect Parameter Constructor
* @param alloc Realtime Memory Allocator
* @param insertion_ 1 when it is an insertion Effect
* @param efxoutl_ Effect output buffer Left channel
* @param efxoutr_ Effect output buffer Right channel
* @param filterpars_ pointer to FilterParams array
* @param Ppreset_ chosen preset
* @return Initialized Effect Parameter object*/
EffectParams(Allocator &alloc_, bool insertion_, float *efxoutl_, float *efxoutr_,
unsigned char Ppreset_, unsigned int srate, int bufsize, FilterParams *filterpars_=0);


Allocator &alloc;
bool insertion;
float *efxoutl;
float *efxoutr;
unsigned char Ppreset;
unsigned int srate;
int bufsize;
FilterParams *filterpars;
};

/**this class is inherited by the all effects(Reverb, Echo, ..)*/
class Effect
{
public:
/**
* Effect Constructor
* @param insertion_ 1 when it is an insertion Effect
* @param efxoutl_ Effect output buffer Left channel
* @param efxoutr_ Effect output buffer Right channel
* @param filterpars_ pointer to FilterParams array
* @param Ppreset_ chosen preset
* @return Initialized Effect object*/
Effect(bool insertion_, float *efxoutl_, float *efxoutr_,
FilterParams *filterpars_, unsigned char Ppreset_,
unsigned int srate, int bufsize);
Effect(EffectParams pars);
virtual ~Effect() {}
/**
* Choose a preset
@@ -102,6 +118,9 @@ class Effect
char Plrcross; // L/R mix
float lrcross;

//Allocator
Allocator &memory;

// current setup
unsigned int samplerate;
int buffersize;


+ 171
- 53
source/native-plugins/zynaddsubfx/Effects/EffectMgr.cpp View File

@@ -20,6 +20,10 @@

*/

#include <rtosc/ports.h>
#include <rtosc/port-sugar.h>


#include "EffectMgr.h"
#include "Effect.h"
#include "Reverb.h"
@@ -29,31 +33,103 @@
#include "EQ.h"
#include "DynamicFilter.h"
#include "../Misc/XMLwrapper.h"
#include "../Misc/Util.h"
#include "../Params/FilterParams.h"

#include <iostream>
using namespace std;

EffectMgr::EffectMgr(const bool insertion_, pthread_mutex_t *mutex_)
#include "../Misc/Allocator.h"


#define rObject EffectMgr
static const rtosc::Ports local_ports = {
rSelf(EffectMgr),
rPaste,
rRecurp(filterpars, "Filter Parameter for Dynamic Filter"),
{"parameter#64::i", rProp(alias) rDoc("Parameter Accessor"), NULL,
[](const char *msg, rtosc::RtData &d)
{
EffectMgr *eff = (EffectMgr*)d.obj;
const char *mm = msg;
while(!isdigit(*mm))++mm;

if(!rtosc_narguments(msg))
d.reply(d.loc, "i", eff->geteffectparrt(atoi(mm)));
else
eff->seteffectparrt(atoi(mm), rtosc_argument(msg, 0).i);
}},
{"preset::i", rProp(alias) rDoc("Effect Preset Selector"), NULL,
[](const char *msg, rtosc::RtData &d)
{
EffectMgr *eff = (EffectMgr*)d.obj;
if(!rtosc_narguments(msg))
d.reply(d.loc, "i", eff->getpreset());
else
eff->changepresetrt(rtosc_argument(msg, 0).i);
}},
{"eq-coeffs:", rProp(internal) rDoc("Get equalizer Coefficients"), NULL,
[](const char *, rtosc::RtData &d)
{
EffectMgr *eff = (EffectMgr*)d.obj;
if(eff->nefx != 7)
return;
EQ *eq = (EQ*)eff->efx;
float a[MAX_EQ_BANDS*MAX_FILTER_STAGES*2+1];
float b[MAX_EQ_BANDS*MAX_FILTER_STAGES*2+1];
memset(a, 0, sizeof(a));
memset(b, 0, sizeof(b));
eq->getFilter(a,b);
d.reply(d.loc, "bb", sizeof(a), a, sizeof(b), b);
}},
{"efftype::i", rDoc("Get Effect Type"), NULL, [](const char *m, rtosc::RtData &d)
{
EffectMgr *eff = (EffectMgr*)d.obj;
if(rtosc_narguments(m))
eff->changeeffectrt(rtosc_argument(m,0).i);
else
d.reply(d.loc, "i", eff->nefx);
}},
{"efftype:b", rProp(internal) rDoc("Pointer swap EffectMgr"), NULL,
[](const char *msg, rtosc::RtData &d)
{
printf("OBSOLETE METHOD CALLED\n");
EffectMgr *eff = (EffectMgr*)d.obj;
EffectMgr *eff_ = *(EffectMgr**)rtosc_argument(msg,0).b.data;

//Lets trade data
std::swap(eff->nefx,eff_->nefx);
std::swap(eff->efx,eff_->efx);
std::swap(eff->filterpars,eff_->filterpars);
std::swap(eff->efxoutl, eff_->efxoutl);
std::swap(eff->efxoutr, eff_->efxoutr);

//Return the old data for distruction
d.reply("/free", "sb", "EffectMgr", sizeof(EffectMgr*), &eff_);
}},

};

const rtosc::Ports &EffectMgr::ports = local_ports;

EffectMgr::EffectMgr(Allocator &alloc, const SYNTH_T &synth_, const bool insertion_)
:insertion(insertion_),
efxoutl(new float[synth->buffersize]),
efxoutr(new float[synth->buffersize]),
efxoutl(new float[synth_.buffersize]),
efxoutr(new float[synth_.buffersize]),
filterpars(NULL),
nefx(0),
efx(NULL),
mutex(mutex_),
dryonly(false)
dryonly(false),
memory(alloc),
synth(synth_)
{
setpresettype("Peffect");
memset(efxoutl, 0, synth->bufferbytes);
memset(efxoutr, 0, synth->bufferbytes);
memset(efxoutl, 0, synth.bufferbytes);
memset(efxoutr, 0, synth.bufferbytes);
memset(settings, 0, sizeof(settings));
defaults();
}


EffectMgr::~EffectMgr()
{
delete efx;
memory.dealloc(efx);
delete [] efxoutl;
delete [] efxoutr;
}
@@ -65,39 +141,41 @@ void EffectMgr::defaults(void)
}

//Change the effect
void EffectMgr::changeeffect(int _nefx)
void EffectMgr::changeeffectrt(int _nefx)
{
cleanup();
if(nefx == _nefx)
if(nefx == _nefx && efx != NULL)
return;
nefx = _nefx;
memset(efxoutl, 0, synth->bufferbytes);
memset(efxoutr, 0, synth->bufferbytes);
delete efx;
memset(efxoutl, 0, synth.bufferbytes);
memset(efxoutr, 0, synth.bufferbytes);
memory.dealloc(efx);
EffectParams pars(memory, insertion, efxoutl, efxoutr, 0,
synth.samplerate, synth.buffersize);
switch(nefx) {
case 1:
efx = new Reverb(insertion, efxoutl, efxoutr, synth->samplerate, synth->buffersize);
efx = memory.alloc<Reverb>(pars);
break;
case 2:
efx = new Echo(insertion, efxoutl, efxoutr, synth->samplerate, synth->buffersize);
efx = memory.alloc<Echo>(pars);
break;
case 3:
efx = new Chorus(insertion, efxoutl, efxoutr, synth->samplerate, synth->buffersize);
efx = memory.alloc<Chorus>(pars);
break;
case 4:
efx = new Phaser(insertion, efxoutl, efxoutr, synth->samplerate, synth->buffersize);
efx = memory.alloc<Phaser>(pars);
break;
case 5:
efx = new Alienwah(insertion, efxoutl, efxoutr, synth->samplerate, synth->buffersize);
efx = memory.alloc<Alienwah>(pars);
break;
case 6:
efx = new Distorsion(insertion, efxoutl, efxoutr, synth->samplerate, synth->buffersize);
efx = memory.alloc<Distorsion>(pars);
break;
case 7:
efx = new EQ(insertion, efxoutl, efxoutr, synth->samplerate, synth->buffersize);
efx = memory.alloc<EQ>(pars);
break;
case 8:
efx = new DynamicFilter(insertion, efxoutl, efxoutr, synth->samplerate, synth->buffersize);
efx = memory.alloc<DynamicFilter>(pars);
break;
//put more effect here
default:
@@ -109,12 +187,35 @@ void EffectMgr::changeeffect(int _nefx)
filterpars = efx->filterpars;
}

void EffectMgr::changeeffect(int _nefx)
{
nefx = _nefx;
//preset = 0;
//memset(settings, 0, sizeof(settings));
}

//Obtain the effect number
int EffectMgr::geteffect(void)
{
return nefx;
}

// Initialize An Effect in RT context
void EffectMgr::init(void)
{
changeeffectrt(nefx);
changepresetrt(preset);
for(int i=0; i<128; ++i)
seteffectparrt(i, settings[i]);
}

//Strip effect manager of it's realtime memory
void EffectMgr::kill(void)
{
//printf("Killing Effect(%d)\n", nefx);
memory.dealloc(efx);
}

// Cleanup the current effect
void EffectMgr::cleanup(void)
{
@@ -133,39 +234,47 @@ unsigned char EffectMgr::getpreset(void)
}

// Change the preset of the current effect
void EffectMgr::changepreset_nolock(unsigned char npreset)
void EffectMgr::changepreset(unsigned char npreset)
{
if(efx)
efx->setpreset(npreset);
preset = npreset;
}

//Change the preset of the current effect(with thread locking)
void EffectMgr::changepreset(unsigned char npreset)
// Change the preset of the current effect
void EffectMgr::changepresetrt(unsigned char npreset)
{
pthread_mutex_lock(mutex);
changepreset_nolock(npreset);
pthread_mutex_unlock(mutex);
preset = npreset;
if(efx)
efx->setpreset(npreset);
}


//Change a parameter of the current effect
void EffectMgr::seteffectpar_nolock(int npar, unsigned char value)
void EffectMgr::seteffectparrt(int npar, unsigned char value)
{
if(npar<128)
settings[npar] = value;
if(!efx)
return;
efx->changepar(npar, value);
}

// Change a parameter of the current effect (with thread locking)
//Change a parameter of the current effect
void EffectMgr::seteffectpar(int npar, unsigned char value)
{
pthread_mutex_lock(mutex);
seteffectpar_nolock(npar, value);
pthread_mutex_unlock(mutex);
settings[npar] = value;
}

//Get a parameter of the current effect
unsigned char EffectMgr::geteffectpar(int npar)
{
if(npar<128)
return settings[npar];

if(!efx)
return 0;
return efx->getpar(npar);
}

unsigned char EffectMgr::geteffectparrt(int npar)
{
if(!efx)
return 0;
@@ -177,7 +286,7 @@ void EffectMgr::out(float *smpsl, float *smpsr)
{
if(!efx) {
if(!insertion)
for(int i = 0; i < synth->buffersize; ++i) {
for(int i = 0; i < synth.buffersize; ++i) {
smpsl[i] = 0.0f;
smpsr[i] = 0.0f;
efxoutl[i] = 0.0f;
@@ -185,7 +294,7 @@ void EffectMgr::out(float *smpsl, float *smpsr)
}
return;
}
for(int i = 0; i < synth->buffersize; ++i) {
for(int i = 0; i < synth.buffersize; ++i) {
smpsl[i] += denormalkillbuf[i];
smpsr[i] += denormalkillbuf[i];
efxoutl[i] = 0.0f;
@@ -196,8 +305,8 @@ void EffectMgr::out(float *smpsl, float *smpsr)
float volume = efx->volume;

if(nefx == 7) { //this is need only for the EQ effect
memcpy(smpsl, efxoutl, synth->bufferbytes);
memcpy(smpsr, efxoutr, synth->bufferbytes);
memcpy(smpsl, efxoutl, synth.bufferbytes);
memcpy(smpsr, efxoutr, synth.bufferbytes);
return;
}

@@ -216,20 +325,20 @@ void EffectMgr::out(float *smpsl, float *smpsr)
v2 *= v2; //for Reverb and Echo, the wet function is not liniar

if(dryonly) //this is used for instrument effect only
for(int i = 0; i < synth->buffersize; ++i) {
for(int i = 0; i < synth.buffersize; ++i) {
smpsl[i] *= v1;
smpsr[i] *= v1;
efxoutl[i] *= v2;
efxoutr[i] *= v2;
}
else // normal instrument/insertion effect
for(int i = 0; i < synth->buffersize; ++i) {
for(int i = 0; i < synth.buffersize; ++i) {
smpsl[i] = smpsl[i] * v1 + efxoutl[i] * v2;
smpsr[i] = smpsr[i] * v1 + efxoutr[i] * v2;
}
}
else // System effect
for(int i = 0; i < synth->buffersize; ++i) {
for(int i = 0; i < synth.buffersize; ++i) {
efxoutl[i] *= 2.0f * volume;
efxoutr[i] *= 2.0f * volume;
smpsl[i] = efxoutl[i];
@@ -241,7 +350,7 @@ void EffectMgr::out(float *smpsl, float *smpsr)
// Get the effect volume for the system effect
float EffectMgr::sysefxgetvolume(void)
{
return (!efx) ? 1.0f : efx->outvolume;
return efx ? efx->outvolume : 1.0f;
}


@@ -257,13 +366,22 @@ void EffectMgr::setdryonly(bool value)
dryonly = value;
}

void EffectMgr::paste(EffectMgr &e)
{
changeeffectrt(e.nefx);
changepresetrt(e.preset);
for(int i=0;i<128;++i){
seteffectparrt(e.settings[i], i);
}
}

void EffectMgr::add2XML(XMLwrapper *xml)
{
xml->addpar("type", geteffect());

if(!efx || !geteffect())
if(!geteffect())
return;
xml->addpar("preset", efx->Ppreset);
xml->addpar("preset", preset);

xml->beginbranch("EFFECT_PARAMETERS");
for(int n = 0; n < 128; ++n) {
@@ -286,18 +404,18 @@ void EffectMgr::getfromXML(XMLwrapper *xml)
{
changeeffect(xml->getpar127("type", geteffect()));

if(!efx || !geteffect())
if(!geteffect())
return;

efx->Ppreset = xml->getpar127("preset", efx->Ppreset);
preset = xml->getpar127("preset", preset);

if(xml->enterbranch("EFFECT_PARAMETERS")) {
for(int n = 0; n < 128; ++n) {
seteffectpar_nolock(n, 0); //erase effect parameter
seteffectpar(n, 0); //erase effect parameter
if(xml->enterbranch("par_no", n) == 0)
continue;
int par = geteffectpar(n);
seteffectpar_nolock(n, xml->getpar127("par", par));
seteffectpar(n, xml->getpar127("par", par));
xml->exitbranch();
}
if(filterpars)


+ 25
- 12
source/native-plugins/zynaddsubfx/Effects/EffectMgr.h View File

@@ -31,11 +31,11 @@
class Effect;
class FilterParams;
class XMLwrapper;
class Allocator;

#include "Distorsion.h"
#include "EQ.h"
#include "DynamicFilter.h"
#include "../Misc/XMLwrapper.h"
#include "../Params/FilterParams.h"
#include "../Params/Presets.h"

@@ -43,30 +43,35 @@ class XMLwrapper;
class EffectMgr:public Presets
{
public:
EffectMgr(const bool insertion_, pthread_mutex_t *mutex_);
EffectMgr(Allocator &alloc, const SYNTH_T &synth, const bool insertion_);
~EffectMgr();

void paste(EffectMgr &e);
void add2XML(XMLwrapper *xml);
void defaults(void);
void defaults(void) REALTIME;
void getfromXML(XMLwrapper *xml);

void out(float *smpsl, float *smpsr);
void out(float *smpsl, float *smpsr) REALTIME;

void setdryonly(bool value);

/**get the output(to speakers) volume of the systemeffect*/
float sysefxgetvolume(void);

void cleanup(void);
void init(void) REALTIME;
void kill(void) REALTIME;
void cleanup(void) REALTIME;

void changeeffect(int nefx_);
void changeeffectrt(int nefx_) REALTIME;
void changeeffect(int nefx_) NONREALTIME;
int geteffect(void);
void changepreset(unsigned char npreset);
void changepreset_nolock(unsigned char npreset);
void changepreset(unsigned char npreset) NONREALTIME;
void changepresetrt(unsigned char npreset) REALTIME;
unsigned char getpreset(void);
void seteffectpar(int npar, unsigned char value);
void seteffectpar_nolock(int npar, unsigned char value);
void seteffectpar(int npar, unsigned char value) NONREALTIME;
void seteffectparrt(int npar, unsigned char value) REALTIME;
unsigned char geteffectpar(int npar);
unsigned char geteffectparrt(int npar) REALTIME;

const bool insertion;
float *efxoutl, *efxoutr;
@@ -76,11 +81,19 @@ class EffectMgr:public Presets

FilterParams *filterpars;

private:
static const rtosc::Ports &ports;
int nefx;
Effect *efx;
pthread_mutex_t *mutex;
private:

//Parameters Prior to initialization
char effect_id;
char preset;
char settings[128];

bool dryonly;
Allocator &memory;
const SYNTH_T &synth;
};

#endif

+ 21
- 33
source/native-plugins/zynaddsubfx/Effects/Phaser.cpp View File

@@ -31,6 +31,7 @@

#include <cmath>
#include <algorithm>
#include "../Misc/Allocator.h"
#include "Phaser.h"

using namespace std;
@@ -39,8 +40,8 @@ using namespace std;
#define ONE_ 0.99999f // To prevent LFO ever reaching 1.0f for filter stability purposes
#define ZERO_ 0.00001f // Same idea as above.

Phaser::Phaser(const int &insertion_, float *efxoutl_, float *efxoutr_, unsigned int srate, int bufsize)
:Effect(insertion_, efxoutl_, efxoutr_, NULL, 0, srate, bufsize), lfo(srate, bufsize), old(NULL), xn1(NULL),
Phaser::Phaser(EffectParams pars)
:Effect(pars), lfo(pars.srate, pars.bufsize), old(NULL), xn1(NULL),
yn1(NULL), diff(0.0f), oldgain(0.0f), fb(0.0f)
{
analog_setup();
@@ -78,18 +79,12 @@ void Phaser::analog_setup()

Phaser::~Phaser()
{
if(old.l)
delete[] old.l;
if(xn1.l)
delete[] xn1.l;
if(yn1.l)
delete[] yn1.l;
if(old.r)
delete[] old.r;
if(xn1.r)
delete[] xn1.r;
if(yn1.r)
delete[] yn1.r;
memory.devalloc(old.l);
memory.devalloc(old.r);
memory.devalloc(xn1.l);
memory.devalloc(xn1.r);
memory.devalloc(yn1.l);
memory.devalloc(yn1.r);
}

/*
@@ -303,30 +298,23 @@ void Phaser::setoffset(unsigned char Poffset)

void Phaser::setstages(unsigned char Pstages)
{
if(old.l)
delete[] old.l;
if(xn1.l)
delete[] xn1.l;
if(yn1.l)
delete[] yn1.l;
if(old.r)
delete[] old.r;
if(xn1.r)
delete[] xn1.r;
if(yn1.r)
delete[] yn1.r;

memory.devalloc(old.l);
memory.devalloc(old.r);
memory.devalloc(xn1.l);
memory.devalloc(xn1.r);
memory.devalloc(yn1.l);
memory.devalloc(yn1.r);

this->Pstages = min(MAX_PHASER_STAGES, (int)Pstages);

old = Stereo<float *>(new float[Pstages * 2],
new float[Pstages * 2]);
old = Stereo<float *>(memory.valloc<float>(Pstages * 2),
memory.valloc<float>(Pstages * 2));

xn1 = Stereo<float *>(new float[Pstages],
new float[Pstages]);
xn1 = Stereo<float *>(memory.valloc<float>(Pstages),
memory.valloc<float>(Pstages));

yn1 = Stereo<float *>(new float[Pstages],
new float[Pstages]);
yn1 = Stereo<float *>(memory.valloc<float>(Pstages),
memory.valloc<float>(Pstages));

cleanup();
}


+ 1
- 1
source/native-plugins/zynaddsubfx/Effects/Phaser.h View File

@@ -35,7 +35,7 @@
class Phaser:public Effect
{
public:
Phaser(const int &insertion_, float *efxoutl_, float *efxoutr_, unsigned int srate, int bufsize);
Phaser(EffectParams pars);
~Phaser();
void out(const Stereo<float *> &input);
void setpreset(unsigned char npreset);


+ 41
- 45
source/native-plugins/zynaddsubfx/Effects/Reverb.cpp View File

@@ -22,12 +22,13 @@

#include "Reverb.h"
#include "../Misc/Util.h"
#include "../Misc/Allocator.h"
#include "../DSP/AnalogFilter.h"
#include "../DSP/Unison.h"
#include <cmath>

Reverb::Reverb(bool insertion_, float *efxoutl_, float *efxoutr_, unsigned int srate, int bufsize)
:Effect(insertion_, efxoutl_, efxoutr_, NULL, 0, srate, bufsize),
Reverb::Reverb(EffectParams pars)
:Effect(pars),
// defaults
Pvolume(48),
Ptime(64),
@@ -39,6 +40,7 @@ Reverb::Reverb(bool insertion_, float *efxoutl_, float *efxoutr_, unsigned int s
Ptype(1),
Proomsize(64),
Pbandwidth(30),
idelaylen(0),
roomsize(1.0f),
rs(1.0f),
bandwidth(NULL),
@@ -66,35 +68,33 @@ Reverb::Reverb(bool insertion_, float *efxoutl_, float *efxoutr_, unsigned int s

Reverb::~Reverb()
{
delete [] idelay;
delete hpf;
delete lpf;
memory.devalloc(idelay);
memory.dealloc(hpf);
memory.dealloc(lpf);

for(int i = 0; i < REV_APS * 2; ++i)
delete [] ap[i];
memory.devalloc(ap[i]);
for(int i = 0; i < REV_COMBS * 2; ++i)
delete [] comb[i];
memory.devalloc(comb[i]);

if(bandwidth)
delete bandwidth;
memory.dealloc(bandwidth);
}

//Cleanup the effect
void Reverb::cleanup(void)
{
int i, j;
for(i = 0; i < REV_COMBS * 2; ++i) {
for(int i = 0; i < REV_COMBS * 2; ++i) {
lpcomb[i] = 0.0f;
for(j = 0; j < comblen[i]; ++j)
for(int j = 0; j < comblen[i]; ++j)
comb[i][j] = 0.0f;
}

for(i = 0; i < REV_APS * 2; ++i)
for(j = 0; j < aplen[i]; ++j)
for(int i = 0; i < REV_APS * 2; ++i)
for(int j = 0; j < aplen[i]; ++j)
ap[i][j] = 0.0f;

if(idelay)
for(i = 0; i < idelaylen; ++i)
for(int i = 0; i < idelaylen; ++i)
idelay[i] = 0.0f;
if(hpf)
hpf->cleanup();
@@ -231,15 +231,16 @@ void Reverb::setidelay(unsigned char _Pidelay)
{
Pidelay = _Pidelay;
float delay = powf(50.0f * Pidelay / 127.0f, 2.0f) - 1.0f;
int newDelayLen = (int) (samplerate_f * delay / 1000);
if(newDelayLen == idelaylen)
return;

if(idelay)
delete [] idelay;
idelay = NULL;
memory.devalloc(idelay);

idelaylen = (int) (samplerate_f * delay / 1000);
idelaylen = newDelayLen;
if(idelaylen > 1) {
idelayk = 0;
idelay = new float[idelaylen];
idelay = memory.valloc<float>(idelaylen);
memset(idelay, 0, idelaylen * sizeof(float));
}
}
@@ -254,14 +255,11 @@ void Reverb::sethpf(unsigned char _Phpf)
{
Phpf = _Phpf;
if(Phpf == 0) { //No HighPass
if(hpf)
delete hpf;
hpf = NULL;
}
else {
float fr = expf(powf(Phpf / 127.0f, 0.5f) * logf(10000.0f)) + 20.0f;
memory.dealloc(hpf);
} else {
float fr = expf(sqrtf(Phpf / 127.0f) * logf(10000.0f)) + 20.0f;
if(hpf == NULL)
hpf = new AnalogFilter(3, fr, 1, 0, samplerate, buffersize);
hpf = memory.alloc<AnalogFilter>(3, fr, 1, 0, samplerate, buffersize);
else
hpf->setfreq(fr);
}
@@ -271,14 +269,11 @@ void Reverb::setlpf(unsigned char _Plpf)
{
Plpf = _Plpf;
if(Plpf == 127) { //No LowPass
if(lpf)
delete lpf;
lpf = NULL;
}
else {
float fr = expf(powf(Plpf / 127.0f, 0.5f) * logf(25000.0f)) + 40.0f;
memory.dealloc(lpf);
} else {
float fr = expf(sqrtf(Plpf / 127.0f) * logf(25000.0f)) + 40.0f;
if(!lpf)
lpf = new AnalogFilter(2, fr, 1, 0, samplerate, buffersize);
lpf = memory.alloc<AnalogFilter>(2, fr, 1, 0, samplerate, buffersize);
else
lpf->setfreq(fr);
}
@@ -323,12 +318,13 @@ void Reverb::settype(unsigned char _Ptype)
tmp *= samplerate_adjust; //adjust the combs according to the samplerate
if(tmp < 10.0f)
tmp = 10.0f;
comblen[i] = (int) tmp;
combk[i] = 0;
lpcomb[i] = 0;
if(comb[i])
delete [] comb[i];
comb[i] = new float[comblen[i]];
if(comblen[i] != (int)tmp || comb[i] == NULL) {
comblen[i] = (int) tmp;
memory.devalloc(comb[i]);
comb[i] = memory.valloc<float>(comblen[i]);
}
}

for(int i = 0; i < REV_APS * 2; ++i) {
@@ -342,20 +338,20 @@ void Reverb::settype(unsigned char _Ptype)
tmp *= samplerate_adjust; //adjust the combs according to the samplerate
if(tmp < 10)
tmp = 10;
aplen[i] = (int) tmp;
apk[i] = 0;
if(ap[i])
delete [] ap[i];
ap[i] = new float[aplen[i]];
if(aplen[i] != (int)tmp || ap[i] == NULL) {
aplen[i] = (int) tmp;
memory.devalloc(ap[i]);
ap[i] = memory.valloc<float>(aplen[i]);
}
}
delete bandwidth;
bandwidth = NULL;
memory.dealloc(bandwidth);
if(Ptype == 2) { //bandwidth
//TODO the size of the unison buffer may be too small, though this has
//not been verified yet.
//As this cannot be resized in a RT context, a good upper bound should
//be found
bandwidth = new Unison(buffersize / 4 + 1, 2.0f, samplerate_f);
bandwidth = memory.alloc<Unison>(&memory, buffersize / 4 + 1, 2.0f, samplerate_f);
bandwidth->setSize(50);
bandwidth->setBaseFrequency(1.0f);
}


+ 1
- 1
source/native-plugins/zynaddsubfx/Effects/Reverb.h View File

@@ -32,7 +32,7 @@
class Reverb:public Effect
{
public:
Reverb(bool insertion_, float *efxoutl_, float *efxoutr_, unsigned int srate, int bufsize);
Reverb(EffectParams pars);
~Reverb();
void out(const Stereo<float *> &smp);
void cleanup(void);


+ 192
- 0
source/native-plugins/zynaddsubfx/Misc/Allocator.cpp View File

@@ -0,0 +1,192 @@
#include <cstddef>
#include <cstdlib>
#include <cassert>
#include <utility>
#include <cstdio>
#include "tlsf/tlsf.h"
#include "Allocator.h"

//Used for dummy allocations
Allocator DummyAlloc;

//recursive type class to avoid void *v = *(void**)v style casting
struct next_t
{
next_t *next;
size_t pool_size;
};

void *data(next_t *n)
{
return n+sizeof(next_t);
}


struct AllocatorImpl
{
void *tlsf = 0;

//singly linked list of memory pools
//XXX this may violate alignment on some platforms if malloc doesn't return
//nice values
next_t *pools = 0;
unsigned long long totalAlloced = 0;
};

Allocator::Allocator(void)
{
impl = new AllocatorImpl;
size_t default_size = 10*1024*1024;
impl->pools = (next_t*)malloc(default_size);
impl->pools->next = 0x0;
impl->pools->pool_size = default_size;
size_t off = tlsf_size() + tlsf_pool_overhead() + sizeof(next_t);
//printf("Generated Memory Pool with '%p'\n", impl->pools);
impl->tlsf = tlsf_create_with_pool(((char*)impl->pools)+off, default_size-2*off);
//printf("Allocator(%p)\n", impl);
}

Allocator::~Allocator(void)
{
next_t *n = impl->pools;
while(n) {
next_t *nn = n->next;
free(n);
n = nn;
}
delete impl;
}

void *Allocator::alloc_mem(size_t mem_size)
{
impl->totalAlloced += mem_size;
void *mem = tlsf_malloc(impl->tlsf, mem_size);
//printf("Allocator.malloc(%p, %d) = %p\n", impl, mem_size, mem);
//void *mem = malloc(mem_size);
//printf("Allocator result = %p\n", mem);
return mem;
}
void Allocator::dealloc_mem(void *memory)
{
//printf("dealloc_mem(%d)\n", tlsf_block_size(memory));
tlsf_free(impl->tlsf, memory);
//free(memory);
}

bool Allocator::lowMemory(unsigned n, size_t chunk_size)
{
//This should stay on the stack
void *buf[n];
for(unsigned i=0; i<n; ++i)
buf[i] = tlsf_malloc(impl->tlsf, chunk_size);
bool outOfMem = false;
for(unsigned i=0; i<n; ++i)
outOfMem |= (buf[i] == nullptr);
for(unsigned i=0; i<n; ++i)
if(buf[i])
tlsf_free(impl->tlsf, buf[i]);

return outOfMem;
}


void Allocator::addMemory(void *v, size_t mem_size)
{
next_t *n = impl->pools;
while(n->next) n = n->next;
n->next = (next_t*)v;
n->next->next = 0x0;
n->next->pool_size = mem_size;
//printf("Inserting '%p'\n", v);
off_t off = sizeof(next_t) + tlsf_pool_overhead();
void *result =
tlsf_add_pool(impl->tlsf, ((char*)n->next)+off,
//0x0eadbeef);
mem_size-off-sizeof(size_t));
if(!result)
printf("FAILED TO INSERT MEMORY POOL\n");
};//{(void)mem_size;};

#ifndef INCLUDED_tlsfbits
//From tlsf internals
typedef struct block_header_t
{
/* Points to the previous physical block. */
struct block_header_t* prev_phys_block;

/* The size of this block, excluding the block header. */
size_t size;

/* Next and previous free blocks. */
struct block_header_t* next_free;
struct block_header_t* prev_free;
} block_header_t;
static const size_t block_header_free_bit = 1 << 0;
#endif

bool Allocator::memFree(void *pool)
{
size_t bh_shift = sizeof(next_t)+sizeof(size_t);
//Assume that memory is free to start with
bool isFree = true;
//Get the block header from the pool
block_header_t &bh = *(block_header_t*)((char*)pool+bh_shift);
//The first block must be free
if((bh.size&block_header_free_bit) == 0)
isFree = false;
block_header_t &bhn = *(block_header_t*)
(((char*)&bh)+((bh.size&~0x3)+bh_shift-2*sizeof(size_t)));
//The next block must be 'non-free' and zero length
if((bhn.size&block_header_free_bit) != 0)
isFree = false;
if((bhn.size&~0x3) != 0)
isFree = false;

return isFree;
}

int Allocator::memPools()
{
int i = 1;
next_t *n = impl->pools;
while(n->next) {
i++;
n = n->next;
}
return i;
}

int Allocator::freePools()
{
int i = 0;
next_t *n = impl->pools->next;
while(n) {
if(memFree(n))
i++;
n = n->next;
}
return i;
}


unsigned long long Allocator::totalAlloced()
{
return impl->totalAlloced;
}

/*
* Notes on tlsf internals
* - TLSF consists of blocks linked by block headers and these form a doubly
* linked list of free segments
* - Original memory is [control_t pool]
* base sentinal
* Pools are [block_t block_t blocks ...]
* Blocks are [memory block_t](??)
* - These are stored in the control_t structure in an order dependent on the
* size that they are
* it's a bit unclear how collisions are handled here, but the basic premise
* makes sense
* - Additional structure is added before the start of each pool to define the
* pool size and the next pool in the list as this information is not
* accessible in O(good) time
*/

+ 113
- 0
source/native-plugins/zynaddsubfx/Misc/Allocator.h View File

@@ -0,0 +1,113 @@

#ifndef ALLOCATOR_H
#define ALLOCATOR_H

#include <cstdlib>
#include <utility>

class Allocator
{
public:
Allocator(void);
Allocator(const Allocator&) = delete;
~Allocator(void);
void *alloc_mem(size_t mem_size);
void dealloc_mem(void *memory);

template <typename T, typename... Ts>
T *alloc(Ts&&... ts)
{
void *data = alloc_mem(sizeof(T));
if(!data)
return nullptr;
return new (data) T(std::forward<Ts>(ts)...);
}

template <typename T, typename... Ts>
T *valloc(size_t len, Ts&&... ts)
{
T *data = (T*)alloc_mem(len*sizeof(T));
if(!data)
return nullptr;
for(unsigned i=0; i<len; ++i)
new ((void*)&data[i]) T(std::forward<Ts>(ts)...);

return data;
}

template <typename T>
void dealloc(T*&t)
{
if(t) {
t->~T();
dealloc_mem((void*)t);
t = nullptr;
}
}

//Destructor Free Version
template <typename T>
void devalloc(T*&t)
{
if(t) {
dealloc_mem(t);
t = nullptr;
}
}

template <typename T>
void devalloc(size_t elms, T*&t)
{
if(t) {
for(size_t i=0; i<elms; ++i)
(t+i)->~T();

dealloc_mem(t);
t = nullptr;
}
}

void addMemory(void *, size_t mem_size);

//Return true if the current pool cannot allocate n chunks of chunk_size
bool lowMemory(unsigned n, size_t chunk_size);
bool memFree(void *pool);

//returns number of pools
int memPools();

int freePools();

unsigned long long totalAlloced();

struct AllocatorImpl *impl;
};

extern Allocator DummyAlloc;

/**
* General notes on Memory Allocation Within ZynAddSubFX
* -----------------------------------------------------
*
* - Parameter Objects Are never allocated within the realtime thread
* - Effects, notes and note subcomponents must be allocated with an allocator
* - 5M Chunks are used to give the allocator the memory it wants
* - If there are 3 chunks that are unused then 1 will be deallocated
* - The system will request more allocated space if 5x 1MB chunks cannot be
* allocated at any given time (this is likely huge overkill, but if this is
* satisfied, then a lot of note spamming would be needed to run out of
* space)
*
* - Things will get a bit weird around the effects due to how pointer swaps
* occur
* * When a new Part instance is provided it may or may not come with some
* instrument effects
* * Merging blocks is an option, but one that is not going to likely be
* implmented too soon, thus all effects need to be reallocated when the
* pointer swap occurs
* * The old effect is extracted from the manager
* * A new one is constructed with a deep copy
* * The old one is returned to middleware for deallocation
*/

#endif

+ 70
- 61
source/native-plugins/zynaddsubfx/Misc/Bank.cpp View File

@@ -23,13 +23,12 @@
*/

#include "Bank.h"
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <cstring>
#include <cstdio>
#include <cstdlib>
#include <dirent.h>
#include <sys/stat.h>
#include <algorithm>
#include <iostream>

#include <sys/types.h>
#include <fcntl.h>
@@ -48,11 +47,19 @@
using namespace std;

Bank::Bank()
:defaultinsname(" ")
:bankpos(0), defaultinsname(" ")
{
clearbank();
bankfiletitle = dirname;
rescanforbanks();
loadbank(config.cfg.currentBankDir);

for(unsigned i=0; i<banks.size(); ++i) {
if(banks[i].dir == config.cfg.currentBankDir) {
bankpos = i;
break;
}
}
}

Bank::~Bank()
@@ -84,10 +91,10 @@ string Bank::getnamenumbered(unsigned int ninstrument)
/*
* Changes the name of an instrument (and the filename)
*/
void Bank::setname(unsigned int ninstrument, const string &newname, int newslot)
int Bank::setname(unsigned int ninstrument, const string &newname, int newslot)
{
if(emptyslot(ninstrument))
return;
return 0;

string newfilename;
char tmpfilename[100 + 1];
@@ -103,12 +110,15 @@ void Bank::setname(unsigned int ninstrument, const string &newname, int newslot)
if(tmpfilename[i] == ' ')
tmpfilename[i] = '0';

newfilename = dirname + '/' + legalizeFilename(tmpfilename) + ".xiz";
newfilename = dirname + legalizeFilename(tmpfilename) + ".xiz";

rename(ins[ninstrument].filename.c_str(), newfilename.c_str());
int err = rename(ins[ninstrument].filename.c_str(), newfilename.c_str());
if(err)
return err;

ins[ninstrument].filename = newfilename;
ins[ninstrument].name = newname;
return err;
}

/*
@@ -121,30 +131,37 @@ bool Bank::emptyslot(unsigned int ninstrument)
if(ins[ninstrument].filename.empty())
return true;

if(ins[ninstrument].used)
return false;
else
return true;
return false;
}

/*
* Removes the instrument from the bank
*/
void Bank::clearslot(unsigned int ninstrument)
int Bank::clearslot(unsigned int ninstrument)
{
if(emptyslot(ninstrument))
return;
return 0;

remove(ins[ninstrument].filename.c_str());
deletefrombank(ninstrument);
//no error when no file
FILE *f = fopen(ins[ninstrument].filename.c_str(), "r");
if(!f)
return 0;
fclose(f);

int err = remove(ins[ninstrument].filename.c_str());
if(!err)
deletefrombank(ninstrument);
return err;
}

/*
* Save the instrument to a slot
*/
void Bank::savetoslot(unsigned int ninstrument, Part *part)
int Bank::savetoslot(unsigned int ninstrument, Part *part)
{
clearslot(ninstrument);
int err = clearslot(ninstrument);
if(err)
return err;

const int maxfilename = 200;
char tmpfilename[maxfilename + 20];
@@ -163,23 +180,35 @@ void Bank::savetoslot(unsigned int ninstrument, Part *part)

string filename = dirname + '/' + legalizeFilename(tmpfilename) + ".xiz";

remove(filename.c_str());
part->saveXML(filename.c_str());
FILE *f = fopen(filename.c_str(), "r");
if(f) {
fclose(f);

err = remove(filename.c_str());
if(err)
return err;
}

err = part->saveXML(filename.c_str());
if(err)
return err;
addtobank(ninstrument, legalizeFilename(tmpfilename) + ".xiz", (char *) part->Pname);
return 0;
}

/*
* Loads the instrument from the bank
*/
void Bank::loadfromslot(unsigned int ninstrument, Part *part)
int Bank::loadfromslot(unsigned int ninstrument, Part *part)
{
if(emptyslot(ninstrument))
return;
return 0;

part->AllNotesOff();
part->defaultsinstrument();

part->loadXMLinstrument(ins[ninstrument].filename.c_str());
return 0;
}

/*
@@ -251,7 +280,6 @@ int Bank::loadbank(string bankdirname)
*/
int Bank::newbank(string newbankdirname)
{
#ifndef CARLA_OS_WIN
string bankdir;
bankdir = config.cfg.bankRootDirList[0];

@@ -269,9 +297,6 @@ int Bank::newbank(string newbankdirname)
fclose(tmpfile);

return loadbank(bankdir);
#else
return -1;
#endif
}

/*
@@ -279,23 +304,27 @@ int Bank::newbank(string newbankdirname)
*/
int Bank::locked()
{
//XXX Fixme
return dirname.empty();
}

/*
* Swaps a slot with another
*/
void Bank::swapslot(unsigned int n1, unsigned int n2)
int Bank::swapslot(unsigned int n1, unsigned int n2)
{
int err = 0;
if((n1 == n2) || (locked()))
return;
return 0;
if(emptyslot(n1) && (emptyslot(n2)))
return;
return 0;
if(emptyslot(n1)) //change n1 to n2 in order to make
swap(n1, n2);

if(emptyslot(n2)) { //this is just a movement from slot1 to slot2
setname(n1, getname(n1), n2);
err |= setname(n1, getname(n1), n2);
if(err)
return err;
ins[n2] = ins[n1];
ins[n1] = ins_t();
}
@@ -303,10 +332,13 @@ void Bank::swapslot(unsigned int n1, unsigned int n2)
if(ins[n1].name == ins[n2].name) //change the name of the second instrument if the name are equal
ins[n2].name += "2";

setname(n1, getname(n1), n2);
setname(n2, getname(n2), n1);
err |= setname(n1, getname(n1), n2);
err |= setname(n2, getname(n2), n1);
if(err)
return err;
swap(ins[n2], ins[n1]);
}
return err;
}


@@ -413,7 +445,7 @@ void Bank::clearbank()
int Bank::addtobank(int pos, string filename, string name)
{
if((pos >= 0) && (pos < BANK_SIZE)) {
if(ins[pos].used)
if(!ins[pos].filename.empty())
pos = -1; //force it to find a new free position
}
else
@@ -423,7 +455,7 @@ int Bank::addtobank(int pos, string filename, string name)

if(pos < 0) //find a free position
for(int i = BANK_SIZE - 1; i >= 0; i--)
if(!ins[i].used) {
if(ins[i].filename.empty()) {
pos = i;
break;
}
@@ -433,32 +465,11 @@ int Bank::addtobank(int pos, string filename, string name)

deletefrombank(pos);

ins[pos].used = true;
ins[pos].name = name;
ins[pos].filename = dirname + '/' + filename;

//see if PADsynth is used
if(config.cfg.CheckPADsynth) {
XMLwrapper xml;
xml.loadXMLfile(ins[pos].filename);

ins[pos].info.PADsynth_used = xml.hasPadSynth();
}
else
ins[pos].info.PADsynth_used = false;

ins[pos].filename = dirname + filename;
return 0;
}

bool Bank::isPADsynth_used(unsigned int ninstrument)
{
if(config.cfg.CheckPADsynth == 0)
return 0;
else
return ins[ninstrument].info.PADsynth_used;
}


void Bank::deletefrombank(int pos)
{
if((pos < 0) || (pos >= BANK_SIZE))
@@ -467,7 +478,5 @@ void Bank::deletefrombank(int pos)
}

Bank::ins_t::ins_t()
:used(false), name(""), filename("")
{
info.PADsynth_used = false;
}
:name(""), filename("")
{}

+ 18
- 20
source/native-plugins/zynaddsubfx/Misc/Bank.h View File

@@ -25,6 +25,7 @@

#include <string>
#include <vector>
#include "../globals.h"

//entries in a bank
#define BANK_SIZE 160
@@ -38,26 +39,26 @@ class Bank
~Bank();
std::string getname(unsigned int ninstrument);
std::string getnamenumbered(unsigned int ninstrument);
void setname(unsigned int ninstrument,
//if newslot==-1 then this is ignored, else it will be put on that slot
int setname(unsigned int ninstrument,
const std::string &newname,
int newslot); //if newslot==-1 then this is ignored, else it will be put on that slot
bool isPADsynth_used(unsigned int ninstrument);
int newslot);

/**returns true when slot is empty*/
bool emptyslot(unsigned int ninstrument);

/**Empties out the selected slot*/
void clearslot(unsigned int ninstrument);
int clearslot(unsigned int ninstrument);
/**Saves the given Part to slot*/
void savetoslot(unsigned int ninstrument, class Part * part);
int savetoslot(unsigned int ninstrument, class Part * part);
/**Loads the given slot into a Part*/
void loadfromslot(unsigned int ninstrument, class Part * part);
int loadfromslot(unsigned int ninstrument, class Part * part);

/**Swaps Slots*/
void swapslot(unsigned int n1, unsigned int n2);
int swapslot(unsigned int n1, unsigned int n2);

int loadbank(std::string bankdirname);
int newbank(std::string newbankdirname);
int loadbank(std::string bankdirname) NONREALTIME;
int newbank(std::string newbankdirname) NONREALTIME;

std::string bankfiletitle; //this is shown on the UI of the bank (the title of the window)
int locked();
@@ -71,6 +72,14 @@ class Bank
};

std::vector<bankstruct> banks;
int bankpos;
struct ins_t {
ins_t(void);
std::string name;
//All valid instruments must have a non-empty filename
std::string filename;
} ins[BANK_SIZE];

private:

@@ -84,17 +93,6 @@ class Bank
void clearbank();

std::string defaultinsname;

struct ins_t {
ins_t();
bool used;
std::string name;
std::string filename;
struct {
bool PADsynth_used;
} info;
} ins[BANK_SIZE];

std::string dirname;

void scanrootdir(std::string rootdir); //scans a root dir for banks


+ 3
- 1
source/native-plugins/zynaddsubfx/Misc/CMakeLists.txt View File

@@ -3,7 +3,6 @@ include_directories(${MXML_INCLUDE_DIR})
set(zynaddsubfx_misc_SRCS
Misc/Bank.cpp
Misc/Config.cpp
Misc/Dump.cpp
Misc/Master.cpp
Misc/Microtonal.cpp
Misc/Part.cpp
@@ -12,6 +11,9 @@ set(zynaddsubfx_misc_SRCS
Misc/Recorder.cpp
Misc/WavFile.cpp
Misc/WaveShapeSmps.cpp
Misc/MiddleWare.cpp
Misc/PresetExtractor.cpp
Misc/Allocator.cpp
)




+ 96
- 23
source/native-plugins/zynaddsubfx/Misc/Config.cpp View File

@@ -19,19 +19,110 @@
Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

*/
#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <string.h>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <cstring>

#include <rtosc/ports.h>
#include <rtosc/port-sugar.h>

#include "Config.h"
#include "XMLwrapper.h"

using namespace std;
#define rStdString(name, len, ...) \
{STRINGIFY(name) "::s", rMap(length, len) DOC(__VA_ARGS__), NULL, rStringCb(name,len)}
#define rStdStringCb(name, length) rBOIL_BEGIN \
if(!strcmp("", args)) {\
data.reply(loc, "s", obj->name); \
} else { \
strncpy(obj->name, rtosc_argument(msg, 0).s, length); \
data.broadcast(loc, "s", obj->name);\
rChangeCb \
} rBOIL_END




#if 1
#define rObject Config
static rtosc::Ports ports = {
//rString(cfg.LinuxOSSWaveOutDev),
//rString(cfg.LinuxOSSSeqInDev),
rParamI(cfg.SampleRate, "samples of audio per second"),
rParamI(cfg.SoundBufferSize, "Size of processed audio buffer"),
rParamI(cfg.OscilSize, "Size Of Oscillator Wavetable"),
rToggle(cfg.SwapStereo, "Swap Left And Right Channels"),
rToggle(cfg.BankUIAutoClose, "Automatic Closing of BackUI After Patch Selection"),
rParamI(cfg.GzipCompression, "Level of Gzip Compression For Save Files"),
rParamI(cfg.Interpolation, "Level of Interpolation, Linear/Cubic"),
{"cfg.presetsDirList", rProp(parameter) rDoc("list of preset search directories"), 0,
[](const char *msg, rtosc::RtData &d)
{
Config &c = *(Config*)d.obj;
if(rtosc_narguments(msg) != 0)
return;
char types[MAX_BANK_ROOT_DIRS+1];
rtosc_arg_t args[MAX_BANK_ROOT_DIRS];
size_t pos = 0;

//zero out data
memset(types, 0, sizeof(types));
memset(args, 0, sizeof(args));

for(int i=0; i<MAX_BANK_ROOT_DIRS; ++i) {
if(!c.cfg.presetsDirList[i].empty()) {
types[pos] = 's';
args[pos].s = c.cfg.presetsDirList[i].c_str();
pos++;
}
}
char buffer[1024*5];
rtosc_amessage(buffer, sizeof(buffer), d.loc, types, args);
d.reply(buffer);
}},
{"cfg.bankRootDirList", rProp(parameter) rDoc("list of bank search directories"), 0,
[](const char *msg, rtosc::RtData &d)
{
Config &c = *(Config*)d.obj;
if(rtosc_narguments(msg) != 0)
return;
char types[MAX_BANK_ROOT_DIRS+1];
rtosc_arg_t args[MAX_BANK_ROOT_DIRS];
size_t pos = 0;

//zero out data
memset(types, 0, sizeof(types));
memset(args, 0, sizeof(args));

for(int i=0; i<MAX_BANK_ROOT_DIRS; ++i) {
if(!c.cfg.bankRootDirList[i].empty()) {
types[pos] = 's';
args[pos].s = c.cfg.bankRootDirList[i].c_str();
pos++;
}
}
char buffer[1024*5];
rtosc_amessage(buffer, sizeof(buffer), d.loc, types, args);
d.reply(buffer);
}},

//rArrayS(cfg.bankRootDirList,MAX_BANK_ROOT_DIRS),
//rString(cfg.currentBankDir),
//rArrayS(cfg.presetsDirList,MAX_BANK_ROOT_DIRS),
rToggle(cfg.CheckPADsynth, "Old Check For PADsynth functionality within a patch"),
rToggle(cfg.IgnoreProgramChange, "Ignore MIDI Program Change Events"),
rParamI(cfg.UserInterfaceMode, "Beginner/Advanced Mode Select"),
rParamI(cfg.VirKeybLayout, "Keyboard Layout For Virtual Piano Keyboard"),
//rParamS(cfg.LinuxALSAaudioDev),
//rParamS(cfg.nameTag)
};
rtosc::Ports &Config::ports = ::ports;
#endif

Config::Config()
{}

void Config::init()
{
maxstringsize = MAX_STRING_SIZE; //for ui
@@ -46,14 +137,10 @@ void Config::init()
cfg.LinuxOSSSeqInDev = new char[MAX_STRING_SIZE];
snprintf(cfg.LinuxOSSSeqInDev, MAX_STRING_SIZE, "/dev/sequencer");

cfg.DumpFile = "zynaddsubfx_dump.txt";

cfg.WindowsWaveOutId = 0;
cfg.WindowsMidiInId = 0;

cfg.BankUIAutoClose = 0;
cfg.DumpNotesToFile = 0;
cfg.DumpAppend = 1;

cfg.GzipCompression = 3;

@@ -168,16 +255,6 @@ void Config::readConfig(const char *filename)
0,
1);

cfg.DumpNotesToFile = xmlcfg.getpar("dump_notes_to_file",
cfg.DumpNotesToFile,
0,
1);
cfg.DumpAppend = xmlcfg.getpar("dump_append",
cfg.DumpAppend,
0,
1);
cfg.DumpFile = xmlcfg.getparstr("dump_file", "");

cfg.GzipCompression = xmlcfg.getpar("gzip_compression",
cfg.GzipCompression,
0,
@@ -259,10 +336,6 @@ void Config::saveConfig(const char *filename)
xmlcfg->addpar("swap_stereo", cfg.SwapStereo);
xmlcfg->addpar("bank_window_auto_close", cfg.BankUIAutoClose);

xmlcfg->addpar("dump_notes_to_file", cfg.DumpNotesToFile);
xmlcfg->addpar("dump_append", cfg.DumpAppend);
xmlcfg->addparstr("dump_file", cfg.DumpFile);

xmlcfg->addpar("gzip_compression", cfg.GzipCompression);

xmlcfg->addpar("check_pad_synth", cfg.CheckPADsynth);


+ 1
- 2
source/native-plugins/zynaddsubfx/Misc/Config.h View File

@@ -40,10 +40,8 @@ class Config
int SampleRate, SoundBufferSize, OscilSize, SwapStereo;
int WindowsWaveOutId, WindowsMidiInId;
int BankUIAutoClose;
int DumpNotesToFile, DumpAppend;
int GzipCompression;
int Interpolation;
std::string DumpFile;
std::string bankRootDirList[MAX_BANK_ROOT_DIRS], currentBankDir;
std::string presetsDirList[MAX_BANK_ROOT_DIRS];
int CheckPADsynth;
@@ -66,6 +64,7 @@ class Config
void init();
void save();

static rtosc::Ports &ports;
private:
void readConfig(const char *filename);
void saveConfig(const char *filename);


+ 0
- 121
source/native-plugins/zynaddsubfx/Misc/Dump.cpp View File

@@ -1,121 +0,0 @@
/*
ZynAddSubFX - a software synthesizer

Dump.cpp - It dumps the notes to a text file

Copyright (C) 2002-2005 Nasca Octavian Paul
Author: Nasca Octavian Paul

This program is free software; you can redistribute it and/or modify
it under the terms of version 2 of the GNU General Public License
as published by the Free Software Foundation.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License (version 2 or later) for more details.

You should have received a copy of the GNU General Public License (version 2)
along with this program; if not, write to the Free Software Foundation,
Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <stdlib.h>
#include <time.h>
#include "Util.h"
#include "Dump.h"

Dump dump;

Dump::Dump()
{
file = NULL;
tick = 0;
k = 0;
keyspressed = 0;
}

Dump::~Dump()
{
if(file != NULL) {
int duration = tick * synth->buffersize_f / synth->samplerate_f;
fprintf(
file,
"\n# statistics: duration = %d seconds; keyspressed = %d\n\n\n\n",
duration,
keyspressed);
fclose(file);
}
}

void Dump::startnow()
{
if(file != NULL)
return; //the file is already open

if(config.cfg.DumpNotesToFile != 0) {
if(config.cfg.DumpAppend != 0)
file = fopen(config.cfg.DumpFile.c_str(), "a");
else
file = fopen(config.cfg.DumpFile.c_str(), "w");
if(file == NULL)
return;
if(config.cfg.DumpAppend != 0)
fprintf(file, "%s", "#************************************\n");

time_t tm = time(NULL);

fprintf(file, "#date/time = %s\n", ctime(&tm));
fprintf(file, "#1 tick = %g milliseconds\n",
synth->buffersize_f * 1000.0f / synth->samplerate_f);
fprintf(file, "SAMPLERATE = %d\n", synth->samplerate);
fprintf(file, "TICKSIZE = %d #samples\n", synth->buffersize);
fprintf(file, "\n\nSTART\n");
}
}

void Dump::inctick()
{
tick++;
}


void Dump::dumpnote(char chan, char note, char vel)
{
if(file == NULL)
return;
if(note == 0)
return;
if(vel == 0)
fprintf(file, "n %d -> %d %d \n", tick, chan, note); //note off
else
fprintf(file, "N %d -> %d %d %d \n", tick, chan, note, vel); //note on

if(vel != 0)
keyspressed++;
#ifndef JACKAUDIOOUT
if(k++ > 25) {
fflush(file);
k = 0;
}
#endif
}

void Dump::dumpcontroller(char chan, unsigned int type, int par)
{
if(file == NULL)
return;
switch(type) {
case C_pitchwheel:
fprintf(file, "P %d -> %d %d\n", tick, chan, par);
break;
default:
fprintf(file, "C %d -> %d %d %d\n", tick, chan, type, par);
break;
}
#ifndef JACKAUDIOOUT
if(k++ > 25) {
fflush(file);
k = 0;
}
#endif
}

+ 0
- 63
source/native-plugins/zynaddsubfx/Misc/Dump.h View File

@@ -1,63 +0,0 @@
/*
ZynAddSubFX - a software synthesizer

Dump.h - It dumps the notes to a text file

Copyright (C) 2002-2005 Nasca Octavian Paul
Author: Nasca Octavian Paul

This program is free software; you can redistribute it and/or modify
it under the terms of version 2 of the GNU General Public License
as published by the Free Software Foundation.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License (version 2 or later) for more details.

You should have received a copy of the GNU General Public License (version 2)
along with this program; if not, write to the Free Software Foundation,
Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef DUMP_H
#define DUMP_H

#include <stdio.h>

/**Object used to dump the notes into a text file
* \todo see if this object should have knowledge about the file
* that it will write to
* \todo upgrade from stdio to iostream*/
class Dump
{
public:
/**Constructor*/
Dump();
/**Destructor
* Closes the dumpfile*/
~Dump();
/**Open dumpfile and prepare it for dumps
* \todo see if this fits better in the constructor*/
void startnow();
/**Tick the timestamp*/
void inctick();
/**Dump Note to dumpfile
* @param chan The channel of the note
* @param note The note
* @param vel The velocity of the note*/
void dumpnote(char chan, char note, char vel);
/** Dump the Controller
* @param chan The channel of the Controller
* @param type The type
* @param par The value of the controller
* \todo figure out what type is exactly meaning*/
void dumpcontroller(char chan, unsigned int type, int par);

private:
FILE *file;
int tick;
int k; //This appears to be a constant used to flush the file
//periodically when JACK is used
int keyspressed;
};
#endif

+ 442
- 126
source/native-plugins/zynaddsubfx/Misc/Master.cpp View File

@@ -28,36 +28,276 @@
#include "../Params/LFOParams.h"
#include "../Effects/EffectMgr.h"
#include "../DSP/FFTwrapper.h"
#include "../Misc/Allocator.h"
#include "../Nio/Nio.h"
#include "PresetExtractor.h"

#include <rtosc/ports.h>
#include <rtosc/port-sugar.h>
#include <rtosc/thread-link.h>
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <atomic>

#include <unistd.h>

using namespace std;
using namespace rtosc;
#define rObject Master

static const Ports sysefxPort =
{
{"part#" STRINGIFY(NUM_MIDI_PARTS) "::i", 0, 0, [](const char *m, RtData&d)
{
//ok, this is going to be an ugly workaround
//we know that if we are here the message previously MUST have
//matched Psysefxvol#/
//and the number is one or two digits at most
const char *index_1 = m;
index_1 -=2;
assert(isdigit(*index_1));
if(isdigit(index_1[-1]))
index_1--;
int ind1 = atoi(index_1);

//Now get the second index like normal
while(!isdigit(*m)) m++;
int ind2 = atoi(m);
Master &mast = *(Master*)d.obj;

if(rtosc_narguments(m))
mast.setPsysefxvol(ind2, ind1, rtosc_argument(m,0).i);
else
d.reply(d.loc, "i", mast.Psysefxvol[ind2][ind1]);
}}
};

static const Ports sysefsendto =
{
{"to#" STRINGIFY(NUM_SYS_EFX) "::i", 0, 0, [](const char *m, RtData&d)
{
//same ugly workaround as before
const char *index_1 = m;
index_1 -=2;
assert(isdigit(*index_1));
if(isdigit(index_1[-1]))
index_1--;
int ind1 = atoi(index_1);

//Now get the second index like normal
while(!isdigit(*m)) m++;
int ind2 = atoi(m);
Master &master = *(Master*)d.obj;

if(rtosc_narguments(m))
master.setPsysefxsend(ind1, ind2, rtosc_argument(m,0).i);
else
d.reply(d.loc, "i", master.Psysefxsend[ind1][ind2]);
}}
};

static const Ports master_ports = {
rRecursp(part, 16, "Part"),//NUM_MIDI_PARTS
rRecursp(sysefx, 4, "System Effect"),//NUM_SYS_EFX
rRecursp(insefx, 8, "Insertion Effect"),//NUM_INS_EFX
rRecur(microtonal, "Micrtonal Mapping Functionality"),
rRecur(ctl, "Controller"),
rParamZyn(Pkeyshift, "Global Key Shift"),
rArrayI(Pinsparts, NUM_INS_EFX, "Part to insert part onto"),
{"echo", rDoc("Hidden port to echo messages"), 0, [](const char *m, RtData&d) {
d.reply(m-1);}},
{"get-vu", rDoc("Grab VU Data"), 0, [](const char *, RtData &d) {
Master *m = (Master*)d.obj;
d.reply("/vu-meter", "bb", sizeof(m->vu), &m->vu, sizeof(float)*NUM_MIDI_PARTS, m->vuoutpeakpart);}},
{"reset-vu", rDoc("Grab VU Data"), 0, [](const char *, RtData &d) {
Master *m = (Master*)d.obj;
m->vuresetpeaks();}},
{"load-part:ib", rProp(internal) rDoc("Load Part From Middleware"), 0, [](const char *msg, RtData &d) {
Master *m = (Master*)d.obj;
Part *p = *(Part**)rtosc_argument(msg, 1).b.data;
int i = rtosc_argument(msg, 0).i;
m->part[i]->cloneTraits(*p);
m->part[i]->kill_rt();
d.reply("/free", "sb", "Part", sizeof(void*), &m->part[i]);
m->part[i] = p;
p->initialize_rt();
//printf("part %d is now pointer %p\n", i, p);
}},
{"Pvolume::i", rDoc("Master Volume"), 0,
[](const char *m, rtosc::RtData &d) {
if(rtosc_narguments(m)==0) {
d.reply(d.loc, "i", ((Master*)d.obj)->Pvolume);
} else if(rtosc_narguments(m)==1 && rtosc_type(m,0)=='i') {
((Master*)d.obj)->setPvolume(limit<char>(rtosc_argument(m,0).i,0,127));
d.broadcast(d.loc, "i", ((Master*)d.obj)->Pvolume);}}},
{"volume::i", rDoc("Master Volume"), 0,
[](const char *m, rtosc::RtData &d) {
if(rtosc_narguments(m)==0) {
d.reply(d.loc, "i", ((Master*)d.obj)->Pvolume);
} else if(rtosc_narguments(m)==1 && rtosc_type(m,0)=='i') {
//printf("looking at value %d\n", rtosc_argument(m,0).i);
//printf("limited value is %d\n", limit<char>(
// rtosc_argument(m,0).i, 0,127));
((Master*)d.obj)->setPvolume(limit<char>(rtosc_argument(m,0).i,0,127));
//printf("sets volume to value %d\n", ((Master*)d.obj)->Pvolume);
d.broadcast(d.loc, "i", ((Master*)d.obj)->Pvolume);}}},
{"Psysefxvol#" STRINGIFY(NUM_SYS_EFX) "/::i", 0, &sysefxPort,
[](const char *msg, rtosc::RtData &d) {
SNIP;
sysefxPort.dispatch(msg, d);
}},
{"sysefxfrom#" STRINGIFY(NUM_SYS_EFX) "/", rDoc("Routing Between System Effects"), &sysefsendto,
[](const char *msg, RtData&d) {
SNIP;
sysefsendto.dispatch(msg, d);
}},

{"noteOn:iii", rDoc("Noteon Event"), 0,
[](const char *m,RtData &d){
Master *M = (Master*)d.obj;
M->noteOn(rtosc_argument(m,0).i,rtosc_argument(m,1).i,rtosc_argument(m,2).i);}},

{"noteOff:ii", rDoc("Noteoff Event"), 0,
[](const char *m,RtData &d){
Master *M = (Master*)d.obj;
M->noteOff(rtosc_argument(m,0).i,rtosc_argument(m,1).i);}},

{"setController:iii", rDoc("MIDI CC Event"), 0,
[](const char *m,RtData &d){
Master *M = (Master*)d.obj;
M->setController(rtosc_argument(m,0).i,rtosc_argument(m,1).i,rtosc_argument(m,2).i);}},
{"Panic:", rDoc("Stop All Sound"), 0,
[](const char *, RtData &d) {
Master &M = *(Master*)d.obj;
M.ShutUp();
}},
{"freeze_state:", rDoc("Internal Read-only Mode"), 0,
[](const char *,RtData &d) {
Master *M = (Master*)d.obj;
std::atomic_thread_fence(std::memory_order_release);
M->frozenState = true;
d.reply("/state_frozen", "");}},
{"thaw_state:", rDoc("Internal Read-only Mode"), 0,
[](const char *,RtData &d) {
Master *M = (Master*)d.obj;
M->frozenState = false;}},
{"register:iis", rDoc("MIDI Mapping Registration"), 0,
[](const char *m,RtData &d){
Master *M = (Master*)d.obj;
M->midi.addElm(rtosc_argument(m,0).i, rtosc_argument(m,1).i,rtosc_argument(m,2).s);}},
{"learn:s", rDoc("Begin Learning for specified address"), 0,
[](const char *m, RtData &d){
Master *M = (Master*)d.obj;
printf("learning '%s'\n", rtosc_argument(m,0).s);
M->midi.learn(rtosc_argument(m,0).s);}},
{"unlearn:s", rDoc("Remove Learning for specified address"), 0,
[](const char *m, RtData &d){
Master *M = (Master*)d.obj;
M->midi.clear_entry(rtosc_argument(m,0).s);}},
{"close-ui", rDoc("Request to close any connection named \"GUI\""), 0, [](const char *, RtData &d) {
d.reply("/close-ui", "");}},
{"add-rt-memory:bi", rProp(internal) rDoc("Add Additional Memory To RT MemPool"), 0,
[](const char *msg, RtData &d)
{
Master &m = *(Master*)d.obj;
char *mem = *(char**)rtosc_argument(msg, 0).b.data;
int i = rtosc_argument(msg, 1).i;
m.memory->addMemory(mem, i);
m.pendingMemory = false;
}},
{"samplerate:", rMap(unit, Hz) rDoc("Synthesizer Global Sample Rate"), 0, [](const char *, RtData &d) {
Master &m = *(Master*)d.obj;
d.reply("/samplerate", "f", m.synth.samplerate_f);
}},
{"oscilsize:", rDoc("Synthesizer Global Oscillator Size"), 0, [](const char *, RtData &d) {
Master &m = *(Master*)d.obj;
d.reply("/oscilsize", "f", m.synth.oscilsize_f);
d.reply("/oscilsize", "i", m.synth.oscilsize);
}},
{"undo_pause",0,0,[](const char *, rtosc::RtData &d)
{d.reply("/undo_pause", "");}},
{"undo_resume",0,0,[](const char *, rtosc::RtData &d)
{d.reply("/undo_resume", "");}},
{"config/", 0, &Config::ports, [](const char *, rtosc::RtData &){}},
{"presets/", 0, &preset_ports, rBOIL_BEGIN
SNIP
preset_ports.dispatch(msg, data);
rBOIL_END},
};
const Ports &Master::ports = master_ports;

//XXX HACKS
Master *the_master;
rtosc::ThreadLink *the_bToU;

class DataObj:public rtosc::RtData
{
public:
DataObj(char *loc_, size_t loc_size_, void *obj_, rtosc::ThreadLink *bToU_)
{
memset(loc_, 0, loc_size_);
loc = loc_;
loc_size = loc_size_;
obj = obj_;
bToU = bToU_;
}

virtual void reply(const char *path, const char *args, ...) override
{
va_list va;
va_start(va,args);
char *buffer = bToU->buffer();
rtosc_vmessage(buffer,bToU->buffer_size(),path,args,va);
reply(buffer);
va_end(va);
}
virtual void reply(const char *msg) override
{
if(rtosc_message_length(msg, -1) == 0)
fprintf(stderr, "Warning: Invalid Rtosc message '%s'\n", msg);
bToU->raw_write(msg);
}
virtual void broadcast(const char *path, const char *args, ...) override{
va_list va;
va_start(va,args);
reply("/broadcast", "");
char *buffer = bToU->buffer();
rtosc_vmessage(buffer,bToU->buffer_size(),path,args,va);
reply(buffer);
va_end(va);
}
virtual void broadcast(const char *msg) override
{
reply("/broadcast");
reply(msg);
};
private:
rtosc::ThreadLink *bToU;
};

vuData::vuData(void)
:outpeakl(0.0f), outpeakr(0.0f), maxoutpeakl(0.0f), maxoutpeakr(0.0f),
rmspeakl(0.0f), rmspeakr(0.0f), clipped(0)
{}

static Master* masterInstance = NULL;

Master::Master()
Master::Master(const SYNTH_T &synth_)
:HDDRecorder(synth_), ctl(synth_), midi(Master::ports), frozenState(false), pendingMemory(false), synth(synth_)
{
bToU = NULL;
uToB = NULL;
memory = new Allocator();
the_master = this;
swaplr = 0;
off = 0;
smps = 0;
bufl = new float[synth->buffersize];
bufr = new float[synth->buffersize];
bufl = new float[synth.buffersize];
bufr = new float[synth.buffersize];

pthread_mutex_init(&mutex, NULL);
pthread_mutex_init(&vumutex, NULL);
fft = new FFTwrapper(synth->oscilsize);
fft = new FFTwrapper(synth.oscilsize);

shutup = 0;
for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart) {
@@ -66,18 +306,43 @@ Master::Master()
}

for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart)
part[npart] = new Part(&microtonal, fft, &mutex);
part[npart] = new Part(*memory, synth, &microtonal, fft);

//Insertion Effects init
for(int nefx = 0; nefx < NUM_INS_EFX; ++nefx)
insefx[nefx] = new EffectMgr(1, &mutex);
insefx[nefx] = new EffectMgr(*memory, synth, 1);

//System Effects init
for(int nefx = 0; nefx < NUM_SYS_EFX; ++nefx)
sysefx[nefx] = new EffectMgr(0, &mutex);
sysefx[nefx] = new EffectMgr(*memory, synth, 0);


defaults();

midi.event_cb = [](const char *m)
{
char loc_buf[1024];
DataObj d{loc_buf, 1024, the_master, the_bToU};
memset(loc_buf, 0, sizeof(loc_buf));
//printf("sending an event to the owner of '%s'\n", m);
Master::ports.dispatch(m+1, d);
};

midi.error_cb = [](const char *a, const char *b)
{
fprintf(stderr, "MIDI- got an error '%s' -- '%s'\n",a,b);
};
}

void Master::applyOscEvent(const char *msg)
{
char loc_buf[1024];
DataObj d{loc_buf, 1024, this, bToU};
memset(loc_buf, 0, sizeof(loc_buf));
d.matches = 0;
ports.dispatch(msg+1, d);
if(d.matches == 0)
fprintf(stderr, "Unknown path '%s'\n", msg);
}

void Master::defaults()
@@ -112,36 +377,6 @@ void Master::defaults()
ShutUp();
}

bool Master::mutexLock(lockset request)
{
switch(request) {
case MUTEX_TRYLOCK:
return !pthread_mutex_trylock(&mutex);
case MUTEX_LOCK:
return !pthread_mutex_lock(&mutex);
case MUTEX_UNLOCK:
return !pthread_mutex_unlock(&mutex);
}
return false;
}

Master &Master::getInstance()
{
if (!masterInstance)
masterInstance = new Master;

return *masterInstance;
}

void Master::deleteInstance()
{
if (masterInstance)
{
delete masterInstance;
masterInstance = NULL;
}
}

/*
* Note On Messages (velocity=0 for NoteOff)
*/
@@ -191,38 +426,36 @@ void Master::polyphonicAftertouch(char chan, char note, char velocity)
*/
void Master::setController(char chan, int type, int par)
{
if(frozenState)
return;
midi.process(chan,type,par);
if((type == C_dataentryhi) || (type == C_dataentrylo)
|| (type == C_nrpnhi) || (type == C_nrpnlo)) { //Process RPN and NRPN by the Master (ignore the chan)
ctl.setparameternumber(type, par);

int parhi = -1, parlo = -1, valhi = -1, vallo = -1;
if(ctl.getnrpn(&parhi, &parlo, &valhi, &vallo) == 0) //this is NRPN
//fprintf(stderr,"rcv. NRPN: %d %d %d %d\n",parhi,parlo,valhi,vallo);
switch(parhi) {
case 0x04: //System Effects
if(parlo < NUM_SYS_EFX)
sysefx[parlo]->seteffectpar_nolock(valhi, vallo);
;
sysefx[parlo]->seteffectparrt(valhi, vallo);
break;
case 0x08: //Insertion Effects
if(parlo < NUM_INS_EFX)
insefx[parlo]->seteffectpar_nolock(valhi, vallo);
;
insefx[parlo]->seteffectparrt(valhi, vallo);
break;
}
;
}
else
if(type == C_bankselectmsb) { // Change current bank
if(((unsigned int)par < bank.banks.size())
&& (bank.banks[par].dir != bank.bankfiletitle))
bank.loadbank(bank.banks[par].dir);
//if(((unsigned int)par < bank.banks.size())
// && (bank.banks[par].dir != bank.bankfiletitle))
// bank.loadbank(bank.banks[par].dir);
}
else { //other controllers
for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart) //Send the controller to all part assigned to the channel
if((chan == part[npart]->Prcvchn) && (part[npart]->Penabled != 0))
part[npart]->SetController(type, par);
;

if(type == C_allsoundsoff) { //cleanup insertion/system FX
for(int nefx = 0; nefx < NUM_SYS_EFX; ++nefx)
@@ -233,30 +466,12 @@ void Master::setController(char chan, int type, int par)
}
}

void Master::setProgram(char chan, unsigned int pgm)
{
if(config.cfg.IgnoreProgramChange)
return;

for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart)
if(chan == part[npart]->Prcvchn) {
bank.loadfromslot(pgm, part[npart]);

//Hack to get pad note parameters to update
//this is not real time safe and makes assumptions about the calling
//convention of this function...
pthread_mutex_unlock(&mutex);
part[npart]->applyparameters();
pthread_mutex_lock(&mutex);
}
}

void Master::vuUpdate(const float *outl, const float *outr)
{
//Peak computation (for vumeters)
vu.outpeakl = 1e-12;
vu.outpeakr = 1e-12;
for(int i = 0; i < synth->buffersize; ++i) {
for(int i = 0; i < synth.buffersize; ++i) {
if(fabs(outl[i]) > vu.outpeakl)
vu.outpeakl = fabs(outl[i]);
if(fabs(outr[i]) > vu.outpeakr)
@@ -272,12 +487,12 @@ void Master::vuUpdate(const float *outl, const float *outr)
//RMS Peak computation (for vumeters)
vu.rmspeakl = 1e-12;
vu.rmspeakr = 1e-12;
for(int i = 0; i < synth->buffersize; ++i) {
for(int i = 0; i < synth.buffersize; ++i) {
vu.rmspeakl += outl[i] * outl[i];
vu.rmspeakr += outr[i] * outr[i];
}
vu.rmspeakl = sqrt(vu.rmspeakl / synth->buffersize_f);
vu.rmspeakr = sqrt(vu.rmspeakr / synth->buffersize_f);
vu.rmspeakl = sqrt(vu.rmspeakl / synth.buffersize_f);
vu.rmspeakr = sqrt(vu.rmspeakr / synth.buffersize_f);

//Part Peak computation (for Part vumeters or fake part vumeters)
for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart) {
@@ -285,7 +500,7 @@ void Master::vuUpdate(const float *outl, const float *outr)
if(part[npart]->Penabled != 0) {
float *outl = part[npart]->partoutl,
*outr = part[npart]->partoutr;
for(int i = 0; i < synth->buffersize; ++i) {
for(int i = 0; i < synth.buffersize; ++i) {
float tmp = fabs(outl[i] + outr[i]);
if(tmp > vuoutpeakpart[npart])
vuoutpeakpart[npart] = tmp;
@@ -312,7 +527,6 @@ void Master::partonoff(int npart, int what)
for(int nefx = 0; nefx < NUM_INS_EFX; ++nefx) {
if(Pinsparts[nefx] == npart)
insefx[nefx]->cleanup();
;
}
}
else { //enabled
@@ -321,26 +535,135 @@ void Master::partonoff(int npart, int what)
}
}

#if 0
template <class T>
struct def_skip
{
static void skip(const char*& argptr) { argptr += sizeof(T); }
};

template <class T>
struct str_skip
{
static void skip(const char*& argptr) { while(argptr++); /*TODO: 4 padding */ }
};

template<class T, class Display = T, template<class TMP> class SkipsizeFunc = def_skip>
void _dump_prim_arg(const char*& argptr, std::ostream& os)
{
os << ' ' << (Display)*(const T*)argptr;
SkipsizeFunc<T>::skip(argptr);
}

void dump_msg(const char* ptr, std::ostream& os = std::cerr)
{
assert(*ptr == '/');
os << ptr;

while(*++ptr) ; // skip address
while(!*++ptr) ; // skip 0s

assert(*ptr == ',');
os << ' ' << (ptr + 1);

const char* argptr = ptr;
while(*++argptr) ; // skip type string
while(!*++argptr) ; // skip 0s

char c;
while((c = *++ptr))
{
switch(c)
{
case 'i':
_dump_prim_arg<int32_t>(argptr, os); break;
case 'c':
_dump_prim_arg<int32_t, char>(argptr, os); break;
// case 's':
// _dump_prim_arg<char, const char*>(argptr, os); break;
default:
exit(1);
}
}

}
#endif
int msg_id=0;

/*
* Master audio out (the final sound)
*/
void Master::AudioOut(float *outl, float *outr)
{
//Danger Limits
if(memory->lowMemory(2,1024*1024))
printf("QUITE LOW MEMORY IN THE RT POOL BE PREPARED FOR WEIRD BEHAVIOR!!\n");
//Normal Limits
if(!pendingMemory && memory->lowMemory(4,1024*1024)) {
printf("Requesting more memory\n");
bToU->write("/request-memory", "");
pendingMemory = true;
}
//Handle user events TODO move me to a proper location
char loc_buf[1024];
DataObj d{loc_buf, 1024, this, bToU};
memset(loc_buf, 0, sizeof(loc_buf));
int events = 0;
while(uToB && uToB->hasNext() && events < 10) {
const char *msg = uToB->read();

#ifndef PLUGINVERSION
if(!strcmp(msg, "/load-master")) {
Master *this_master = this;
Master *new_master = *(Master**)rtosc_argument(msg, 0).b.data;
new_master->AudioOut(outl, outr);
Nio::masterSwap(new_master);
bToU->write("/free", "sb", "Master", sizeof(Master*), &this_master);
return;
}
#endif

//XXX yes, this is not realtime safe, but it is useful...
if(strcmp(msg, "/get-vu") && false) {
fprintf(stdout, "%c[%d;%d;%dm", 0x1B, 0, 5 + 30, 0 + 40);
fprintf(stdout, "backend[%d]: '%s'<%s>\n", msg_id++, msg,
rtosc_argument_string(msg));
fprintf(stdout, "%c[%d;%d;%dm", 0x1B, 0, 7 + 30, 0 + 40);
}
d.matches = 0;
//fprintf(stdout, "address '%s'\n", uToB->peak());
ports.dispatch(msg+1, d);
events++;
if(!d.matches) {// && !ports.apropos(msg)) {
fprintf(stderr, "%c[%d;%d;%dm", 0x1B, 1, 7 + 30, 0 + 40);
fprintf(stderr, "Unknown address<BACKEND> '%s:%s'\n", uToB->peak(), rtosc_argument_string(uToB->peak()));
#if 0
if(strstr(msg, "PFMVelocity"))
dump_msg(msg);
if(ports.apropos(msg))
fprintf(stderr, " -> best match: '%s'\n", ports.apropos(msg)->name);
if(ports.apropos(msg+1))
fprintf(stderr, " -> best match: '%s'\n", ports.apropos(msg+1)->name);
#endif
fprintf(stderr, "%c[%d;%d;%dm", 0x1B, 0, 7 + 30, 0 + 40);
}
}
if(events>1 && false)
fprintf(stderr, "backend: %d events per cycle\n",events);

//Swaps the Left channel with Right Channel
if(swaplr)
swap(outl, outr);

//clean up the output samples (should not be needed?)
memset(outl, 0, synth->bufferbytes);
memset(outr, 0, synth->bufferbytes);
memset(outl, 0, synth.bufferbytes);
memset(outr, 0, synth.bufferbytes);

//Compute part samples and store them part[npart]->partoutl,partoutr
for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart) {
if(part[npart]->Penabled != 0 && !pthread_mutex_trylock(&part[npart]->load_mutex)) {
for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart)
if(part[npart]->Penabled)
part[npart]->ComputePartSmps();
pthread_mutex_unlock(&part[npart]->load_mutex);
}
}

//Insertion effects
for(int nefx = 0; nefx < NUM_INS_EFX; ++nefx)
@@ -354,7 +677,7 @@ void Master::AudioOut(float *outl, float *outr)

//Apply the part volumes and pannings (after insertion effects)
for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart) {
if(part[npart]->Penabled == 0)
if(!part[npart]->Penabled)
continue;

Stereo<float> newvol(part[npart]->volume),
@@ -366,26 +689,29 @@ void Master::AudioOut(float *outl, float *outr)
newvol.l *= pan * 2.0f;
else
newvol.r *= (1.0f - pan) * 2.0f;
//if(npart==0)
//printf("[%d]vol = %f->%f\n", npart, oldvol.l, newvol.l);

//the volume or the panning has changed and needs interpolation
if(ABOVE_AMPLITUDE_THRESHOLD(oldvol.l, newvol.l)
|| ABOVE_AMPLITUDE_THRESHOLD(oldvol.r, newvol.r)) {
for(int i = 0; i < synth->buffersize; ++i) {
for(int i = 0; i < synth.buffersize; ++i) {
Stereo<float> vol(INTERPOLATE_AMPLITUDE(oldvol.l, newvol.l,
i, synth->buffersize),
i, synth.buffersize),
INTERPOLATE_AMPLITUDE(oldvol.r, newvol.r,
i, synth->buffersize));
i, synth.buffersize));
part[npart]->partoutl[i] *= vol.l;
part[npart]->partoutr[i] *= vol.r;
}
part[npart]->oldvolumel = newvol.l;
part[npart]->oldvolumer = newvol.r;
}
else
for(int i = 0; i < synth->buffersize; ++i) { //the volume did not changed
else {
for(int i = 0; i < synth.buffersize; ++i) { //the volume did not changed
part[npart]->partoutl[i] *= newvol.l;
part[npart]->partoutr[i] *= newvol.r;
}
}
}


@@ -394,11 +720,11 @@ void Master::AudioOut(float *outl, float *outr)
if(sysefx[nefx]->geteffect() == 0)
continue; //the effect is disabled

float tmpmixl[synth->buffersize];
float tmpmixr[synth->buffersize];
float tmpmixl[synth.buffersize];
float tmpmixr[synth.buffersize];
//Clean up the samples used by the system effects
memset(tmpmixl, 0, synth->bufferbytes);
memset(tmpmixr, 0, synth->bufferbytes);
memset(tmpmixl, 0, synth.bufferbytes);
memset(tmpmixr, 0, synth.bufferbytes);

//Mix the channels according to the part settings about System Effect
for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart) {
@@ -412,7 +738,7 @@ void Master::AudioOut(float *outl, float *outr)

//the output volume of each part to system effect
const float vol = sysefxvol[nefx][npart];
for(int i = 0; i < synth->buffersize; ++i) {
for(int i = 0; i < synth.buffersize; ++i) {
tmpmixl[i] += part[npart]->partoutl[i] * vol;
tmpmixr[i] += part[npart]->partoutr[i] * vol;
}
@@ -422,7 +748,7 @@ void Master::AudioOut(float *outl, float *outr)
for(int nefxfrom = 0; nefxfrom < nefx; ++nefxfrom)
if(Psysefxsend[nefxfrom][nefx] != 0) {
const float vol = sysefxsend[nefxfrom][nefx];
for(int i = 0; i < synth->buffersize; ++i) {
for(int i = 0; i < synth.buffersize; ++i) {
tmpmixl[i] += sysefx[nefxfrom]->efxoutl[i] * vol;
tmpmixr[i] += sysefx[nefxfrom]->efxoutr[i] * vol;
}
@@ -432,7 +758,7 @@ void Master::AudioOut(float *outl, float *outr)

//Add the System Effect to sound output
const float outvol = sysefx[nefx]->sysefxgetvolume();
for(int i = 0; i < synth->buffersize; ++i) {
for(int i = 0; i < synth.buffersize; ++i) {
outl[i] += tmpmixl[i] * outvol;
outr[i] += tmpmixr[i] * outvol;
}
@@ -441,7 +767,7 @@ void Master::AudioOut(float *outl, float *outr)
//Mix all parts
for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart)
if(part[npart]->Penabled) //only mix active parts
for(int i = 0; i < synth->buffersize; ++i) { //the volume did not changed
for(int i = 0; i < synth.buffersize; ++i) { //the volume did not changed
outl[i] += part[npart]->partoutl[i];
outr[i] += part[npart]->partoutr[i];
}
@@ -453,20 +779,17 @@ void Master::AudioOut(float *outl, float *outr)


//Master Volume
for(int i = 0; i < synth->buffersize; ++i) {
for(int i = 0; i < synth.buffersize; ++i) {
outl[i] *= volume;
outr[i] *= volume;
}

if(!pthread_mutex_trylock(&vumutex)) {
vuUpdate(outl, outr);
pthread_mutex_unlock(&vumutex);
}
vuUpdate(outl, outr);

//Shutup if it is asked (with fade-out)
if(shutup) {
for(int i = 0; i < synth->buffersize; ++i) {
float tmp = (synth->buffersize_f - i) / synth->buffersize_f;
for(int i = 0; i < synth.buffersize; ++i) {
float tmp = (synth.buffersize_f - i) / synth.buffersize_f;
outl[i] *= tmp;
outr[i] *= tmp;
}
@@ -475,8 +798,6 @@ void Master::AudioOut(float *outl, float *outr)

//update the LFO's time
LFOParams::time++;

dump.inctick();
}

//TODO review the respective code from yoshimi for this
@@ -489,8 +810,8 @@ void Master::GetAudioOutSamples(size_t nsamples,
off_t out_off = 0;

//Fail when resampling rather than doing a poor job
if(synth->samplerate != samplerate) {
printf("darn it: %d vs %d\n", synth->samplerate, samplerate);
if(synth.samplerate != samplerate) {
printf("darn it: %d vs %d\n", synth.samplerate, samplerate);
return;
}

@@ -505,7 +826,7 @@ void Master::GetAudioOutSamples(size_t nsamples,
AudioOut(bufl, bufr);
off = 0;
out_off += smps;
smps = synth->buffersize;
smps = synth.buffersize;
}
else { //use some samples
memcpy(outl + out_off, bufl + off, sizeof(float) * nsamples);
@@ -530,9 +851,7 @@ Master::~Master()
delete sysefx[nefx];

delete fft;

pthread_mutex_destroy(&mutex);
pthread_mutex_destroy(&vumutex);
delete memory;
}


@@ -588,28 +907,28 @@ void Master::ShutUp()
*/
void Master::vuresetpeaks()
{
pthread_mutex_lock(&vumutex);
vu.outpeakl = 1e-9;
vu.outpeakr = 1e-9;
vu.maxoutpeakl = 1e-9;
vu.maxoutpeakr = 1e-9;
vu.clipped = 0;
pthread_mutex_unlock(&vumutex);
}

vuData Master::getVuData()
void Master::applyparameters(void)
{
vuData tmp;
pthread_mutex_lock(&vumutex);
tmp = vu;
pthread_mutex_unlock(&vumutex);
return tmp;
for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart)
part[npart]->applyparameters();
}

void Master::applyparameters(bool lockmutex)
void Master::initialize_rt(void)
{
for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart)
part[npart]->applyparameters(lockmutex);
for(int i=0; i<NUM_SYS_EFX; ++i)
sysefx[i]->init();
for(int i=0; i<NUM_INS_EFX; ++i)
insefx[i]->init();

for(int i=0; i<NUM_MIDI_PARTS; ++i)
part[i]->initialize_rt();
}

void Master::add2XML(XMLwrapper *xml)
@@ -673,9 +992,7 @@ int Master::getalldata(char **data)

xml->beginbranch("MASTER");

pthread_mutex_lock(&mutex);
add2XML(xml);
pthread_mutex_unlock(&mutex);

xml->endbranch();

@@ -695,9 +1012,7 @@ void Master::putalldata(char *data, int /*size*/)
if(xml->enterbranch("MASTER") == 0)
return;

pthread_mutex_lock(&mutex);
getfromXML(xml);
pthread_mutex_unlock(&mutex);

xml->exitbranch();

@@ -733,6 +1048,7 @@ int Master::loadXML(const char *filename)
xml->exitbranch();

delete (xml);
initialize_rt();
return 0;
}



+ 31
- 31
source/native-plugins/zynaddsubfx/Misc/Master.h View File

@@ -23,23 +23,17 @@

#ifndef MASTER_H
#define MASTER_H
#include <pthread.h>

#include "../globals.h"
#include "Microtonal.h"
#include <rtosc/miditable.h>
#include <rtosc/ports.h>

#include "Bank.h"
#include "Recorder.h"
#include "Dump.h"
#include "XMLwrapper.h"

#include "../Params/Controller.h"

typedef enum {
MUTEX_TRYLOCK, MUTEX_LOCK, MUTEX_UNLOCK
} lockset;

extern Dump dump;
class Allocator;

struct vuData {
vuData(void);
@@ -55,12 +49,11 @@ class Master
{
public:
/** Constructor TODO make private*/
Master();
Master(const SYNTH_T &synth);
/** Destructor*/
~Master();

static Master &getInstance();
static void deleteInstance();
void applyOscEvent(const char *event);

/**Saves all settings to a XML file
* @return 0 for ok or <0 if there is an error*/
@@ -75,28 +68,27 @@ class Master
/**loads all settings from a XML file
* @return 0 for ok or -1 if there is an error*/
int loadXML(const char *filename);
void applyparameters(bool lockmutex = true);

/**Regenerate PADsynth and other non-RT parameters
* It is NOT SAFE to call this from a RT context*/
void applyparameters(void) NONREALTIME;

//This must be called prior-to/at-the-time-of RT insertion
void initialize_rt(void) REALTIME;

void getfromXML(XMLwrapper *xml);

/**get all data to a newly allocated array (used for VST)
* @return the datasize*/
int getalldata(char **data);
int getalldata(char **data) NONREALTIME;
/**put all data from the *data array to zynaddsubfx parameters (used for VST)*/
void putalldata(char *data, int size);

//Mutex control
/**Control the Master's mutex state.
* @param lockset either trylock, lock, or unlock.
* @return true when successful false otherwise.*/
bool mutexLock(lockset request);

//Midi IN
void noteOn(char chan, char note, char velocity);
void noteOff(char chan, char note);
void polyphonicAftertouch(char chan, char note, char velocity);
void setController(char chan, int type, int par);
void setProgram(char chan, unsigned int pgm);
//void NRPN...


@@ -106,12 +98,12 @@ class Master
void vuUpdate(const float *outl, const float *outr);

/**Audio Output*/
void AudioOut(float *outl, float *outr);
void AudioOut(float *outl, float *outr) REALTIME;
/**Audio Output (for callback mode). This allows the program to be controled by an external program*/
void GetAudioOutSamples(size_t nsamples,
unsigned samplerate,
float *outl,
float *outr);
float *outr) REALTIME;


void partonoff(int npart, int what);
@@ -146,11 +138,8 @@ class Master

//peaks for VU-meter
void vuresetpeaks();
//get VU-meter data
vuData getVuData();

//peaks for part VU-meters
/**\todo synchronize this with a mutex*/
float vuoutpeakpart[NUM_MIDI_PARTS];
unsigned char fakepeakpart[NUM_MIDI_PARTS]; //this is used to compute the "peak" when the part is disabled

@@ -159,16 +148,27 @@ class Master

//other objects
Microtonal microtonal;
Bank bank;

//Strictly Non-RT instrument bank object
Bank bank;

class FFTwrapper * fft;
pthread_mutex_t mutex;
pthread_mutex_t vumutex;

static const rtosc::Ports &ports;
float volume;

private:
//Statistics on output levels
vuData vu;
float volume;

rtosc::MidiTable midi;//<1024,64>

bool frozenState;//read-only parameters for threadsafe actions
Allocator *memory;
rtosc::ThreadLink *bToU;
rtosc::ThreadLink *uToB;
bool pendingMemory;
const SYNTH_T &synth;
private:
float sysefxvol[NUM_SYS_EFX][NUM_MIDI_PARTS];
float sysefxsend[NUM_SYS_EFX][NUM_SYS_EFX];
int keyshift;


+ 83
- 66
source/native-plugins/zynaddsubfx/Misc/Microtonal.cpp View File

@@ -20,16 +20,63 @@

*/

#include <math.h>
#include <string.h>
#include <cmath>
#include <cstring>
#include <cstdio>

#include <rtosc/ports.h>
#include <rtosc/port-sugar.h>


#include "XMLwrapper.h"
#include "Util.h"
#include "Microtonal.h"


#define MAX_LINE_SIZE 80

#define rObject Microtonal
using namespace rtosc;

/**
* TODO
* Consider how much of this should really exist on the rt side of things.
* All the rt side needs is a function to map notes at various keyshifts to
* frequencies, which does not require this many parameters...
*
* A good lookup table should be a good finalization of this
*/
const rtosc::Ports Microtonal::ports = {
rToggle(Pinvertupdown, "key mapping inverse"),
rParamZyn(Pinvertupdowncenter, "center of the inversion"),
rToggle(Penabled, "Enable for microtonal mode"),
rParamZyn(PAnote, "The note for 'A'"),
rParamF(PAfreq, "Frequency of the 'A' note"),
rParamZyn(Pscaleshift, "UNDOCUMENTED"),
rParamZyn(Pfirstkey, "First key to retune"),
rParamZyn(Plastkey, "Last key to retune"),
rParamZyn(Pmiddlenote, "Scale degree 0 note"),

//TODO check to see if this should be exposed
rParamZyn(Pmapsize, "UNDOCUMENTED"),
rToggle(Pmappingenabled, "Mapping Enable"),

rParams(Pmapping, "UNDOCUMENTED"),
rParamZyn(Pglobalfinedetune, "Fine detune for all notes"),

rString(Pname, MICROTONAL_MAX_NAME_LEN, "Microtonal Name"),
rString(Pcomment, MICROTONAL_MAX_NAME_LEN, "Microtonal Name"),

{"octavesize:", 0, 0, [](const char*, RtData &d)
{
Microtonal &m = *(Microtonal*)d.obj;
d.reply(d.loc, "i", m.getoctavesize());
}},
};


Microtonal::Microtonal()
{
Pname = new unsigned char[MICROTONAL_MAX_NAME_LEN];
Pcomment = new unsigned char[MICROTONAL_MAX_NAME_LEN];
defaults();
}

@@ -76,10 +123,7 @@ void Microtonal::defaults()
}

Microtonal::~Microtonal()
{
delete [] Pname;
delete [] Pcomment;
}
{}

/*
* Get the size of the octave
@@ -106,13 +150,13 @@ float Microtonal::getnotefreq(int note, int keyshift) const
note = (int) Pinvertupdowncenter * 2 - note;

//compute global fine detune
float globalfinedetunerap = powf(2.0f,
(Pglobalfinedetune - 64.0f) / 1200.0f); //-64.0f .. 63.0f cents
float globalfinedetunerap =
powf(2.0f, (Pglobalfinedetune - 64.0f) / 1200.0f); //-64.0f .. 63.0f cents

if(Penabled == 0)
if(Penabled == 0) //12tET
return powf(2.0f,
(note - PAnote
+ keyshift) / 12.0f) * PAfreq * globalfinedetunerap; //12tET
+ keyshift) / 12.0f) * PAfreq * globalfinedetunerap;

int scaleshift =
((int)Pscaleshift - 64 + (int) octavesize * 100) % octavesize;
@@ -127,7 +171,7 @@ float Microtonal::getnotefreq(int note, int keyshift) const
}

//if the mapping is enabled
if(Pmappingenabled != 0) {
if(Pmappingenabled) {
if((note < Pfirstkey) || (note > Plastkey))
return -1.0f;
//Compute how many mapped keys are from middle note to reference note
@@ -144,11 +188,11 @@ float Microtonal::getnotefreq(int note, int keyshift) const
float rap_anote_middlenote =
(deltanote ==
0) ? (1.0f) : (octave[(deltanote - 1) % octavesize].tuning);
if(deltanote != 0)
if(deltanote)
rap_anote_middlenote *=
powf(octave[octavesize - 1].tuning,
(deltanote - 1) / octavesize);
if(minus != 0)
if(minus)
rap_anote_middlenote = 1.0f / rap_anote_middlenote;

//Convert from note (midi) to degree (note from the tunning)
@@ -175,7 +219,7 @@ float Microtonal::getnotefreq(int note, int keyshift) const
freq *= powf(octave[octavesize - 1].tuning, degoct);
freq *= PAfreq / rap_anote_middlenote;
freq *= globalfinedetunerap;
if(scaleshift != 0)
if(scaleshift)
freq /= octave[scaleshift - 1].tuning;
return freq * rap_keyshift;
}
@@ -189,11 +233,10 @@ float Microtonal::getnotefreq(int note, int keyshift) const
octave[(ntkey + octavesize - 1) % octavesize].tuning * powf(oct,
ntoct)
* PAfreq;
if(ntkey == 0)
if(!ntkey)
freq /= oct;
if(scaleshift != 0)
if(scaleshift)
freq /= octave[scaleshift - 1].tuning;
// fprintf(stderr,"note=%d freq=%.3f cents=%d\n",note,freq,(int)floor(logf(freq/PAfreq)/logf(2.0f)*1200.0f+0.5f));
freq *= globalfinedetunerap;
return freq * rap_keyshift;
}
@@ -460,77 +503,51 @@ int Microtonal::loadkbm(const char *filename)
{
FILE *file = fopen(filename, "r");
int x;
float tmpPAfreq = 440.0f;
char tmp[500];

fseek(file, 0, SEEK_SET);
//loads the mapsize
if(loadline(file, &tmp[0]) != 0)
if(loadline(file, tmp) != 0 || sscanf(tmp, "%d", &x) == 0)
return 2;
if(sscanf(&tmp[0], "%d", &x) == 0)
return 2;
if(x < 1)
x = 0;
if(x > 127)
x = 127; //just in case...
Pmapsize = x;
Pmapsize = limit(x, 0, 127);

//loads first MIDI note to retune
if(loadline(file, &tmp[0]) != 0)
if(loadline(file, tmp) != 0 || sscanf(tmp, "%d", &x) == 0)
return 2;
if(sscanf(&tmp[0], "%d", &x) == 0)
return 2;
if(x < 1)
x = 0;
if(x > 127)
x = 127; //just in case...
Pfirstkey = x;
Pfirstkey = limit(x, 0, 127);

//loads last MIDI note to retune
if(loadline(file, &tmp[0]) != 0)
return 2;
if(sscanf(&tmp[0], "%d", &x) == 0)
if(loadline(file, tmp) != 0 || sscanf(tmp, "%d", &x) == 0)
return 2;
if(x < 1)
x = 0;
if(x > 127)
x = 127; //just in case...
Plastkey = x;
Plastkey = limit(x, 0, 127);

//loads last the middle note where scale fro scale degree=0
if(loadline(file, &tmp[0]) != 0)
return 2;
if(sscanf(&tmp[0], "%d", &x) == 0)
if(loadline(file, tmp) != 0 || sscanf(tmp, "%d", &x) == 0)
return 2;
if(x < 1)
x = 0;
if(x > 127)
x = 127; //just in case...
Pmiddlenote = x;
Pmiddlenote = limit(x, 0, 127);

//loads the reference note
if(loadline(file, &tmp[0]) != 0)
if(loadline(file, tmp) != 0 || sscanf(tmp, "%d", &x) == 0)
return 2;
if(sscanf(&tmp[0], "%d", &x) == 0)
return 2;
if(x < 1)
x = 0;
if(x > 127)
x = 127; //just in case...
PAnote = x;
PAnote = limit(x,0,127);

//loads the reference freq.
if(loadline(file, &tmp[0]) != 0)
return 2;
float tmpPAfreq = 440.0f;
if(sscanf(&tmp[0], "%f", &tmpPAfreq) == 0)
if(loadline(file, tmp) != 0 || sscanf(tmp, "%f", &tmpPAfreq) == 0)
return 2;
PAfreq = tmpPAfreq;

//the scale degree(which is the octave) is not loaded, it is obtained by the tunnings with getoctavesize() method
//the scale degree(which is the octave) is not loaded,
//it is obtained by the tunnings with getoctavesize() method
if(loadline(file, &tmp[0]) != 0)
return 2;

//load the mappings
if(Pmapsize != 0) {
for(int nline = 0; nline < Pmapsize; ++nline) {
if(loadline(file, &tmp[0]) != 0)
if(loadline(file, tmp) != 0)
return 2;
if(sscanf(&tmp[0], "%d", &x) == 0)
if(sscanf(tmp, "%d", &x) == 0)
x = -1;
Pmapping[nline] = x;
}


+ 8
- 6
source/native-plugins/zynaddsubfx/Misc/Microtonal.h View File

@@ -23,13 +23,12 @@
#ifndef MICROTONAL_H
#define MICROTONAL_H

#include <cstdio>
#include "../globals.h"
#include "XMLwrapper.h"

#define MAX_OCTAVE_SIZE 128
#define MICROTONAL_MAX_NAME_LEN 120

#include <stdio.h>
class XMLwrapper;


/**Tuning settings and microtonal capabilities*/
@@ -102,9 +101,9 @@ class Microtonal
void texttomapping(const char *text);

/**Name of Microtonal tuning*/
unsigned char *Pname;
char Pname[MICROTONAL_MAX_NAME_LEN];
/**Comment about the tuning*/
unsigned char *Pcomment;
char Pcomment[MICROTONAL_MAX_NAME_LEN];

void add2XML(XMLwrapper *xml) const;
void getfromXML(XMLwrapper *xml);
@@ -115,9 +114,12 @@ class Microtonal
bool operator==(const Microtonal &micro) const;
bool operator!=(const Microtonal &micro) const;

static const rtosc::Ports ports;
private:
int linetotunings(unsigned int nline, const char *line);
int loadline(FILE *file, char *line); //loads a line from the text file, while ignoring the lines beggining with "!"
//loads a line from the text file, while ignoring the lines beggining with "!"
int loadline(FILE *file, char *line);
//Grab a 0..127 integer from the provided descriptor
unsigned char octavesize;
struct {
unsigned char type; //1 for cents or 2 for division


+ 1296
- 0
source/native-plugins/zynaddsubfx/Misc/MiddleWare.cpp
File diff suppressed because it is too large
View File


+ 40
- 0
source/native-plugins/zynaddsubfx/Misc/MiddleWare.h View File

@@ -0,0 +1,40 @@
#pragma once
#include <functional>
#include <cstdarg>
#include <string>

struct SYNTH_T;
//Link between realtime and non-realtime layers
class MiddleWare
{
public:
MiddleWare(SYNTH_T synth, int prefered_port = -1);
~MiddleWare(void);
//returns internal master pointer
class Master *spawnMaster(void);
//return UI interface
class Fl_Osc_Interface *spawnUiApi(void);
//Set callback to push UI events to
void setUiCallback(void(*cb)(void*,const char *),void *ui);
//Set callback to run while busy
void setIdleCallback(void(*cb)(void));
//Handle events
void tick(void);
//Do A Readonly Operation (For Parameter Copy)
void doReadOnlyOp(std::function<void()>);
//Handle a rtosc Message uToB
void transmitMsg(const char *);
//Handle a rtosc Message uToB
void transmitMsg(const char *, const char *args, ...);
//Handle a rtosc Message uToB
void transmitMsg(const char *, const char *args, va_list va);
//Indicate that a program will be loaded on a known part
void pendingSetProgram(int part, int program);
//Get/Set the active bToU url
std::string activeUrl(void);
void activeUrl(std::string u);
//View Synthesis Parameters
const SYNTH_T &getSynth(void) const;
private:
class MiddleWareImpl *impl;
};

+ 491
- 442
source/native-plugins/zynaddsubfx/Misc/Part.cpp
File diff suppressed because it is too large
View File


+ 52
- 51
source/native-plugins/zynaddsubfx/Misc/Part.h View File

@@ -27,18 +27,8 @@

#include "../globals.h"
#include "../Params/Controller.h"
#include "../Misc/Microtonal.h"

#include <pthread.h>
#include <list> // For the monomemnotes list.

class EffectMgr;
class ADnoteParameters;
class SUBnoteParameters;
class PADnoteParameters;
class SynthNote;
class XMLWrapper;
class FFTwrapper;
#include <functional>

/** Part implementation*/
class Part
@@ -46,29 +36,29 @@ class Part
public:
/**Constructor
* @param microtonal_ Pointer to the microtonal object
* @param fft_ Pointer to the FFTwrapper
* @param mutex_ Pointer to the master pthread_mutex_t*/
Part(Microtonal *microtonal_, FFTwrapper *fft_, pthread_mutex_t *mutex_);
* @param fft_ Pointer to the FFTwrapper*/
Part(Allocator &alloc, const SYNTH_T &synth, Microtonal *microtonal_, FFTwrapper *fft_);
/**Destructor*/
~Part();

// Copy misc parameters not stored in .xiz format
void cloneTraits(Part &part) const REALTIME;

// Midi commands implemented
void NoteOn(unsigned char note,
unsigned char velocity,
int masterkeyshift);
void NoteOff(unsigned char note);
int masterkeyshift) REALTIME;
void NoteOff(unsigned char note) REALTIME;
void PolyphonicAftertouch(unsigned char note,
unsigned char velocity,
int masterkeyshift);
void AllNotesOff(); //panic
void SetController(unsigned int type, int par);
void RelaseSustainedKeys(); //this is called when the sustain pedal is relased
void RelaseAllKeys(); //this is called on AllNotesOff controller
int masterkeyshift) REALTIME;
void AllNotesOff() REALTIME; //panic
void SetController(unsigned int type, int par) REALTIME;
void ReleaseSustainedKeys() REALTIME; //this is called when the sustain pedal is released
void ReleaseAllKeys() REALTIME; //this is called on AllNotesOff controller

/* The synthesizer part output */
void ComputePartSmps(); //Part output

//instrumentonly: 0 - save all, 1 - save only instrumnet, 2 - save only instrument without the name(used in bank)
void ComputePartSmps() REALTIME; //Part output


//saves the instrument settings to a XML file
@@ -82,7 +72,11 @@ class Part
void defaults();
void defaultsinstrument();

void applyparameters(bool lockmutex = true);
void applyparameters(void) NONREALTIME;
void applyparameters(std::function<bool()> do_abort) NONREALTIME;

void initialize_rt(void) REALTIME;
void kill_rt(void) REALTIME;

void getfromXML(XMLwrapper *xml);
void getfromXMLinstrument(XMLwrapper *xml);
@@ -90,22 +84,25 @@ class Part
void cleanup(bool final = false);

//the part's kit
struct {
unsigned char Penabled, Pmuted, Pminkey, Pmaxkey;
unsigned char *Pname;
unsigned char Padenabled, Psubenabled, Ppadenabled;
struct Kit {
bool Penabled, Pmuted;
unsigned char Pminkey, Pmaxkey;
char *Pname;
bool Padenabled, Psubenabled, Ppadenabled;
unsigned char Psendtoparteffect;
ADnoteParameters *adpars;
SUBnoteParameters *subpars;
PADnoteParameters *padpars;

const static rtosc::Ports &ports;
} kit[NUM_KIT_ITEMS];


//Part parameters
void setkeylimit(unsigned char Pkeylimit);
void setkititemstatus(int kititem, int Penabled_);
void setkititemstatus(unsigned kititem, bool Penabled_);

unsigned char Penabled; /**<if the part is enabled*/
bool Penabled; /**<if the part is enabled*/
unsigned char Pvolume; /**<part volume*/
unsigned char Pminkey; /**<the minimum key that the part receives noteon messages*/
unsigned char Pmaxkey; //the maximum key that the part receives noteon messages
@@ -116,19 +113,19 @@ class Part
void setPpanning(char Ppanning);
unsigned char Pvelsns; //velocity sensing (amplitude velocity scale)
unsigned char Pveloffs; //velocity offset
unsigned char Pnoteon; //if the part receives NoteOn messages
unsigned char Pkitmode; //if the kitmode is enabled
unsigned char Pdrummode; //if all keys are mapped and the system is 12tET (used for drums)
bool Pnoteon; //if the part receives NoteOn messages
int Pkitmode; //if the kitmode is enabled
bool Pdrummode; //if all keys are mapped and the system is 12tET (used for drums)

unsigned char Ppolymode; //Part mode - 0=monophonic , 1=polyphonic
unsigned char Plegatomode; // 0=normal, 1=legato
unsigned char Pkeylimit; //how many keys are alowed to be played same time (0=off), the older will be relased
bool Ppolymode; //Part mode - 0=monophonic , 1=polyphonic
bool Plegatomode; // 0=normal, 1=legato
unsigned char Pkeylimit; //how many keys are alowed to be played same time (0=off), the older will be released

unsigned char *Pname; //name of the instrument
char *Pname; //name of the instrument
struct { //instrument additional information
unsigned char Ptype;
unsigned char Pauthor[MAX_INFO_TEXT_SIZE + 1];
unsigned char Pcomments[MAX_INFO_TEXT_SIZE + 1];
char Pauthor[MAX_INFO_TEXT_SIZE + 1];
char Pcomments[MAX_INFO_TEXT_SIZE + 1];
} info;


@@ -139,7 +136,7 @@ class Part
*partfxinputr[NUM_PART_EFX + 1]; //partfxinput l/r [NUM_PART_EFX] is for "no effect" buffer

enum NoteStatus {
KEY_OFF, KEY_PLAYING, KEY_RELASED_AND_SUSTAINED, KEY_RELASED
KEY_OFF, KEY_PLAYING, KEY_RELEASED_AND_SUSTAINED, KEY_RELEASED
};

float volume, oldvolumel, oldvolumer; //this is applied by Master
@@ -151,16 +148,14 @@ class Part
unsigned char Pefxroute[NUM_PART_EFX]; //how the effect's output is routed(to next effect/to out)
bool Pefxbypass[NUM_PART_EFX]; //if the effects are bypassed


pthread_mutex_t *mutex;
pthread_mutex_t load_mutex;

int lastnote;

const static rtosc::Ports &ports;

private:
void RunNote(unsigned k);
void KillNotePos(int pos);
void RelaseNotePos(int pos);
void ReleaseNotePos(int pos);
void MonoMemRenote(); // MonoMem stuff.

int killallnotes; //is set to 1 if I want to kill all notes
@@ -170,9 +165,7 @@ class Part
int note; //if there is no note playing, the "note"=-1
int itemsplaying;
struct {
SynthNote *adnote,
*subnote,
*padnote;
SynthNote *adnote, *subnote, *padnote;
int sendtoparteffect;
} kititem[NUM_KIT_ITEMS];
int time;
@@ -182,7 +175,13 @@ class Part
bool lastlegatomodevalid; // To keep track of previous legatomodevalid.

// MonoMem stuff
std::list<unsigned char> monomemnotes; // A list to remember held notes.
void monomemPush(char note);
void monomemPop(char note);
char monomemBack(void) const;
bool monomemEmpty(void) const;
void monomemClear(void);

short monomemnotes[256]; // A list to remember held notes.
struct {
unsigned char velocity;
int mkeyshift; // I'm not sure masterkeyshift should be remembered.
@@ -192,11 +191,13 @@ class Part
store the velocity and masterkeyshift values of a given note (the list only store note values).
For example 'monomem[note].velocity' would be the velocity value of the note 'note'.*/

PartNotes partnote[POLIPHONY];
PartNotes partnote[POLYPHONY];

float oldfreq; //this is used for portamento
Microtonal *microtonal;
FFTwrapper *fft;
Allocator &memory;
const SYNTH_T &synth;
};

#endif

+ 479
- 0
source/native-plugins/zynaddsubfx/Misc/PresetExtractor.cpp View File

@@ -0,0 +1,479 @@
/**
* Extract Presets from realtime data
*/

#include "../Params/PresetsStore.h"

#include "../Misc/Master.h"
#include "../Misc/Util.h"
#include "../Misc/Allocator.h"
#include "../Effects/EffectMgr.h"
#include "../Synth/OscilGen.h"
#include "../Synth/Resonance.h"
#include "../Params/ADnoteParameters.h"
#include "../Params/EnvelopeParams.h"
#include "../Params/FilterParams.h"
#include "../Params/LFOParams.h"
#include "../Params/PADnoteParameters.h"
#include "../Params/Presets.h"
#include "../Params/PresetsArray.h"
#include "../Params/PresetsStore.h"
#include "../Params/SUBnoteParameters.h"
#include "../Misc/MiddleWare.h"
#include "PresetExtractor.h"
#include <rtosc/ports.h>
#include <rtosc/port-sugar.h>
#include <string>
using std::string;

static void dummy(const char *, rtosc::RtData&) {}

const rtosc::Ports real_preset_ports =
{
{"scan-for-presets:", 0, 0,
[](const char *msg, rtosc::RtData &d) {
presetsstore.scanforpresets();
auto &pre = presetsstore.presets;
d.reply(d.loc, "i", pre.size());
for(unsigned i=0; i<pre.size();++i)
d.reply(d.loc, "isss", i,
pre[i].file.c_str(),
pre[i].name.c_str(),
pre[i].type.c_str());

}},
{"copy:s:ss:si:ssi", 0, 0,
[](const char *msg, rtosc::RtData &d) {
MiddleWare &mw = *(MiddleWare*)d.obj;
std::string args = rtosc_argument_string(msg);
d.reply(d.loc, "s", "clipboard copy...");
printf("\nClipboard Copy...\n");
if(args == "s")
presetCopy(mw, rtosc_argument(msg, 0).s, "");
else if(args == "ss")
presetCopy(mw, rtosc_argument(msg, 0).s,
rtosc_argument(msg, 1).s);
else if(args == "si")
presetCopyArray(mw, rtosc_argument(msg, 0).s,
rtosc_argument(msg, 1).i, "");
else if(args == "ssi")
presetCopyArray(mw, rtosc_argument(msg, 0).s,
rtosc_argument(msg, 2).i, rtosc_argument(msg, 1).s);
else
assert(false && "bad arguments");
}},
{"paste:s:ss:si:ssi", 0, 0,
[](const char *msg, rtosc::RtData &d) {
MiddleWare &mw = *(MiddleWare*)d.obj;
std::string args = rtosc_argument_string(msg);
d.reply(d.loc, "s", "clipboard paste...");
printf("\nClipboard Paste...\n");
if(args == "s")
presetPaste(mw, rtosc_argument(msg, 0).s, "");
else if(args == "ss")
presetPaste(mw, rtosc_argument(msg, 0).s,
rtosc_argument(msg, 1).s);
else if(args == "si")
presetPasteArray(mw, rtosc_argument(msg, 0).s,
rtosc_argument(msg, 1).i, "");
else if(args == "ssi")
presetPasteArray(mw, rtosc_argument(msg, 0).s,
rtosc_argument(msg, 2).i, rtosc_argument(msg, 1).s);
else
assert(false && "bad arguments");
}},
{"clipboard-type:", 0, 0,
[](const char *msg, rtosc::RtData &d) {
d.reply(d.loc, "s", presetsstore.clipboard.type.c_str());
}},
{"delete:s", 0, 0,
[](const char *msg, rtosc::RtData &d) {
presetsstore.deletepreset(rtosc_argument(msg,0).s);
}},

};


const rtosc::Ports preset_ports
{
{"scan-for-presets:", rDoc("Scan For Presets"), 0, dummy},
{"copy:s:ss:si:ssi", rDoc("Copy <s> URL to <s> Name/Clipboard from subfield <i>"), 0, dummy},
{"paste:s:ss:si:ssi", rDoc("Paste <s> URL to <s> File-Name/Clipboard from subfield <i>"), 0, dummy},
{"clipboard-type:", rDoc("Type Stored In Clipboard"), 0, dummy},
{"delete:s", rDoc("Delete the given preset file"), 0, dummy},
};

//Relevant types to keep in mind
//Effects/EffectMgr.cpp: setpresettype("Peffect");
//Params/ADnoteParameters.cpp: setpresettype("Padsynth");
//Params/EnvelopeParams.cpp: //setpresettype("Penvamplitude");
//Params/EnvelopeParams.cpp: //setpresettype("Penvamplitude");
//Params/EnvelopeParams.cpp: //setpresettype("Penvfrequency");
//Params/EnvelopeParams.cpp: //setpresettype("Penvfilter");
//Params/EnvelopeParams.cpp: //setpresettype("Penvbandwidth");
//Params/FilterParams.cpp: //setpresettype("Pfilter");
//Params/LFOParams.cpp: // setpresettype("Plfofrequency");
//Params/LFOParams.cpp: // setpresettype("Plfoamplitude");
//Params/LFOParams.cpp: // setpresettype("Plfofilter");
//Params/PADnoteParameters.cpp: setpresettype("Ppadsynth");
//Params/SUBnoteParameters.cpp: setpresettype("Psubsynth");
//Synth/OscilGen.cpp: setpresettype("Poscilgen");
//Synth/Resonance.cpp: setpresettype("Presonance");


//Translate newer symbols to old preset types
std::vector<string> translate_preset_types(std::string metatype)
{
std::vector<string> results;
return results;
}


/*****************************************************************************
* Implementation Methods *
*****************************************************************************/
class Capture:public rtosc::RtData
{
public:
Capture(void *obj_)
{
matches = 0;
memset(locbuf, 0, sizeof(locbuf));
loc = locbuf;
loc_size = sizeof(locbuf);
obj = obj_;
}

virtual void reply(const char *path, const char *args, ...)
{
printf("reply(%p)(%s)(%s)...\n", msgbuf, path, args);
//printf("size is %d\n", sizeof(msgbuf));
va_list va;
va_start(va,args);
char *buffer = msgbuf;
rtosc_vmessage(buffer,sizeof(msgbuf),path,args,va);
va_end(va);
}
char msgbuf[1024];
char locbuf[1024];
};

template <class T>
T capture(Master *m, std::string url);

template <>
std::string capture(Master *m, std::string url)
{
Capture c(m);
char query[1024];
rtosc_message(query, 1024, url.c_str(), "");
Master::ports.dispatch(query+1,c);
if(rtosc_message_length(c.msgbuf, sizeof(c.msgbuf))) {
if(rtosc_type(c.msgbuf, 0) == 's')
return rtosc_argument(c.msgbuf,0).s;
}

return "";
}

template <>
void *capture(Master *m, std::string url)
{
Capture c(m);
char query[1024];
rtosc_message(query, 1024, url.c_str(), "");
Master::ports.dispatch(query+1,c);
if(rtosc_message_length(c.msgbuf, sizeof(c.msgbuf))) {
if(rtosc_type(c.msgbuf, 0) == 'b' &&
rtosc_argument(c.msgbuf, 0).b.len == sizeof(void*))
return *(void**)rtosc_argument(c.msgbuf,0).b.data;
}

return NULL;
}

template<class T>
std::string doCopy(MiddleWare &mw, string url, string name)
{
XMLwrapper xml;
mw.doReadOnlyOp([&xml, url, name, &mw](){
Master *m = mw.spawnMaster();
//Get the pointer
T *t = (T*)capture<void*>(m, url+"self");
//Extract Via mxml
//t->add2XML(&xml);
t->copy(presetsstore, name.empty()? NULL:name.c_str());
});

return "";//xml.getXMLdata();
}

template<class T, typename... Ts>
void doPaste(MiddleWare &mw, string url, string type, XMLwrapper &xml, Ts&&... args)
{
//Generate a new object
T *t = new T(std::forward<Ts>(args)...);
if(xml.enterbranch(type) == 0)
return;

t->getfromXML(&xml);

//Send the pointer
string path = url+"paste";
char buffer[1024];
rtosc_message(buffer, 1024, path.c_str(), "b", sizeof(void*), &t);
if(!Master::ports.apropos(path.c_str()))
fprintf(stderr, "Warning: Missing Paste URL: '%s'\n", path.c_str());
printf("Sending info to '%s'\n", buffer);
mw.transmitMsg(buffer);

//Let the pointer be reclaimed later
}

template<class T>
std::string doArrayCopy(MiddleWare &mw, int field, string url, string name)
{
XMLwrapper xml;
printf("Getting info from '%s'<%d>\n", url.c_str(), field);
mw.doReadOnlyOp([&xml, url, field, name, &mw](){
Master *m = mw.spawnMaster();
//Get the pointer
T *t = (T*)capture<void*>(m, url+"self");
//Extract Via mxml
t->copy(presetsstore, field, name.empty()?NULL:name.c_str());
});

return "";//xml.getXMLdata();
}

template<class T, typename... Ts>
void doArrayPaste(MiddleWare &mw, int field, string url, string type,
XMLwrapper &xml, Ts&&... args)
{
//Generate a new object
T *t = new T(std::forward<Ts>(args)...);

if(xml.enterbranch(type+"n") == 0) {
delete t;
return;
}
t->defaults(field);
t->getfromXMLsection(&xml, field);
xml.exitbranch();

//Send the pointer
string path = url+"paste-array";
char buffer[1024];
rtosc_message(buffer, 1024, path.c_str(), "bi", sizeof(void*), &t, field);
if(!Master::ports.apropos(path.c_str()))
fprintf(stderr, "Warning: Missing Paste URL: '%s'\n", path.c_str());
printf("Sending info to '%s'<%d>\n", buffer, field);
mw.transmitMsg(buffer);

//Let the pointer be reclaimed later
}

/*
* Dispatch to class specific operators
*
* Oscilgen and PADnoteParameters have mixed RT/non-RT parameters and require
* extra handling.
* See MiddleWare.cpp for these specifics
*/
void doClassPaste(std::string type, std::string type_, MiddleWare &mw, string url, XMLwrapper &data)
{
printf("Class Paste\n");
if(type == "EnvelopeParams")
doPaste<EnvelopeParams>(mw, url, type_, data);
else if(type == "LFOParams")
doPaste<LFOParams>(mw, url, type_, data);
else if(type == "FilterParams")
doPaste<FilterParams>(mw, url, type_, data);
else if(type == "ADnoteParameters")
doPaste<ADnoteParameters>(mw, url, type_, data, mw.getSynth(), (FFTwrapper*)NULL);
else if(type == "PADnoteParameters")
doPaste<PADnoteParameters>(mw, url, type_, data, mw.getSynth(), (FFTwrapper*)NULL);
else if(type == "SUBnoteParameters")
doPaste<SUBnoteParameters>(mw, url, type_, data);
else if(type == "OscilGen")
doPaste<OscilGen>(mw, url, type_, data, mw.getSynth(), (FFTwrapper*)NULL, (Resonance*)NULL);
else if(type == "Resonance")
doPaste<Resonance>(mw, url, type_, data);
else if(type == "EffectMgr")
doPaste<EffectMgr>(mw, url, type_, data, DummyAlloc, mw.getSynth(), false);
else {
fprintf(stderr, "Warning: Unknown type<%s> from url<%s>\n", type.c_str(), url.c_str());
}
}

std::string doClassCopy(std::string type, MiddleWare &mw, string url, string name)
{
if(type == "EnvelopeParams")
return doCopy<EnvelopeParams>(mw, url, name);
else if(type == "LFOParams")
return doCopy<LFOParams>(mw, url, name);
else if(type == "FilterParams")
return doCopy<FilterParams>(mw, url, name);
else if(type == "ADnoteParameters")
return doCopy<ADnoteParameters>(mw, url, name);
else if(type == "PADnoteParameters")
return doCopy<PADnoteParameters>(mw, url, name);
else if(type == "SUBnoteParameters")
return doCopy<SUBnoteParameters>(mw, url, name);
else if(type == "OscilGen")
return doCopy<OscilGen>(mw, url, name);
else if(type == "Resonance")
return doCopy<Resonance>(mw, url, name);
else if(type == "EffectMgr")
doCopy<EffectMgr>(mw, url, name);
return "UNDEF";
}

void doClassArrayPaste(std::string type, std::string type_, int field, MiddleWare &mw, string url,
XMLwrapper &data)
{
if(type == "FilterParams")
doArrayPaste<FilterParams>(mw, field, url, type_, data);
else if(type == "ADnoteParameters")
doArrayPaste<ADnoteParameters>(mw, field, url, type_, data, mw.getSynth(), (FFTwrapper*)NULL);
}

std::string doClassArrayCopy(std::string type, int field, MiddleWare &mw, string url, string name)
{
if(type == "FilterParams")
return doArrayCopy<FilterParams>(mw, field, url, name);
else if(type == "ADnoteParameters")
return doArrayCopy<ADnoteParameters>(mw, field, url, name);
return "UNDEF";
}

//This is an abuse of the readonly op, but one that might look reasonable from a
//user perspective...
std::string getUrlPresetType(std::string url, MiddleWare &mw)
{
std::string result;
mw.doReadOnlyOp([url, &result, &mw](){
Master *m = mw.spawnMaster();
//Get the pointer
result = capture<std::string>(m, url+"preset-type");
});
printf("preset type = %s\n", result.c_str());
return result;
}

std::string getUrlType(std::string url)
{
assert(!url.empty());
printf("Searching for '%s'\n", (url+"self").c_str());
auto self = Master::ports.apropos((url+"self").c_str());
if(!self)
fprintf(stderr, "Warning: URL Metadata Not Found For '%s'\n", url.c_str());

if(self)
return self->meta()["class"];
else
return "";
}


/*****************************************************************************
* API Stubs *
*****************************************************************************/

#if 0
Clipboard clipboardCopy(MiddleWare &mw, string url)
{
//Identify The Self Type of the Object
string type = getUrlType(url);
printf("Copying a '%s' object", type.c_str());

//Copy The Object
string data = doClassCopy(type, mw, url);
printf("Object Information '%s'\n", data.c_str());

return {type, data};
}

void clipBoardPaste(const char *url, Clipboard clip)
{
(void) url;
(void) clip;
}
#endif

void presetCopy(MiddleWare &mw, std::string url, std::string name)
{
(void) name;
doClassCopy(getUrlType(url), mw, url, name);
printf("PresetCopy()\n");
}
void presetPaste(MiddleWare &mw, std::string url, std::string name)
{
(void) name;
printf("PresetPaste()\n");
string data = "";
XMLwrapper xml;
if(name.empty()) {
data = presetsstore.clipboard.data;
if(data.length() < 20)
return;
if(!xml.putXMLdata(data.c_str()))
return;
} else {
if(xml.loadXMLfile(name))
return;
}

doClassPaste(getUrlType(url), getUrlPresetType(url, mw), mw, url, xml);
}
void presetCopyArray(MiddleWare &mw, std::string url, int field, std::string name)
{
(void) name;
printf("PresetArrayCopy()\n");
doClassArrayCopy(getUrlType(url), field, mw, url, name);
}
void presetPasteArray(MiddleWare &mw, std::string url, int field, std::string name)
{
(void) name;
printf("PresetArrayPaste()\n");
string data = "";
XMLwrapper xml;
if(name.empty()) {
data = presetsstore.clipboard.data;
if(data.length() < 20)
return;
if(!xml.putXMLdata(data.c_str()))
return;
} else {
if(xml.loadXMLfile(name))
return;
}
printf("Performing Paste...\n");
doClassArrayPaste(getUrlType(url), getUrlPresetType(url, mw), field, mw, url, xml);
}
#if 0
void presetPaste(std::string url, int)
{
printf("PresetPaste()\n");
doClassPaste(getUrlType(url), *middlewarepointer, url, presetsstore.clipboard.data);
}
#endif
void presetDelete(int)
{
printf("PresetDelete()\n");
}
void presetRescan()
{
printf("PresetRescan()\n");
}
std::string presetClipboardType()
{
printf("PresetClipboardType()\n");
return "dummy";
}
bool presetCheckClipboardType()
{
printf("PresetCheckClipboardType()\n");
return true;
}

+ 22
- 0
source/native-plugins/zynaddsubfx/Misc/PresetExtractor.h View File

@@ -0,0 +1,22 @@
#pragma once
#include <string>
#include <rtosc/ports.h>

extern const rtosc::Ports real_preset_ports;
extern const rtosc::Ports preset_ports;
struct Clipboard {
std::string data;
std::string type;
};

Clipboard clipboardCopy(class MiddleWare &mw, std::string url);

void presetCopy(MiddleWare &mw, std::string url, std::string name);
void presetPaste(MiddleWare &mw, std::string url, std::string name);
void presetCopyArray(MiddleWare &mw, std::string url, int field, std::string name);
void presetPasteArray(MiddleWare &mw, std::string url, int field, std::string name);
void presetPaste(std::string url, int);
void presetDelete(int);
void presetRescan();
std::string presetClipboardType();
bool presetCheckClipboardType();

+ 4
- 3
source/native-plugins/zynaddsubfx/Misc/Recorder.cpp View File

@@ -23,10 +23,11 @@
#include <sys/stat.h>
#include "Recorder.h"
#include "WavFile.h"
#include "../globals.h"
#include "../Nio/Nio.h"

Recorder::Recorder()
:status(0), notetrigger(0)
Recorder::Recorder(const SYNTH_T &synth_)
:status(0), notetrigger(0),synth(synth_)
{}

Recorder::~Recorder()
@@ -45,7 +46,7 @@ int Recorder::preparefile(std::string filename_, int overwrite)
return 1;
}

Nio::waveNew(new WavFile(filename_, synth->samplerate, 2));
Nio::waveNew(new WavFile(filename_, synth.samplerate, 2));

status = 1; //ready



+ 3
- 2
source/native-plugins/zynaddsubfx/Misc/Recorder.h View File

@@ -23,14 +23,14 @@
#ifndef RECORDER_H
#define RECORDER_H
#include <string>
#include "../globals.h"

struct SYNTH_T;
/**Records sound to a file*/
class Recorder
{
public:

Recorder();
Recorder(const SYNTH_T &synth);
~Recorder();
/**Prepare the given file.
* @returns 1 if the file exists */
@@ -49,6 +49,7 @@ class Recorder

private:
int notetrigger;
const SYNTH_T &synth;
};

#endif

+ 41
- 5
source/native-plugins/zynaddsubfx/Misc/Util.cpp View File

@@ -23,11 +23,10 @@
#include "Util.h"
#include <vector>
#include <cassert>
#include <math.h>
#include <stdio.h>
#ifndef CARLA_OS_WIN
#include <cmath>
#include <cstdio>
#include <fstream>
#include <err.h>
#endif

#include <sys/types.h>
#include <sys/stat.h>
@@ -87,7 +86,7 @@ float getdetune(unsigned char type,
findet = fabs(fdetune / 8192.0f) * 10.0f;
break;
case 3:
cdet = fabs(cdetune * 100);
cdet = fabs(cdetune * 100.0f);
findet = powf(10, fabs(fdetune / 8192.0f) * 3.0f) / 10.0f - 0.1f;
break;
case 4:
@@ -138,6 +137,43 @@ void os_sleep(long length)
usleep(length);
}

//!< maximum lenght a pid has on any POSIX system
//!< this is an estimation, but more than 12 looks insane
constexpr std::size_t max_pid_len = 12;

//!< safe pid lenght guess, posix conform
std::size_t os_guess_pid_length()
{
const char* pid_max_file = "/proc/sys/kernel/pid_max";
if(-1 == access(pid_max_file, R_OK)) {
return max_pid_len;
}
else {
std::ifstream is(pid_max_file);
if(!is.good())
return max_pid_len;
else {
std::string s;
is >> s;
for(const auto& c : s)
if(c < '0' || c > '9')
return max_pid_len;
return std::min(s.length(), max_pid_len);
}
}
}

//!< returns pid padded, posix conform
std::string os_pid_as_padded_string()
{
char result_str[max_pid_len << 1];
std::fill_n(result_str, max_pid_len, '0');
std::size_t written = snprintf(result_str + max_pid_len, max_pid_len,
"%d", (int)getpid());
// the below pointer should never cause segfaults:
return result_str + max_pid_len + written - os_guess_pid_length();
}

std::string legalizeFilename(std::string filename)
{
for(int i = 0; i < (int) filename.size(); ++i) {


+ 72
- 2
source/native-plugins/zynaddsubfx/Misc/Util.h View File

@@ -26,9 +26,13 @@
#include <string>
#include <sstream>
#include <stdint.h>
#include <algorithm>
#include "Config.h"
#include "../globals.h"

using std::min;
using std::max;

//Velocity Sensing function
extern float VelF(float velocity, unsigned char scaling);

@@ -48,6 +52,9 @@ void set_realtime();
/**Os independent sleep in microsecond*/
void os_sleep(long length);

//! returns pid padded to maximum pid lenght, posix conform
std::string os_pid_as_padded_string();

std::string legalizeFilename(std::string filename);

extern float *denormalkillbuf; /**<the buffer to add noise in order to avoid denormalisation*/
@@ -64,6 +71,12 @@ std::string stringFrom(T x)
return ss.str();
}

template<class T>
std::string to_s(T x)
{
return stringFrom(x);
}

template<class T>
T stringTo(const char *x)
{
@@ -74,12 +87,31 @@ T stringTo(const char *x)
return ans;
}



template<class T>
T limit(T val, T min, T max)
{
return val < min ? min : (val > max ? max : val);
}

template<class T>
bool inRange(T val, T min, T max)
{
return val >= min && val <= max;
}

template<class T>
T array_max(const T *data, size_t len)
{
T max = 0;

for(unsigned i = 0; i < len; ++i)
if(max < data[i])
max = data[i];
return max;
}

//Random number generator

typedef uint32_t prng_t;
@@ -105,9 +137,9 @@ inline void sprng(prng_t p)
* The random generator (0.0f..1.0f)
*/
#ifndef INT32_MAX
# define INT32_MAX (2147483647)
#define INT32_MAX (2147483647)
#endif
#define RND (float(prng()) / float(INT32_MAX))
#define RND (prng() / (INT32_MAX * 1.0f))

//Linear Interpolation
float interpolate(const float *data, size_t len, float pos);
@@ -115,4 +147,42 @@ float interpolate(const float *data, size_t len, float pos);
//Linear circular interpolation
float cinterpolate(const float *data, size_t len, float pos);

template<class T>
static inline void nullify(T &t) {delete t; t = NULL; }
template<class T>
static inline void arrayNullify(T &t) {delete [] t; t = NULL; }

/**
* Port macros - these produce easy and regular port definitions for common
* types
*/
#define rParamZyn(name, ...) \
{STRINGIFY(name) "::i", rProp(parameter) rMap(min, 0) rMap(max, 127) DOC(__VA_ARGS__), NULL, rParamICb(name)}

#define rSelf(type) \
{"self", rProp(internal) rMap(class, type) rDoc("port metadata"), 0, \
[](const char *, rtosc::RtData &d){ \
d.reply(d.loc, "b", sizeof(d.obj), &d.obj);}}\

#define rPaste \
{"preset-type", rProp(internal), 0, \
[](const char *, rtosc::RtData &d){ \
rObject *obj = (rObject*)d.obj; \
d.reply(d.loc, "s", obj->type);}},\
{"paste:b", rProp(internal) rDoc("paste port"), 0, \
[](const char *m, rtosc::RtData &d){ \
printf("rPaste...\n"); \
rObject &paste = **(rObject **)rtosc_argument(m,0).b.data; \
rObject &o = *(rObject*)d.obj;\
o.paste(paste);}}

#define rArrayPaste \
{"paste-array:bi", rProp(internal) rDoc("array paste port"), 0, \
[](const char *m, rtosc::RtData &d){ \
printf("rArrayPaste...\n"); \
rObject &paste = **(rObject **)rtosc_argument(m,0).b.data; \
int field = rtosc_argument(m,1).i; \
rObject &o = *(rObject*)d.obj;\
o.pasteArray(paste,field);}}

#endif

+ 4
- 3
source/native-plugins/zynaddsubfx/Misc/XMLwrapper.cpp View File

@@ -97,8 +97,8 @@ const char *mxmlElementGetAttr(const mxml_node_t *node, const char *name)
XMLwrapper::XMLwrapper()
{
version.Major = 2;
version.Minor = 4;
version.Revision = 4;
version.Minor = 5;
version.Revision = 0;

minimal = true;

@@ -311,7 +311,7 @@ int XMLwrapper::loadXMLfile(const string &filename)
mxmlDelete(tree);
tree = NULL;

const char *xmldata = doloadfile(filename.c_str());
const char *xmldata = doloadfile(filename);
if(xmldata == NULL)
return -1; //the file could not be loaded or uncompressed

@@ -618,6 +618,7 @@ mxml_node_t *XMLwrapper::addparams(const char *name, unsigned int params,
<< ParamName << "=\"" << ParamValue << "\"" << endl;
mxmlElementSetAttr(element, ParamName, ParamValue);
}
va_end(variableList);
}
return element;
}

+ 20
- 19
source/native-plugins/zynaddsubfx/Nio/AlsaEngine.cpp View File

@@ -1,21 +1,22 @@
/*
AlsaEngine.cpp
ZynAddSubFX - a software synthesizer
AlsaEngine.cpp - ALSA Driver

Copyright 2009, Alan Calvert
2010, Mark McCurry
Copyright 2009, Alan Calvert
2014, Mark McCurry

This file is part of ZynAddSubFX, which is free software: you can
redistribute it and/or modify it under the terms of the GNU General
Public License as published by the Free Software Foundation, either
version 3 of the License, or (at your option) any later version.
This program is free software; you can redistribute it and/or modify
it under the terms of version 2 of the GNU General Public License
as published by the Free Software Foundation.

ZynAddSubFX is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License (version 2 or later) for more details.

You should have received a copy of the GNU General Public License
along with ZynAddSubFX. If not, see <http://www.gnu.org/licenses/>.
You should have received a copy of the GNU General Public License (version 2)
along with this program; if not, write to the Free Software Foundation,
Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/

#include <iostream>
@@ -28,10 +29,10 @@ using namespace std;
#include "InMgr.h"
#include "AlsaEngine.h"

AlsaEngine::AlsaEngine()
:AudioOut()
AlsaEngine::AlsaEngine(const SYNTH_T &synth)
:AudioOut(synth)
{
audio.buffer = new short[synth->buffersize * 2];
audio.buffer = new short[synth.buffersize * 2];
name = "ALSA";
audio.handle = NULL;

@@ -295,7 +296,7 @@ bool AlsaEngine::openAudio()
/* Two channels (stereo) */
snd_pcm_hw_params_set_channels(audio.handle, audio.params, 2);

audio.sampleRate = synth->samplerate;
audio.sampleRate = synth.samplerate;
snd_pcm_hw_params_set_rate_near(audio.handle, audio.params,
&audio.sampleRate, NULL);

@@ -320,7 +321,7 @@ bool AlsaEngine::openAudio()
/* latency = periodsize * periods / (rate * bytes_per_frame) */
snd_pcm_hw_params_set_buffer_size(audio.handle,
audio.params,
synth->buffersize);
synth.buffersize);

//snd_pcm_hw_params_get_period_size(audio.params, &audio.frames, NULL);
//snd_pcm_hw_params_get_period_time(audio.params, &val, NULL);
@@ -352,7 +353,7 @@ void *AlsaEngine::processAudio()
while(audio.handle) {
audio.buffer = interleave(getNext());
snd_pcm_t *handle = audio.handle;
int rc = snd_pcm_writei(handle, audio.buffer, synth->buffersize);
int rc = snd_pcm_writei(handle, audio.buffer, synth.buffersize);
if(rc == -EPIPE) {
/* EPIPE means underrun */
cerr << "underrun occurred" << endl;


+ 15
- 14
source/native-plugins/zynaddsubfx/Nio/AlsaEngine.h View File

@@ -1,21 +1,22 @@
/*
AlsaEngine.h
ZynAddSubFX - a software synthesizer
AlsaEngine.h - ALSA Driver

Copyright 2009, Alan Calvert
2010, Mark McCurry
Copyright 2009, Alan Calvert
2014, Mark McCurry

This file is part of ZynAddSubFX, which is free software: you can
redistribute it and/or modify it under the terms of the GNU General
Public License as published by the Free Software Foundation, either
version 3 of the License, or (at your option) any later version.
This program is free software; you can redistribute it and/or modify
it under the terms of version 2 of the GNU General Public License
as published by the Free Software Foundation.

ZynAddSubFX is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License (version 2 or later) for more details.

You should have received a copy of the GNU General Public License
along with ZynAddSubFX. If not, see <http://www.gnu.org/licenses/>.
You should have received a copy of the GNU General Public License (version 2)
along with this program; if not, write to the Free Software Foundation,
Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/

#ifndef ALSA_ENGINE_H
@@ -34,7 +35,7 @@
class AlsaEngine:public AudioOut, MidiIn
{
public:
AlsaEngine();
AlsaEngine(const SYNTH_T &synth);
~AlsaEngine();

bool Start();


+ 2
- 2
source/native-plugins/zynaddsubfx/Nio/AudioOut.cpp View File

@@ -30,8 +30,8 @@ using namespace std;
#include "../Misc/Master.h"
#include "AudioOut.h"

AudioOut::AudioOut()
:samplerate(synth->samplerate), bufferSize(synth->buffersize)
AudioOut::AudioOut(const SYNTH_T &synth_)
:synth(synth_), samplerate(synth.samplerate), bufferSize(synth.buffersize)
{}

AudioOut::~AudioOut()


+ 2
- 1
source/native-plugins/zynaddsubfx/Nio/AudioOut.h View File

@@ -30,7 +30,7 @@
class AudioOut:public virtual Engine
{
public:
AudioOut();
AudioOut(const SYNTH_T &synth);
virtual ~AudioOut();

/**Sets the Sample Rate of this Output
@@ -54,6 +54,7 @@ class AudioOut:public virtual Engine
* (has nsamples sampled at a rate of samplerate)*/
const Stereo<float *> getNext();

const SYNTH_T &synth;
int samplerate;
int bufferSize;
};


+ 9
- 4
source/native-plugins/zynaddsubfx/Nio/CMakeLists.txt View File

@@ -15,19 +15,24 @@ set(zynaddsubfx_nio_SRCS
Nio.cpp
)

set(zynaddsubfx_nio_lib )
set(zynaddsubfx_nio_lib)

add_definitions(-DOUT_DEFAULT="${DefaultOutput}")
add_definitions(-DIN_DEFAULT="${DefaultInput}")

if(JackEnable)
include_directories(${JACK_INCLUDE_DIR})
list(APPEND zynaddsubfx_nio_SRCS JackEngine.cpp)
list(APPEND zynaddsubfx_nio_SRCS JackEngine.cpp JackMultiEngine.cpp)
list(APPEND zynaddsubfx_nio_lib ${JACK_LIBRARIES})

CHECK_INCLUDE_FILES("jack/metadata.h" JACK_HAS_METADATA_API)
if(JACK_HAS_METADATA_API)
add_definitions(-DJACK_HAS_METADATA_API)
endif(JACK_HAS_METADATA_API)
endif(JackEnable)

if(PaEnable)
include_directories(${PORTAUDIO_INCLUDE_DIR})
include_directories(${PORTAUDIO_INCLUDE_DIRS})
list(APPEND zynaddsubfx_nio_SRCS PaEngine.cpp)
list(APPEND zynaddsubfx_nio_lib ${PORTAUDIO_LIBRARIES})
endif(PaEnable)
@@ -38,7 +43,7 @@ if(AlsaEnable)
endif(AlsaEnable)

if(OssEnable)
list(APPEND zynaddsubfx_nio_SRCS OssEngine.cpp)
list(APPEND zynaddsubfx_nio_SRCS OssEngine.cpp OssMultiEngine.cpp)
endif(OssEnable)




+ 14
- 8
source/native-plugins/zynaddsubfx/Nio/EngineMgr.cpp View File

@@ -1,6 +1,7 @@
#include "EngineMgr.h"
#include <algorithm>
#include <iostream>
#include <cassert>
#include "Nio.h"
#include "InMgr.h"
#include "OutMgr.h"
@@ -9,12 +10,14 @@
#include "NulEngine.h"
#if OSS
#include "OssEngine.h"
#include "OssMultiEngine.h"
#endif
#if ALSA
#include "AlsaEngine.h"
#endif
#if JACK
#include "JackEngine.h"
#include "JackMultiEngine.h"
#endif
#if PORTAUDIO
#include "PaEngine.h"
@@ -22,29 +25,32 @@

using namespace std;

EngineMgr &EngineMgr::getInstance()
EngineMgr &EngineMgr::getInstance(const SYNTH_T *synth)
{
static EngineMgr instance;
static EngineMgr instance(synth);
return instance;
}

EngineMgr::EngineMgr()
EngineMgr::EngineMgr(const SYNTH_T *synth)
{
Engine *defaultEng = new NulEngine();
assert(synth);
Engine *defaultEng = new NulEngine(*synth);

//conditional compiling mess (but contained)
engines.push_back(defaultEng);
#if OSS
engines.push_back(new OssEngine());
engines.push_back(new OssEngine(*synth));
engines.push_back(new OssMultiEngine(*synth));
#endif
#if ALSA
engines.push_back(new AlsaEngine());
engines.push_back(new AlsaEngine(*synth));
#endif
#if JACK
engines.push_back(new JackEngine());
engines.push_back(new JackEngine(*synth));
engines.push_back(new JackMultiEngine(*synth));
#endif
#if PORTAUDIO
engines.push_back(new PaEngine());
engines.push_back(new PaEngine(*synth));
#endif

defaultOut = dynamic_cast<AudioOut *>(defaultEng);


+ 3
- 2
source/native-plugins/zynaddsubfx/Nio/EngineMgr.h View File

@@ -9,11 +9,12 @@
class MidiIn;
class AudioOut;
class OutMgr;
struct SYNTH_T;
/**Container/Owner of the long lived Engines*/
class EngineMgr
{
public:
static EngineMgr &getInstance();
static EngineMgr &getInstance(const SYNTH_T *synth=NULL);
~EngineMgr();

/**Gets requested engine
@@ -38,6 +39,6 @@ class EngineMgr
AudioOut *defaultOut;
MidiIn *defaultIn;
private:
EngineMgr();
EngineMgr(const SYNTH_T *synth);
};
#endif

+ 23
- 10
source/native-plugins/zynaddsubfx/Nio/InMgr.cpp View File

@@ -2,10 +2,15 @@
#include "MidiIn.h"
#include "EngineMgr.h"
#include "../Misc/Master.h"
#include "../Misc/Part.h"
#include "../Misc/MiddleWare.h"
#include <rtosc/thread-link.h>
#include <iostream>

using namespace std;

extern MiddleWare *middleware;

ostream &operator<<(ostream &out, const MidiEvent &ev)
{
switch(ev.type) {
@@ -41,7 +46,7 @@ InMgr &InMgr::getInstance()
}

InMgr::InMgr()
:queue(100), master(Master::getInstance())
:queue(100), master(NULL)
{
current = NULL;
work.init(PTHREAD_PROCESS_PRIVATE, 0);
@@ -76,24 +81,27 @@ void InMgr::flush(unsigned frameStart, unsigned frameStop)

switch(ev.type) {
case M_NOTE:
dump.dumpnote(ev.channel, ev.num, ev.value);

if(ev.value)
master.noteOn(ev.channel, ev.num, ev.value);
master->noteOn(ev.channel, ev.num, ev.value);
else
master.noteOff(ev.channel, ev.num);
master->noteOff(ev.channel, ev.num);
break;

case M_CONTROLLER:
dump.dumpcontroller(ev.channel, ev.num, ev.value);
master.setController(ev.channel, ev.num, ev.value);
master->setController(ev.channel, ev.num, ev.value);
break;

case M_PGMCHANGE:
master.setProgram(ev.channel, ev.num);
for(int i=0; i < NUM_MIDI_PARTS; ++i) {
//set the program of the parts assigned to the midi channel
if(master->part[i]->Prcvchn == ev.channel) {
middleware->pendingSetProgram(i, ev.num);
}
}
break;

case M_PRESSURE:
master.polyphonicAftertouch(ev.channel, ev.num, ev.value);
master->polyphonicAftertouch(ev.channel, ev.num, ev.value);
break;
}
}
@@ -136,6 +144,11 @@ string InMgr::getSource() const

MidiIn *InMgr::getIn(string name)
{
EngineMgr &eng = EngineMgr::getInstance();
EngineMgr &eng = EngineMgr::getInstance(NULL);
return dynamic_cast<MidiIn *>(eng.getEng(name));
}

void InMgr::setMaster(Master *master_)
{
master = master_;
}

+ 3
- 1
source/native-plugins/zynaddsubfx/Nio/InMgr.h View File

@@ -40,6 +40,8 @@ class InMgr

std::string getSource() const;

void setMaster(class Master *master);

friend class EngineMgr;
private:
InMgr();
@@ -49,7 +51,7 @@ class InMgr
class MidiIn * current;

/**the link to the rest of zyn*/
class Master & master;
class Master *master;
};

#endif

+ 58
- 17
source/native-plugins/zynaddsubfx/Nio/JackEngine.cpp View File

@@ -1,32 +1,42 @@
/*
JackEngine.cpp
ZynAddSubFX - a software synthesizer
JackEngine.cpp - Jack Driver

Copyright 2009, Alan Calvert
Copyright 2009, Alan Calvert
2014, Mark McCurry

This file is part of yoshimi, which is free software: you can
redistribute it and/or modify it under the terms of the GNU General
Public License as published by the Free Software Foundation, either
version 3 of the License, or (at your option) any later version.
This program is free software; you can redistribute it and/or modify
it under the terms of version 2 of the GNU General Public License
as published by the Free Software Foundation.

yoshimi is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License (version 2 or later) for more details.

You should have received a copy of the GNU General Public License
along with yoshimi. If not, see <http://www.gnu.org/licenses/>.
You should have received a copy of the GNU General Public License (version 2)
along with this program; if not, write to the Free Software Foundation,
Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/

#include <iostream>

#include <jack/midiport.h>
#ifdef JACK_HAS_METADATA_API
# include <jack/metadata.h>
#endif // JACK_HAS_METADATA_API
#include "jack_osc.h"
#include <fcntl.h>
#include <sys/stat.h>
#include <cassert>
#include <cstring>
#include <unistd.h> // access()
#include <fstream> // std::istream

#include "Nio.h"
#include "OutMgr.h"
#include "InMgr.h"
#include "Misc/Util.h"

#include "JackEngine.h"

@@ -34,8 +44,8 @@ using namespace std;

extern char *instance_name;

JackEngine::JackEngine()
:AudioOut(), jackClient(NULL)
JackEngine::JackEngine(const SYNTH_T &synth)
:AudioOut(synth), jackClient(NULL)
{
name = "JACK";
audio.jackSamplerate = 0;
@@ -58,6 +68,9 @@ bool JackEngine::connectServer(string server)
string postfix = Nio::getPostfix();
if(!postfix.empty())
clientname += "_" + postfix;
if(Nio::pidInClientName)
clientname += "_" + os_pid_as_padded_string();

jack_status_t jackstatus;
bool use_server_name = server.size() && server.compare("default") != 0;
jack_options_t jopts = (jack_options_t)
@@ -93,12 +106,11 @@ bool JackEngine::connectJack()
connectServer("");
if(NULL != jackClient) {
setBufferSize(jack_get_buffer_size(jackClient));
int chk;
jack_set_error_function(_errorCallback);
jack_set_info_function(_infoCallback);
if(jack_set_buffer_size_callback(jackClient, _bufferSizeCallback, this))
cerr << "Error setting the bufferSize callback" << endl;
if((chk = jack_set_xrun_callback(jackClient, _xrunCallback, this)))
if((jack_set_xrun_callback(jackClient, _xrunCallback, this)))
cerr << "Error setting jack xrun callback" << endl;
if(jack_set_process_callback(jackClient, _processCallback, this)) {
cerr << "Error, JackEngine failed to set process callback" << endl;
@@ -213,6 +225,12 @@ bool JackEngine::openAudio()
cerr << "Warning, No outputs to autoconnect to" << endl;
}
midi.jack_sync = true;
osc.oscport = jack_port_register(jackClient, "osc",
JACK_DEFAULT_OSC_TYPE, JackPortIsInput, 0);
#ifdef JACK_HAS_METADATA_API
jack_uuid_t uuid = jack_port_uuid(osc.oscport);
jack_set_property(jackClient, uuid, "http://jackaudio.org/metadata/event-types", JACK_EVENT_TYPE__OSC, "text/plain");
#endif // JACK_HAS_METADATA_API
return true;
}
else
@@ -226,10 +244,19 @@ void JackEngine::stopAudio()
for(int i = 0; i < 2; ++i) {
jack_port_t *port = audio.ports[i];
audio.ports[i] = NULL;
if(NULL != port)
if(jackClient != NULL && NULL != port)
jack_port_unregister(jackClient, port);
}
midi.jack_sync = false;
if(osc.oscport) {
if (jackClient != NULL) {
jack_port_unregister(jackClient, osc.oscport);
#ifdef JACK_HAS_METADATA_API
jack_uuid_t uuid = jack_port_uuid(osc.oscport);
jack_remove_property(jackClient, uuid, "http://jackaudio.org/metadata/event-types");
#endif // JACK_HAS_METADATA_API
}
}
if(!getMidiEn())
disconnectJack();
}
@@ -293,6 +320,20 @@ int JackEngine::processCallback(jack_nframes_t nframes)

bool JackEngine::processAudio(jack_nframes_t nframes)
{
//handle rt osc events first
void *oscport = jack_port_get_buffer(osc.oscport, nframes);
size_t osc_packets = jack_osc_get_event_count(oscport);

for(size_t i = 0; i < osc_packets; ++i) {
jack_osc_event_t event;
if(jack_osc_event_get(&event, oscport, i))
continue;
if(*event.buffer!='/') //Bundles are unhandled
continue;
//TODO validate message length
OutMgr::getInstance().applyOscEventRt((char*)event.buffer);
}

for(int port = 0; port < 2; ++port) {
audio.portBuffs[port] =
(jsample_t *)jack_port_get_buffer(audio.ports[port], nframes);


+ 18
- 13
source/native-plugins/zynaddsubfx/Nio/JackEngine.h View File

@@ -1,20 +1,22 @@
/*
JackEngine.h
ZynAddSubFX - a software synthesizer
JackEngine.h - Jack Driver

Copyright 2009, Alan Calvert
Copyright 2009, Alan Calvert
2014, Mark McCurry

This file is part of yoshimi, which is free software: you can
redistribute it and/or modify it under the terms of the GNU General
Public License as published by the Free Software Foundation, either
version 3 of the License, or (at your option) any later version.
This program is free software; you can redistribute it and/or modify
it under the terms of version 2 of the GNU General Public License
as published by the Free Software Foundation.

yoshimi is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License (version 2 or later) for more details.

You should have received a copy of the GNU General Public License
along with yoshimi. If not, see <http://www.gnu.org/licenses/>.
You should have received a copy of the GNU General Public License (version 2)
along with this program; if not, write to the Free Software Foundation,
Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/

#ifndef JACK_ENGINE_H
@@ -34,7 +36,7 @@ typedef jack_default_audio_sample_t jsample_t;
class JackEngine:public AudioOut, MidiIn
{
public:
JackEngine();
JackEngine(const SYNTH_T &synth);
~JackEngine() { }

bool Start();
@@ -78,6 +80,9 @@ class JackEngine:public AudioOut, MidiIn
jack_port_t *ports[2];
jsample_t *portBuffs[2];
} audio;
struct osc {
jack_port_t *oscport;
} osc;
struct midi {
jack_port_t *inport;
bool jack_sync;


+ 176
- 0
source/native-plugins/zynaddsubfx/Nio/JackMultiEngine.cpp View File

@@ -0,0 +1,176 @@
/*
ZynAddSubFX - a software synthesizer

JackMultiEngine.cpp - Channeled Audio output JACK
Copyright (C) 2012-2012 Mark McCurry
Author: Mark McCurry

This program is free software; you can redistribute it and/or modify
it under the terms of version 2 of the GNU General Public License
as published by the Free Software Foundation.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License (version 2 or later) for more details.

You should have received a copy of the GNU General Public License (version 2)
along with this program; if not, write to the Free Software Foundation,
Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

*/

#include <jack/jack.h>
#include <string>
#include <cstring>
#include <err.h>
#include <cstdio>
#include <cassert>

#include "Nio.h"
#include "../Misc/Util.h"
#include "../Misc/Master.h"
#include "../Misc/Part.h"
#include "../Misc/MiddleWare.h"

#include "JackMultiEngine.h"

extern MiddleWare *middleware;
using std::string;

struct jack_multi
{
jack_port_t *ports[NUM_MIDI_PARTS * 2 + 2];
jack_client_t *client;
bool running;
};

JackMultiEngine::JackMultiEngine(const SYNTH_T &synth)
:AudioOut(synth), impl(new jack_multi())
{
impl->running = false;
impl->client = NULL;

name = "JACK-MULTI";
}

JackMultiEngine::~JackMultiEngine(void)
{
delete impl;
}

void JackMultiEngine::setAudioEn(bool nval)
{
if(nval)
Start();
else
Stop();
}

bool JackMultiEngine::getAudioEn() const
{
return impl->running;
}



bool JackMultiEngine::Start(void)
{
if(impl->client)
return true;

string clientname = "zynaddsubfx";
string postfix = Nio::getPostfix();
if(!postfix.empty())
clientname += "_" + postfix;
if(Nio::pidInClientName)
clientname += "_" + os_pid_as_padded_string();
jack_status_t jackstatus;

impl->client = jack_client_open(clientname.c_str(), JackNullOption, &jackstatus);

if(!impl->client)
errx(1, "failed to connect to jack...");
//Create the set of jack ports
char portName[20];
memset(portName,0,sizeof(portName));

#define JACK_REGISTER(x) jack_port_register(impl->client, x, \
JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput | JackPortIsTerminal, 0)
//Create the master wet port

impl->ports[0] = JACK_REGISTER("out-L");
impl->ports[1] = JACK_REGISTER("out-R");

//Create all part's outputs
for(int i = 0; i < NUM_MIDI_PARTS * 2; i += 2) {
snprintf(portName, 19, "part%d/out-L", i / 2);
impl->ports[2 + i] = JACK_REGISTER(portName);
snprintf(portName, 19, "part%d/out-R", i / 2);
impl->ports[3 + i] = JACK_REGISTER(portName);
}

//verify that all sample rate and buffer_size are the same in jack.
//This insures that the connection can be made with no resampling or
//buffering
if(synth.samplerate != jack_get_sample_rate(impl->client))
errx(1, "jack must have the same sample rate!");
if(synth.buffersize != (int) jack_get_buffer_size(impl->client))
errx(1, "jack must have the same buffer size");

jack_set_process_callback(impl->client, _processCallback, this);

//run
if(jack_activate(impl->client))
errx(1, "failed at starting the jack client");
impl->running = true;
return true;
}

int JackMultiEngine::_processCallback(jack_nframes_t nframes, void *arg)
{
return static_cast<JackMultiEngine *>(arg)->processAudio(nframes);
}

int JackMultiEngine::processAudio(jack_nframes_t nframes)
{
//Gather all buffers
float *buffers[NUM_MIDI_PARTS * 2 + 2];

for(int i = 0; i < NUM_MIDI_PARTS * 2 + 2; ++i) {
buffers[i] =
(float *)jack_port_get_buffer(impl->ports[i], nframes);
assert(buffers[i]);
}

//Get the wet samples from OutMgr
Stereo<float *> smp = getNext();
memcpy(buffers[0], smp.l, synth.bufferbytes);
memcpy(buffers[1], smp.r, synth.bufferbytes);

//Gather other samples from individual parts
Master &master = *middleware->spawnMaster();
for(int i = 0; i < NUM_MIDI_PARTS; ++i) {
memcpy(buffers[2*i + 2], master.part[i]->partoutl, synth.bufferbytes);
memcpy(buffers[2*i + 3], master.part[i]->partoutr, synth.bufferbytes);
}

return false;
}

void JackMultiEngine::Stop()
{
for(int i = 0; i < NUM_MIDI_PARTS * 2 + 2; ++i) {
jack_port_t *port = impl->ports[i];
impl->ports[i] = NULL;
if(port)
jack_port_unregister(impl->client, port);
}

jack_client_close(impl->client);
impl->client = NULL;

impl->running = false;
}

+ 46
- 0
source/native-plugins/zynaddsubfx/Nio/JackMultiEngine.h View File

@@ -0,0 +1,46 @@
/*
ZynAddSubFX - a software synthesizer

JackMultiEngine.h - Channeled Audio output JACK
Copyright (C) 2012-2012 Mark McCurry
Author: Mark McCurry

This program is free software; you can redistribute it and/or modify
it under the terms of version 2 of the GNU General Public License
as published by the Free Software Foundation.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License (version 2 or later) for more details.

You should have received a copy of the GNU General Public License (version 2)
along with this program; if not, write to the Free Software Foundation,
Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

*/
#ifndef JACK_MULTI_ENGINE
#define JACK_MULTI_ENGINE

#include "AudioOut.h"

class JackMultiEngine:public AudioOut
{
public:
JackMultiEngine(const SYNTH_T &synth);
~JackMultiEngine(void);

void setAudioEn(bool nval);
bool getAudioEn() const;

bool Start(void);
void Stop(void);

private:
static int _processCallback(unsigned nframes, void *arg);
int processAudio(unsigned nframes);

struct jack_multi *impl;
};

#endif

+ 31
- 8
source/native-plugins/zynaddsubfx/Nio/Nio.cpp View File

@@ -12,26 +12,39 @@ using std::set;
using std::cerr;
using std::endl;

#ifndef IN_DEFAULT
#define IN_DEFAULT "NULL"
#endif
#ifndef OUT_DEFAULT
#define OUT_DEFAULT "NULL"
#endif

InMgr *in = NULL;
OutMgr *out = NULL;
EngineMgr *eng = NULL;
string postfix;

bool Nio::autoConnect = false;
string Nio::defaultSource = IN_DEFAULT;
string Nio::defaultSink = OUT_DEFAULT;
bool Nio::autoConnect = false;
bool Nio::pidInClientName = false;
string Nio::defaultSource = IN_DEFAULT;
string Nio::defaultSink = OUT_DEFAULT;

void Nio::init(void)
void Nio::init(const SYNTH_T &synth, class Master *master)
{
in = &InMgr::getInstance(); //Enable input wrapper
out = &OutMgr::getInstance(); //Initialize the Output Systems
eng = &EngineMgr::getInstance(); //Initialize The Engines
out = &OutMgr::getInstance(&synth); //Initialize the Output Systems
eng = &EngineMgr::getInstance(&synth); //Initialize The Engines

in->setMaster(master);
out->setMaster(master);
}

bool Nio::start()
{
init();
return eng->start();
if(eng)
return eng->start();
else
return false;
}

void Nio::stop()
@@ -105,6 +118,10 @@ string Nio::getSink()
#include <jack/jack.h>
void Nio::preferedSampleRate(unsigned &rate)
{
//XXX hello non portable code
if(system("ps aux | grep jack"))
return;

jack_client_t *client = jack_client_open("temp-client",
JackNoStartServer, 0);
if(client) {
@@ -117,6 +134,12 @@ void Nio::preferedSampleRate(unsigned &)
{}
#endif

void Nio::masterSwap(Master *master)
{
in->setMaster(master);
out->setMaster(master);
}

void Nio::waveNew(class WavFile *wave)
{
out->wave->newFile(wave);


+ 6
- 1
source/native-plugins/zynaddsubfx/Nio/Nio.h View File

@@ -4,13 +4,15 @@
#include <set>

class WavFile;
class Master;
struct SYNTH_T;

/**Interface to Nio Subsystem
*
* Should be only externally included header */
namespace Nio
{
void init(void);
void init(const SYNTH_T &synth, Master *master);
bool start(void);
void stop(void);

@@ -32,6 +34,8 @@ namespace Nio
//Get the prefered sample rate from jack (if running)
void preferedSampleRate(unsigned &rate);

//Complete Master Swaps to ONLY BE CALLED FROM RT CONTEXT
void masterSwap(Master *master);

//Wave writing
void waveNew(class WavFile *wave);
@@ -40,6 +44,7 @@ namespace Nio
void waveEnd(void);

extern bool autoConnect;
extern bool pidInClientName;
extern std::string defaultSource;
extern std::string defaultSink;
};


+ 4
- 4
source/native-plugins/zynaddsubfx/Nio/NulEngine.cpp View File

@@ -28,8 +28,8 @@

using namespace std;

NulEngine::NulEngine()
:AudioOut(), pThread(NULL)
NulEngine::NulEngine(const SYNTH_T &synth_)
:AudioOut(synth_), pThread(NULL)
{
name = "NULL";
playing_until.tv_sec = 0;
@@ -62,8 +62,8 @@ void *NulEngine::AudioThread()
if(remaining < 0)
cerr << "WARNING - too late" << endl;
}
playing_until.tv_usec += synth->buffersize * 1000000
/ synth->samplerate;
playing_until.tv_usec += synth.buffersize * 1000000
/ synth.samplerate;
if(remaining < 0)
playing_until.tv_usec -= remaining;
playing_until.tv_sec += playing_until.tv_usec / 1000000;


+ 1
- 1
source/native-plugins/zynaddsubfx/Nio/NulEngine.h View File

@@ -32,7 +32,7 @@
class NulEngine:public AudioOut, MidiIn
{
public:
NulEngine();
NulEngine(const SYNTH_T &synth_);
~NulEngine();

bool Start();


+ 304
- 104
source/native-plugins/zynaddsubfx/Nio/OssEngine.cpp View File

@@ -28,71 +28,254 @@
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/soundcard.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <iostream>
#include <signal.h>

#include "InMgr.h"

using namespace std;

OssEngine::OssEngine()
:AudioOut(), engThread(NULL)
/*
* The following statemachine converts MIDI commands to USB MIDI
* packets, derived from Linux's usbmidi.c, which was written by
* "Clemens Ladisch". It is used to figure out when a MIDI command is
* complete, without having to read the first byte of the next MIDI
* command. This is useful when connecting to so-called system PIPEs
* and FIFOs. See "man mkfifo".
*
* Return values:
* 0: No command
* Else: Command is complete
*/
static unsigned char
OssMidiParse(struct OssMidiParse &midi_parse,
unsigned char cn, unsigned char b)
{
unsigned char p0 = (cn << 4);

if(b >= 0xf8) {
midi_parse.temp_0[0] = p0 | 0x0f;
midi_parse.temp_0[1] = b;
midi_parse.temp_0[2] = 0;
midi_parse.temp_0[3] = 0;
midi_parse.temp_cmd = midi_parse.temp_0;
return (1);

} else if(b >= 0xf0) {
switch (b) {
case 0xf0: /* system exclusive begin */
midi_parse.temp_1[1] = b;
midi_parse.state = OSSMIDI_ST_SYSEX_1;
break;
case 0xf1: /* MIDI time code */
case 0xf3: /* song select */
midi_parse.temp_1[1] = b;
midi_parse.state = OSSMIDI_ST_1PARAM;
break;
case 0xf2: /* song position pointer */
midi_parse.temp_1[1] = b;
midi_parse.state = OSSMIDI_ST_2PARAM_1;
break;
case 0xf4: /* unknown */
case 0xf5: /* unknown */
midi_parse.state = OSSMIDI_ST_UNKNOWN;
break;
case 0xf6: /* tune request */
midi_parse.temp_1[0] = p0 | 0x05;
midi_parse.temp_1[1] = 0xf6;
midi_parse.temp_1[2] = 0;
midi_parse.temp_1[3] = 0;
midi_parse.temp_cmd = midi_parse.temp_1;
midi_parse.state = OSSMIDI_ST_UNKNOWN;
return (1);

case 0xf7: /* system exclusive end */
switch (midi_parse.state) {
case OSSMIDI_ST_SYSEX_0:
midi_parse.temp_1[0] = p0 | 0x05;
midi_parse.temp_1[1] = 0xf7;
midi_parse.temp_1[2] = 0;
midi_parse.temp_1[3] = 0;
midi_parse.temp_cmd = midi_parse.temp_1;
midi_parse.state = OSSMIDI_ST_UNKNOWN;
return (1);
case OSSMIDI_ST_SYSEX_1:
midi_parse.temp_1[0] = p0 | 0x06;
midi_parse.temp_1[2] = 0xf7;
midi_parse.temp_1[3] = 0;
midi_parse.temp_cmd = midi_parse.temp_1;
midi_parse.state = OSSMIDI_ST_UNKNOWN;
return (1);
case OSSMIDI_ST_SYSEX_2:
midi_parse.temp_1[0] = p0 | 0x07;
midi_parse.temp_1[3] = 0xf7;
midi_parse.temp_cmd = midi_parse.temp_1;
midi_parse.state = OSSMIDI_ST_UNKNOWN;
return (1);
}
midi_parse.state = OSSMIDI_ST_UNKNOWN;
break;
}
} else if(b >= 0x80) {
midi_parse.temp_1[1] = b;
if((b >= 0xc0) && (b <= 0xdf)) {
midi_parse.state = OSSMIDI_ST_1PARAM;
} else {
midi_parse.state = OSSMIDI_ST_2PARAM_1;
}
} else { /* b < 0x80 */
switch (midi_parse.state) {
case OSSMIDI_ST_1PARAM:
if(midi_parse.temp_1[1] < 0xf0) {
p0 |= midi_parse.temp_1[1] >> 4;
} else {
p0 |= 0x02;
midi_parse.state = OSSMIDI_ST_UNKNOWN;
}
midi_parse.temp_1[0] = p0;
midi_parse.temp_1[2] = b;
midi_parse.temp_1[3] = 0;
midi_parse.temp_cmd = midi_parse.temp_1;
return (1);
case OSSMIDI_ST_2PARAM_1:
midi_parse.temp_1[2] = b;
midi_parse.state = OSSMIDI_ST_2PARAM_2;
break;
case OSSMIDI_ST_2PARAM_2:
if(midi_parse.temp_1[1] < 0xf0) {
p0 |= midi_parse.temp_1[1] >> 4;
midi_parse.state = OSSMIDI_ST_2PARAM_1;
} else {
p0 |= 0x03;
midi_parse.state = OSSMIDI_ST_UNKNOWN;
}
midi_parse.temp_1[0] = p0;
midi_parse.temp_1[3] = b;
midi_parse.temp_cmd = midi_parse.temp_1;
return (1);
case OSSMIDI_ST_SYSEX_0:
midi_parse.temp_1[1] = b;
midi_parse.state = OSSMIDI_ST_SYSEX_1;
break;
case OSSMIDI_ST_SYSEX_1:
midi_parse.temp_1[2] = b;
midi_parse.state = OSSMIDI_ST_SYSEX_2;
break;
case OSSMIDI_ST_SYSEX_2:
midi_parse.temp_1[0] = p0 | 0x04;
midi_parse.temp_1[3] = b;
midi_parse.temp_cmd = midi_parse.temp_1;
midi_parse.state = OSSMIDI_ST_SYSEX_0;
return (1);
default:
break;
}
}
return (0);
}

OssEngine::OssEngine(const SYNTH_T &synth)
:AudioOut(synth), audioThread(NULL), midiThread(NULL)
{
name = "OSS";

midi.handle = -1;
audio.handle = -1;

audio.smps = new short[synth->buffersize * 2];
memset(audio.smps, 0, synth->bufferbytes);
/* allocate worst case audio buffer */
audio.smps.ps32 = new int[synth.buffersize * 2];
memset(audio.smps.ps32, 0, sizeof(int) * synth.buffersize * 2);
memset(&midi.state, 0, sizeof(midi.state));
}

OssEngine::~OssEngine()
{
Stop();
delete [] audio.smps;
delete [] audio.smps.ps32;
}

bool OssEngine::openAudio()
{
int x;

if(audio.handle != -1)
return true; //already open

int snd_bitsize = 16;
int snd_fragment = 0x00080009; //fragment size (?);
int snd_fragment;
int snd_stereo = 1; //stereo;
int snd_format = AFMT_S16_LE;
int snd_samplerate = synth->samplerate;
int snd_samplerate = synth.samplerate;

const char *device = config.cfg.LinuxOSSWaveOutDev;
if(getenv("DSP_DEVICE"))
device = getenv("DSP_DEVICE");
const char *device = getenv("DSP_DEVICE");
if(device == NULL)
device = config.cfg.LinuxOSSWaveOutDev;

audio.handle = open(device, O_WRONLY, 0);
/* NOTE: PIPEs and FIFOs can block when opening them */
audio.handle = open(device, O_WRONLY, O_NONBLOCK);
if(audio.handle == -1) {
cerr << "ERROR - I can't open the "
<< device << '.' << endl;
return false;
}
ioctl(audio.handle, SNDCTL_DSP_RESET, NULL);
ioctl(audio.handle, SNDCTL_DSP_SETFMT, &snd_format);

/* Figure out the correct format first */

int snd_format16 = AFMT_S16_NE;

#ifdef AFMT_S32_NE
int snd_format32 = AFMT_S32_NE;
if (ioctl(audio.handle, SNDCTL_DSP_SETFMT, &snd_format32) == 0) {
audio.is32bit = true;
} else
#endif
if (ioctl(audio.handle, SNDCTL_DSP_SETFMT, &snd_format16) == 0) {
audio.is32bit = false;
} else {
cerr << "ERROR - I cannot set DSP format for "
<< device << '.' << endl;
goto error;
}
ioctl(audio.handle, SNDCTL_DSP_STEREO, &snd_stereo);
ioctl(audio.handle, SNDCTL_DSP_SPEED, &snd_samplerate);
ioctl(audio.handle, SNDCTL_DSP_SAMPLESIZE, &snd_bitsize);
ioctl(audio.handle, SNDCTL_DSP_SETFRAGMENT, &snd_fragment);

if(!getMidiEn()) {
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
engThread = new pthread_t;
pthread_create(engThread, &attr, _thread, this);
if (snd_samplerate != (int)synth.samplerate) {
cerr << "ERROR - Cannot set samplerate for "
<< device << ". " << snd_samplerate
<< " != " << synth.samplerate << endl;
goto error;
}

/* compute buffer size for 16-bit stereo samples */
audio.buffersize = 4 * synth.buffersize;
if (audio.is32bit)
audio.buffersize *= 2;

for (x = 4; x < 20; x++) {
if ((1 << x) >= audio.buffersize)
break;
}

snd_fragment = 0x20000 | x; /* 2x buffer */

ioctl(audio.handle, SNDCTL_DSP_SETFRAGMENT, &snd_fragment);

pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
audioThread = new pthread_t;
pthread_create(audioThread, &attr, _audioThreadCb, this);

return true;

error:
close(audio.handle);
audio.handle = -1;
return false;
}

void OssEngine::stopAudio()
@@ -102,12 +285,12 @@ void OssEngine::stopAudio()
return;
audio.handle = -1;

if(!getMidiEn() && engThread)
pthread_join(*engThread, NULL);
delete engThread;
engThread = NULL;

/* close handle first, so that write() exits */
close(handle);

pthread_join(*audioThread, NULL);
delete audioThread;
audioThread = NULL;
}

bool OssEngine::Start()
@@ -165,19 +348,22 @@ bool OssEngine::openMidi()
if(handle != -1)
return true; //already open

handle = open(config.cfg.LinuxOSSSeqInDev, O_RDONLY, 0);
const char *device = getenv("MIDI_DEVICE");
if(device == NULL)
device = config.cfg.LinuxOSSSeqInDev;

/* NOTE: PIPEs and FIFOs can block when opening them */
handle = open(device, O_RDONLY, O_NONBLOCK);

if(-1 == handle)
return false;
midi.handle = handle;

if(!getAudioEn()) {
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
engThread = new pthread_t;
pthread_create(engThread, &attr, _thread, this);
}
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
midiThread = new pthread_t;
pthread_create(midiThread, &attr, _midiThreadCb, this);

return true;
}
@@ -190,96 +376,110 @@ void OssEngine::stopMidi()

midi.handle = -1;

if(!getAudioEn() && engThread) {
pthread_join(*engThread, NULL);
delete engThread;
engThread = NULL;
}

/* close handle first, so that read() exits */
close(handle);

pthread_join(*midiThread, NULL);
delete midiThread;
midiThread = NULL;
}

void *OssEngine::_thread(void *arg)
void *OssEngine::_audioThreadCb(void *arg)
{
return (static_cast<OssEngine *>(arg))->thread();
return (static_cast<OssEngine *>(arg))->audioThreadCb();
}

void *OssEngine::thread()
void *OssEngine::_midiThreadCb(void *arg)
{
unsigned char tmp[4] = {0, 0, 0, 0};
return (static_cast<OssEngine *>(arg))->midiThreadCb();
}

void *OssEngine::audioThreadCb()
{
/*
* In case the audio device is a PIPE/FIFO,
* we need to ignore any PIPE signals:
*/
signal(SIGPIPE, SIG_IGN);

set_realtime();
while(getAudioEn() || getMidiEn()) {
if(getAudioEn()) {
const Stereo<float *> smps = getNext();

float l, r;
for(int i = 0; i < synth->buffersize; ++i) {
l = smps.l[i];
r = smps.r[i];

if(l < -1.0f)
l = -1.0f;
else
while(getAudioEn()) {
const Stereo<float *> smps = getNext();

float l, r;
for(int i = 0; i < synth.buffersize; ++i) {
l = smps.l[i];
r = smps.r[i];

if(l < -1.0f)
l = -1.0f;
else
if(l > 1.0f)
l = 1.0f;
if(r < -1.0f)
r = -1.0f;
else
if(r < -1.0f)
r = -1.0f;
else
if(r > 1.0f)
r = 1.0f;

audio.smps[i * 2] = (short int) (l * 32767.0f);
audio.smps[i * 2 + 1] = (short int) (r * 32767.0f);
if (audio.is32bit) {
audio.smps.ps32[i * 2] = (int) (l * 2147483647.0f);
audio.smps.ps32[i * 2 + 1] = (int) (r * 2147483647.0f);
} else {/* 16bit */
audio.smps.ps16[i * 2] = (short int) (l * 32767.0f);
audio.smps.ps16[i * 2 + 1] = (short int) (r * 32767.0f);
}
int handle = audio.handle;
if(handle != -1)
write(handle, audio.smps, synth->buffersize * 4); // *2 because is 16 bit, again * 2 because is stereo
else
break;
}

//Collect up to 30 midi events
for(int k = 0; k < 30 && getMidiEn(); ++k) {
static char escaped;

memset(tmp, 0, 4);
int error;
do {
/* make a copy of handle, in case of OSS audio disable */
int handle = audio.handle;
if(handle == -1)
goto done;
error = write(handle, audio.smps.ps32, audio.buffersize);
} while (error == -1 && errno == EINTR);

if(escaped) {
tmp[0] = escaped;
escaped = 0;
}
else {
getMidi(tmp);
if(!(tmp[0] & 0x80))
continue;
}
getMidi(tmp + 1);
if(tmp[1] & 0x80) {
escaped = tmp[1];
tmp[1] = 0;
}
else {
getMidi(tmp + 2);
if(tmp[2] & 0x80) {
escaped = tmp[2];
tmp[2] = 0;
}
else {
getMidi(tmp + 3);
if(tmp[3] & 0x80) {
escaped = tmp[3];
tmp[3] = 0;
}
}
}
midiProcess(tmp[0], tmp[1], tmp[2]);
}
if(error == -1)
goto done;
}
done:
pthread_exit(NULL);
return NULL;
}

void OssEngine::getMidi(unsigned char *midiPtr)
void *OssEngine::midiThreadCb()
{
read(midi.handle, midiPtr, 1);
/*
* In case the MIDI device is a PIPE/FIFO,
* we need to ignore any PIPE signals:
*/
signal(SIGPIPE, SIG_IGN);
set_realtime();
while(getMidiEn()) {
unsigned char tmp;
int error;
do {
/* make a copy of handle, in case of OSS MIDI disable */
int handle = midi.handle;
if(handle == -1)
goto done;
error = read(handle, &tmp, 1);
} while (error == -1 && errno == EINTR);

/* check that we got one byte */
if(error != 1)
goto done;

/* feed MIDI byte into statemachine */
if(OssMidiParse(midi.state, 0, tmp)) {
/* we got a complete MIDI command */
midiProcess(midi.state.temp_cmd[1],
midi.state.temp_cmd[2],
midi.state.temp_cmd[3]);
}
}
done:
pthread_exit(NULL);
return NULL;
}

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save