Browse Source

Import ZynAddSubFX source code (latest git + NTK patches)

tags/v0.9.0
falkTX 13 years ago
parent
commit
aab5564bd5
100 changed files with 17828 additions and 0 deletions
  1. +371
    -0
      c++/carla-backend/plugins/zynaddsubfx/CMakeLists.txt
  2. +396
    -0
      c++/carla-backend/plugins/zynaddsubfx/DSP/AnalogFilter.cpp
  3. +85
    -0
      c++/carla-backend/plugins/zynaddsubfx/DSP/AnalogFilter.h
  4. +9
    -0
      c++/carla-backend/plugins/zynaddsubfx/DSP/CMakeLists.txt
  5. +85
    -0
      c++/carla-backend/plugins/zynaddsubfx/DSP/FFTwrapper.cpp
  6. +52
    -0
      c++/carla-backend/plugins/zynaddsubfx/DSP/FFTwrapper.h
  7. +62
    -0
      c++/carla-backend/plugins/zynaddsubfx/DSP/Filter.cpp
  8. +45
    -0
      c++/carla-backend/plugins/zynaddsubfx/DSP/Filter.h
  9. +231
    -0
      c++/carla-backend/plugins/zynaddsubfx/DSP/FormantFilter.cpp
  10. +66
    -0
      c++/carla-backend/plugins/zynaddsubfx/DSP/FormantFilter.h
  11. +178
    -0
      c++/carla-backend/plugins/zynaddsubfx/DSP/SVFilter.cpp
  12. +69
    -0
      c++/carla-backend/plugins/zynaddsubfx/DSP/SVFilter.h
  13. +198
    -0
      c++/carla-backend/plugins/zynaddsubfx/DSP/Unison.cpp
  14. +73
    -0
      c++/carla-backend/plugins/zynaddsubfx/DSP/Unison.h
  15. +235
    -0
      c++/carla-backend/plugins/zynaddsubfx/Effects/Alienwah.cpp
  16. +80
    -0
      c++/carla-backend/plugins/zynaddsubfx/Effects/Alienwah.h
  17. +14
    -0
      c++/carla-backend/plugins/zynaddsubfx/Effects/CMakeLists.txt
  18. +268
    -0
      c++/carla-backend/plugins/zynaddsubfx/Effects/Chorus.cpp
  19. +106
    -0
      c++/carla-backend/plugins/zynaddsubfx/Effects/Chorus.h
  20. +245
    -0
      c++/carla-backend/plugins/zynaddsubfx/Effects/Distorsion.cpp
  21. +61
    -0
      c++/carla-backend/plugins/zynaddsubfx/Effects/Distorsion.h
  22. +311
    -0
      c++/carla-backend/plugins/zynaddsubfx/Effects/DynamicFilter.cpp
  23. +65
    -0
      c++/carla-backend/plugins/zynaddsubfx/Effects/DynamicFilter.h
  24. +198
    -0
      c++/carla-backend/plugins/zynaddsubfx/Effects/EQ.cpp
  25. +55
    -0
      c++/carla-backend/plugins/zynaddsubfx/Effects/EQ.h
  26. +231
    -0
      c++/carla-backend/plugins/zynaddsubfx/Effects/Echo.cpp
  27. +104
    -0
      c++/carla-backend/plugins/zynaddsubfx/Effects/Echo.h
  28. +62
    -0
      c++/carla-backend/plugins/zynaddsubfx/Effects/Effect.cpp
  29. +105
    -0
      c++/carla-backend/plugins/zynaddsubfx/Effects/Effect.h
  30. +111
    -0
      c++/carla-backend/plugins/zynaddsubfx/Effects/EffectLFO.cpp
  31. +50
    -0
      c++/carla-backend/plugins/zynaddsubfx/Effects/EffectLFO.h
  32. +311
    -0
      c++/carla-backend/plugins/zynaddsubfx/Effects/EffectMgr.cpp
  33. +86
    -0
      c++/carla-backend/plugins/zynaddsubfx/Effects/EffectMgr.h
  34. +462
    -0
      c++/carla-backend/plugins/zynaddsubfx/Effects/Phaser.cpp
  35. +98
    -0
      c++/carla-backend/plugins/zynaddsubfx/Effects/Phaser.h
  36. +498
    -0
      c++/carla-backend/plugins/zynaddsubfx/Effects/Reverb.cpp
  37. +97
    -0
      c++/carla-backend/plugins/zynaddsubfx/Effects/Reverb.h
  38. +469
    -0
      c++/carla-backend/plugins/zynaddsubfx/Misc/Bank.cpp
  39. +103
    -0
      c++/carla-backend/plugins/zynaddsubfx/Misc/Bank.h
  40. +28
    -0
      c++/carla-backend/plugins/zynaddsubfx/Misc/CMakeLists.txt
  41. +300
    -0
      c++/carla-backend/plugins/zynaddsubfx/Misc/Config.cpp
  42. +73
    -0
      c++/carla-backend/plugins/zynaddsubfx/Misc/Config.h
  43. +99
    -0
      c++/carla-backend/plugins/zynaddsubfx/Misc/Control.h
  44. +121
    -0
      c++/carla-backend/plugins/zynaddsubfx/Misc/Dump.cpp
  45. +63
    -0
      c++/carla-backend/plugins/zynaddsubfx/Misc/Dump.h
  46. +103
    -0
      c++/carla-backend/plugins/zynaddsubfx/Misc/LASHClient.cpp
  47. +63
    -0
      c++/carla-backend/plugins/zynaddsubfx/Misc/LASHClient.h
  48. +798
    -0
      c++/carla-backend/plugins/zynaddsubfx/Misc/Master.cpp
  49. +175
    -0
      c++/carla-backend/plugins/zynaddsubfx/Misc/Master.h
  50. +694
    -0
      c++/carla-backend/plugins/zynaddsubfx/Misc/Microtonal.cpp
  51. +134
    -0
      c++/carla-backend/plugins/zynaddsubfx/Misc/Microtonal.h
  52. +1342
    -0
      c++/carla-backend/plugins/zynaddsubfx/Misc/Part.cpp
  53. +201
    -0
      c++/carla-backend/plugins/zynaddsubfx/Misc/Part.h
  54. +93
    -0
      c++/carla-backend/plugins/zynaddsubfx/Misc/Recorder.cpp
  55. +54
    -0
      c++/carla-backend/plugins/zynaddsubfx/Misc/Recorder.h
  56. +38
    -0
      c++/carla-backend/plugins/zynaddsubfx/Misc/Stereo.cpp
  57. +40
    -0
      c++/carla-backend/plugins/zynaddsubfx/Misc/Stereo.h
  58. +227
    -0
      c++/carla-backend/plugins/zynaddsubfx/Misc/Util.cpp
  59. +123
    -0
      c++/carla-backend/plugins/zynaddsubfx/Misc/Util.h
  60. +97
    -0
      c++/carla-backend/plugins/zynaddsubfx/Misc/WavFile.cpp
  61. +44
    -0
      c++/carla-backend/plugins/zynaddsubfx/Misc/WavFile.h
  62. +189
    -0
      c++/carla-backend/plugins/zynaddsubfx/Misc/WaveShapeSmps.cpp
  63. +31
    -0
      c++/carla-backend/plugins/zynaddsubfx/Misc/WaveShapeSmps.h
  64. +623
    -0
      c++/carla-backend/plugins/zynaddsubfx/Misc/XMLwrapper.cpp
  65. +271
    -0
      c++/carla-backend/plugins/zynaddsubfx/Misc/XMLwrapper.h
  66. +366
    -0
      c++/carla-backend/plugins/zynaddsubfx/Nio/AlsaEngine.cpp
  67. +82
    -0
      c++/carla-backend/plugins/zynaddsubfx/Nio/AlsaEngine.h
  68. +58
    -0
      c++/carla-backend/plugins/zynaddsubfx/Nio/AudioOut.cpp
  69. +61
    -0
      c++/carla-backend/plugins/zynaddsubfx/Nio/AudioOut.h
  70. +47
    -0
      c++/carla-backend/plugins/zynaddsubfx/Nio/CMakeLists.txt
  71. +28
    -0
      c++/carla-backend/plugins/zynaddsubfx/Nio/Engine.cpp
  72. +41
    -0
      c++/carla-backend/plugins/zynaddsubfx/Nio/Engine.h
  73. +156
    -0
      c++/carla-backend/plugins/zynaddsubfx/Nio/EngineMgr.cpp
  74. +43
    -0
      c++/carla-backend/plugins/zynaddsubfx/Nio/EngineMgr.h
  75. +129
    -0
      c++/carla-backend/plugins/zynaddsubfx/Nio/InMgr.cpp
  76. +52
    -0
      c++/carla-backend/plugins/zynaddsubfx/Nio/InMgr.h
  77. +402
    -0
      c++/carla-backend/plugins/zynaddsubfx/Nio/JackEngine.cpp
  78. +88
    -0
      c++/carla-backend/plugins/zynaddsubfx/Nio/JackEngine.h
  79. +77
    -0
      c++/carla-backend/plugins/zynaddsubfx/Nio/MidiIn.cpp
  80. +43
    -0
      c++/carla-backend/plugins/zynaddsubfx/Nio/MidiIn.h
  81. +117
    -0
      c++/carla-backend/plugins/zynaddsubfx/Nio/Nio.cpp
  82. +38
    -0
      c++/carla-backend/plugins/zynaddsubfx/Nio/Nio.h
  83. +113
    -0
      c++/carla-backend/plugins/zynaddsubfx/Nio/NulEngine.cpp
  84. +56
    -0
      c++/carla-backend/plugins/zynaddsubfx/Nio/NulEngine.h
  85. +281
    -0
      c++/carla-backend/plugins/zynaddsubfx/Nio/OssEngine.cpp
  86. +76
    -0
      c++/carla-backend/plugins/zynaddsubfx/Nio/OssEngine.h
  87. +182
    -0
      c++/carla-backend/plugins/zynaddsubfx/Nio/OutMgr.cpp
  88. +64
    -0
      c++/carla-backend/plugins/zynaddsubfx/Nio/OutMgr.h
  89. +118
    -0
      c++/carla-backend/plugins/zynaddsubfx/Nio/PaEngine.cpp
  90. +57
    -0
      c++/carla-backend/plugins/zynaddsubfx/Nio/PaEngine.h
  91. +82
    -0
      c++/carla-backend/plugins/zynaddsubfx/Nio/SafeQueue.cpp
  92. +46
    -0
      c++/carla-backend/plugins/zynaddsubfx/Nio/SafeQueue.h
  93. +134
    -0
      c++/carla-backend/plugins/zynaddsubfx/Nio/WavEngine.cpp
  94. +61
    -0
      c++/carla-backend/plugins/zynaddsubfx/Nio/WavEngine.h
  95. +698
    -0
      c++/carla-backend/plugins/zynaddsubfx/Output/DSSIaudiooutput.cpp
  96. +123
    -0
      c++/carla-backend/plugins/zynaddsubfx/Output/DSSIaudiooutput.h
  97. +792
    -0
      c++/carla-backend/plugins/zynaddsubfx/Params/ADnoteParameters.cpp
  98. +316
    -0
      c++/carla-backend/plugins/zynaddsubfx/Params/ADnoteParameters.h
  99. +13
    -0
      c++/carla-backend/plugins/zynaddsubfx/Params/CMakeLists.txt
  100. +419
    -0
      c++/carla-backend/plugins/zynaddsubfx/Params/Controller.cpp

+ 371
- 0
c++/carla-backend/plugins/zynaddsubfx/CMakeLists.txt View File

@@ -0,0 +1,371 @@
#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)
find_package(NTK)
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)

######### 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, qt 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}")

# 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(
-DASM_F2I_YES
-g #TODO #todo put in a better location
-Wall
-Wextra
)

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 NTK_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)
#UGLY WORKAROUND
find_program (NTK_CONFIG ntk-config)
if (NTK_CONFIG)
execute_process (COMMAND ${NTK_CONFIG} --use-images --ldflags OUTPUT_VARIABLE NTK_LDFLAGS)
string(STRIP ${NTK_LDFLAGS} NTK_LIBRARIES)
endif()

message(STATUS ${NTK_LDFLAGS})


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

add_definitions(-DNTK_GUI)

message(STATUS "Will build NTK gui")

include_directories(
${NTK_INCLUDE_DIR}
"${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)
if(CompileTests)
add_subdirectory(Tests)
endif(CompileTests)
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)

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_nio
zynaddsubfx_core
${GUI_LIBRARIES}
${NIO_LIBRARIES}
${AUDIO_LIBRARIES}
)

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

target_link_libraries(zynaddsubfx_dssi
zynaddsubfx_core
${NIO_LIBRARIES}
${AUDIO_LIBRARIES}
${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/")
endif(NtkGui)

include(CTest)

+ 396
- 0
c++/carla-backend/plugins/zynaddsubfx/DSP/AnalogFilter.cpp View File

@@ -0,0 +1,396 @@
/*
ZynAddSubFX - a software synthesizer

AnalogFilter.cpp - Several analog filters (lowpass, highpass...)
Copyright (C) 2002-2005 Nasca Octavian Paul
Copyright (C) 2010-2010 Mark McCurry
Author: Nasca Octavian Paul
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 <cstring> //memcpy
#include <cmath>

#include "../Misc/Util.h"
#include "AnalogFilter.h"

AnalogFilter::AnalogFilter(unsigned char Ftype,
float Ffreq,
float Fq,
unsigned char Fstages)
:type(Ftype),
stages(Fstages),
freq(Ffreq),
q(Fq),
gain(1.0),
abovenq(false),
oldabovenq(false)
{
for(int i = 0; i < 3; ++i)
coeff.c[i] = coeff.d[i] = oldCoeff.c[i] = oldCoeff.d[i] = 0.0f;
if(stages >= MAX_FILTER_STAGES)
stages = MAX_FILTER_STAGES;
cleanup();
firsttime = false;
setfreq_and_q(Ffreq, Fq);
firsttime = true;
coeff.d[0] = 0; //this is not used
outgain = 1.0f;
}

AnalogFilter::~AnalogFilter()
{}

void AnalogFilter::cleanup()
{
for(int i = 0; i < MAX_FILTER_STAGES + 1; ++i) {
history[i].x1 = 0.0f;
history[i].x2 = 0.0f;
history[i].y1 = 0.0f;
history[i].y2 = 0.0f;
oldHistory[i] = history[i];
}
needsinterpolation = false;
}

void AnalogFilter::computefiltercoefs(void)
{
float tmp;
bool zerocoefs = false; //this is used if the freq is too high

//do not allow frequencies bigger than samplerate/2
float freq = this->freq;
if(freq > (synth->halfsamplerate_f - 500.0f)) {
freq = synth->halfsamplerate_f - 500.0f;
zerocoefs = true;
}
if(freq < 0.1f)
freq = 0.1f;
//do not allow bogus Q
if(q < 0.0f)
q = 0.0f;
float tmpq, tmpgain;
if(stages == 0) {
tmpq = q;
tmpgain = gain;
}
else {
tmpq = (q > 1.0f) ? powf(q, 1.0f / (stages + 1)) : q;
tmpgain = powf(gain, 1.0f / (stages + 1));
}

//Alias Terms
float *c = coeff.c;
float *d = coeff.d;

//General Constants
const float omega = 2 * PI * freq / synth->samplerate_f;
const float sn = sinf(omega), cs = cosf(omega);
float alpha, beta;

//most of theese are implementations of
//the "Cookbook formulae for audio EQ" by Robert Bristow-Johnson
//The original location of the Cookbook is:
//http://www.harmony-central.com/Computer/Programming/Audio-EQ-Cookbook.txt
switch(type) {
case 0: //LPF 1 pole
if(!zerocoefs)
tmp = expf(-2.0f * PI * freq / synth->samplerate_f);
else
tmp = 0.0f;
c[0] = 1.0f - tmp;
c[1] = 0.0f;
c[2] = 0.0f;
d[1] = tmp;
d[2] = 0.0f;
order = 1;
break;
case 1: //HPF 1 pole
if(!zerocoefs)
tmp = expf(-2.0f * PI * freq / synth->samplerate_f);
else
tmp = 0.0f;
c[0] = (1.0f + tmp) / 2.0f;
c[1] = -(1.0f + tmp) / 2.0f;
c[2] = 0.0f;
d[1] = tmp;
d[2] = 0.0f;
order = 1;
break;
case 2: //LPF 2 poles
if(!zerocoefs) {
alpha = sn / (2.0f * tmpq);
tmp = 1 + alpha;
c[1] = (1.0f - cs) / tmp;
c[0] = c[2] = c[1] / 2.0f;
d[1] = -2.0f * cs / tmp * -1.0f;
d[2] = (1.0f - alpha) / tmp * -1.0f;
}
else {
c[0] = 1.0f;
c[1] = c[2] = d[1] = d[2] = 0.0f;
}
order = 2;
break;
case 3: //HPF 2 poles
if(!zerocoefs) {
alpha = sn / (2.0f * tmpq);
tmp = 1 + alpha;
c[0] = (1.0f + cs) / 2.0f / tmp;
c[1] = -(1.0f + cs) / tmp;
c[2] = (1.0f + cs) / 2.0f / tmp;
d[1] = -2.0f * cs / tmp * -1.0f;
d[2] = (1.0f - alpha) / tmp * -1.0f;
}
else
c[0] = c[1] = c[2] = d[1] = d[2] = 0.0f;
order = 2;
break;
case 4: //BPF 2 poles
if(!zerocoefs) {
alpha = sn / (2.0f * tmpq);
tmp = 1.0f + alpha;
c[0] = alpha / tmp *sqrtf(tmpq + 1.0f);
c[1] = 0.0f;
c[2] = -alpha / tmp *sqrtf(tmpq + 1.0f);
d[1] = -2.0f * cs / tmp * -1.0f;
d[2] = (1.0f - alpha) / tmp * -1.0f;
}
else
c[0] = c[1] = c[2] = d[1] = d[2] = 0.0f;
order = 2;
break;
case 5: //NOTCH 2 poles
if(!zerocoefs) {
alpha = sn / (2.0f * sqrtf(tmpq));
tmp = 1.0f + alpha;
c[0] = 1.0f / tmp;
c[1] = -2.0f * cs / tmp;
c[2] = 1.0f / tmp;
d[1] = -2.0f * cs / tmp * -1.0f;
d[2] = (1.0f - alpha) / tmp * -1.0f;
}
else {
c[0] = 1.0f;
c[1] = c[2] = d[1] = d[2] = 0.0f;
}
order = 2;
break;
case 6: //PEAK (2 poles)
if(!zerocoefs) {
tmpq *= 3.0f;
alpha = sn / (2.0f * tmpq);
tmp = 1.0f + alpha / tmpgain;
c[0] = (1.0f + alpha * tmpgain) / tmp;
c[1] = (-2.0f * cs) / tmp;
c[2] = (1.0f - alpha * tmpgain) / tmp;
d[1] = -2.0f * cs / tmp * -1.0f;
d[2] = (1.0f - alpha / tmpgain) / tmp * -1.0f;
}
else {
c[0] = 1.0f;
c[1] = c[2] = d[1] = d[2] = 0.0f;
}
order = 2;
break;
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;

c[0] = tmpgain
* ((tmpgain
+ 1.0f) - (tmpgain - 1.0f) * cs + beta * sn) / tmp;
c[1] = 2.0f * tmpgain
* ((tmpgain - 1.0f) - (tmpgain + 1.0f) * cs) / tmp;
c[2] = tmpgain
* ((tmpgain
+ 1.0f) - (tmpgain - 1.0f) * cs - beta * sn) / tmp;
d[1] = -2.0f * ((tmpgain - 1.0f) + (tmpgain + 1.0f) * cs)
/ tmp * -1.0f;
d[2] = ((tmpgain + 1.0f) + (tmpgain - 1.0f) * cs - beta * sn)
/ tmp * -1.0f;
}
else {
c[0] = tmpgain;
c[1] = c[2] = d[1] = d[2] = 0.0f;
}
order = 2;
break;
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;

c[0] = tmpgain
* ((tmpgain
+ 1.0f) + (tmpgain - 1.0f) * cs + beta * sn) / tmp;
c[1] = -2.0f * tmpgain
* ((tmpgain - 1.0f) + (tmpgain + 1.0f) * cs) / tmp;
c[2] = tmpgain
* ((tmpgain
+ 1.0f) + (tmpgain - 1.0f) * cs - beta * sn) / tmp;
d[1] = 2.0f * ((tmpgain - 1.0f) - (tmpgain + 1.0f) * cs)
/ tmp * -1.0f;
d[2] = ((tmpgain + 1.0f) - (tmpgain - 1.0f) * cs - beta * sn)
/ tmp * -1.0f;
}
else {
c[0] = 1.0f;
c[1] = c[2] = d[1] = d[2] = 0.0f;
}
order = 2;
break;
default: //wrong type
type = 0;
computefiltercoefs();
break;
}
}


void AnalogFilter::setfreq(float frequency)
{
if(frequency < 0.1f)
frequency = 0.1f;
float rap = freq / frequency;
if(rap < 1.0f)
rap = 1.0f / rap;

oldabovenq = abovenq;
abovenq = frequency > (synth->halfsamplerate_f - 500.0f);

bool nyquistthresh = (abovenq ^ oldabovenq);


//if the frequency is changed fast, it needs interpolation
if((rap > 3.0f) || nyquistthresh) { //(now, filter and coeficients backup)
oldCoeff = coeff;
for(int i = 0; i < MAX_FILTER_STAGES + 1; ++i)
oldHistory[i] = history[i];
if(!firsttime)
needsinterpolation = true;
}
freq = frequency;
computefiltercoefs();
firsttime = false;
}

void AnalogFilter::setfreq_and_q(float frequency, float q_)
{
q = q_;
setfreq(frequency);
}

void AnalogFilter::setq(float q_)
{
q = q_;
computefiltercoefs();
}

void AnalogFilter::settype(int type_)
{
type = type_;
computefiltercoefs();
}

void AnalogFilter::setgain(float dBgain)
{
gain = dB2rap(dBgain);
computefiltercoefs();
}

void AnalogFilter::setstages(int stages_)
{
if(stages_ >= MAX_FILTER_STAGES)
stages_ = MAX_FILTER_STAGES - 1;
stages = stages_;
cleanup();
computefiltercoefs();
}

void AnalogFilter::singlefilterout(float *smp, fstage &hist,
const Coeff &coeff)
{
if(order == 1) //First order filter
for(int i = 0; i < synth->buffersize; ++i) {
float y0 = smp[i] * coeff.c[0] + hist.x1 * coeff.c[1]
+ hist.y1 * coeff.d[1];
hist.y1 = y0;
hist.x1 = smp[i];
smp[i] = y0;
}
if(order == 2) //Second order filter
for(int i = 0; i < synth->buffersize; ++i) {
float y0 = smp[i] * coeff.c[0] + hist.x1 * coeff.c[1]
+ hist.x2 * coeff.c[2] + hist.y1 * coeff.d[1]
+ hist.y2 * coeff.d[2];
hist.y2 = hist.y1;
hist.y1 = y0;
hist.x2 = hist.x1;
hist.x1 = smp[i];
smp[i] = y0;
}
}

void AnalogFilter::filterout(float *smp)
{
for(int i = 0; i < stages + 1; ++i)
singlefilterout(smp, history[i], coeff);

if(needsinterpolation) {
//Merge Filter at old coeff with new coeff
float *ismp = getTmpBuffer();
memcpy(ismp, smp, synth->bufferbytes);

for(int i = 0; i < stages + 1; ++i)
singlefilterout(ismp, oldHistory[i], oldCoeff);

for(int i = 0; i < synth->buffersize; ++i) {
float x = (float)i / synth->buffersize_f;
smp[i] = ismp[i] * (1.0f - x) + smp[i] * x;
}
returnTmpBuffer(ismp);
needsinterpolation = false;
}

for(int i = 0; i < synth->buffersize; ++i)
smp[i] *= outgain;
}

float AnalogFilter::H(float freq)
{
float fr = freq / synth->samplerate_f * PI * 2.0f;
float x = coeff.c[0], y = 0.0f;
for(int n = 1; n < 3; ++n) {
x += cosf(n * fr) * coeff.c[n];
y -= sinf(n * fr) * coeff.c[n];
}
float h = x * x + y * y;
x = 1.0f;
y = 0.0f;
for(int n = 1; n < 3; ++n) {
x -= cosf(n * fr) * coeff.d[n];
y += sinf(n * fr) * coeff.d[n];
}
h = h / (x * x + y * y);
return powf(h, (stages + 1.0f) / 2.0f);
}

+ 85
- 0
c++/carla-backend/plugins/zynaddsubfx/DSP/AnalogFilter.h View File

@@ -0,0 +1,85 @@
/*
ZynAddSubFX - a software synthesizer

Analog Filter.h - Several analog filters (lowpass, highpass...)
Copyright (C) 2002-2005 Nasca Octavian Paul
Copyright (C) 2010-2010 Mark McCurry
Author: Nasca Octavian Paul
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 ANALOG_FILTER_H
#define ANALOG_FILTER_H

#include "../globals.h"
#include "Filter.h"

/**Implementation of Several analog filters (lowpass, highpass...)
* Implemented with IIR filters
* Coefficients generated with "Cookbook formulae for audio EQ"*/
class AnalogFilter:public Filter
{
public:
AnalogFilter(unsigned char Ftype, float Ffreq, float Fq,
unsigned char Fstages);
~AnalogFilter();
void filterout(float *smp);
void setfreq(float frequency);
void setfreq_and_q(float frequency, float q_);
void setq(float q_);

void settype(int type_);
void setgain(float dBgain);
void setstages(int stages_);
void cleanup();

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

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
void singlefilterout(float *smp, fstage &hist, const Coeff &coeff);
//Update coeff and order
void computefiltercoefs(void);

int type; //The type of the filter (LPF1,HPF1,LPF2,HPF2...)
int stages; //how many times the filter is applied (0->1,1->2,etc.)
float freq; //Frequency given in Hz
float q; //Q factor (resonance or Q factor)
float gain; //the gain of the filter (if are shelf/peak) filters

int order; //the order of the filter (number of poles)

bool needsinterpolation, //Interpolation between coeff changes
firsttime; //First Iteration of filter
bool abovenq, //if the frequency is above the nyquist
oldabovenq; //if the last time was above nyquist
//(used to see if it needs interpolation)
};


#endif

+ 9
- 0
c++/carla-backend/plugins/zynaddsubfx/DSP/CMakeLists.txt View File

@@ -0,0 +1,9 @@
set(zynaddsubfx_dsp_SRCS
DSP/AnalogFilter.cpp
DSP/FFTwrapper.cpp
DSP/Filter.cpp
DSP/FormantFilter.cpp
DSP/SVFilter.cpp
DSP/Unison.cpp
PARENT_SCOPE
)

+ 85
- 0
c++/carla-backend/plugins/zynaddsubfx/DSP/FFTwrapper.cpp View File

@@ -0,0 +1,85 @@
/*
ZynAddSubFX - a software synthesizer

FFTwrapper.c - A wrapper for Fast Fourier Transforms
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 <cmath>
#include <cassert>
#include <cstring>
#include "FFTwrapper.h"

FFTwrapper::FFTwrapper(int fftsize_)
{
fftsize = fftsize_;
time = new fftw_real[fftsize];
fft = new fftw_complex[fftsize + 1];
planfftw = fftw_plan_dft_r2c_1d(fftsize,
time,
fft,
FFTW_ESTIMATE);
planfftw_inv = fftw_plan_dft_c2r_1d(fftsize,
fft,
time,
FFTW_ESTIMATE);
}

FFTwrapper::~FFTwrapper()
{
fftw_destroy_plan(planfftw);
fftw_destroy_plan(planfftw_inv);

delete [] time;
delete [] fft;
}

void FFTwrapper::smps2freqs(const float *smps, fft_t *freqs)
{
//Load data
for(int i = 0; i < fftsize; ++i)
time[i] = static_cast<double>(smps[i]);

//DFT
fftw_execute(planfftw);

//Grab data
memcpy((void *)freqs, (const void *)fft, fftsize * sizeof(double));
}

void FFTwrapper::freqs2smps(const fft_t *freqs, float *smps)
{
//Load data
memcpy((void *)fft, (const void *)freqs, fftsize * sizeof(double));

//clear unused freq channel
fft[fftsize / 2][0] = 0.0f;
fft[fftsize / 2][1] = 0.0f;

//IDFT
fftw_execute(planfftw_inv);

//Grab data
for(int i = 0; i < fftsize; ++i)
smps[i] = static_cast<float>(time[i]);
}

void FFT_cleanup()
{
fftw_cleanup();
}

+ 52
- 0
c++/carla-backend/plugins/zynaddsubfx/DSP/FFTwrapper.h View File

@@ -0,0 +1,52 @@
/*
ZynAddSubFX - a software synthesizer

FFTwrapper.h - A wrapper for Fast Fourier Transforms
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 FFT_WRAPPER_H
#define FFT_WRAPPER_H
#include <fftw3.h>
#include <complex>
typedef double fftw_real;
typedef std::complex<fftw_real> fft_t;

/**A wrapper for the FFTW library (Fast Fourier Transforms)*/
class FFTwrapper
{
public:
/**Constructor
* @param fftsize The size of samples to be fed to fftw*/
FFTwrapper(int fftsize_);
/**Destructor*/
~FFTwrapper();
/**Convert Samples to Frequencies using Fourier Transform
* @param smps Pointer to Samples to be converted; has length fftsize_
* @param freqs Structure FFTFREQS which stores the frequencies*/
void smps2freqs(const float *smps, fft_t *freqs);
void freqs2smps(const fft_t *freqs, float *smps);
private:
int fftsize;
fftw_real *time;
fftw_complex *fft;
fftw_plan planfftw, planfftw_inv;
};

void FFT_cleanup();
#endif

+ 62
- 0
c++/carla-backend/plugins/zynaddsubfx/DSP/Filter.cpp View File

@@ -0,0 +1,62 @@
/*
ZynAddSubFX - a software synthesizer

Filter.cpp - Filters, uses analog,formant,etc. filters
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 <math.h>
#include <stdio.h>

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

Filter *Filter::generate(FilterParams *pars)
{
unsigned char Ftype = pars->Ptype;
unsigned char Fstages = pars->Pstages;

Filter *filter;
switch(pars->Pcategory) {
case 1:
filter = new FormantFilter(pars);
break;
case 2:
filter = new SVFilter(Ftype, 1000.0f, pars->getq(), Fstages);
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);
if((Ftype >= 6) && (Ftype <= 8))
filter->setgain(pars->getgain());
else
filter->outgain = dB2rap(pars->getgain());
break;
}
return filter;
}

float Filter::getrealfreq(float freqpitch)
{
return powf(2.0f, freqpitch + 9.96578428f); //log2(1000)=9.95748f
}

+ 45
- 0
c++/carla-backend/plugins/zynaddsubfx/DSP/Filter.h View File

@@ -0,0 +1,45 @@
/*
ZynAddSubFX - a software synthesizer

Filter.h - Filters, uses analog,formant,etc. filters
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 FILTER_H
#define FILTER_H

#include "../globals.h"

class Filter
{
public:
static float getrealfreq(float freqpitch);
static Filter *generate(class FilterParams * pars);

virtual ~Filter() {}
virtual void filterout(float *smp) = 0;
virtual void setfreq(float frequency) = 0;
virtual void setfreq_and_q(float frequency, float q_) = 0;
virtual void setq(float q_) = 0;
virtual void setgain(float dBgain) = 0;

protected:
float outgain;
};

#endif

+ 231
- 0
c++/carla-backend/plugins/zynaddsubfx/DSP/FormantFilter.cpp View File

@@ -0,0 +1,231 @@
/*
ZynAddSubFX - a software synthesizer

FormantFilter.cpp - formant filters
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 <cmath>
#include <cstdio>
#include "../Misc/Util.h"
#include "FormantFilter.h"
#include "AnalogFilter.h"
#include "../Params/FilterParams.h"

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

for(int j = 0; j < FF_MAX_VOWELS; ++j)
for(int i = 0; i < numformants; ++i) {
formantpar[j][i].freq = pars->getformantfreq(
pars->Pvowels[j].formants[i].freq);
formantpar[j][i].amp = pars->getformantamp(
pars->Pvowels[j].formants[i].amp);
formantpar[j][i].q = pars->getformantq(
pars->Pvowels[j].formants[i].q);
}

for(int i = 0; i < FF_MAX_FORMANTS; ++i)
oldformantamp[i] = 1.0f;
for(int i = 0; i < numformants; ++i) {
currentformants[i].freq = 1000.0f;
currentformants[i].amp = 1.0f;
currentformants[i].q = 2.0f;
}

formantslowness = powf(1.0f - (pars->Pformantslowness / 128.0f), 3.0f);

sequencesize = pars->Psequencesize;
if(sequencesize == 0)
sequencesize = 1;
for(int k = 0; k < sequencesize; ++k)
sequence[k].nvowel = pars->Psequence[k].nvowel;

vowelclearness = powf(10.0f, (pars->Pvowelclearness - 32.0f) / 48.0f);

sequencestretch = powf(0.1f, (pars->Psequencestretch - 32.0f) / 48.0f);
if(pars->Psequencereversed)
sequencestretch *= -1.0f;

outgain = dB2rap(pars->getgain());

oldinput = -1.0f;
Qfactor = 1.0f;
oldQfactor = Qfactor;
firsttime = 1;
}

FormantFilter::~FormantFilter()
{
for(int i = 0; i < numformants; ++i)
delete (formant[i]);
}

void FormantFilter::cleanup()
{
for(int i = 0; i < numformants; ++i)
formant[i]->cleanup();
}

void FormantFilter::setpos(float input)
{
int p1, p2;

if(firsttime != 0)
slowinput = input;
else
slowinput = slowinput
* (1.0f - formantslowness) + input * formantslowness;

if((fabsf(oldinput - input) < 0.001f) && (fabsf(slowinput - input) < 0.001f)
&& (fabsf(Qfactor - oldQfactor) < 0.001f)) {
// oldinput=input; daca setez asta, o sa faca probleme la schimbari foarte lente
firsttime = 0;
return;
}
else
oldinput = input;

float pos = fmodf(input * sequencestretch, 1.0f);
if(pos < 0.0f)
pos += 1.0f;

F2I(pos * sequencesize, p2);
p1 = p2 - 1;
if(p1 < 0)
p1 += sequencesize;

pos = fmodf(pos * sequencesize, 1.0f);
if(pos < 0.0f)
pos = 0.0f;
else
if(pos > 1.0f)
pos = 1.0f;
pos =
(atanf((pos * 2.0f
- 1.0f)
* vowelclearness) / atanf(vowelclearness) + 1.0f) * 0.5f;

p1 = sequence[p1].nvowel;
p2 = sequence[p2].nvowel;

if(firsttime != 0) {
for(int i = 0; i < numformants; ++i) {
currentformants[i].freq =
formantpar[p1][i].freq
* (1.0f - pos) + formantpar[p2][i].freq * pos;
currentformants[i].amp =
formantpar[p1][i].amp
* (1.0f - pos) + formantpar[p2][i].amp * pos;
currentformants[i].q =
formantpar[p1][i].q * (1.0f - pos) + formantpar[p2][i].q * pos;
formant[i]->setfreq_and_q(currentformants[i].freq,
currentformants[i].q * Qfactor);
oldformantamp[i] = currentformants[i].amp;
}
firsttime = 0;
}
else
for(int i = 0; i < numformants; ++i) {
currentformants[i].freq =
currentformants[i].freq * (1.0f - formantslowness)
+ (formantpar[p1][i].freq
* (1.0f - pos) + formantpar[p2][i].freq * pos)
* formantslowness;

currentformants[i].amp =
currentformants[i].amp * (1.0f - formantslowness)
+ (formantpar[p1][i].amp * (1.0f - pos)
+ formantpar[p2][i].amp * pos) * formantslowness;

currentformants[i].q = currentformants[i].q
* (1.0f - formantslowness)
+ (formantpar[p1][i].q * (1.0f - pos)
+ formantpar[p2][i].q
* pos) * formantslowness;


formant[i]->setfreq_and_q(currentformants[i].freq,
currentformants[i].q * Qfactor);
}

oldQfactor = Qfactor;
}

void FormantFilter::setfreq(float frequency)
{
setpos(frequency);
}

void FormantFilter::setq(float q_)
{
Qfactor = q_;
for(int i = 0; i < numformants; ++i)
formant[i]->setq(Qfactor * currentformants[i].q);
}

void FormantFilter::setgain(float /*dBgain*/)
{}

inline float log_2(float x)
{
return logf(x) / logf(2.0f);
}

void FormantFilter::setfreq_and_q(float frequency, float q_)
{
//Convert form real freq[Hz]
const float freq = log_2(frequency) - 9.96578428f; //log2(1000)=9.95748f.

Qfactor = q_;
setpos(freq);
}


void FormantFilter::filterout(float *smp)
{
float *inbuffer = getTmpBuffer();

memcpy(inbuffer, smp, synth->bufferbytes);
memset(smp, 0, synth->bufferbytes);

for(int j = 0; j < numformants; ++j) {
float *tmpbuf = getTmpBuffer();
for(int i = 0; i < synth->buffersize; ++i)
tmpbuf[i] = inbuffer[i] * outgain;
formant[j]->filterout(tmpbuf);

if(ABOVE_AMPLITUDE_THRESHOLD(oldformantamp[j], currentformants[j].amp))
for(int i = 0; i < synth->buffersize; ++i)
smp[i] += tmpbuf[i]
* INTERPOLATE_AMPLITUDE(oldformantamp[j],
currentformants[j].amp,
i,
synth->buffersize);
else
for(int i = 0; i < synth->buffersize; ++i)
smp[i] += tmpbuf[i] * currentformants[j].amp;
returnTmpBuffer(tmpbuf);
oldformantamp[j] = currentformants[j].amp;
}
returnTmpBuffer(inbuffer);
}

+ 66
- 0
c++/carla-backend/plugins/zynaddsubfx/DSP/FormantFilter.h View File

@@ -0,0 +1,66 @@
/*
ZynAddSubFX - a software synthesizer

FormantFilter.h - formant filter
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 FORMANT_FILTER_H
#define FORMANT_FILTER_H

#include "../globals.h"
#include "Filter.h"


class FormantFilter:public Filter
{
public:
FormantFilter(class FilterParams *pars);
~FormantFilter();
void filterout(float *smp);
void setfreq(float frequency);
void setfreq_and_q(float frequency, float q_);
void setq(float q_);
void setgain(float dBgain);

void cleanup(void);

private:
void setpos(float input);


class AnalogFilter * formant[FF_MAX_FORMANTS];

struct {
float freq, amp, q; //frequency,amplitude,Q
} formantpar[FF_MAX_VOWELS][FF_MAX_FORMANTS],
currentformants[FF_MAX_FORMANTS];

struct {
unsigned char nvowel;
} sequence [FF_MAX_SEQUENCE];

float oldformantamp[FF_MAX_FORMANTS];

int sequencesize, numformants, firsttime;
float oldinput, slowinput;
float Qfactor, formantslowness, oldQfactor;
float vowelclearness, sequencestretch;
};

#endif

+ 178
- 0
c++/carla-backend/plugins/zynaddsubfx/DSP/SVFilter.cpp View File

@@ -0,0 +1,178 @@
/*
ZynAddSubFX - a software synthesizer

SVFilter.cpp - Several state-variable filters
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 <cmath>
#include <cstdio>
#include <cstring>
#include <cassert>
#include <err.h>
#include "../Misc/Util.h"
#include "SVFilter.h"

SVFilter::SVFilter(unsigned char Ftype, float Ffreq, float Fq,
unsigned char Fstages)
:type(Ftype),
stages(Fstages),
freq(Ffreq),
q(Fq),
gain(1.0f),
needsinterpolation(false),
firsttime(true)
{
if(stages >= MAX_FILTER_STAGES)
stages = MAX_FILTER_STAGES;
outgain = 1.0f;
cleanup();
setfreq_and_q(Ffreq, Fq);
}

SVFilter::~SVFilter()
{}

void SVFilter::cleanup()
{
for(int i = 0; i < MAX_FILTER_STAGES + 1; ++i)
st[i].low = st[i].high = st[i].band = st[i].notch = 0.0f;
oldabovenq = false;
abovenq = false;
}

void SVFilter::computefiltercoefs(void)
{
par.f = freq / synth->samplerate_f * 4.0f;
if(par.f > 0.99999f)
par.f = 0.99999f;
par.q = 1.0f - atanf(sqrtf(q)) * 2.0f / PI;
par.q = powf(par.q, 1.0f / (stages + 1));
par.q_sqrt = sqrtf(par.q);
}


void SVFilter::setfreq(float frequency)
{
if(frequency < 0.1f)
frequency = 0.1f;
float rap = freq / frequency;
if(rap < 1.0f)
rap = 1.0f / rap;

oldabovenq = abovenq;
abovenq = frequency > (synth->samplerate_f / 2 - 500.0f);

bool nyquistthresh = (abovenq ^ oldabovenq);

//if the frequency is changed fast, it needs interpolation
if((rap > 3.0f) || nyquistthresh) { //(now, filter and coeficients backup)
if(!firsttime)
needsinterpolation = true;
ipar = par;
}
freq = frequency;
computefiltercoefs();
firsttime = false;
}

void SVFilter::setfreq_and_q(float frequency, float q_)
{
q = q_;
setfreq(frequency);
}

void SVFilter::setq(float q_)
{
q = q_;
computefiltercoefs();
}

void SVFilter::settype(int type_)
{
type = type_;
computefiltercoefs();
}

void SVFilter::setgain(float dBgain)
{
gain = dB2rap(dBgain);
computefiltercoefs();
}

void SVFilter::setstages(int stages_)
{
if(stages_ >= MAX_FILTER_STAGES)
stages_ = MAX_FILTER_STAGES - 1;
stages = stages_;
cleanup();
computefiltercoefs();
}

void SVFilter::singlefilterout(float *smp, fstage &x, parameters &par)
{
float *out = NULL;
switch(type) {
case 0:
out = &x.low;
break;
case 1:
out = &x.high;
break;
case 2:
out = &x.band;
break;
case 3:
out = &x.notch;
break;
default:
errx(1, "Impossible SVFilter type encountered [%d]", type);
}

for(int i = 0; i < synth->buffersize; ++i) {
x.low = x.low + par.f * x.band;
x.high = par.q_sqrt * smp[i] - x.low - par.q * x.band;
x.band = par.f * x.high + x.band;
x.notch = x.high + x.low;
smp[i] = *out;
}
}

void SVFilter::filterout(float *smp)
{
for(int i = 0; i < stages + 1; ++i)
singlefilterout(smp, st[i], par);

if(needsinterpolation) {
float *ismp = getTmpBuffer();
memcpy(ismp, smp, synth->bufferbytes);

for(int i = 0; i < stages + 1; ++i)
singlefilterout(ismp, st[i], ipar);

for(int i = 0; i < synth->buffersize; ++i) {
float x = i / synth->buffersize_f;
smp[i] = ismp[i] * (1.0f - x) + smp[i] * x;
}
returnTmpBuffer(ismp);
needsinterpolation = false;
}

for(int i = 0; i < synth->buffersize; ++i)
smp[i] *= outgain;
}

+ 69
- 0
c++/carla-backend/plugins/zynaddsubfx/DSP/SVFilter.h View File

@@ -0,0 +1,69 @@
/*
ZynAddSubFX - a software synthesizer

SV Filter.h - Several state-variable filters
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 SV_FILTER_H
#define SV_FILTER_H

#include "../globals.h"
#include "Filter.h"

class SVFilter:public Filter
{
public:
SVFilter(unsigned char Ftype,
float Ffreq,
float Fq,
unsigned char Fstages);
~SVFilter();
void filterout(float *smp);
void setfreq(float frequency);
void setfreq_and_q(float frequency, float q_);
void setq(float q_);

void settype(int type_);
void setgain(float dBgain);
void setstages(int stages_);
void cleanup();

private:
struct fstage {
float low, high, band, notch;
} st[MAX_FILTER_STAGES + 1];

struct parameters {
float f, q, q_sqrt;
} par, ipar;

void singlefilterout(float *smp, fstage &x, parameters &par);
void computefiltercoefs(void);
int type; // The type of the filter (LPF1,HPF1,LPF2,HPF2...)
int stages; // how many times the filter is applied (0->1,1->2,etc.)
float freq; // Frequency given in Hz
float q; // Q factor (resonance or Q factor)
float gain; // the gain of the filter (if are shelf/peak) filters

bool abovenq, //if the frequency is above the nyquist
oldabovenq;
bool needsinterpolation, firsttime;
};

#endif

+ 198
- 0
c++/carla-backend/plugins/zynaddsubfx/DSP/Unison.cpp View File

@@ -0,0 +1,198 @@
/*
ZynAddSubFX - a software synthesizer

Unison.cpp - Unison effect (multivoice chorus)
Copyright (C) 2002-2009 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 <cmath>
#include <cstring>
#include <err.h>

#include "Unison.h"

Unison::Unison(int update_period_samples_, float max_delay_sec_)
:unison_size(0),
base_freq(1.0f),
uv(NULL),
update_period_samples(update_period_samples_),
update_period_sample_k(0),
max_delay((int)(synth->samplerate_f * max_delay_sec_) + 1),
delay_k(0),
first_time(false),
delay_buffer(NULL),
unison_amplitude_samples(0.0f),
unison_bandwidth_cents(10.0f)
{
if(max_delay < 10)
max_delay = 10;
delay_buffer = new float[max_delay];
memset(delay_buffer, 0, max_delay * sizeof(float));
setSize(1);
}

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

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];
first_time = true;
updateParameters();
}

void Unison::setBaseFrequency(float freq)
{
base_freq = freq;
updateParameters();
}

void Unison::setBandwidth(float bandwidth)
{
if(bandwidth < 0)
bandwidth = 0.0f;
if(bandwidth > 1200.0f)
bandwidth = 1200.0f;

/* If the bandwidth is too small, the audio may cancel itself out
* (due to the sign change of the outputs)
* TODO figure out the acceptable lower bound and codify it
*/
unison_bandwidth_cents = bandwidth;
updateParameters();
}

void Unison::updateParameters(void)
{
if(!uv)
return;
float increments_per_second = synth->samplerate_f
/ (float) update_period_samples;
// printf("#%g, %g\n",increments_per_second,base_freq);
for(int i = 0; i < unison_size; ++i) {
float base = powf(UNISON_FREQ_SPAN, synth->numRandom() * 2.0f - 1.0f);
uv[i].relative_amplitude = base;
float period = base / base_freq;
float m = 4.0f / (period * increments_per_second);
if(synth->numRandom() < 0.5f)
m = -m;
uv[i].step = m;
// printf("%g %g\n",uv[i].relative_amplitude,period);
}

float max_speed = powf(2.0f, unison_bandwidth_cents / 1200.0f);
unison_amplitude_samples = 0.125f * (max_speed - 1.0f)
* synth->samplerate_f / base_freq;

//If functions exceed this limit, they should have requested a bigguer delay
//and thus are buggy
if(unison_amplitude_samples >= max_delay - 1) {
warnx("BUG: Unison amplitude samples too big");
warnx("Unision max_delay should be larger");
unison_amplitude_samples = max_delay - 2;
}

updateUnisonData();
}

void Unison::process(int bufsize, float *inbuf, float *outbuf)
{
if(!uv)
return;
if(!outbuf)
outbuf = inbuf;

float volume = 1.0f / sqrtf(unison_size);
float xpos_step = 1.0f / (float) update_period_samples;
float xpos = (float) update_period_sample_k * xpos_step;
for(int i = 0; i < bufsize; ++i) {
if(update_period_sample_k++ >= update_period_samples) {
updateUnisonData();
update_period_sample_k = 0;
xpos = 0.0f;
}
xpos += xpos_step;
float in = inbuf[i], out = 0.0f;
float sign = 1.0f;
for(int k = 0; k < unison_size; ++k) {
float vpos = uv[k].realpos1 * (1.0f - xpos) + uv[k].realpos2 * xpos; //optimize
float pos = (float)(delay_k + max_delay) - vpos - 1.0f;
int posi;
F2I(pos, posi); //optimize!
int posi_next = posi + 1;
if(posi >= max_delay)
posi -= max_delay;
if(posi_next >= max_delay)
posi_next -= max_delay;
float posf = pos - floorf(pos);
out += ((1.0f - posf) * delay_buffer[posi] + posf
* delay_buffer[posi_next]) * sign;
sign = -sign;
}
outbuf[i] = out * volume;
// printf("%d %g\n",i,outbuf[i]);
delay_buffer[delay_k] = in;
delay_k = (++delay_k < max_delay) ? delay_k : 0;
}
}

void Unison::updateUnisonData()
{
if(!uv)
return;

for(int k = 0; k < unison_size; ++k) {
float pos = uv[k].position;
float step = uv[k].step;
pos += step;
if(pos <= -1.0f) {
pos = -1.0f;
step = -step;
}
else
if(pos >= 1.0f) {
pos = 1.0f;
step = -step;
}
float vibratto_val = (pos - 0.333333333f * pos * pos * pos) * 1.5f; //make the vibratto lfo smoother

//Relative amplitude is utilized, so the delay may be larger than the
//whole buffer, if the buffer is too small, this indicates a buggy call
//to Unison()
float newval = 1.0f + 0.5f
* (vibratto_val + 1.0f) * unison_amplitude_samples
* uv[k].relative_amplitude;

if(first_time)
uv[k].realpos1 = uv[k].realpos2 = newval;
else {
uv[k].realpos1 = uv[k].realpos2;
uv[k].realpos2 = newval;
}

uv[k].position = pos;
uv[k].step = step;
}
first_time = false;
}

+ 73
- 0
c++/carla-backend/plugins/zynaddsubfx/DSP/Unison.h View File

@@ -0,0 +1,73 @@
/*
ZynAddSubFX - a software synthesizer

Unison.h - Unison effect (multivoice chorus)
Copyright (C) 2002-2009 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 UNISON_H
#define UNISON_H

#include "../Misc/Util.h"

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

class Unison
{
public:
Unison(int update_period_samples_, float max_delay_sec_);
~Unison();

void setSize(int new_size);
void setBaseFrequency(float freq);
void setBandwidth(float bandwidth_cents);

void process(int bufsize, float *inbuf, float *outbuf = NULL);

private:
void updateParameters(void);
void updateUnisonData(void);

int unison_size;
float base_freq;
struct UnisonVoice {
float step; //base LFO
float position;
float realpos1; //the position regarding samples
float realpos2;
float relative_amplitude;
float lin_fpos;
float lin_ffreq;
UnisonVoice() {
position = RND * 1.8f - 0.9f;
realpos1 = 0.0f;
realpos2 = 0.0f;
step = 0.0f;
relative_amplitude = 1.0f;
}
} *uv;

int update_period_samples;
int update_period_sample_k;
int max_delay, delay_k;
bool first_time;
float *delay_buffer;
float unison_amplitude_samples;
float unison_bandwidth_cents;
};
#endif

+ 235
- 0
c++/carla-backend/plugins/zynaddsubfx/Effects/Alienwah.cpp View File

@@ -0,0 +1,235 @@
/*
ZynAddSubFX - a software synthesizer

Alienwah.cpp - "AlienWah" effect
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 <cmath>
#include "Alienwah.h"

Alienwah::Alienwah(bool insertion_, float *efxoutl_, float *efxoutr_)
:Effect(insertion_, efxoutl_, efxoutr_, NULL, 0),
oldl(NULL),
oldr(NULL)
{
setpreset(Ppreset);
cleanup();
oldclfol = complex<float>(fb, 0.0f);
oldclfor = complex<float>(fb, 0.0f);
}

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


//Apply the effect
void Alienwah::out(const Stereo<float *> &smp)
{
float lfol, lfor; //Left/Right LFOs
complex<float> clfol, clfor;
/**\todo Rework, as optimization can be used when the new complex type is
* utilized.
* Before all calculations needed to be done with individual float,
* but now they can be done together*/
lfo.effectlfoout(&lfol, &lfor);
lfol *= depth * PI * 2.0f;
lfor *= depth * PI * 2.0f;
clfol = complex<float>(cosf(lfol + phase) * fb, sinf(lfol + phase) * fb); //rework
clfor = complex<float>(cosf(lfor + phase) * fb, sinf(lfor + phase) * fb); //rework

for(int i = 0; i < synth->buffersize; ++i) {
float x = ((float) i) / synth->buffersize_f;
float x1 = 1.0f - x;
//left
complex<float> tmp = clfol * x + oldclfol * x1;

complex<float> out = tmp * oldl[oldk];
out += (1 - fabs(fb)) * smp.l[i] * pangainL;

oldl[oldk] = out;
float l = out.real() * 10.0f * (fb + 0.1f);

//right
tmp = clfor * x + oldclfor * x1;

out = tmp * oldr[oldk];
out += (1 - fabs(fb)) * smp.r[i] * pangainR;

oldr[oldk] = out;
float r = out.real() * 10.0f * (fb + 0.1f);


if(++oldk >= Pdelay)
oldk = 0;
//LRcross
efxoutl[i] = l * (1.0f - lrcross) + r * lrcross;
efxoutr[i] = r * (1.0f - lrcross) + l * lrcross;
}

oldclfol = clfol;
oldclfor = clfor;
}

//Cleanup the effect
void Alienwah::cleanup(void)
{
for(int i = 0; i < Pdelay; ++i) {
oldl[i] = complex<float>(0.0f, 0.0f);
oldr[i] = complex<float>(0.0f, 0.0f);
}
oldk = 0;
}


//Parameter control
void Alienwah::setdepth(unsigned char _Pdepth)
{
Pdepth = _Pdepth;
depth = Pdepth / 127.0f;
}

void Alienwah::setfb(unsigned char _Pfb)
{
Pfb = _Pfb;
fb = fabs((Pfb - 64.0f) / 64.1f);
fb = sqrtf(fb);
if(fb < 0.4f)
fb = 0.4f;
if(Pfb < 64)
fb = -fb;
}

void Alienwah::setvolume(unsigned char _Pvolume)
{
Pvolume = _Pvolume;
outvolume = Pvolume / 127.0f;
if(insertion == 0)
volume = 1.0f;
else
volume = outvolume;
}

void Alienwah::setphase(unsigned char _Pphase)
{
Pphase = _Pphase;
phase = (Pphase - 64.0f) / 64.0f * PI;
}

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

void Alienwah::setpreset(unsigned char npreset)
{
const int PRESET_SIZE = 11;
const int NUM_PRESETS = 4;
unsigned char presets[NUM_PRESETS][PRESET_SIZE] = {
//AlienWah1
{127, 64, 70, 0, 0, 62, 60, 105, 25, 0, 64},
//AlienWah2
{127, 64, 73, 106, 0, 101, 60, 105, 17, 0, 64},
//AlienWah3
{127, 64, 63, 0, 1, 100, 112, 105, 31, 0, 42},
//AlienWah4
{93, 64, 25, 0, 1, 66, 101, 11, 47, 0, 86}
};

if(npreset >= NUM_PRESETS)
npreset = NUM_PRESETS - 1;
for(int n = 0; n < PRESET_SIZE; ++n)
changepar(n, presets[npreset][n]);
if(insertion == 0)
changepar(0, presets[npreset][0] / 2); //lower the volume if this is system effect
Ppreset = npreset;
}


void Alienwah::changepar(int npar, unsigned char value)
{
switch(npar) {
case 0:
setvolume(value);
break;
case 1:
setpanning(value);
break;
case 2:
lfo.Pfreq = value;
lfo.updateparams();
break;
case 3:
lfo.Prandomness = value;
lfo.updateparams();
break;
case 4:
lfo.PLFOtype = value;
lfo.updateparams();
break;
case 5:
lfo.Pstereo = value;
lfo.updateparams();
break;
case 6:
setdepth(value);
break;
case 7:
setfb(value);
break;
case 8:
setdelay(value);
break;
case 9:
setlrcross(value);
break;
case 10:
setphase(value);
break;
}
}

unsigned char Alienwah::getpar(int npar) const
{
switch(npar) {
case 0: return Pvolume;
case 1: return Ppanning;
case 2: return lfo.Pfreq;
case 3: return lfo.Prandomness;
case 4: return lfo.PLFOtype;
case 5: return lfo.Pstereo;
case 6: return Pdepth;
case 7: return Pfb;
case 8: return Pdelay;
case 9: return Plrcross;
case 10: return Pphase;
default: return 0;
}
}

+ 80
- 0
c++/carla-backend/plugins/zynaddsubfx/Effects/Alienwah.h View File

@@ -0,0 +1,80 @@
/*
ZynAddSubFX - a software synthesizer

Alienwah.h - "AlienWah" effect
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 ALIENWAH_H
#define ALIENWAH_H

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

using namespace std;

#define MAX_ALIENWAH_DELAY 100

/**"AlienWah" Effect*/
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_);
~Alienwah();
void out(const Stereo<float *> &smp);

void setpreset(unsigned char npreset);
void changepar(int npar, unsigned char value);
unsigned char getpar(int npar) const;
void cleanup(void);

private:
//Alienwah Parameters
EffectLFO lfo; //lfo-ul Alienwah
unsigned char Pvolume;
unsigned char Pdepth; //the depth of the Alienwah
unsigned char Pfb; //feedback
unsigned char Pdelay;
unsigned char Pphase;


//Control Parameters
void setvolume(unsigned char _Pvolume);
void setdepth(unsigned char _Pdepth);
void setfb(unsigned char _Pfb);
void setdelay(unsigned char _Pdelay);
void setphase(unsigned char _Pphase);

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

#endif

+ 14
- 0
c++/carla-backend/plugins/zynaddsubfx/Effects/CMakeLists.txt View File

@@ -0,0 +1,14 @@
set(zynaddsubfx_effect_SRCS
Effects/Alienwah.cpp
Effects/Chorus.cpp
Effects/Distorsion.cpp
Effects/DynamicFilter.cpp
Effects/Echo.cpp
Effects/Effect.cpp
Effects/EffectLFO.cpp
Effects/EffectMgr.cpp
Effects/EQ.cpp
Effects/Phaser.cpp
Effects/Reverb.cpp
PARENT_SCOPE
)

+ 268
- 0
c++/carla-backend/plugins/zynaddsubfx/Effects/Chorus.cpp View File

@@ -0,0 +1,268 @@
/*
ZynAddSubFX - a software synthesizer

Chorus.cpp - Chorus and Flange effects
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 <cmath>
#include "Chorus.h"
#include <iostream>

using namespace std;

Chorus::Chorus(bool insertion_, float *const efxoutl_, float *efxoutr_)
:Effect(insertion_, efxoutl_, efxoutr_, NULL, 0),
maxdelay((int)(MAX_CHORUS_DELAY / 1000.0f * synth->samplerate_f)),
delaySample(new float[maxdelay], new float[maxdelay])
{
dlk = 0;
drk = 0;
setpreset(Ppreset);
changepar(1, 64);
lfo.effectlfoout(&lfol, &lfor);
dl2 = getdelay(lfol);
dr2 = getdelay(lfor);
cleanup();
}

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

//get the delay value in samples; xlfo is the current lfo value
float Chorus::getdelay(float xlfo)
{
float result =
(Pflangemode) ? 0 : (delay + xlfo * depth) * synth->samplerate_f;

//check if delay is too big (caused by bad setdelay() and setdepth()
if((result + 0.5f) >= maxdelay) {
cerr
<<
"WARNING: Chorus.cpp::getdelay(..) too big delay (see setdelay and setdepth funcs.)"
<< endl;
result = maxdelay - 1.0f;
}
return result;
}

//Apply the effect
void Chorus::out(const Stereo<float *> &input)
{
const float one = 1.0f;
dl1 = dl2;
dr1 = dr2;
lfo.effectlfoout(&lfol, &lfor);

dl2 = getdelay(lfol);
dr2 = getdelay(lfor);

for(int i = 0; i < synth->buffersize; ++i) {
float inL = input.l[i];
float inR = input.r[i];
//LRcross
Stereo<float> tmpc(inL, inR);
inL = tmpc.l * (1.0f - lrcross) + tmpc.r * lrcross;
inR = tmpc.r * (1.0f - lrcross) + tmpc.l * lrcross;

//Left channel

//compute the delay in samples using linear interpolation between the lfo delays
float mdel =
(dl1 * (synth->buffersize - i) + dl2 * i) / synth->buffersize_f;
if(++dlk >= maxdelay)
dlk = 0;
float tmp = dlk - mdel + maxdelay * 2.0f; //where should I get the sample from

dlhi = (int) tmp;
dlhi %= maxdelay;

float dlhi2 = (dlhi - 1 + maxdelay) % maxdelay;
float dllo = 1.0f - fmod(tmp, one);
efxoutl[i] = cinterpolate(delaySample.l, maxdelay, dlhi2) * dllo
+ cinterpolate(delaySample.l, maxdelay,
dlhi) * (1.0f - dllo);
delaySample.l[dlk] = inL + efxoutl[i] * fb;

//Right channel

//compute the delay in samples using linear interpolation between the lfo delays
mdel = (dr1 * (synth->buffersize - i) + dr2 * i) / synth->buffersize_f;
if(++drk >= maxdelay)
drk = 0;
tmp = drk * 1.0f - mdel + maxdelay * 2.0f; //where should I get the sample from

dlhi = (int) tmp;
dlhi %= maxdelay;

dlhi2 = (dlhi - 1 + maxdelay) % maxdelay;
dllo = 1.0f - fmodf(tmp, one);
efxoutr[i] = cinterpolate(delaySample.r, maxdelay, dlhi2) * dllo
+ cinterpolate(delaySample.r, maxdelay,
dlhi) * (1.0f - dllo);
delaySample.r[dlk] = inR + efxoutr[i] * fb;
}

if(Poutsub)
for(int i = 0; i < synth->buffersize; ++i) {
efxoutl[i] *= -1.0f;
efxoutr[i] *= -1.0f;
}

for(int i = 0; i < synth->buffersize; ++i) {
efxoutl[i] *= pangainL;
efxoutr[i] *= pangainR;
}
}

//Cleanup the effect
void Chorus::cleanup(void)
{
memset(delaySample.l, 0, maxdelay * sizeof(float));
memset(delaySample.r, 0, maxdelay * sizeof(float));
}

//Parameter control
void Chorus::setdepth(unsigned char _Pdepth)
{
Pdepth = _Pdepth;
depth = (powf(8.0f, (Pdepth / 127.0f) * 2.0f) - 1.0f) / 1000.0f; //seconds
}

void Chorus::setdelay(unsigned char _Pdelay)
{
Pdelay = _Pdelay;
delay = (powf(10.0f, (Pdelay / 127.0f) * 2.0f) - 1.0f) / 1000.0f; //seconds
}

void Chorus::setfb(unsigned char _Pfb)
{
Pfb = _Pfb;
fb = (Pfb - 64.0f) / 64.1f;
}

void Chorus::setvolume(unsigned char _Pvolume)
{
Pvolume = _Pvolume;
outvolume = Pvolume / 127.0f;
volume = (!insertion) ? 1.0f : outvolume;
}


void Chorus::setpreset(unsigned char npreset)
{
const int PRESET_SIZE = 12;
const int NUM_PRESETS = 10;
unsigned char presets[NUM_PRESETS][PRESET_SIZE] = {
//Chorus1
{64, 64, 50, 0, 0, 90, 40, 85, 64, 119, 0, 0},
//Chorus2
{64, 64, 45, 0, 0, 98, 56, 90, 64, 19, 0, 0},
//Chorus3
{64, 64, 29, 0, 1, 42, 97, 95, 90, 127, 0, 0},
//Celeste1
{64, 64, 26, 0, 0, 42, 115, 18, 90, 127, 0, 0},
//Celeste2
{64, 64, 29, 117, 0, 50, 115, 9, 31, 127, 0, 1},
//Flange1
{64, 64, 57, 0, 0, 60, 23, 3, 62, 0, 0, 0},
//Flange2
{64, 64, 33, 34, 1, 40, 35, 3, 109, 0, 0, 0},
//Flange3
{64, 64, 53, 34, 1, 94, 35, 3, 54, 0, 0, 1},
//Flange4
{64, 64, 40, 0, 1, 62, 12, 19, 97, 0, 0, 0},
//Flange5
{64, 64, 55, 105, 0, 24, 39, 19, 17, 0, 0, 1}
};

if(npreset >= NUM_PRESETS)
npreset = NUM_PRESETS - 1;
for(int n = 0; n < PRESET_SIZE; ++n)
changepar(n, presets[npreset][n]);
Ppreset = npreset;
}


void Chorus::changepar(int npar, unsigned char value)
{
switch(npar) {
case 0:
setvolume(value);
break;
case 1:
setpanning(value);
break;
case 2:
lfo.Pfreq = value;
lfo.updateparams();
break;
case 3:
lfo.Prandomness = value;
lfo.updateparams();
break;
case 4:
lfo.PLFOtype = value;
lfo.updateparams();
break;
case 5:
lfo.Pstereo = value;
lfo.updateparams();
break;
case 6:
setdepth(value);
break;
case 7:
setdelay(value);
break;
case 8:
setfb(value);
break;
case 9:
setlrcross(value);
break;
case 10:
Pflangemode = (value > 1) ? 1 : value;
break;
case 11:
Poutsub = (value > 1) ? 1 : value;
break;
}
}

unsigned char Chorus::getpar(int npar) const
{
switch(npar) {
case 0: return Pvolume;
case 1: return Ppanning;
case 2: return lfo.Pfreq;
case 3: return lfo.Prandomness;
case 4: return lfo.PLFOtype;
case 5: return lfo.Pstereo;
case 6: return Pdepth;
case 7: return Pdelay;
case 8: return Pfb;
case 9: return Plrcross;
case 10: return Pflangemode;
case 11: return Poutsub;
default: return 0;
}
}

+ 106
- 0
c++/carla-backend/plugins/zynaddsubfx/Effects/Chorus.h View File

@@ -0,0 +1,106 @@
/*
ZynAddSubFX - a software synthesizer

Chorus.h - Chorus and Flange effects
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 CHORUS_H
#define CHORUS_H
#include "Effect.h"
#include "EffectLFO.h"
#include "../Misc/Stereo.h"

#define MAX_CHORUS_DELAY 250.0f //ms

/**Chorus and Flange effects*/
class Chorus:public Effect
{
public:
Chorus(bool insertion_, float *efxoutl_, float *efxoutr_);
/**Destructor*/
~Chorus();
void out(const Stereo<float *> &input);
void setpreset(unsigned char npreset);
/**
* Sets the value of the chosen variable
*
* The possible parameters are:
* -# Volume
* -# Panning
* -# LFO Frequency
* -# LFO Randomness
* -# LFO Type
* -# LFO stereo
* -# Depth
* -# Delay
* -# Feedback
* -# Flange Mode
* -# Subtractive
* @param npar number of chosen parameter
* @param value the new value
*/
void changepar(int npar, unsigned char value);
/**
* Gets the value of the chosen variable
*
* The possible parameters are:
* -# Volume
* -# Panning
* -# LFO Frequency
* -# LFO Randomness
* -# LFO Type
* -# LFO stereo
* -# Depth
* -# Delay
* -# Feedback
* -# Flange Mode
* -# Subtractive
* @param npar number of chosen parameter
* @return the value of the parameter
*/
unsigned char getpar(int npar) const;
void cleanup(void);

private:
//Chorus Parameters
unsigned char Pvolume;
unsigned char Pdepth; //the depth of the Chorus(ms)
unsigned char Pdelay; //the delay (ms)
unsigned char Pfb; //feedback
unsigned char Pflangemode; //how the LFO is scaled, to result chorus or flange
unsigned char Poutsub; //if I wish to substract the output instead of the adding it
EffectLFO lfo; //lfo-ul chorus


//Parameter Controls
void setvolume(unsigned char _Pvolume);
void setdepth(unsigned char _Pdepth);
void setdelay(unsigned char _Pdelay);
void setfb(unsigned char _Pfb);

//Internal Values
float depth, delay, fb;
float dl1, dl2, dr1, dr2, lfol, lfor;
int maxdelay;
Stereo<float *> delaySample;
int dlk, drk, dlhi;
float getdelay(float xlfo);
};

#endif

+ 245
- 0
c++/carla-backend/plugins/zynaddsubfx/Effects/Distorsion.cpp View File

@@ -0,0 +1,245 @@
/*
ZynAddSubFX - a software synthesizer

Distorsion.cpp - Distorsion effect
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 "Distorsion.h"
#include "../DSP/AnalogFilter.h"
#include "../Misc/WaveShapeSmps.h"
#include <cmath>

Distorsion::Distorsion(bool insertion_, float *efxoutl_, float *efxoutr_)
:Effect(insertion_, efxoutl_, efxoutr_, NULL, 0),
Pvolume(50),
Pdrive(90),
Plevel(64),
Ptype(0),
Pnegate(0),
Plpf(127),
Phpf(0),
Pstereo(0),
Pprefiltering(0)
{
lpfl = new AnalogFilter(2, 22000, 1, 0);
lpfr = new AnalogFilter(2, 22000, 1, 0);
hpfl = new AnalogFilter(3, 20, 1, 0);
hpfr = new AnalogFilter(3, 20, 1, 0);
setpreset(Ppreset);
cleanup();
}

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

//Cleanup the effect
void Distorsion::cleanup(void)
{
lpfl->cleanup();
hpfl->cleanup();
lpfr->cleanup();
hpfr->cleanup();
}


//Apply the filters
void Distorsion::applyfilters(float *efxoutl, float *efxoutr)
{
lpfl->filterout(efxoutl);
hpfl->filterout(efxoutl);
if(Pstereo != 0) { //stereo
lpfr->filterout(efxoutr);
hpfr->filterout(efxoutr);
}
}


//Effect output
void Distorsion::out(const Stereo<float *> &smp)
{
float inputvol = powf(5.0f, (Pdrive - 32.0f) / 127.0f);
if(Pnegate)
inputvol *= -1.0f;

if(Pstereo) //Stereo
for(int i = 0; i < synth->buffersize; ++i) {
efxoutl[i] = smp.l[i] * inputvol * pangainL;
efxoutr[i] = smp.r[i] * inputvol * pangainR;
}
else //Mono
for(int i = 0; i < synth->buffersize; ++i)
efxoutl[i] = (smp.l[i] * pangainL + smp.r[i] * pangainR) * inputvol;

if(Pprefiltering)
applyfilters(efxoutl, efxoutr);

waveShapeSmps(synth->buffersize, efxoutl, Ptype + 1, Pdrive);
if(Pstereo)
waveShapeSmps(synth->buffersize, efxoutr, Ptype + 1, Pdrive);

if(!Pprefiltering)
applyfilters(efxoutl, efxoutr);

if(!Pstereo)
memcpy(efxoutr, efxoutl, synth->bufferbytes);

float level = dB2rap(60.0f * Plevel / 127.0f - 40.0f);
for(int i = 0; i < synth->buffersize; ++i) {
float lout = efxoutl[i];
float rout = efxoutr[i];
float l = lout * (1.0f - lrcross) + rout * lrcross;
float r = rout * (1.0f - lrcross) + lout * lrcross;
lout = l;
rout = r;

efxoutl[i] = lout * 2.0f * level;
efxoutr[i] = rout * 2.0f * level;
}
}


//Parameter control
void Distorsion::setvolume(unsigned char _Pvolume)
{
Pvolume = _Pvolume;

if(insertion == 0) {
outvolume = powf(0.01f, (1.0f - Pvolume / 127.0f)) * 4.0f;
volume = 1.0f;
}
else
volume = outvolume = Pvolume / 127.0f;
if(Pvolume == 0)
cleanup();
}

void Distorsion::setlpf(unsigned char _Plpf)
{
Plpf = _Plpf;
float fr = expf(powf(Plpf / 127.0f, 0.5f) * logf(25000.0f)) + 40.0f;
lpfl->setfreq(fr);
lpfr->setfreq(fr);
}

void Distorsion::sethpf(unsigned char _Phpf)
{
Phpf = _Phpf;
float fr = expf(powf(Phpf / 127.0f, 0.5f) * logf(25000.0f)) + 20.0f;
hpfl->setfreq(fr);
hpfr->setfreq(fr);
}


void Distorsion::setpreset(unsigned char npreset)
{
const int PRESET_SIZE = 11;
const int NUM_PRESETS = 6;
unsigned char presets[NUM_PRESETS][PRESET_SIZE] = {
//Overdrive 1
{127, 64, 35, 56, 70, 0, 0, 96, 0, 0, 0},
//Overdrive 2
{127, 64, 35, 29, 75, 1, 0, 127, 0, 0, 0},
//A. Exciter 1
{64, 64, 35, 75, 80, 5, 0, 127, 105, 1, 0},
//A. Exciter 2
{64, 64, 35, 85, 62, 1, 0, 127, 118, 1, 0},
//Guitar Amp
{127, 64, 35, 63, 75, 2, 0, 55, 0, 0, 0},
//Quantisize
{127, 64, 35, 88, 75, 4, 0, 127, 0, 1, 0}
};

if(npreset >= NUM_PRESETS)
npreset = NUM_PRESETS - 1;
for(int n = 0; n < PRESET_SIZE; ++n)
changepar(n, presets[npreset][n]);
if(!insertion) //lower the volume if this is system effect
changepar(0, (int) (presets[npreset][0] / 1.5f));
Ppreset = npreset;
cleanup();
}


void Distorsion::changepar(int npar, unsigned char value)
{
switch(npar) {
case 0:
setvolume(value);
break;
case 1:
setpanning(value);
break;
case 2:
setlrcross(value);
break;
case 3:
Pdrive = value;
break;
case 4:
Plevel = value;
break;
case 5:
if(value > 13)
Ptype = 13; //this must be increased if more distorsion types are added
else
Ptype = value;
break;
case 6:
if(value > 1)
Pnegate = 1;
else
Pnegate = value;
break;
case 7:
setlpf(value);
break;
case 8:
sethpf(value);
break;
case 9:
Pstereo = (value > 1) ? 1 : value;
break;
case 10:
Pprefiltering = value;
break;
}
}

unsigned char Distorsion::getpar(int npar) const
{
switch(npar) {
case 0: return Pvolume;
case 1: return Ppanning;
case 2: return Plrcross;
case 3: return Pdrive;
case 4: return Plevel;
case 5: return Ptype;
case 6: return Pnegate;
case 7: return Plpf;
case 8: return Phpf;
case 9: return Pstereo;
case 10: return Pprefiltering;
default: return 0; //in case of bogus parameter number
}
}

+ 61
- 0
c++/carla-backend/plugins/zynaddsubfx/Effects/Distorsion.h View File

@@ -0,0 +1,61 @@
/*
ZynAddSubFX - a software synthesizer

Distorsion.h - Distorsion Effect
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 DISTORSION_H
#define DISTORSION_H

#include "Effect.h"

/**Distortion Effect*/
class Distorsion:public Effect
{
public:
Distorsion(bool insertion, float *efxoutl_, float *efxoutr_);
~Distorsion();
void out(const Stereo<float *> &smp);
void setpreset(unsigned char npreset);
void changepar(int npar, unsigned char value);
unsigned char getpar(int npar) const;
void cleanup(void);
void applyfilters(float *efxoutl, float *efxoutr);

private:
//Parameters
unsigned char Pvolume; //Volume or E/R
unsigned char Pdrive; //the input amplification
unsigned char Plevel; //the output amplification
unsigned char Ptype; //Distorsion type
unsigned char Pnegate; //if the input is negated
unsigned char Plpf; //lowpass filter
unsigned char Phpf; //highpass filter
unsigned char Pstereo; //0=mono, 1=stereo
unsigned char Pprefiltering; //if you want to do the filtering before the distorsion

void setvolume(unsigned char _Pvolume);
void setlpf(unsigned char _Plpf);
void sethpf(unsigned char _Phpf);

//Real Parameters
class AnalogFilter * lpfl, *lpfr, *hpfl, *hpfr;
};

#endif

+ 311
- 0
c++/carla-backend/plugins/zynaddsubfx/Effects/DynamicFilter.cpp View File

@@ -0,0 +1,311 @@
/*
ZynAddSubFX - a software synthesizer

DynamicFilter.cpp - "WahWah" effect and others
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 <cmath>
#include "DynamicFilter.h"
#include "../DSP/Filter.h"

DynamicFilter::DynamicFilter(bool insertion_, float *efxoutl_, float *efxoutr_)
:Effect(insertion_, efxoutl_, efxoutr_, new FilterParams(0, 64, 64), 0),
Pvolume(110),
Pdepth(0),
Pampsns(90),
Pampsnsinv(0),
Pampsmooth(60),
filterl(NULL),
filterr(NULL)
{
setpreset(Ppreset);
cleanup();
}

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


// Apply the effect
void DynamicFilter::out(const Stereo<float *> &smp)
{
if(filterpars->changed) {
filterpars->changed = false;
cleanup();
}

float lfol, lfor;
lfo.effectlfoout(&lfol, &lfor);
lfol *= depth * 5.0f;
lfor *= depth * 5.0f;
const float freq = filterpars->getfreq();
const float q = filterpars->getq();

for(int i = 0; i < synth->buffersize; ++i) {
efxoutl[i] = smp.l[i];
efxoutr[i] = smp.r[i];

const float x = (fabsf(smp.l[i]) + fabsf(smp.l[i])) * 0.5f;
ms1 = ms1 * (1.0f - ampsmooth) + x * ampsmooth + 1e-10;
}

const float ampsmooth2 = powf(ampsmooth, 0.2f) * 0.3f;
ms2 = ms2 * (1.0f - ampsmooth2) + ms1 * ampsmooth2;
ms3 = ms3 * (1.0f - ampsmooth2) + ms2 * ampsmooth2;
ms4 = ms4 * (1.0f - ampsmooth2) + ms3 * ampsmooth2;
const float rms = (sqrtf(ms4)) * ampsns;

const float frl = Filter::getrealfreq(freq + lfol + rms);
const float frr = Filter::getrealfreq(freq + lfor + rms);

filterl->setfreq_and_q(frl, q);
filterr->setfreq_and_q(frr, q);

filterl->filterout(efxoutl);
filterr->filterout(efxoutr);

//panning
for(int i = 0; i < synth->buffersize; ++i) {
efxoutl[i] *= pangainL;
efxoutr[i] *= pangainR;
}
}

// Cleanup the effect
void DynamicFilter::cleanup(void)
{
reinitfilter();
ms1 = ms2 = ms3 = ms4 = 0.0f;
}


//Parameter control
void DynamicFilter::setdepth(unsigned char _Pdepth)
{
Pdepth = _Pdepth;
depth = powf(Pdepth / 127.0f, 2.0f);
}


void DynamicFilter::setvolume(unsigned char _Pvolume)
{
Pvolume = _Pvolume;
outvolume = Pvolume / 127.0f;
if(!insertion)
volume = 1.0f;
else
volume = outvolume;
}

void DynamicFilter::setampsns(unsigned char _Pampsns)
{
Pampsns = _Pampsns;
ampsns = powf(Pampsns / 127.0f, 2.5f) * 10.0f;
if(Pampsnsinv)
ampsns = -ampsns;
ampsmooth = expf(-Pampsmooth / 127.0f * 10.0f) * 0.99f;
}

void DynamicFilter::reinitfilter(void)
{
delete filterl;
delete filterr;
filterl = Filter::generate(filterpars);
filterr = Filter::generate(filterpars);
}

void DynamicFilter::setpreset(unsigned char npreset)
{
const int PRESET_SIZE = 10;
const int NUM_PRESETS = 5;
unsigned char presets[NUM_PRESETS][PRESET_SIZE] = {
//WahWah
{110, 64, 80, 0, 0, 64, 0, 90, 0, 60},
//AutoWah
{110, 64, 70, 0, 0, 80, 70, 0, 0, 60},
//Sweep
{100, 64, 30, 0, 0, 50, 80, 0, 0, 60},
//VocalMorph1
{110, 64, 80, 0, 0, 64, 0, 64, 0, 60},
//VocalMorph1
{127, 64, 50, 0, 0, 96, 64, 0, 0, 60}
};

if(npreset >= NUM_PRESETS)
npreset = NUM_PRESETS - 1;
for(int n = 0; n < PRESET_SIZE; ++n)
changepar(n, presets[npreset][n]);

filterpars->defaults();

switch(npreset) {
case 0:
filterpars->Pcategory = 0;
filterpars->Ptype = 2;
filterpars->Pfreq = 45;
filterpars->Pq = 64;
filterpars->Pstages = 1;
filterpars->Pgain = 64;
break;
case 1:
filterpars->Pcategory = 2;
filterpars->Ptype = 0;
filterpars->Pfreq = 72;
filterpars->Pq = 64;
filterpars->Pstages = 0;
filterpars->Pgain = 64;
break;
case 2:
filterpars->Pcategory = 0;
filterpars->Ptype = 4;
filterpars->Pfreq = 64;
filterpars->Pq = 64;
filterpars->Pstages = 2;
filterpars->Pgain = 64;
break;
case 3:
filterpars->Pcategory = 1;
filterpars->Ptype = 0;
filterpars->Pfreq = 50;
filterpars->Pq = 70;
filterpars->Pstages = 1;
filterpars->Pgain = 64;

filterpars->Psequencesize = 2;
// "I"
filterpars->Pvowels[0].formants[0].freq = 34;
filterpars->Pvowels[0].formants[0].amp = 127;
filterpars->Pvowels[0].formants[0].q = 64;
filterpars->Pvowels[0].formants[1].freq = 99;
filterpars->Pvowels[0].formants[1].amp = 122;
filterpars->Pvowels[0].formants[1].q = 64;
filterpars->Pvowels[0].formants[2].freq = 108;
filterpars->Pvowels[0].formants[2].amp = 112;
filterpars->Pvowels[0].formants[2].q = 64;
// "A"
filterpars->Pvowels[1].formants[0].freq = 61;
filterpars->Pvowels[1].formants[0].amp = 127;
filterpars->Pvowels[1].formants[0].q = 64;
filterpars->Pvowels[1].formants[1].freq = 71;
filterpars->Pvowels[1].formants[1].amp = 121;
filterpars->Pvowels[1].formants[1].q = 64;
filterpars->Pvowels[1].formants[2].freq = 99;
filterpars->Pvowels[1].formants[2].amp = 117;
filterpars->Pvowels[1].formants[2].q = 64;
break;
case 4:
filterpars->Pcategory = 1;
filterpars->Ptype = 0;
filterpars->Pfreq = 64;
filterpars->Pq = 70;
filterpars->Pstages = 1;
filterpars->Pgain = 64;

filterpars->Psequencesize = 2;
filterpars->Pnumformants = 2;
filterpars->Pvowelclearness = 0;

filterpars->Pvowels[0].formants[0].freq = 70;
filterpars->Pvowels[0].formants[0].amp = 127;
filterpars->Pvowels[0].formants[0].q = 64;
filterpars->Pvowels[0].formants[1].freq = 80;
filterpars->Pvowels[0].formants[1].amp = 122;
filterpars->Pvowels[0].formants[1].q = 64;

filterpars->Pvowels[1].formants[0].freq = 20;
filterpars->Pvowels[1].formants[0].amp = 127;
filterpars->Pvowels[1].formants[0].q = 64;
filterpars->Pvowels[1].formants[1].freq = 100;
filterpars->Pvowels[1].formants[1].amp = 121;
filterpars->Pvowels[1].formants[1].q = 64;
break;
}

// for (int i=0;i<5;i++){
// printf("freq=%d amp=%d q=%d\n",filterpars->Pvowels[0].formants[i].freq,filterpars->Pvowels[0].formants[i].amp,filterpars->Pvowels[0].formants[i].q);
// };
if(insertion == 0) //lower the volume if this is system effect
changepar(0, presets[npreset][0] * 0.5f);
Ppreset = npreset;
reinitfilter();
}


void DynamicFilter::changepar(int npar, unsigned char value)
{
switch(npar) {
case 0:
setvolume(value);
break;
case 1:
setpanning(value);
break;
case 2:
lfo.Pfreq = value;
lfo.updateparams();
break;
case 3:
lfo.Prandomness = value;
lfo.updateparams();
break;
case 4:
lfo.PLFOtype = value;
lfo.updateparams();
break;
case 5:
lfo.Pstereo = value;
lfo.updateparams();
break;
case 6:
setdepth(value);
break;
case 7:
setampsns(value);
break;
case 8:
Pampsnsinv = value;
setampsns(Pampsns);
break;
case 9:
Pampsmooth = value;
setampsns(Pampsns);
break;
}
}

unsigned char DynamicFilter::getpar(int npar) const
{
switch(npar) {
case 0: return Pvolume;
case 1: return Ppanning;
case 2: return lfo.Pfreq;
case 3: return lfo.Prandomness;
case 4: return lfo.PLFOtype;
case 5: return lfo.Pstereo;
case 6: return Pdepth;
case 7: return Pampsns;
case 8: return Pampsnsinv;
case 9: return Pampsmooth;
default: return 0;
}
}

+ 65
- 0
c++/carla-backend/plugins/zynaddsubfx/Effects/DynamicFilter.h View File

@@ -0,0 +1,65 @@
/*
ZynAddSubFX - a software synthesizer

DynamicFilter.h - "WahWah" effect and others
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 DYNAMICFILTER_H
#define DYNAMICFILTER_H

#include "Effect.h"
#include "EffectLFO.h"

/**DynamicFilter Effect*/
class DynamicFilter:public Effect
{
public:
DynamicFilter(bool insertion_, float *efxoutl_, float *efxoutr_);
~DynamicFilter();
void out(const Stereo<float *> &smp);

void setpreset(unsigned char npreset);
void changepar(int npar, unsigned char value);
unsigned char getpar(int npar) const;
void cleanup(void);

private:
//Parametrii DynamicFilter
EffectLFO lfo; //lfo-ul DynamicFilter
unsigned char Pvolume; //Volume
unsigned char Pdepth; //the depth of the lfo
unsigned char Pampsns; //how the filter varies according to the input amplitude
unsigned char Pampsnsinv; //if the filter freq is lowered if the input amplitude rises
unsigned char Pampsmooth; //how smooth the input amplitude changes the filter

//Parameter Control
void setvolume(unsigned char _Pvolume);
void setdepth(unsigned char _Pdepth);
void setampsns(unsigned char _Pampsns);

void reinitfilter(void);

//Internal Values
float depth, ampsns, ampsmooth;

class Filter * filterl, *filterr;
float ms1, ms2, ms3, ms4; //mean squares
};

#endif

+ 198
- 0
c++/carla-backend/plugins/zynaddsubfx/Effects/EQ.cpp View File

@@ -0,0 +1,198 @@
/*
ZynAddSubFX - a software synthesizer

EQ.cpp - EQ effect
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 <cmath>
#include "EQ.h"
#include "../DSP/AnalogFilter.h"

EQ::EQ(bool insertion_, float *efxoutl_, float *efxoutr_)
:Effect(insertion_, efxoutl_, efxoutr_, NULL, 0)
{
for(int i = 0; i < MAX_EQ_BANDS; ++i) {
filter[i].Ptype = 0;
filter[i].Pfreq = 64;
filter[i].Pgain = 64;
filter[i].Pq = 64;
filter[i].Pstages = 0;
filter[i].l = new AnalogFilter(6, 1000.0f, 1.0f, 0);
filter[i].r = new AnalogFilter(6, 1000.0f, 1.0f, 0);
}
//default values
Pvolume = 50;

setpreset(Ppreset);
cleanup();
}


// Cleanup the effect
void EQ::cleanup(void)
{
for(int i = 0; i < MAX_EQ_BANDS; ++i) {
filter[i].l->cleanup();
filter[i].r->cleanup();
}
}

//Effect output
void EQ::out(const Stereo<float *> &smp)
{
for(int i = 0; i < synth->buffersize; ++i) {
efxoutl[i] = smp.l[i] * volume;
efxoutr[i] = smp.r[i] * volume;
}

for(int i = 0; i < MAX_EQ_BANDS; ++i) {
if(filter[i].Ptype == 0)
continue;
filter[i].l->filterout(efxoutl);
filter[i].r->filterout(efxoutr);
}
}


//Parameter control
void EQ::setvolume(unsigned char _Pvolume)
{
Pvolume = _Pvolume;
outvolume = powf(0.005f, (1.0f - Pvolume / 127.0f)) * 10.0f;
volume = (!insertion) ? 1.0f : outvolume;
}


void EQ::setpreset(unsigned char npreset)
{
const int PRESET_SIZE = 1;
const int NUM_PRESETS = 2;
unsigned char presets[NUM_PRESETS][PRESET_SIZE] = {
{67}, //EQ 1
{67} //EQ 2
};

if(npreset >= NUM_PRESETS)
npreset = NUM_PRESETS - 1;
for(int n = 0; n < PRESET_SIZE; ++n)
changepar(n, presets[npreset][n]);
Ppreset = npreset;
}


void EQ::changepar(int npar, unsigned char value)
{
switch(npar) {
case 0:
setvolume(value);
break;
}
if(npar < 10)
return;

int nb = (npar - 10) / 5; //number of the band (filter)
if(nb >= MAX_EQ_BANDS)
return;
int bp = npar % 5; //band paramenter

float tmp;
switch(bp) {
case 0:
filter[nb].Ptype = value;
if(value > 9)
filter[nb].Ptype = 0; //has to be changed if more filters will be added
if(filter[nb].Ptype != 0) {
filter[nb].l->settype(value - 1);
filter[nb].r->settype(value - 1);
}
break;
case 1:
filter[nb].Pfreq = value;
tmp = 600.0f * powf(30.0f, (value - 64.0f) / 64.0f);
filter[nb].l->setfreq(tmp);
filter[nb].r->setfreq(tmp);
break;
case 2:
filter[nb].Pgain = value;
tmp = 30.0f * (value - 64.0f) / 64.0f;
filter[nb].l->setgain(tmp);
filter[nb].r->setgain(tmp);
break;
case 3:
filter[nb].Pq = value;
tmp = powf(30.0f, (value - 64.0f) / 64.0f);
filter[nb].l->setq(tmp);
filter[nb].r->setq(tmp);
break;
case 4:
filter[nb].Pstages = value;
if(value >= MAX_FILTER_STAGES)
filter[nb].Pstages = MAX_FILTER_STAGES - 1;
filter[nb].l->setstages(value);
filter[nb].r->setstages(value);
break;
}
}

unsigned char EQ::getpar(int npar) const
{
switch(npar) {
case 0:
return Pvolume;
break;
}

if(npar < 10)
return 0;

int nb = (npar - 10) / 5; //number of the band (filter)
if(nb >= MAX_EQ_BANDS)
return 0;
int bp = npar % 5; //band paramenter
switch(bp) {
case 0:
return filter[nb].Ptype;
break;
case 1:
return filter[nb].Pfreq;
break;
case 2:
return filter[nb].Pgain;
break;
case 3:
return filter[nb].Pq;
break;
case 4:
return filter[nb].Pstages;
break;
default: return 0; //in case of bogus parameter number
}
}


float EQ::getfreqresponse(float freq)
{
float resp = 1.0f;
for(int i = 0; i < MAX_EQ_BANDS; ++i) {
if(filter[i].Ptype == 0)
continue;
resp *= filter[i].l->H(freq);
}
return rap2dB(resp * outvolume);
}

+ 55
- 0
c++/carla-backend/plugins/zynaddsubfx/Effects/EQ.h View File

@@ -0,0 +1,55 @@
/*
ZynAddSubFX - a software synthesizer

EQ.h - EQ Effect
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 EQ_H
#define EQ_H

#include "Effect.h"

/**EQ Effect*/
class EQ:public Effect
{
public:
EQ(bool insertion_, float *efxoutl_, float *efxoutr_);
~EQ() {}
void out(const Stereo<float *> &smp);
void setpreset(unsigned char npreset);
void changepar(int npar, unsigned char value);
unsigned char getpar(int npar) const;
void cleanup(void);
float getfreqresponse(float freq);

private:
//Parameters
unsigned char Pvolume;

void setvolume(unsigned char _Pvolume);

struct {
//parameters
unsigned char Ptype, Pfreq, Pgain, Pq, Pstages;
//internal values
class AnalogFilter * l, *r;
} filter[MAX_EQ_BANDS];
};

#endif

+ 231
- 0
c++/carla-backend/plugins/zynaddsubfx/Effects/Echo.cpp View File

@@ -0,0 +1,231 @@
/*
ZynAddSubFX - a software synthesizer

Echo.cpp - Echo effect
Copyright (C) 2002-2005 Nasca Octavian Paul
Copyright (C) 2009-2010 Mark McCurry
Author: Nasca Octavian Paul
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 <cmath>
#include "Echo.h"

#define MAX_DELAY 2

Echo::Echo(bool insertion_, float *efxoutl_, float *efxoutr_)
:Effect(insertion_, efxoutl_, efxoutr_, NULL, 0),
Pvolume(50),
Pdelay(60),
Plrdelay(100),
Pfb(40),
Phidamp(60),
delayTime(1),
lrdelay(0),
avgDelay(0),
delay(new float[(int)(MAX_DELAY * synth->samplerate)],
new float[(int)(MAX_DELAY * synth->samplerate)]),
old(0.0f),
pos(0),
delta(1),
ndelta(1)
{
initdelays();
setpreset(Ppreset);
}

Echo::~Echo()
{
delete[] delay.l;
delete[] delay.r;
}

//Cleanup the effect
void Echo::cleanup(void)
{
memset(delay.l, 0, MAX_DELAY * synth->samplerate * sizeof(float));
memset(delay.r, 0, MAX_DELAY * synth->samplerate * sizeof(float));
old = Stereo<float>(0.0f);
}

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

//Initialize the delays
void Echo::initdelays(void)
{
cleanup();
//number of seconds to delay left chan
float dl = avgDelay - lrdelay;

//number of seconds to delay right chan
float dr = avgDelay + lrdelay;

ndelta.l = max(1, (int) (dl * synth->samplerate));
ndelta.r = max(1, (int) (dr * synth->samplerate));
}

//Effect output
void Echo::out(const Stereo<float *> &input)
{
for(int i = 0; i < synth->buffersize; ++i) {
float ldl = delay.l[pos.l];
float rdl = delay.r[pos.r];
ldl = ldl * (1.0f - lrcross) + rdl * lrcross;
rdl = rdl * (1.0f - lrcross) + ldl * lrcross;

efxoutl[i] = ldl * 2.0f;
efxoutr[i] = rdl * 2.0f;

ldl = input.l[i] * pangainL - ldl * fb;
rdl = input.r[i] * pangainR - rdl * fb;

//LowPass Filter
old.l = delay.l[(pos.l + delta.l) % (MAX_DELAY * synth->samplerate)] =
ldl * hidamp + old.l * (1.0f - hidamp);
old.r = delay.r[(pos.r + delta.r) % (MAX_DELAY * synth->samplerate)] =
rdl * hidamp + old.r * (1.0f - hidamp);

//increment
++pos.l; // += delta.l;
++pos.r; // += delta.r;

//ensure that pos is still in bounds
pos.l %= MAX_DELAY * synth->samplerate;
pos.r %= MAX_DELAY * synth->samplerate;

//adjust delay if needed
delta.l = (15 * delta.l + ndelta.l) / 16;
delta.r = (15 * delta.r + ndelta.r) / 16;
}
}


//Parameter control
void Echo::setvolume(unsigned char _Pvolume)
{
Pvolume = _Pvolume;

if(insertion == 0) {
outvolume = powf(0.01f, (1.0f - Pvolume / 127.0f)) * 4.0f;
volume = 1.0f;
}
else
volume = outvolume = Pvolume / 127.0f;
if(Pvolume == 0)
cleanup();
}

void Echo::setdelay(unsigned char _Pdelay)
{
Pdelay = _Pdelay;
avgDelay = (Pdelay / 127.0f * 1.5f); //0 .. 1.5 sec
initdelays();
}

void Echo::setlrdelay(unsigned char _Plrdelay)
{
float tmp;
Plrdelay = _Plrdelay;
tmp =
(powf(2.0f, fabsf(Plrdelay - 64.0f) / 64.0f * 9.0f) - 1.0f) / 1000.0f;
if(Plrdelay < 64.0f)
tmp = -tmp;
lrdelay = tmp;
initdelays();
}

void Echo::setfb(unsigned char _Pfb)
{
Pfb = _Pfb;
fb = Pfb / 128.0f;
}

void Echo::sethidamp(unsigned char _Phidamp)
{
Phidamp = _Phidamp;
hidamp = 1.0f - Phidamp / 127.0f;
}

void Echo::setpreset(unsigned char npreset)
{
const int PRESET_SIZE = 7;
const int NUM_PRESETS = 9;
unsigned char presets[NUM_PRESETS][PRESET_SIZE] = {
{67, 64, 35, 64, 30, 59, 0 }, //Echo 1
{67, 64, 21, 64, 30, 59, 0 }, //Echo 2
{67, 75, 60, 64, 30, 59, 10}, //Echo 3
{67, 60, 44, 64, 30, 0, 0 }, //Simple Echo
{67, 60, 102, 50, 30, 82, 48}, //Canyon
{67, 64, 44, 17, 0, 82, 24}, //Panning Echo 1
{81, 60, 46, 118, 100, 68, 18}, //Panning Echo 2
{81, 60, 26, 100, 127, 67, 36}, //Panning Echo 3
{62, 64, 28, 64, 100, 90, 55} //Feedback Echo
};

if(npreset >= NUM_PRESETS)
npreset = NUM_PRESETS - 1;
for(int n = 0; n < PRESET_SIZE; ++n)
changepar(n, presets[npreset][n]);
if(insertion)
setvolume(presets[npreset][0] / 2); //lower the volume if this is insertion effect
Ppreset = npreset;
}


void Echo::changepar(int npar, unsigned char value)
{
switch(npar) {
case 0:
setvolume(value);
break;
case 1:
setpanning(value);
break;
case 2:
setdelay(value);
break;
case 3:
setlrdelay(value);
break;
case 4:
setlrcross(value);
break;
case 5:
setfb(value);
break;
case 6:
sethidamp(value);
break;
}
}

unsigned char Echo::getpar(int npar) const
{
switch(npar) {
case 0: return Pvolume;
case 1: return Ppanning;
case 2: return Pdelay;
case 3: return Plrdelay;
case 4: return Plrcross;
case 5: return Pfb;
case 6: return Phidamp;
default: return 0; // in case of bogus parameter number
}
}

+ 104
- 0
c++/carla-backend/plugins/zynaddsubfx/Effects/Echo.h View File

@@ -0,0 +1,104 @@
/*
ZynAddSubFX - a software synthesizer

Echo.h - Echo Effect
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 ECHO_H
#define ECHO_H

#include "Effect.h"
#include "../Misc/Stereo.h"

/**Echo Effect*/
class Echo:public Effect
{
public:
Echo(bool insertion_, float *efxoutl_, float *efxoutr_);
~Echo();

void out(const Stereo<float *> &input);
void setpreset(unsigned char npreset);
/**
* Sets the value of the chosen variable
*
* The possible parameters are:
* -# Volume
* -# Panning
* -# Delay
* -# L/R Delay
* -# L/R Crossover
* -# Feedback
* -# Dampening
* @param npar number of chosen parameter
* @param value the new value
*/
void changepar(int npar, unsigned char value);

/**
* Gets the specified parameter
*
* The possible parameters are
* -# Volume
* -# Panning
* -# Delay
* -# L/R Delay
* -# L/R Crossover
* -# Feedback
* -# Dampening
* @param npar number of chosen parameter
* @return value of parameter
*/
unsigned char getpar(int npar) const;
int getnumparams(void);
void cleanup(void);
private:
//Parameters
unsigned char Pvolume; /**<#1 Volume or Dry/Wetness*/
unsigned char Pdelay; /**<#3 Delay of the Echo*/
unsigned char Plrdelay; /**<#4 L/R delay difference*/
unsigned char Pfb; /**<#6Feedback*/
unsigned char Phidamp; /**<#7Dampening of the Echo*/

void setvolume(unsigned char _Pvolume);
void setdelay(unsigned char _Pdelay);
void setlrdelay(unsigned char _Plrdelay);
void setfb(unsigned char _Pfb);
void sethidamp(unsigned char _Phidamp);

//Real Parameters
float fb, hidamp;
//Left/Right delay lengths
Stereo<int> delayTime;
float lrdelay;
float avgDelay;

void initdelays(void);
//2 channel ring buffer
Stereo<float *> delay;
Stereo<float> old;

//position of reading/writing from delaysample
Stereo<int> pos;
//step size for delay buffer
Stereo<int> delta;
Stereo<int> ndelta;
};

#endif

+ 62
- 0
c++/carla-backend/plugins/zynaddsubfx/Effects/Effect.cpp View File

@@ -0,0 +1,62 @@
/*
ZynAddSubFX - a software synthesizer

Effect.cpp - this class is inherited by the all effects(Reverb, Echo, ..)
Copyright (C) 2002-2005 Nasca Octavian Paul
Copyright 2011, Alan Calvert
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 "Effect.h"
#include "../Params/FilterParams.h"
#include <cmath>

Effect::Effect(bool insertion_, float *efxoutl_, float *efxoutr_,
FilterParams *filterpars_, unsigned char Ppreset_)
:Ppreset(Ppreset_),
efxoutl(efxoutl_),
efxoutr(efxoutr_),
filterpars(filterpars_),
insertion(insertion_)
{}

void Effect::out(float *const smpsl, float *const smpsr)
{
out(Stereo<float *>(smpsl, smpsr));
}

void Effect::crossover(float &a, float &b, float crossover)
{
float tmpa = a;
float tmpb = b;
a = tmpa * (1.0f - crossover) + tmpb * crossover;
b = tmpb * (1.0f - crossover) + tmpa * crossover;
}

void Effect::setpanning(char Ppanning_)
{
Ppanning = Ppanning_;
float t = (Ppanning > 0) ? (float)(Ppanning - 1) / 126.0f : 0.0f;
pangainL = cosf(t * PI / 2.0f);
pangainR = cosf((1.0f - t) * PI / 2.0f);
}

void Effect::setlrcross(char Plrcross_)
{
Plrcross = Plrcross_;
lrcross = (float)Plrcross / 127.0f;
}

+ 105
- 0
c++/carla-backend/plugins/zynaddsubfx/Effects/Effect.h View File

@@ -0,0 +1,105 @@
/*
ZynAddSubFX - a software synthesizer

Effect.h - this class is inherited by the all effects(Reverb, Echo, ..)
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 EFFECT_H
#define EFFECT_H

#include "../Misc/Util.h"
#include "../globals.h"
#include "../Params/FilterParams.h"
#include "../Misc/Stereo.h"

class FilterParams;

/**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_);
virtual ~Effect() {}
/**
* Choose a preset
* @param npreset number of chosen preset*/
virtual void setpreset(unsigned char npreset) = 0;
/**Change parameter npar to value
* @param npar chosen parameter
* @param value chosen new value*/
virtual void changepar(int npar, unsigned char value) = 0;
/**Get the value of parameter npar
* @param npar chosen parameter
* @return the value of the parameter in an unsigned char or 0 if it
* does not exist*/
virtual unsigned char getpar(int npar) const = 0;
/**Output result of effect based on the given buffers
*
* This method should result in the effect generating its results
* and placing them into the efxoutl and efxoutr buffers.
* Every Effect should overide this method.
*
* @param smpsl Input buffer for the Left channel
* @param smpsr Input buffer for the Right channel
*/
void out(float *const smpsl, float *const smpsr);
virtual void out(const Stereo<float *> &smp) = 0;
/**Reset the state of the effect*/
virtual void cleanup(void) {}
virtual float getfreqresponse(float freq) { return freq; }

unsigned char Ppreset; /**<Currently used preset*/
float *const efxoutl; /**<Effect out Left Channel*/
float *const efxoutr; /**<Effect out Right Channel*/
float outvolume; /**<This is the volume of effect and is public because
* it is needed in system effects.
* The out volume of such effects are always 1.0f, so
* this setting tells me how is the volume to the
* Master Output only.*/

float volume;

FilterParams *filterpars; /**<Parameters for filters used by Effect*/

//Perform L/R crossover
static void crossover(float &a, float &b, float crossover);

protected:
void setpanning(char Ppanning_);
void setlrcross(char Plrcross_);

const bool insertion;
//panning parameters
char Ppanning;
float pangainL;
float pangainR;
char Plrcross; // L/R mix
float lrcross;
};

#endif

+ 111
- 0
c++/carla-backend/plugins/zynaddsubfx/Effects/EffectLFO.cpp View File

@@ -0,0 +1,111 @@
/*
ZynAddSubFX - a software synthesizer

EffectLFO.cpp - Stereo LFO used by some effects
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 "EffectLFO.h"
#include "../Misc/Util.h"

#include <cmath>

EffectLFO::EffectLFO(void)
:Pfreq(40),
Prandomness(0),
PLFOtype(0),
Pstereo(64),
xl(0.0f),
xr(0.0f),
ampl1(RND),
ampl2(RND),
ampr1(RND),
ampr2(RND),
lfornd(0.0f)
{
updateparams();
}

EffectLFO::~EffectLFO() {}

//Update the changed parameters
void EffectLFO::updateparams(void)
{
float lfofreq = (powf(2.0f, Pfreq / 127.0f * 10.0f) - 1.0f) * 0.03f;
incx = fabsf(lfofreq) * synth->buffersize_f / synth->samplerate_f;
if(incx > 0.49999999f)
incx = 0.499999999f; //Limit the Frequency

lfornd = Prandomness / 127.0f;
lfornd = (lfornd > 1.0f) ? 1.0f : lfornd;

if(PLFOtype > 1)
PLFOtype = 1; //this has to be updated if more lfo's are added
lfotype = PLFOtype;
xr = fmodf(xl + (Pstereo - 64.0f) / 127.0f + 1.0f, 1.0f);
}


//Compute the shape of the LFO
float EffectLFO::getlfoshape(float x)
{
float out;
switch(lfotype) {
case 1: //EffectLFO_TRIANGLE
if((x > 0.0f) && (x < 0.25f))
out = 4.0f * x;
else
if((x > 0.25f) && (x < 0.75f))
out = 2.0f - 4.0f * x;
else
out = 4.0f * x - 4.0f;
break;
//when adding more, ensure ::updateparams() gets updated
default:
out = cosf(x * 2.0f * PI); //EffectLFO_SINE
}
return out;
}

//LFO output
void EffectLFO::effectlfoout(float *outl, float *outr)
{
float out;

out = getlfoshape(xl);
if((lfotype == 0) || (lfotype == 1))
out *= (ampl1 + xl * (ampl2 - ampl1));
xl += incx;
if(xl > 1.0f) {
xl -= 1.0f;
ampl1 = ampl2;
ampl2 = (1.0f - lfornd) + lfornd * RND;
}
*outl = (out + 1.0f) * 0.5f;

out = getlfoshape(xr);
if((lfotype == 0) || (lfotype == 1))
out *= (ampr1 + xr * (ampr2 - ampr1));
xr += incx;
if(xr > 1.0f) {
xr -= 1.0f;
ampr1 = ampr2;
ampr2 = (1.0f - lfornd) + lfornd * RND;
}
*outr = (out + 1.0f) * 0.5f;
}

+ 50
- 0
c++/carla-backend/plugins/zynaddsubfx/Effects/EffectLFO.h View File

@@ -0,0 +1,50 @@
/*
ZynAddSubFX - a software synthesizer

EffectLFO.h - Stereo LFO used by some effects
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 EFFECT_LFO_H
#define EFFECT_LFO_H

/**LFO for some of the Effect objects
* \todo see if this should inherit LFO*/
class EffectLFO
{
public:
EffectLFO();
~EffectLFO();
void effectlfoout(float *outl, float *outr);
void updateparams(void);
unsigned char Pfreq;
unsigned char Prandomness;
unsigned char PLFOtype;
unsigned char Pstereo; // 64 is centered
private:
float getlfoshape(float x);

float xl, xr;
float incx;
float ampl1, ampl2, ampr1, ampr2; //necessary for "randomness"
float lfointensity;
float lfornd;
char lfotype;
};

#endif

+ 311
- 0
c++/carla-backend/plugins/zynaddsubfx/Effects/EffectMgr.cpp View File

@@ -0,0 +1,311 @@
/*
ZynAddSubFX - a software synthesizer

EffectMgr.cpp - Effect manager, an interface betwen the program and effects
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 "EffectMgr.h"
#include "Effect.h"
#include "Reverb.h"
#include "Echo.h"
#include "Chorus.h"
#include "Distorsion.h"
#include "EQ.h"
#include "DynamicFilter.h"
#include "../Misc/XMLwrapper.h"
#include "../Params/FilterParams.h"

#include <iostream>
using namespace std;

EffectMgr::EffectMgr(const bool insertion_, pthread_mutex_t *mutex_)
:insertion(insertion_),
efxoutl(new float[synth->buffersize]),
efxoutr(new float[synth->buffersize]),
filterpars(NULL),
nefx(0),
efx(NULL),
mutex(mutex_),
dryonly(false)
{
setpresettype("Peffect");
memset(efxoutl, 0, synth->bufferbytes);
memset(efxoutr, 0, synth->bufferbytes);
defaults();
}


EffectMgr::~EffectMgr()
{
delete efx;
delete [] efxoutl;
delete [] efxoutr;
}

void EffectMgr::defaults(void)
{
changeeffect(0);
setdryonly(false);
}

//Change the effect
void EffectMgr::changeeffect(int _nefx)
{
cleanup();
if(nefx == _nefx)
return;
nefx = _nefx;
memset(efxoutl, 0, synth->bufferbytes);
memset(efxoutr, 0, synth->bufferbytes);
delete efx;
switch(nefx) {
case 1:
efx = new Reverb(insertion, efxoutl, efxoutr);
break;
case 2:
efx = new Echo(insertion, efxoutl, efxoutr);
break;
case 3:
efx = new Chorus(insertion, efxoutl, efxoutr);
break;
case 4:
efx = new Phaser(insertion, efxoutl, efxoutr);
break;
case 5:
efx = new Alienwah(insertion, efxoutl, efxoutr);
break;
case 6:
efx = new Distorsion(insertion, efxoutl, efxoutr);
break;
case 7:
efx = new EQ(insertion, efxoutl, efxoutr);
break;
case 8:
efx = new DynamicFilter(insertion, efxoutl, efxoutr);
break;
//put more effect here
default:
efx = NULL;
break; //no effect (thru)
}

if(efx)
filterpars = efx->filterpars;
}

//Obtain the effect number
int EffectMgr::geteffect(void)
{
return nefx;
}

// Cleanup the current effect
void EffectMgr::cleanup(void)
{
if(efx)
efx->cleanup();
}


// Get the preset of the current effect
unsigned char EffectMgr::getpreset(void)
{
if(efx)
return efx->Ppreset;
else
return 0;
}

// Change the preset of the current effect
void EffectMgr::changepreset_nolock(unsigned char npreset)
{
if(efx)
efx->setpreset(npreset);
}

//Change the preset of the current effect(with thread locking)
void EffectMgr::changepreset(unsigned char npreset)
{
pthread_mutex_lock(mutex);
changepreset_nolock(npreset);
pthread_mutex_unlock(mutex);
}


//Change a parameter of the current effect
void EffectMgr::seteffectpar_nolock(int npar, unsigned char value)
{
if(!efx)
return;
efx->changepar(npar, value);
}

// Change a parameter of the current effect (with thread locking)
void EffectMgr::seteffectpar(int npar, unsigned char value)
{
pthread_mutex_lock(mutex);
seteffectpar_nolock(npar, value);
pthread_mutex_unlock(mutex);
}

//Get a parameter of the current effect
unsigned char EffectMgr::geteffectpar(int npar)
{
if(!efx)
return 0;
return efx->getpar(npar);
}

// Apply the effect
void EffectMgr::out(float *smpsl, float *smpsr)
{
if(!efx) {
if(!insertion)
for(int i = 0; i < synth->buffersize; ++i) {
smpsl[i] = 0.0f;
smpsr[i] = 0.0f;
efxoutl[i] = 0.0f;
efxoutr[i] = 0.0f;
}
return;
}
for(int i = 0; i < synth->buffersize; ++i) {
smpsl[i] += denormalkillbuf[i];
smpsr[i] += denormalkillbuf[i];
efxoutl[i] = 0.0f;
efxoutr[i] = 0.0f;
}
efx->out(smpsl, 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);
return;
}

//Insertion effect
if(insertion != 0) {
float v1, v2;
if(volume < 0.5f) {
v1 = 1.0f;
v2 = volume * 2.0f;
}
else {
v1 = (1.0f - volume) * 2.0f;
v2 = 1.0f;
}
if((nefx == 1) || (nefx == 2))
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) {
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) {
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) {
efxoutl[i] *= 2.0f * volume;
efxoutr[i] *= 2.0f * volume;
smpsl[i] = efxoutl[i];
smpsr[i] = efxoutr[i];
}
}


// Get the effect volume for the system effect
float EffectMgr::sysefxgetvolume(void)
{
return (!efx) ? 1.0f : efx->outvolume;
}


// Get the EQ response
float EffectMgr::getEQfreqresponse(float freq)
{
return (nefx == 7) ? efx->getfreqresponse(freq) : 0.0f;
}


void EffectMgr::setdryonly(bool value)
{
dryonly = value;
}

void EffectMgr::add2XML(XMLwrapper *xml)
{
xml->addpar("type", geteffect());

if(!efx || !geteffect())
return;
xml->addpar("preset", efx->Ppreset);

xml->beginbranch("EFFECT_PARAMETERS");
for(int n = 0; n < 128; ++n) {
int par = geteffectpar(n);
if(par == 0)
continue;
xml->beginbranch("par_no", n);
xml->addpar("par", par);
xml->endbranch();
}
if(filterpars) {
xml->beginbranch("FILTER");
filterpars->add2XML(xml);
xml->endbranch();
}
xml->endbranch();
}

void EffectMgr::getfromXML(XMLwrapper *xml)
{
changeeffect(xml->getpar127("type", geteffect()));

if(!efx || !geteffect())
return;

efx->Ppreset = xml->getpar127("preset", efx->Ppreset);

if(xml->enterbranch("EFFECT_PARAMETERS")) {
for(int n = 0; n < 128; ++n) {
seteffectpar_nolock(n, 0); //erase effect parameter
if(xml->enterbranch("par_no", n) == 0)
continue;
int par = geteffectpar(n);
seteffectpar_nolock(n, xml->getpar127("par", par));
xml->exitbranch();
}
if(filterpars)
if(xml->enterbranch("FILTER")) {
filterpars->getfromXML(xml);
xml->exitbranch();
}
xml->exitbranch();
}
cleanup();
}

+ 86
- 0
c++/carla-backend/plugins/zynaddsubfx/Effects/EffectMgr.h View File

@@ -0,0 +1,86 @@
/*
ZynAddSubFX - a software synthesizer

EffectMgr.h - Effect manager, an interface betwen the program and effects
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 EFFECTMGR_H
#define EFFECTMGR_H

#include <pthread.h>

#include "Alienwah.h"
#include "Phaser.h"
#include "../Params/Presets.h"

class Effect;
class FilterParams;
class XMLwrapper;

#include "Distorsion.h"
#include "EQ.h"
#include "DynamicFilter.h"
#include "../Misc/XMLwrapper.h"
#include "../Params/FilterParams.h"
#include "../Params/Presets.h"

/**Effect manager, an interface betwen the program and effects*/
class EffectMgr:public Presets
{
public:
EffectMgr(const bool insertion_, pthread_mutex_t *mutex_);
~EffectMgr();

void add2XML(XMLwrapper *xml);
void defaults(void);
void getfromXML(XMLwrapper *xml);

void out(float *smpsl, float *smpsr);

void setdryonly(bool value);

/**get the output(to speakers) volume of the systemeffect*/
float sysefxgetvolume(void);

void cleanup(void);

void changeeffect(int nefx_);
int geteffect(void);
void changepreset(unsigned char npreset);
void changepreset_nolock(unsigned char npreset);
unsigned char getpreset(void);
void seteffectpar(int npar, unsigned char value);
void seteffectpar_nolock(int npar, unsigned char value);
unsigned char geteffectpar(int npar);

const bool insertion;
float *efxoutl, *efxoutr;

// used by UI
float getEQfreqresponse(float freq);

FilterParams *filterpars;

private:
int nefx;
Effect *efx;
pthread_mutex_t *mutex;
bool dryonly;
};

#endif

+ 462
- 0
c++/carla-backend/plugins/zynaddsubfx/Effects/Phaser.cpp View File

@@ -0,0 +1,462 @@
/*

Phaser.cpp - Phasing and Approximate digital model of an analog JFET phaser.
Analog modeling implemented by Ryan Billing aka Transmogrifox.
ZynAddSubFX - a software synthesizer

Phaser.cpp - Phaser effect
Copyright (C) 2002-2005 Nasca Octavian Paul
Copyright (C) 2009-2010 Ryan Billing
Copyright (C) 2010-2010 Mark McCurry
Author: Nasca Octavian Paul
Ryan Billing
Mark McCurry

DSP analog modeling theory & practice largely influenced by various CCRMA publications, particularly works by Julius O. Smith.

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 <cmath>
#include <algorithm>
#include "Phaser.h"

using namespace std;

#define PHASER_LFO_SHAPE 2
#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_)
:Effect(insertion_, efxoutl_, efxoutr_, NULL, 0), old(NULL), xn1(NULL),
yn1(NULL), diff(0.0f), oldgain(0.0f), fb(0.0f)
{
analog_setup();
setpreset(Ppreset);
cleanup();
}

void Phaser::analog_setup()
{
//model mismatch between JFET devices
offset[0] = -0.2509303f;
offset[1] = 0.9408924f;
offset[2] = 0.998f;
offset[3] = -0.3486182f;
offset[4] = -0.2762545f;
offset[5] = -0.5215785f;
offset[6] = 0.2509303f;
offset[7] = -0.9408924f;
offset[8] = -0.998f;
offset[9] = 0.3486182f;
offset[10] = 0.2762545f;
offset[11] = 0.5215785f;

barber = 0; //Deactivate barber pole phasing by default

mis = 1.0f;
Rmin = 625.0f; // 2N5457 typical on resistance at Vgs = 0
Rmax = 22000.0f; // Resistor parallel to FET
Rmx = Rmin / Rmax;
Rconst = 1.0f + Rmx; // Handle parallel resistor relationship
C = 0.00000005f; // 50 nF
CFs = 2.0f * synth->samplerate_f * C;
invperiod = 1.0f / synth->buffersize_f;
}

Phaser::~Phaser()
{
if(xn1.l)
delete[] xn1.l;
if(yn1.l)
delete[] yn1.l;
if(xn1.r)
delete[] xn1.r;
if(yn1.r)
delete[] yn1.r;
}

/*
* Effect output
*/
void Phaser::out(const Stereo<float *> &input)
{
if(Panalog)
AnalogPhase(input);
else
normalPhase(input);
}

void Phaser::AnalogPhase(const Stereo<float *> &input)
{
Stereo<float> gain(0.0f), lfoVal(0.0f), mod(0.0f), g(0.0f), b(0.0f), hpf(
0.0f);

lfo.effectlfoout(&lfoVal.l, &lfoVal.r);
mod.l = lfoVal.l * width + (depth - 0.5f);
mod.r = lfoVal.r * width + (depth - 0.5f);

mod.l = limit(mod.l, ZERO_, ONE_);
mod.r = limit(mod.r, ZERO_, ONE_);

if(Phyper) {
//Triangle wave squared is approximately sin on bottom, tri on top
//Result is exponential sweep more akin to filter in synth with
//exponential generator circuitry.
mod.l *= mod.l;
mod.r *= mod.r;
}

//g.l,g.r is Vp - Vgs. Typical FET drain-source resistance follows constant/[1-sqrt(Vp - Vgs)]
mod.l = sqrtf(1.0f - mod.l);
mod.r = sqrtf(1.0f - mod.r);

diff.r = (mod.r - oldgain.r) * invperiod;
diff.l = (mod.l - oldgain.l) * invperiod;

g = oldgain;
oldgain = mod;

for(int i = 0; i < synth->buffersize; ++i) {
g.l += diff.l; // Linear interpolation between LFO samples
g.r += diff.r;

Stereo<float> xn(input.l[i] * pangainL, input.r[i] * pangainR);

if(barber) {
g.l = fmodf((g.l + 0.25f), ONE_);
g.r = fmodf((g.r + 0.25f), ONE_);
}

xn.l = applyPhase(xn.l, g.l, fb.l, hpf.l, yn1.l, xn1.l);
xn.r = applyPhase(xn.r, g.r, fb.r, hpf.r, yn1.r, xn1.r);


fb.l = xn.l * feedback;
fb.r = xn.r * feedback;
efxoutl[i] = xn.l;
efxoutr[i] = xn.r;
}

if(Poutsub) {
invSignal(efxoutl, synth->buffersize);
invSignal(efxoutr, synth->buffersize);
}
}

float Phaser::applyPhase(float x, float g, float fb,
float &hpf, float *yn1, float *xn1)
{
for(int j = 0; j < Pstages; ++j) { //Phasing routine
mis = 1.0f + offsetpct * offset[j];

//This is symmetrical.
//FET is not, so this deviates slightly, however sym dist. is
//better sounding than a real FET.
float d = (1.0f + 2.0f * (0.25f + g) * hpf * hpf * distortion) * mis;
Rconst = 1.0f + mis * Rmx;

// This is 1/R. R is being modulated to control filter fc.
float b = (Rconst - g) / (d * Rmin);
float gain = (CFs - b) / (CFs + b);
yn1[j] = gain * (x + yn1[j]) - xn1[j];

//high pass filter:
//Distortion depends on the high-pass part of the AP stage.
hpf = yn1[j] + (1.0f - gain) * xn1[j];

xn1[j] = x;
x = yn1[j];
if(j == 1)
x += fb; //Insert feedback after first phase stage
}
return x;
}
void Phaser::normalPhase(const Stereo<float *> &input)
{
Stereo<float> gain(0.0f), lfoVal(0.0f);

lfo.effectlfoout(&lfoVal.l, &lfoVal.r);
gain.l =
(expf(lfoVal.l
* PHASER_LFO_SHAPE) - 1) / (expf(PHASER_LFO_SHAPE) - 1.0f);
gain.r =
(expf(lfoVal.r
* PHASER_LFO_SHAPE) - 1) / (expf(PHASER_LFO_SHAPE) - 1.0f);

gain.l = 1.0f - phase * (1.0f - depth) - (1.0f - phase) * gain.l * depth;
gain.r = 1.0f - phase * (1.0f - depth) - (1.0f - phase) * gain.r * depth;

gain.l = limit(gain.l, ZERO_, ONE_);
gain.r = limit(gain.r, ZERO_, ONE_);

for(int i = 0; i < synth->buffersize; ++i) {
float x = (float) i / synth->buffersize_f;
float x1 = 1.0f - x;
//TODO think about making panning an external feature
Stereo<float> xn(input.l[i] * pangainL + fb.l,
input.r[i] * pangainR + fb.r);

Stereo<float> g(gain.l * x + oldgain.l * x1,
gain.r * x + oldgain.r * x1);

xn.l = applyPhase(xn.l, g.l, old.l);
xn.r = applyPhase(xn.r, g.r, old.r);

//Left/Right crossing
crossover(xn.l, xn.r, lrcross);

fb.l = xn.l * feedback;
fb.r = xn.r * feedback;
efxoutl[i] = xn.l;
efxoutr[i] = xn.r;
}

oldgain = gain;

if(Poutsub) {
invSignal(efxoutl, synth->buffersize);
invSignal(efxoutr, synth->buffersize);
}
}

float Phaser::applyPhase(float x, float g, float *old)
{
for(int j = 0; j < Pstages * 2; ++j) { //Phasing routine
float tmp = old[j];
old[j] = g * tmp + x;
x = tmp - g * old[j];
}
return x;
}

/*
* Cleanup the effect
*/
void Phaser::cleanup()
{
fb = oldgain = Stereo<float>(0.0f);
for(int i = 0; i < Pstages * 2; ++i) {
old.l[i] = 0.0f;
old.r[i] = 0.0f;
}
for(int i = 0; i < Pstages; ++i) {
xn1.l[i] = 0.0f;
yn1.l[i] = 0.0f;
xn1.r[i] = 0.0f;
yn1.r[i] = 0.0f;
}
}

/*
* Parameter control
*/
void Phaser::setwidth(unsigned char Pwidth)
{
this->Pwidth = Pwidth;
width = ((float)Pwidth / 127.0f);
}

void Phaser::setfb(unsigned char Pfb)
{
this->Pfb = Pfb;
feedback = (float) (Pfb - 64) / 64.2f;
}

void Phaser::setvolume(unsigned char Pvolume)
{
this->Pvolume = Pvolume;
outvolume = Pvolume / 127.0f;
if(insertion == 0)
volume = 1.0f;
else
volume = outvolume;
}

void Phaser::setdistortion(unsigned char Pdistortion)
{
this->Pdistortion = Pdistortion;
distortion = (float)Pdistortion / 127.0f;
}

void Phaser::setoffset(unsigned char Poffset)
{
this->Poffset = Poffset;
offsetpct = (float)Poffset / 127.0f;
}

void Phaser::setstages(unsigned char Pstages)
{
if(xn1.l)
delete[] xn1.l;
if(yn1.l)
delete[] yn1.l;
if(xn1.r)
delete[] xn1.r;
if(yn1.r)
delete[] yn1.r;


this->Pstages = min(MAX_PHASER_STAGES, (int)Pstages);

old = Stereo<float *>(new float[Pstages * 2],
new float[Pstages * 2]);

xn1 = Stereo<float *>(new float[Pstages],
new float[Pstages]);

yn1 = Stereo<float *>(new float[Pstages],
new float[Pstages]);

cleanup();
}

void Phaser::setphase(unsigned char Pphase)
{
this->Pphase = Pphase;
phase = (Pphase / 127.0f);
}

void Phaser::setdepth(unsigned char Pdepth)
{
this->Pdepth = Pdepth;
depth = (float)(Pdepth) / 127.0f;
}


void Phaser::setpreset(unsigned char npreset)
{
const int PRESET_SIZE = 15;
const int NUM_PRESETS = 12;
unsigned char presets[NUM_PRESETS][PRESET_SIZE] = {
//Phaser
//0 1 2 3 4 5 6 7 8 9 10 11 12 13 14
{64, 64, 36, 0, 0, 64, 110, 64, 1, 0, 0, 20,
0, 0,
0 },
{64, 64, 35, 0, 0, 88, 40, 64, 3, 0, 0, 20, 0, 0,
0 },
{64, 64, 31, 0, 0, 66, 68, 107, 2, 0, 0, 20, 0, 0,
0 },
{39, 64, 22, 0, 0, 66, 67, 10, 5, 0, 1, 20, 0, 0,
0 },
{64, 64, 20, 0, 1, 110, 67, 78, 10, 0, 0, 20, 0, 0,
0 },
{64, 64, 53, 100, 0, 58, 37, 78, 3, 0, 0, 20, 0, 0,
0 },
//APhaser
//0 1 2 3 4 5 6 7 8 9 10 11 12 13 14
{64, 64, 14, 0, 1, 64, 64, 40, 4, 10, 0, 110,1, 20,
1 },
{64, 64, 14, 5, 1, 64, 70, 40, 6, 10, 0, 110,1, 20,
1 },
{64, 64, 9, 0, 0, 64, 60, 40, 8, 10, 0, 40, 0, 20,
1 },
{64, 64, 14, 10, 0, 64, 45, 80, 7, 10, 1, 110,1, 20,
1 },
{25, 64, 127, 10, 0, 64, 25, 16, 8, 100, 0, 25, 0, 20,
1 },
{64, 64, 1, 10, 1, 64, 70, 40, 12, 10, 0, 110,1, 20,
1 }
};
if(npreset >= NUM_PRESETS)
npreset = NUM_PRESETS - 1;
for(int n = 0; n < PRESET_SIZE; ++n)
changepar(n, presets[npreset][n]);
Ppreset = npreset;
}


void Phaser::changepar(int npar, unsigned char value)
{
switch(npar) {
case 0:
setvolume(value);
break;
case 1:
setpanning(value);
break;
case 2:
lfo.Pfreq = value;
lfo.updateparams();
break;
case 3:
lfo.Prandomness = value;
lfo.updateparams();
break;
case 4:
lfo.PLFOtype = value;
lfo.updateparams();
barber = (2 == value);
break;
case 5:
lfo.Pstereo = value;
lfo.updateparams();
break;
case 6:
setdepth(value);
break;
case 7:
setfb(value);
break;
case 8:
setstages(value);
break;
case 9:
setlrcross(value);
setoffset(value);
break;
case 10:
Poutsub = min((int)value, 1);
break;
case 11:
setphase(value);
setwidth(value);
break;
case 12:
Phyper = min((int)value, 1);
break;
case 13:
setdistortion(value);
break;
case 14:
Panalog = value;
break;
}
}

unsigned char Phaser::getpar(int npar) const
{
switch(npar) {
case 0: return Pvolume;
case 1: return Ppanning;
case 2: return lfo.Pfreq;
case 3: return lfo.Prandomness;
case 4: return lfo.PLFOtype;
case 5: return lfo.Pstereo;
case 6: return Pdepth;
case 7: return Pfb;
case 8: return Pstages;
case 9: return Plrcross;
return Poffset; //same
case 10: return Poutsub;
case 11: return Pphase;
return Pwidth; //same
case 12: return Phyper;
case 13: return Pdistortion;
case 14: return Panalog;
default: return 0;
}
}

+ 98
- 0
c++/carla-backend/plugins/zynaddsubfx/Effects/Phaser.h View File

@@ -0,0 +1,98 @@
/*
ZynAddSubFX - a software synthesizer

Phaser.h - Phaser effect
Copyright (C) 2002-2005 Nasca Octavian Paul
Copyright (C) 2009-2010 Ryan Billing
Copyright (C) 2010-2010 Mark McCurry
Author: Nasca Octavian Paul
Ryan Billing
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 PHASER_H
#define PHASER_H
#include "../globals.h"
#include "Effect.h"
#include "EffectLFO.h"

#define MAX_PHASER_STAGES 12

class Phaser:public Effect
{
public:
Phaser(const int &insertion_, float *efxoutl_, float *efxoutr_);
~Phaser();
void out(const Stereo<float *> &input);
void setpreset(unsigned char npreset);
void changepar(int npar, unsigned char value);
unsigned char getpar(int npar) const;
void cleanup();

private:
//Phaser parameters
EffectLFO lfo; //Phaser modulator
unsigned char Pvolume; //Used to set wet/dry mix
unsigned char Pdistortion; //Model distortion added by FET element
unsigned char Pdepth; //Depth of phaser sweep
unsigned char Pwidth; //Phaser width (LFO amplitude)
unsigned char Pfb; //feedback
unsigned char Poffset; //Model mismatch between variable resistors
unsigned char Pstages; //Number of first-order All-Pass stages
unsigned char Poutsub; //if I wish to subtract the output instead of adding
unsigned char Pphase;
unsigned char Phyper; //lfo^2 -- converts tri into hyper-sine
unsigned char Panalog;

//Control parameters
void setvolume(unsigned char Pvolume);
void setdepth(unsigned char Pdepth);
void setfb(unsigned char Pfb);
void setdistortion(unsigned char Pdistortion);
void setwidth(unsigned char Pwidth);
void setoffset(unsigned char Poffset);
void setstages(unsigned char Pstages);
void setphase(unsigned char Pphase);

//Internal Variables
bool barber; //Barber pole phasing flag
float distortion, width, offsetpct;
float feedback, depth, phase;
Stereo<float *> old, xn1, yn1;
Stereo<float> diff, oldgain, fb;
float invperiod;
float offset[12];

float mis;
float Rmin; // 3N5457 typical on resistance at Vgs = 0
float Rmax; // Resistor parallel to FET
float Rmx; // Rmin/Rmax to avoid division in loop
float Rconst; // Handle parallel resistor relationship
float C; // Capacitor
float CFs; // A constant derived from capacitor and resistor relationships

void analog_setup();
void AnalogPhase(const Stereo<float *> &input);
//analog case
float applyPhase(float x, float g, float fb,
float &hpf, float *yn1, float *xn1);

void normalPhase(const Stereo<float *> &input);
float applyPhase(float x, float g, float *old);
};

#endif

+ 498
- 0
c++/carla-backend/plugins/zynaddsubfx/Effects/Reverb.cpp View File

@@ -0,0 +1,498 @@
/*
ZynAddSubFX - a software synthesizer

Reverb.cpp - Reverberation effect
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 "Reverb.h"
#include "../Misc/Util.h"
#include "../DSP/AnalogFilter.h"
#include "../DSP/Unison.h"
#include <cmath>

//todo: EarlyReflections, Prdelay, Perbalance

Reverb::Reverb(bool insertion_, float *efxoutl_, float *efxoutr_)
:Effect(insertion_, efxoutl_, efxoutr_, NULL, 0),
// defaults
Pvolume(48),
Ptime(64),
Pidelay(40),
Pidelayfb(0),
Prdelay(0),
Perbalance(64),
Plpf(127),
Phpf(0),
Plohidamp(80),
Ptype(1),
Proomsize(64),
Pbandwidth(30),
roomsize(1.0f),
rs(1.0f),
bandwidth(NULL),
idelay(NULL),
lpf(NULL),
hpf(NULL) // no filter
{
for(int i = 0; i < REV_COMBS * 2; ++i) {
comblen[i] = 800 + (int)(RND * 1400.0f);
combk[i] = 0;
lpcomb[i] = 0;
combfb[i] = -0.97f;
comb[i] = NULL;
}

for(int i = 0; i < REV_APS * 2; ++i) {
aplen[i] = 500 + (int)(RND * 500.0f);
apk[i] = 0;
ap[i] = NULL;
}
setpreset(Ppreset);
cleanup(); //do not call this before the comb initialisation
}


Reverb::~Reverb()
{
delete [] idelay;
delete hpf;
delete lpf;

for(int i = 0; i < REV_APS * 2; ++i)
delete [] ap[i];
for(int i = 0; i < REV_COMBS * 2; ++i)
delete [] comb[i];

if(bandwidth)
delete bandwidth;
}

//Cleanup the effect
void Reverb::cleanup(void)
{
int i, j;
for(i = 0; i < REV_COMBS * 2; ++i) {
lpcomb[i] = 0.0f;
for(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)
ap[i][j] = 0.0f;

if(idelay)
for(i = 0; i < idelaylen; ++i)
idelay[i] = 0.0f;
if(hpf)
hpf->cleanup();
if(lpf)
lpf->cleanup();
}

//Process one channel; 0=left, 1=right
void Reverb::processmono(int ch, float *output, float *inputbuf)
{
//todo: implement the high part from lohidamp

for(int j = REV_COMBS * ch; j < REV_COMBS * (ch + 1); ++j) {
int &ck = combk[j];
const int comblength = comblen[j];
float &lpcombj = lpcomb[j];

for(int i = 0; i < synth->buffersize; ++i) {
float fbout = comb[j][ck] * combfb[j];
fbout = fbout * (1.0f - lohifb) + lpcombj * lohifb;
lpcombj = fbout;

comb[j][ck] = inputbuf[i] + fbout;
output[i] += fbout;

if((++ck) >= comblength)
ck = 0;
}
}

for(int j = REV_APS * ch; j < REV_APS * (1 + ch); ++j) {
int &ak = apk[j];
const int aplength = aplen[j];
for(int i = 0; i < synth->buffersize; ++i) {
float tmp = ap[j][ak];
ap[j][ak] = 0.7f * tmp + output[i];
output[i] = tmp - 0.7f * ap[j][ak];
if((++ak) >= aplength)
ak = 0;
}
}
}

//Effect output
void Reverb::out(const Stereo<float *> &smp)
{
if(!Pvolume && insertion)
return;

float *inputbuf = getTmpBuffer();
for(int i = 0; i < synth->buffersize; ++i)
inputbuf[i] = (smp.l[i] + smp.r[i]) / 2.0f;

if(idelay)
for(int i = 0; i < synth->buffersize; ++i) {
//Initial delay r
float tmp = inputbuf[i] + idelay[idelayk] * idelayfb;
inputbuf[i] = idelay[idelayk];
idelay[idelayk] = tmp;
idelayk++;
if(idelayk >= idelaylen)
idelayk = 0;
}

if(bandwidth)
bandwidth->process(synth->buffersize, inputbuf);

if(lpf)
lpf->filterout(inputbuf);
if(hpf)
hpf->filterout(inputbuf);

processmono(0, efxoutl, inputbuf); //left
processmono(1, efxoutr, inputbuf); //right
returnTmpBuffer(inputbuf);

float lvol = rs / REV_COMBS * pangainL;
float rvol = rs / REV_COMBS * pangainR;
if(insertion != 0) {
lvol *= 2.0f;
rvol *= 2.0f;
}
for(int i = 0; i < synth->buffersize; ++i) {
efxoutl[i] *= lvol;
efxoutr[i] *= rvol;
}
}


//Parameter control
void Reverb::setvolume(unsigned char _Pvolume)
{
Pvolume = _Pvolume;
if(!insertion) {
outvolume = powf(0.01f, (1.0f - Pvolume / 127.0f)) * 4.0f;
volume = 1.0f;
}
else {
volume = outvolume = Pvolume / 127.0f;
if(Pvolume == 0)
cleanup();
}
}

void Reverb::settime(unsigned char _Ptime)
{
Ptime = _Ptime;
float t = powf(60.0f, Ptime / 127.0f) - 0.97f;

for(int i = 0; i < REV_COMBS * 2; ++i)
combfb[i] =
-expf((float)comblen[i] / synth->samplerate_f * logf(0.001f) / t);
//the feedback is negative because it removes the DC
}

void Reverb::setlohidamp(unsigned char _Plohidamp)
{
Plohidamp = (_Plohidamp < 64) ? 64 : _Plohidamp;
//remove this when the high part from lohidamp is added
if(Plohidamp == 64) {
lohidamptype = 0;
lohifb = 0.0f;
}
else {
if(Plohidamp < 64)
lohidamptype = 1;
if(Plohidamp > 64)
lohidamptype = 2;
float x = fabsf((float)(Plohidamp - 64) / 64.1f);
lohifb = x * x;
}
}

void Reverb::setidelay(unsigned char _Pidelay)
{
Pidelay = _Pidelay;
float delay = powf(50.0f * Pidelay / 127.0f, 2.0f) - 1.0f;

if(idelay)
delete [] idelay;
idelay = NULL;

idelaylen = (int) (synth->samplerate_f * delay / 1000);
if(idelaylen > 1) {
idelayk = 0;
idelay = new float[idelaylen];
memset(idelay, 0, idelaylen * sizeof(float));
}
}

void Reverb::setidelayfb(unsigned char _Pidelayfb)
{
Pidelayfb = _Pidelayfb;
idelayfb = Pidelayfb / 128.0f;
}

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;
if(hpf == NULL)
hpf = new AnalogFilter(3, fr, 1, 0);
else
hpf->setfreq(fr);
}
}

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;
if(!lpf)
lpf = new AnalogFilter(2, fr, 1, 0);
else
lpf->setfreq(fr);
}
}

void Reverb::settype(unsigned char _Ptype)
{
Ptype = _Ptype;
const int NUM_TYPES = 3;
const int combtunings[NUM_TYPES][REV_COMBS] = {
//this is unused (for random)
{0, 0, 0, 0, 0, 0, 0, 0 },
//Freeverb by Jezar at Dreampoint
{1116, 1188, 1277, 1356, 1422, 1491, 1557, 1617 },
//duplicate of Freeverb by Jezar at Dreampoint
{1116, 1188, 1277, 1356, 1422, 1491, 1557, 1617 }
};

const int aptunings[NUM_TYPES][REV_APS] = {
//this is unused (for random)
{0, 0, 0, 0 },
//Freeverb by Jezar at Dreampoint
{225, 341, 441, 556 },
//duplicate of Freeverb by Jezar at Dreampoint
{225, 341, 441, 556 }
};

if(Ptype >= NUM_TYPES)
Ptype = NUM_TYPES - 1;

// adjust the combs according to the samplerate
float samplerate_adjust = synth->samplerate_f / 44100.0f;
float tmp;
for(int i = 0; i < REV_COMBS * 2; ++i) {
if(Ptype == 0)
tmp = 800.0f + (int)(RND * 1400.0f);
else
tmp = combtunings[Ptype][i % REV_COMBS];
tmp *= roomsize;
if(i > REV_COMBS)
tmp += 23.0f;
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]];
}

for(int i = 0; i < REV_APS * 2; ++i) {
if(Ptype == 0)
tmp = 500 + (int)(RND * 500.0f);
else
tmp = aptunings[Ptype][i % REV_APS];
tmp *= roomsize;
if(i > REV_APS)
tmp += 23.0f;
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]];
}
delete bandwidth;
bandwidth = NULL;
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(synth->buffersize / 4 + 1, 2.0f);
bandwidth->setSize(50);
bandwidth->setBaseFrequency(1.0f);
}
settime(Ptime);
cleanup();
}

void Reverb::setroomsize(unsigned char _Proomsize)
{
Proomsize = _Proomsize;
if(!Proomsize)
this->Proomsize = 64; //this is because the older versions consider roomsize=0
roomsize = (this->Proomsize - 64.0f) / 64.0f;
if(roomsize > 0.0f)
roomsize *= 2.0f;
roomsize = powf(10.0f, roomsize);
rs = sqrtf(roomsize);
settype(Ptype);
}

void Reverb::setbandwidth(unsigned char _Pbandwidth)
{
Pbandwidth = _Pbandwidth;
float v = Pbandwidth / 127.0f;
if(bandwidth)
bandwidth->setBandwidth(powf(v, 2.0f) * 200.0f);
}

void Reverb::setpreset(unsigned char npreset)
{
const int PRESET_SIZE = 13;
const int NUM_PRESETS = 13;
unsigned char presets[NUM_PRESETS][PRESET_SIZE] = {
//Cathedral1
{80, 64, 63, 24, 0, 0, 0, 85, 5, 83, 1, 64, 20},
//Cathedral2
{80, 64, 69, 35, 0, 0, 0, 127, 0, 71, 0, 64, 20},
//Cathedral3
{80, 64, 69, 24, 0, 0, 0, 127, 75, 78, 1, 85, 20},
//Hall1
{90, 64, 51, 10, 0, 0, 0, 127, 21, 78, 1, 64, 20},
//Hall2
{90, 64, 53, 20, 0, 0, 0, 127, 75, 71, 1, 64, 20},
//Room1
{100, 64, 33, 0, 0, 0, 0, 127, 0, 106, 0, 30, 20},
//Room2
{100, 64, 21, 26, 0, 0, 0, 62, 0, 77, 1, 45, 20},
//Basement
{110, 64, 14, 0, 0, 0, 0, 127, 5, 71, 0, 25, 20},
//Tunnel
{85, 80, 84, 20, 42, 0, 0, 51, 0, 78, 1, 105, 20},
//Echoed1
{95, 64, 26, 60, 71, 0, 0, 114, 0, 64, 1, 64, 20},
//Echoed2
{90, 64, 40, 88, 71, 0, 0, 114, 0, 88, 1, 64, 20},
//VeryLong1
{90, 64, 93, 15, 0, 0, 0, 114, 0, 77, 0, 95, 20},
//VeryLong2
{90, 64, 111, 30, 0, 0, 0, 114, 90, 74, 1, 80, 20}
};

if(npreset >= NUM_PRESETS)
npreset = NUM_PRESETS - 1;
for(int n = 0; n < PRESET_SIZE; ++n)
changepar(n, presets[npreset][n]);
if(insertion)
changepar(0, presets[npreset][0] / 2); //lower the volume if reverb is insertion effect
Ppreset = npreset;
}


void Reverb::changepar(int npar, unsigned char value)
{
switch(npar) {
case 0:
setvolume(value);
break;
case 1:
setpanning(value);
break;
case 2:
settime(value);
break;
case 3:
setidelay(value);
break;
case 4:
setidelayfb(value);
break;
// case 5:
// setrdelay(value);
// break;
// case 6:
// seterbalance(value);
// break;
case 7:
setlpf(value);
break;
case 8:
sethpf(value);
break;
case 9:
setlohidamp(value);
break;
case 10:
settype(value);
break;
case 11:
setroomsize(value);
break;
case 12:
setbandwidth(value);
break;
}
}

unsigned char Reverb::getpar(int npar) const
{
switch(npar) {
case 0: return Pvolume;
case 1: return Ppanning;
case 2: return Ptime;
case 3: return Pidelay;
case 4: return Pidelayfb;
// case 5: return Prdelay;
// case 6: return Perbalance;
case 7: return Plpf;
case 8: return Phpf;
case 9: return Plohidamp;
case 10: return Ptype;
case 11: return Proomsize;
case 12: return Pbandwidth;
default: return 0;
}
}

+ 97
- 0
c++/carla-backend/plugins/zynaddsubfx/Effects/Reverb.h View File

@@ -0,0 +1,97 @@
/*
ZynAddSubFX - a software synthesizer

Reverb.h - Reverberation effect
Copyright (C) 2002-2009 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 REVERB_H
#define REVERB_H

#include "Effect.h"

#define REV_COMBS 8
#define REV_APS 4

/**Creates Reverberation Effects*/
class Reverb:public Effect
{
public:
Reverb(bool insertion_, float *efxoutl_, float *efxoutr_);
~Reverb();
void out(const Stereo<float *> &smp);
void cleanup(void);

void setpreset(unsigned char npreset);
void changepar(int npar, unsigned char value);
unsigned char getpar(int npar) const;

private:
//Parametrii
unsigned char Pvolume;
unsigned char Ptime; //duration
unsigned char Pidelay; //initial delay
unsigned char Pidelayfb; //initial feedback
unsigned char Prdelay; //delay between ER/Reverbs
unsigned char Perbalance; //EarlyReflections/Reverb Balance
unsigned char Plpf;
unsigned char Phpf;
unsigned char Plohidamp; //Low/HighFrequency Damping
unsigned char Ptype; //reverb type
unsigned char Proomsize; //room size
unsigned char Pbandwidth; //bandwidth

//parameter control
void setvolume(unsigned char _Pvolume);
void settime(unsigned char _Ptime);
void setlohidamp(unsigned char _Plohidamp);
void setidelay(unsigned char _Pidelay);
void setidelayfb(unsigned char _Pidelayfb);
void sethpf(unsigned char _Phpf);
void setlpf(unsigned char _Plpf);
void settype(unsigned char _Ptype);
void setroomsize(unsigned char _Proomsize);
void setbandwidth(unsigned char _Pbandwidth);
void processmono(int ch, float *output, float *inputbuf);

float erbalance;

//Parameters
int lohidamptype; //0=disable, 1=highdamp (lowpass), 2=lowdamp (highpass)
int idelaylen, rdelaylen;
int idelayk;
float lohifb;
float idelayfb;
float roomsize;
float rs; //rs is used to "normalise" the volume according to the roomsize
int comblen[REV_COMBS * 2];
int aplen[REV_APS * 2];
class Unison * bandwidth;

//Internal Variables
float *comb[REV_COMBS * 2];
int combk[REV_COMBS * 2];
float combfb[REV_COMBS * 2]; //feedback-ul fiecarui filtru "comb"
float lpcomb[REV_COMBS * 2]; //pentru Filtrul LowPass
float *ap[REV_APS * 2];
int apk[REV_APS * 2];
float *idelay;
class AnalogFilter * lpf, *hpf; //filters
};

#endif

+ 469
- 0
c++/carla-backend/plugins/zynaddsubfx/Misc/Bank.cpp View File

@@ -0,0 +1,469 @@
/*
ZynAddSubFX - a software synthesizer

Bank.cpp - Instrument Bank
Copyright (C) 2002-2005 Nasca Octavian Paul
Copyright (C) 2010-2010 Mark McCurry
Author: Nasca Octavian Paul
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 "Bank.h"
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>
#include <sys/stat.h>
#include <algorithm>
#include <iostream>

#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>

#include "Config.h"
#include "Util.h"
#include "Part.h"

#define INSTRUMENT_EXTENSION ".xiz"

//if this file exists into a directory, this make the directory to be considered as a bank, even if it not contains a instrument file
#define FORCE_BANK_DIR_FILE ".bankdir"

using namespace std;

Bank::Bank()
:defaultinsname(" ")
{
clearbank();
bankfiletitle = dirname;
loadbank(config.cfg.currentBankDir);
}

Bank::~Bank()
{
clearbank();
}

/*
* Get the name of an instrument from the bank
*/
string Bank::getname(unsigned int ninstrument)
{
if(emptyslot(ninstrument))
return defaultinsname;
return ins[ninstrument].name;
}

/*
* Get the numbered name of an instrument from the bank
*/
string Bank::getnamenumbered(unsigned int ninstrument)
{
if(emptyslot(ninstrument))
return defaultinsname;

return stringFrom(ninstrument + 1) + ". " + getname(ninstrument);
}

/*
* Changes the name of an instrument (and the filename)
*/
void Bank::setname(unsigned int ninstrument, const string &newname, int newslot)
{
if(emptyslot(ninstrument))
return;

string newfilename;
char tmpfilename[100 + 1];
tmpfilename[100] = 0;

if(newslot >= 0)
snprintf(tmpfilename, 100, "%4d-%s", newslot + 1, newname.c_str());
else
snprintf(tmpfilename, 100, "%4d-%s", ninstrument + 1, newname.c_str());

//add the zeroes at the start of filename
for(int i = 0; i < 4; ++i)
if(tmpfilename[i] == ' ')
tmpfilename[i] = '0';

newfilename = dirname + '/' + legalizeFilename(tmpfilename) + ".xiz";

rename(ins[ninstrument].filename.c_str(), newfilename.c_str());

ins[ninstrument].filename = newfilename;
ins[ninstrument].name = newname;
}

/*
* Check if there is no instrument on a slot from the bank
*/
bool Bank::emptyslot(unsigned int ninstrument)
{
if(ninstrument >= BANK_SIZE)
return true;
if(ins[ninstrument].filename.empty())
return true;

if(ins[ninstrument].used)
return false;
else
return true;
}

/*
* Removes the instrument from the bank
*/
void Bank::clearslot(unsigned int ninstrument)
{
if(emptyslot(ninstrument))
return;

remove(ins[ninstrument].filename.c_str());
deletefrombank(ninstrument);
}

/*
* Save the instrument to a slot
*/
void Bank::savetoslot(unsigned int ninstrument, Part *part)
{
clearslot(ninstrument);

const int maxfilename = 200;
char tmpfilename[maxfilename + 20];
ZERO(tmpfilename, maxfilename + 20);

snprintf(tmpfilename,
maxfilename,
"%4d-%s",
ninstrument + 1,
(char *)part->Pname);

//add the zeroes at the start of filename
for(int i = 0; i < 4; ++i)
if(tmpfilename[i] == ' ')
tmpfilename[i] = '0';

string filename = dirname + '/' + legalizeFilename(tmpfilename) + ".xiz";

remove(filename.c_str());
part->saveXML(filename.c_str());
addtobank(ninstrument, legalizeFilename(tmpfilename) + ".xiz", (char *) part->Pname);
}

/*
* Loads the instrument from the bank
*/
void Bank::loadfromslot(unsigned int ninstrument, Part *part)
{
if(emptyslot(ninstrument))
return;

part->AllNotesOff();
part->defaultsinstrument();

part->loadXMLinstrument(ins[ninstrument].filename.c_str());
}

/*
* Makes current a bank directory
*/
int Bank::loadbank(string bankdirname)
{
DIR *dir = opendir(bankdirname.c_str());
clearbank();

if(dir == NULL)
return -1;

dirname = bankdirname;

bankfiletitle = dirname;

struct dirent *fn;

while((fn = readdir(dir))) {
const char *filename = fn->d_name;

//check for extension
if(strstr(filename, INSTRUMENT_EXTENSION) == NULL)
continue;

//verify if the name is like this NNNN-name (where N is a digit)
int no = 0;
unsigned int startname = 0;

for(unsigned int i = 0; i < 4; ++i) {
if(strlen(filename) <= i)
break;

if((filename[i] >= '0') && (filename[i] <= '9')) {
no = no * 10 + (filename[i] - '0');
startname++;
}
}

if((startname + 1) < strlen(filename))
startname++; //to take out the "-"

string name = filename;

//remove the file extension
for(int i = name.size() - 1; i >= 2; i--)
if(name[i] == '.') {
name = name.substr(0, i);
break;
}

if(no != 0) //the instrument position in the bank is found
addtobank(no - 1, filename, name.substr(startname));
else
addtobank(-1, filename, name);
}

closedir(dir);

if(!dirname.empty())
config.cfg.currentBankDir = dirname;

return 0;
}

/*
* Makes a new bank, put it on a file and makes it current bank
*/
int Bank::newbank(string newbankdirname)
{
string bankdir;
bankdir = config.cfg.bankRootDirList[0];

if(((bankdir[bankdir.size() - 1]) != '/')
&& ((bankdir[bankdir.size() - 1]) != '\\'))
bankdir += "/";

bankdir += newbankdirname;
if(mkdir(bankdir.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) < 0)
return -1;

const string tmpfilename = bankdir + '/' + FORCE_BANK_DIR_FILE;

FILE *tmpfile = fopen(tmpfilename.c_str(), "w+");
fclose(tmpfile);

return loadbank(bankdir);
}

/*
* Check if the bank is locked (i.e. the file opened was readonly)
*/
int Bank::locked()
{
return dirname.empty();
}

/*
* Swaps a slot with another
*/
void Bank::swapslot(unsigned int n1, unsigned int n2)
{
if((n1 == n2) || (locked()))
return;
if(emptyslot(n1) && (emptyslot(n2)))
return;
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);
ins[n2] = ins[n1];
ins[n1] = ins_t();
}
else { //if both slots are used
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);
swap(ins[n2], ins[n1]);
}
}


bool Bank::bankstruct::operator<(const bankstruct &b) const
{
return name < b.name;
}

/*
* Re-scan for directories containing instrument banks
*/

void Bank::rescanforbanks()
{
//remove old banks
banks.clear();

for(int i = 0; i < MAX_BANK_ROOT_DIRS; ++i)
if(!config.cfg.bankRootDirList[i].empty())
scanrootdir(config.cfg.bankRootDirList[i]);

//sort the banks
sort(banks.begin(), banks.end());

//remove duplicate bank names
int dupl = 0;
for(int j = 0; j < (int) banks.size() - 1; ++j)
for(int i = j + 1; i < (int) banks.size(); ++i) {
if(banks[i].name == banks[j].name) {
//add a [1] to the first bankname and [n] to others
banks[i].name = banks[i].name + '['
+ stringFrom(dupl + 2) + ']';
if(dupl == 0)
banks[j].name += "[1]";

dupl++;
}
else
dupl = 0;
}
}


// private stuff

void Bank::scanrootdir(string rootdir)
{
DIR *dir = opendir(rootdir.c_str());
if(dir == NULL)
return;

bankstruct bank;

const char *separator = "/";
if(rootdir.size()) {
char tmp = rootdir[rootdir.size() - 1];
if((tmp == '/') || (tmp == '\\'))
separator = "";
}

struct dirent *fn;
while((fn = readdir(dir))) {
const char *dirname = fn->d_name;
if(dirname[0] == '.')
continue;

bank.dir = rootdir + separator + dirname + '/';
bank.name = dirname;
//find out if the directory contains at least 1 instrument
bool isbank = false;

DIR *d = opendir(bank.dir.c_str());
if(d == NULL)
continue;

struct dirent *fname;

while((fname = readdir(d))) {
if((strstr(fname->d_name, INSTRUMENT_EXTENSION) != NULL)
|| (strstr(fname->d_name, FORCE_BANK_DIR_FILE) != NULL)) {
isbank = true;
break; //could put a #instrument counter here instead
}
}

if(isbank)
banks.push_back(bank);

closedir(d);
}

closedir(dir);
}

void Bank::clearbank()
{
for(int i = 0; i < BANK_SIZE; ++i)
ins[i] = ins_t();

bankfiletitle.clear();
dirname.clear();
}

int Bank::addtobank(int pos, string filename, string name)
{
if((pos >= 0) && (pos < BANK_SIZE)) {
if(ins[pos].used)
pos = -1; //force it to find a new free position
}
else
if(pos >= BANK_SIZE)
pos = -1;


if(pos < 0) //find a free position
for(int i = BANK_SIZE - 1; i >= 0; i--)
if(!ins[i].used) {
pos = i;
break;
}

if(pos < 0)
return -1; //the bank is full

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;

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 >= (int) banks.size()))
return;
ins[pos] = ins_t();
}

Bank::ins_t::ins_t()
:used(false), name(""), filename("")
{
info.PADsynth_used = false;
}

+ 103
- 0
c++/carla-backend/plugins/zynaddsubfx/Misc/Bank.h View File

@@ -0,0 +1,103 @@
/*
ZynAddSubFX - a software synthesizer

Bank.h - Instrument Bank
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 BANK_H
#define BANK_H

#include <string>
#include <vector>

//entries in a bank
#define BANK_SIZE 160

/**The instrument Bank*/
class Bank
{
public:
/**Constructor*/
Bank();
~Bank();
std::string getname(unsigned int ninstrument);
std::string getnamenumbered(unsigned int ninstrument);
void 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);

/**returns true when slot is empty*/
bool emptyslot(unsigned int ninstrument);

/**Empties out the selected slot*/
void clearslot(unsigned int ninstrument);
/**Saves the given Part to slot*/
void savetoslot(unsigned int ninstrument, class Part * part);
/**Loads the given slot into a Part*/
void loadfromslot(unsigned int ninstrument, class Part * part);

/**Swaps Slots*/
void swapslot(unsigned int n1, unsigned int n2);

int loadbank(std::string bankdirname);
int newbank(std::string newbankdirname);

std::string bankfiletitle; //this is shown on the UI of the bank (the title of the window)
int locked();

void rescanforbanks();

struct bankstruct {
bool operator<(const bankstruct &b) const;
std::string dir;
std::string name;
};

std::vector<bankstruct> banks;

private:

//it adds a filename to the bank
//if pos is -1 it try to find a position
//returns -1 if the bank is full, or 0 if the instrument was added
int addtobank(int pos, std::string filename, std::string name);

void deletefrombank(int pos);

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
};

#endif

+ 28
- 0
c++/carla-backend/plugins/zynaddsubfx/Misc/CMakeLists.txt View File

@@ -0,0 +1,28 @@
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
Misc/Util.cpp
Misc/XMLwrapper.cpp
Misc/Recorder.cpp
Misc/WavFile.cpp
Misc/WaveShapeSmps.cpp
)



if(LashEnable)
set(zynaddsubfx_misc_SRCS
${zynaddsubfx_misc_SRCS}
Misc/LASHClient.cpp
PARENT_SCOPE)
else()
set(zynaddsubfx_misc_SRCS
${zynaddsubfx_misc_SRCS}
PARENT_SCOPE)
endif()

+ 300
- 0
c++/carla-backend/plugins/zynaddsubfx/Misc/Config.cpp View File

@@ -0,0 +1,300 @@
/*
ZynAddSubFX - a software synthesizer

Config.cpp - Configuration file functions
Copyright (C) 2003-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 <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <string.h>


#include "Config.h"
#include "XMLwrapper.h"

using namespace std;

Config::Config()
{}
void Config::init()
{
maxstringsize = MAX_STRING_SIZE; //for ui
//defaults
cfg.SampleRate = 44100;
cfg.SoundBufferSize = 256;
cfg.OscilSize = 1024;
cfg.SwapStereo = 0;

cfg.LinuxOSSWaveOutDev = new char[MAX_STRING_SIZE];
snprintf(cfg.LinuxOSSWaveOutDev, MAX_STRING_SIZE, "/dev/dsp");
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;

cfg.Interpolation = 0;
cfg.CheckPADsynth = 1;

cfg.UserInterfaceMode = 0;
cfg.VirKeybLayout = 1;
winwavemax = 1;
winmidimax = 1;
//try to find out how many input midi devices are there
winmididevices = new winmidionedevice[winmidimax];
for(int i = 0; i < winmidimax; ++i) {
winmididevices[i].name = new char[MAX_STRING_SIZE];
for(int j = 0; j < MAX_STRING_SIZE; ++j)
winmididevices[i].name[j] = '\0';
}


//get the midi input devices name
cfg.currentBankDir = "./testbnk";

char filename[MAX_STRING_SIZE];
getConfigFileName(filename, MAX_STRING_SIZE);
readConfig(filename);

if(cfg.bankRootDirList[0].empty()) {
//banks
cfg.bankRootDirList[0] = "~/banks";
cfg.bankRootDirList[1] = "./";
cfg.bankRootDirList[2] = "/usr/share/zynaddsubfx/banks";
cfg.bankRootDirList[3] = "/usr/local/share/zynaddsubfx/banks";
cfg.bankRootDirList[4] = "../banks";
cfg.bankRootDirList[5] = "banks";
}

if(cfg.presetsDirList[0].empty()) {
//presets
cfg.presetsDirList[0] = "./";
cfg.presetsDirList[1] = "../presets";
cfg.presetsDirList[2] = "presets";
cfg.presetsDirList[3] = "/usr/share/zynaddsubfx/presets";
cfg.presetsDirList[4] = "/usr/local/share/zynaddsubfx/presets";
}
cfg.LinuxALSAaudioDev = "default";
cfg.nameTag = "";
}

Config::~Config()
{
delete [] cfg.LinuxOSSWaveOutDev;
delete [] cfg.LinuxOSSSeqInDev;

for(int i = 0; i < winmidimax; ++i)
delete [] winmididevices[i].name;
delete [] winmididevices;
}


void Config::save()
{
char filename[MAX_STRING_SIZE];
getConfigFileName(filename, MAX_STRING_SIZE);
saveConfig(filename);
}

void Config::clearbankrootdirlist()
{
for(int i = 0; i < MAX_BANK_ROOT_DIRS; ++i)
cfg.bankRootDirList[i].clear();
}

void Config::clearpresetsdirlist()
{
for(int i = 0; i < MAX_BANK_ROOT_DIRS; ++i)
cfg.presetsDirList[i].clear();
}

void Config::readConfig(const char *filename)
{
XMLwrapper xmlcfg;
if(xmlcfg.loadXMLfile(filename) < 0)
return;
if(xmlcfg.enterbranch("CONFIGURATION")) {
cfg.SampleRate = xmlcfg.getpar("sample_rate",
cfg.SampleRate,
4000,
1024000);
cfg.SoundBufferSize = xmlcfg.getpar("sound_buffer_size",
cfg.SoundBufferSize,
16,
8192);
cfg.OscilSize = xmlcfg.getpar("oscil_size",
cfg.OscilSize,
MAX_AD_HARMONICS * 2,
131072);
cfg.SwapStereo = xmlcfg.getpar("swap_stereo",
cfg.SwapStereo,
0,
1);
cfg.BankUIAutoClose = xmlcfg.getpar("bank_window_auto_close",
cfg.BankUIAutoClose,
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,
9);

cfg.currentBankDir = xmlcfg.getparstr("bank_current", "");
cfg.Interpolation = xmlcfg.getpar("interpolation",
cfg.Interpolation,
0,
1);

cfg.CheckPADsynth = xmlcfg.getpar("check_pad_synth",
cfg.CheckPADsynth,
0,
1);


cfg.UserInterfaceMode = xmlcfg.getpar("user_interface_mode",
cfg.UserInterfaceMode,
0,
2);
cfg.VirKeybLayout = xmlcfg.getpar("virtual_keyboard_layout",
cfg.VirKeybLayout,
0,
10);

//get bankroot dirs
for(int i = 0; i < MAX_BANK_ROOT_DIRS; ++i)
if(xmlcfg.enterbranch("BANKROOT", i)) {
cfg.bankRootDirList[i] = xmlcfg.getparstr("bank_root", "");
xmlcfg.exitbranch();
}

//get preset root dirs
for(int i = 0; i < MAX_BANK_ROOT_DIRS; ++i)
if(xmlcfg.enterbranch("PRESETSROOT", i)) {
cfg.presetsDirList[i] = xmlcfg.getparstr("presets_root", "");
xmlcfg.exitbranch();
}

//linux stuff
xmlcfg.getparstr("linux_oss_wave_out_dev",
cfg.LinuxOSSWaveOutDev,
MAX_STRING_SIZE);
xmlcfg.getparstr("linux_oss_seq_in_dev",
cfg.LinuxOSSSeqInDev,
MAX_STRING_SIZE);

//windows stuff
cfg.WindowsWaveOutId = xmlcfg.getpar("windows_wave_out_id",
cfg.WindowsWaveOutId,
0,
winwavemax);
cfg.WindowsMidiInId = xmlcfg.getpar("windows_midi_in_id",
cfg.WindowsMidiInId,
0,
winmidimax);

xmlcfg.exitbranch();
}

cfg.OscilSize = (int) powf(2, ceil(logf(cfg.OscilSize - 1.0f) / logf(2.0f)));
}

void Config::saveConfig(const char *filename)
{
XMLwrapper *xmlcfg = new XMLwrapper();

xmlcfg->beginbranch("CONFIGURATION");

xmlcfg->addpar("sample_rate", cfg.SampleRate);
xmlcfg->addpar("sound_buffer_size", cfg.SoundBufferSize);
xmlcfg->addpar("oscil_size", cfg.OscilSize);
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);

xmlcfg->addparstr("bank_current", cfg.currentBankDir);

xmlcfg->addpar("user_interface_mode", cfg.UserInterfaceMode);
xmlcfg->addpar("virtual_keyboard_layout", cfg.VirKeybLayout);


for(int i = 0; i < MAX_BANK_ROOT_DIRS; ++i)
if(!cfg.bankRootDirList[i].empty()) {
xmlcfg->beginbranch("BANKROOT", i);
xmlcfg->addparstr("bank_root", cfg.bankRootDirList[i]);
xmlcfg->endbranch();
}

for(int i = 0; i < MAX_BANK_ROOT_DIRS; ++i)
if(!cfg.presetsDirList[i].empty()) {
xmlcfg->beginbranch("PRESETSROOT", i);
xmlcfg->addparstr("presets_root", cfg.presetsDirList[i]);
xmlcfg->endbranch();
}

xmlcfg->addpar("interpolation", cfg.Interpolation);

//linux stuff
xmlcfg->addparstr("linux_oss_wave_out_dev", cfg.LinuxOSSWaveOutDev);
xmlcfg->addparstr("linux_oss_seq_in_dev", cfg.LinuxOSSSeqInDev);

//windows stuff
xmlcfg->addpar("windows_wave_out_id", cfg.WindowsWaveOutId);
xmlcfg->addpar("windows_midi_in_id", cfg.WindowsMidiInId);

xmlcfg->endbranch();

int tmp = cfg.GzipCompression;
cfg.GzipCompression = 0;
xmlcfg->saveXMLfile(filename);
cfg.GzipCompression = tmp;

delete (xmlcfg);
}

void Config::getConfigFileName(char *name, int namesize)
{
name[0] = 0;
snprintf(name, namesize, "%s%s", getenv("HOME"), "/.zynaddsubfxXML.cfg");
}

+ 73
- 0
c++/carla-backend/plugins/zynaddsubfx/Misc/Config.h View File

@@ -0,0 +1,73 @@
/*
ZynAddSubFX - a software synthesizer

Config.h - Configuration file functions
Copyright (C) 2003-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 CONFIG_H
#define CONFIG_H
#include "../globals.h"
#include <string>
#define MAX_STRING_SIZE 4000
#define MAX_BANK_ROOT_DIRS 100

/**Configuration file functions*/
class Config
{
public:
/** Constructor*/
Config();
/** Destructor*/
~Config();
struct {
char *LinuxOSSWaveOutDev, *LinuxOSSSeqInDev;
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;
int UserInterfaceMode;
int VirKeybLayout;
std::string LinuxALSAaudioDev;
std::string nameTag;
} cfg;
int winwavemax, winmidimax; //number of wave/midi devices on Windows
int maxstringsize;

struct winmidionedevice {
char *name;
};
winmidionedevice *winmididevices;

void clearbankrootdirlist();
void clearpresetsdirlist();
void init();
void save();

private:
void readConfig(const char *filename);
void saveConfig(const char *filename);
void getConfigFileName(char *name, int namesize);
};
#endif

+ 99
- 0
c++/carla-backend/plugins/zynaddsubfx/Misc/Control.h View File

@@ -0,0 +1,99 @@
/*
ZynAddSubFX - a software synthesizer

Control.h - Defines a variable that can be controled from a frontend

Copyright (C) 2009 Harald Hvaal
Author: Harald Hvaal

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 _CONTROL_H_
#define _CONTROL_H_

#include <string>

class Control
{
public:
/**
* The parent is the logical owner of this control. Parent should only
* be null for the root node.
* The id is a string uniquely identifying this control within the
* context of the parent control. No spaces or dots are allowed in this
* id.
* Children id's are denoted by <parent-id>.<children-id>, so that one
* can refer to any control in the hierarchy by separating them with
* dots. Example: Main.AddSynth.FrequencyLFO.Amplitude
*/
Control(Control *parent, string id);

/**
* Will recursively get the XML representation for all the subcontrols.
* Used for saving to file and copy-pasting settings
*/
string getXMLRepresentation();

/**
* Set the value of this (and possibly subcomponents as well) based on
* a xml description.
*/
void restoreFromXML(string xml);

/**
* Register a controluser. This will cause this user to be notified
* whenever the contents of the control changes.
*/
void registerControlUser(ControlUser *user);

/**
* This should return a string representation of the controls internal
* value
*/
virtual string getStringRepresentation() = 0;
};

class FloatControl:public Control
{
public:
/**
* Set the value of this control. If the ControlUser variable is set,
* then this user will not be updated with the new value. This is to
* avoid setting a value being set back to the source that set it
* (which would be redundant, or possibly causing infinite setValue
* loops).
* NOTE: this function is thread-safe (using a mutex internally)
*/
void setValue(float value, ControlUser *user = NULL);

/**
* Reimplemented from Control
*/
virtual string getStringRepresentation();

float value();
};

class ControlUser
{
public:
/**
* Pure virtual method, to notify the controluser that the value has
* been changed internally, and needs to be read again.
*/
virtual void controlUpdated(Control *control) = 0;
};

#endif /* _CONTROL_H_ */

+ 121
- 0
c++/carla-backend/plugins/zynaddsubfx/Misc/Dump.cpp View File

@@ -0,0 +1,121 @@
/*
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
}

+ 63
- 0
c++/carla-backend/plugins/zynaddsubfx/Misc/Dump.h View File

@@ -0,0 +1,63 @@
/*
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

+ 103
- 0
c++/carla-backend/plugins/zynaddsubfx/Misc/LASHClient.cpp View File

@@ -0,0 +1,103 @@
/*
ZynAddSubFX - a software synthesizer

LASHClient.cpp - LASH support
Copyright (C) 2006-2009 Lars Luthman
Author: Lars Luthman

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 <unistd.h>
#include <iostream>
#include <string>

#include "LASHClient.h"


LASHClient::LASHClient(int *argc, char ***argv)
{
client = lash_init(lash_extract_args(argc, argv), "ZynAddSubFX",
LASH_Config_File, LASH_PROTOCOL(2, 0));
}


void LASHClient::setalsaid(int id)
{
if(lash_enabled(client))
if(id != -1)
lash_alsa_client_id(client, id);
}


void LASHClient::setjackname(const char *name)
{
if(lash_enabled(client))
if(name != NULL) {
lash_jack_client_name(client, name);

lash_event_t *event = lash_event_new_with_type(LASH_Client_Name);
lash_event_set_string(event, name);
lash_send_event(client, event);
}
}


LASHClient::Event LASHClient::checkevents(std::string &filename)
{
if(!lash_enabled(client))
return NoEvent;

Event received = NoEvent;
lash_event_t *event;
while((event = lash_get_event(client))) {
// save
if(lash_event_get_type(event) == LASH_Save_File) {
std::cerr << "LASH event: LASH_Save_File" << std::endl;
filename = std::string(lash_event_get_string(event))
+ "/master.xmz";
received = Save;
break;
}
// restore
else
if(lash_event_get_type(event) == LASH_Restore_File) {
std::cerr << "LASH event: LASH_Restore_File" << std::endl;
filename = std::string(lash_event_get_string(event))
+ "/master.xmz";
received = Restore;
break;
}
// quit
else
if(lash_event_get_type(event) == LASH_Quit) {
std::cerr << "LASH event: LASH_Quit" << std::endl;
received = Quit;
break;
}

lash_event_destroy(event);
}
return received;
}


void LASHClient::confirmevent(Event event)
{
if(event == Save)
lash_send_event(client, lash_event_new_with_type(LASH_Save_File));
else
if(event == Restore)
lash_send_event(client, lash_event_new_with_type(LASH_Restore_File));
}

+ 63
- 0
c++/carla-backend/plugins/zynaddsubfx/Misc/LASHClient.h View File

@@ -0,0 +1,63 @@
/*
ZynAddSubFX - a software synthesizer

LASHClient.h - LASH support
Copyright (C) 2006-2009 Lars Luthman
Author: Lars Luthman

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 LASHClient_h
#define LASHClient_h

#include <string>
#include <pthread.h>
#include <lash/lash.h>


/** This class wraps up some functions for initialising and polling
* the LASH daemon.*/
class LASHClient
{
public:
/**Enum to represent the LASH events that are currently handled*/
enum Event {
Save,
Restore,
Quit,
NoEvent
};

/** Constructor
* @param argc number of arguments
* @param argv the text arguments*/
LASHClient(int *argc, char ***argv);

/**set the ALSA id
* @param id new ALSA id*/
void setalsaid(int id);
/**Set the JACK name
* @param name the new name*/
void setjackname(const char *name);
Event checkevents(std::string &filename);
void confirmevent(Event event);

private:

lash_client_t *client;
};


#endif

+ 798
- 0
c++/carla-backend/plugins/zynaddsubfx/Misc/Master.cpp View File

@@ -0,0 +1,798 @@
/*
ZynAddSubFX - a software synthesizer

Master.cpp - It sends Midi Messages to Parts, receives samples from parts,
process them with system/insertion effects and mix them
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 "Master.h"

#include "Part.h"

#include "../Params/LFOParams.h"
#include "../Effects/EffectMgr.h"
#include "../DSP/FFTwrapper.h"

#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <iostream>
#include <algorithm>
#include <cmath>

#include <unistd.h>

using namespace std;

vuData::vuData(void)
:outpeakl(0.0f), outpeakr(0.0f), maxoutpeakl(0.0f), maxoutpeakr(0.0f),
rmspeakl(0.0f), rmspeakr(0.0f), clipped(0)
{}

Master::Master()
{
swaplr = 0;

pthread_mutex_init(&mutex, NULL);
pthread_mutex_init(&vumutex, NULL);
fft = new FFTwrapper(synth->oscilsize);

shutup = 0;
for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart) {
vuoutpeakpart[npart] = 1e-9;
fakepeakpart[npart] = 0;
}

for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart)
part[npart] = new Part(&microtonal, fft, &mutex);

//Insertion Effects init
for(int nefx = 0; nefx < NUM_INS_EFX; ++nefx)
insefx[nefx] = new EffectMgr(1, &mutex);

//System Effects init
for(int nefx = 0; nefx < NUM_SYS_EFX; ++nefx)
sysefx[nefx] = new EffectMgr(0, &mutex);


defaults();
}

void Master::defaults()
{
volume = 1.0f;
setPvolume(80);
setPkeyshift(64);

for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart) {
part[npart]->defaults();
part[npart]->Prcvchn = npart % NUM_MIDI_CHANNELS;
}

partonoff(0, 1); //enable the first part

for(int nefx = 0; nefx < NUM_INS_EFX; ++nefx) {
insefx[nefx]->defaults();
Pinsparts[nefx] = -1;
}

//System Effects init
for(int nefx = 0; nefx < NUM_SYS_EFX; ++nefx) {
sysefx[nefx]->defaults();
for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart)
setPsysefxvol(npart, nefx, 0);

for(int nefxto = 0; nefxto < NUM_SYS_EFX; ++nefxto)
setPsysefxsend(nefx, nefxto, 0);
}

microtonal.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()
{
static Master *instance = NULL;
if(!instance)
instance = new Master;

return *instance;
}

/*
* Note On Messages (velocity=0 for NoteOff)
*/
void Master::noteOn(char chan, char note, char velocity)
{
if(velocity) {
for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart)
if(chan == part[npart]->Prcvchn) {
fakepeakpart[npart] = velocity * 2;
if(part[npart]->Penabled)
part[npart]->NoteOn(note, velocity, keyshift);
}
}
else
this->noteOff(chan, note);
HDDRecorder.triggernow();
}

/*
* Note Off Messages
*/
void Master::noteOff(char chan, char note)
{
for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart)
if((chan == part[npart]->Prcvchn) && part[npart]->Penabled)
part[npart]->NoteOff(note);
}

/*
* Pressure Messages (velocity=0 for NoteOff)
*/
void Master::polyphonicAftertouch(char chan, char note, char velocity)
{
if(velocity) {
for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart)
if(chan == part[npart]->Prcvchn)
if(part[npart]->Penabled)
part[npart]->PolyphonicAftertouch(note, velocity, keyshift);

}
else
this->noteOff(chan, note);
}

/*
* Controllers
*/
void Master::setController(char chan, int type, int 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);
;
break;
case 0x08: //Insertion Effects
if(parlo < NUM_INS_EFX)
insefx[parlo]->seteffectpar_nolock(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);
}
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)
sysefx[nefx]->cleanup();
for(int nefx = 0; nefx < NUM_INS_EFX; ++nefx)
insefx[nefx]->cleanup();
}
}
}

void Master::setProgram(char chan, unsigned int pgm)
{
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) {
if(fabs(outl[i]) > vu.outpeakl)
vu.outpeakl = fabs(outl[i]);
if(fabs(outr[i]) > vu.outpeakr)
vu.outpeakr = fabs(outr[i]);
}
if((vu.outpeakl > 1.0f) || (vu.outpeakr > 1.0f))
vu.clipped = 1;
if(vu.maxoutpeakl < vu.outpeakl)
vu.maxoutpeakl = vu.outpeakl;
if(vu.maxoutpeakr < vu.outpeakr)
vu.maxoutpeakr = vu.outpeakr;

//RMS Peak computation (for vumeters)
vu.rmspeakl = 1e-12;
vu.rmspeakr = 1e-12;
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);

//Part Peak computation (for Part vumeters or fake part vumeters)
for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart) {
vuoutpeakpart[npart] = 1.0e-12f;
if(part[npart]->Penabled != 0) {
float *outl = part[npart]->partoutl,
*outr = part[npart]->partoutr;
for(int i = 0; i < synth->buffersize; ++i) {
float tmp = fabs(outl[i] + outr[i]);
if(tmp > vuoutpeakpart[npart])
vuoutpeakpart[npart] = tmp;
}
vuoutpeakpart[npart] *= volume;
}
else
if(fakepeakpart[npart] > 1)
fakepeakpart[npart]--;
}
}

/*
* Enable/Disable a part
*/
void Master::partonoff(int npart, int what)
{
if(npart >= NUM_MIDI_PARTS)
return;
if(what == 0) { //disable part
fakepeakpart[npart] = 0;
part[npart]->Penabled = 0;
part[npart]->cleanup();
for(int nefx = 0; nefx < NUM_INS_EFX; ++nefx) {
if(Pinsparts[nefx] == npart)
insefx[nefx]->cleanup();
;
}
}
else { //enabled
part[npart]->Penabled = 1;
fakepeakpart[npart] = 0;
}
}

/*
* Master audio out (the final sound)
*/
void Master::AudioOut(float *outl, float *outr)
{
//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);

//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)) {
part[npart]->ComputePartSmps();
pthread_mutex_unlock(&part[npart]->load_mutex);
}
}

//Insertion effects
for(int nefx = 0; nefx < NUM_INS_EFX; ++nefx)
if(Pinsparts[nefx] >= 0) {
int efxpart = Pinsparts[nefx];
if(part[efxpart]->Penabled)
insefx[nefx]->out(part[efxpart]->partoutl,
part[efxpart]->partoutr);
}


//Apply the part volumes and pannings (after insertion effects)
for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart) {
if(part[npart]->Penabled == 0)
continue;

Stereo<float> newvol(part[npart]->volume),
oldvol(part[npart]->oldvolumel,
part[npart]->oldvolumer);

float pan = part[npart]->panning;
if(pan < 0.5f)
newvol.l *= pan * 2.0f;
else
newvol.r *= (1.0f - pan) * 2.0f;

//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) {
Stereo<float> vol(INTERPOLATE_AMPLITUDE(oldvol.l, newvol.l,
i, synth->buffersize),
INTERPOLATE_AMPLITUDE(oldvol.r, newvol.r,
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
part[npart]->partoutl[i] *= newvol.l;
part[npart]->partoutr[i] *= newvol.r;
}
}


//System effects
for(int nefx = 0; nefx < NUM_SYS_EFX; ++nefx) {
if(sysefx[nefx]->geteffect() == 0)
continue; //the effect is disabled

float *tmpmixl = getTmpBuffer();
float *tmpmixr = getTmpBuffer();
//Clean up the samples used by the system effects
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) {
//skip if the part has no output to effect
if(Psysefxvol[nefx][npart] == 0)
continue;

//skip if the part is disabled
if(part[npart]->Penabled == 0)
continue;

//the output volume of each part to system effect
const float vol = sysefxvol[nefx][npart];
for(int i = 0; i < synth->buffersize; ++i) {
tmpmixl[i] += part[npart]->partoutl[i] * vol;
tmpmixr[i] += part[npart]->partoutr[i] * vol;
}
}

// system effect send to next ones
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) {
tmpmixl[i] += sysefx[nefxfrom]->efxoutl[i] * vol;
tmpmixr[i] += sysefx[nefxfrom]->efxoutr[i] * vol;
}
}

sysefx[nefx]->out(tmpmixl, tmpmixr);

//Add the System Effect to sound output
const float outvol = sysefx[nefx]->sysefxgetvolume();
for(int i = 0; i < synth->buffersize; ++i) {
outl[i] += tmpmixl[i] * outvol;
outr[i] += tmpmixr[i] * outvol;
}

returnTmpBuffer(tmpmixl);
returnTmpBuffer(tmpmixr);
}

//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
outl[i] += part[npart]->partoutl[i];
outr[i] += part[npart]->partoutr[i];
}

//Insertion effects for Master Out
for(int nefx = 0; nefx < NUM_INS_EFX; ++nefx)
if(Pinsparts[nefx] == -2)
insefx[nefx]->out(outl, outr);


//Master Volume
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);
}

//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;
outl[i] *= tmp;
outr[i] *= tmp;
}
ShutUp();
}

//update the LFO's time
LFOParams::time++;

dump.inctick();
}

//TODO review the respective code from yoshimi for this
//If memory serves correctly, libsamplerate was used
void Master::GetAudioOutSamples(size_t nsamples,
unsigned samplerate,
float *outl,
float *outr)
{
static float *bufl = new float[synth->buffersize],
*bufr = new float[synth->buffersize];
static off_t off = 0;
static size_t smps = 0;

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);
return;
}

while(nsamples) {
//use all available samples
if(nsamples >= smps) {
memcpy(outl + out_off, bufl + off, sizeof(float) * smps);
memcpy(outr + out_off, bufr + off, sizeof(float) * smps);

//generate samples
AudioOut(bufl, bufr);
off = 0;
smps = synth->buffersize;

out_off += smps;
nsamples -= smps;
}
else { //use some samples
memcpy(outl + out_off, bufl + off, sizeof(float) * nsamples);
memcpy(outr + out_off, bufr + off, sizeof(float) * nsamples);
smps -= nsamples;
off += nsamples;
nsamples = 0;
}
}
}

Master::~Master()
{
for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart)
delete part[npart];
for(int nefx = 0; nefx < NUM_INS_EFX; ++nefx)
delete insefx[nefx];
for(int nefx = 0; nefx < NUM_SYS_EFX; ++nefx)
delete sysefx[nefx];

delete fft;
FFT_cleanup();

pthread_mutex_destroy(&mutex);
pthread_mutex_destroy(&vumutex);
}


/*
* Parameter control
*/
void Master::setPvolume(char Pvolume_)
{
Pvolume = Pvolume_;
volume = dB2rap((Pvolume - 96.0f) / 96.0f * 40.0f);
}

void Master::setPkeyshift(char Pkeyshift_)
{
Pkeyshift = Pkeyshift_;
keyshift = (int)Pkeyshift - 64;
}


void Master::setPsysefxvol(int Ppart, int Pefx, char Pvol)
{
Psysefxvol[Pefx][Ppart] = Pvol;
sysefxvol[Pefx][Ppart] = powf(0.1f, (1.0f - Pvol / 96.0f) * 2.0f);
}

void Master::setPsysefxsend(int Pefxfrom, int Pefxto, char Pvol)
{
Psysefxsend[Pefxfrom][Pefxto] = Pvol;
sysefxsend[Pefxfrom][Pefxto] = powf(0.1f, (1.0f - Pvol / 96.0f) * 2.0f);
}


/*
* Panic! (Clean up all parts and effects)
*/
void Master::ShutUp()
{
for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart) {
part[npart]->cleanup();
fakepeakpart[npart] = 0;
}
for(int nefx = 0; nefx < NUM_INS_EFX; ++nefx)
insefx[nefx]->cleanup();
for(int nefx = 0; nefx < NUM_SYS_EFX; ++nefx)
sysefx[nefx]->cleanup();
vuresetpeaks();
shutup = 0;
}


/*
* Reset peaks and clear the "cliped" flag (for VU-meter)
*/
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()
{
vuData tmp;
pthread_mutex_lock(&vumutex);
tmp = vu;
pthread_mutex_unlock(&vumutex);
return tmp;
}

void Master::applyparameters(bool lockmutex)
{
for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart)
part[npart]->applyparameters(lockmutex);
}

void Master::add2XML(XMLwrapper *xml)
{
xml->addpar("volume", Pvolume);
xml->addpar("key_shift", Pkeyshift);
xml->addparbool("nrpn_receive", ctl.NRPN.receive);

xml->beginbranch("MICROTONAL");
microtonal.add2XML(xml);
xml->endbranch();

for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart) {
xml->beginbranch("PART", npart);
part[npart]->add2XML(xml);
xml->endbranch();
}

xml->beginbranch("SYSTEM_EFFECTS");
for(int nefx = 0; nefx < NUM_SYS_EFX; ++nefx) {
xml->beginbranch("SYSTEM_EFFECT", nefx);
xml->beginbranch("EFFECT");
sysefx[nefx]->add2XML(xml);
xml->endbranch();

for(int pefx = 0; pefx < NUM_MIDI_PARTS; ++pefx) {
xml->beginbranch("VOLUME", pefx);
xml->addpar("vol", Psysefxvol[nefx][pefx]);
xml->endbranch();
}

for(int tonefx = nefx + 1; tonefx < NUM_SYS_EFX; ++tonefx) {
xml->beginbranch("SENDTO", tonefx);
xml->addpar("send_vol", Psysefxsend[nefx][tonefx]);
xml->endbranch();
}


xml->endbranch();
}
xml->endbranch();

xml->beginbranch("INSERTION_EFFECTS");
for(int nefx = 0; nefx < NUM_INS_EFX; ++nefx) {
xml->beginbranch("INSERTION_EFFECT", nefx);
xml->addpar("part", Pinsparts[nefx]);

xml->beginbranch("EFFECT");
insefx[nefx]->add2XML(xml);
xml->endbranch();
xml->endbranch();
}

xml->endbranch();
}


int Master::getalldata(char **data)
{
XMLwrapper *xml = new XMLwrapper();

xml->beginbranch("MASTER");

pthread_mutex_lock(&mutex);
add2XML(xml);
pthread_mutex_unlock(&mutex);

xml->endbranch();

*data = xml->getXMLdata();
delete (xml);
return strlen(*data) + 1;
}

void Master::putalldata(char *data, int /*size*/)
{
XMLwrapper *xml = new XMLwrapper();
if(!xml->putXMLdata(data)) {
delete (xml);
return;
}

if(xml->enterbranch("MASTER") == 0)
return;

pthread_mutex_lock(&mutex);
getfromXML(xml);
pthread_mutex_unlock(&mutex);

xml->exitbranch();

delete (xml);
}

int Master::saveXML(const char *filename)
{
XMLwrapper *xml = new XMLwrapper();

xml->beginbranch("MASTER");
add2XML(xml);
xml->endbranch();

int result = xml->saveXMLfile(filename);
delete (xml);
return result;
}



int Master::loadXML(const char *filename)
{
XMLwrapper *xml = new XMLwrapper();
if(xml->loadXMLfile(filename) < 0) {
delete (xml);
return -1;
}

if(xml->enterbranch("MASTER") == 0)
return -10;
getfromXML(xml);
xml->exitbranch();

delete (xml);
return 0;
}

void Master::getfromXML(XMLwrapper *xml)
{
setPvolume(xml->getpar127("volume", Pvolume));
setPkeyshift(xml->getpar127("key_shift", Pkeyshift));
ctl.NRPN.receive = xml->getparbool("nrpn_receive", ctl.NRPN.receive);


part[0]->Penabled = 0;
for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart) {
if(xml->enterbranch("PART", npart) == 0)
continue;
part[npart]->getfromXML(xml);
xml->exitbranch();
}

if(xml->enterbranch("MICROTONAL")) {
microtonal.getfromXML(xml);
xml->exitbranch();
}

sysefx[0]->changeeffect(0);
if(xml->enterbranch("SYSTEM_EFFECTS")) {
for(int nefx = 0; nefx < NUM_SYS_EFX; ++nefx) {
if(xml->enterbranch("SYSTEM_EFFECT", nefx) == 0)
continue;
if(xml->enterbranch("EFFECT")) {
sysefx[nefx]->getfromXML(xml);
xml->exitbranch();
}

for(int partefx = 0; partefx < NUM_MIDI_PARTS; ++partefx) {
if(xml->enterbranch("VOLUME", partefx) == 0)
continue;
setPsysefxvol(partefx, nefx,
xml->getpar127("vol", Psysefxvol[partefx][nefx]));
xml->exitbranch();
}

for(int tonefx = nefx + 1; tonefx < NUM_SYS_EFX; ++tonefx) {
if(xml->enterbranch("SENDTO", tonefx) == 0)
continue;
setPsysefxsend(nefx, tonefx,
xml->getpar127("send_vol",
Psysefxsend[nefx][tonefx]));
xml->exitbranch();
}
xml->exitbranch();
}
xml->exitbranch();
}


if(xml->enterbranch("INSERTION_EFFECTS")) {
for(int nefx = 0; nefx < NUM_INS_EFX; ++nefx) {
if(xml->enterbranch("INSERTION_EFFECT", nefx) == 0)
continue;
Pinsparts[nefx] = xml->getpar("part",
Pinsparts[nefx],
-2,
NUM_MIDI_PARTS);
if(xml->enterbranch("EFFECT")) {
insefx[nefx]->getfromXML(xml);
xml->exitbranch();
}
xml->exitbranch();
}

xml->exitbranch();
}
}

+ 175
- 0
c++/carla-backend/plugins/zynaddsubfx/Misc/Master.h View File

@@ -0,0 +1,175 @@
/*
ZynAddSubFX - a software synthesizer

Master.h - It sends Midi Messages to Parts, receives samples from parts,
process them with system/insertion effects and mix them
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 MASTER_H
#define MASTER_H
#include "../globals.h"
#include "Microtonal.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;

struct vuData {
vuData(void);
float outpeakl, outpeakr, maxoutpeakl, maxoutpeakr,
rmspeakl, rmspeakr;
int clipped;
};


/** It sends Midi Messages to Parts, receives samples from parts,
* process them with system/insertion effects and mix them */
class Master
{
public:
/** Constructor TODO make private*/
Master();
/** Destructor*/
~Master();

static Master &getInstance();

/**Saves all settings to a XML file
* @return 0 for ok or <0 if there is an error*/
int saveXML(const char *filename);

/**This adds the parameters to the XML data*/
void add2XML(XMLwrapper *xml);

void defaults();


/**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);

void getfromXML(XMLwrapper *xml);

/**get all data to a newly allocated array (used for VST)
* @return the datasize*/
int getalldata(char **data);
/**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...


void ShutUp();
int shutup;

void vuUpdate(const float *outl, const float *outr);

/**Audio Output*/
void AudioOut(float *outl, float *outr);
/**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);


void partonoff(int npart, int what);

/**parts \todo see if this can be made to be dynamic*/
class Part * part[NUM_MIDI_PARTS];

//parameters

unsigned char Pvolume;
unsigned char Pkeyshift;
unsigned char Psysefxvol[NUM_SYS_EFX][NUM_MIDI_PARTS];
unsigned char Psysefxsend[NUM_SYS_EFX][NUM_SYS_EFX];

//parameters control
void setPvolume(char Pvolume_);
void setPkeyshift(char Pkeyshift_);
void setPsysefxvol(int Ppart, int Pefx, char Pvol);
void setPsysefxsend(int Pefxfrom, int Pefxto, char Pvol);

//effects
class EffectMgr * sysefx[NUM_SYS_EFX]; //system
class EffectMgr * insefx[NUM_INS_EFX]; //insertion
// void swapcopyeffects(int what,int type,int neff1,int neff2);

//HDD recorder
Recorder HDDRecorder;

//part that's apply the insertion effect; -1 to disable
short int Pinsparts[NUM_INS_EFX];


//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

Controller ctl;
bool swaplr; //if L and R are swapped

//other objects
Microtonal microtonal;
Bank bank;

class FFTwrapper * fft;
pthread_mutex_t mutex;
pthread_mutex_t vumutex;


private:
bool nullRun;
vuData vu;
float volume;
float sysefxvol[NUM_SYS_EFX][NUM_MIDI_PARTS];
float sysefxsend[NUM_SYS_EFX][NUM_SYS_EFX];
int keyshift;
};

#endif

+ 694
- 0
c++/carla-backend/plugins/zynaddsubfx/Misc/Microtonal.cpp View File

@@ -0,0 +1,694 @@
/*
ZynAddSubFX - a software synthesizer

Microtonal.cpp - Tuning settings and microtonal capabilities
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 <math.h>
#include <string.h>
#include "Microtonal.h"

#define MAX_LINE_SIZE 80

Microtonal::Microtonal()
{
Pname = new unsigned char[MICROTONAL_MAX_NAME_LEN];
Pcomment = new unsigned char[MICROTONAL_MAX_NAME_LEN];
defaults();
}

void Microtonal::defaults()
{
Pinvertupdown = 0;
Pinvertupdowncenter = 60;
octavesize = 12;
Penabled = 0;
PAnote = 69;
PAfreq = 440.0f;
Pscaleshift = 64;

Pfirstkey = 0;
Plastkey = 127;
Pmiddlenote = 60;
Pmapsize = 12;
Pmappingenabled = 0;

for(int i = 0; i < 128; ++i)
Pmapping[i] = i;

for(int i = 0; i < MAX_OCTAVE_SIZE; ++i) {
octave[i].tuning = tmpoctave[i].tuning = powf(
2,
(i % octavesize
+ 1) / 12.0f);
octave[i].type = tmpoctave[i].type = 1;
octave[i].x1 = tmpoctave[i].x1 = (i % octavesize + 1) * 100;
octave[i].x2 = tmpoctave[i].x2 = 0;
}
octave[11].type = 2;
octave[11].x1 = 2;
octave[11].x2 = 1;
for(int i = 0; i < MICROTONAL_MAX_NAME_LEN; ++i) {
Pname[i] = '\0';
Pcomment[i] = '\0';
}
snprintf((char *) Pname, MICROTONAL_MAX_NAME_LEN, "12tET");
snprintf((char *) Pcomment,
MICROTONAL_MAX_NAME_LEN,
"Equal Temperament 12 notes per octave");
Pglobalfinedetune = 64;
}

Microtonal::~Microtonal()
{
delete [] Pname;
delete [] Pcomment;
}

/*
* Get the size of the octave
*/
unsigned char Microtonal::getoctavesize() const
{
if(Penabled != 0)
return octavesize;
else
return 12;
}

/*
* Get the frequency according the note number
*/
float Microtonal::getnotefreq(int note, int keyshift) const
{
// in this function will appears many times things like this:
// var=(a+b*100)%b
// I had written this way because if I use var=a%b gives unwanted results when a<0
// This is the same with divisions.

if((Pinvertupdown != 0) && ((Pmappingenabled == 0) || (Penabled == 0)))
note = (int) Pinvertupdowncenter * 2 - note;

//compute global fine detune
float globalfinedetunerap = powf(2.0f,
(Pglobalfinedetune - 64.0f) / 1200.0f); //-64.0f .. 63.0f cents

if(Penabled == 0)
return powf(2.0f,
(note - PAnote
+ keyshift) / 12.0f) * PAfreq * globalfinedetunerap; //12tET

int scaleshift =
((int)Pscaleshift - 64 + (int) octavesize * 100) % octavesize;

//compute the keyshift
float rap_keyshift = 1.0f;
if(keyshift != 0) {
int kskey = (keyshift + (int)octavesize * 100) % octavesize;
int ksoct = (keyshift + (int)octavesize * 100) / octavesize - 100;
rap_keyshift = (kskey == 0) ? (1.0f) : (octave[kskey - 1].tuning);
rap_keyshift *= powf(octave[octavesize - 1].tuning, ksoct);
}

//if the mapping is enabled
if(Pmappingenabled != 0) {
if((note < Pfirstkey) || (note > Plastkey))
return -1.0f;
//Compute how many mapped keys are from middle note to reference note
//and find out the proportion between the freq. of middle note and "A" note
int tmp = PAnote - Pmiddlenote, minus = 0;
if(tmp < 0) {
tmp = -tmp;
minus = 1;
}
int deltanote = 0;
for(int i = 0; i < tmp; ++i)
if(Pmapping[i % Pmapsize] >= 0)
deltanote++;
float rap_anote_middlenote =
(deltanote ==
0) ? (1.0f) : (octave[(deltanote - 1) % octavesize].tuning);
if(deltanote != 0)
rap_anote_middlenote *=
powf(octave[octavesize - 1].tuning,
(deltanote - 1) / octavesize);
if(minus != 0)
rap_anote_middlenote = 1.0f / rap_anote_middlenote;

//Convert from note (midi) to degree (note from the tunning)
int degoct =
(note - (int)Pmiddlenote + (int) Pmapsize
* 200) / (int)Pmapsize - 200;
int degkey = (note - Pmiddlenote + (int)Pmapsize * 100) % Pmapsize;
degkey = Pmapping[degkey];
if(degkey < 0)
return -1.0f; //this key is not mapped

//invert the keyboard upside-down if it is asked for
//TODO: do the right way by using Pinvertupdowncenter
if(Pinvertupdown != 0) {
degkey = octavesize - degkey - 1;
degoct = -degoct;
}
//compute the frequency of the note
degkey = degkey + scaleshift;
degoct += degkey / octavesize;
degkey %= octavesize;

float freq = (degkey == 0) ? (1.0f) : octave[degkey - 1].tuning;
freq *= powf(octave[octavesize - 1].tuning, degoct);
freq *= PAfreq / rap_anote_middlenote;
freq *= globalfinedetunerap;
if(scaleshift != 0)
freq /= octave[scaleshift - 1].tuning;
return freq * rap_keyshift;
}
else { //if the mapping is disabled
int nt = note - PAnote + scaleshift;
int ntkey = (nt + (int)octavesize * 100) % octavesize;
int ntoct = (nt - ntkey) / octavesize;

float oct = octave[octavesize - 1].tuning;
float freq =
octave[(ntkey + octavesize - 1) % octavesize].tuning * powf(oct,
ntoct)
* PAfreq;
if(ntkey == 0)
freq /= oct;
if(scaleshift != 0)
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;
}
}

bool Microtonal::operator==(const Microtonal &micro) const
{
return !(*this != micro);
}

bool Microtonal::operator!=(const Microtonal &micro) const
{
//A simple macro to test equality MiCRotonal EQuals (not the perfect
//approach, but good enough)
#define MCREQ(x) if(x != micro.x) \
return true

//for floats
#define FMCREQ(x) if(!((x < micro.x + 0.0001f) && (x > micro.x - 0.0001f))) \
return true

MCREQ(Pinvertupdown);
MCREQ(Pinvertupdowncenter);
MCREQ(octavesize);
MCREQ(Penabled);
MCREQ(PAnote);
FMCREQ(PAfreq);
MCREQ(Pscaleshift);

MCREQ(Pfirstkey);
MCREQ(Plastkey);
MCREQ(Pmiddlenote);
MCREQ(Pmapsize);
MCREQ(Pmappingenabled);

for(int i = 0; i < 128; ++i)
MCREQ(Pmapping[i]);

for(int i = 0; i < octavesize; ++i) {
FMCREQ(octave[i].tuning);
MCREQ(octave[i].type);
MCREQ(octave[i].x1);
MCREQ(octave[i].x2);
}
if(strcmp((const char *)this->Pname, (const char *)micro.Pname))
return true;
if(strcmp((const char *)this->Pcomment, (const char *)micro.Pcomment))
return true;
MCREQ(Pglobalfinedetune);
return false;

//undefine macros, as they are no longer needed
#undef MCREQ
#undef FMCREQ
}


/*
* Convert a line to tunings; returns -1 if it ok
*/
int Microtonal::linetotunings(unsigned int nline, const char *line)
{
int x1 = -1, x2 = -1, type = -1;
float x = -1.0f, tmp, tuning = 1.0f;
if(strstr(line, "/") == NULL) {
if(strstr(line, ".") == NULL) { // M case (M=M/1)
sscanf(line, "%d", &x1);
x2 = 1;
type = 2; //division
}
else { // float number case
sscanf(line, "%f", &x);
if(x < 0.000001f)
return 1;
type = 1; //float type(cents)
}
}
else { // M/N case
sscanf(line, "%d/%d", &x1, &x2);
if((x1 < 0) || (x2 < 0))
return 1;
if(x2 == 0)
x2 = 1;
type = 2; //division
}

if(x1 <= 0)
x1 = 1; //not allow zero frequency sounds (consider 0 as 1)

//convert to float if the number are too big
if((type == 2)
&& ((x1 > (128 * 128 * 128 - 1)) || (x2 > (128 * 128 * 128 - 1)))) {
type = 1;
x = ((float) x1) / x2;
}
switch(type) {
case 1:
x1 = (int) floor(x);
tmp = fmod(x, 1.0f);
x2 = (int) (floor(tmp * 1e6));
tuning = powf(2.0f, x / 1200.0f);
break;
case 2:
x = ((float)x1) / x2;
tuning = x;
break;
}

tmpoctave[nline].tuning = tuning;
tmpoctave[nline].type = type;
tmpoctave[nline].x1 = x1;
tmpoctave[nline].x2 = x2;

return -1; //ok
}

/*
* Convert the text to tunnings
*/
int Microtonal::texttotunings(const char *text)
{
unsigned int i, k = 0, nl = 0;
char *lin;
lin = new char[MAX_LINE_SIZE + 1];
while(k < strlen(text)) {
for(i = 0; i < MAX_LINE_SIZE; ++i) {
lin[i] = text[k++];
if(lin[i] < 0x20)
break;
}
lin[i] = '\0';
if(strlen(lin) == 0)
continue;
int err = linetotunings(nl, lin);
if(err != -1) {
delete [] lin;
return nl; //Parse error
}
nl++;
}
delete [] lin;
if(nl > MAX_OCTAVE_SIZE)
nl = MAX_OCTAVE_SIZE;
if(nl == 0)
return -2; //the input is empty
octavesize = nl;
for(i = 0; i < octavesize; ++i) {
octave[i].tuning = tmpoctave[i].tuning;
octave[i].type = tmpoctave[i].type;
octave[i].x1 = tmpoctave[i].x1;
octave[i].x2 = tmpoctave[i].x2;
}
return -1; //ok
}

/*
* Convert the text to mapping
*/
void Microtonal::texttomapping(const char *text)
{
unsigned int i, k = 0;
char *lin;
lin = new char[MAX_LINE_SIZE + 1];
for(i = 0; i < 128; ++i)
Pmapping[i] = -1;
int tx = 0;
while(k < strlen(text)) {
for(i = 0; i < MAX_LINE_SIZE; ++i) {
lin[i] = text[k++];
if(lin[i] < 0x20)
break;
}
lin[i] = '\0';
if(strlen(lin) == 0)
continue;

int tmp = 0;
if(sscanf(lin, "%d", &tmp) == 0)
tmp = -1;
if(tmp < -1)
tmp = -1;
Pmapping[tx] = tmp;

if((tx++) > 127)
break;
}
delete [] lin;

if(tx == 0)
tx = 1;
Pmapsize = tx;
}

/*
* Convert tunning to text line
*/
void Microtonal::tuningtoline(int n, char *line, int maxn)
{
if((n > octavesize) || (n > MAX_OCTAVE_SIZE)) {
line[0] = '\0';
return;
}
if(octave[n].type == 1)
snprintf(line, maxn, "%d.%06d", octave[n].x1, octave[n].x2);
if(octave[n].type == 2)
snprintf(line, maxn, "%d/%d", octave[n].x1, octave[n].x2);
}


int Microtonal::loadline(FILE *file, char *line)
{
do {
if(fgets(line, 500, file) == 0)
return 1;
} while(line[0] == '!');
return 0;
}
/*
* Loads the tunnings from a scl file
*/
int Microtonal::loadscl(const char *filename)
{
FILE *file = fopen(filename, "r");
char tmp[500];
fseek(file, 0, SEEK_SET);
//loads the short description
if(loadline(file, &tmp[0]) != 0)
return 2;
for(int i = 0; i < 500; ++i)
if(tmp[i] < 32)
tmp[i] = 0;
snprintf((char *) Pname, MICROTONAL_MAX_NAME_LEN, "%s", tmp);
snprintf((char *) Pcomment, MICROTONAL_MAX_NAME_LEN, "%s", tmp);
//loads the number of the notes
if(loadline(file, &tmp[0]) != 0)
return 2;
int nnotes = MAX_OCTAVE_SIZE;
sscanf(&tmp[0], "%d", &nnotes);
if(nnotes > MAX_OCTAVE_SIZE)
return 2;
//load the tunnings
for(int nline = 0; nline < nnotes; ++nline) {
if(loadline(file, &tmp[0]) != 0)
return 2;
linetotunings(nline, &tmp[0]);
}
fclose(file);

octavesize = nnotes;
for(int i = 0; i < octavesize; ++i) {
octave[i].tuning = tmpoctave[i].tuning;
octave[i].type = tmpoctave[i].type;
octave[i].x1 = tmpoctave[i].x1;
octave[i].x2 = tmpoctave[i].x2;
}

return 0;
}

/*
* Loads the mapping from a kbm file
*/
int Microtonal::loadkbm(const char *filename)
{
FILE *file = fopen(filename, "r");
int x;
char tmp[500];

fseek(file, 0, SEEK_SET);
//loads the mapsize
if(loadline(file, &tmp[0]) != 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;
//loads first MIDI note to retune
if(loadline(file, &tmp[0]) != 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;
//loads last MIDI note to retune
if(loadline(file, &tmp[0]) != 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...
Plastkey = x;
//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)
return 2;
if(x < 1)
x = 0;
if(x > 127)
x = 127; //just in case...
Pmiddlenote = x;
//loads the reference note
if(loadline(file, &tmp[0]) != 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;
//loads the reference freq.
if(loadline(file, &tmp[0]) != 0)
return 2;
float tmpPAfreq = 440.0f;
if(sscanf(&tmp[0], "%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
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)
return 2;
if(sscanf(&tmp[0], "%d", &x) == 0)
x = -1;
Pmapping[nline] = x;
}
Pmappingenabled = 1;
}
else {
Pmappingenabled = 0;
Pmapping[0] = 0;
Pmapsize = 1;
}
fclose(file);

return 0;
}



void Microtonal::add2XML(XMLwrapper *xml) const
{
xml->addparstr("name", (char *) Pname);
xml->addparstr("comment", (char *) Pcomment);

xml->addparbool("invert_up_down", Pinvertupdown);
xml->addpar("invert_up_down_center", Pinvertupdowncenter);

xml->addparbool("enabled", Penabled);
xml->addpar("global_fine_detune", Pglobalfinedetune);

xml->addpar("a_note", PAnote);
xml->addparreal("a_freq", PAfreq);

if((Penabled == 0) && (xml->minimal))
return;

xml->beginbranch("SCALE");
xml->addpar("scale_shift", Pscaleshift);
xml->addpar("first_key", Pfirstkey);
xml->addpar("last_key", Plastkey);
xml->addpar("middle_note", Pmiddlenote);

xml->beginbranch("OCTAVE");
xml->addpar("octave_size", octavesize);
for(int i = 0; i < octavesize; ++i) {
xml->beginbranch("DEGREE", i);
if(octave[i].type == 1)
xml->addparreal("cents", octave[i].tuning);
;
if(octave[i].type == 2) {
xml->addpar("numerator", octave[i].x1);
xml->addpar("denominator", octave[i].x2);
}
xml->endbranch();
}
xml->endbranch();

xml->beginbranch("KEYBOARD_MAPPING");
xml->addpar("map_size", Pmapsize);
xml->addpar("mapping_enabled", Pmappingenabled);
for(int i = 0; i < Pmapsize; ++i) {
xml->beginbranch("KEYMAP", i);
xml->addpar("degree", Pmapping[i]);
xml->endbranch();
}

xml->endbranch();
xml->endbranch();
}

void Microtonal::getfromXML(XMLwrapper *xml)
{
xml->getparstr("name", (char *) Pname, MICROTONAL_MAX_NAME_LEN);
xml->getparstr("comment", (char *) Pcomment, MICROTONAL_MAX_NAME_LEN);

Pinvertupdown = xml->getparbool("invert_up_down", Pinvertupdown);
Pinvertupdowncenter = xml->getpar127("invert_up_down_center",
Pinvertupdowncenter);

Penabled = xml->getparbool("enabled", Penabled);
Pglobalfinedetune = xml->getpar127("global_fine_detune", Pglobalfinedetune);

PAnote = xml->getpar127("a_note", PAnote);
PAfreq = xml->getparreal("a_freq", PAfreq, 1.0f, 10000.0f);

if(xml->enterbranch("SCALE")) {
Pscaleshift = xml->getpar127("scale_shift", Pscaleshift);
Pfirstkey = xml->getpar127("first_key", Pfirstkey);
Plastkey = xml->getpar127("last_key", Plastkey);
Pmiddlenote = xml->getpar127("middle_note", Pmiddlenote);

if(xml->enterbranch("OCTAVE")) {
octavesize = xml->getpar127("octave_size", octavesize);
for(int i = 0; i < octavesize; ++i) {
if(xml->enterbranch("DEGREE", i) == 0)
continue;
octave[i].x2 = 0;
octave[i].tuning = xml->getparreal("cents", octave[i].tuning);
octave[i].x1 = xml->getpar127("numerator", octave[i].x1);
octave[i].x2 = xml->getpar127("denominator", octave[i].x2);

if(octave[i].x2 != 0)
octave[i].type = 2;
else {
octave[i].type = 1;
//populate fields for display
float x = logf(octave[i].tuning) / LOG_2 * 1200.0f;
octave[i].x1 = (int) floor(x);
octave[i].x2 = (int) (floor(fmodf(x, 1.0f) * 1e6));
}


xml->exitbranch();
}
xml->exitbranch();
}

if(xml->enterbranch("KEYBOARD_MAPPING")) {
Pmapsize = xml->getpar127("map_size", Pmapsize);
Pmappingenabled = xml->getpar127("mapping_enabled", Pmappingenabled);
for(int i = 0; i < Pmapsize; ++i) {
if(xml->enterbranch("KEYMAP", i) == 0)
continue;
Pmapping[i] = xml->getpar127("degree", Pmapping[i]);
xml->exitbranch();
}
xml->exitbranch();
}
xml->exitbranch();
}
}



int Microtonal::saveXML(const char *filename) const
{
XMLwrapper *xml = new XMLwrapper();

xml->beginbranch("MICROTONAL");
add2XML(xml);
xml->endbranch();

int result = xml->saveXMLfile(filename);
delete (xml);
return result;
}

int Microtonal::loadXML(const char *filename)
{
XMLwrapper *xml = new XMLwrapper();
if(xml->loadXMLfile(filename) < 0) {
delete (xml);
return -1;
}

if(xml->enterbranch("MICROTONAL") == 0)
return -10;
getfromXML(xml);
xml->exitbranch();

delete (xml);
return 0;
}

+ 134
- 0
c++/carla-backend/plugins/zynaddsubfx/Misc/Microtonal.h View File

@@ -0,0 +1,134 @@
/*
ZynAddSubFX - a software synthesizer

Microtonal.h - Tuning settings and microtonal capabilities
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 MICROTONAL_H
#define MICROTONAL_H

#include "../globals.h"
#include "XMLwrapper.h"

#define MAX_OCTAVE_SIZE 128
#define MICROTONAL_MAX_NAME_LEN 120

#include <stdio.h>


/**Tuning settings and microtonal capabilities*/
class Microtonal
{
public:
/**Constructor*/
Microtonal();
/**Destructor*/
~Microtonal();
void defaults();
/**Calculates the frequency for a given note
*/
float getnotefreq(int note, int keyshift) const;


//Parameters
/**if the keys are inversed (the pitch is lower to keys from the right direction)*/
unsigned char Pinvertupdown;

/**the central key of the inversion*/
unsigned char Pinvertupdowncenter;

/**0 for 12 key temperate scale, 1 for microtonal*/
unsigned char Penabled;

/**the note of "A" key*/
unsigned char PAnote;

/**the frequency of the "A" note*/
float PAfreq;

/**if the scale is "tuned" to a note, you can tune to other note*/
unsigned char Pscaleshift;

//first and last key (to retune)
unsigned char Pfirstkey;
unsigned char Plastkey;

/**The middle note where scale degree 0 is mapped to*/
unsigned char Pmiddlenote;

/**Map size*/
unsigned char Pmapsize;

/**Mapping ON/OFF*/
unsigned char Pmappingenabled;
/**Mapping (keys)*/
short int Pmapping[128];

/**Fine detune to be applied to all notes*/
unsigned char Pglobalfinedetune;

// Functions
/** Return the current octave size*/
unsigned char getoctavesize() const;
/**Convert tunning to string*/
void tuningtoline(int n, char *line, int maxn);
/**load the tunnings from a .scl file*/
int loadscl(const char *filename);
/**load the mapping from .kbm file*/
int loadkbm(const char *filename);
/**Load text into the internal tunings
*
*\todo better description*/
int texttotunings(const char *text);
/**Load text into the internal mappings
*
*\todo better description*/
void texttomapping(const char *text);

/**Name of Microtonal tuning*/
unsigned char *Pname;
/**Comment about the tuning*/
unsigned char *Pcomment;

void add2XML(XMLwrapper *xml) const;
void getfromXML(XMLwrapper *xml);
int saveXML(const char *filename) const;
int loadXML(const char *filename);

//simple operators primarily for debug
bool operator==(const Microtonal &micro) const;
bool operator!=(const Microtonal &micro) const;

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 "!"
unsigned char octavesize;
struct {
unsigned char type; //1 for cents or 2 for division

// the real tuning (eg. +1.05946f for one halftone)
// or 2.0f for one octave
float tuning;

//the real tunning is x1/x2
unsigned int x1, x2;
} octave[MAX_OCTAVE_SIZE], tmpoctave[MAX_OCTAVE_SIZE];
};

#endif

+ 1342
- 0
c++/carla-backend/plugins/zynaddsubfx/Misc/Part.cpp
File diff suppressed because it is too large
View File


+ 201
- 0
c++/carla-backend/plugins/zynaddsubfx/Misc/Part.h View File

@@ -0,0 +1,201 @@
/*
ZynAddSubFX - a software synthesizer

Part.h - Part implementation
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 PART_H
#define PART_H

#define MAX_INFO_TEXT_SIZE 1000

#include "../globals.h"
#include "../Params/Controller.h"
#include "../Misc/Microtonal.h"

#include <list> // For the monomemnotes list.

class EffectMgr;
class ADnoteParameters;
class SUBnoteParameters;
class PADnoteParameters;
class SynthNote;
class XMLWrapper;
class FFTwrapper;

/** Part implementation*/
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_);
/**Destructor*/
~Part();

// Midi commands implemented
void NoteOn(unsigned char note,
unsigned char velocity,
int masterkeyshift);
void NoteOff(unsigned char note);
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

/* 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)


//saves the instrument settings to a XML file
//returns 0 for ok or <0 if there is an error
int saveXML(const char *filename);
int loadXMLinstrument(const char *filename);

void add2XML(XMLwrapper *xml);
void add2XMLinstrument(XMLwrapper *xml);

void defaults();
void defaultsinstrument();

void applyparameters(bool lockmutex = true);

void getfromXML(XMLwrapper *xml);
void getfromXMLinstrument(XMLwrapper *xml);

void cleanup(bool final = false);

//the part's kit
struct {
unsigned char Penabled, Pmuted, Pminkey, Pmaxkey;
unsigned char *Pname;
unsigned char Padenabled, Psubenabled, Ppadenabled;
unsigned char Psendtoparteffect;
ADnoteParameters *adpars;
SUBnoteParameters *subpars;
PADnoteParameters *padpars;
} kit[NUM_KIT_ITEMS];


//Part parameters
void setkeylimit(unsigned char Pkeylimit);
void setkititemstatus(int kititem, int Penabled_);

unsigned char 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
void setPvolume(char Pvolume);
unsigned char Pkeyshift; //Part keyshift
unsigned char Prcvchn; //from what midi channel it receive commnads
unsigned char Ppanning; //part panning
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)

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

unsigned 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];
} info;


float *partoutl; //Left channel output of the part
float *partoutr; //Right channel output of the part

float *partfxinputl[NUM_PART_EFX + 1], //Left and right signal that pass thru part effects;
*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
};

float volume, oldvolumel, oldvolumer; //this is applied by Master
float panning; //this is applied by Master, too

Controller ctl; //Part controllers

EffectMgr *partefx[NUM_PART_EFX]; //insertion part effects (they are part of the instrument)
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;

private:
void RunNote(unsigned k);
void KillNotePos(int pos);
void RelaseNotePos(int pos);
void MonoMemRenote(); // MonoMem stuff.

int killallnotes; //is set to 1 if I want to kill all notes

struct PartNotes {
NoteStatus status;
int note; //if there is no note playing, the "note"=-1
int itemsplaying;
struct {
SynthNote *adnote,
*subnote,
*padnote;
int sendtoparteffect;
} kititem[NUM_KIT_ITEMS];
int time;
};

int lastpos, lastposb; // To keep track of previously used pos and posb.
bool lastlegatomodevalid; // To keep track of previous legatomodevalid.

// MonoMem stuff
std::list<unsigned char> monomemnotes; // A list to remember held notes.
struct {
unsigned char velocity;
int mkeyshift; // I'm not sure masterkeyshift should be remembered.
} monomem[256];
/* 256 is to cover all possible note values.
monomem[] is used in conjunction with the list to
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];

float oldfreq; //this is used for portamento
Microtonal *microtonal;
FFTwrapper *fft;
};

#endif

+ 93
- 0
c++/carla-backend/plugins/zynaddsubfx/Misc/Recorder.cpp View File

@@ -0,0 +1,93 @@
/*
ZynAddSubFX - a software synthesizer

Recorder.cpp - Records sound to a 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 <sys/stat.h>
#include "Recorder.h"
#include "WavFile.h"
#include "../Nio/OutMgr.h"
#include "../Nio/WavEngine.h"

Recorder::Recorder()
:status(0), notetrigger(0)
{}

Recorder::~Recorder()
{
if(recording() == 1)
stop();
}

int Recorder::preparefile(std::string filename_, int overwrite)
{
if(!overwrite) {
struct stat fileinfo;
int statr;
statr = stat(filename_.c_str(), &fileinfo);
if(statr == 0) //file exists
return 1;
}

OutMgr::getInstance(). wave->newFile(new WavFile(filename_,
synth->samplerate, 2));

status = 1; //ready

return 0;
}

void Recorder::start()
{
notetrigger = 0;
status = 2; //recording
}

void Recorder::stop()
{
OutMgr::getInstance(). wave->Stop();
OutMgr::getInstance(). wave->destroyFile();
status = 0;
}

void Recorder::pause()
{
status = 0;
OutMgr::getInstance(). wave->Stop();
}

int Recorder::recording()
{
if((status == 2) && (notetrigger != 0))
return 1;
else
return 0;
}

void Recorder::triggernow()
{
if(status == 2) {
if(notetrigger != 1)
OutMgr::getInstance().wave->Start();
notetrigger = 1;
}
}

//TODO move recorder inside nio system

+ 54
- 0
c++/carla-backend/plugins/zynaddsubfx/Misc/Recorder.h View File

@@ -0,0 +1,54 @@
/*
ZynAddSubFX - a software synthesizer

Recorder.h - Records sound to a 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 RECORDER_H
#define RECORDER_H
#include <string>
#include "../globals.h"

/**Records sound to a file*/
class Recorder
{
public:

Recorder();
~Recorder();
/**Prepare the given file.
* @returns 1 if the file exists */
int preparefile(std::string filename_, int overwrite);
void start();
void stop();
void pause();
int recording();
void triggernow();

/** Status:
* 0 - not ready(no file selected),
* 1 - ready
* 2 - recording */
int status;

private:
int notetrigger;
};

#endif

+ 38
- 0
c++/carla-backend/plugins/zynaddsubfx/Misc/Stereo.cpp View File

@@ -0,0 +1,38 @@
/*
ZynAddSubFX - a software synthesizer

Stereo.cpp - Object for storing a pair of objects
Copyright (C) 2009-2009 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
*/

template<class T>
Stereo<T>::Stereo(const T &left, const T &right)
:l(left), r(right)
{}

template<class T>
Stereo<T>::Stereo(const T &val)
:l(val), r(val)
{}

template<class T>
Stereo<T> &Stereo<T>::operator=(const Stereo<T> &nstr)
{
l = nstr.l;
r = nstr.r;
return *this;
}

+ 40
- 0
c++/carla-backend/plugins/zynaddsubfx/Misc/Stereo.h View File

@@ -0,0 +1,40 @@
/*
ZynAddSubFX - a software synthesizer

Stereo.h - Object for storing a pair of objects
Copyright (C) 2009-2009 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 STEREO_H
#define STEREO_H

template<class T>
struct Stereo {
public:
Stereo(const T &left, const T &right);

/**Initializes Stereo with left and right set to val
* @param val the value for both channels*/
Stereo(const T &val);
~Stereo() {}

Stereo<T> &operator=(const Stereo<T> &smp);

//data
T l, r;
};
#include "Stereo.cpp"
#endif

+ 227
- 0
c++/carla-backend/plugins/zynaddsubfx/Misc/Util.cpp View File

@@ -0,0 +1,227 @@
/*
ZynAddSubFX - a software synthesizer

Util.cpp - Miscellaneous functions
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 "Util.h"
#include <vector>
#include <cassert>
#include <math.h>
#include <stdio.h>
#include <err.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sched.h>


prng_t prng_state = 0x1234;

Config config;
float *denormalkillbuf;


/*
* Transform the velocity according the scaling parameter (velocity sensing)
*/
float VelF(float velocity, unsigned char scaling)
{
float x;
x = powf(VELOCITY_MAX_SCALE, (64.0f - scaling) / 64.0f);
if((scaling == 127) || (velocity > 0.99f))
return 1.0f;
else
return powf(velocity, x);
}

/*
* Get the detune in cents
*/
float getdetune(unsigned char type,
unsigned short int coarsedetune,
unsigned short int finedetune)
{
float det = 0.0f, octdet = 0.0f, cdet = 0.0f, findet = 0.0f;
//Get Octave
int octave = coarsedetune / 1024;
if(octave >= 8)
octave -= 16;
octdet = octave * 1200.0f;

//Coarse and fine detune
int cdetune = coarsedetune % 1024;
if(cdetune > 512)
cdetune -= 1024;

int fdetune = finedetune - 8192;

switch(type) {
// case 1: is used for the default (see below)
case 2:
cdet = fabs(cdetune * 10.0f);
findet = fabs(fdetune / 8192.0f) * 10.0f;
break;
case 3:
cdet = fabs(cdetune * 100);
findet = powf(10, fabs(fdetune / 8192.0f) * 3.0f) / 10.0f - 0.1f;
break;
case 4:
cdet = fabs(cdetune * 701.95500087f); //perfect fifth
findet =
(powf(2, fabs(fdetune / 8192.0f) * 12.0f) - 1.0f) / 4095 * 1200;
break;
//case ...: need to update N_DETUNE_TYPES, if you'll add more
default:
cdet = fabs(cdetune * 50.0f);
findet = fabs(fdetune / 8192.0f) * 35.0f; //almost like "Paul's Sound Designer 2"
break;
}
if(finedetune < 8192)
findet = -findet;
if(cdetune < 0)
cdet = -cdet;

det = octdet + cdet + findet;
return det;
}


bool fileexists(const char *filename)
{
struct stat tmp;
int result = stat(filename, &tmp);
if(result >= 0)
return true;

return false;
}

void set_realtime()
{
sched_param sc;
sc.sched_priority = 60;
//if you want get "sched_setscheduler undeclared" from compilation,
//you can safely remove the folowing line:
sched_setscheduler(0, SCHED_FIFO, &sc);
//if (err==0) printf("Real-time");
}

void os_sleep(long length)
{
usleep(length);
}

std::string legalizeFilename(std::string filename)
{
for(int i = 0; i < (int) filename.size(); ++i) {
char c = filename[i];
if(!(isdigit(c) || isalpha(c) || (c == '-') || (c == ' ')))
filename[i] = '_';
}
return filename;
}

void invSignal(float *sig, size_t len)
{
for(size_t i = 0; i < len; ++i)
sig[i] *= -1.0f;
}

//Some memory pools for short term buffer use
//(avoid the use of new in RT thread(s))

struct pool_entry {
bool free;
float *dat;
};
typedef std::vector<pool_entry> pool_t;
typedef pool_t::iterator pool_itr_t;

pool_t pool;

float *getTmpBuffer()
{
for(pool_itr_t itr = pool.begin(); itr != pool.end(); ++itr)
if(itr->free) { //Use Pool
itr->free = false;
return itr->dat;
}
pool_entry p; //Extend Pool
p.free = false;
p.dat = new float[synth->buffersize];
pool.push_back(p);

return p.dat;
}

void returnTmpBuffer(float *buf)
{
for(pool_itr_t itr = pool.begin(); itr != pool.end(); ++itr)
if(itr->dat == buf) { //Return to Pool
itr->free = true;
return;
}
fprintf(stderr,
"ERROR: invalid buffer returned %s %d\n",
__FILE__,
__LINE__);
}

void clearTmpBuffers(void)
{
for(pool_itr_t itr = pool.begin(); itr != pool.end(); ++itr) {
if(!itr->free) //Warn about used buffers
warn("Temporary buffer (%p) about to be freed may be in use",
itr->dat);
delete [] itr->dat;
}
pool.clear();
}

float SYNTH_T::numRandom() const
{
return RND;
}

float interpolate(const float *data, size_t len, float pos)
{
assert(len > (size_t)pos + 1);
const int l_pos = (int)pos,
r_pos = l_pos + 1;
const float leftness = pos - l_pos;
return data[l_pos] * leftness + data[r_pos] * (1.0f - leftness);

#ifdef NDEBUG
// unused
(void)len;
#endif
}

float cinterpolate(const float *data, size_t len, float pos)
{
const int l_pos = ((int)pos) % len,
r_pos = (l_pos + 1) % len;
const float leftness = pos - l_pos;
return data[l_pos] * leftness + data[r_pos] * (1.0f - leftness);
}

+ 123
- 0
c++/carla-backend/plugins/zynaddsubfx/Misc/Util.h View File

@@ -0,0 +1,123 @@
/*
ZynAddSubFX - a software synthesizer

Util.h - Miscellaneous functions
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 UTIL_H
#define UTIL_H

#include <string>
#include <sstream>
#include <stdint.h>
#include "Config.h"
#include "../globals.h"

//Velocity Sensing function
extern float VelF(float velocity, unsigned char scaling);

bool fileexists(const char *filename);

#define N_DETUNE_TYPES 4 //the number of detune types
extern float getdetune(unsigned char type,
unsigned short int coarsedetune,
unsigned short int finedetune);

/**Try to set current thread to realtime priority program priority
* \todo see if the right pid is being sent
* \todo see if this is having desired effect, if not then look at
* pthread_attr_t*/
void set_realtime();

/**Os independent sleep in microsecond*/
void os_sleep(long length);

std::string legalizeFilename(std::string filename);

extern float *denormalkillbuf; /**<the buffer to add noise in order to avoid denormalisation*/

extern class Config config;

void invSignal(float *sig, size_t len);

//Memory pool for temporary buffers
//No allocation in *normal* case
//All should be sized to synth->buffersize
float *getTmpBuffer();
void returnTmpBuffer(float *buf);
void clearTmpBuffers(void);

template<class T>
std::string stringFrom(T x)
{
std::stringstream ss;
ss << x;
return ss.str();
}

template<class T>
T stringTo(const char *x)
{
std::string str = x != NULL ? x : "0"; //should work for the basic float/int
std::stringstream ss(str);
T ans;
ss >> ans;
return ans;
}

template<class T>
T limit(T val, T min, T max)
{
return val < min ? min : (val > max ? max : val);
}

//Random number generator

typedef uint32_t prng_t;
extern prng_t prng_state;

// Portable Pseudo-Random Number Generator
inline prng_t prng_r(prng_t &p)
{
return p = p * 1103515245 + 12345;
}

inline prng_t prng(void)
{
return prng_r(prng_state) & 0x7fffffff;
}

inline void sprng(prng_t p)
{
prng_state = p;
}

/*
* The random generator (0.0f..1.0f)
*/
# define INT32_MAX (2147483647)
#define RND (prng() / (INT32_MAX * 1.0f))

//Linear Interpolation
float interpolate(const float *data, size_t len, float pos);

//Linear circular interpolation
float cinterpolate(const float *data, size_t len, float pos);

#endif

+ 97
- 0
c++/carla-backend/plugins/zynaddsubfx/Misc/WavFile.cpp View File

@@ -0,0 +1,97 @@
/*
Copyright (C) 2006 Nasca Octavian Paul
Author: Nasca Octavian Paul
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) 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 <cstdio>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include "WavFile.h"
using namespace std;

WavFile::WavFile(string filename, int samplerate, int channels)
:sampleswritten(0), samplerate(samplerate), channels(channels),
file(fopen(filename.c_str(), "w"))

{
if(file) {
cout << "INFO: Making space for wave file header" << endl;
//making space for the header written at destruction
char tmp[44];
memset(tmp, 0, 44 * sizeof(char));
fwrite(tmp, 1, 44, file);
}
}

WavFile::~WavFile()
{
if(file) {
cout << "INFO: Writing wave file header" << endl;

unsigned int chunksize;
rewind(file);

fwrite("RIFF", 4, 1, file);
chunksize = sampleswritten * 4 + 36;
fwrite(&chunksize, 4, 1, file);

fwrite("WAVEfmt ", 8, 1, file);
chunksize = 16;
fwrite(&chunksize, 4, 1, file);
unsigned short int formattag = 1; //uncompresed wave
fwrite(&formattag, 2, 1, file);
unsigned short int nchannels = channels; //stereo
fwrite(&nchannels, 2, 1, file);
unsigned int samplerate_ = samplerate; //samplerate
fwrite(&samplerate_, 4, 1, file);
unsigned int bytespersec = samplerate * 2 * channels; //bytes/sec
fwrite(&bytespersec, 4, 1, file);
unsigned short int blockalign = 2 * channels; //2 channels * 16 bits/8
fwrite(&blockalign, 2, 1, file);
unsigned short int bitspersample = 16;
fwrite(&bitspersample, 2, 1, file);

fwrite("data", 4, 1, file);
chunksize = sampleswritten * blockalign;
fwrite(&chunksize, 4, 1, file);

fclose(file);
file = NULL;
}
}

bool WavFile::good() const
{
return file;
}

void WavFile::writeStereoSamples(int nsmps, short int *smps)
{
if(file) {
fwrite(smps, nsmps, 4, file);
sampleswritten += nsmps;
}
}

void WavFile::writeMonoSamples(int nsmps, short int *smps)
{
if(file) {
fwrite(smps, nsmps, 2, file);
sampleswritten += nsmps;
}
}

+ 44
- 0
c++/carla-backend/plugins/zynaddsubfx/Misc/WavFile.h View File

@@ -0,0 +1,44 @@
/*
ZynAddSubFX - a software synthesizer

WavFile.h - Records sound to a file
Copyright (C) 2008 Nasca Octavian Paul
Author: Nasca Octavian Paul
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) 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 WAVFILE_H
#define WAVFILE_H
#include <string>

class WavFile
{
public:
WavFile(std::string filename, int samplerate, int channels);
~WavFile();

bool good() const;

void writeMonoSamples(int nsmps, short int *smps);
void writeStereoSamples(int nsmps, short int *smps);

private:
int sampleswritten;
int samplerate;
int channels;
FILE *file;
};
#endif

+ 189
- 0
c++/carla-backend/plugins/zynaddsubfx/Misc/WaveShapeSmps.cpp View File

@@ -0,0 +1,189 @@
/*
ZynAddSubFX - a software synthesizer

WaveShapeSmps.cpp - Sample Distortion
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 "WaveShapeSmps.h"
#include <cmath>

void waveShapeSmps(int n,
float *smps,
unsigned char type,
unsigned char drive)
{
int i;
float ws = drive / 127.0f;
float tmpv;

switch(type) {
case 1:
ws = powf(10, ws * ws * 3.0f) - 1.0f + 0.001f; //Arctangent
for(i = 0; i < n; ++i)
smps[i] = atanf(smps[i] * ws) / atanf(ws);
break;
case 2:
ws = ws * ws * 32.0f + 0.0001f; //Asymmetric
if(ws < 1.0f)
tmpv = sinf(ws) + 0.1f;
else
tmpv = 1.1f;
for(i = 0; i < n; ++i)
smps[i] = sinf(smps[i] * (0.1f + ws - ws * smps[i])) / tmpv;
;
break;
case 3:
ws = ws * ws * ws * 20.0f + 0.0001f; //Pow
for(i = 0; i < n; ++i) {
smps[i] *= ws;
if(fabs(smps[i]) < 1.0f) {
smps[i] = (smps[i] - powf(smps[i], 3.0f)) * 3.0f;
if(ws < 1.0f)
smps[i] /= ws;
}
else
smps[i] = 0.0f;
}
break;
case 4:
ws = ws * ws * ws * 32.0f + 0.0001f; //Sine
if(ws < 1.57f)
tmpv = sinf(ws);
else
tmpv = 1.0f;
for(i = 0; i < n; ++i)
smps[i] = sinf(smps[i] * ws) / tmpv;
break;
case 5:
ws = ws * ws + 0.000001f; //Quantisize
for(i = 0; i < n; ++i)
smps[i] = floor(smps[i] / ws + 0.5f) * ws;
break;
case 6:
ws = ws * ws * ws * 32 + 0.0001f; //Zigzag
if(ws < 1.0f)
tmpv = sinf(ws);
else
tmpv = 1.0f;
for(i = 0; i < n; ++i)
smps[i] = asinf(sinf(smps[i] * ws)) / tmpv;
break;
case 7:
ws = powf(2.0f, -ws * ws * 8.0f); //Limiter
for(i = 0; i < n; ++i) {
float tmp = smps[i];
if(fabs(tmp) > ws) {
if(tmp >= 0.0f)
smps[i] = 1.0f;
else
smps[i] = -1.0f;
}
else
smps[i] /= ws;
}
break;
case 8:
ws = powf(2.0f, -ws * ws * 8.0f); //Upper Limiter
for(i = 0; i < n; ++i) {
float tmp = smps[i];
if(tmp > ws)
smps[i] = ws;
smps[i] *= 2.0f;
}
break;
case 9:
ws = powf(2.0f, -ws * ws * 8.0f); //Lower Limiter
for(i = 0; i < n; ++i) {
float tmp = smps[i];
if(tmp < -ws)
smps[i] = -ws;
smps[i] *= 2.0f;
}
break;
case 10:
ws = (powf(2.0f, ws * 6.0f) - 1.0f) / powf(2.0f, 6.0f); //Inverse Limiter
for(i = 0; i < n; ++i) {
float tmp = smps[i];
if(fabs(tmp) > ws) {
if(tmp >= 0.0f)
smps[i] = tmp - ws;
else
smps[i] = tmp + ws;
}
else
smps[i] = 0;
}
break;
case 11:
ws = powf(5, ws * ws * 1.0f) - 1.0f; //Clip
for(i = 0; i < n; ++i)
smps[i] = smps[i]
* (ws + 0.5f) * 0.9999f - floor(
0.5f + smps[i] * (ws + 0.5f) * 0.9999f);
break;
case 12:
ws = ws * ws * ws * 30 + 0.001f; //Asym2
if(ws < 0.3f)
tmpv = ws;
else
tmpv = 1.0f;
for(i = 0; i < n; ++i) {
float tmp = smps[i] * ws;
if((tmp > -2.0f) && (tmp < 1.0f))
smps[i] = tmp * (1.0f - tmp) * (tmp + 2.0f) / tmpv;
else
smps[i] = 0.0f;
}
break;
case 13:
ws = ws * ws * ws * 32.0f + 0.0001f; //Pow2
if(ws < 1.0f)
tmpv = ws * (1 + ws) / 2.0f;
else
tmpv = 1.0f;
for(i = 0; i < n; ++i) {
float tmp = smps[i] * ws;
if((tmp > -1.0f) && (tmp < 1.618034f))
smps[i] = tmp * (1.0f - tmp) / tmpv;
else
if(tmp > 0.0f)
smps[i] = -1.0f;
else
smps[i] = -2.0f;
}
break;
case 14:
ws = powf(ws, 5.0f) * 80.0f + 0.0001f; //sigmoid
if(ws > 10.0f)
tmpv = 0.5f;
else
tmpv = 0.5f - 1.0f / (expf(ws) + 1.0f);
for(i = 0; i < n; ++i) {
float tmp = smps[i] * ws;
if(tmp < -10.0f)
tmp = -10.0f;
else
if(tmp > 10.0f)
tmp = 10.0f;
tmp = 0.5f - 1.0f / (expf(tmp) + 1.0f);
smps[i] = tmp / tmpv;
}
break;
}
}

+ 31
- 0
c++/carla-backend/plugins/zynaddsubfx/Misc/WaveShapeSmps.h View File

@@ -0,0 +1,31 @@
/*
ZynAddSubFX - a software synthesizer

WaveShapeSmps.h - Sample distortions
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 WAVESHAPESMPS_H
#define WAVESHAPESMPS_H

//Waveshaping(called by Distorsion effect and waveshape from OscilGen)
void waveShapeSmps(int n,
float *smps,
unsigned char type,
unsigned char drive);

#endif

+ 623
- 0
c++/carla-backend/plugins/zynaddsubfx/Misc/XMLwrapper.cpp View File

@@ -0,0 +1,623 @@
/*
ZynAddSubFX - a software synthesizer

XMLwrapper.cpp - XML wrapper
Copyright (C) 2003-2005 Nasca Octavian Paul
Copyright (C) 2009-2009 Mark McCurry
Author: Nasca Octavian Paul
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 "XMLwrapper.h"
#include <cstring>
#include <stdio.h>
#include <stdlib.h>
#include <cstdarg>
#include <zlib.h>
#include <iostream>
#include <sstream>

#include "../globals.h"
#include "Util.h"

using namespace std;

int xml_k = 0;
bool verbose = false;

const char *XMLwrapper_whitespace_callback(mxml_node_t *node, int where)
{
const char *name = node->value.element.name;

if((where == MXML_WS_BEFORE_OPEN) && (!strcmp(name, "?xml")))
return NULL;
if((where == MXML_WS_BEFORE_CLOSE) && (!strcmp(name, "string")))
return NULL;

if((where == MXML_WS_BEFORE_OPEN) || (where == MXML_WS_BEFORE_CLOSE))
/* const char *tmp=node->value.element.name;
if (tmp!=NULL) {
if ((strstr(tmp,"par")!=tmp)&&(strstr(tmp,"string")!=tmp)) {
printf("%s ",tmp);
if (where==MXML_WS_BEFORE_OPEN) xml_k++;
if (where==MXML_WS_BEFORE_CLOSE) xml_k--;
if (xml_k>=STACKSIZE) xml_k=STACKSIZE-1;
if (xml_k<0) xml_k=0;
printf("%d\n",xml_k);
printf("\n");
};

};
int i=0;
for (i=1;i<xml_k;i++) tabs[i]='\t';
tabs[0]='\n';tabs[i+1]='\0';
if (where==MXML_WS_BEFORE_OPEN) return(tabs);
else return("\n");
*/
return "\n";
;

return 0;
}

//temporary const overload of mxmlFindElement
const mxml_node_t *mxmlFindElement(const mxml_node_t *node,
const mxml_node_t *top,
const char *name,
const char *attr,
const char *value,
int descend)
{
return const_cast<const mxml_node_t *>(mxmlFindElement(
const_cast<mxml_node_t *>(node),
const_cast<mxml_node_t *>(top),
name, attr, value, descend));
}

//temporary const overload of mxmlElementGetAttr
const char *mxmlElementGetAttr(const mxml_node_t *node, const char *name)
{
return mxmlElementGetAttr(const_cast<mxml_node_t *>(node), name);
}

XMLwrapper::XMLwrapper()
{
version.Major = 2;
version.Minor = 4;
version.Revision = 3;

minimal = true;

node = tree = mxmlNewElement(MXML_NO_PARENT,
"?xml version=\"1.0f\" encoding=\"UTF-8\"?");
/* for mxml 2.1f (and older)
tree=mxmlNewElement(MXML_NO_PARENT,"?xml");
mxmlElementSetAttr(tree,"version","1.0f");
mxmlElementSetAttr(tree,"encoding","UTF-8");
*/

mxml_node_t *doctype = mxmlNewElement(tree, "!DOCTYPE");
mxmlElementSetAttr(doctype, "ZynAddSubFX-data", NULL);

node = root = addparams("ZynAddSubFX-data", 4,
"version-major", stringFrom<int>(
version.Major).c_str(),
"version-minor", stringFrom<int>(
version.Minor).c_str(),
"version-revision",
stringFrom<int>(version.Revision).c_str(),
"ZynAddSubFX-author", "Nasca Octavian Paul");

//make the empty branch that will contain the information parameters
info = addparams("INFORMATION", 0);

//save zynaddsubfx specifications
beginbranch("BASE_PARAMETERS");
addpar("max_midi_parts", NUM_MIDI_PARTS);
addpar("max_kit_items_per_instrument", NUM_KIT_ITEMS);

addpar("max_system_effects", NUM_SYS_EFX);
addpar("max_insertion_effects", NUM_INS_EFX);
addpar("max_instrument_effects", NUM_PART_EFX);

addpar("max_addsynth_voices", NUM_VOICES);
endbranch();
}

XMLwrapper::~XMLwrapper()
{
if(tree)
mxmlDelete(tree);
}

void XMLwrapper::setPadSynth(bool enabled)
{
/**@bug this might create multiple nodes when only one is needed*/
mxml_node_t *oldnode = node;
node = info;
//Info storing
addparbool("PADsynth_used", enabled);
node = oldnode;
}

bool XMLwrapper::hasPadSynth() const
{
/**Right now this has a copied implementation of setparbool, so this should
* be reworked as XMLwrapper evolves*/
mxml_node_t *tmp = mxmlFindElement(tree,
tree,
"INFORMATION",
NULL,
NULL,
MXML_DESCEND);

mxml_node_t *parameter = mxmlFindElement(tmp,
tmp,
"par_bool",
"name",
"PADsynth_used",
MXML_DESCEND_FIRST);
if(parameter == NULL) //no information availiable
return false;

const char *strval = mxmlElementGetAttr(parameter, "value");
if(strval == NULL) //no information available
return false;

if((strval[0] == 'Y') || (strval[0] == 'y'))
return true;
else
return false;
}


/* SAVE XML members */

int XMLwrapper::saveXMLfile(const string &filename) const
{
char *xmldata = getXMLdata();
if(xmldata == NULL)
return -2;

int compression = config.cfg.GzipCompression;
int result = dosavefile(filename.c_str(), compression, xmldata);

free(xmldata);
return result;
}

char *XMLwrapper::getXMLdata() const
{
xml_k = 0;

char *xmldata = mxmlSaveAllocString(tree, XMLwrapper_whitespace_callback);

return xmldata;
}


int XMLwrapper::dosavefile(const char *filename,
int compression,
const char *xmldata) const
{
if(compression == 0) {
FILE *file;
file = fopen(filename, "w");
if(file == NULL)
return -1;
fputs(xmldata, file);
fclose(file);
}
else {
if(compression > 9)
compression = 9;
if(compression < 1)
compression = 1;
char options[10];
snprintf(options, 10, "wb%d", compression);

gzFile gzfile;
gzfile = gzopen(filename, options);
if(gzfile == NULL)
return -1;
gzputs(gzfile, xmldata);
gzclose(gzfile);
}

return 0;
}



void XMLwrapper::addpar(const string &name, int val)
{
addparams("par", 2, "name", name.c_str(), "value", stringFrom<int>(
val).c_str());
}

void XMLwrapper::addparreal(const string &name, float val)
{
addparams("par_real", 2, "name", name.c_str(), "value",
stringFrom<float>(val).c_str());
}

void XMLwrapper::addparbool(const string &name, int val)
{
if(val != 0)
addparams("par_bool", 2, "name", name.c_str(), "value", "yes");
else
addparams("par_bool", 2, "name", name.c_str(), "value", "no");
}

void XMLwrapper::addparstr(const string &name, const string &val)
{
mxml_node_t *element = mxmlNewElement(node, "string");
mxmlElementSetAttr(element, "name", name.c_str());
mxmlNewText(element, 0, val.c_str());
}


void XMLwrapper::beginbranch(const string &name)
{
if(verbose)
cout << "beginbranch()" << name << endl;
node = addparams(name.c_str(), 0);
}

void XMLwrapper::beginbranch(const string &name, int id)
{
if(verbose)
cout << "beginbranch(" << id << ")" << name << endl;
node = addparams(name.c_str(), 1, "id", stringFrom<int>(id).c_str());
}

void XMLwrapper::endbranch()
{
if(verbose)
cout << "endbranch()" << node << "-" << node->value.element.name
<< " To "
<< node->parent << "-" << node->parent->value.element.name << endl;
node = node->parent;
}


//workaround for memory leak
const char *trimLeadingWhite(const char *c)
{
while(isspace(*c))
++c;
return c;
}

/* LOAD XML members */

int XMLwrapper::loadXMLfile(const string &filename)
{
if(tree != NULL)
mxmlDelete(tree);
tree = NULL;

const char *xmldata = doloadfile(filename.c_str());
if(xmldata == NULL)
return -1; //the file could not be loaded or uncompressed

root = tree = mxmlLoadString(NULL, trimLeadingWhite(
xmldata), MXML_OPAQUE_CALLBACK);

delete[] xmldata;

if(tree == NULL)
return -2; //this is not XML

node = root = mxmlFindElement(tree,
tree,
"ZynAddSubFX-data",
NULL,
NULL,
MXML_DESCEND);
if(root == NULL)
return -3; //the XML doesnt embbed zynaddsubfx data

//fetch version information
version.Major = stringTo<int>(mxmlElementGetAttr(root, "version-major"));
version.Minor = stringTo<int>(mxmlElementGetAttr(root, "version-minor"));
version.Revision =
stringTo<int>(mxmlElementGetAttr(root, "version-revision"));

if(verbose)
cout << "loadXMLfile() version: " << version.Major << '.'
<< version.Minor << '.' << version.Revision << endl;

return 0;
}


char *XMLwrapper::doloadfile(const string &filename) const
{
char *xmldata = NULL;
gzFile gzfile = gzopen(filename.c_str(), "rb");

if(gzfile != NULL) { //The possibly compressed file opened
stringstream strBuf; //reading stream
const int bufSize = 500; //fetch size
char fetchBuf[bufSize + 1]; //fetch buffer
int read = 0; //chars read in last fetch

fetchBuf[bufSize] = 0; //force null termination

while(bufSize == (read = gzread(gzfile, fetchBuf, bufSize)))
strBuf << fetchBuf;

fetchBuf[read] = 0; //Truncate last partial read
strBuf << fetchBuf;

gzclose(gzfile);

//Place data in output format
string tmp = strBuf.str();
xmldata = new char[tmp.size() + 1];
strncpy(xmldata, tmp.c_str(), tmp.size() + 1);
}

return xmldata;
}

bool XMLwrapper::putXMLdata(const char *xmldata)
{
if(tree != NULL)
mxmlDelete(tree);

tree = NULL;
if(xmldata == NULL)
return false;

root = tree = mxmlLoadString(NULL, trimLeadingWhite(
xmldata), MXML_OPAQUE_CALLBACK);
if(tree == NULL)
return false;

node = root = mxmlFindElement(tree,
tree,
"ZynAddSubFX-data",
NULL,
NULL,
MXML_DESCEND);
if(root == NULL)
return false;

return true;
}



int XMLwrapper::enterbranch(const string &name)
{
if(verbose)
cout << "enterbranch() " << name << endl;
mxml_node_t *tmp = mxmlFindElement(node, node,
name.c_str(), NULL, NULL,
MXML_DESCEND_FIRST);
if(tmp == NULL)
return 0;

node = tmp;
return 1;
}

int XMLwrapper::enterbranch(const string &name, int id)
{
if(verbose)
cout << "enterbranch(" << id << ") " << name << endl;
mxml_node_t *tmp = mxmlFindElement(node, node,
name.c_str(), "id", stringFrom<int>(
id).c_str(), MXML_DESCEND_FIRST);
if(tmp == NULL)
return 0;

node = tmp;
return 1;
}


void XMLwrapper::exitbranch()
{
if(verbose)
cout << "exitbranch()" << node << "-" << node->value.element.name
<< " To "
<< node->parent << "-" << node->parent->value.element.name << endl;
node = node->parent;
}


int XMLwrapper::getbranchid(int min, int max) const
{
int id = stringTo<int>(mxmlElementGetAttr(node, "id"));
if((min == 0) && (max == 0))
return id;

if(id < min)
id = min;
else
if(id > max)
id = max;

return id;
}

int XMLwrapper::getpar(const string &name, int defaultpar, int min,
int max) const
{
const mxml_node_t *tmp = mxmlFindElement(node,
node,
"par",
"name",
name.c_str(),
MXML_DESCEND_FIRST);

if(tmp == NULL)
return defaultpar;

const char *strval = mxmlElementGetAttr(tmp, "value");
if(strval == NULL)
return defaultpar;

int val = stringTo<int>(strval);
if(val < min)
val = min;
else
if(val > max)
val = max;

return val;
}

int XMLwrapper::getpar127(const string &name, int defaultpar) const
{
return getpar(name, defaultpar, 0, 127);
}

int XMLwrapper::getparbool(const string &name, int defaultpar) const
{
const mxml_node_t *tmp = mxmlFindElement(node,
node,
"par_bool",
"name",
name.c_str(),
MXML_DESCEND_FIRST);

if(tmp == NULL)
return defaultpar;

const char *strval = mxmlElementGetAttr(tmp, "value");
if(strval == NULL)
return defaultpar;

if((strval[0] == 'Y') || (strval[0] == 'y'))
return 1;
else
return 0;
}

void XMLwrapper::getparstr(const string &name, char *par, int maxstrlen) const
{
ZERO(par, maxstrlen);
const mxml_node_t *tmp = mxmlFindElement(node,
node,
"string",
"name",
name.c_str(),
MXML_DESCEND_FIRST);

if(tmp == NULL)
return;
if(tmp->child == NULL)
return;
if(tmp->child->type == MXML_OPAQUE) {
snprintf(par, maxstrlen, "%s", tmp->child->value.element.name);
return;
}
if((tmp->child->type == MXML_TEXT)
&& (tmp->child->value.text.string != NULL)) {
snprintf(par, maxstrlen, "%s", tmp->child->value.text.string);
return;
}
}

string XMLwrapper::getparstr(const string &name,
const std::string &defaultpar) const
{
const mxml_node_t *tmp = mxmlFindElement(node,
node,
"string",
"name",
name.c_str(),
MXML_DESCEND_FIRST);

if((tmp == NULL) || (tmp->child == NULL))
return defaultpar;

if((tmp->child->type == MXML_OPAQUE)
&& (tmp->child->value.element.name != NULL))
return tmp->child->value.element.name;

if((tmp->child->type == MXML_TEXT)
&& (tmp->child->value.text.string != NULL))
return tmp->child->value.text.string;

return defaultpar;
}

float XMLwrapper::getparreal(const char *name, float defaultpar) const
{
const mxml_node_t *tmp = mxmlFindElement(node,
node,
"par_real",
"name",
name,
MXML_DESCEND_FIRST);
if(tmp == NULL)
return defaultpar;

const char *strval = mxmlElementGetAttr(tmp, "value");
if(strval == NULL)
return defaultpar;

return stringTo<float>(strval);
}

float XMLwrapper::getparreal(const char *name,
float defaultpar,
float min,
float max) const
{
float result = getparreal(name, defaultpar);

if(result < min)
result = min;
else
if(result > max)
result = max;
return result;
}


/** Private members **/

mxml_node_t *XMLwrapper::addparams(const char *name, unsigned int params,
...) const
{
/**@todo make this function send out a good error message if something goes
* wrong**/
mxml_node_t *element = mxmlNewElement(node, name);

if(params) {
va_list variableList;
va_start(variableList, params);

const char *ParamName;
const char *ParamValue;
while(params--) {
ParamName = va_arg(variableList, const char *);
ParamValue = va_arg(variableList, const char *);
if(verbose)
cout << "addparams()[" << params << "]=" << name << " "
<< ParamName << "=\"" << ParamValue << "\"" << endl;
mxmlElementSetAttr(element, ParamName, ParamValue);
}
}
return element;
}

+ 271
- 0
c++/carla-backend/plugins/zynaddsubfx/Misc/XMLwrapper.h View File

@@ -0,0 +1,271 @@
/*
ZynAddSubFX - a software synthesizer

XMLwrapper.h - XML wrapper
Copyright (C) 2003-2005 Nasca Octavian Paul
Copyright (C) 2009-2009 Mark McCurry
Author: Nasca Octavian Paul
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 <mxml.h>
#include <string>
#ifndef float
#define float float
#endif

#ifndef XML_WRAPPER_H
#define XML_WRAPPER_H

/**Mxml wrapper*/
class XMLwrapper
{
public:
/**
* Constructor.
* Will Construct the object and fill in top level branch
* */
XMLwrapper();

/**Destructor*/
~XMLwrapper();

/**
* Saves the XML to a file.
* @param filename the name of the destination file.
* @returns 0 if ok or -1 if the file cannot be saved.
*/
int saveXMLfile(const std::string &filename) const;

/**
* Return XML tree as a string.
* Note: The string must be freed with free() to deallocate
* @returns a newly allocated NULL terminated string of the XML data.
*/
char *getXMLdata() const;

/**
* Add simple parameter.
* @param name The name of the mXML node.
* @param val The string value of the mXml node
*/
void addpar(const std::string &name, int val);

/**
* Adds a realtype parameter.
* @param name The name of the mXML node.
* @param val The float value of the node.
*/
void addparreal(const std::string &name, float val);

/**
* Add boolean parameter.
* \todo Fix this reverse boolean logic.
* @param name The name of the mXML node.
* @param val The boolean value of the node (0->"yes";else->"no").
*/
void addparbool(const std::string &name, int val);

/**
* Add string parameter.
* @param name The name of the mXML node.
* @param val The string value of the node.
*/
void addparstr(const std::string &name, const std::string &val);

/**
* Create a new branch.
* @param name Name of new branch
* @see void endbranch()
*/
void beginbranch(const std::string &name);
/**
* Create a new branch.
* @param name Name of new branch
* @param id "id" value of branch
* @see void endbranch()
*/
void beginbranch(const std::string &name, int id);

/**Closes new branches.
* This must be called to exit each branch created by beginbranch( ).
* @see void beginbranch(const std::string &name)
* @see void beginbranch(const std::string &name, int id)
*/
void endbranch();

/**
* Loads file into XMLwrapper.
* @param filename file to be loaded
* @returns 0 if ok or -1 if the file cannot be loaded
*/
int loadXMLfile(const std::string &filename);

/**
* Loads string into XMLwrapper.
* @param xmldata NULL terminated string of XML data.
* @returns true if successful.
*/
bool putXMLdata(const char *xmldata);

/**
* Enters the branch.
* @param name Name of branch.
* @returns 1 if is ok, or 0 otherwise.
*/
int enterbranch(const std::string &name);

/**
* Enter into the branch \c name with id \c id.
* @param name Name of branch.
* @param id Value of branch's "id".
* @returns 1 if is ok, or 0 otherwise.
*/
int enterbranch(const std::string &name, int id);

/**Exits from a branch*/
void exitbranch();

/**Get the the branch_id and limits it between the min and max.
* if min==max==0, it will not limit it
* if there isn't any id, will return min
* this must be called only imediately after enterbranch()
*/
int getbranchid(int min, int max) const;

/**
* Returns the integer value stored in node name.
* It returns the integer value between the limits min and max.
* If min==max==0, then the value will not be limited.
* If there is no location named name, then defaultpar will be returned.
* @param name The parameter name.
* @param defaultpar The default value if the real value is not found.
* @param min The minimum return value.
* @param max The maximum return value.
*/
int getpar(const std::string &name, int defaultpar, int min,
int max) const;

/**
* Returns the integer value stored in the node with range [0,127].
* @param name The parameter name.
* @param defaultpar The default value if the real value is not found.
*/
int getpar127(const std::string &name, int defaultpar) const;

/**
* Returns the boolean value stored in the node.
* @param name The parameter name.
* @param defaultpar The default value if the real value is not found.
*/
int getparbool(const std::string &name, int defaultpar) const;

/**
* Get the string value stored in the node.
* @param name The parameter name.
* @param par Pointer to destination string
* @param maxstrlen Max string length for destination
*/
void getparstr(const std::string &name, char *par, int maxstrlen) const;

/**
* Get the string value stored in the node.
* @param name The parameter name.
* @param defaultpar The default value if the real value is not found.
*/
std::string getparstr(const std::string &name,
const std::string &defaultpar) const;

/**
* Returns the real value stored in the node.
* @param name The parameter name.
* @param defaultpar The default value if the real value is not found.
*/
float getparreal(const char *name, float defaultpar) const;

/**
* Returns the real value stored in the node.
* @param name The parameter name.
* @param defaultpar The default value if the real value is not found.
* @param min The minimum value
* @param max The maximum value
*/
float getparreal(const char *name,
float defaultpar,
float min,
float max) const;

bool minimal; /**<false if all parameters will be stored (used only for clipboard)*/

/**
* Sets the current tree's PAD Synth usage
*/
void setPadSynth(bool enabled);
/**
* Checks the current tree for PADsynth usage
*/
bool hasPadSynth() const;

private:

/**
* Save the file.
* @param filename File to save to
* @param compression Level of gzip compression
* @param xmldata String to be saved
*/
int dosavefile(const char *filename,
int compression,
const char *xmldata) const;

/**
* Loads specified file and returns data.
*
* Will load a gziped file or an uncompressed file.
* @param filename the file
* @return The decompressed data
*/
char *doloadfile(const std::string &filename) const;

mxml_node_t *tree; /**<all xml data*/
mxml_node_t *root; /**<xml data used by zynaddsubfx*/
mxml_node_t *node; /**<current subtree in parsing or writing */
mxml_node_t *info; /**<Node used to store the information about the data*/

/**
* Create mxml_node_t with specified name and parameters
*
* Results should look like:
* <name optionalParam1="value1" optionalParam2="value2" ...>
*
* @param name The name of the xml node
* @param params The number of the attributes
* @param ... const char * pairs that are in the format attribute_name,
* attribute_value
*/
mxml_node_t *addparams(const char *name, unsigned int params,
...) const;

/**@todo keep these numbers up to date*/
struct {
int Major; /**<major version number.*/
int Minor; /**<minor version number.*/
int Revision; /**<version revision number.*/
} version;
};

#endif

+ 366
- 0
c++/carla-backend/plugins/zynaddsubfx/Nio/AlsaEngine.cpp View File

@@ -0,0 +1,366 @@
/*
AlsaEngine.cpp

Copyright 2009, Alan Calvert
2010, 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.

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.

You should have received a copy of the GNU General Public License
along with ZynAddSubFX. If not, see <http://www.gnu.org/licenses/>.
*/

#include <iostream>
#include <cmath>

using namespace std;

#include "../Misc/Util.h"
#include "../Misc/Config.h"
#include "InMgr.h"
#include "AlsaEngine.h"

AlsaEngine::AlsaEngine()
:AudioOut()
{
audio.buffer = new short[synth->buffersize * 2];
name = "ALSA";
audio.handle = NULL;

midi.handle = NULL;
midi.alsaId = -1;
midi.pThread = 0;
}

AlsaEngine::~AlsaEngine()
{
Stop();
delete[] audio.buffer;
}

void *AlsaEngine::_AudioThread(void *arg)
{
return (static_cast<AlsaEngine *>(arg))->AudioThread();
}

void *AlsaEngine::AudioThread()
{
set_realtime();
return processAudio();
}

bool AlsaEngine::Start()
{
return openAudio() && openMidi();
}

void AlsaEngine::Stop()
{
if(getMidiEn())
setMidiEn(false);
if(getAudioEn())
setAudioEn(false);
snd_config_update_free_global();
}

void AlsaEngine::setMidiEn(bool nval)
{
if(nval)
openMidi();
else
stopMidi();
}

bool AlsaEngine::getMidiEn() const
{
return midi.handle;
}

void AlsaEngine::setAudioEn(bool nval)
{
if(nval)
openAudio();
else
stopAudio();
}

bool AlsaEngine::getAudioEn() const
{
return audio.handle;
}

void *AlsaEngine::_MidiThread(void *arg)
{
return static_cast<AlsaEngine *>(arg)->MidiThread();
}


void *AlsaEngine::MidiThread(void)
{
snd_seq_event_t *event;
MidiEvent ev;
set_realtime();
while(snd_seq_event_input(midi.handle, &event) > 0) {
//ensure ev is empty
ev.channel = 0;
ev.num = 0;
ev.value = 0;
ev.type = 0;

if(!event)
continue;
switch(event->type) {
case SND_SEQ_EVENT_NOTEON:
if(event->data.note.note) {
ev.type = M_NOTE;
ev.channel = event->data.note.channel;
ev.num = event->data.note.note;
ev.value = event->data.note.velocity;
InMgr::getInstance().putEvent(ev);
}
break;

case SND_SEQ_EVENT_NOTEOFF:
ev.type = M_NOTE;
ev.channel = event->data.note.channel;
ev.num = event->data.note.note;
ev.value = 0;
InMgr::getInstance().putEvent(ev);
break;

case SND_SEQ_EVENT_KEYPRESS:
ev.type = M_PRESSURE;
ev.channel = event->data.note.channel;
ev.num = event->data.note.note;
ev.value = event->data.note.velocity;
InMgr::getInstance().putEvent(ev);
break;

case SND_SEQ_EVENT_PITCHBEND:
ev.type = M_CONTROLLER;
ev.channel = event->data.control.channel;
ev.num = C_pitchwheel;
ev.value = event->data.control.value;
InMgr::getInstance().putEvent(ev);
break;

case SND_SEQ_EVENT_CONTROLLER:
ev.type = M_CONTROLLER;
ev.channel = event->data.control.channel;
ev.num = event->data.control.param;
ev.value = event->data.control.value;
InMgr::getInstance().putEvent(ev);
break;

case SND_SEQ_EVENT_PGMCHANGE:
ev.type = M_PGMCHANGE;
ev.channel = event->data.control.channel;
ev.num = event->data.control.value;
InMgr::getInstance().putEvent(ev);
break;

case SND_SEQ_EVENT_RESET: // reset to power-on state
ev.type = M_CONTROLLER;
ev.channel = event->data.control.channel;
ev.num = C_resetallcontrollers;
ev.value = 0;
InMgr::getInstance().putEvent(ev);
break;

case SND_SEQ_EVENT_PORT_SUBSCRIBED: // ports connected
if(true)
cout << "Info, alsa midi port connected" << endl;
break;

case SND_SEQ_EVENT_PORT_UNSUBSCRIBED: // ports disconnected
if(true)
cout << "Info, alsa midi port disconnected" << endl;
break;

case SND_SEQ_EVENT_SYSEX: // system exclusive
case SND_SEQ_EVENT_SENSING: // midi device still there
break;

default:
if(true)
cout << "Info, other non-handled midi event, type: "
<< (int)event->type << endl;
break;
}
snd_seq_free_event(event);
}
return NULL;
}

bool AlsaEngine::openMidi()
{
if(getMidiEn())
return true;

int alsaport;
midi.handle = NULL;

if(snd_seq_open(&midi.handle, "default", SND_SEQ_OPEN_INPUT, 0) != 0)
return false;

snd_seq_set_client_name(midi.handle, "ZynAddSubFX");

alsaport = snd_seq_create_simple_port(
midi.handle,
"ZynAddSubFX",
SND_SEQ_PORT_CAP_WRITE
| SND_SEQ_PORT_CAP_SUBS_WRITE,
SND_SEQ_PORT_TYPE_SYNTH);
if(alsaport < 0)
return false;

pthread_attr_t attr;

pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
pthread_create(&midi.pThread, &attr, _MidiThread, this);
return true;
}

void AlsaEngine::stopMidi()
{
if(!getMidiEn())
return;

snd_seq_t *handle = midi.handle;
if((NULL != midi.handle) && midi.pThread)
pthread_cancel(midi.pThread);
midi.handle = NULL;
if(handle)
snd_seq_close(handle);
}

short *AlsaEngine::interleave(const Stereo<float *> &smps)
{
/**\todo TODO fix repeated allocation*/
short *shortInterleaved = audio.buffer;
memset(shortInterleaved, 0, bufferSize * 2 * sizeof(short));
int idx = 0; //possible off by one error here
double scaled;
for(int frame = 0; frame < bufferSize; ++frame) { // with a nod to libsamplerate ...
scaled = smps.l[frame] * (8.0f * 0x10000000);
shortInterleaved[idx++] = (short int)(lrint(scaled) >> 16);
scaled = smps.r[frame] * (8.0f * 0x10000000);
shortInterleaved[idx++] = (short int)(lrint(scaled) >> 16);
}
return shortInterleaved;
}

bool AlsaEngine::openAudio()
{
if(getAudioEn())
return true;

int rc = 0;
/* Open PCM device for playback. */
audio.handle = NULL;
rc = snd_pcm_open(&audio.handle, "hw:0",
SND_PCM_STREAM_PLAYBACK, 0);
if(rc < 0) {
fprintf(stderr,
"unable to open pcm device: %s\n",
snd_strerror(rc));
return false;
}

/* Allocate a hardware parameters object. */
snd_pcm_hw_params_alloca(&audio.params);

/* Fill it in with default values. */
snd_pcm_hw_params_any(audio.handle, audio.params);

/* Set the desired hardware parameters. */

/* Interleaved mode */
snd_pcm_hw_params_set_access(audio.handle, audio.params,
SND_PCM_ACCESS_RW_INTERLEAVED);

/* Signed 16-bit little-endian format */
snd_pcm_hw_params_set_format(audio.handle, audio.params,
SND_PCM_FORMAT_S16_LE);

/* Two channels (stereo) */
snd_pcm_hw_params_set_channels(audio.handle, audio.params, 2);

audio.sampleRate = synth->samplerate;
snd_pcm_hw_params_set_rate_near(audio.handle, audio.params,
&audio.sampleRate, NULL);

audio.frames = 512;
snd_pcm_hw_params_set_period_size_near(audio.handle,
audio.params, &audio.frames, NULL);

audio.periods = 4;
snd_pcm_hw_params_set_periods_near(audio.handle,
audio.params, &audio.periods, NULL);

/* Write the parameters to the driver */
rc = snd_pcm_hw_params(audio.handle, audio.params);
if(rc < 0) {
fprintf(stderr,
"unable to set hw parameters: %s\n",
snd_strerror(rc));
return false;
}

/* Set buffer size (in frames). The resulting latency is given by */
/* latency = periodsize * periods / (rate * bytes_per_frame) */
snd_pcm_hw_params_set_buffer_size(audio.handle,
audio.params,
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);


pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
pthread_create(&audio.pThread, &attr, _AudioThread, this);
return true;
}

void AlsaEngine::stopAudio()
{
if(!getAudioEn())
return;

snd_pcm_t *handle = audio.handle;
audio.handle = NULL;
pthread_join(audio.pThread, NULL);
snd_pcm_drain(handle);
if(snd_pcm_close(handle))
cout << "Error: in snd_pcm_close " << __LINE__ << ' ' << __FILE__
<< endl;
}

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);
if(rc == -EPIPE) {
/* EPIPE means underrun */
cerr << "underrun occurred" << endl;
snd_pcm_prepare(handle);
}
else
if(rc < 0)
cerr << "error from writei: " << snd_strerror(rc) << endl;
}
return NULL;
}

+ 82
- 0
c++/carla-backend/plugins/zynaddsubfx/Nio/AlsaEngine.h View File

@@ -0,0 +1,82 @@
/*
AlsaEngine.h

Copyright 2009, Alan Calvert
2010, 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.

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.

You should have received a copy of the GNU General Public License
along with ZynAddSubFX. If not, see <http://www.gnu.org/licenses/>.
*/

#ifndef ALSA_ENGINE_H
#define ALSA_ENGINE_H

#include <pthread.h>
#include <string>
#include <alsa/asoundlib.h>
#include <queue>

#include "AudioOut.h"
#include "MidiIn.h"
#include "OutMgr.h"
#include "../Misc/Stereo.h"

class AlsaEngine:public AudioOut, MidiIn
{
public:
AlsaEngine();
~AlsaEngine();

bool Start();
void Stop();

void setAudioEn(bool nval);
bool getAudioEn() const;
void setMidiEn(bool nval);
bool getMidiEn() const;

protected:
void *AudioThread();
static void *_AudioThread(void *arg);
void *MidiThread();
static void *_MidiThread(void *arg);

private:
bool openMidi();
void stopMidi();
bool openAudio();
void stopAudio();

short *interleave(const Stereo<float *> &smps);

struct {
std::string device;
snd_seq_t *handle;
int alsaId;
pthread_t pThread;
} midi;

struct {
snd_pcm_t *handle;
snd_pcm_hw_params_t *params;
unsigned int sampleRate;
snd_pcm_uframes_t frames;
unsigned int periods;
short *buffer;
pthread_t pThread;
} audio;

void *processAudio();
};

#endif

+ 58
- 0
c++/carla-backend/plugins/zynaddsubfx/Nio/AudioOut.cpp View File

@@ -0,0 +1,58 @@
/*
ZynAddSubFX - a software synthesizer

AudioOut.h - Audio Output superclass
Copyright (C) 2009-2010 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 <iostream>
#include <cstring>
#include "SafeQueue.h"

using namespace std;

#include "OutMgr.h"
#include "../Misc/Master.h"
#include "AudioOut.h"

AudioOut::AudioOut()
:samplerate(synth->samplerate), bufferSize(synth->buffersize)
{}

AudioOut::~AudioOut()
{}

void AudioOut::setSamplerate(int _samplerate)
{
samplerate = _samplerate;
}

int AudioOut::getSampleRate()
{
return samplerate;
}

void AudioOut::setBufferSize(int _bufferSize)
{
bufferSize = _bufferSize;
}

const Stereo<float *> AudioOut::getNext()
{
return OutMgr::getInstance().tick(bufferSize);
}

+ 61
- 0
c++/carla-backend/plugins/zynaddsubfx/Nio/AudioOut.h View File

@@ -0,0 +1,61 @@
/*
ZynAddSubFX - a software synthesizer

AudioOut.h - Audio Output superclass
Copyright (C) 2009-2010 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 AUDIO_OUT_H
#define AUDIO_OUT_H

#include "../Misc/Stereo.h"
#include "../globals.h"
#include "Engine.h"

class AudioOut:public virtual Engine
{
public:
AudioOut();
virtual ~AudioOut();

/**Sets the Sample Rate of this Output
* (used for getNext()).*/
void setSamplerate(int _samplerate);

/**Sets the Samples required per Out of this driver
* not a realtime opperation */
int getSampleRate();
void setBufferSize(int _bufferSize);

/**Sets the Frame Size for output*/
void bufferingSize(int nBuffering);
int bufferingSize();

virtual void setAudioEn(bool nval) = 0;
virtual bool getAudioEn() const = 0;

protected:
/**Get the next sample for output.
* (has nsamples sampled at a rate of samplerate)*/
const Stereo<float *> getNext();

int samplerate;
int bufferSize;
};

#endif

+ 47
- 0
c++/carla-backend/plugins/zynaddsubfx/Nio/CMakeLists.txt View File

@@ -0,0 +1,47 @@
#Defaults:
# - Wave Output (enabled with the record function)
# - Null Output
# - Null Output Running by default
# - Managed with OutMgr
set(zynaddsubfx_nio_SRCS
WavEngine.cpp
NulEngine.cpp
AudioOut.cpp
MidiIn.cpp
OutMgr.cpp
InMgr.cpp
Engine.cpp
EngineMgr.cpp
Nio.cpp
)

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_lib ${JACK_LIBRARIES})
endif(JackEnable)

if(PaEnable)
include_directories(${PORTAUDIO_INCLUDE_DIR})
list(APPEND zynaddsubfx_nio_SRCS PaEngine.cpp)
list(APPEND zynaddsubfx_nio_lib ${PORTAUDIO_LIBRARIES})
endif(PaEnable)

if(AlsaEnable)
list(APPEND zynaddsubfx_nio_SRCS AlsaEngine.cpp)
list(APPEND zynaddsubfx_nio_lib ${ASOUND_LIBRARY})
endif(AlsaEnable)

if(OssEnable)
list(APPEND zynaddsubfx_nio_SRCS OssEngine.cpp)
endif(OssEnable)


add_library(zynaddsubfx_nio STATIC
${zynaddsubfx_nio_SRCS}
)

+ 28
- 0
c++/carla-backend/plugins/zynaddsubfx/Nio/Engine.cpp View File

@@ -0,0 +1,28 @@
/*
ZynAddSubFX - a software synthesizer

Engine.cpp - Audio Driver base class
Copyright (C) 2009-2010 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 "Engine.h"

Engine::Engine()
{}

Engine::~Engine()
{}

+ 41
- 0
c++/carla-backend/plugins/zynaddsubfx/Nio/Engine.h View File

@@ -0,0 +1,41 @@
/*
ZynAddSubFX - a software synthesizer

Engine.h - Audio Driver base class
Copyright (C) 2009-2010 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 ENGINE_H
#define ENGINE_H
#include <string>
/**Marker for input/output driver*/
class Engine
{
public:
Engine();
virtual ~Engine();

/**Start the Driver with all capabilities
* @return true on success*/
virtual bool Start() = 0;
/**Completely stop the Driver*/
virtual void Stop() = 0;

std::string name;
};
#endif

+ 156
- 0
c++/carla-backend/plugins/zynaddsubfx/Nio/EngineMgr.cpp View File

@@ -0,0 +1,156 @@
#include "EngineMgr.h"
#include <algorithm>
#include <iostream>
#include "Nio.h"
#include "InMgr.h"
#include "OutMgr.h"
#include "AudioOut.h"
#include "MidiIn.h"
#include "NulEngine.h"
#if OSS
#include "OssEngine.h"
#endif
#if ALSA
#include "AlsaEngine.h"
#endif
#if JACK
#include "JackEngine.h"
#endif
#if PORTAUDIO
#include "PaEngine.h"
#endif

using namespace std;

EngineMgr &EngineMgr::getInstance()
{
static EngineMgr instance;
return instance;
}

EngineMgr::EngineMgr()
{
Engine *defaultEng = new NulEngine();

//conditional compiling mess (but contained)
engines.push_back(defaultEng);
#if OSS
engines.push_back(new OssEngine());
#endif
#if ALSA
engines.push_back(new AlsaEngine());
#endif
#if JACK
engines.push_back(new JackEngine());
#endif
#if PORTAUDIO
engines.push_back(new PaEngine());
#endif

defaultOut = dynamic_cast<AudioOut *>(defaultEng);

defaultIn = dynamic_cast<MidiIn *>(defaultEng);

//Accept command line/compile time options
if(!Nio::defaultSink.empty())
setOutDefault(Nio::defaultSink);

if(!Nio::defaultSource.empty())
setInDefault(Nio::defaultSource);
}

EngineMgr::~EngineMgr()
{
for(list<Engine *>::iterator itr = engines.begin();
itr != engines.end(); ++itr)
delete *itr;
}

Engine *EngineMgr::getEng(string name)
{
transform(name.begin(), name.end(), name.begin(), ::toupper);
for(list<Engine *>::iterator itr = engines.begin();
itr != engines.end(); ++itr)
if((*itr)->name == name)
return *itr;
return NULL;
}

bool EngineMgr::start()
{
bool expected = true;
if(!(defaultOut && defaultIn)) {
cerr << "ERROR: It looks like someone broke the Nio Output\n"
<< " Attempting to recover by defaulting to the\n"
<< " Null Engine." << endl;
defaultOut = dynamic_cast<AudioOut *>(getEng("NULL"));
defaultIn = dynamic_cast<MidiIn *>(getEng("NULL"));
}

OutMgr::getInstance(). currentOut = defaultOut;
InMgr::getInstance(). current = defaultIn;

//open up the default output(s)
cout << "Starting Audio: " << defaultOut->name << endl;
defaultOut->setAudioEn(true);
if(defaultOut->getAudioEn())
cout << "Audio Started" << endl;
else {
expected = false;
cerr << "ERROR: The default audio output failed to open!" << endl;
OutMgr::getInstance(). currentOut =
dynamic_cast<AudioOut *>(getEng("NULL"));
OutMgr::getInstance(). currentOut->setAudioEn(true);
}

cout << "Starting MIDI: " << defaultIn->name << endl;
defaultIn->setMidiEn(true);
if(defaultIn->getMidiEn())
cout << "MIDI Started" << endl;
else { //recover
expected = false;
cerr << "ERROR: The default MIDI input failed to open!" << endl;
InMgr::getInstance(). current = dynamic_cast<MidiIn *>(getEng("NULL"));
InMgr::getInstance(). current->setMidiEn(true);
}

//Show if expected drivers were booted
return expected;
}

void EngineMgr::stop()
{
for(list<Engine *>::iterator itr = engines.begin();
itr != engines.end(); ++itr)
(*itr)->Stop();
}

bool EngineMgr::setInDefault(string name)
{
MidiIn *chosen;
if((chosen = dynamic_cast<MidiIn *>(getEng(name)))) { //got the input
defaultIn = chosen;
return true;
}

//Warn user
cerr << "Error: " << name << " is not a recognized MIDI input source"
<< endl;
cerr << " Defaulting to the NULL input source" << endl;

return false;
}

bool EngineMgr::setOutDefault(string name)
{
AudioOut *chosen;
if((chosen = dynamic_cast<AudioOut *>(getEng(name)))) { //got the output
defaultOut = chosen;
return true;
}

//Warn user
cerr << "Error: " << name << " is not a recognized audio backend" << endl;
cerr << " Defaulting to the NULL audio backend" << endl;
return false;
}

+ 43
- 0
c++/carla-backend/plugins/zynaddsubfx/Nio/EngineMgr.h View File

@@ -0,0 +1,43 @@
#ifndef ENGINE_MGR_H
#define ENGINE_MGR_H

#include <list>
#include <string>
#include "Engine.h"


class MidiIn;
class AudioOut;
class OutMgr;
/**Container/Owner of the long lived Engines*/
class EngineMgr
{
public:
static EngineMgr &getInstance();
~EngineMgr();

/**Gets requested engine
* @param name case unsensitive name of engine
* @return pointer to Engine or NULL
*/
Engine *getEng(std::string name);

/**Start up defaults*/
bool start();

/**Stop all engines*/
void stop();

std::list<Engine *> engines;

//return false on failure
bool setInDefault(std::string name);
bool setOutDefault(std::string name);

//default I/O
AudioOut *defaultOut;
MidiIn *defaultIn;
private:
EngineMgr();
};
#endif

+ 129
- 0
c++/carla-backend/plugins/zynaddsubfx/Nio/InMgr.cpp View File

@@ -0,0 +1,129 @@
#include "InMgr.h"
#include "MidiIn.h"
#include "EngineMgr.h"
#include "../Misc/Master.h"
#include <iostream>

using namespace std;

ostream &operator<<(ostream &out, const zMidiEvent &ev)
{
switch(ev.type) {
case M_NOTE:
out << "MidiNote: note(" << ev.num << ")\n"
<< " channel(" << ev.channel << ")\n"
<< " velocity(" << ev.value << ")";
break;

case M_CONTROLLER:
out << "MidiCtl: controller(" << ev.num << ")\n"
<< " channel(" << ev.channel << ")\n"
<< " value(" << ev.value << ")";
break;

case M_PGMCHANGE:
out << "PgmChange: program(" << ev.num << ")\n"
<< " channel(" << ev.channel << ")";
break;
}

return out;
}

zMidiEvent::zMidiEvent()
:channel(0), type(0), num(0), value(0)
{}

InMgr &InMgr::getInstance()
{
static InMgr instance;
return instance;
}

InMgr::InMgr()
:queue(100), master(Master::getInstance())
{
current = NULL;
sem_init(&work, PTHREAD_PROCESS_PRIVATE, 0);
}

InMgr::~InMgr()
{
//lets stop the consumer thread
sem_destroy(&work);
}

void InMgr::putEvent(zMidiEvent ev)
{
if(queue.push(ev)) //check for error
cerr << "ERROR: Midi Ringbuffer is FULL" << endl;
else
sem_post(&work);
}

void InMgr::flush()
{
zMidiEvent ev;
while(!sem_trywait(&work)) {
queue.pop(ev);
//cout << ev << endl;

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);
else
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);
break;

case M_PGMCHANGE:
master.setProgram(ev.channel, ev.num);
break;
case M_PRESSURE:
master.polyphonicAftertouch(ev.channel, ev.num, ev.value);
break;
}
}
}

bool InMgr::setSource(string name)
{
MidiIn *src = getIn(name);

if(!src)
return false;

if(current)
current->setMidiEn(false);
current = src;
current->setMidiEn(true);

bool success = current->getMidiEn();

//Keep system in a valid state (aka with a running driver)
if(!success)
(current = getIn("NULL"))->setMidiEn(true);

return success;
}

string InMgr::getSource() const
{
if(current)
return current->name;
else
return "ERROR";
}

MidiIn *InMgr::getIn(string name)
{
EngineMgr &eng = EngineMgr::getInstance();
return dynamic_cast<MidiIn *>(eng.getEng(name));
}

+ 52
- 0
c++/carla-backend/plugins/zynaddsubfx/Nio/InMgr.h View File

@@ -0,0 +1,52 @@
#ifndef INMGR_H
#define INMGR_H

#include <string>
#include <semaphore.h>
#include "SafeQueue.h"

enum midi_type {
M_NOTE = 1,
M_CONTROLLER = 2,
M_PGMCHANGE = 3,
M_PRESSURE = 4
}; //type=1 for note, type=2 for controller, type=3 for program change
//type=4 for polyphonic aftertouch

struct zMidiEvent {
zMidiEvent();
int channel; //the midi channel for the event
int type; //type=1 for note, type=2 for controller
int num; //note, controller or program number
int value; //velocity or controller value
};

//super simple class to manage the inputs
class InMgr
{
public:
static InMgr &getInstance();
~InMgr();

void putEvent(zMidiEvent ev);

/**Flush the Midi Queue*/
void flush();

bool setSource(std::string name);

std::string getSource() const;

friend class EngineMgr;
private:
InMgr();
class MidiIn *getIn(std::string name);
SafeQueue<zMidiEvent> queue;
sem_t work;
class MidiIn * current;

/**the link to the rest of zyn*/
class Master & master;
};

#endif

+ 402
- 0
c++/carla-backend/plugins/zynaddsubfx/Nio/JackEngine.cpp View File

@@ -0,0 +1,402 @@
/*
JackEngine.cpp

Copyright 2009, Alan Calvert

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.

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.

You should have received a copy of the GNU General Public License
along with yoshimi. If not, see <http://www.gnu.org/licenses/>.
*/

#include <iostream>

#include <jack/midiport.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <cassert>
#include <cstring>

#include "Nio.h"
#include "InMgr.h"

#include "JackEngine.h"

using namespace std;

extern char *instance_name;

JackEngine::JackEngine()
:AudioOut(), jackClient(NULL)
{
name = "JACK";
audio.jackSamplerate = 0;
audio.jackNframes = 0;
for(int i = 0; i < 2; ++i) {
audio.ports[i] = NULL;
audio.portBuffs[i] = NULL;
}
midi.inport = NULL;
}

bool JackEngine::connectServer(string server)
{
bool autostart_jack = true;
if(jackClient)
return true;

string clientname = "zynaddsubfx";
string postfix = Nio::getPostfix();
if(!postfix.empty())
clientname += "_" + postfix;
jack_status_t jackstatus;
bool use_server_name = server.size() && server.compare("default") != 0;
jack_options_t jopts = (jack_options_t)
(((!instance_name
&& use_server_name) ? JackServerName :
JackNullOption)
| ((autostart_jack) ? JackNullOption :
JackNoStartServer));

if(instance_name)
jackClient = jack_client_open(instance_name, jopts, &jackstatus);
else {
if(use_server_name)
jackClient = jack_client_open(
clientname.c_str(), jopts, &jackstatus,
server.c_str());
else
jackClient = jack_client_open(
clientname.c_str(), jopts, &jackstatus);
}


if(NULL != jackClient)
return true;
else
cerr << "Error, failed to open jack client on server: " << server
<< " status " << jackstatus << endl;
return false;
}

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)))
cerr << "Error setting jack xrun callback" << endl;
if(jack_set_process_callback(jackClient, _processCallback, this)) {
cerr << "Error, JackEngine failed to set process callback" << endl;
return false;
}
if(jack_activate(jackClient)) {
cerr << "Error, failed to activate jack client" << endl;
return false;
}

return true;
}
else
cerr << "Error, NULL jackClient through Start()" << endl;
return false;
}

void JackEngine::disconnectJack()
{
if(jackClient) {
cout << "Deactivating and closing JACK client" << endl;

jack_deactivate(jackClient);
jack_client_close(jackClient);
jackClient = NULL;
}
}

bool JackEngine::Start()
{
return openMidi() && openAudio();
}

void JackEngine::Stop()
{
stopMidi();
stopAudio();
}

void JackEngine::setMidiEn(bool nval)
{
if(nval)
openMidi();
else
stopMidi();
}

bool JackEngine::getMidiEn() const
{
return midi.inport;
}

void JackEngine::setAudioEn(bool nval)
{
if(nval)
openAudio();
else
stopAudio();
}

bool JackEngine::getAudioEn() const
{
return audio.ports[0];
}

bool JackEngine::openAudio()
{
if(getAudioEn())
return true;

if(!getMidiEn())
if(!connectJack())
return false;


const char *portnames[] = { "out_1", "out_2" };
for(int port = 0; port < 2; ++port)
audio.ports[port] = jack_port_register(
jackClient,
portnames[port],
JACK_DEFAULT_AUDIO_TYPE,
JackPortIsOutput
| JackPortIsTerminal,
0);
if((NULL != audio.ports[0]) && (NULL != audio.ports[1])) {
audio.jackSamplerate = jack_get_sample_rate(jackClient);
audio.jackNframes = jack_get_buffer_size(jackClient);
samplerate = audio.jackSamplerate;
bufferSize = audio.jackNframes;


//Attempt to autoConnect when specified
if(Nio::autoConnect) {
const char **outPorts = jack_get_ports(
jackClient,
NULL,
NULL,
JackPortIsPhysical
| JackPortIsInput);
if(outPorts != NULL) {
//Verify that stereo is available
assert(outPorts[0]);
assert(outPorts[1]);

//Connect to physical outputs
jack_connect(jackClient, jack_port_name(
audio.ports[0]), outPorts[0]);
jack_connect(jackClient, jack_port_name(
audio.ports[1]), outPorts[1]);
}
else
cerr << "Warning, No outputs to autoconnect to" << endl;
}
return true;
}
else
cerr << "Error, failed to register jack audio ports" << endl;
return false;
}

void JackEngine::stopAudio()
{
for(int i = 0; i < 2; ++i) {
jack_port_t *port = audio.ports[i];
audio.ports[i] = NULL;
if(NULL != port)
jack_port_unregister(jackClient, port);
}
if(!getMidiEn())
disconnectJack();
}

bool JackEngine::openMidi()
{
if(getMidiEn())
return true;
if(!getAudioEn())
if(!connectJack())
return false;

midi.inport = jack_port_register(jackClient, "midi_input",
JACK_DEFAULT_MIDI_TYPE,
JackPortIsInput | JackPortIsTerminal, 0);
return midi.inport;
}

void JackEngine::stopMidi()
{
jack_port_t *port = midi.inport;
midi.inport = NULL;
if(port)
jack_port_unregister(jackClient, port);

if(!getAudioEn())
disconnectJack();
}

int JackEngine::clientId()
{
if(NULL != jackClient)
return (long)jack_client_thread_id(jackClient);
else
return -1;
}

string JackEngine::clientName()
{
if(NULL != jackClient)
return string(jack_get_client_name(jackClient));
else
cerr << "Error, clientName() with null jackClient" << endl;
return string("Oh, yoshimi :-(");
}

int JackEngine::_processCallback(jack_nframes_t nframes, void *arg)
{
return static_cast<JackEngine *>(arg)->processCallback(nframes);
}

int JackEngine::processCallback(jack_nframes_t nframes)
{
bool okaudio = true;

if((NULL != audio.ports[0]) && (NULL != audio.ports[1]))
okaudio = processAudio(nframes);
if(okaudio)
handleMidi(nframes);
return okaudio ? 0 : -1;
}

bool JackEngine::processAudio(jack_nframes_t nframes)
{
for(int port = 0; port < 2; ++port) {
audio.portBuffs[port] =
(jsample_t *)jack_port_get_buffer(audio.ports[port], nframes);
if(NULL == audio.portBuffs[port]) {
cerr << "Error, failed to get jack audio port buffer: "
<< port << endl;
return false;
}
}

Stereo<float *> smp = getNext();

//Assumes size of smp.l == nframes
memcpy(audio.portBuffs[0], smp.l, bufferSize * sizeof(float));
memcpy(audio.portBuffs[1], smp.r, bufferSize * sizeof(float));
return true;
}

int JackEngine::_xrunCallback(void *)
{
cerr << "Jack reports xrun" << endl;
return 0;
}

void JackEngine::_errorCallback(const char *msg)
{
cerr << "Jack reports error: " << msg << endl;
}

void JackEngine::_infoCallback(const char *msg)
{
cerr << "Jack info message: " << msg << endl;
}

int JackEngine::_bufferSizeCallback(jack_nframes_t nframes, void *arg)
{
return static_cast<JackEngine *>(arg)->bufferSizeCallback(nframes);
}

int JackEngine::bufferSizeCallback(jack_nframes_t nframes)
{
cerr << "Jack buffer resized" << endl;
setBufferSize(nframes);
return 0;
}

void JackEngine::handleMidi(unsigned long frames)
{
if(!midi.inport)
return;
void *midi_buf = jack_port_get_buffer(midi.inport, frames);
jack_midi_event_t jack_midi_event;
jack_nframes_t event_index = 0;
unsigned char *midi_data;
unsigned char type;

while(jack_midi_event_get(&jack_midi_event, midi_buf,
event_index++) == 0) {
MidiEvent ev;
midi_data = jack_midi_event.buffer;
type = midi_data[0] & 0xF0;
ev.channel = midi_data[0] & 0x0F;

switch(type) {
case 0x80: /* note-off */
ev.type = M_NOTE;
ev.num = midi_data[1];
ev.value = 0;
InMgr::getInstance().putEvent(ev);
break;

case 0x90: /* note-on */
ev.type = M_NOTE;
ev.num = midi_data[1];
ev.value = midi_data[2];
InMgr::getInstance().putEvent(ev);
break;

case 0xA0: /* pressure, aftertouch */
ev.type = M_PRESSURE;
ev.num = midi_data[1];
ev.value = midi_data[2];
InMgr::getInstance().putEvent(ev);
break;

case 0xB0: /* controller */
ev.type = M_CONTROLLER;
ev.num = midi_data[1];
ev.value = midi_data[2];
InMgr::getInstance().putEvent(ev);
break;

case 0xC0: /* program change */
ev.type = M_PGMCHANGE;
ev.num = midi_data[1];
ev.value = 0;
InMgr::getInstance().putEvent(ev);
break;

case 0xE0: /* pitch bend */
ev.type = M_CONTROLLER;
ev.num = C_pitchwheel;
ev.value = ((midi_data[2] << 7) | midi_data[1]) - 8192;
InMgr::getInstance().putEvent(ev);
break;

/* XXX TODO: handle MSB/LSB controllers and RPNs and NRPNs */
}
}
}

+ 88
- 0
c++/carla-backend/plugins/zynaddsubfx/Nio/JackEngine.h View File

@@ -0,0 +1,88 @@
/*
JackEngine.h

Copyright 2009, Alan Calvert

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.

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.

You should have received a copy of the GNU General Public License
along with yoshimi. If not, see <http://www.gnu.org/licenses/>.
*/

#ifndef JACK_ENGINE_H
#define JACK_ENGINE_H

#include <string>
#include <pthread.h>
#include <semaphore.h>
#include <jack/jack.h>
#include <pthread.h>

#include "MidiIn.h"
#include "AudioOut.h"

typedef jack_default_audio_sample_t jsample_t;

class JackEngine:public AudioOut, MidiIn
{
public:
JackEngine();
~JackEngine() { }

bool Start();
void Stop();

void setMidiEn(bool nval);
bool getMidiEn() const;

void setAudioEn(bool nval);
bool getAudioEn() const;

int getBuffersize() { return audio.jackNframes; }

std::string clientName();
int clientId();

protected:

int processCallback(jack_nframes_t nframes);
static int _processCallback(jack_nframes_t nframes, void *arg);
int bufferSizeCallback(jack_nframes_t nframes);
static int _bufferSizeCallback(jack_nframes_t nframes, void *arg);
static void _errorCallback(const char *msg);
static void _infoCallback(const char *msg);
static int _xrunCallback(void *arg);

private:
bool connectServer(std::string server);
bool connectJack();
void disconnectJack();
bool openAudio();
void stopAudio();
bool processAudio(jack_nframes_t nframes);
bool openMidi();
void stopMidi();

jack_client_t *jackClient;
struct audio {
unsigned int jackSamplerate;
unsigned int jackNframes;
jack_port_t *ports[2];
jsample_t *portBuffs[2];
} audio;
struct midi {
jack_port_t *inport;
} midi;

void handleMidi(unsigned long frames);
};

#endif

+ 77
- 0
c++/carla-backend/plugins/zynaddsubfx/Nio/MidiIn.cpp View File

@@ -0,0 +1,77 @@
/*
ZynAddSubFX - a software synthesizer

MidiIn.C - This class is inherited by all the Midi input classes
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 "MidiIn.h"
#include "../globals.h"
#include "InMgr.h"

void MidiIn::midiProcess(unsigned char head,
unsigned char num,
unsigned char value)
{
MidiEvent ev;
unsigned char chan = head & 0x0f;
switch(head & 0xf0) {
case 0x80: //Note Off
ev.type = M_NOTE;
ev.channel = chan;
ev.num = num;
ev.value = 0;
InMgr::getInstance().putEvent(ev);
break;
case 0x90: //Note On
ev.type = M_NOTE;
ev.channel = chan;
ev.num = num;
ev.value = value;
InMgr::getInstance().putEvent(ev);
break;
case 0xA0: /* pressure, aftertouch */
ev.type = M_PRESSURE;
ev.channel = chan;
ev.num = num;
ev.value = value;
InMgr::getInstance().putEvent(ev);
break;
case 0xb0: //Controller
ev.type = M_CONTROLLER;
ev.channel = chan;
ev.num = num;
ev.value = value;
InMgr::getInstance().putEvent(ev);
break;
case 0xc0: //Program Change
ev.type = M_PGMCHANGE;
ev.channel = chan;
ev.num = num;
ev.value = 0;
InMgr::getInstance().putEvent(ev);
break;
case 0xe0: //Pitch Wheel
ev.type = M_CONTROLLER;
ev.channel = chan;
ev.num = C_pitchwheel;
ev.value = (num + value * (int) 128) - 8192;
InMgr::getInstance().putEvent(ev);
break;
}
}

+ 43
- 0
c++/carla-backend/plugins/zynaddsubfx/Nio/MidiIn.h View File

@@ -0,0 +1,43 @@
/*
ZynAddSubFX - a software synthesizer

MidiIn.h - This class is inherited by all the Midi input classes
Copyright (C) 2002-2005 Nasca Octavian Paul
Copyright (C) 2009-2010 Mark McCurry
Author: Nasca Octavian Paula
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 MIDI_IN_H
#define MIDI_IN_H

#include "Engine.h"

/**This class is inherited by all the Midi input classes*/
class MidiIn:public virtual Engine
{
public:
/**Enables or disables driver based upon value*/
virtual void setMidiEn(bool nval) = 0;
/**Returns if driver is initialized*/
virtual bool getMidiEn() const = 0;
static void midiProcess(unsigned char head,
unsigned char num,
unsigned char value);
};

#endif

+ 117
- 0
c++/carla-backend/plugins/zynaddsubfx/Nio/Nio.cpp View File

@@ -0,0 +1,117 @@
#include "Nio.h"
#include "OutMgr.h"
#include "InMgr.h"
#include "EngineMgr.h"
#include "MidiIn.h"
#include "AudioOut.h"
#include <iostream>
#include <algorithm>
using std::string;
using std::set;
using std::cerr;
using std::endl;

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;

void Nio::init(void)
{
in = &InMgr::getInstance(); //Enable input wrapper
out = &OutMgr::getInstance(); //Initialize the Output Systems
eng = &EngineMgr::getInstance(); //Initialize The Engines
}

bool Nio::start()
{
init();
return eng->start();
}

void Nio::stop()
{
eng->stop();
}

void Nio::setDefaultSource(string name)
{
std::transform(name.begin(), name.end(), name.begin(), ::toupper);
defaultSource = name;
}

void Nio::setDefaultSink(string name)
{
std::transform(name.begin(), name.end(), name.begin(), ::toupper);
defaultSink = name;
}

bool Nio::setSource(string name)
{
return in->setSource(name);
}

bool Nio::setSink(string name)
{
return out->setSink(name);
}

void Nio::setPostfix(std::string post)
{
postfix = post;
}

std::string Nio::getPostfix(void)
{
return postfix;
}

set<string> Nio::getSources(void)
{
set<string> sources;
for(std::list<Engine *>::iterator itr = eng->engines.begin();
itr != eng->engines.end(); ++itr)
if(dynamic_cast<MidiIn *>(*itr))
sources.insert((*itr)->name);
return sources;
}

set<string> Nio::getSinks(void)
{
set<string> sinks;
for(std::list<Engine *>::iterator itr = eng->engines.begin();
itr != eng->engines.end(); ++itr)
if(dynamic_cast<AudioOut *>(*itr))
sinks.insert((*itr)->name);
return sinks;
}

string Nio::getSource()
{
return in->getSource();
}

string Nio::getSink()
{
return out->getSink();
}

#if JACK
#include <jack/jack.h>
void Nio::preferedSampleRate(unsigned &rate)
{
jack_client_t *client = jack_client_open("temp-client",
JackNoStartServer, 0);
if(client) {
rate = jack_get_sample_rate(client);
jack_client_close(client);
}
}
#else
void Nio::preferedSampleRate(unsigned &)
{}
#endif

+ 38
- 0
c++/carla-backend/plugins/zynaddsubfx/Nio/Nio.h View File

@@ -0,0 +1,38 @@
#ifndef NIO_H
#define NIO_H
#include <string>
#include <set>

/**Interface to Nio Subsystem
*
* Should be only externally included header */
namespace Nio
{
void init(void);
bool start(void);
void stop(void);

void setDefaultSource(std::string name);
void setDefaultSink(std::string name);

bool setSource(std::string name);
bool setSink(std::string name);

void setPostfix(std::string post);
std::string getPostfix(void);

std::set<std::string> getSources(void);
std::set<std::string> getSinks(void);

std::string getSource(void);
std::string getSink(void);

//Get the prefered sample rate from jack (if running)
void preferedSampleRate(unsigned &rate);

extern bool autoConnect;
extern std::string defaultSource;
extern std::string defaultSink;
};

#endif

+ 113
- 0
c++/carla-backend/plugins/zynaddsubfx/Nio/NulEngine.cpp View File

@@ -0,0 +1,113 @@
/*
ZynAddSubFX - a software synthesizer

OSSaudiooutput.C - Audio output for Open Sound System
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 "NulEngine.h"
#include "../globals.h"

#include <unistd.h>
#include <iostream>

using namespace std;

NulEngine::NulEngine()
:AudioOut(), pThread(NULL)
{
name = "NULL";
playing_until.tv_sec = 0;
playing_until.tv_usec = 0;
}

void *NulEngine::_AudioThread(void *arg)
{
return (static_cast<NulEngine *>(arg))->AudioThread();
}

void *NulEngine::AudioThread()
{
while(pThread) {
getNext();

struct timeval now;
int remaining = 0;
gettimeofday(&now, NULL);
if((playing_until.tv_usec == 0) && (playing_until.tv_sec == 0)) {
playing_until.tv_usec = now.tv_usec;
playing_until.tv_sec = now.tv_sec;
}
else {
remaining = (playing_until.tv_usec - now.tv_usec)
+ (playing_until.tv_sec - now.tv_sec) * 1000000;
if(remaining > 10000) //Don't sleep() less than 10ms.
//This will add latency...
usleep(remaining - 10000);
if(remaining < 0)
cerr << "WARNING - too late" << endl;
}
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;
playing_until.tv_usec %= 1000000;
}
return NULL;
}

NulEngine::~NulEngine()
{}

bool NulEngine::Start()
{
setAudioEn(true);
return getAudioEn();
}

void NulEngine::Stop()
{
setAudioEn(false);
}

void NulEngine::setAudioEn(bool nval)
{
if(nval) {
if(!getAudioEn()) {
pthread_t *thread = new pthread_t;
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
pThread = thread;
pthread_create(pThread, &attr, _AudioThread, this);
}
}
else
if(getAudioEn()) {
pthread_t *thread = pThread;
pThread = NULL;
pthread_join(*thread, NULL);
delete thread;
}
}

bool NulEngine::getAudioEn() const
{
return pThread;
}

+ 56
- 0
c++/carla-backend/plugins/zynaddsubfx/Nio/NulEngine.h View File

@@ -0,0 +1,56 @@
/*
ZynAddSubFX - a software synthesizer

NulEngine.h - Dummy In/Out driver
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 NUL_ENGINE_H
#define NUL_ENGINE_H

#include <sys/time.h>
#include <pthread.h>
#include "../globals.h"
#include "AudioOut.h"
#include "MidiIn.h"

class NulEngine:public AudioOut, MidiIn
{
public:
NulEngine();
~NulEngine();

bool Start();
void Stop();

void setAudioEn(bool nval);
bool getAudioEn() const;

void setMidiEn(bool) {}
bool getMidiEn() const {return true; }

protected:
void *AudioThread();
static void *_AudioThread(void *arg);

private:
struct timeval playing_until;
pthread_t *pThread;
};

#endif

+ 281
- 0
c++/carla-backend/plugins/zynaddsubfx/Nio/OssEngine.cpp View File

@@ -0,0 +1,281 @@
/*
ZynAddSubFX - a software synthesizer

OSSaudiooutput.C - Audio output for Open Sound System
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 "OssEngine.h"
#include "../Misc/Util.h"
#include "../globals.h"

#include <cstring>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <sys/soundcard.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <iostream>

#include "InMgr.h"

using namespace std;

OssEngine::OssEngine()
:AudioOut(), engThread(NULL)
{
name = "OSS";

midi.handle = -1;
audio.handle = -1;

audio.smps = new short[synth->buffersize * 2];
memset(audio.smps, 0, synth->bufferbytes);
}

OssEngine::~OssEngine()
{
Stop();
delete [] audio.smps;
}

bool OssEngine::openAudio()
{
if(audio.handle != -1)
return true; //already open

int snd_bitsize = 16;
int snd_fragment = 0x00080009; //fragment size (?);
int snd_stereo = 1; //stereo;
int snd_format = AFMT_S16_LE;
int snd_samplerate = synth->samplerate;

audio.handle = open(config.cfg.LinuxOSSWaveOutDev, O_WRONLY, 0);
if(audio.handle == -1) {
cerr << "ERROR - I can't open the "
<< config.cfg.LinuxOSSWaveOutDev << '.' << endl;
return false;
}
ioctl(audio.handle, SNDCTL_DSP_RESET, NULL);
ioctl(audio.handle, SNDCTL_DSP_SETFMT, &snd_format);
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);
}

return true;
}

void OssEngine::stopAudio()
{
int handle = audio.handle;
if(handle == -1) //already closed
return;
audio.handle = -1;

if(!getMidiEn() && engThread)
pthread_join(*engThread, NULL);
delete engThread;
engThread = NULL;

close(handle);
}

bool OssEngine::Start()
{
bool good = true;

if(!openAudio()) {
cerr << "Failed to open OSS audio" << endl;
good = false;
}

if(!openMidi()) {
cerr << "Failed to open OSS midi" << endl;
good = false;
}

return good;
}

void OssEngine::Stop()
{
stopAudio();
stopMidi();
}

void OssEngine::setMidiEn(bool nval)
{
if(nval)
openMidi();
else
stopMidi();
}

bool OssEngine::getMidiEn() const
{
return midi.handle != -1;
}

void OssEngine::setAudioEn(bool nval)
{
if(nval)
openAudio();
else
stopAudio();
}

bool OssEngine::getAudioEn() const
{
return audio.handle != -1;
}

bool OssEngine::openMidi()
{
int handle = midi.handle;
if(handle != -1)
return true; //already open

handle = open(config.cfg.LinuxOSSSeqInDev, O_RDONLY, 0);

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);
}

return true;
}

void OssEngine::stopMidi()
{
int handle = midi.handle;
if(handle == -1) //already closed
return;

midi.handle = -1;

if(!getAudioEn() && engThread) {
pthread_join(*engThread, NULL);
delete engThread;
engThread = NULL;
}

close(handle);
}

void *OssEngine::_thread(void *arg)
{
return (static_cast<OssEngine *>(arg))->thread();
}

void *OssEngine::thread()
{
unsigned char tmp[4] = {0, 0, 0, 0};
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
if(l > 1.0f)
l = 1.0f;
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);
}
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);

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]);
}
}
pthread_exit(NULL);
return NULL;
}

void OssEngine::getMidi(unsigned char *midiPtr)
{
read(midi.handle, midiPtr, 1);
}

+ 76
- 0
c++/carla-backend/plugins/zynaddsubfx/Nio/OssEngine.h View File

@@ -0,0 +1,76 @@
/*
ZynAddSubFX - a software synthesizer

OSSaudiooutput.h - Audio output for Open Sound System
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 OSS_ENGINE_H
#define OSS_ENGINE_H

#include <sys/time.h>
#include "../globals.h"
#include "AudioOut.h"
#include "MidiIn.h"

class OssEngine:public AudioOut, MidiIn
{
public:
OssEngine();
~OssEngine();

bool Start();
void Stop();

void setAudioEn(bool nval);
bool getAudioEn() const;

void setMidiEn(bool nval);
bool getMidiEn() const;


protected:
void *thread();
static void *_thread(void *arg);

private:
pthread_t *engThread;

//Audio
bool openAudio();
void stopAudio();

struct audio {
int handle;
short int *smps; //Samples to be sent to soundcard
bool en;
} audio;

//Midi
bool openMidi();
void stopMidi();
void getMidi(unsigned char *midiPtr);

struct midi {
int handle;
bool en;
bool run;
} midi;
};

#endif

+ 182
- 0
c++/carla-backend/plugins/zynaddsubfx/Nio/OutMgr.cpp View File

@@ -0,0 +1,182 @@
#include "OutMgr.h"
#include <algorithm>
#include <iostream>
#include <cassert>
#include "AudioOut.h"
#include "Engine.h"
#include "EngineMgr.h"
#include "InMgr.h"
#include "WavEngine.h"
#include "../Misc/Master.h"
#include "../Misc/Util.h" //for set_realtime()

using namespace std;

OutMgr &OutMgr::getInstance()
{
static OutMgr instance;
return instance;
}

OutMgr::OutMgr()
:wave(new WavEngine()),
priBuf(new float[4096],
new float[4096]), priBuffCurrent(priBuf),
master(Master::getInstance())
{
currentOut = NULL;
stales = 0;
master = Master::getInstance();

//init samples
outr = new float[synth->buffersize];
outl = new float[synth->buffersize];
memset(outl, 0, synth->bufferbytes);
memset(outr, 0, synth->bufferbytes);
}

OutMgr::~OutMgr()
{
delete wave;
delete [] priBuf.l;
delete [] priBuf.r;
delete [] outr;
delete [] outl;
}

/* Sequence of a tick
* 1) lets see if we have any stuff to do via midi
* 2) Lets do that stuff
* 3) Lets see if the event queue has anything for us
* 4) Lets empty that out
* 5) Lets remove old/stale samples
* 6) Lets see if we need to generate samples
* 7) Lets generate some
* 8) Lets return those samples to the primary and secondary outputs
* 9) Lets wait for another tick
*/
const Stereo<float *> OutMgr::tick(unsigned int frameSize)
{
pthread_mutex_lock(&(master.mutex));
InMgr::getInstance().flush();
pthread_mutex_unlock(&(master.mutex));
//SysEv->execute();
removeStaleSmps();
while(frameSize > storedSmps()) {
pthread_mutex_lock(&(master.mutex));
master.AudioOut(outl, outr);
pthread_mutex_unlock(&(master.mutex));
addSmps(outl, outr);
}
stales = frameSize;
return priBuf;
}

AudioOut *OutMgr::getOut(string name)
{
return dynamic_cast<AudioOut *>(EngineMgr::getInstance().getEng(name));
}

string OutMgr::getDriver() const
{
return currentOut->name;
}

bool OutMgr::setSink(string name)
{
AudioOut *sink = getOut(name);

if(!sink)
return false;

if(currentOut)
currentOut->setAudioEn(false);

currentOut = sink;
currentOut->setAudioEn(true);

bool success = currentOut->getAudioEn();

//Keep system in a valid state (aka with a running driver)
if(!success)
(currentOut = getOut("NULL"))->setAudioEn(true);

return success;
}

string OutMgr::getSink() const
{
if(currentOut)
return currentOut->name;
else {
cerr << "BUG: No current output in OutMgr " << __LINE__ << endl;
return "ERROR";
}
return "ERROR";
}

//perform a cheap linear interpolation for resampling
//This will result in some distortion at frame boundries
//returns number of samples produced
static size_t resample(float *dest,
const float *src,
float s_in,
float s_out,
size_t elms)
{
size_t out_elms = elms * s_out / s_in;
float r_pos = 0.0f;
for(int i = 0; i < (int)out_elms; ++i, r_pos += s_in / s_out)
dest[i] = interpolate(src, elms, r_pos);

return out_elms;
}

void OutMgr::addSmps(float *l, float *r)
{
//allow wave file to syphon off stream
wave->push(Stereo<float *>(l, r), synth->buffersize);

const int s_out = currentOut->getSampleRate(),
s_sys = synth->samplerate;

if(s_out != s_sys) { //we need to resample
const size_t steps = resample(priBuffCurrent.l,
l,
s_sys,
s_out,
synth->buffersize);
resample(priBuffCurrent.r, r, s_sys, s_out, synth->buffersize);

priBuffCurrent.l += steps;
priBuffCurrent.r += steps;
}
else { //just copy the samples
memcpy(priBuffCurrent.l, l, synth->bufferbytes);
memcpy(priBuffCurrent.r, r, synth->bufferbytes);
priBuffCurrent.l += synth->buffersize;
priBuffCurrent.r += synth->buffersize;
}
}

void OutMgr::removeStaleSmps()
{
if(!stales)
return;

const int leftover = storedSmps() - stales;

assert(leftover > -1);

//leftover samples [seen at very low latencies]
if(leftover) {
memmove(priBuf.l, priBuffCurrent.l - leftover, leftover * sizeof(float));
memmove(priBuf.r, priBuffCurrent.r - leftover, leftover * sizeof(float));
priBuffCurrent.l = priBuf.l + leftover;
priBuffCurrent.r = priBuf.r + leftover;
}
else
priBuffCurrent = priBuf;

stales = 0;
}

+ 64
- 0
c++/carla-backend/plugins/zynaddsubfx/Nio/OutMgr.h View File

@@ -0,0 +1,64 @@
#ifndef OUTMGR_H
#define OUTMGR_H

#include "../Misc/Stereo.h"
#include <list>
#include <string>
#include <semaphore.h>


class AudioOut;
class OutMgr
{
public:
static OutMgr &getInstance();
~OutMgr();

/**Execute a tick*/
const Stereo<float *> tick(unsigned int frameSize);

/**Request a new set of samples
* @param n number of requested samples (defaults to 1)
* @return -1 for locking issues 0 for valid request*/
void requestSamples(unsigned int n = 1);

/**Gets requested driver
* @param name case unsensitive name of driver
* @return pointer to Audio Out or NULL
*/
AudioOut *getOut(std::string name);

/**Gets the name of the first running driver
* Deprecated
* @return if no running output, "" is returned
*/
std::string getDriver() const;

bool setSink(std::string name);

std::string getSink() const;

class WavEngine * wave; /**<The Wave Recorder*/
friend class EngineMgr;
private:
OutMgr();
void addSmps(float *l, float *r);
unsigned int storedSmps() const {return priBuffCurrent.l - priBuf.l; }
void removeStaleSmps();

AudioOut *currentOut; /**<The current output driver*/

sem_t requested;

/**Buffer*/
Stereo<float *> priBuf; //buffer for primary drivers
Stereo<float *> priBuffCurrent; //current array accessor

float *outl;
float *outr;
class Master & master;

int stales;
};

#endif

+ 118
- 0
c++/carla-backend/plugins/zynaddsubfx/Nio/PaEngine.cpp View File

@@ -0,0 +1,118 @@
/*
ZynAddSubFX - a software synthesizer

PaEngine.cpp - Audio output for PortAudio
Copyright (C) 2002 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 "PaEngine.h"
#include <iostream>

using namespace std;

PaEngine::PaEngine()
:stream(NULL)
{
name = "PA";
}


PaEngine::~PaEngine()
{
Stop();
}

bool PaEngine::Start()
{
if(getAudioEn())
return true;
Pa_Initialize();

PaStreamParameters outputParameters;
outputParameters.device = Pa_GetDefaultOutputDevice();
if(outputParameters.device == paNoDevice) {
cerr << "Error: No default output device." << endl;
Pa_Terminate();
return false;
}
outputParameters.channelCount = 2; /* stereo output */
outputParameters.sampleFormat = paFloat32; /* 32 bit floating point output */
outputParameters.suggestedLatency =
Pa_GetDeviceInfo(outputParameters.device)->defaultLowOutputLatency;
outputParameters.hostApiSpecificStreamInfo = NULL;


Pa_OpenStream(&stream,
NULL,
&outputParameters,
synth->samplerate,
synth->buffersize,
0,
PAprocess,
(void *) this);
Pa_StartStream(stream);
return true;
}

void PaEngine::setAudioEn(bool nval)
{
if(nval)
Start();
else
Stop();
}

bool PaEngine::getAudioEn() const
{
return stream;
}

int PaEngine::PAprocess(const void *inputBuffer,
void *outputBuffer,
unsigned long framesPerBuffer,
const PaStreamCallbackTimeInfo *outTime,
PaStreamCallbackFlags flags,
void *userData)
{
(void) inputBuffer;
(void) outTime;
(void) flags;
return static_cast<PaEngine *>(userData)->process((float *) outputBuffer,
framesPerBuffer);
}

int PaEngine::process(float *out, unsigned long framesPerBuffer)
{
const Stereo<float *> smp = getNext();
for(unsigned i = 0; i < framesPerBuffer; ++i) {
*out++ = smp.l[i];
*out++ = smp.r[i];
}

return 0;
}

void PaEngine::Stop()
{
if(!getAudioEn())
return;
Pa_StopStream(stream);
Pa_CloseStream(stream);
stream = NULL;
Pa_Terminate();
}

+ 57
- 0
c++/carla-backend/plugins/zynaddsubfx/Nio/PaEngine.h View File

@@ -0,0 +1,57 @@
/*
ZynAddSubFX - a software synthesizer

PAaudiooutput.h - Audio output for PortAudio
Copyright (C) 2002 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 PA_ENGINE_H
#define PA_ENGINE_H

#include <portaudio.h>

#include "../globals.h"
#include "AudioOut.h"

class PaEngine:public AudioOut
{
public:
PaEngine();
~PaEngine();

bool Start();
void Stop();

void setAudioEn(bool nval);
bool getAudioEn() const;

protected:
static int PAprocess(const void *inputBuffer,
void *outputBuffer,
unsigned long framesPerBuffer,
const PaStreamCallbackTimeInfo *outTime,
PaStreamCallbackFlags flags,
void *userData);
int process(float *out, unsigned long framesPerBuffer);
private:
PaStream *stream;
};


void PAfinish();

#endif

+ 82
- 0
c++/carla-backend/plugins/zynaddsubfx/Nio/SafeQueue.cpp View File

@@ -0,0 +1,82 @@

template<class T>
SafeQueue<T>::SafeQueue(size_t maxlen)
:writePtr(0), readPtr(0), bufSize(maxlen)
{
sem_init(&w_space, PTHREAD_PROCESS_PRIVATE, maxlen - 1);
sem_init(&r_space, PTHREAD_PROCESS_PRIVATE, 0);
buffer = new T[maxlen];
}

template<class T>
SafeQueue<T>::~SafeQueue()
{
sem_destroy(&w_space);
sem_destroy(&r_space);
delete [] buffer;
}

template<class T>
unsigned int SafeQueue<T>::size() const
{
return rSpace();
}

template<class T>
unsigned int SafeQueue<T>::rSpace() const
{
int space = 0;
sem_getvalue(&r_space, &space);
return space;
}

template<class T>
unsigned int SafeQueue<T>::wSpace() const
{
int space = 0;
sem_getvalue(&w_space, &space);
return space;
}

template<class T>
int SafeQueue<T>::push(const T &in)
{
if(!wSpace())
return -1;

//ok, there is space to write
size_t w = (writePtr + 1) % bufSize;
buffer[w] = in;
writePtr = w;

//adjust ranges
sem_wait(&w_space); //guaranteed not to wait
sem_post(&r_space);
return 0;
}

template<class T>
int SafeQueue<T>::pop(T &out)
{
if(!rSpace())
return -1;

//ok, there is space to read
size_t r = (readPtr + 1) % bufSize;
out = buffer[r];
readPtr = r;

//adjust ranges
sem_wait(&r_space); //guaranteed not to wait
sem_post(&w_space);
return 0;
}

template<class T>
void SafeQueue<T>::clear()
{
//thread unsafe
while(!sem_trywait(&r_space))
sem_post(&w_space);
readPtr = writePtr;
}

+ 46
- 0
c++/carla-backend/plugins/zynaddsubfx/Nio/SafeQueue.h View File

@@ -0,0 +1,46 @@

#ifndef SAFEQUEUE_H
#define SAFEQUEUE_H
#include <cstdlib>
#include <semaphore.h>

/**
* C++ thread safe lockless queue
* Based off of jack's ringbuffer*/
template<class T>
class SafeQueue
{
public:
SafeQueue(size_t maxlen);
~SafeQueue();

/**Return read size*/
unsigned int size() const;

/**Returns 0 for normal
* Returns -1 on error*/
int push(const T &in);
int pop(T &out);

//clears reading space
void clear();

private:
unsigned int wSpace() const;
unsigned int rSpace() const;

//write space
mutable sem_t w_space;
//read space
mutable sem_t r_space;

//next writing spot
size_t writePtr;
//next reading spot
size_t readPtr;
const size_t bufSize;
T *buffer;
};

#include "SafeQueue.cpp"
#endif

+ 134
- 0
c++/carla-backend/plugins/zynaddsubfx/Nio/WavEngine.cpp View File

@@ -0,0 +1,134 @@
/*
Copyright (C) 2006 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) 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 "WavEngine.h"
#include <cstdio>
#include <iostream>
#include <cstdlib>
#include "../Misc/WavFile.h"
#include "../Misc/Util.h"

using namespace std;

WavEngine::WavEngine()
:AudioOut(), file(NULL), buffer(synth->samplerate * 4), pThread(NULL)
{
sem_init(&work, PTHREAD_PROCESS_PRIVATE, 0);
}

WavEngine::~WavEngine()
{
Stop();
sem_destroy(&work);
destroyFile();
}

bool WavEngine::openAudio()
{
return file && file->good();
}

bool WavEngine::Start()
{
if(pThread)
return true;
pThread = new pthread_t;

pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
pthread_create(pThread, &attr, _AudioThread, this);

return true;
}

void WavEngine::Stop()
{
if(!pThread)
return;

pthread_t *tmp = pThread;
pThread = NULL;

sem_post(&work);
pthread_join(*tmp, NULL);
delete pThread;
}

void WavEngine::push(Stereo<float *> smps, size_t len)
{
if(!pThread)
return;


//copy the input [overflow when needed]
for(size_t i = 0; i < len; ++i) {
buffer.push(*smps.l++);
buffer.push(*smps.r++);
}
sem_post(&work);
}

void WavEngine::newFile(WavFile *_file)
{
//ensure system is clean
destroyFile();
file = _file;

//check state
if(!file->good())
cerr
<< "ERROR: WavEngine handed bad file output WavEngine::newFile()"
<< endl;
}

void WavEngine::destroyFile()
{
if(file)
delete file;
file = NULL;
}

void *WavEngine::_AudioThread(void *arg)
{
return (static_cast<WavEngine *>(arg))->AudioThread();
}

void *WavEngine::AudioThread()
{
short *recordbuf_16bit = new short[2 * synth->buffersize];

while(!sem_wait(&work) && pThread) {
for(int i = 0; i < synth->buffersize; ++i) {
float left = 0.0f, right = 0.0f;
buffer.pop(left);
buffer.pop(right);
recordbuf_16bit[2 * i] = limit((int)(left * 32767.0f),
-32768,
32767);
recordbuf_16bit[2 * i + 1] = limit((int)(right * 32767.0f),
-32768,
32767);
}
file->writeStereoSamples(synth->buffersize, recordbuf_16bit);
}

delete[] recordbuf_16bit;

return NULL;
}

+ 61
- 0
c++/carla-backend/plugins/zynaddsubfx/Nio/WavEngine.h View File

@@ -0,0 +1,61 @@
/*
ZynAddSubFX - a software synthesizer

WavEngine.h - Records sound to a file
Copyright (C) 2008 Nasca Octavian Paul
Author: Nasca Octavian Paul
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) 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 WAVENGINE_H
#define WAVENGINE_H
#include "AudioOut.h"
#include <string>
#include <pthread.h>
#include <semaphore.h>
#include "SafeQueue.h"

class WavFile;
class WavEngine:public AudioOut
{
public:
WavEngine();
~WavEngine();

bool openAudio();
bool Start();
void Stop();

void setAudioEn(bool /*nval*/) {}
bool getAudioEn() const {return true; }

void push(Stereo<float *> smps, size_t len);

void newFile(WavFile *_file);
void destroyFile();

protected:
void *AudioThread();
static void *_AudioThread(void *arg);

private:
WavFile *file;
sem_t work;
SafeQueue<float> buffer;

pthread_t *pThread;
};
#endif

+ 698
- 0
c++/carla-backend/plugins/zynaddsubfx/Output/DSSIaudiooutput.cpp View File

@@ -0,0 +1,698 @@
/*
ZynAddSubFX - a software synthesizer

DSSIaudiooutput.cpp - Audio functions for DSSI
Copyright (C) 2002 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

*/

/*
* Inital working DSSI output code contributed by Stephen G. Parry
*/

//this file contains code used from trivial_synth.c from
//the DSSI (published by Steve Harris under public domain) as a template.

#include "DSSIaudiooutput.h"
#include "../Misc/Config.h"
#include "../Misc/Bank.h"
#include "../Misc/Util.h"
#include <string.h>
#include <limits.h>

using std::string;
using std::vector;

//
// Static stubs for LADSPA member functions
//
// LADSPA is essentially a C handle based API; This plug-in implementation is
// a C++ OO one so we need stub functions to map from C API calls to C++ object
// method calls.
void DSSIaudiooutput::stub_connectPort(LADSPA_Handle instance,
unsigned long port,
LADSPA_Data *data)
{
getInstance(instance)->connectPort(port, data);
}

void DSSIaudiooutput::stub_activate(LADSPA_Handle instance)
{
getInstance(instance)->activate();
}

void DSSIaudiooutput::stub_run(LADSPA_Handle instance,
unsigned long sample_count)
{
getInstance(instance)->run(sample_count);
}

void DSSIaudiooutput::stub_deactivate(LADSPA_Handle instance)
{
getInstance(instance)->deactivate();
}


void DSSIaudiooutput::stub_cleanup(LADSPA_Handle instance)
{
DSSIaudiooutput *plugin_instance = getInstance(instance);
plugin_instance->cleanup();
delete plugin_instance;
}


const LADSPA_Descriptor *ladspa_descriptor(unsigned long index)
{
return DSSIaudiooutput::getLadspaDescriptor(index);
}

//
// Static stubs for DSSI member functions
//
// DSSI is essentially a C handle based API; This plug-in implementation is
// a C++ OO one so we need stub functions to map from C API calls to C++ object
// method calls.
const DSSI_Program_Descriptor *DSSIaudiooutput::stub_getProgram(
LADSPA_Handle instance,
unsigned long index)
{
return getInstance(instance)->getProgram(index);
}

void DSSIaudiooutput::stub_selectProgram(LADSPA_Handle instance,
unsigned long bank,
unsigned long program)
{
getInstance(instance)->selectProgram(bank, program);
}

int DSSIaudiooutput::stub_getMidiControllerForPort(LADSPA_Handle instance,
unsigned long port)
{
return getInstance(instance)->getMidiControllerForPort(port);
}

void DSSIaudiooutput::stub_runSynth(LADSPA_Handle instance,
unsigned long sample_count,
snd_seq_event_t *events,
unsigned long event_count)
{
getInstance(instance)->runSynth(sample_count, events, event_count);
}

const DSSI_Descriptor *dssi_descriptor(unsigned long index)
{
return DSSIaudiooutput::getDssiDescriptor(index);
}

//
// LADSPA member functions
//

/**
* Instantiates a plug-in.
*
* This LADSPA member function instantiates a plug-in.
* Note that instance initialisation should generally occur in
* activate() rather than here.
*
* Zyn Implementation
* ------------------
* This implementation creates a C++ class object and hides its pointer
* in the handle by type casting.
*
* @param descriptor [in] the descriptor for this plug-in
* @param s_rate [in] the sample rate
* @return the plug-in instance handle if successful else NULL
*/
LADSPA_Handle DSSIaudiooutput::instantiate(const LADSPA_Descriptor *descriptor,
unsigned long s_rate)
{
if(descriptor->UniqueID == dssiDescriptor->LADSPA_Plugin->UniqueID)
return (LADSPA_Handle)(new DSSIaudiooutput(s_rate));
else
return NULL;
}

/**
* Connects a port on an instantiated plug-in.
*
* This LADSPA member function connects a port on an instantiated plug-in to a
* memory location at which a block of data for the port will be read/written.
* The data location is expected to be an array of LADSPA_Data for audio ports
* or a single LADSPA_Data value for control ports. Memory issues will be
* managed by the host. The plug-in must read/write the data at these locations
* every time run() or run_adding() is called and the data present at the time
* of this connection call should not be considered meaningful.
*
* Zyn Implementation
* ------------------
* The buffer pointers are stored as member variables
*
* @param port [in] the port to be connected
* @param data [in] the data buffer to write to / read from
*/
void DSSIaudiooutput::connectPort(unsigned long port, LADSPA_Data *data)
{
switch(port) {
case 0:
outl = data;
break;
case 1:
outr = data;
break;
}
}

/**
* Initialises a plug-in instance and activates it for use.
*
* This LADSPA member function initialises a plug-in instance and activates it
* for use. This is separated from instantiate() to aid real-time support and
* so that hosts can reinitialise a plug-in instance by calling deactivate() and
* then activate(). In this case the plug-in instance must reset all state
* information dependent on the history of the plug-in instance except for any
* data locations provided by connect_port() and any gain set by
* set_run_adding_gain().
*
* Zyn Implementation
* ------------------
* Currently this does nothing; Care must be taken as to code placed here as
* too much code here seems to cause time-out problems in jack-dssi-host.
*/
void DSSIaudiooutput::activate()
{}

/**
* Runs an instance of a plug-in for a block.
*
* This LADSPA member function runs an instance of a plug-in for a block.
* Note that if an activate() function exists then it must be called before
* run() or run_adding(). If deactivate() is called for a plug-in instance then
* the plug-in instance may not be reused until activate() has been called again.
*
* Zyn Implementation
* ------------------
* This is a LADSPA function that does not process any MIDI events; it is hence
* implemented by simply calling runSynth() with an empty event list.
*
* @param sample_count [in] the block size (in samples) for which the plug-in instance may run
*/
void DSSIaudiooutput::run(unsigned long sample_count)
{
runSynth(sample_count, NULL, (unsigned long)0);
}

/**
* Counterpart to activate().
*
* This LADSPA member function is the counterpart to activate() (see above).
* Deactivation is not similar to pausing as the plug-in instance will be
* reinitialised when activate() is called to reuse it.
*
* Zyn Implementation
* ------------------
* Currently this function does nothing.
*/
void DSSIaudiooutput::deactivate()
{}

/**
* Deletes a plug-in instance that is no longer required.
*
* LADSPA member function; once an instance of a plug-in has been finished with
* it can be deleted using this function. The instance handle ceases to be
* valid after this call.
*
* If activate() was called for a plug-in instance then a corresponding call to
* deactivate() must be made before cleanup() is called.
*
* Zyn Implementation
* ------------------
* Currently cleanup is deferred to the destructor that is invoked after cleanup()
*/
void DSSIaudiooutput::cleanup()
{}

/**
* Initial entry point for the LADSPA plug-in library.
*
* This LADSPA function is the initial entry point for the plug-in library.
* The LADSPA host looks for this entry point in each shared library object it
* finds and then calls the function to enumerate the plug-ins within the
* library.
*
* Zyn Implementation
* ------------------
* As the Zyn plug-in is a DSSI plug-in, the LADSPA descriptor is embedded inside
* the DSSI descriptor, which is created by DSSIaudiooutput::initDssiDescriptor()
* statically when the library is loaded. This function then merely returns a pointer
* to that embedded descriptor.
*
* @param index [in] the index number of the plug-in within the library.
* @return if index is in range, a pointer to the plug-in descriptor is returned, else NULL
*/
const LADSPA_Descriptor *DSSIaudiooutput::getLadspaDescriptor(
unsigned long index)
{
if((index > 0) || (dssiDescriptor == NULL))
return NULL;
else
return dssiDescriptor->LADSPA_Plugin;
}

//
// DSSI member functions
//

/**
* Provides a description of a program available on this synth.
*
* This DSSI member function pointer provides a description of a program (named
* preset sound) available on this synth.
*
* Zyn Implementation
* ------------------
* The instruments in all Zyn's bank directories, as shown by the `instrument
* -> show instrument bank` command, are enumerated to the host by this
* function, allowing access to all those instruments.
* The first time an instrument is requested, the bank it is in and any
* unmapped ones preceding that are mapped; all the instruments names and
* filenames from those banks are stored in the programMap member variable for
* later use. This is done on demand in this way, rather than up front in one
* go because loading all the instrument names in one go can lead to timeouts
* and zombies.
*
* @param index [in] index into the plug-in's list of
* programs, not a program number as represented by the Program
* field of the DSSI_Program_Descriptor. (This distinction is
* needed to support synths that use non-contiguous program or
* bank numbers.)
* @return a DSSI_Program_Descriptor pointer that is
* guaranteed to be valid only until the next call to get_program,
* deactivate, or configure, on the same plug-in instance, or NULL if index is out of range.
*/
const DSSI_Program_Descriptor *DSSIaudiooutput::getProgram(unsigned long index)
{
static DSSI_Program_Descriptor retVal;

/* Make sure we have the list of banks loaded */
initBanks();

/* Make sure that the bank containing the instrument has been mapped */
while(index >= programMap.size() && mapNextBank())
/* DO NOTHING MORE */;

if(index >= programMap.size())
/* No more instruments */
return NULL;
else {
/* OK, return the instrument */
retVal.Name = programMap[index].name.c_str();
retVal.Program = programMap[index].program;
retVal.Bank = programMap[index].bank;
return &retVal;
}
}

/**
* Selects a new program for this synth.
*
* This DSSI member function selects a new program for this synth. The program
* change will take effect immediately at the start of the next run_synth()
* call. An invalid bank / instrument combination is ignored.
*
* Zyn Implementation
* ------------------
* the banks and instruments are as shown in the `instrument -> show instrument
* bank` command in Zyn. The bank no is a 1-based index into the list of banks
* Zyn loads and shows in the drop down and the program number is the
* instrument within that bank.
*
* @param bank [in] the bank number to select
* @param program [in] the program number within the bank to select
*/
void DSSIaudiooutput::selectProgram(unsigned long bank, unsigned long program)
{
initBanks();
// cerr << "selectProgram(" << (bank & 0x7F) << ':' << ((bank >> 7) & 0x7F) << "," << program << ")" << '\n';
if((bank < master->bank.banks.size()) && (program < BANK_SIZE)) {
const std::string bankdir = master->bank.banks[bank].dir;
if(!bankdir.empty()) {
pthread_mutex_lock(&master->mutex);

/* We have to turn off the CheckPADsynth functionality, else
* the program change takes way too long and we get timeouts
* and hence zombies (!) */
int save = config.cfg.CheckPADsynth;
config.cfg.CheckPADsynth = 0;

/* Load the bank... */
master->bank.loadbank(bankdir);

/* restore the CheckPADsynth flag */
config.cfg.CheckPADsynth = save;

/* Now load the instrument... */
master->bank.loadfromslot((unsigned int)program, master->part[0]);

pthread_mutex_unlock(&master->mutex);
}
}
}

/**
* Returns the MIDI controller number or NRPN for a input control port
*
* This DSSI member function returns the MIDI controller number or NRPN that
* should be mapped to the given input control port. If the given port should
* not have any MIDI controller mapped to it, the function will return DSSI_NONE.
* The behaviour of this function is undefined if the given port
* number does not correspond to an input control port.
*
* Zyn Implementation
* ------------------
* Currently Zyn does not define any controller ports, but may do in the future.
*
* @param port [in] the input controller port
* @return the CC and NRPN values shifted and ORed together.
*/
int DSSIaudiooutput::getMidiControllerForPort(unsigned long port)
{
return DSSI_NONE;
}

/**
* Runs the synth for a block.
*
* This DSSI member function runs the synth for a block. This is identical in
* function to the LADSPA run() function, except that it also supplies events
* to the synth.
*
* Zyn Implementation
* ------------------
* Zyn implements synthesis in Master::GetAudioOutSamples; runSynth calls this
* function in chunks delimited by the sample_count and the frame indexes in
* the events block, calling the appropriate NoteOn, NoteOff and SetController
* members of Master to process the events supplied between each chunk.
*
* @param sample_count [in] the block size (in samples) for which the synth
* instance may run.
* @param events [in] The Events pointer points to a block of ALSA
* sequencer events, used to communicate MIDI and related events to the synth.
* Each event must be timestamped relative to the start of the block,
* (mis)using the ALSA "tick time" field as a frame count. The host is
* responsible for ensuring that events with differing timestamps are already
* ordered by time. Must not include NOTE (only NOTE_ON / NOTE_OFF), LSB or MSB
* events.
* @param event_count [in] the number of entries in the `events` block
*/
void DSSIaudiooutput::runSynth(unsigned long sample_count,
snd_seq_event_t *events,
unsigned long event_count)
{
unsigned long from_frame = 0;
unsigned long event_index = 0;
unsigned long next_event_frame = 0;
unsigned long to_frame = 0;
pthread_mutex_lock(&master->mutex);

do {
/* Find the time of the next event, if any */
if((events == NULL) || (event_index >= event_count))
next_event_frame = ULONG_MAX;
else
next_event_frame = events[event_index].time.tick;

/* find the end of the sub-sample to be processed this time round... */
/* if the next event falls within the desired sample interval... */
if((next_event_frame < sample_count) && (next_event_frame >= to_frame))
/* set the end to be at that event */
to_frame = next_event_frame;
else
/* ...else go for the whole remaining sample */
to_frame = sample_count;
if(from_frame < to_frame) {
// call master to fill from `from_frame` to `to_frame`:
master->GetAudioOutSamples(to_frame - from_frame,
(int)sampleRate,
&(outl[from_frame]),
&(outr[from_frame]));
// next sub-sample please...
from_frame = to_frame;
}

// Now process any event(s) at the current timing point
while(events != NULL && event_index < event_count
&& events[event_index].time.tick == to_frame) {
if(events[event_index].type == SND_SEQ_EVENT_NOTEON)
master->noteOn(events[event_index].data.note.channel,
events[event_index].data.note.note,
events[event_index].data.note.velocity);
else
if(events[event_index].type == SND_SEQ_EVENT_NOTEOFF)
master->noteOff(events[event_index].data.note.channel,
events[event_index].data.note.note);
else
if(events[event_index].type == SND_SEQ_EVENT_CONTROLLER)
master->setController(events[event_index].data.control.channel,
events[event_index].data.control.param,
events[event_index].data.control.value);
else {}
event_index++;
}

// Keep going until we have the desired total length of sample...
} while(to_frame < sample_count);

pthread_mutex_unlock(&master->mutex);
}

/**
* Initial entry point for the DSSI plug-in library.
*
* This DSSI function is the initial entry point for the plug-in library.
* The DSSI host looks for this entry point in each shared library object it
* finds and then calls the function to enumerate the plug-ins within the
* library.
*
* Zyn Implementation
* ------------------
* The descriptor is created statically by DSSIaudiooutput::initDssiDescriptor()
* when the plug-in library is loaded. This function merely returns a pointer to
* that descriptor.
*
* @param index [in] the index number of the plug-in within the library.
* @return if index is in range, a pointer to the plug-in descriptor is returned, else NULL
*/
const DSSI_Descriptor *DSSIaudiooutput::getDssiDescriptor(unsigned long index)
{
if((index > 0) || (dssiDescriptor == NULL))
return NULL;
else
return dssiDescriptor;
}

//
// Internal member functions
//

// Initialise the DSSI descriptor, statically:
DSSI_Descriptor *DSSIaudiooutput::dssiDescriptor =
DSSIaudiooutput::initDssiDescriptor();

/**
* Initializes the DSSI (and LADSPA) descriptor, returning it is an object.
*/
DSSI_Descriptor *DSSIaudiooutput::initDssiDescriptor()
{
DSSI_Descriptor *newDssiDescriptor = new DSSI_Descriptor;

LADSPA_PortDescriptor *newPortDescriptors;
const char **newPortNames;
LADSPA_PortRangeHint *newPortRangeHints;

if(newDssiDescriptor) {
LADSPA_Descriptor *newLadspaDescriptor = new LADSPA_Descriptor;
if(newLadspaDescriptor) {
newLadspaDescriptor->UniqueID = 100;
newLadspaDescriptor->Label = "ZASF";
newLadspaDescriptor->Properties = 0;
newLadspaDescriptor->Name = "ZynAddSubFX";
newLadspaDescriptor->Maker =
"Nasca Octavian Paul <zynaddsubfx@yahoo.com>";
newLadspaDescriptor->Copyright = "GNU General Public License v.2";
newLadspaDescriptor->PortCount = 2;

newPortNames = new const char *[newLadspaDescriptor->PortCount];
newPortNames[0] = "Output L";
newPortNames[1] = "Output R";
newLadspaDescriptor->PortNames = newPortNames;

newPortDescriptors =
new LADSPA_PortDescriptor[newLadspaDescriptor->PortCount];
newPortDescriptors[0] = LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO;
newPortDescriptors[1] = LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO;
newLadspaDescriptor->PortDescriptors = newPortDescriptors;

newPortRangeHints =
new LADSPA_PortRangeHint[newLadspaDescriptor->PortCount];
newPortRangeHints[0].HintDescriptor = 0;
newPortRangeHints[1].HintDescriptor = 0;
newLadspaDescriptor->PortRangeHints = newPortRangeHints;

newLadspaDescriptor->activate = stub_activate;
newLadspaDescriptor->cleanup = stub_cleanup;
newLadspaDescriptor->connect_port = stub_connectPort;
newLadspaDescriptor->deactivate = stub_deactivate;
newLadspaDescriptor->instantiate = instantiate;
newLadspaDescriptor->run = stub_run;
newLadspaDescriptor->run_adding = NULL;
newLadspaDescriptor->set_run_adding_gain = NULL;
}
newDssiDescriptor->LADSPA_Plugin = newLadspaDescriptor;
newDssiDescriptor->DSSI_API_Version = 1;
newDssiDescriptor->configure = NULL;
newDssiDescriptor->get_program = stub_getProgram;
newDssiDescriptor->get_midi_controller_for_port =
stub_getMidiControllerForPort;
newDssiDescriptor->select_program = stub_selectProgram;
newDssiDescriptor->run_synth = stub_runSynth;
newDssiDescriptor->run_synth_adding = NULL;
newDssiDescriptor->run_multiple_synths = NULL;
newDssiDescriptor->run_multiple_synths_adding = NULL;
}

dssiDescriptor = newDssiDescriptor;

return dssiDescriptor;
}

/**
* Converts a LADSPA / DSSI handle into a DSSIaudiooutput instance.
*
* @param instance [in]
* @return the instance
*/
DSSIaudiooutput *DSSIaudiooutput::getInstance(LADSPA_Handle instance)
{
return (DSSIaudiooutput *)(instance);
}

SYNTH_T *synth;

/**
* The private sole constructor for the DSSIaudiooutput class.
*
* Only ever called via instantiate().
* @param sampleRate [in] the sample rate to be used by the synth.
* @return
*/
DSSIaudiooutput::DSSIaudiooutput(unsigned long sampleRate)
{
synth = new SYNTH_T;
synth->samplerate = sampleRate;

this->sampleRate = sampleRate;
this->banksInited = false;

config.init();

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

this->master = new Master();
}

/**
* The destructor for the DSSIaudiooutput class
* @return
*/
DSSIaudiooutput::~DSSIaudiooutput()
{}

/**
* Ensures the list of bank (directories) has been initialised.
*/
void DSSIaudiooutput::initBanks(void)
{
if(!banksInited) {
pthread_mutex_lock(&master->mutex);
master->bank.rescanforbanks();
banksInited = true;
pthread_mutex_unlock(&master->mutex);
}
}

/**
* constructor for the internally used ProgramDescriptor class
*
* @param _bank [in] bank number
* @param _program [in] program number
* @param _name [in] instrument / sample name
* @return
*/
DSSIaudiooutput::ProgramDescriptor::ProgramDescriptor(unsigned long _bank,
unsigned long _program,
char *_name)
:bank(_bank), program(_program), name(_name)
{}

/**
* The map of programs available; held as a single shared statically allocated object.
*/
vector<DSSIaudiooutput::ProgramDescriptor> DSSIaudiooutput::programMap =
vector<DSSIaudiooutput::ProgramDescriptor>();

/**
* Index controlling the map of banks
*/
long DSSIaudiooutput::bankNoToMap = 1;

/**
* Queries and maps the next available bank of instruments.
*
* If the program index requested to getProgram() lies beyond the banks mapped to date,
* this member function is called to map the next one.
* @return true if a new bank has been found and mapped, else false.
*/
bool DSSIaudiooutput::mapNextBank()
{
pthread_mutex_lock(&master->mutex);
Bank &bank = master->bank;
bool retval;
if((bankNoToMap >= (int)bank.banks.size())
|| bank.banks[bankNoToMap].dir.empty())
retval = false;
else {
bank.loadbank(bank.banks[bankNoToMap].dir);
for(unsigned long instrument = 0; instrument < BANK_SIZE;
++instrument) {
string insName = bank.getname(instrument);
if(!insName.empty() && (insName[0] != '\0') && (insName[0] != ' '))
programMap.push_back(ProgramDescriptor(bankNoToMap, instrument,
const_cast<char *>(
insName.c_str())));
}
bankNoToMap++;
retval = true;
}
pthread_mutex_unlock(&master->mutex);
return retval;
}

+ 123
- 0
c++/carla-backend/plugins/zynaddsubfx/Output/DSSIaudiooutput.h View File

@@ -0,0 +1,123 @@
/*
ZynAddSubFX - a software synthesizer

VSTaudiooutput.h - Audio output for VST
Copyright (C) 2002 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 VST_AUDIO_OUTPUT_H
#define VST_AUDIO_OUTPUT_H

#include <pthread.h>

#include "../globals.h"
#include "../Misc/Master.h"

#include <dssi.h>
#include <ladspa.h>
#include <vector>

class DSSIaudiooutput
{
public:
//
// Static stubs for LADSPA member functions
//
static void stub_connectPort(LADSPA_Handle instance,
unsigned long port,
LADSPA_Data *data);
static void stub_activate(LADSPA_Handle instance);
static void stub_run(LADSPA_Handle instance, unsigned long sample_count);
static void stub_deactivate(LADSPA_Handle Instance);
static void stub_cleanup(LADSPA_Handle instance);

//
// Static stubs for DSSI member functions
//
static const DSSI_Program_Descriptor *stub_getProgram(
LADSPA_Handle instance,
unsigned long Index);
static void stub_selectProgram(LADSPA_Handle instance,
unsigned long bank,
unsigned long program);
static int stub_getMidiControllerForPort(LADSPA_Handle instance,
unsigned long port);
static void stub_runSynth(LADSPA_Handle instance,
unsigned long sample_count,
snd_seq_event_t *events,
unsigned long event_count);

/*
* LADSPA member functions
*/
static LADSPA_Handle instantiate(const LADSPA_Descriptor *descriptor,
unsigned long s_rate);
void connectPort(unsigned long port, LADSPA_Data *data);
void activate();
void run(unsigned long sample_count);
void deactivate();
void cleanup();
static const LADSPA_Descriptor *getLadspaDescriptor(unsigned long index);

/*
* DSSI member functions
*/
const DSSI_Program_Descriptor *getProgram(unsigned long Index);
void selectProgram(unsigned long bank, unsigned long program);
int getMidiControllerForPort(unsigned long port);
void runSynth(unsigned long sample_count,
snd_seq_event_t *events,
unsigned long event_count);
static const DSSI_Descriptor *getDssiDescriptor(unsigned long index);

struct ProgramDescriptor {
unsigned long bank;
unsigned long program;
std::string name;
ProgramDescriptor(unsigned long _bank,
unsigned long _program,
char *_name);
};

private:

DSSIaudiooutput(unsigned long sampleRate);
~DSSIaudiooutput();
static DSSI_Descriptor *initDssiDescriptor();
static DSSIaudiooutput *getInstance(LADSPA_Handle instance);
void initBanks();
bool mapNextBank();

LADSPA_Data *outl;
LADSPA_Data *outr;
long sampleRate;
Master *master;
static DSSI_Descriptor *dssiDescriptor;
static std::string bankDirNames[];
static
std::vector<ProgramDescriptor> programMap;

/**
* Flag controlling the list of bank directories
*/
bool banksInited;

static
long bankNoToMap;
};

#endif

+ 792
- 0
c++/carla-backend/plugins/zynaddsubfx/Params/ADnoteParameters.cpp View File

@@ -0,0 +1,792 @@
/*
ZynAddSubFX - a software synthesizer

ADnoteParameters.cpp - Parameters for ADnote (ADsynth)
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 <stdio.h>
#include <math.h>

#include "ADnoteParameters.h"
#include "EnvelopeParams.h"
#include "LFOParams.h"
#include "../Misc/XMLwrapper.h"
#include "../DSP/FFTwrapper.h"
#include "../Synth/OscilGen.h"
#include "../Synth/Resonance.h"
#include "FilterParams.h"

int ADnote_unison_sizes[] =
{1, 2, 3, 4, 5, 6, 8, 10, 12, 15, 20, 25, 30, 40, 50, 0};

ADnoteParameters::ADnoteParameters(FFTwrapper *fft_)
:PresetsArray()
{
setpresettype("Padsyth");
fft = fft_;


for(int nvoice = 0; nvoice < NUM_VOICES; ++nvoice)
EnableVoice(nvoice);

defaults();
}

ADnoteGlobalParam::ADnoteGlobalParam()
{
FreqEnvelope = new EnvelopeParams(0, 0);
FreqEnvelope->ASRinit(64, 50, 64, 60);
FreqLfo = new LFOParams(70, 0, 64, 0, 0, 0, 0, 0);

AmpEnvelope = new EnvelopeParams(64, 1);
AmpEnvelope->ADSRinit_dB(0, 40, 127, 25);
AmpLfo = new LFOParams(80, 0, 64, 0, 0, 0, 0, 1);

GlobalFilter = new FilterParams(2, 94, 40);
FilterEnvelope = new EnvelopeParams(0, 1);
FilterEnvelope->ADSRinit_filter(64, 40, 64, 70, 60, 64);
FilterLfo = new LFOParams(80, 0, 64, 0, 0, 0, 0, 2);
Reson = new Resonance();
}

void ADnoteParameters::defaults()
{
//Default Parameters
GlobalPar.defaults();

for(int nvoice = 0; nvoice < NUM_VOICES; ++nvoice)
defaults(nvoice);

VoicePar[0].Enabled = 1;
}

void ADnoteGlobalParam::defaults()
{
/* Frequency Global Parameters */
PStereo = 1; //stereo
PDetune = 8192; //zero
PCoarseDetune = 0;
PDetuneType = 1;
FreqEnvelope->defaults();
FreqLfo->defaults();
PBandwidth = 64;

/* Amplitude Global Parameters */
PVolume = 90;
PPanning = 64; //center
PAmpVelocityScaleFunction = 64;
AmpEnvelope->defaults();
AmpLfo->defaults();
PPunchStrength = 0;
PPunchTime = 60;
PPunchStretch = 64;
PPunchVelocitySensing = 72;
Hrandgrouping = 0;

/* Filter Global Parameters*/
PFilterVelocityScale = 64;
PFilterVelocityScaleFunction = 64;
GlobalFilter->defaults();
FilterEnvelope->defaults();
FilterLfo->defaults();
Reson->defaults();
}

/*
* Defaults a voice
*/
void ADnoteParameters::defaults(int n)
{
VoicePar[n].defaults();
}

void ADnoteVoiceParam::defaults()
{
Enabled = 0;

Unison_size = 1;
Unison_frequency_spread = 60;
Unison_stereo_spread = 64;
Unison_vibratto = 64;
Unison_vibratto_speed = 64;
Unison_invert_phase = 0;

Type = 0;
Pfixedfreq = 0;
PfixedfreqET = 0;
Presonance = 1;
Pfilterbypass = 0;
Pextoscil = -1;
PextFMoscil = -1;
Poscilphase = 64;
PFMoscilphase = 64;
PDelay = 0;
PVolume = 100;
PVolumeminus = 0;
PPanning = 64; //center
PDetune = 8192; //8192=0
PCoarseDetune = 0;
PDetuneType = 0;
PFreqLfoEnabled = 0;
PFreqEnvelopeEnabled = 0;
PAmpEnvelopeEnabled = 0;
PAmpLfoEnabled = 0;
PAmpVelocityScaleFunction = 127;
PFilterEnabled = 0;
PFilterEnvelopeEnabled = 0;
PFilterLfoEnabled = 0;
PFMEnabled = 0;

//I use the internal oscillator (-1)
PFMVoice = -1;

PFMVolume = 90;
PFMVolumeDamp = 64;
PFMDetune = 8192;
PFMCoarseDetune = 0;
PFMDetuneType = 0;
PFMFreqEnvelopeEnabled = 0;
PFMAmpEnvelopeEnabled = 0;
PFMVelocityScaleFunction = 64;

OscilSmp->defaults();
FMSmp->defaults();

AmpEnvelope->defaults();
AmpLfo->defaults();

FreqEnvelope->defaults();
FreqLfo->defaults();

VoiceFilter->defaults();
FilterEnvelope->defaults();
FilterLfo->defaults();

FMFreqEnvelope->defaults();
FMAmpEnvelope->defaults();
}



/*
* Init the voice parameters
*/
void ADnoteParameters::EnableVoice(int nvoice)
{
VoicePar[nvoice].enable(fft, GlobalPar.Reson);
}

void ADnoteVoiceParam::enable(FFTwrapper *fft, Resonance *Reson)
{
OscilSmp = new OscilGen(fft, Reson);
FMSmp = new OscilGen(fft, NULL);

AmpEnvelope = new EnvelopeParams(64, 1);
AmpEnvelope->ADSRinit_dB(0, 100, 127, 100);
AmpLfo = new LFOParams(90, 32, 64, 0, 0, 30, 0, 1);

FreqEnvelope = new EnvelopeParams(0, 0);
FreqEnvelope->ASRinit(30, 40, 64, 60);
FreqLfo = new LFOParams(50, 40, 0, 0, 0, 0, 0, 0);

VoiceFilter = new FilterParams(2, 50, 60);
FilterEnvelope = new EnvelopeParams(0, 0);
FilterEnvelope->ADSRinit_filter(90, 70, 40, 70, 10, 40);
FilterLfo = new LFOParams(50, 20, 64, 0, 0, 0, 0, 2);

FMFreqEnvelope = new EnvelopeParams(0, 0);
FMFreqEnvelope->ASRinit(20, 90, 40, 80);
FMAmpEnvelope = new EnvelopeParams(64, 1);
FMAmpEnvelope->ADSRinit(80, 90, 127, 100);
}

/*
* Get the Multiplier of the fine detunes of the voices
*/
float ADnoteParameters::getBandwidthDetuneMultiplier()
{
float bw = (GlobalPar.PBandwidth - 64.0f) / 64.0f;
bw = powf(2.0f, bw * powf(fabs(bw), 0.2f) * 5.0f);

return bw;
}

/*
* Get the unison spread in cents for a voice
*/

float ADnoteParameters::getUnisonFrequencySpreadCents(int nvoice) {
float unison_spread = VoicePar[nvoice].Unison_frequency_spread / 127.0f;
unison_spread = powf(unison_spread * 2.0f, 2.0f) * 50.0f; //cents
return unison_spread;
}

/*
* Kill the voice
*/
void ADnoteParameters::KillVoice(int nvoice)
{
VoicePar[nvoice].kill();
}

void ADnoteVoiceParam::kill()
{
delete OscilSmp;
delete FMSmp;

delete AmpEnvelope;
delete AmpLfo;

delete FreqEnvelope;
delete FreqLfo;

delete VoiceFilter;
delete FilterEnvelope;
delete FilterLfo;

delete FMFreqEnvelope;
delete FMAmpEnvelope;
}


ADnoteGlobalParam::~ADnoteGlobalParam()
{
delete FreqEnvelope;
delete FreqLfo;
delete AmpEnvelope;
delete AmpLfo;
delete GlobalFilter;
delete FilterEnvelope;
delete FilterLfo;
delete Reson;
}

ADnoteParameters::~ADnoteParameters()
{
for(int nvoice = 0; nvoice < NUM_VOICES; ++nvoice)
KillVoice(nvoice);
}

int ADnoteParameters::get_unison_size_index(int nvoice) {
int index = 0;
if(nvoice >= NUM_VOICES)
return 0;
int unison = VoicePar[nvoice].Unison_size;

while(1) {
if(ADnote_unison_sizes[index] >= unison)
return index;

if(ADnote_unison_sizes[index] == 0)
return index - 1;

index++;
}
return 0;
}

void ADnoteParameters::set_unison_size_index(int nvoice, int index) {
int unison = 1;
for(int i = 0; i <= index; ++i) {
unison = ADnote_unison_sizes[i];
if(unison == 0) {
unison = ADnote_unison_sizes[i - 1];
break;
}
}

VoicePar[nvoice].Unison_size = unison;
}



void ADnoteParameters::add2XMLsection(XMLwrapper *xml, int n)
{
int nvoice = n;
if(nvoice >= NUM_VOICES)
return;

int oscilused = 0, fmoscilused = 0; //if the oscil or fmoscil are used by another voice

for(int i = 0; i < NUM_VOICES; ++i) {
if(VoicePar[i].Pextoscil == nvoice)
oscilused = 1;
if(VoicePar[i].PextFMoscil == nvoice)
fmoscilused = 1;
}

xml->addparbool("enabled", VoicePar[nvoice].Enabled);
if(((VoicePar[nvoice].Enabled == 0) && (oscilused == 0)
&& (fmoscilused == 0)) && (xml->minimal))
return;

VoicePar[nvoice].add2XML(xml, fmoscilused);
}

void ADnoteVoiceParam::add2XML(XMLwrapper *xml, bool fmoscilused)
{
xml->addpar("type", Type);

xml->addpar("unison_size", Unison_size);
xml->addpar("unison_frequency_spread",
Unison_frequency_spread);
xml->addpar("unison_stereo_spread", Unison_stereo_spread);
xml->addpar("unison_vibratto", Unison_vibratto);
xml->addpar("unison_vibratto_speed", Unison_vibratto_speed);
xml->addpar("unison_invert_phase", Unison_invert_phase);

xml->addpar("delay", PDelay);
xml->addparbool("resonance", Presonance);

xml->addpar("ext_oscil", Pextoscil);
xml->addpar("ext_fm_oscil", PextFMoscil);

xml->addpar("oscil_phase", Poscilphase);
xml->addpar("oscil_fm_phase", PFMoscilphase);

xml->addparbool("filter_enabled", PFilterEnabled);
xml->addparbool("filter_bypass", Pfilterbypass);

xml->addpar("fm_enabled", PFMEnabled);

xml->beginbranch("OSCIL");
OscilSmp->add2XML(xml);
xml->endbranch();


xml->beginbranch("AMPLITUDE_PARAMETERS");
xml->addpar("panning", PPanning);
xml->addpar("volume", PVolume);
xml->addparbool("volume_minus", PVolumeminus);
xml->addpar("velocity_sensing", PAmpVelocityScaleFunction);

xml->addparbool("amp_envelope_enabled",
PAmpEnvelopeEnabled);
if((PAmpEnvelopeEnabled != 0) || (!xml->minimal)) {
xml->beginbranch("AMPLITUDE_ENVELOPE");
AmpEnvelope->add2XML(xml);
xml->endbranch();
}
xml->addparbool("amp_lfo_enabled", PAmpLfoEnabled);
if((PAmpLfoEnabled != 0) || (!xml->minimal)) {
xml->beginbranch("AMPLITUDE_LFO");
AmpLfo->add2XML(xml);
xml->endbranch();
}
xml->endbranch();

xml->beginbranch("FREQUENCY_PARAMETERS");
xml->addparbool("fixed_freq", Pfixedfreq);
xml->addpar("fixed_freq_et", PfixedfreqET);
xml->addpar("detune", PDetune);
xml->addpar("coarse_detune", PCoarseDetune);
xml->addpar("detune_type", PDetuneType);

xml->addparbool("freq_envelope_enabled",
PFreqEnvelopeEnabled);
if((PFreqEnvelopeEnabled != 0) || (!xml->minimal)) {
xml->beginbranch("FREQUENCY_ENVELOPE");
FreqEnvelope->add2XML(xml);
xml->endbranch();
}
xml->addparbool("freq_lfo_enabled", PFreqLfoEnabled);
if((PFreqLfoEnabled != 0) || (!xml->minimal)) {
xml->beginbranch("FREQUENCY_LFO");
FreqLfo->add2XML(xml);
xml->endbranch();
}
xml->endbranch();


if((PFilterEnabled != 0) || (!xml->minimal)) {
xml->beginbranch("FILTER_PARAMETERS");
xml->beginbranch("FILTER");
VoiceFilter->add2XML(xml);
xml->endbranch();

xml->addparbool("filter_envelope_enabled",
PFilterEnvelopeEnabled);
if((PFilterEnvelopeEnabled != 0) || (!xml->minimal)) {
xml->beginbranch("FILTER_ENVELOPE");
FilterEnvelope->add2XML(xml);
xml->endbranch();
}

xml->addparbool("filter_lfo_enabled",
PFilterLfoEnabled);
if((PFilterLfoEnabled != 0) || (!xml->minimal)) {
xml->beginbranch("FILTER_LFO");
FilterLfo->add2XML(xml);
xml->endbranch();
}
xml->endbranch();
}

if((PFMEnabled != 0) || (fmoscilused != 0)
|| (!xml->minimal)) {
xml->beginbranch("FM_PARAMETERS");
xml->addpar("input_voice", PFMVoice);

xml->addpar("volume", PFMVolume);
xml->addpar("volume_damp", PFMVolumeDamp);
xml->addpar("velocity_sensing",
PFMVelocityScaleFunction);

xml->addparbool("amp_envelope_enabled",
PFMAmpEnvelopeEnabled);
if((PFMAmpEnvelopeEnabled != 0) || (!xml->minimal)) {
xml->beginbranch("AMPLITUDE_ENVELOPE");
FMAmpEnvelope->add2XML(xml);
xml->endbranch();
}
xml->beginbranch("MODULATOR");
xml->addpar("detune", PFMDetune);
xml->addpar("coarse_detune", PFMCoarseDetune);
xml->addpar("detune_type", PFMDetuneType);

xml->addparbool("freq_envelope_enabled",
PFMFreqEnvelopeEnabled);
if((PFMFreqEnvelopeEnabled != 0) || (!xml->minimal)) {
xml->beginbranch("FREQUENCY_ENVELOPE");
FMFreqEnvelope->add2XML(xml);
xml->endbranch();
}

xml->beginbranch("OSCIL");
FMSmp->add2XML(xml);
xml->endbranch();

xml->endbranch();
xml->endbranch();
}
}

void ADnoteGlobalParam::add2XML(XMLwrapper *xml)
{
xml->addparbool("stereo", PStereo);

xml->beginbranch("AMPLITUDE_PARAMETERS");
xml->addpar("volume", PVolume);
xml->addpar("panning", PPanning);
xml->addpar("velocity_sensing", PAmpVelocityScaleFunction);
xml->addpar("punch_strength", PPunchStrength);
xml->addpar("punch_time", PPunchTime);
xml->addpar("punch_stretch", PPunchStretch);
xml->addpar("punch_velocity_sensing", PPunchVelocitySensing);
xml->addpar("harmonic_randomness_grouping", Hrandgrouping);

xml->beginbranch("AMPLITUDE_ENVELOPE");
AmpEnvelope->add2XML(xml);
xml->endbranch();

xml->beginbranch("AMPLITUDE_LFO");
AmpLfo->add2XML(xml);
xml->endbranch();
xml->endbranch();

xml->beginbranch("FREQUENCY_PARAMETERS");
xml->addpar("detune", PDetune);

xml->addpar("coarse_detune", PCoarseDetune);
xml->addpar("detune_type", PDetuneType);

xml->addpar("bandwidth", PBandwidth);

xml->beginbranch("FREQUENCY_ENVELOPE");
FreqEnvelope->add2XML(xml);
xml->endbranch();

xml->beginbranch("FREQUENCY_LFO");
FreqLfo->add2XML(xml);
xml->endbranch();
xml->endbranch();


xml->beginbranch("FILTER_PARAMETERS");
xml->addpar("velocity_sensing_amplitude", PFilterVelocityScale);
xml->addpar("velocity_sensing", PFilterVelocityScaleFunction);

xml->beginbranch("FILTER");
GlobalFilter->add2XML(xml);
xml->endbranch();

xml->beginbranch("FILTER_ENVELOPE");
FilterEnvelope->add2XML(xml);
xml->endbranch();

xml->beginbranch("FILTER_LFO");
FilterLfo->add2XML(xml);
xml->endbranch();
xml->endbranch();

xml->beginbranch("RESONANCE");
Reson->add2XML(xml);
xml->endbranch();
}

void ADnoteParameters::add2XML(XMLwrapper *xml)
{
GlobalPar.add2XML(xml);
for(int nvoice = 0; nvoice < NUM_VOICES; ++nvoice) {
xml->beginbranch("VOICE", nvoice);
add2XMLsection(xml, nvoice);
xml->endbranch();
}
}


void ADnoteGlobalParam::getfromXML(XMLwrapper *xml)
{
PStereo = xml->getparbool("stereo", PStereo);

if(xml->enterbranch("AMPLITUDE_PARAMETERS")) {
PVolume = xml->getpar127("volume", PVolume);
PPanning = xml->getpar127("panning", PPanning);
PAmpVelocityScaleFunction = xml->getpar127("velocity_sensing",
PAmpVelocityScaleFunction);

PPunchStrength = xml->getpar127("punch_strength", PPunchStrength);
PPunchTime = xml->getpar127("punch_time", PPunchTime);
PPunchStretch = xml->getpar127("punch_stretch", PPunchStretch);
PPunchVelocitySensing = xml->getpar127("punch_velocity_sensing",
PPunchVelocitySensing);
Hrandgrouping = xml->getpar127("harmonic_randomness_grouping",
Hrandgrouping);

if(xml->enterbranch("AMPLITUDE_ENVELOPE")) {
AmpEnvelope->getfromXML(xml);
xml->exitbranch();
}

if(xml->enterbranch("AMPLITUDE_LFO")) {
AmpLfo->getfromXML(xml);
xml->exitbranch();
}

xml->exitbranch();
}

if(xml->enterbranch("FREQUENCY_PARAMETERS")) {
PDetune = xml->getpar("detune", PDetune, 0, 16383);
PCoarseDetune = xml->getpar("coarse_detune", PCoarseDetune, 0, 16383);
PDetuneType = xml->getpar127("detune_type", PDetuneType);
PBandwidth = xml->getpar127("bandwidth", PBandwidth);

xml->enterbranch("FREQUENCY_ENVELOPE");
FreqEnvelope->getfromXML(xml);
xml->exitbranch();

xml->enterbranch("FREQUENCY_LFO");
FreqLfo->getfromXML(xml);
xml->exitbranch();

xml->exitbranch();
}


if(xml->enterbranch("FILTER_PARAMETERS")) {
PFilterVelocityScale = xml->getpar127("velocity_sensing_amplitude",
PFilterVelocityScale);
PFilterVelocityScaleFunction = xml->getpar127(
"velocity_sensing",
PFilterVelocityScaleFunction);

xml->enterbranch("FILTER");
GlobalFilter->getfromXML(xml);
xml->exitbranch();

xml->enterbranch("FILTER_ENVELOPE");
FilterEnvelope->getfromXML(xml);
xml->exitbranch();

xml->enterbranch("FILTER_LFO");
FilterLfo->getfromXML(xml);
xml->exitbranch();
xml->exitbranch();
}

if(xml->enterbranch("RESONANCE")) {
Reson->getfromXML(xml);
xml->exitbranch();
}
}

void ADnoteParameters::getfromXML(XMLwrapper *xml)
{
GlobalPar.getfromXML(xml);

for(int nvoice = 0; nvoice < NUM_VOICES; ++nvoice) {
VoicePar[nvoice].Enabled = 0;
if(xml->enterbranch("VOICE", nvoice) == 0)
continue;
getfromXMLsection(xml, nvoice);
xml->exitbranch();
}
}

void ADnoteParameters::getfromXMLsection(XMLwrapper *xml, int n)
{
int nvoice = n;
if(nvoice >= NUM_VOICES)
return;

VoicePar[nvoice].getfromXML(xml, nvoice);
}


void ADnoteVoiceParam::getfromXML(XMLwrapper *xml, unsigned nvoice)
{
Enabled = xml->getparbool("enabled", 0);
Unison_size = xml->getpar127("unison_size", Unison_size);
Unison_frequency_spread = xml->getpar127("unison_frequency_spread",
Unison_frequency_spread);
Unison_stereo_spread = xml->getpar127("unison_stereo_spread",
Unison_stereo_spread);
Unison_vibratto = xml->getpar127("unison_vibratto", Unison_vibratto);
Unison_vibratto_speed = xml->getpar127("unison_vibratto_speed",
Unison_vibratto_speed);
Unison_invert_phase = xml->getpar127("unison_invert_phase",
Unison_invert_phase);

Type = xml->getpar127("type", Type);
PDelay = xml->getpar127("delay", PDelay);
Presonance = xml->getparbool("resonance", Presonance);

Pextoscil = xml->getpar("ext_oscil", -1, -1, nvoice - 1);
PextFMoscil = xml->getpar("ext_fm_oscil", -1, -1, nvoice - 1);

Poscilphase = xml->getpar127("oscil_phase", Poscilphase);
PFMoscilphase = xml->getpar127("oscil_fm_phase", PFMoscilphase);
PFilterEnabled = xml->getparbool("filter_enabled", PFilterEnabled);
Pfilterbypass = xml->getparbool("filter_bypass", Pfilterbypass);
PFMEnabled = xml->getpar127("fm_enabled", PFMEnabled);

if(xml->enterbranch("OSCIL")) {
OscilSmp->getfromXML(xml);
xml->exitbranch();
}


if(xml->enterbranch("AMPLITUDE_PARAMETERS")) {
PPanning = xml->getpar127("panning", PPanning);
PVolume = xml->getpar127("volume", PVolume);
PVolumeminus = xml->getparbool("volume_minus", PVolumeminus);
PAmpVelocityScaleFunction = xml->getpar127("velocity_sensing",
PAmpVelocityScaleFunction);

PAmpEnvelopeEnabled = xml->getparbool("amp_envelope_enabled",
PAmpEnvelopeEnabled);
if(xml->enterbranch("AMPLITUDE_ENVELOPE")) {
AmpEnvelope->getfromXML(xml);
xml->exitbranch();
}

PAmpLfoEnabled = xml->getparbool("amp_lfo_enabled", PAmpLfoEnabled);
if(xml->enterbranch("AMPLITUDE_LFO")) {
AmpLfo->getfromXML(xml);
xml->exitbranch();
}
xml->exitbranch();
}

if(xml->enterbranch("FREQUENCY_PARAMETERS")) {
Pfixedfreq = xml->getparbool("fixed_freq", Pfixedfreq);
PfixedfreqET = xml->getpar127("fixed_freq_et", PfixedfreqET);
PDetune = xml->getpar("detune", PDetune, 0, 16383);
PCoarseDetune = xml->getpar("coarse_detune", PCoarseDetune, 0, 16383);
PDetuneType = xml->getpar127("detune_type", PDetuneType);
PFreqEnvelopeEnabled = xml->getparbool("freq_envelope_enabled",
PFreqEnvelopeEnabled);

if(xml->enterbranch("FREQUENCY_ENVELOPE")) {
FreqEnvelope->getfromXML(xml);
xml->exitbranch();
}

PFreqLfoEnabled = xml->getparbool("freq_lfo_enabled", PFreqLfoEnabled);

if(xml->enterbranch("FREQUENCY_LFO")) {
FreqLfo->getfromXML(xml);
xml->exitbranch();
}
xml->exitbranch();
}

if(xml->enterbranch("FILTER_PARAMETERS")) {
if(xml->enterbranch("FILTER")) {
VoiceFilter->getfromXML(xml);
xml->exitbranch();
}

PFilterEnvelopeEnabled = xml->getparbool("filter_envelope_enabled",
PFilterEnvelopeEnabled);
if(xml->enterbranch("FILTER_ENVELOPE")) {
FilterEnvelope->getfromXML(xml);
xml->exitbranch();
}

PFilterLfoEnabled = xml->getparbool("filter_lfo_enabled",
PFilterLfoEnabled);
if(xml->enterbranch("FILTER_LFO")) {
FilterLfo->getfromXML(xml);
xml->exitbranch();
}
xml->exitbranch();
}

if(xml->enterbranch("FM_PARAMETERS")) {
PFMVoice = xml->getpar("input_voice", PFMVoice, -1, nvoice - 1);
PFMVolume = xml->getpar127("volume", PFMVolume);
PFMVolumeDamp = xml->getpar127("volume_damp", PFMVolumeDamp);
PFMVelocityScaleFunction = xml->getpar127("velocity_sensing",
PFMVelocityScaleFunction);

PFMAmpEnvelopeEnabled = xml->getparbool("amp_envelope_enabled",
PFMAmpEnvelopeEnabled);
if(xml->enterbranch("AMPLITUDE_ENVELOPE")) {
FMAmpEnvelope->getfromXML(xml);
xml->exitbranch();
}

if(xml->enterbranch("MODULATOR")) {
PFMDetune = xml->getpar("detune", PFMDetune, 0, 16383);
PFMCoarseDetune = xml->getpar("coarse_detune",
PFMCoarseDetune,
0,
16383);
PFMDetuneType = xml->getpar127("detune_type", PFMDetuneType);

PFMFreqEnvelopeEnabled = xml->getparbool("freq_envelope_enabled",
PFMFreqEnvelopeEnabled);
if(xml->enterbranch("FREQUENCY_ENVELOPE")) {
FMFreqEnvelope->getfromXML(xml);
xml->exitbranch();
}

if(xml->enterbranch("OSCIL")) {
FMSmp->getfromXML(xml);
xml->exitbranch();
}

xml->exitbranch();
}
xml->exitbranch();
}
}

+ 316
- 0
c++/carla-backend/plugins/zynaddsubfx/Params/ADnoteParameters.h View File

@@ -0,0 +1,316 @@
/*
ZynAddSubFX - a software synthesizer

ADnoteParameters.h - Parameters for ADnote (ADsynth)
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 AD_NOTE_PARAMETERS_H
#define AD_NOTE_PARAMETERS_H


#include "../globals.h"
#include "../Misc/Util.h"
#include "PresetsArray.h"

class EnvelopeParams;
class LFOParams;
class FilterParams;
class Resonance;
class OscilGen;
class FFTwrapper;

enum FMTYPE {
NONE, MORPH, RING_MOD, PHASE_MOD, FREQ_MOD, PITCH_MOD
};
extern int ADnote_unison_sizes[];

/*****************************************************************/
/* GLOBAL PARAMETERS */
/*****************************************************************/

struct ADnoteGlobalParam {
ADnoteGlobalParam();
~ADnoteGlobalParam();
void defaults();
void add2XML(XMLwrapper *xml);
void getfromXML(XMLwrapper *xml);
/* The instrument type - MONO/STEREO
If the mode is MONO, the panning of voices are not used
Stereo=1, Mono=0. */

unsigned char PStereo;


/******************************************
* FREQUENCY GLOBAL PARAMETERS *
******************************************/
unsigned short int PDetune; //fine detune
unsigned short int PCoarseDetune; //coarse detune+octave
unsigned char PDetuneType; //detune type

unsigned char PBandwidth; //how much the relative fine detunes of the voices are changed

EnvelopeParams *FreqEnvelope; //Frequency Envelope

LFOParams *FreqLfo; //Frequency LFO

/********************************************
* AMPLITUDE GLOBAL PARAMETERS *
********************************************/

/* Panning - 0 - random
1 - left
64 - center
127 - right */
unsigned char PPanning;

unsigned char PVolume;

unsigned char PAmpVelocityScaleFunction;

EnvelopeParams *AmpEnvelope;

LFOParams *AmpLfo;

unsigned char PPunchStrength, PPunchTime, PPunchStretch,
PPunchVelocitySensing;

/******************************************
* FILTER GLOBAL PARAMETERS *
******************************************/
FilterParams *GlobalFilter;

// filter velocity sensing
unsigned char PFilterVelocityScale;

// filter velocity sensing
unsigned char PFilterVelocityScaleFunction;

EnvelopeParams *FilterEnvelope;

LFOParams *FilterLfo;

// RESONANCE
Resonance *Reson;

//how the randomness is applied to the harmonics on more voices using the same oscillator
unsigned char Hrandgrouping;
};



/***********************************************************/
/* VOICE PARAMETERS */
/***********************************************************/
struct ADnoteVoiceParam {
void getfromXML(XMLwrapper *xml, unsigned nvoice);
void add2XML(XMLwrapper *xml, bool fmoscilused);
void defaults();
void enable(FFTwrapper *fft, Resonance *Reson);
void kill();
/** If the voice is enabled */
unsigned char Enabled;

/** How many subvoices are used in this voice */
unsigned char Unison_size;

/** How subvoices are spread */
unsigned char Unison_frequency_spread;

/** Stereo spread of the subvoices*/
unsigned char Unison_stereo_spread;

/** Vibratto of the subvoices (which makes the unison more "natural")*/
unsigned char Unison_vibratto;

/** Medium speed of the vibratto of the subvoices*/
unsigned char Unison_vibratto_speed;

/** Unison invert phase */
unsigned char Unison_invert_phase; //0=none,1=random,2=50%,3=33%,4=25%

/** Type of the voice (0=Sound,1=Noise)*/
unsigned char Type;

/** Voice Delay */
unsigned char PDelay;

/** If the resonance is enabled for this voice */
unsigned char Presonance;

// What external oscil should I use, -1 for internal OscilSmp&FMSmp
short int Pextoscil, PextFMoscil;
// it is not allowed that the externoscil,externFMoscil => current voice

// oscillator phases
unsigned char Poscilphase, PFMoscilphase;

// filter bypass
unsigned char Pfilterbypass;

/** Voice oscillator */
OscilGen *OscilSmp;

/**********************************
* FREQUENCY PARAMETERS *
**********************************/

/** If the base frequency is fixed to 440 Hz*/
unsigned char Pfixedfreq;

/* Equal temperate (this is used only if the Pfixedfreq is enabled)
If this parameter is 0, the frequency is fixed (to 440 Hz);
if this parameter is 64, 1 MIDI halftone -> 1 frequency halftone */
unsigned char PfixedfreqET;

/** Fine detune */
unsigned short int PDetune;

/** Coarse detune + octave */
unsigned short int PCoarseDetune;

/** Detune type */
unsigned char PDetuneType;

/* Frequency Envelope */
unsigned char PFreqEnvelopeEnabled;
EnvelopeParams *FreqEnvelope;

/* Frequency LFO */
unsigned char PFreqLfoEnabled;
LFOParams *FreqLfo;


/***************************
* AMPLITUDE PARAMETERS *
***************************/

/* Panning 0 - random
1 - left
64 - center
127 - right
The Panning is ignored if the instrument is mono */
unsigned char PPanning;

/* Voice Volume */
unsigned char PVolume;

/* If the Volume negative */
unsigned char PVolumeminus;

/* Velocity sensing */
unsigned char PAmpVelocityScaleFunction;

/* Amplitude Envelope */
unsigned char PAmpEnvelopeEnabled;
EnvelopeParams *AmpEnvelope;

/* Amplitude LFO */
unsigned char PAmpLfoEnabled;
LFOParams *AmpLfo;



/*************************
* FILTER PARAMETERS *
*************************/

/* Voice Filter */
unsigned char PFilterEnabled;
FilterParams *VoiceFilter;

/* Filter Envelope */
unsigned char PFilterEnvelopeEnabled;
EnvelopeParams *FilterEnvelope;

/* LFO Envelope */
unsigned char PFilterLfoEnabled;
LFOParams *FilterLfo;

/****************************
* MODULLATOR PARAMETERS *
****************************/

/* Modullator Parameters (0=off,1=Morph,2=RM,3=PM,4=FM.. */
unsigned char PFMEnabled;

/* Voice that I use as modullator instead of FMSmp.
It is -1 if I use FMSmp(default).
It maynot be equal or bigger than current voice */
short int PFMVoice;

/* Modullator oscillator */
OscilGen *FMSmp;

/* Modullator Volume */
unsigned char PFMVolume;

/* Modullator damping at higher frequencies */
unsigned char PFMVolumeDamp;

/* Modullator Velocity Sensing */
unsigned char PFMVelocityScaleFunction;

/* Fine Detune of the Modullator*/
unsigned short int PFMDetune;

/* Coarse Detune of the Modullator */
unsigned short int PFMCoarseDetune;

/* The detune type */
unsigned char PFMDetuneType;

/* Frequency Envelope of the Modullator */
unsigned char PFMFreqEnvelopeEnabled;
EnvelopeParams *FMFreqEnvelope;

/* Frequency Envelope of the Modullator */
unsigned char PFMAmpEnvelopeEnabled;
EnvelopeParams *FMAmpEnvelope;
};

class ADnoteParameters:public PresetsArray
{
public:
ADnoteParameters(FFTwrapper *fft_);
~ADnoteParameters();

ADnoteGlobalParam GlobalPar;
ADnoteVoiceParam VoicePar[NUM_VOICES];

void defaults();
void add2XML(XMLwrapper *xml);
void getfromXML(XMLwrapper *xml);

float getBandwidthDetuneMultiplier();
float getUnisonFrequencySpreadCents(int nvoice);
int get_unison_size_index(int nvoice);
void set_unison_size_index(int nvoice, int index);
private:
void defaults(int n); //n is the nvoice

void EnableVoice(int nvoice);
void KillVoice(int nvoice);
FFTwrapper *fft;

void add2XMLsection(XMLwrapper *xml, int n);
void getfromXMLsection(XMLwrapper *xml, int n);
};

#endif

+ 13
- 0
c++/carla-backend/plugins/zynaddsubfx/Params/CMakeLists.txt View File

@@ -0,0 +1,13 @@
set(zynaddsubfx_params_SRCS
Params/ADnoteParameters.cpp
Params/Controller.cpp
Params/EnvelopeParams.cpp
Params/FilterParams.cpp
Params/LFOParams.cpp
Params/PADnoteParameters.cpp
Params/Presets.cpp
Params/PresetsArray.cpp
Params/PresetsStore.cpp
Params/SUBnoteParameters.cpp
PARENT_SCOPE
)

+ 419
- 0
c++/carla-backend/plugins/zynaddsubfx/Params/Controller.cpp View File

@@ -0,0 +1,419 @@
/*
ZynAddSubFX - a software synthesizer

Controller.cpp - (Midi) Controllers implementation
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 "Controller.h"
#include <math.h>
#include <stdio.h>

Controller::Controller()
{
defaults();
resetall();
}

Controller::~Controller()
{}

void Controller::defaults()
{
setpitchwheelbendrange(200); //2 halftones
expression.receive = 1;
panning.depth = 64;
filtercutoff.depth = 64;
filterq.depth = 64;
bandwidth.depth = 64;
bandwidth.exponential = 0;
modwheel.depth = 80;
modwheel.exponential = 0;
fmamp.receive = 1;
volume.receive = 1;
sustain.receive = 1;
NRPN.receive = 1;

portamento.portamento = 0;
portamento.used = 0;
portamento.proportional = 0;
portamento.propRate = 80;
portamento.propDepth = 90;
portamento.receive = 1;
portamento.time = 64;
portamento.updowntimestretch = 64;
portamento.pitchthresh = 3;
portamento.pitchthreshtype = 1;
portamento.noteusing = -1;
resonancecenter.depth = 64;
resonancebandwidth.depth = 64;

initportamento(440.0f, 440.0f, false); // Now has a third argument
setportamento(0);
}

void Controller::resetall()
{
setpitchwheel(0); //center
setexpression(127);
setpanning(64);
setfiltercutoff(64);
setfilterq(64);
setbandwidth(64);
setmodwheel(64);
setfmamp(127);
setvolume(127);
setsustain(0);
setresonancecenter(64);
setresonancebw(64);

//reset the NRPN
NRPN.parhi = -1;
NRPN.parlo = -1;
NRPN.valhi = -1;
NRPN.vallo = -1;
}

void Controller::setpitchwheel(int value)
{
pitchwheel.data = value;
float cents = value / 8192.0f;
cents *= pitchwheel.bendrange;
pitchwheel.relfreq = powf(2, cents / 1200.0f);
//fprintf(stderr,"%ld %ld -> %.3f\n",pitchwheel.bendrange,pitchwheel.data,pitchwheel.relfreq);fflush(stderr);
}

void Controller::setpitchwheelbendrange(unsigned short int value)
{
pitchwheel.bendrange = value;
}

void Controller::setexpression(int value)
{
expression.data = value;
if(expression.receive != 0)
expression.relvolume = value / 127.0f;
else
expression.relvolume = 1.0f;
}

void Controller::setpanning(int value)
{
panning.data = value;
panning.pan = (value / 128.0f - 0.5f) * (panning.depth / 64.0f);
}

void Controller::setfiltercutoff(int value)
{
filtercutoff.data = value;
filtercutoff.relfreq =
(value - 64.0f) * filtercutoff.depth / 4096.0f * 3.321928f; //3.3219f..=ln2(10)
}

void Controller::setfilterq(int value)
{
filterq.data = value;
filterq.relq = powf(30.0f, (value - 64.0f) / 64.0f * (filterq.depth / 64.0f));
}

void Controller::setbandwidth(int value)
{
bandwidth.data = value;
if(bandwidth.exponential == 0) {
float tmp = powf(25.0f, powf(bandwidth.depth / 127.0f, 1.5f)) - 1.0f;
if((value < 64) && (bandwidth.depth >= 64))
tmp = 1.0f;
bandwidth.relbw = (value / 64.0f - 1.0f) * tmp + 1.0f;
if(bandwidth.relbw < 0.01f)
bandwidth.relbw = 0.01f;
}
else
bandwidth.relbw =
powf(25.0f, (value - 64.0f) / 64.0f * (bandwidth.depth / 64.0f));
;
}

void Controller::setmodwheel(int value)
{
modwheel.data = value;
if(modwheel.exponential == 0) {
float tmp =
powf(25.0f, powf(modwheel.depth / 127.0f, 1.5f) * 2.0f) / 25.0f;
if((value < 64) && (modwheel.depth >= 64))
tmp = 1.0f;
modwheel.relmod = (value / 64.0f - 1.0f) * tmp + 1.0f;
if(modwheel.relmod < 0.0f)
modwheel.relmod = 0.0f;
}
else
modwheel.relmod =
powf(25.0f, (value - 64.0f) / 64.0f * (modwheel.depth / 80.0f));
}

void Controller::setfmamp(int value)
{
fmamp.data = value;
fmamp.relamp = value / 127.0f;
if(fmamp.receive != 0)
fmamp.relamp = value / 127.0f;
else
fmamp.relamp = 1.0f;
}

void Controller::setvolume(int value)
{
volume.data = value;
if(volume.receive != 0)
volume.volume = powf(0.1f, (127 - value) / 127.0f * 2.0f);
else
volume.volume = 1.0f;
}

void Controller::setsustain(int value)
{
sustain.data = value;
if(sustain.receive != 0)
sustain.sustain = ((value < 64) ? 0 : 1);
else
sustain.sustain = 0;
}

void Controller::setportamento(int value)
{
portamento.data = value;
if(portamento.receive != 0)
portamento.portamento = ((value < 64) ? 0 : 1);
}

int Controller::initportamento(float oldfreq,
float newfreq,
bool legatoflag)
{
portamento.x = 0.0f;

if(legatoflag) { // Legato in progress
if(portamento.portamento == 0)
return 0;
}
else // No legato, do the original if...return
if((portamento.used != 0) || (portamento.portamento == 0))
return 0;
;

float portamentotime = powf(100.0f, portamento.time / 127.0f) / 50.0f; //portamento time in seconds

if(portamento.proportional) {
//If there is a min(float,float) and a max(float,float) then they
//could be used here
//Linear functors could also make this nicer
if(oldfreq > newfreq) //2 is the center of propRate
portamentotime *=
powf(oldfreq / newfreq
/ (portamento.propRate / 127.0f * 3 + .05),
(portamento.propDepth / 127.0f * 1.6f + .2));
else //1 is the center of propDepth
portamentotime *=
powf(newfreq / oldfreq
/ (portamento.propRate / 127.0f * 3 + .05),
(portamento.propDepth / 127.0f * 1.6f + .2));
}

if((portamento.updowntimestretch >= 64) && (newfreq < oldfreq)) {
if(portamento.updowntimestretch == 127)
return 0;
portamentotime *= powf(0.1f,
(portamento.updowntimestretch - 64) / 63.0f);
}
if((portamento.updowntimestretch < 64) && (newfreq > oldfreq)) {
if(portamento.updowntimestretch == 0)
return 0;
portamentotime *= powf(0.1f,
(64.0f - portamento.updowntimestretch) / 64.0f);
}

//printf("%f->%f : Time %f\n",oldfreq,newfreq,portamentotime);

portamento.dx = synth->buffersize_f / (portamentotime * synth->samplerate_f);
portamento.origfreqrap = oldfreq / newfreq;

float tmprap = ((portamento.origfreqrap > 1.0f) ?
(portamento.origfreqrap) :
(1.0f / portamento.origfreqrap));

float thresholdrap = powf(2.0f, portamento.pitchthresh / 12.0f);
if((portamento.pitchthreshtype == 0) && (tmprap - 0.00001f > thresholdrap))
return 0;
if((portamento.pitchthreshtype == 1) && (tmprap + 0.00001f < thresholdrap))
return 0;

portamento.used = 1;
portamento.freqrap = portamento.origfreqrap;
return 1;
}

void Controller::updateportamento()
{
if(portamento.used == 0)
return;

portamento.x += portamento.dx;
if(portamento.x > 1.0f) {
portamento.x = 1.0f;
portamento.used = 0;
}
portamento.freqrap =
(1.0f - portamento.x) * portamento.origfreqrap + portamento.x;
}


void Controller::setresonancecenter(int value)
{
resonancecenter.data = value;
resonancecenter.relcenter =
powf(3.0f, (value - 64.0f) / 64.0f * (resonancecenter.depth / 64.0f));
}
void Controller::setresonancebw(int value)
{
resonancebandwidth.data = value;
resonancebandwidth.relbw =
powf(1.5f, (value - 64.0f) / 64.0f * (resonancebandwidth.depth / 127.0f));
}


//Returns 0 if there is NRPN or 1 if there is not
int Controller::getnrpn(int *parhi, int *parlo, int *valhi, int *vallo)
{
if(NRPN.receive == 0)
return 1;
if((NRPN.parhi < 0) || (NRPN.parlo < 0) || (NRPN.valhi < 0)
|| (NRPN.vallo < 0))
return 1;

*parhi = NRPN.parhi;
*parlo = NRPN.parlo;
*valhi = NRPN.valhi;
*vallo = NRPN.vallo;
return 0;
}


void Controller::setparameternumber(unsigned int type, int value)
{
switch(type) {
case C_nrpnhi:
NRPN.parhi = value;
NRPN.valhi = -1;
NRPN.vallo = -1; //clear the values
break;
case C_nrpnlo:
NRPN.parlo = value;
NRPN.valhi = -1;
NRPN.vallo = -1; //clear the values
break;
case C_dataentryhi:
if((NRPN.parhi >= 0) && (NRPN.parlo >= 0))
NRPN.valhi = value;
break;
case C_dataentrylo:
if((NRPN.parhi >= 0) && (NRPN.parlo >= 0))
NRPN.vallo = value;
break;
}
}



void Controller::add2XML(XMLwrapper *xml)
{
xml->addpar("pitchwheel_bendrange", pitchwheel.bendrange);

xml->addparbool("expression_receive", expression.receive);
xml->addpar("panning_depth", panning.depth);
xml->addpar("filter_cutoff_depth", filtercutoff.depth);
xml->addpar("filter_q_depth", filterq.depth);
xml->addpar("bandwidth_depth", bandwidth.depth);
xml->addpar("mod_wheel_depth", modwheel.depth);
xml->addparbool("mod_wheel_exponential", modwheel.exponential);
xml->addparbool("fm_amp_receive", fmamp.receive);
xml->addparbool("volume_receive", volume.receive);
xml->addparbool("sustain_receive", sustain.receive);

xml->addparbool("portamento_receive", portamento.receive);
xml->addpar("portamento_time", portamento.time);
xml->addpar("portamento_pitchthresh", portamento.pitchthresh);
xml->addpar("portamento_pitchthreshtype", portamento.pitchthreshtype);
xml->addpar("portamento_portamento", portamento.portamento);
xml->addpar("portamento_updowntimestretch", portamento.updowntimestretch);
xml->addpar("portamento_proportional", portamento.proportional);
xml->addpar("portamento_proprate", portamento.propRate);
xml->addpar("portamento_propdepth", portamento.propDepth);

xml->addpar("resonance_center_depth", resonancecenter.depth);
xml->addpar("resonance_bandwidth_depth", resonancebandwidth.depth);
}

void Controller::getfromXML(XMLwrapper *xml)
{
pitchwheel.bendrange = xml->getpar("pitchwheel_bendrange",
pitchwheel.bendrange,
-6400,
6400);

expression.receive = xml->getparbool("expression_receive",
expression.receive);
panning.depth = xml->getpar127("panning_depth", panning.depth);
filtercutoff.depth = xml->getpar127("filter_cutoff_depth",
filtercutoff.depth);
filterq.depth = xml->getpar127("filter_q_depth", filterq.depth);
bandwidth.depth = xml->getpar127("bandwidth_depth", bandwidth.depth);
modwheel.depth = xml->getpar127("mod_wheel_depth", modwheel.depth);
modwheel.exponential = xml->getparbool("mod_wheel_exponential",
modwheel.exponential);
fmamp.receive = xml->getparbool("fm_amp_receive",
fmamp.receive);
volume.receive = xml->getparbool("volume_receive",
volume.receive);
sustain.receive = xml->getparbool("sustain_receive",
sustain.receive);

portamento.receive = xml->getparbool("portamento_receive",
portamento.receive);
portamento.time = xml->getpar127("portamento_time",
portamento.time);
portamento.pitchthresh = xml->getpar127("portamento_pitchthresh",
portamento.pitchthresh);
portamento.pitchthreshtype = xml->getpar127("portamento_pitchthreshtype",
portamento.pitchthreshtype);
portamento.portamento = xml->getpar127("portamento_portamento",
portamento.portamento);
portamento.updowntimestretch = xml->getpar127(
"portamento_updowntimestretch",
portamento.updowntimestretch);
portamento.proportional = xml->getpar127("portamento_proportional",
portamento.proportional);
portamento.propRate = xml->getpar127("portamento_proprate",
portamento.propRate);
portamento.propDepth = xml->getpar127("portamento_propdepth",
portamento.propDepth);


resonancecenter.depth = xml->getpar127("resonance_center_depth",
resonancecenter.depth);
resonancebandwidth.depth = xml->getpar127("resonance_bandwidth_depth",
resonancebandwidth.depth);
}

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save