| @@ -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) | |||
| @@ -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); | |||
| } | |||
| @@ -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 | |||
| @@ -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 | |||
| ) | |||
| @@ -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(); | |||
| } | |||
| @@ -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 | |||
| @@ -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 | |||
| } | |||
| @@ -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 | |||
| @@ -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); | |||
| } | |||
| @@ -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 | |||
| @@ -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; | |||
| } | |||
| @@ -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 | |||
| @@ -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; | |||
| } | |||
| @@ -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 | |||
| @@ -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; | |||
| } | |||
| } | |||
| @@ -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 | |||
| @@ -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 | |||
| ) | |||
| @@ -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; | |||
| } | |||
| } | |||
| @@ -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 | |||
| @@ -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 | |||
| } | |||
| } | |||
| @@ -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 | |||
| @@ -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; | |||
| } | |||
| } | |||
| @@ -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 | |||
| @@ -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); | |||
| } | |||
| @@ -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 | |||
| @@ -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 | |||
| } | |||
| } | |||
| @@ -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 | |||
| @@ -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; | |||
| } | |||
| @@ -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 | |||
| @@ -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; | |||
| } | |||
| @@ -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 | |||
| @@ -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(); | |||
| } | |||
| @@ -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 | |||
| @@ -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; | |||
| } | |||
| } | |||
| @@ -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 | |||
| @@ -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; | |||
| } | |||
| } | |||
| @@ -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 | |||
| @@ -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; | |||
| } | |||
| @@ -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 | |||
| @@ -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() | |||
| @@ -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"); | |||
| } | |||
| @@ -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 | |||
| @@ -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_ */ | |||
| @@ -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 | |||
| } | |||
| @@ -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 | |||
| @@ -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)); | |||
| } | |||
| @@ -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 | |||
| @@ -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(µtonal, 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(); | |||
| } | |||
| } | |||
| @@ -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 | |||
| @@ -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 µ) const | |||
| { | |||
| return !(*this != micro); | |||
| } | |||
| bool Microtonal::operator!=(const Microtonal µ) 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; | |||
| } | |||
| @@ -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 µ) const; | |||
| bool operator!=(const Microtonal µ) 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 | |||
| @@ -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 | |||
| @@ -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 | |||
| @@ -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 | |||
| @@ -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; | |||
| } | |||
| @@ -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 | |||
| @@ -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); | |||
| } | |||
| @@ -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 | |||
| @@ -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; | |||
| } | |||
| } | |||
| @@ -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 | |||
| @@ -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; | |||
| } | |||
| } | |||
| @@ -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 | |||
| @@ -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; | |||
| } | |||
| @@ -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 | |||
| @@ -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; | |||
| } | |||
| @@ -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 | |||
| @@ -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); | |||
| } | |||
| @@ -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 | |||
| @@ -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} | |||
| ) | |||
| @@ -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() | |||
| {} | |||
| @@ -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 | |||
| @@ -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; | |||
| } | |||
| @@ -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 | |||
| @@ -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)); | |||
| } | |||
| @@ -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 | |||
| @@ -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 */ | |||
| } | |||
| } | |||
| } | |||
| @@ -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 | |||
| @@ -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; | |||
| } | |||
| } | |||
| @@ -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 | |||
| @@ -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 | |||
| @@ -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 | |||
| @@ -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; | |||
| } | |||
| @@ -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 | |||
| @@ -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); | |||
| } | |||
| @@ -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 | |||
| @@ -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; | |||
| } | |||
| @@ -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 | |||
| @@ -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(); | |||
| } | |||
| @@ -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 | |||
| @@ -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; | |||
| } | |||
| @@ -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 | |||
| @@ -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; | |||
| } | |||
| @@ -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 | |||
| @@ -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; | |||
| } | |||
| @@ -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 | |||
| @@ -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(); | |||
| } | |||
| } | |||
| @@ -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 | |||
| @@ -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 | |||
| ) | |||
| @@ -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); | |||
| } | |||