zynaddsubfx

ZynAddSubFX open source synthesizer
Log | Files | Refs | Submodules | LICENSE

commit e85dd19254df52b18d4d53554d4a60c8d61a16a4
parent 5fe48831dfd1faa966b571a08ec04ed4aa8b0628
Author: fundamental <mark.d.mccurry@gmail.com>
Date:   Sat, 22 May 2010 07:21:17 -0400

Merge branch 'nio'

Diffstat:
M.gitignore | 2--
MAUTHORS.txt | 1+
MChangeLog | 8++++++++
Mcmake/Findfftw.cmake | 4++--
Msrc/CMakeLists.txt | 163++++++++++++++++++++++++++++++++++++++++++++++---------------------------------
Dsrc/DSP/Makefile | 14--------------
Msrc/Effects/Effect.cpp | 1+
Msrc/Effects/Effect.h | 1+
Msrc/Effects/EffectMgr.cpp | 10++++++++++
Msrc/Effects/EffectMgr.h | 12+++++++-----
Dsrc/Effects/Makefile | 16----------------
Dsrc/Input/ALSAMidiIn.cpp | 118-------------------------------------------------------------------------------
Dsrc/Input/ALSAMidiIn.h | 51---------------------------------------------------
Dsrc/Input/CMakeLists.txt | 20--------------------
Dsrc/Input/Makefile | 26--------------------------
Dsrc/Input/NULLMidiIn.cpp | 41-----------------------------------------
Dsrc/Input/NULLMidiIn.h | 50--------------------------------------------------
Dsrc/Input/OSSMidiIn.cpp | 123-------------------------------------------------------------------------------
Dsrc/Input/OSSMidiIn.h | 50--------------------------------------------------
Dsrc/Makefile | 134-------------------------------------------------------------------------------
Dsrc/Makefile.inc | 84-------------------------------------------------------------------------------
Asrc/Misc/Atomic.cpp | 70++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/Misc/Atomic.h | 46++++++++++++++++++++++++++++++++++++++++++++++
Msrc/Misc/Bank.cpp | 6++++--
Msrc/Misc/Bank.h | 1+
Msrc/Misc/CMakeLists.txt | 4+++-
Msrc/Misc/Config.cpp | 22+++++++++++++++-------
Msrc/Misc/Config.h | 3+++
Dsrc/Misc/Makefile | 18------------------
Msrc/Misc/Master.cpp | 277+++++++++++++++++++++++++++++--------------------------------------------------
Msrc/Misc/Master.h | 61+++++++++++++++++++++++++++++++------------------------------
Msrc/Misc/Part.cpp | 7+++++++
Msrc/Misc/Part.h | 15++++++++-------
Asrc/Misc/Recorder.cpp | 92+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/Misc/Recorder.h | 55+++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/Misc/Stereo.h | 1+
Msrc/Misc/Util.cpp | 30++++++++++++++++++++++++++++++
Msrc/Misc/Util.h | 15+++++++++++++++
Asrc/Misc/WavFile.cpp | 94+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/Misc/WavFile.h | 45+++++++++++++++++++++++++++++++++++++++++++++
Asrc/Nio/AlsaEngine.cpp | 351+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/Nio/AlsaEngine.h | 82+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/Nio/AudioOut.cpp | 92+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/Nio/AudioOut.h | 63+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/Nio/CMakeLists.txt | 55+++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/Nio/Engine.cpp | 29+++++++++++++++++++++++++++++
Asrc/Nio/Engine.h | 41+++++++++++++++++++++++++++++++++++++++++
Asrc/Nio/EngineMgr.cpp | 150+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/Nio/EngineMgr.h | 43+++++++++++++++++++++++++++++++++++++++++++
Asrc/Nio/InMgr.cpp | 101+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/Nio/InMgr.h | 52++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/Nio/JackEngine.cpp | 349+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/Nio/JackEngine.h | 89+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/Nio/MidiIn.cpp | 62++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/Nio/MidiIn.h | 41+++++++++++++++++++++++++++++++++++++++++
Asrc/Nio/NulEngine.cpp | 115+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/Nio/NulEngine.h | 57+++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/Nio/OssEngine.cpp | 264+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/Nio/OssEngine.h | 77+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/Nio/OutMgr.cpp | 135+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/Nio/OutMgr.h | 70++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/Nio/PaEngine.cpp | 111+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/Nio/PaEngine.h | 54++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/Nio/SafeQueue.cpp | 83+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/Nio/SafeQueue.h | 40++++++++++++++++++++++++++++++++++++++++
Asrc/Nio/WavEngine.cpp | 120+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/Nio/WavEngine.h | 62++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Dsrc/Output/CMakeLists.txt | 33---------------------------------
Dsrc/Output/JACK_RTaudiooutput.cpp | 229-------------------------------------------------------------------------------
Dsrc/Output/JACKaudiooutput.cpp | 185-------------------------------------------------------------------------------
Dsrc/Output/JACKaudiooutput.h | 48------------------------------------------------
Dsrc/Output/Makefile | 42------------------------------------------
Dsrc/Output/OSSaudiooutput.cpp | 128-------------------------------------------------------------------------------
Dsrc/Output/OSSaudiooutput.h | 50--------------------------------------------------
Dsrc/Output/Recorder.cpp | 113-------------------------------------------------------------------------------
Dsrc/Output/Recorder.h | 57---------------------------------------------------------
Dsrc/Output/WAVaudiooutput.cpp | 101-------------------------------------------------------------------------------
Dsrc/Output/WAVaudiooutput.h | 43-------------------------------------------
Msrc/Params/ADnoteParameters.cpp | 3+++
Msrc/Params/ADnoteParameters.h | 5+++--
Msrc/Params/CMakeLists.txt | 2+-
Dsrc/Params/Makefile | 15---------------
Msrc/Params/PADnoteParameters.cpp | 9++++-----
Dsrc/Samples/Makefile | 14--------------
Msrc/Samples/Sample.cpp | 92+++++++++++++++++++++++++++++++++++++++++--------------------------------------
Msrc/Samples/Sample.h | 15++++++++++-----
Dsrc/Seq/Makefile | 14--------------
Dsrc/Synth/Makefile | 14--------------
Msrc/Tests/EchoTest.h | 1+
Msrc/Tests/SampleTest.h | 18++++++++++++++++++
Msrc/UI/CMakeLists.txt | 1+
Dsrc/UI/Makefile | 41-----------------------------------------
Msrc/UI/MasterUI.fl | 53+++++++++++++++++++++++++++++++++--------------------
Asrc/UI/NioUI.cpp | 82+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/UI/NioUI.h | 29+++++++++++++++++++++++++++++
Msrc/UI/VirKeyboard.fl | 15+++++++--------
Msrc/main.cpp | 483++++++++++++++++++++++---------------------------------------------------------
97 files changed, 3909 insertions(+), 2605 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -1,7 +1,5 @@ *.o *~ -src/UI/*.h -src/UI/*.cc src/zynaddsubfx src/Tests/runner src/Tests/runner.cpp diff --git a/AUTHORS.txt b/AUTHORS.txt @@ -15,6 +15,7 @@ Contributors: Alexis Ballier (const char* <-> string mismatch, NULLMidi prototype fix) Tobias Doerffel (static-instance variables fix, missing include fix) James Morris (Memory leaks in FLTK GUI) + Alan Calvert (Portions of New IO) Stephen Parry (DSSI rebuild) Ryan Billing (APhaser) diff --git a/ChangeLog b/ChangeLog @@ -963,6 +963,9 @@ 28 Oct 2009 (Paul Nasca) - Disable "bw" control on Reverb when Bandwidth mode is not enabled +30 Oct 2009 (Mark McCurry) + - Commited first stage of Nio (New IO) WIP + 18 Nov 2009 (Mark McCurry) - Fixed segfault in VirKeyBoard @@ -974,6 +977,11 @@ the Wextra flag - Minor change to Filter_ and FormantFilter to reduce unwanted warnings +13 Dec 2009 (Mark McCurry) + - Deprecating Output system for the Nio system + - General Code Cleanup + - Adding OpenGL linking for proper compiles + 14 Jan 2010 (Mark McCurry) - Fixed No UI Flag "-U" as it was previously partially initializing the gui diff --git a/cmake/Findfftw.cmake b/cmake/Findfftw.cmake @@ -7,7 +7,7 @@ SET(fftw_FOUND 0) -IF(UNIX) +IF(UNIX OR CYGWIN) FIND_PATH(fftw_INCLUDE_DIR fftw3.h /usr/include @@ -18,7 +18,7 @@ IF(UNIX) /usr/lib ) -ENDIF(UNIX) +ENDIF(UNIX OR CYGWIN) # handle the QUIETLY and REQUIRED arguments and set fftw_FOUND to TRUE if # all listed variables are TRUE diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt @@ -1,23 +1,38 @@ +#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(zlib REQUIRED) find_package(fftw REQUIRED) find_package(MXML REQUIRED) find_package(PkgConfig REQUIRED) #find_package(pthread REQUIRED) +#find_package(OSS) find_package(Alsa) find_package(JACK) find_package(PortAudio) +set(FLTK_SKIP_OPENGL true) find_package(FLTK) find_package(OpenGL) #for FLTK ######### Settings ########### -# NOTE: These cache variables should normallly not be changed in this +# 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 (FFTW_VERSION 3 CACHE STRING "Version number of FFTW") -SET (OutputModule alsa CACHE STRING "Output module, either alsa, jack or portaudio") +SET (DefaultOutput oss CACHE STRING + "Default Output module: [null, alsa, oss, jack, portaudio]") SET (GuiModule fltk CACHE STRING "GUI module, either fltk, qt or off") SET (CompileTests OFF 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 ${ALSA_FOUND} CACHE BOOL #TODO perhaps check for /dev/dsp + "Enable support for Open Sound System") +SET (PaEnable ${PORTAUDIO_FOUND} CACHE BOOL + "Enable support for Port Audio System") # Now, handle the incoming settings and set define flags/variables based @@ -35,16 +50,6 @@ else () message(STATUS "GUI module defaulting to off") endif() -if (OutputModule STREQUAL alsa AND ALSA_FOUND) - set(AlsaMidiOutput TRUE) -elseif(OutputModule STREQUAL jack AND JACK_FOUND) - set(JackOutput TRUE) -elseif(OutputModule STREQUAL portaudio AND PortAudio_FOUND) - set(PortAudioOutput TRUE) -else () - message(FATAL_ERROR "OutputModule must be either alsa, jack or portaudio") -endif() - if(NOT PKG_CONFIG_FOUND) message(FATAL_ERROR "pkg-config not found") endif(NOT PKG_CONFIG_FOUND) @@ -57,27 +62,32 @@ mark_as_advanced(LASH_LIBRARIES) # From here on, the setting variables have been prepared so concentrate # on the actual compiling. -if(AlsaMidiOutput) +if(AlsaEnable) + list(APPEND AUDIO_LIBRARIES ${ASOUND_LIBRARY}) + add_definitions(-DALSA=1) +endif(AlsaEnable) + +if(JackEnable) + list(APPEND AUDIO_LIBRARIES ${JACK_LIBRARIES}) + add_definitions(-DJACK=1) +endif(JackEnable) + +if(OssEnable) add_definitions(-DOSSAUDIOOUT) - set(AUDIO_LIBRARIES ${AUDIO_LIBRARIES} ${ASOUND_LIBRARY}) + list(APPEND AUDIO_LIBRARIES ${ASOUND_LIBRARY}) + add_definitions(-DOSS=1) +endif(OssEnable) + +if(PaEnable) + include_directories(${PORTAUDIO_INCLUDE_DIR}) + add_definitions(-DPORTAUDIO=1) + list(APPEND AUDIO_LIBRARIES ${PORTAUDIO_LIBRARIES}) endif() if (CompileTests) ENABLE_TESTING() endif() -if(JackOutput) - include_directories(${JACK_INCLUDE_DIR}) - add_definitions(-DJACKAUDIOOUT) - set(AUDIO_LIBRARIES ${AUDIO_LIBRARIES} ${JACK_LIBRARIES}) -endif() - -if(PortAudioOutput) - include_directories(${PORTAUDIO_INCLUDE_DIR}) - add_definitions(-DPAAUDIOOUT) - set(AUDIO_LIBRARIES ${AUDIO_LIBRARIES} ${PORTAUDIO_LIBRARIES}) -endif() - if(LASH_FOUND) include_directories(${LASH_INCLUDE_DIRS}) add_definitions(-DUSE_LASH) @@ -86,22 +96,28 @@ if(LASH_FOUND) endif() add_definitions(-DFFTW_VERSION_${FFTW_VERSION} - -DOS_LINUX - -DALSAMIDIIN -DASM_F2I_YES + -g #TODO #todo put in a better location + -Wall + -Wextra ) +#set os flag +if(CYGWIN) + add_definitions(-DOS_CYGWIN=1) + set(OS_LIBRARIES "-Wl,--enable-auto-import") +elseif(WINDOWS) + add_definitions(-DOS_WINDOWS=1) + set(OS_LIBRARIES "") +elseif(UNIX) + add_definitions(-DOS_LINUX=1) + set(OS_LIBRARIES "") +else() + message(STATUS "Error: building on unsupported OS") +endif() -if(FltkGui) - #message(STATUS "FLTK_LIBRARIES: ${FLTK_LIBRARIES}") - #message(STATUS "FLTK_MATH_LIBRARY: ${FLTK_MATH_LIBRARY}") - #UGLY WORKAROUND - find_program (MYFLTK_CONFIG fltk-config) - if (MYFLTK_CONFIG) - execute_process (COMMAND ${MYFLTK_CONFIG} --ldflags OUTPUT_VARIABLE MYFLTK_LDFLAGS) - string(STRIP ${MYFLTK_LDFLAGS} MYFLTK_LIBRARIES) - endif() +if(FLTK_FOUND) mark_as_advanced(FORCE FLTK_BASE_LIBRARY) mark_as_advanced(FORCE FLTK_CONFIG_SCRIPT) mark_as_advanced(FORCE FLTK_DIR) @@ -111,9 +127,21 @@ if(FltkGui) 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(FltkGui) + #UGLY WORKAROUND + find_program (MYFLTK_CONFIG fltk-config) + if (MYFLTK_CONFIG) + execute_process (COMMAND ${MYFLTK_CONFIG} --ldflags OUTPUT_VARIABLE MYFLTK_LDFLAGS) + string(STRIP ${MYFLTK_LDFLAGS} MYFLTK_LIBRARIES) + endif() + + message(STATUS ${MYFLTK_LDFLAGS}) set(GUI_LIBRARIES ${FLTK_LIBRARIES} ${MYFLTK_LIBRARIES} ${OPENGL_LIBRARIES} zynaddsubfx_gui) + add_definitions(-DFLTK_GUI) message(STATUS "Will build fltk gui") @@ -141,33 +169,32 @@ include_directories( add_definitions(-DSOURCE_DIR="${CMAKE_CURRENT_SOURCE_DIR}") #macro for tests macro(unit_test NAME CXX_FILE FILES) - if (CompileTests) - set(PATH_FILES "") - foreach(part ${FILES}) - set(PATH_FILES "${CMAKE_CURRENT_SOURCE_DIR}/${part}" ${PATH_FILES}) - endforeach(part ${FILES}) - set(CXX_FILE_REAL "${CMAKE_CURRENT_SOURCE_DIR}/${CXX_FILE}") - add_custom_command( - OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${NAME}.cxx" - COMMAND cxxtestgen.py --error-printer -o "${CMAKE_CURRENT_BINARY_DIR}/${NAME}.cxx" ${CXX_FILE_REAL} - DEPENDS "${FILE}" - ) - set(CXXTEST_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${NAME}.cxx") - add_executable("${NAME}" "${CXXTEST_OUTPUT}" ${PATH_FILES}) - target_link_libraries("${NAME}" ${CXXTEST_LINK_LIBS} - ${NONGUI_LIBRARIES} - ${GUI_LIBRARIES} - ${zlib_LIBRARIES} - ${fftw_LIBRARIES} - ${MXML_LIBRARIES} - ) - add_test("${NAME}" "${EXECUTABLE_OUTPUT_PATH}/${NAME}") - endif() + if (CompileTests) + set(PATH_FILES "") + foreach(part ${FILES}) + set(PATH_FILES "${CMAKE_CURRENT_SOURCE_DIR}/${part}" ${PATH_FILES}) + endforeach(part ${FILES}) + set(CXX_FILE_REAL "${CMAKE_CURRENT_SOURCE_DIR}/${CXX_FILE}") + add_custom_command( + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${NAME}.cxx" + COMMAND cxxtestgen.py --error-printer -o "${CMAKE_CURRENT_BINARY_DIR}/${NAME}.cxx" ${CXX_FILE_REAL} + DEPENDS "${FILE}" + ) + set(CXXTEST_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${NAME}.cxx") + add_executable("${NAME}" "${CXXTEST_OUTPUT}" ${PATH_FILES}) + target_link_libraries("${NAME}" ${CXXTEST_LINK_LIBS} + ${NONGUI_LIBRARIES} + ${GUI_LIBRARIES} + ${zlib_LIBRARIES} + ${fftw_LIBRARIES} + ${MXML_LIBRARIES} + ${OS_LIBRARIES} + ) + add_test("${NAME}" "${EXECUTABLE_OUTPUT_PATH}/${NAME}") + endif() endmacro(unit_test) set(NONGUI_LIBRARIES - zynaddsubfx_input - zynaddsubfx_output zynaddsubfx_misc zynaddsubfx_synth zynaddsubfx_seq @@ -175,27 +202,28 @@ set(NONGUI_LIBRARIES zynaddsubfx_params zynaddsubfx_dsp zynaddsubfx_samples + zynaddsubfx_nio ) set(CXXTEST_LINK_LIBS ${NONGUI_LIBRARIES}) -set(MIDIINPUT_LIBRARIES "") add_subdirectory(Misc) -add_subdirectory(Input) add_subdirectory(Synth) -add_subdirectory(Output) add_subdirectory(Seq) add_subdirectory(Effects) add_subdirectory(Params) add_subdirectory(DSP) add_subdirectory(Samples) -add_subdirectory(Tests) +if(CompileTests) + add_subdirectory(Tests) +endif(CompileTests) +add_subdirectory(Nio) set(zynaddsubfx_SRCS main.cpp ) -add_executable(zynaddsubfx +add_executable(zynaddsubfx ${zynaddsubfx_SRCS} ) @@ -205,8 +233,9 @@ target_link_libraries(zynaddsubfx ${zlib_LIBRARIES} ${fftw_LIBRARIES} ${MXML_LIBRARIES} + ${NIO_LIBRARIES} ${AUDIO_LIBRARIES} - ${MIDIINPUT_LIBRARIES} + ${OS_LIBRARIES} ) install(TARGETS zynaddsubfx diff --git a/src/DSP/Makefile b/src/DSP/Makefile @@ -1,14 +0,0 @@ -include ../Makefile.inc - -objects=FFTwrapper.o AnalogFilter.o FormantFilter.o SVFilter.o Filter.o Unison.o - - -all: $(objects) - --include ../Make.deps - -.PHONY : clean -clean: - rm -f $(objects) - rm -f makeinclude.deps - diff --git a/src/Effects/Effect.cpp b/src/Effects/Effect.cpp @@ -21,6 +21,7 @@ */ #include "Effect.h" +#include "../Params/FilterParams.h" Effect::Effect(bool insertion_, REALTYPE *const efxoutl_, REALTYPE *const efxoutr_, FilterParams *filterpars_, diff --git a/src/Effects/Effect.h b/src/Effects/Effect.h @@ -28,6 +28,7 @@ #include "../Params/FilterParams.h" #include "../Misc/Stereo.h" +class FilterParams; /**this class is inherited by the all effects(Reverb, Echo, ..)*/ class Effect diff --git a/src/Effects/EffectMgr.cpp b/src/Effects/EffectMgr.cpp @@ -21,6 +21,16 @@ */ #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; diff --git a/src/Effects/EffectMgr.h b/src/Effects/EffectMgr.h @@ -24,12 +24,14 @@ #include <pthread.h> -#include "Effect.h" -#include "Reverb.h" -#include "Echo.h" -#include "Chorus.h" -#include "Phaser.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" diff --git a/src/Effects/Makefile b/src/Effects/Makefile @@ -1,16 +0,0 @@ -include ../Makefile.inc - -objects=Alienwah.o Chorus.o Echo.o Effect.o \ - EffectLFO.o EffectMgr.o Phaser.o Reverb.o \ - Distorsion.o EQ.o DynamicFilter.o APhaser.o - - -all: $(objects) - --include ../Make.deps - -.PHONY : clean -clean: - rm -f $(objects) - rm -f makeinclude.deps - diff --git a/src/Input/ALSAMidiIn.cpp b/src/Input/ALSAMidiIn.cpp @@ -1,118 +0,0 @@ -/* - ZynAddSubFX - a software synthesizer - - ALSAMidiIn.cpp - Midi input for ALSA (this creates an ALSA virtual port) - 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 "ALSAMidiIn.h" - -ALSAMidiIn::ALSAMidiIn() -{ - int alsaport; - inputok = false; - - midi_handle = NULL; - - if(snd_seq_open(&midi_handle, "default", SND_SEQ_OPEN_INPUT, 0) != 0) - return; - - snd_seq_set_client_name(midi_handle, "ZynAddSubFX"); //thanks to Frank Neumann - - 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; - - inputok = true; -} - -ALSAMidiIn::~ALSAMidiIn() -{ - if(midi_handle) - snd_seq_close(midi_handle); -} - - -/* - * Get the midi command,channel and parameters - */ -void ALSAMidiIn::getmidicmd(MidiCmdType &cmdtype, - unsigned char &cmdchan, - int *cmdparams) -{ - snd_seq_event_t *midievent = NULL; - cmdtype = MidiNull; - - if(inputok == false) { - /* The input is broken. We need to block for a while anyway so other - non-RT threads get a chance to run. */ - sleep(1); - return; - } - - snd_seq_event_input(midi_handle, &midievent); - - if(midievent == NULL) - return; - switch(midievent->type) { - case SND_SEQ_EVENT_NOTEON: - cmdtype = MidiNoteON; - cmdchan = midievent->data.note.channel; - cmdparams[0] = midievent->data.note.note; - cmdparams[1] = midievent->data.note.velocity; - break; - case SND_SEQ_EVENT_NOTEOFF: - cmdtype = MidiNoteOFF; - cmdchan = midievent->data.note.channel; - cmdparams[0] = midievent->data.note.note; - break; - case SND_SEQ_EVENT_PITCHBEND: - cmdtype = MidiController; - cmdchan = midievent->data.control.channel; - cmdparams[0] = C_pitchwheel; //Pitch Bend - cmdparams[1] = midievent->data.control.value; - break; - case SND_SEQ_EVENT_CONTROLLER: - cmdtype = MidiController; - cmdchan = midievent->data.control.channel; - cmdparams[0] = getcontroller(midievent->data.control.param); - cmdparams[1] = midievent->data.control.value; - //fprintf(stderr,"t=%d val=%d\n",midievent->data.control.param,midievent->data.control.value); - break; - } -} - - -int ALSAMidiIn::getalsaid() -{ - if(midi_handle) { - snd_seq_client_info_t *seq_info; - snd_seq_client_info_malloc(&seq_info); - snd_seq_get_client_info(midi_handle, seq_info); - int id = snd_seq_client_info_get_client(seq_info); - snd_seq_client_info_free(seq_info); - return id; - } - return -1; -} - diff --git a/src/Input/ALSAMidiIn.h b/src/Input/ALSAMidiIn.h @@ -1,51 +0,0 @@ -/* - ZynAddSubFX - a software synthesizer - - ALSAMidiIn.h - Midi input for ALSA (this creates an ALSA virtual port) - 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 ALSA_MIDI_IN_H -#define ALSA_MIDI_IN_H - -#include <alsa/asoundlib.h> -#include "MidiIn.h" - - -/**Midi input for ALSA (this creates an ALSA virtual port)*/ -class ALSAMidiIn:public MidiIn -{ - public: - /**Constructor*/ - ALSAMidiIn(); - /**Destructor*/ - ~ALSAMidiIn(); - - void getmidicmd(MidiCmdType &cmdtype, - unsigned char &cmdchan, - int *cmdparams); - /**Get the ALSA id - * @return ALSA id*/ - int getalsaid(); - - private: - snd_seq_t *midi_handle; -}; - -#endif - diff --git a/src/Input/CMakeLists.txt b/src/Input/CMakeLists.txt @@ -1,20 +0,0 @@ -set(AlsaMidiInput On CACHE BOOL "Include ALSA Midi input") -set(zynaddsubfx_input_SRCS - MidiIn.cpp - NULLMidiIn.cpp - #OSSMidiIn.cpp #[TODO] get OSS midi detection and - #WINMidiIn.cpp # Win midi detection working -) - -if(AlsaMidiInput) - set(zynaddsubfx_input_SRCS - ${zynaddsubfx_input_SRCS} - ALSAMidiIn.cpp - ) - message(STATUS "Alsa midi input enabled") - set(MIDIINPUT_LIBRARIES ${ASOUND_LIBRARY} PARENT_SCOPE) -endif(AlsaMidiInput) - -add_library(zynaddsubfx_input STATIC - ${zynaddsubfx_input_SRCS} - ) diff --git a/src/Input/Makefile b/src/Input/Makefile @@ -1,26 +0,0 @@ -include ../Makefile.inc - -objects=NULLMidiIn.o MidiIn.o - -ifeq ($(MIDIIN),ALSA) -objects+=ALSAMidiIn.o -endif - -ifeq ($(MIDIIN),OSS) -objects+=OSSMidiIn.o -endif - -ifeq ($(MIDIIN),WIN) -objects+=WINMidiIn.o -endif - - -all: $(objects) - --include ../Make.deps - -.PHONY : clean -clean: - rm -f $(objects) - rm -f makeinclude.deps - diff --git a/src/Input/NULLMidiIn.cpp b/src/Input/NULLMidiIn.cpp @@ -1,41 +0,0 @@ -/* - ZynAddSubFX - a software synthesizer - - NULLMidiIn.cpp - a dummy Midi port - 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 "NULLMidiIn.h" - -NULLMidiIn::NULLMidiIn() -{} - -NULLMidiIn::~NULLMidiIn() -{} - -/* - * Get the midi command,channel and parameters - * It returns MidiNull because it is a dummy driver - */ -void NULLMidiIn::getmidicmd(MidiCmdType &cmdtype, - unsigned char &cmdchan, - int *cmdparams) -{ - cmdtype = MidiNull; -} - diff --git a/src/Input/NULLMidiIn.h b/src/Input/NULLMidiIn.h @@ -1,50 +0,0 @@ -/* - ZynAddSubFX - a software synthesizer - - NULLMidiIn.h - a dummy Midi port - 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 NULL_MIDI_IN_H -#define NULL_MIDI_IN_H - -#include "MidiIn.h" - - -/**a dummy Midi port*/ -class NULLMidiIn:public MidiIn -{ - public: - /**Dummy Constructor - * \todo see if the default constructor would work here*/ - NULLMidiIn(); - /**Dummy Destructor - * \todo see if the default destructor would work here*/ - ~NULLMidiIn(); - /**Get the midi command,channel and parameters - * It returns MidiNull because it is a dummy driver - */ - void getmidicmd(MidiCmdType &cmdtype, - unsigned char &cmdchan, - int *cmdparams); - - private: -}; - -#endif - diff --git a/src/Input/OSSMidiIn.cpp b/src/Input/OSSMidiIn.cpp @@ -1,123 +0,0 @@ -/* - ZynAddSubFX - a software synthesizer - - OSSMidiIn.cpp - Midi input 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 <stdlib.h> -#include <stdio.h> -#include <sys/types.h> -#include <sys/stat.h> -#include <fcntl.h> -#include <unistd.h> -#include <sys/soundcard.h> - -#include "OSSMidiIn.h" -#include "../Misc/Util.h" - -OSSMidiIn::OSSMidiIn() -{ - inputok = false; - midi_handle = open(config.cfg.LinuxOSSSeqInDev, O_RDONLY, 0); - if(midi_handle != -1) - inputok = true; - - lastmidicmd = 0; - cmdtype = 0; - cmdchan = 0; -} - -OSSMidiIn::~OSSMidiIn() -{ - close(midi_handle); -} - -unsigned char OSSMidiIn::readbyte() -{ - unsigned char tmp[4]; - read(midi_handle, &tmp[0], 1); - while(tmp[0] != SEQ_MIDIPUTC) { - read(midi_handle, &tmp[0], 4); - } - return tmp[1]; -} - -unsigned char OSSMidiIn::getmidibyte() -{ - unsigned char b; - do { - b = readbyte(); - } while(b == 0xfe); //drops the Active Sense Messages - return b; -} - -/* - * Get the midi command,channel and parameters - */ -void OSSMidiIn::getmidicmd(MidiCmdType &cmdtype, - unsigned char &cmdchan, - int *cmdparams) -{ - unsigned char tmp, i; - if(inputok == false) { - cmdtype = MidiNull; - return; - } - i = 0; - if(lastmidicmd == 0) { //asteapta prima data pana cand vine prima comanda midi - while(tmp < 0x80) - tmp = getmidibyte(); - lastmidicmd = tmp; - } - - tmp = getmidibyte(); - - if(tmp >= 0x80) { - lastmidicmd = tmp; - tmp = getmidibyte(); - } - - if((lastmidicmd >= 0x80) && (lastmidicmd <= 0x8f)) { //Note OFF - cmdtype = MidiNoteOFF; - cmdchan = lastmidicmd % 16; - cmdparams[0] = tmp; //note number - } - - if((lastmidicmd >= 0x90) && (lastmidicmd <= 0x9f)) { //Note ON - cmdtype = MidiNoteON; - cmdchan = lastmidicmd % 16; - cmdparams[0] = tmp; //note number - cmdparams[1] = getmidibyte(); //velocity - if(cmdparams[1] == 0) - cmdtype = MidiNoteOFF; //if velocity==0 then is note off - } - if((lastmidicmd >= 0xB0) && (lastmidicmd <= 0xBF)) { //Controllers - cmdtype = MidiController; - cmdchan = lastmidicmd % 16; - cmdparams[0] = getcontroller(tmp); - cmdparams[1] = getmidibyte(); - } - if((lastmidicmd >= 0xE0) && (lastmidicmd <= 0xEF)) { //Pitch Wheel - cmdtype = MidiController; - cmdchan = lastmidicmd % 16; - cmdparams[0] = C_pitchwheel; - cmdparams[1] = (tmp + getmidibyte() * (int) 128) - 8192; //hope this is correct - } -} - diff --git a/src/Input/OSSMidiIn.h b/src/Input/OSSMidiIn.h @@ -1,50 +0,0 @@ -/* - ZynAddSubFX - a software synthesizer - - OSSMidiIn.h - Midi input 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_MIDI_IN_H -#define OSS_MIDI_IN_H - -#include "MidiIn.h" - -class OSSMidiIn:public MidiIn -{ - public: - OSSMidiIn(); - ~OSSMidiIn(); - unsigned char getmidibyte(); - unsigned char readbyte(); - - //Midi parser - void getmidicmd(MidiCmdType &cmdtype, - unsigned char &cmdchan, - int *cmdparams); - unsigned char cmdtype; //the Message Type (noteon,noteof,sysex..) - unsigned char cmdchan; //the channel number - - private: - int midi_handle; - unsigned char lastmidicmd; //last byte (>=80) received from the Midi -}; - - -#endif - diff --git a/src/Makefile b/src/Makefile @@ -1,134 +0,0 @@ -include Makefile.inc - -ifneq ($(MAKECMDGOALS),debug) - CXXFLAGS= -O2 -Wall -g -fPIC -else - CXXFLAGS= -O2 -Wall -Wpointer-arith -fPIC -endif - -CXXFLAGS += -DOS_$(OS_PORT) -D$(MIDIIN)MIDIIN -DFFTW_VERSION_$(FFTW_VERSION) -DASM_F2I_$(ASM_F2I) -ggdb - -ifeq ($(DISABLE_GUI),YES) - CXXFLAGS += -DDISABLE_GUI -else - CXXFLAGS += -DFLTK_GUI `fltk-config --cflags` -endif - -ifeq ($(AUDIOOUT),OSS_AND_JACK) - CXXFLAGS += -DOSSAUDIOOUT -DJACKAUDIOOUT -else - CXXFLAGS += -D$(AUDIOOUT)AUDIOOUT -endif - -export CXXFLAGS - -LIBS= -lm -lmxml -lz -ifneq ($(DISABLE_GUI),YES) - LIBS+=`fltk-config --ldflags` -endif - -ifeq ($(FFTW_VERSION),2) -LIBS += -lrfftw -lfftw -else -LIBS += -lfftw3 -endif - -ifeq ($(OS_PORT),LINUX) -LIBS+= -lpthread -else -#dynamic linking -#LIBS+= -lpthreadGC -#static linking -LIBS+= /usr/lib/libpthreadGC1.a -endif - -ifeq ($(MIDIIN),ALSA) -LIBS+= -lasound -endif - -ifeq ($(AUDIOOUT),PA) -LIBS+= -lportaudio -endif - -ifeq ($(OS_PORT),WINDOWS) -LIBS+= -lwinmm -endif - - -ifeq ($(AUDIOOUT),OSS_AND_JACK) -CXXFLAGS += `pkg-config --cflags jack` -LIBS+= `pkg-config --libs jack` -endif - -ifeq ($(AUDIOOUT),JACK) -CXXFLAGS += `pkg-config --cflags jack` -LIBS+= `pkg-config --libs jack` -endif - -ifeq ($(AUDIOOUT),JACK_RT) -CXXFLAGS += `pkg-config --cflags jack` -LIBS+= `pkg-config --libs jack` -endif - -ifeq ($(LINUX_USE_LASH),YES) -CXXFLAGS += `pkg-config --cflags lash-1.0` -DUSE_LASH -LIBS+= `pkg-config --libs lash-1.0` -endif - - -objects=main.o -SUBDIRS=DSP Effects Input Misc Output Params Samples Synth Seq - -.PHONY: subdirs $(SUBDIRS) - -all: -# yes " " | head - - $(MAKE) -C UI $@ -# @sh -c "cd UI ; $(CXX) -MM -MG -w *.cc >> ../Make.deps ; cd .." - @for name in $(SUBDIRS); do sh -c "cd $$name ; $(CXX) -MM -MG -w *.cpp >> ../Make.deps ; cd .."; done - $(MAKE) subdirs - $(MAKE) objs - rm -f zynaddsubfx zynaddsubfx.exe - rm -f Make.deps - - -ifeq ($(AUDIOOUT),DSSI) - $(CXX) -shared -o zynaddsubfx.so */*.o *.o $(LIBS) -else -ifeq ($(AUDIOOUT),VST) - $(CXX) -shared -o zynaddsubfx_vst.dll */*.o *.o ../../vstsdk2/source/common/AudioEffect.cpp ../../vstsdk2/source/common/audioeffectx.cpp $(LIBS) zynaddsubfx_gcc.def -else - - -ifeq ($(OS_PORT),LINUX) - $(CXX) -o zynaddsubfx */*.o *.o $(LIBS) -else - $(CXX) -o zynaddsubfx.exe */*.o *.o $(LIBS) -endif -endif -endif - -subdirs: $(SUBDIRS) - -$(SUBDIRS): - $(MAKE) -C $@ - - -objs:$(objects) - -debug: all - -main.o:Misc/Master.h Misc/Util.h Output/OSSaudiooutput.h\ - Input/OSSMidiIn.h Input/ALSAMidiIn.h - - -.PHONY : clean -clean: - rm -f $(objects) zynaddsubfx zynaddsubfx_vst.dll zynaddsubfx.exe zynaddsubfx.so - @for name in $(SUBDIRS); do sh -c "make -C $$name $@"; done - rm -f Make.deps - rm -f */*.o *.o - rm -f ./*/*~ ./*~ - $(MAKE) -C UI $@ - diff --git a/src/Makefile.inc b/src/Makefile.inc @@ -1,84 +0,0 @@ -CXX=g++ - -#You can set the on what OS is compiling (Linux/Windows) -OS_PORT=LINUX -#OS_PORT=WINDOWS - -#The version of the FFTW which is used (2 or 3) -#FFTW_VERSION=2 -FFTW_VERSION=3 - -#Assembler FLOAT to INT conversions -ASM_F2I=YES -#ASM_F2I=NO - -#Graphic user interface disable option (ZynAddSubFX will run only in text-mode) -#DISABLE_GUI=YES -DISABLE_GUI=NO - -# L I N U X C O N F I G U R A T I O N -#Next line sets the midi input. It can be "ALSA", "OSS" or "NONE". -LINUX_MIDIIN=ALSA -#LINUX_MIDIIN=OSS -#LINUX_MIDIIN=NONE - -#Next lines sets the audio output (OSS/JACK/PA) -#You may use only one at the time -#If you use "OSS_AND_JACK",,at runtime, zynaddsubfx will run by the default with jack support and -#it will try OSS if JACK fails. At runtime you can set the OSS by default by command-line -#parameters (run 'zynaddsubfx --help' for help) - -#LINUX_AUDIOOUT=OSS_AND_JACK -LINUX_AUDIOOUT=OSS -#LINUX_AUDIOOUT=NONE -#LINUX_AUDIOOUT=JACK -#LINUX_AUDIOOUT=JACK_RT JACK_RT support is broken -#for PortAudio (PA) -#LINUX_AUDIOOUT=PA - - -#Next line sets if the synth is compiled for DSSI plugin (as .so file) -#If this setting is "YES", MIDI in and AUDIOOUT are set automatically to DSSI -LINUX_DSSI=NO -#LINUX_DSSI=YES - -#Next line sets if the LASH session handler will be used -#LINUX_USE_LASH=YES -LINUX_USE_LASH=NO - -# W I N D O W S C O N F I G U R A T I O N - -#Next line sets the midi input -#WINDOWS_MIDIIN=NONE -WINDOWS_MIDIIN=WIN - -#Next line sets the audio output -#WINDOWS_AUDIOOUT=NONE -WINDOWS_AUDIOOUT=PA - -#Next line sets if the synth is compiled for VST (as .dll file) -#If this setting is "YES", MIDI in and AUDIOOUT are set automatically to VST -WINDOWS_VST=NO -#WINDOWS_VST=YES - -#configuration end - -ifeq ($(OS_PORT),LINUX) - MIDIIN=$(LINUX_MIDIIN) - AUDIOOUT=$(LINUX_AUDIOOUT) - WINDOWS_VST=NO - ifeq ($(LINUX_DSSI),YES) - DISABLE_GUI=YES - MIDIIN=DSSI - AUDIOOUT=DSSI - endif -else - MIDIIN=$(WINDOWS_MIDIIN) - AUDIOOUT=$(WINDOWS_AUDIOOUT) - LINUX_DSSI=NO - ifeq ($(WINDOWS_VST),YES) - MIDIIN=VST - AUDIOOUT=VST - endif -endif - diff --git a/src/Misc/Atomic.cpp b/src/Misc/Atomic.cpp @@ -0,0 +1,70 @@ +/* + ZynAddSubFX - a software synthesizer + + Atomic.cpp - Simple Atomic operation wrapper + 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> +Atomic<T>::Atomic(const T &val) + :value(val) +{ + pthread_mutex_init(&mutex, NULL); +} + +template<class T> +Atomic<T>::~Atomic() +{ + pthread_mutex_destroy(&mutex); +} + +template<class T> +void Atomic<T>::operator=(const T &nval) +{ + pthread_mutex_lock(&mutex); + value = nval; + pthread_mutex_unlock(&mutex); +} + +template<class T> +T Atomic<T>::operator()() const +{ + T tmp; + pthread_mutex_lock(&mutex); + tmp = value; + pthread_mutex_unlock(&mutex); + return tmp; +} + +template<class T> +T Atomic<T>::operator++() +{ + T tmp; + pthread_mutex_lock(&mutex); + tmp = ++value; + pthread_mutex_unlock(&mutex); + return tmp; +} + +template<class T> +T Atomic<T>::operator--(){ + T tmp; + pthread_mutex_lock(&mutex); + tmp = --value; + pthread_mutex_unlock(&mutex); + return tmp; +} diff --git a/src/Misc/Atomic.h b/src/Misc/Atomic.h @@ -0,0 +1,46 @@ +/* + ZynAddSubFX - a software synthesizer + + Atomic.h - Simple Atomic operation wrapper + 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 ATOM_H +#define ATOM_H + +#include <pthread.h> + +/**Very simple threaded value container*/ +template<class T> +class Atomic +{ + public: + /**Initializes Atom + * @param val the value of the atom*/ + Atomic(const T &val); + ~Atomic(); + + void operator=(const T &val); + T operator()() const; + T operator++(); + T operator--(); + private: + mutable pthread_mutex_t mutex; + T value; +}; +#include "Atomic.cpp" +#endif + diff --git a/src/Misc/Bank.cpp b/src/Misc/Bank.cpp @@ -340,10 +340,12 @@ int Bank::newbank(const char *newbankdirname) strncat(bankdir, "/", MAX_STRING_SIZE); ; strncat(bankdir, newbankdirname, MAX_STRING_SIZE); -#ifdef OS_WINDOWS +#if OS_WINDOWS result = mkdir(bankdir); -#else +#elif OS_LINUX || OS_CYGWIN result = mkdir(bankdir, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); +#else +#warning Undefined OS #endif if(result < 0) return -1; diff --git a/src/Misc/Bank.h b/src/Misc/Bank.h @@ -24,6 +24,7 @@ #define BANK_H #include "../globals.h" +#include "Util.h" #include "XMLwrapper.h" #include "Part.h" diff --git a/src/Misc/CMakeLists.txt b/src/Misc/CMakeLists.txt @@ -9,6 +9,8 @@ set(zynaddsubfx_misc_SRCS Part.cpp Util.cpp XMLwrapper.cpp + Recorder.cpp + WavFile.cpp ) if (LASH_FOUND) @@ -19,4 +21,4 @@ add_library(zynaddsubfx_misc STATIC ${zynaddsubfx_misc_SRCS} ) -target_link_libraries(zynaddsubfx_misc zynaddsubfx_output) +target_link_libraries(zynaddsubfx_misc zynaddsubfx_nio) diff --git a/src/Misc/Config.cpp b/src/Misc/Config.cpp @@ -24,7 +24,7 @@ #include <stdlib.h> #include <string.h> -#ifdef OS_WINDOWS +#if OS_WINDOWS #include <windows.h> #include <mmsystem.h> #endif @@ -32,6 +32,8 @@ #include "Config.h" #include "XMLwrapper.h" +using namespace std; + Config::Config() {} void Config::init() @@ -105,7 +107,7 @@ void Config::init() readConfig(filename); if(cfg.bankRootDirList[0] == NULL) { -#if defined(OS_LINUX) +#if OS_LINUX //banks cfg.bankRootDirList[0] = new char[MAX_STRING_SIZE]; sprintf(cfg.bankRootDirList[0], "~/banks"); @@ -144,7 +146,7 @@ void Config::init() } if(cfg.presetsDirList[0] == NULL) { -#if defined(OS_LINUX) +#if OS_LINUX || OS_CYGWIN //presets cfg.presetsDirList[0] = new char[MAX_STRING_SIZE]; sprintf(cfg.presetsDirList[0], "./"); @@ -161,7 +163,7 @@ void Config::init() cfg.presetsDirList[4] = new char[MAX_STRING_SIZE]; sprintf(cfg.presetsDirList[4], "/usr/local/share/zynaddsubfx/presets"); -#else +#elif OS_WINDOWS //presets cfg.presetsDirList[0] = new char[MAX_STRING_SIZE]; sprintf(cfg.presetsDirList[0], "./"); @@ -172,12 +174,16 @@ void Config::init() #else cfg.presetsDirList[1] = new char[MAX_STRING_SIZE]; sprintf(cfg.presetsDirList[1], "../presets"); -#endif +#endif //end vst cfg.presetsDirList[2] = new char[MAX_STRING_SIZE]; sprintf(cfg.presetsDirList[2], "presets"); -#endif +#else +#error Undefined OS +#endif //end OS } + cfg.LinuxALSAaudioDev = "default"; + cfg.nameTag = ""; } Config::~Config() @@ -394,8 +400,10 @@ void Config::getConfigFileName(char *name, int namesize) name[0] = 0; #ifdef OS_LINUX snprintf(name, namesize, "%s%s", getenv("HOME"), "/.zynaddsubfxXML.cfg"); -#else +#elif OS_WINDOWS || OS_CYGWIN snprintf(name, namesize, "%s", "zynaddsubfxXML.cfg"); +#else +#error Undefined OS #endif } diff --git a/src/Misc/Config.h b/src/Misc/Config.h @@ -23,6 +23,7 @@ #ifndef CONFIG_H #define CONFIG_H #include "../globals.h" +#include <string> #define MAX_STRING_SIZE 4000 #define MAX_BANK_ROOT_DIRS 100 @@ -48,6 +49,8 @@ class Config 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; diff --git a/src/Misc/Makefile b/src/Misc/Makefile @@ -1,18 +0,0 @@ -include ../Makefile.inc - -objects=Bank.o Master.o Microtonal.o Part.o Util.o Config.o Dump.o XMLwrapper.o - -ifeq ($(LINUX_USE_LASH),YES) -objects += LASHClient.o -endif - - -all: $(objects) - --include ../Make.deps - -.PHONY : clean -clean: - rm -f $(objects) - rm -f makeinclude.deps - diff --git a/src/Misc/Master.cpp b/src/Misc/Master.cpp @@ -21,46 +21,42 @@ */ +#warning TODO move Sequencer out of master + #include "Master.h" +#include "../Params/LFOParams.h" +#include "../Effects/EffectMgr.h" + #include <stdio.h> #include <sys/stat.h> #include <sys/types.h> +#include <iostream> #include <unistd.h> +using namespace std; + Master::Master() { swaplr = 0; pthread_mutex_init(&mutex, NULL); + pthread_mutex_init(&vumutex, NULL); fft = new FFTwrapper(OSCIL_SIZE); tmpmixl = new REALTYPE[SOUND_BUFFER_SIZE]; tmpmixr = new REALTYPE[SOUND_BUFFER_SIZE]; - audiooutl = new REALTYPE[SOUND_BUFFER_SIZE]; - audiooutr = new REALTYPE[SOUND_BUFFER_SIZE]; - ksoundbuffersample = -1; //this is only time when this is -1; this means that the GetAudioOutSamples was never called - ksoundbuffersamplelow = 0.0; - oldsamplel = 0.0; - oldsampler = 0.0; shutup = 0; for(int npart = 0; npart < NUM_MIDI_PARTS; npart++) { vuoutpeakpart[npart] = 1e-9; fakepeakpart[npart] = 0; } - for(int i = 0; i < SOUND_BUFFER_SIZE; i++) { - audiooutl[i] = 0.0; - audiooutr[i] = 0.0; - } - for(int npart = 0; npart < NUM_MIDI_PARTS; npart++) part[npart] = new Part(&microtonal, fft, &mutex); - - //Insertion Effects init for(int nefx = 0; nefx < NUM_INS_EFX; nefx++) insefx[nefx] = new EffectMgr(1, &mutex); @@ -109,24 +105,25 @@ void Master::defaults() ShutUp(); } -/* - * Note On Messages (velocity=0 for NoteOff) - */ -void Master::NoteOn(unsigned char chan, - unsigned char note, - unsigned char velocity) +bool Master::mutexLock(lockset request) { - dump.dumpnote(chan, note, velocity); - - noteon(chan, note, velocity); + 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; } + /* - * Internal Note On (velocity=0 for NoteOff) + * Note On Messages (velocity=0 for NoteOff) */ -void Master::noteon(unsigned char chan, - unsigned char note, - unsigned char velocity) +void Master::noteOn(char chan, char note, char velocity) { int npart; if(velocity != 0) { @@ -139,25 +136,14 @@ void Master::noteon(unsigned char chan, } } else - this->NoteOff(chan, note); - ; + this->noteOff(chan, note); HDDRecorder.triggernow(); } /* * Note Off Messages */ -void Master::NoteOff(unsigned char chan, unsigned char note) -{ - dump.dumpnote(chan, note, 0); - - noteoff(chan, note); -} - -/* - * Internal Note Off - */ -void Master::noteoff(unsigned char chan, unsigned char note) +void Master::noteOff(char chan, char note) { int npart; for(npart = 0; npart < NUM_MIDI_PARTS; npart++) @@ -169,17 +155,7 @@ void Master::noteoff(unsigned char chan, unsigned char note) /* * Controllers */ -void Master::SetController(unsigned char chan, unsigned int type, int par) -{ - dump.dumpcontroller(chan, type, par); - - setcontroller(chan, type, par); -} - -/* - * Internal Controllers - */ -void Master::setcontroller(unsigned char chan, unsigned int type, int par) +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) @@ -217,6 +193,52 @@ void Master::setcontroller(unsigned char chan, unsigned int type, int par) } } +void Master::vuUpdate(const REALTYPE *outl, const REALTYPE *outr) +{ + //Peak computation (for vumeters) + vu.outpeakl = 1e-12; + vu.outpeakr = 1e-12; + for(int i = 0; i < SOUND_BUFFER_SIZE; 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.0) || (vu.outpeakr > 1.0)) + 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 < SOUND_BUFFER_SIZE; i++) { + vu.rmspeakl += outl[i] * outl[i]; + vu.rmspeakr += outr[i] * outr[i]; + } + vu.rmspeakl = sqrt(vu.rmspeakl / SOUND_BUFFER_SIZE); + vu.rmspeakr = sqrt(vu.rmspeakr / SOUND_BUFFER_SIZE); + + //Part Peak computation (for Part vumeters or fake part vumeters) + for(int npart = 0; npart < NUM_MIDI_PARTS; npart++) { + vuoutpeakpart[npart] = 1.0e-12; + if(part[npart]->Penabled != 0) { + REALTYPE *outl = part[npart]->partoutl, + *outr = part[npart]->partoutr; + for(int i = 0; i < SOUND_BUFFER_SIZE; i++) { + REALTYPE 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 @@ -392,9 +414,11 @@ void Master::AudioOut(REALTYPE *outl, REALTYPE *outr) //Mix all parts for(npart = 0; npart < NUM_MIDI_PARTS; npart++) { - for(i = 0; i < SOUND_BUFFER_SIZE; i++) { //the volume did not changed - outl[i] += part[npart]->partoutl[i]; - outr[i] += part[npart]->partoutr[i]; + if(part[npart]->Penabled) { //only mix active parts + for(i = 0; i < SOUND_BUFFER_SIZE; i++) { //the volume did not changed + outl[i] += part[npart]->partoutl[i]; + outr[i] += part[npart]->partoutr[i]; + } } } @@ -410,49 +434,9 @@ void Master::AudioOut(REALTYPE *outl, REALTYPE *outr) outr[i] *= volume; } - //Peak computation (for vumeters) - vuoutpeakl = 1e-12; - vuoutpeakr = 1e-12; - for(i = 0; i < SOUND_BUFFER_SIZE; i++) { - if(fabs(outl[i]) > vuoutpeakl) - vuoutpeakl = fabs(outl[i]); - if(fabs(outr[i]) > vuoutpeakr) - vuoutpeakr = fabs(outr[i]); - } - if((vuoutpeakl > 1.0) || (vuoutpeakr > 1.0)) - vuclipped = 1; - if(vumaxoutpeakl < vuoutpeakl) - vumaxoutpeakl = vuoutpeakl; - if(vumaxoutpeakr < vuoutpeakr) - vumaxoutpeakr = vuoutpeakr; - - //RMS Peak computation (for vumeters) - vurmspeakl = 1e-12; - vurmspeakr = 1e-12; - for(i = 0; i < SOUND_BUFFER_SIZE; i++) { - vurmspeakl += outl[i] * outl[i]; - vurmspeakr += outr[i] * outr[i]; - } - vurmspeakl = sqrt(vurmspeakl / SOUND_BUFFER_SIZE); - vurmspeakr = sqrt(vurmspeakr / SOUND_BUFFER_SIZE); - - //Part Peak computation (for Part vumeters or fake part vumeters) - for(npart = 0; npart < NUM_MIDI_PARTS; npart++) { - vuoutpeakpart[npart] = 1.0e-12; - if(part[npart]->Penabled != 0) { - REALTYPE *outl = part[npart]->partoutl, - *outr = part[npart]->partoutr; - for(i = 0; i < SOUND_BUFFER_SIZE; i++) { - REALTYPE tmp = fabs(outl[i] + outr[i]); - if(tmp > vuoutpeakpart[npart]) - vuoutpeakpart[npart] = tmp; - } - vuoutpeakpart[npart] *= volume; - } - else - if(fakepeakpart[npart] > 1) - fakepeakpart[npart]--; - ; + if(!pthread_mutex_trylock(&vumutex)) { + vuUpdate(outl, outr); + pthread_mutex_unlock(&vumutex); } @@ -470,80 +454,9 @@ void Master::AudioOut(REALTYPE *outl, REALTYPE *outr) //update the LFO's time LFOParams::time++; - if(HDDRecorder.recording()) - HDDRecorder.recordbuffer(outl, outr); dump.inctick(); } -void Master::GetAudioOutSamples(int nsamples, - int samplerate, - REALTYPE *outl, - REALTYPE *outr) -{ - if(ksoundbuffersample == -1) { //first time - AudioOut(&audiooutl[0], &audiooutr[0]); - ksoundbuffersample = 0; - } - - - if(samplerate == SAMPLE_RATE) { //no resample - int ksample = 0; - while(ksample < nsamples) { - outl[ksample] = audiooutl[ksoundbuffersample]; - outr[ksample] = audiooutr[ksoundbuffersample]; - - ksample++; - ksoundbuffersample++; - if(ksoundbuffersample >= SOUND_BUFFER_SIZE) { - AudioOut(&audiooutl[0], &audiooutr[0]); - ksoundbuffersample = 0; - } - } - } - else { //Resample - int ksample = 0; - REALTYPE srinc = SAMPLE_RATE / (REALTYPE)samplerate; - - while(ksample < nsamples) { - if(ksoundbuffersample != 0) { - outl[ksample] = audiooutl[ksoundbuffersample] - * ksoundbuffersamplelow - + audiooutl[ksoundbuffersample - - 1] * (1.0 - ksoundbuffersamplelow); - outr[ksample] = audiooutr[ksoundbuffersample] - * ksoundbuffersamplelow - + audiooutr[ksoundbuffersample - - 1] * (1.0 - ksoundbuffersamplelow); - } - else { - outl[ksample] = audiooutl[ksoundbuffersample] - * ksoundbuffersamplelow - + oldsamplel * (1.0 - ksoundbuffersamplelow); - outr[ksample] = audiooutr[ksoundbuffersample] - * ksoundbuffersamplelow - + oldsampler * (1.0 - ksoundbuffersamplelow); - } - - ksample++; - - ksoundbuffersamplelow += srinc; - if(ksoundbuffersamplelow >= 1.0) { - ksoundbuffersample += (int) floor(ksoundbuffersamplelow); - ksoundbuffersamplelow = ksoundbuffersamplelow - floor( - ksoundbuffersamplelow); - } - - if(ksoundbuffersample >= SOUND_BUFFER_SIZE) { - oldsamplel = audiooutl[SOUND_BUFFER_SIZE - 1]; - oldsampler = audiooutr[SOUND_BUFFER_SIZE - 1]; - AudioOut(&audiooutl[0], &audiooutr[0]); - ksoundbuffersample = 0; - } - } - } -} - - Master::~Master() { for(int npart = 0; npart < NUM_MIDI_PARTS; npart++) @@ -553,13 +466,12 @@ Master::~Master() for(int nefx = 0; nefx < NUM_SYS_EFX; nefx++) delete sysefx[nefx]; - delete [] audiooutl; - delete [] audiooutr; delete [] tmpmixl; delete [] tmpmixr; delete (fft); pthread_mutex_destroy(&mutex); + pthread_mutex_destroy(&vumutex); } @@ -615,14 +527,29 @@ void Master::ShutUp() */ void Master::vuresetpeaks() { - vuoutpeakl = 1e-9; - vuoutpeakr = 1e-9; - vumaxoutpeakl = 1e-9; - vumaxoutpeakr = 1e-9; - vuclipped = 0; + 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.outpeakl=vu.outpeakl; + tmp.outpeakr=vu.outpeakr; + tmp.maxoutpeakl=vu.maxoutpeakl; + tmp.maxoutpeakr=vu.maxoutpeakr; + tmp.rmspeakl=vu.rmspeakl; + tmp.rmspeakr=vu.rmspeakr; + tmp.clipped=vu.clipped; + pthread_mutex_unlock(&vumutex); + return tmp; +} void Master::applyparameters() { diff --git a/src/Misc/Master.h b/src/Misc/Master.h @@ -25,17 +25,26 @@ #define MASTER_H #include "../globals.h" -#include "../Effects/EffectMgr.h" -#include "Part.h" -#include "../Output/Recorder.h" #include "Microtonal.h" #include "Bank.h" +#include "Recorder.h" +#include "Part.h" #include "Dump.h" #include "../Seq/Sequencer.h" #include "XMLwrapper.h" +typedef enum { MUTEX_TRYLOCK, MUTEX_LOCK, MUTEX_UNLOCK } lockset; + extern Dump dump; + +typedef struct vuData_t { + REALTYPE outpeakl, outpeakr, maxoutpeakl, maxoutpeakr, + rmspeakl, rmspeakr; + int clipped; +} vuData; + + /** It sends Midi Messages to Parts, receives samples from parts, * process them with system/insertion effects and mix them */ class Master @@ -69,20 +78,24 @@ class Master /**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(unsigned char chan, - unsigned char note, - unsigned char velocity); - void NoteOff(unsigned char chan, unsigned char note); - void SetController(unsigned char chan, unsigned int type, int par); + void noteOn(char chan, char note, char velocity); + void noteOff(char chan, char note); + void setController(char chan, int type, int par); //void NRPN... void ShutUp(); int shutup; + void vuUpdate(const REALTYPE *outl, const REALTYPE *outr); + /**Audio Output*/ void AudioOut(REALTYPE *outl, REALTYPE *outr); /**Audio Output (for callback mode). This allows the program to be controled by an external program*/ @@ -112,7 +125,7 @@ class Master //effects EffectMgr *sysefx[NUM_SYS_EFX]; //system EffectMgr *insefx[NUM_INS_EFX]; //insertion -// void swapcopyeffects(int what,int type,int neff1,int neff2); +// void swapcopyeffects(int what,int type,int neff1,int neff2); //HDD recorder Recorder HDDRecorder; @@ -120,13 +133,14 @@ class Master //part that's apply the insertion effect; -1 to disable short int Pinsparts[NUM_INS_EFX]; + //peaks for VU-meter void vuresetpeaks(); - REALTYPE vuoutpeakl, vuoutpeakr, vumaxoutpeakl, vumaxoutpeakr, - vurmspeakl, vurmspeakr; - int vuclipped; + //get VU-meter data + vuData getVuData(); //peaks for part VU-meters + /**\todo synchronize this with a mutex*/ REALTYPE vuoutpeakpart[NUM_MIDI_PARTS]; unsigned char fakepeakpart[NUM_MIDI_PARTS]; //this is used to compute the "peak" when the part is disabled @@ -142,8 +156,12 @@ class Master FFTwrapper *fft; pthread_mutex_t mutex; + pthread_mutex_t vumutex; + private: + bool nullRun; + vuData vu; REALTYPE volume; REALTYPE sysefxvol[NUM_SYS_EFX][NUM_MIDI_PARTS]; REALTYPE sysefxsend[NUM_SYS_EFX][NUM_SYS_EFX]; @@ -152,24 +170,7 @@ class Master REALTYPE *tmpmixl; REALTYPE *tmpmixr; - int keyshift; - - //Audio Output samples (if it used GetAudioOutSamples - eg. for Jack output; elsewhere is unused) - REALTYPE *audiooutl; - REALTYPE *audiooutr; - - int ksoundbuffersample; //this is used to know if there is need to call AudioOut by GetAudioOutSamples method - REALTYPE ksoundbuffersamplelow; //this is used for resampling (eg. if Jack samplerate!= SAMPLE_RATE) - REALTYPE oldsamplel, oldsampler; //this is used for resampling - - //These are called by the NoteOn, NoteOff,SetController (which are from external sources like MIDI, Virtual Keyboard) - //and are called by internal parts of the program (like sequencer) - void noteon(unsigned char chan, - unsigned char note, - unsigned char velocity); - void noteoff(unsigned char chan, unsigned char note); - void setcontroller(unsigned char chan, unsigned int type, int par); }; diff --git a/src/Misc/Part.cpp b/src/Misc/Part.cpp @@ -22,6 +22,13 @@ #include "Part.h" #include "Microtonal.h" +#include "../Effects/EffectMgr.h" +#include "../Params/ADnoteParameters.h" +#include "../Params/SUBnoteParameters.h" +#include "../Params/PADnoteParameters.h" +#include "../Synth/ADnote.h" +#include "../Synth/SUBnote.h" +#include "../Synth/PADnote.h" #include <stdlib.h> #include <stdio.h> #include <string.h> diff --git a/src/Misc/Part.h b/src/Misc/Part.h @@ -26,20 +26,21 @@ #define MAX_INFO_TEXT_SIZE 1000 #include "../globals.h" -#include "../Params/ADnoteParameters.h" -#include "../Params/SUBnoteParameters.h" -#include "../Params/PADnoteParameters.h" -#include "../Synth/ADnote.h" -#include "../Synth/SUBnote.h" -#include "../Synth/PADnote.h" #include "../Params/Controller.h" #include "../Misc/Microtonal.h" #include "../DSP/FFTwrapper.h" -#include "../Effects/EffectMgr.h" #include "XMLwrapper.h" #include <list> // For the monomemnotes list. +class EffectMgr; +class ADnoteParameters; +class SUBnoteParameters; +class PADnoteParameters; +class ADnote; +class SUBnote; +class PADnote; + /** Part implementation*/ class Part { diff --git a/src/Misc/Recorder.cpp b/src/Misc/Recorder.cpp @@ -0,0 +1,92 @@ +/* + 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; + } + + sysOut->wave->newFile(new WavFile(filename_, SAMPLE_RATE, 2)); + + status = 1; //ready + + return 0; +} + +void Recorder::start() +{ + notetrigger = 0; + status = 2; //recording +} + +void Recorder::stop() +{ + sysOut->wave->Stop(); + sysOut->wave->destroyFile(); + status = 0; +} + +void Recorder::pause() +{ + status = 0; + sysOut->wave->Stop(); +} + +int Recorder::recording() +{ + if((status == 2) && (notetrigger != 0)) + return 1; + else + return 0; +} + +void Recorder::triggernow() +{ + if(status == 2) { + if(notetrigger!=1) { + sysOut->wave->Start(); + } + notetrigger = 1; + } +} + diff --git a/src/Misc/Recorder.h b/src/Misc/Recorder.h @@ -0,0 +1,55 @@ +/* + 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 + diff --git a/src/Misc/Stereo.h b/src/Misc/Stereo.h @@ -30,6 +30,7 @@ class Stereo /**Initializes Stereo with left and right set to val * @param val the value for both channels*/ Stereo(const T &val); + Stereo() {}; ~Stereo() {} void operator=(const Stereo<T> &smp); diff --git a/src/Misc/Util.cpp b/src/Misc/Util.cpp @@ -30,6 +30,13 @@ #include <unistd.h> #include <errno.h> #include <string.h> +#include <sched.h> +#if OS_WINDOWS +//used for the sleep func +#include <winbase.h> +#include <windows.h> +#endif + int SAMPLE_RATE = 44100; int SOUND_BUFFER_SIZE = 256; @@ -113,3 +120,26 @@ bool fileexists(const char *filename) return false; } +void set_realtime() +{ +#if OS_LINUX || OS_CYGWIN + 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"); +#else +#warning set_realtime() undefined for your opperating system +#endif +} + +void os_sleep(long length) +{ +#if OS_LINUX || OS_CYGWIN + usleep(length); +#elif OS_WINDOWS + Sleep((long)length/1000); +#endif +} + diff --git a/src/Misc/Util.h b/src/Misc/Util.h @@ -38,6 +38,15 @@ extern REALTYPE 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); + extern REALTYPE *denormalkillbuf; /**<the buffer to add noise in order to avoid denormalisation*/ extern Config config; @@ -60,5 +69,11 @@ T stringTo(const char *x) return ans; } +template <class T> +T limit(T val, T min, T max) +{ + return (val < min ? min : (val > max ? max : val)); +} + #endif diff --git a/src/Misc/WavFile.cpp b/src/Misc/WavFile.cpp @@ -0,0 +1,94 @@ +/* + 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 "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) { + //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) { + 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 NULL != 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; + } +} + diff --git a/src/Misc/WavFile.h b/src/Misc/WavFile.h @@ -0,0 +1,45 @@ +/* + 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 + diff --git a/src/Nio/AlsaEngine.cpp b/src/Nio/AlsaEngine.cpp @@ -0,0 +1,351 @@ +/* + AlsaEngine.cpp + + Copyright 2009, Alan Calvert + + 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 "../Misc/Master.h" +#include "InMgr.h" +#include "AlsaEngine.h" + +AlsaEngine::AlsaEngine(OutMgr *out) + :AudioOut(out) +{ + name = "ALSA"; + audio.handle = NULL; + + midi.handle = NULL; + midi.alsaId = -1; + midi.pThread = 0; + +} + +AlsaEngine::~AlsaEngine() +{ + Stop(); +} + +void *AlsaEngine::_AudioThread(void *arg) +{ + return (static_cast<AlsaEngine*>(arg))->AudioThread(); +} + +void *AlsaEngine::AudioThread() +{ + set_realtime(); + processAudio(); + return NULL; +} + +bool AlsaEngine::Start() +{ + return openAudio() && openMidi(); +} + +void AlsaEngine::Stop() +{ + if(getMidiEn()) + setMidiEn(false); + if(getAudioEn()) + setAudioEn(false); +} + +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; + sysIn->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; + sysIn->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; + sysIn->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; + sysIn->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; + sysIn->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); +} + +const short *AlsaEngine::interleave(const Stereo<Sample> smps)const +{ + /**\todo TODO fix repeated allocation*/ + short *shortInterleaved = new short[smps.l().size()*2]; + memset(shortInterleaved,0,smps.l().size()*2*sizeof(short)); + int idx = 0;//possible off by one error here + double scaled; + for (int frame = 0; frame < smps.l().size(); ++frame) + { // with a nod to libsamplerate ... + scaled = smps.l()[frame] * (8.0 * 0x10000000); + shortInterleaved[idx++] = (short int)(lrint(scaled) >> 16); + scaled = smps.r()[frame] * (8.0 * 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 = SAMPLE_RATE; + 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, SOUND_BUFFER_SIZE); + + //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); + snd_pcm_close(handle); +} + +void AlsaEngine::processAudio() +{ + int rc; + while (audio.handle) { + audio.buffer = interleave(getNext()); + snd_pcm_t *handle = audio.handle; + if(handle) + rc = snd_pcm_writei(handle, audio.buffer, SOUND_BUFFER_SIZE); + delete[] audio.buffer; + 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; + } + pthread_exit(NULL); +} diff --git a/src/Nio/AlsaEngine.h b/src/Nio/AlsaEngine.h @@ -0,0 +1,82 @@ +/* + AlsaEngine.h + + Copyright 2009, Alan Calvert + + 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" +#include "../Samples/Sample.h" + +class AlsaEngine : public AudioOut, MidiIn +{ + public: + AlsaEngine(OutMgr *out); + ~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(); + + const short *interleave(const Stereo<Sample> smps) const; + + 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; + const short *buffer; + pthread_t pThread; + } audio; + + void processAudio(); +}; + +#endif diff --git a/src/Nio/AudioOut.cpp b/src/Nio/AudioOut.cpp @@ -0,0 +1,92 @@ +/* + 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" + +struct AudioOut::Data +{ + Data(OutMgr *out); + + int samplerate; + int bufferSize; + + Stereo<Sample> current;/**<used for xrun defence*/ + + OutMgr *manager; +}; + +AudioOut::Data::Data(OutMgr *out) + :samplerate(SAMPLE_RATE),bufferSize(SOUND_BUFFER_SIZE), + current(Sample(SOUND_BUFFER_SIZE,0.0)),manager(out) +{} + +AudioOut::AudioOut(OutMgr *out) + :dat(new Data(out)) +{} + +AudioOut::~AudioOut() +{ + delete dat; +} + +void AudioOut::setSamplerate(int _samplerate) +{ + dat->samplerate = _samplerate; +} + +int AudioOut::getSampleRate() +{ + return dat->samplerate; +} + +void AudioOut::setBufferSize(int _bufferSize) +{ + dat->bufferSize = _bufferSize; +} + +//delete me +void AudioOut::bufferingSize(int nBuffering) +{ + //dat->buffering = nBuffering; +} + +//delete me +int AudioOut::bufferingSize() +{ + //return dat->buffering; +} + +const Stereo<Sample> AudioOut::getNext(bool wait) +{ + Stereo<REALTYPE *> tmp = sysOut->tick(dat->bufferSize); + + //stop the samples + return Stereo<Sample>(Sample(dat->bufferSize, tmp.l()), Sample(dat->bufferSize, tmp.r())); +} diff --git a/src/Nio/AudioOut.h b/src/Nio/AudioOut.h @@ -0,0 +1,63 @@ +/* + 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 "../Samples/Sample.h" +#include "Engine.h" + +class AudioOut : public virtual Engine +{ + public: + AudioOut(class OutMgr *out); + 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<Sample> getNext(bool wait = false); + + //using opaque pointer to reduce compile times + struct Data; + Data *dat; +}; + +#endif + diff --git a/src/Nio/CMakeLists.txt b/src/Nio/CMakeLists.txt @@ -0,0 +1,55 @@ +#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 + ) + +set(zynaddsubfx_nio_lib ) + +if (DefaultOutput STREQUAL alsa) + add_definitions(-DALSA_DEFAULT=1) +elseif(DefaultOutput STREQUAL oss) + add_definitions(-DOSS_DEFAULT=1) +elseif(DefaultOutput STREQUAL jack) + add_definitions(-DJACK_DEFAULT=1) +elseif(DefaultOutput STREQUAL portaudio) + add_definitions(-DPORTAUDIO_DEFAULT=1) +endif() + +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} + ) + +target_link_libraries(zynaddsubfx_nio zynaddsubfx_misc) #for WavFile diff --git a/src/Nio/Engine.cpp b/src/Nio/Engine.cpp @@ -0,0 +1,29 @@ +/* + 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() +{}; + diff --git a/src/Nio/Engine.h b/src/Nio/Engine.h @@ -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 diff --git a/src/Nio/EngineMgr.cpp b/src/Nio/EngineMgr.cpp @@ -0,0 +1,150 @@ +#include "EngineMgr.h" +#include <algorithm> +#include <iostream> +#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 *sysEngine; + +EngineMgr::EngineMgr() +{ + Engine *defaultEng = NULL; + + //conditional compiling mess (but contained) + engines.push_back(defaultEng = new NulEngine(sysOut)); +#if OSS +#if OSS_DEFAULT + engines.push_back(defaultEng = new OssEngine(sysOut)); +#else + engines.push_back(new OssEngine(sysOut)); +#endif +#endif +#if ALSA +#if ALSA_DEFAULT + engines.push_back(defaultEng = new AlsaEngine(sysOut)); +#else + engines.push_back(new AlsaEngine(sysOut)); +#endif +#endif +#if JACK +#if JACK_DEFAULT + engines.push_back(defaultEng = new JackEngine(sysOut)); +#else + engines.push_back(new JackEngine(sysOut)); +#endif +#endif +#if PORTAUDIO +#if PORTAUDIO_DEFAULT + engines.push_back(defaultEng = new PaEngine(sysOut)); +#else + engines.push_back(new PaEngine(sysOut)); +#endif +#endif + + defaultOut = dynamic_cast<AudioOut *>(defaultEng); + + defaultIn = dynamic_cast<MidiIn *>(defaultEng); +}; + +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; +} + +void EngineMgr::start() +{ + 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")); + } + + sysOut->currentOut = defaultOut; + sysIn->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 { + cerr << "ERROR: The default audio output failed to open!" << endl; + sysOut->currentOut = dynamic_cast<AudioOut *>(sysEngine->getEng("NULL")); + sysOut->currentOut->setAudioEn(true); + } + + cout << "Starting MIDI: " << defaultIn->name << endl; + defaultIn->setMidiEn(true); + if(defaultIn->getMidiEn()) { + cout << "MIDI Started" << endl; + } + else { //recover + cerr << "ERROR: The default MIDI input failed to open!" << endl; + sysIn->current = dynamic_cast<MidiIn *>(sysEngine->getEng("NULL")); + sysIn->current->setMidiEn(true); + } +} + +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; + } + return false; +} + +bool EngineMgr::setOutDefault(string name) +{ + AudioOut *chosen; + if((chosen = dynamic_cast<AudioOut *>(getEng(name)))){ //got the output + defaultOut = chosen; + return true; + } + return false; +} + + diff --git a/src/Nio/EngineMgr.h b/src/Nio/EngineMgr.h @@ -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*/ +struct EngineMgr +{ + EngineMgr(); + ~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*/ + void 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; +}; + +extern EngineMgr *sysEngine; +#endif + diff --git a/src/Nio/InMgr.cpp b/src/Nio/InMgr.cpp @@ -0,0 +1,101 @@ +#include "InMgr.h" +#include "MidiIn.h" +#include "EngineMgr.h" +#include "../Misc/Master.h" +#include <iostream> + +using namespace std; + +InMgr *sysIn; + +ostream &operator<<(ostream &out, const MidiEvent& ev) +{ + if(ev.type == M_NOTE) + out << "MidiNote: note(" << ev.num << ")\n" + << " channel(" << ev.channel << ")\n" + << " velocity(" << ev.value << ")"; + else + out << "MidiCtl: controller(" << ev.num << ")\n" + << " channel(" << ev.channel << ")\n" + << " value(" << ev.value << ")"; + return out; +} + +MidiEvent::MidiEvent() + :channel(0),type(0),num(0),value(0) +{} + +InMgr::InMgr(Master *_master) + :queue(100), master(_master) +{ + current = NULL; + sem_init(&work, PTHREAD_PROCESS_PRIVATE, 0); +} + +InMgr::~InMgr() +{ + //lets stop the consumer thread + sem_destroy(&work); +} + +void InMgr::putEvent(MidiEvent ev) +{ + if(queue.push(ev)) //check for error + cerr << "ERROR: Midi Ringbuffer is FULL" << endl; + else + sem_post(&work); +} + +void InMgr::flush() +{ + MidiEvent ev; + while(!sem_trywait(&work)) { + queue.pop(ev); + cout << ev << endl; + + if(M_NOTE == ev.type) { + 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); + } + else { + dump.dumpcontroller(ev.channel, ev.num, ev.value); + master->setController(ev.channel, ev.num, ev.value); + } + } +} + +bool InMgr::setSource(string name) +{ + MidiIn *src = NULL; + for(list<Engine*>::iterator itr = sysEngine->engines.begin(); + itr != sysEngine->engines.end(); ++itr) { + MidiIn *in = dynamic_cast<MidiIn *>(*itr); + if(in && in->name == name) { + src = in; + break; + } + } + + if(!src) + return false; + + if(current) + current->setMidiEn(false); + current = src; + current->setMidiEn(true); + + return current->getMidiEn(); +} + +string InMgr::getSource() const +{ + if(current) + return current->name; + else + return "ERROR"; +} + diff --git a/src/Nio/InMgr.h b/src/Nio/InMgr.h @@ -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 +}; //type=1 for note, type=2 for controller + +struct MidiEvent +{ + MidiEvent(); + int channel; //the midi channel for the event + int type; //type=1 for note, type=2 for controller + int num; //note or contoller number + int value; //velocity or controller value +}; + +class Master; +class MidiIn; +//super simple class to manage the inputs +class InMgr +{ + public: + InMgr(Master *nmaster); + ~InMgr(); + + void putEvent(MidiEvent ev); + + /**Flush the Midi Queue*/ + void flush(); + + bool setSource(std::string name); + + std::string getSource() const; + + friend class EngineMgr; + private: + SafeQueue<MidiEvent> queue; + sem_t work; + MidiIn *current; + + /**the link to the rest of zyn*/ + Master *master; +}; + +extern InMgr *sysIn; +#endif + diff --git a/src/Nio/JackEngine.cpp b/src/Nio/JackEngine.cpp @@ -0,0 +1,349 @@ +/* + 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 "InMgr.h" + +#include "../Misc/Master.h" +#include "JackEngine.h" + +using namespace std; + +JackEngine::JackEngine(OutMgr *out) + :AudioOut(out), 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; + } +} + +bool JackEngine::connectServer(string server) +{ + bool autostart_jack = true; + if(jackClient) + return true; + + + string clientname = "zynaddsubfx"; + jack_status_t jackstatus; + bool use_server_name = server.size() && server.compare("default") != 0; + jack_options_t jopts = (jack_options_t) + (((use_server_name) ? JackServerName : JackNullOption) + | ((autostart_jack) ? JackNullOption : JackNoStartServer)); + 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; + + return true; +} + +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) { + 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); + 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 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); + 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<Sample> smp = getNext(); + + //Assumes smp.l().size() == nframes + memcpy(audio.portBuffs[0], smp.l().c_buf(), smp.l().size()*sizeof(REALTYPE)); + memcpy(audio.portBuffs[1], smp.r().c_buf(), smp.r().size()*sizeof(REALTYPE)); + handleMidi(nframes); + return true; + +} + +int JackEngine::_xrunCallback(void * /*/arg*/) +{ + 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; + sysIn->putEvent(ev); + break; + + case 0x90: /* note-on */ + ev.type = M_NOTE; + ev.num = midi_data[1]; + ev.value = midi_data[2]; + sysIn->putEvent(ev); + break; + + case 0xB0: /* controller */ + ev.type = M_CONTROLLER; + ev.num = midi_data[1]; + ev.value = midi_data[2]; + sysIn->putEvent(ev); + break; + + case 0xE0: /* pitch bend */ + ev.type = M_CONTROLLER; + ev.num = C_pitchwheel; + ev.value = ((midi_data[2] << 7) | midi_data[1]); + sysIn->putEvent(ev); + break; + + /* XXX TODO: handle MSB/LSB controllers and RPNs and NRPNs */ + } + } +} + diff --git a/src/Nio/JackEngine.h b/src/Nio/JackEngine.h @@ -0,0 +1,89 @@ +/* + 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(OutMgr *out); + ~JackEngine() { }; + + bool Start(); + void Stop(); + + void setMidiEn(bool nval); + bool getMidiEn() const; + + void setAudioEn(bool nval); + bool getAudioEn() const; + + unsigned int getSamplerate() { return audio.jackSamplerate; }; + unsigned 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 diff --git a/src/Nio/MidiIn.cpp b/src/Nio/MidiIn.cpp @@ -0,0 +1,62 @@ +/* + 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; + sysIn->putEvent(ev); + break; + case 0x90: //Note On + ev.type = M_NOTE; + ev.channel = chan; + ev.num = num; + ev.value = value; + sysIn->putEvent(ev); + break; + case 0xb0: //Controller + ev.type = M_CONTROLLER; + ev.channel = chan; + ev.num = num; + ev.value = value; + sysIn->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; + sysIn->putEvent(ev); + break; + } +} diff --git a/src/Nio/MidiIn.h b/src/Nio/MidiIn.h @@ -0,0 +1,41 @@ +/* + 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*/ +struct MidiIn : public virtual Engine +{ + /**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 + diff --git a/src/Nio/NulEngine.cpp b/src/Nio/NulEngine.cpp @@ -0,0 +1,115 @@ +/* + 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 <iostream> + +using namespace std; + +NulEngine::NulEngine(OutMgr *out) + :AudioOut(out), 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) + { + const Stereo<Sample> smps = 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 += SOUND_BUFFER_SIZE * 1000000 / SAMPLE_RATE; + if(remaining < 0) + playing_until.tv_usec -= remaining; + playing_until.tv_sec += playing_until.tv_usec / 1000000; + playing_until.tv_usec %= 1000000; + } + pthread_exit(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; +} + diff --git a/src/Nio/NulEngine.h b/src/Nio/NulEngine.h @@ -0,0 +1,57 @@ +/* + 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(OutMgr *out); + ~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 + diff --git a/src/Nio/OssEngine.cpp b/src/Nio/OssEngine.cpp @@ -0,0 +1,264 @@ +/* + 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(OutMgr *out) + :AudioOut(out), engThread(NULL) +{ + name = "OSS"; + + midi.handle = -1; + audio.handle = -1; + + audio.smps = new short[SOUND_BUFFER_SIZE * 2]; + memset(audio.smps, 0, sizeof(short) * SOUND_BUFFER_SIZE * 2); +} + +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 = SAMPLE_RATE;; + + 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<Sample> smps = getNext(); + + REALTYPE l, r; + for(int i = 0; i < SOUND_BUFFER_SIZE; i++) { + l = smps.l()[i]; + r = smps.r()[i]; + + if(l < -1.0) + l = -1.0; + else + if(l > 1.0) + l = 1.0; + if(r < -1.0) + r = -1.0; + else + if(r > 1.0) + r = 1.0; + + audio.smps[i * 2] = (short int) (l * 32767.0); + audio.smps[i * 2 + 1] = (short int) (r * 32767.0); + } + int handle = audio.handle; + if(handle != -1) + write(handle, audio.smps, SOUND_BUFFER_SIZE * 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) { + getMidi(tmp); + unsigned char type = tmp[0]; + unsigned char header = tmp[1]; + if(header!=0xfe&&type==SEQ_MIDIPUTC&&header>=0x80) + { + getMidi(tmp); + unsigned char num = tmp[1]; + getMidi(tmp); + unsigned char value = tmp[1]; + + midiProcess(header, num, value); + } + } + } + pthread_exit(NULL); + return NULL; +} + +void OssEngine::getMidi(unsigned char *midiPtr) +{ + read(midi.handle, midiPtr, 4); +} + diff --git a/src/Nio/OssEngine.h b/src/Nio/OssEngine.h @@ -0,0 +1,77 @@ +/* + 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(OutMgr *out); + ~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 + diff --git a/src/Nio/OutMgr.cpp b/src/Nio/OutMgr.cpp @@ -0,0 +1,135 @@ +#include "OutMgr.h" +#include <algorithm> +#include <iostream> +#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 *sysOut; + +OutMgr::OutMgr(Master *nmaster) + :wave(new WavEngine(this)), + priBuf(new REALTYPE[4096],new REALTYPE[4096]),priBuffCurrent(priBuf) +{ + currentOut = NULL; + stales = 0; + master = nmaster; + + //init samples + outr = new REALTYPE[SOUND_BUFFER_SIZE]; + outl = new REALTYPE[SOUND_BUFFER_SIZE]; +}; + +OutMgr::~OutMgr() +{ + 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<REALTYPE *> OutMgr::tick(unsigned int frameSize) +{ + pthread_mutex_lock(&(master->mutex)); + sysIn->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); + } + Stereo<REALTYPE *> ans = priBuffCurrent; + ans.l() -= frameSize; + ans.r() -= frameSize; + //cout << storedSmps() << '=' << frameSize << endl; + return priBuf; +} + +AudioOut *OutMgr::getOut(string name) +{ + return dynamic_cast<AudioOut *>(sysEngine->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); + return currentOut->getAudioEn(); +} + +string OutMgr::getSink() const +{ + if(currentOut) + return currentOut->name; + else { + cerr << "BUG: No current output in OutMgr " << __LINE__ << endl; + return "ERROR"; + } + return "ERROR"; +} + +void OutMgr::addSmps(REALTYPE *l, REALTYPE *r) +{ + //allow wave file to syphon off stream + wave->push(Stereo<REALTYPE *>(l,r),SOUND_BUFFER_SIZE); + + Stereo<Sample> smps(Sample(SOUND_BUFFER_SIZE, l), Sample(SOUND_BUFFER_SIZE, r)); + + if(currentOut->getSampleRate() != SAMPLE_RATE) { //we need to resample + //cout << "BAD RESAMPLING" << endl; + smps.l().resample(SAMPLE_RATE,currentOut->getSampleRate()); + smps.r().resample(SAMPLE_RATE,currentOut->getSampleRate()); + } + + memcpy(priBuffCurrent.l(), smps.l().c_buf(), SOUND_BUFFER_SIZE*sizeof(REALTYPE)); + memcpy(priBuffCurrent.r(), smps.r().c_buf(), SOUND_BUFFER_SIZE*sizeof(REALTYPE)); + priBuffCurrent.l() += SOUND_BUFFER_SIZE; + priBuffCurrent.r() += SOUND_BUFFER_SIZE; + stales += SOUND_BUFFER_SIZE; +} + +void OutMgr::removeStaleSmps() +{ + int toShift = storedSmps() - stales; + //cout << "toShift: " << toShift << endl << "stales: " << stales << endl << priBuf.l() << ' ' << priBuffCurrent.l() << endl; + if(!stales) + return; + + memset(priBuf.l(), '0', 4096*sizeof(REALTYPE)); + memset(priBuf.r(), '0', 4096*sizeof(REALTYPE)); + priBuffCurrent = priBuf; + stales = 0; + +} + diff --git a/src/Nio/OutMgr.h b/src/Nio/OutMgr.h @@ -0,0 +1,70 @@ +#ifndef OUTMGR_H +#define OUTMGR_H + +#include "../globals.h" +#include "../Misc/Stereo.h" +#include "../Samples/Sample.h" //deprecated +#include <list> +#include <string> +#include <semaphore.h> + + +class AudioOut; +class WavEngine; +class Master; +class OutMgr +{ + public: + OutMgr(Master *nmaster); + ~OutMgr(); + + /**Execute a tick*/ + const Stereo<REALTYPE *> 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; + + WavEngine *wave; /**<The Wave Recorder*/ + friend class EngineMgr; + private: + void addSmps(REALTYPE *l, REALTYPE *r); + int storedSmps() const {return priBuffCurrent.l() - priBuf.l();}; + void removeStaleSmps(); + + AudioOut *currentOut;/**<The current output driver*/ + + sem_t requested; + + /**Buffer*/ + Stereo<REALTYPE *> priBuf; //buffer for primary drivers + Stereo<REALTYPE *> priBuffCurrent; //current array accessor + Stereo<Sample> smps; /**Deprecated TODO Remove*/ + + REALTYPE *outl; + REALTYPE *outr; + Master *master; + + int stales; +}; + +extern OutMgr *sysOut; +#endif + diff --git a/src/Nio/PaEngine.cpp b/src/Nio/PaEngine.cpp @@ -0,0 +1,111 @@ +/* + 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 "../Samples/Sample.h" +#include <iostream> + +using namespace std; + +PaEngine::PaEngine(OutMgr *out) + :AudioOut(out) +{ + name = "PA"; +} + + +PaEngine::~PaEngine() +{ + Stop(); +} + +bool PaEngine::Start() +{ + if(enabled()) + return true; + enabled = 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, + SAMPLE_RATE, + SOUND_BUFFER_SIZE, + NULL, + PAprocess, + (void *) this); + Pa_StartStream(stream); + return true; +} + +int PaEngine::PAprocess(const void *inputBuffer, void *outputBuffer, + unsigned long framesPerBuffer, + const PaStreamCallbackTimeInfo *outTime, PaStreamCallbackFlags flags, + void *userData) +{ + return static_cast<PaEngine *>(userData)->process((float *) outputBuffer, + framesPerBuffer); +} + +int PaEngine::process(float *out, unsigned long framesPerBuffer) +{ + + const Stereo<Sample> smp = getNext(); + + if(framesPerBuffer != smp.l().size()) + cerr << "Bug: PaEngine::process SOUND_BUFFER_SIZE!=framesPerBuffer" + << framesPerBuffer << ' ' << smp.l().size() << endl; + + for(int i = 0; i < framesPerBuffer; i++) { + if(i >= smp.l().size()) + break;//this should never happens, except only when framesPerBuffer!>SOUND_BUFFER_SIZE + *out++ = smp.l()[i]; + *out++ = smp.r()[i]; + } + + return 0; +} + +void PaEngine::Stop() +{ + if(!enabled()) + return; + enabled = false; + Pa_StopStream(stream); + Pa_CloseStream(stream); + Pa_Terminate(); +} + diff --git a/src/Nio/PaEngine.h b/src/Nio/PaEngine.h @@ -0,0 +1,54 @@ +/* + 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(OutMgr *out); + ~PaEngine(); + + bool Start(); + void Stop(); + + 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 PAaudiooutputinit(Master *master_); +void PAfinish(); + +#endif + diff --git a/src/Nio/SafeQueue.cpp b/src/Nio/SafeQueue.cpp @@ -0,0 +1,83 @@ + +template<class T> +SafeQueue<T>::SafeQueue(size_t maxlen) + :writePtr(0),readPtr(0),bufSize(maxlen) +{ + buffer = new T[maxlen]; +} + +template<class T> +SafeQueue<T>::~SafeQueue() +{ + delete[] buffer; +} + +template<class T> +unsigned int SafeQueue<T>::size() const +{ + return rSpace(); +} + +template<class T> +unsigned int SafeQueue<T>::rSpace() const +{ + size_t w, r; + + w = writePtr; + r = readPtr; + + if (w > r) { + return w - r; + } + else { + return (w - r + bufSize) % bufSize; + } +} + +template<class T> +unsigned int SafeQueue<T>::wSpace() const +{ + size_t w, r; + + w = writePtr; + r = readPtr - 1; + + if (r > w) { + return r - w; + } + else { + return (r - w + bufSize) % bufSize; + } +} + +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; + 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; + return 0; +} + +template<class T> +void SafeQueue<T>::clear() +{ + readPtr = writePtr; +} diff --git a/src/Nio/SafeQueue.h b/src/Nio/SafeQueue.h @@ -0,0 +1,40 @@ + +#ifndef SAFEQUEUE_H +#define SAFEQUEUE_H +#include <cstdlib> + +/** + * 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; + + //next writting spot + volatile size_t writePtr; + //next reading spot + volatile size_t readPtr; + const size_t bufSize; + T *buffer; +}; + +#include "SafeQueue.cpp" +#endif diff --git a/src/Nio/WavEngine.cpp b/src/Nio/WavEngine.cpp @@ -0,0 +1,120 @@ +/* + 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(OutMgr *out) + :AudioOut(out), file(NULL), buffer(SAMPLE_RATE*2), 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<REALTYPE *> 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; +} + +void WavEngine::destroyFile() +{ + if(file) + delete file; +} + +void *WavEngine::_AudioThread(void *arg) +{ + return (static_cast<WavEngine*>(arg))->AudioThread(); +} + +void *WavEngine::AudioThread() +{ + short int recordbuf_16bit[2]; + + while(!sem_wait(&work) && pThread) + { + float left=0.0f, right=0.0f; + buffer.pop(left); + buffer.pop(right); + recordbuf_16bit[0] = limit((int)(left * 32767.0), -32768, 32767); + recordbuf_16bit[1] = limit((int)(right * 32767.0), -32768, 32767); + file->writeStereoSamples(1, recordbuf_16bit); + } + pthread_exit(NULL); +} + diff --git a/src/Nio/WavEngine.h b/src/Nio/WavEngine.h @@ -0,0 +1,62 @@ +/* + 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(OutMgr *out); + ~WavEngine(); + + bool openAudio(); + bool Start(); + void Stop(); + + void setAudioEn(bool /*nval*/){}; + bool getAudioEn() const{return true;}; + + void push(Stereo<REALTYPE *> 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 + diff --git a/src/Output/CMakeLists.txt b/src/Output/CMakeLists.txt @@ -1,33 +0,0 @@ -set(zynaddsubfx_output_SRCS - Recorder.cpp - WAVaudiooutput.cpp -) - -if(AlsaMidiOutput) - set(zynaddsubfx_output_SRCS - ${zynaddsubfx_output_SRCS} - OSSaudiooutput.cpp) - set(zynaddsubfx_output_lib ${ASOUND_LIBRARY}) -endif(AlsaMidiOutput) - -if(JackOutput) - include_directories(${JACK_INCLUDE_DIR}) - set(zynaddsubfx_output_SRCS - ${zynaddsubfx_output_SRCS} - JACKaudiooutput.cpp) - set(zynaddsubfx_output_lib ${JACK_LIBRARIES}) -endif(JackOutput) - -if(PortAudioOutput) - include_directories(${PORTAUDIO_INCLUDE_DIR}) - set(zynaddsubfx_output_SRCS - ${zynaddsubfx_output_SRCS} - PAaudiooutput.cpp) - set(zynaddsubfx_output_lib ${PORTAUDIO_LIBRARIES}) -endif(PortAudioOutput) - -add_library(zynaddsubfx_output STATIC - ${zynaddsubfx_output_SRCS} - ) - -target_link_libraries(zynaddsubfx_output ${zynaddsubfx_output_lib}) diff --git a/src/Output/JACK_RTaudiooutput.cpp b/src/Output/JACK_RTaudiooutput.cpp @@ -1,229 +0,0 @@ -/* - ZynAddSubFX - a software synthesizer - - JACKaudiooutput.cpp - Audio output for JACK - 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 <stdlib.h> -#include <string.h> -#include <pthread.h> -#include <unistd.h> - - -extern "C" -{ -#include <jack/ringbuffer.h> -}; -#include "JACKaudiooutput.h" - -Master *jackmaster; -jack_client_t *jackclient; -jack_port_t *outport_left, *outport_right; -jack_ringbuffer_t *rb = NULL; - -REALTYPE *jackoutl, *jackoutr; -int jackfinish = 0; - -void *thread_blocked(void *arg); -int jackprocess(jack_nframes_t nframes, void *arg); -int jacksrate(jack_nframes_t nframes, void *arg); -void jackshutdown(void *arg); - -pthread_cond_t more_data = PTHREAD_COND_INITIALIZER; -pthread_mutex_t zyn_thread_lock = PTHREAD_MUTEX_INITIALIZER; - -pthread_t bthr; - - -bool JACKaudiooutputinit(Master *master_) -{ - jackmaster = master_; - jackclient = 0; - char tmpstr[100]; - - jackoutl = new REALTYPE [SOUND_BUFFER_SIZE]; - jackoutr = new REALTYPE [SOUND_BUFFER_SIZE]; - - int rbbufsize = SOUND_BUFFER_SIZE * sizeof(REALTYPE) * 2 * 2; - printf("%d\n", rbbufsize); - rb = jack_ringbuffer_create(rbbufsize); - for(int i = 0; i < rbbufsize; i++) - rb->buf[i] = 0.0; - - - for(int i = 0; i < 15; i++) { - if(i != 0) - snprintf(tmpstr, 100, "ZynAddSubFX_%d", i); - else - snprintf(tmpstr, 100, "ZynAddSubFX"); - jackclient = jack_client_new(tmpstr); - if(jackclient != 0) - break; - } - - if(jackclient == 0) { - fprintf( - stderr, - "\nERROR: Cannot make a jack client (possible reasons: JACK server is not running or jackd is launched by root and zynaddsubfx by another user.).\n\n\n"); - return false; - } - - fprintf(stderr, - "Internal SampleRate = %d\nJack Output SampleRate= %d\n", - SAMPLE_RATE, - jack_get_sample_rate(jackclient)); - if((unsigned int)jack_get_sample_rate(jackclient) != - (unsigned int) SAMPLE_RATE) - fprintf(stderr, - "It is recomanded that the both samplerates to be equal.\n"); - - jack_set_process_callback(jackclient, jackprocess, 0); - jack_set_sample_rate_callback(jackclient, jacksrate, 0); - jack_on_shutdown(jackclient, jackshutdown, 0); - - outport_left = jack_port_register(jackclient, - "out_1", - JACK_DEFAULT_AUDIO_TYPE, - JackPortIsOutput | JackPortIsTerminal, - 0); - outport_right = jack_port_register(jackclient, - "out_2", - JACK_DEFAULT_AUDIO_TYPE, - JackPortIsOutput | JackPortIsTerminal, - 0); - - if(jack_activate(jackclient)) { - fprintf(stderr, "Cannot activate jack client\n"); - return false; - } - - pthread_create(&bthr, NULL, thread_blocked, NULL); - - /* - jack_connect(jackclient,jack_port_name(outport_left),"alsa_pcm:out_1"); - jack_connect(jackclient,jack_port_name(outport_right),"alsa_pcm:out_2"); - */ - - return true; -} - -void *thread_blocked(void *arg) -{ - int datasize = SOUND_BUFFER_SIZE * sizeof(REALTYPE); - - //try to get realtime - sched_param sc; - sc.sched_priority = 50; - int err = sched_setscheduler(0, SCHED_FIFO, &sc); - - pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL); - pthread_mutex_lock(&zyn_thread_lock); - - while(jackfinish == 0) { - while(jack_ringbuffer_write_space(rb) >= datasize) { - pthread_mutex_lock(&jackmaster->mutex); - jackmaster->GetAudioOutSamples(SOUND_BUFFER_SIZE, - jack_get_sample_rate(jackclient), - jackoutl, - jackoutr); - pthread_mutex_unlock(&jackmaster->mutex); - - jack_ringbuffer_write(rb, (char *) jackoutl, datasize); - jack_ringbuffer_write(rb, (char *) jackoutr, datasize); - } - pthread_cond_wait(&more_data, &zyn_thread_lock); - } - pthread_mutex_unlock(&zyn_thread_lock); - - return 0; -} - - -int jackprocess(jack_nframes_t nframes, void *arg) -{ - jack_default_audio_sample_t *outl = - (jack_default_audio_sample_t *) jack_port_get_buffer(outport_left, - nframes); - jack_default_audio_sample_t *outr = - (jack_default_audio_sample_t *) jack_port_get_buffer(outport_right, - nframes); - - int datasize = nframes * sizeof(REALTYPE); - int incoming_datasize = SOUND_BUFFER_SIZE * sizeof(REALTYPE); - int data_read = 0; - - - if(jack_ringbuffer_read_space(rb) >= (2 * incoming_datasize)) { - if(datasize > incoming_datasize) { - data_read = 0; - while(data_read < datasize) { - jack_ringbuffer_read(rb, (char *) outl + data_read, datasize); - jack_ringbuffer_read(rb, (char *) outr + data_read, datasize); - data_read += incoming_datasize; - } - } - else - if(datasize == incoming_datasize) { - jack_ringbuffer_read(rb, (char *) outl, datasize); - jack_ringbuffer_read(rb, (char *) outr, datasize); - } - else {} - } - else { //the ringbuffer is empty or there are too small amount of samples in it - for(int i = 0; i < nframes; i++) { - outl[i] = 0.0; - outr[i] = 0.0; - } - } - /* if (jack_ringbuffer_read_space(rb)>=datasize){ - jack_ringbuffer_read(rb, (char *) outl,datasize); - jack_ringbuffer_read(rb, (char *) outr,datasize); - } else {//the ringbuffer is empty or there are too small amount of samples in it - for (int i=0;i<nframes;i++){ - outl[i]=0.0;outr[i]=0.0; - }; - }; - */ - if(pthread_mutex_trylock(&zyn_thread_lock) == 0) { - pthread_cond_signal(&more_data); - pthread_mutex_unlock(&zyn_thread_lock); - } - - return 0; -} - -void JACKfinish() -{ - jackfinish = 1; - jack_ringbuffer_free(rb); - jack_client_close(jackclient); - - usleep(100000); - delete (jackoutl); - delete (jackoutr); -} - -int jacksrate(jack_nframes_t nframes, void *arg) -{ - return 0; -} - -void jackshutdown(void *arg) -{} - diff --git a/src/Output/JACKaudiooutput.cpp b/src/Output/JACKaudiooutput.cpp @@ -1,185 +0,0 @@ -/* - ZynAddSubFX - a software synthesizer - - JACKaudiooutput.cpp - Audio output for JACK - 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 <stdlib.h> -#include <jack/midiport.h> -#include "JACKaudiooutput.h" - -Master *jackmaster; -jack_client_t *jackclient; -char jackname[100]; -jack_port_t *outport_left, *outport_right, *midi_inport; - -int jackprocess(jack_nframes_t nframes, void *arg); -int jacksrate(jack_nframes_t nframes, void *arg); -void jackshutdown(void *arg); - -bool JACKaudiooutputinit(Master *master_) -{ - jackmaster = master_; - jackclient = 0; - - for(int i = 0; i < 15; i++) { - if(i != 0) - snprintf(jackname, 100, "ZynAddSubFX_%d", i); - else - snprintf(jackname, 100, "ZynAddSubFX"); - jackclient = jack_client_new(jackname); - if(jackclient != 0) - break; - } - - if(jackclient == 0) { - fprintf( - stderr, - "\nERROR: Cannot make a jack client (possible reasons: JACK server is not running or jackd is launched by root and zynaddsubfx by another user.).\n"); - return false; - } - - fprintf(stderr, - "Internal SampleRate = %d\nJack Output SampleRate= %d\n", - SAMPLE_RATE, - jack_get_sample_rate(jackclient)); - if((unsigned int)jack_get_sample_rate(jackclient) != - (unsigned int) SAMPLE_RATE) - fprintf(stderr, - "It is recomanded that the both samplerates to be equal.\n"); - - jack_set_process_callback(jackclient, jackprocess, 0); - jack_set_sample_rate_callback(jackclient, jacksrate, 0); - jack_on_shutdown(jackclient, jackshutdown, 0); - - outport_left = jack_port_register(jackclient, - "out_1", - JACK_DEFAULT_AUDIO_TYPE, - JackPortIsOutput | JackPortIsTerminal, - 0); - outport_right = jack_port_register(jackclient, - "out_2", - JACK_DEFAULT_AUDIO_TYPE, - JackPortIsOutput | JackPortIsTerminal, - 0); - midi_inport = jack_port_register(jackclient, - "midi_input", - JACK_DEFAULT_MIDI_TYPE, - JackPortIsInput | JackPortIsTerminal, - 0); - - if(jack_activate(jackclient)) { - fprintf(stderr, "Cannot activate jack client\n"); - return false; - } - - /* - jack_connect(jackclient,jack_port_name(outport_left),"alsa_pcm:out_1"); - jack_connect(jackclient,jack_port_name(outport_right),"alsa_pcm:out_2"); - */ - return true; -} - -int jackprocess(jack_nframes_t nframes, void *arg) -{ - jack_default_audio_sample_t *outl = - (jack_default_audio_sample_t *) jack_port_get_buffer(outport_left, - nframes); - jack_default_audio_sample_t *outr = - (jack_default_audio_sample_t *) jack_port_get_buffer(outport_right, - nframes); - - if(!pthread_mutex_trylock(&jackmaster->mutex)) { - JACKhandlemidi(nframes); - jackmaster->GetAudioOutSamples(nframes, jack_get_sample_rate( - jackclient), outl, outr); - pthread_mutex_unlock(&jackmaster->mutex); - } - else { - memset(outl, 0, sizeof(jack_default_audio_sample_t) * nframes); - memset(outr, 0, sizeof(jack_default_audio_sample_t) * nframes); - } - - return 0; -} - -void JACKfinish() -{ - jack_client_close(jackclient); -} - -int jacksrate(jack_nframes_t nframes, void *arg) -{ - return 0; -} - -void jackshutdown(void *arg) -{} - - -void JACKhandlemidi(unsigned long frames) -{ - // We must have the master mutex before we run this function - - // XXX This is really nasty, not only do we lose the sample accuracy of - // JACK MIDI, but any accuracy at all below the buffer size - - 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, chan; - - while(jack_midi_event_get(&jack_midi_event, midi_buf, - event_index++) == 0) { - midi_data = jack_midi_event.buffer; - type = midi_data[0] & 0xF0; - chan = midi_data[0] & 0x0F; - - switch(type) { - case 0x80: /* note-off */ - jackmaster->NoteOff(chan, midi_data[1]); - break; - - case 0x90: /* note-on */ - jackmaster->NoteOn(chan, midi_data[1], midi_data[2]); - break; - - case 0xB0: /* controller */ - jackmaster->SetController(chan, midi_data[1], midi_data[2]); - break; - - case 0xE0: /* pitch bend */ - jackmaster->SetController(chan, C_pitchwheel, - ((midi_data[2] << 7) | midi_data[1])); - break; - - /* XXX TODO: handle MSB/LSB controllers and RPNs and NRPNs */ - } - } -} - - -const char *JACKgetname() -{ - if(jackclient != NULL) - return jackname; - return NULL; -} - diff --git a/src/Output/JACKaudiooutput.h b/src/Output/JACKaudiooutput.h @@ -1,48 +0,0 @@ -/* - ZynAddSubFX - a software synthesizer - - JACKaudiooutput.h - Audio output for JACK - 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 JACK_AUDIO_OUTPUT_H -#define JACK_AUDIO_OUTPUT_H - -#include <jack/jack.h> - -#include "../globals.h" -#include "../Misc/Master.h" - - -#if (REALTYPE != jack_default_audio_sample_t) -#error \ - "The internal sample datatype of ZynAddSubFX and the datatype of jack differs. \ - In order to compile ZynAddSubFX the 'REALTYPE' and 'jack_default_audio_sample_t' must be equal. \ - Set the 'REALTYPE' data type (which is defined in 'globals.h') to what is defined \ - in the file types.h from jack include directory as 'jack_default_audio_sample_t' (as float or double)." -#endif - - - - -bool JACKaudiooutputinit(Master *master_); -void JACKfinish(); -void JACKhandlemidi(unsigned long frames); -const char *JACKgetname(); - -#endif - diff --git a/src/Output/Makefile b/src/Output/Makefile @@ -1,42 +0,0 @@ -include ../Makefile.inc - -objects=Recorder.o WAVaudiooutput.o - -ifeq ($(AUDIOOUT),OSS_AND_JACK) -objects+=OSSaudiooutput.o JACKaudiooutput.o -endif - -ifeq ($(AUDIOOUT),JACK) -objects+=JACKaudiooutput.o -endif - -ifeq ($(AUDIOOUT),DSSI) -objects+=DSSIaudiooutput.o -endif - -ifeq ($(AUDIOOUT),JACK_RT) -objects+=JACK_RTaudiooutput.o -endif - -ifeq ($(AUDIOOUT),PA) -objects+=PAaudiooutput.o -endif - -ifeq ($(AUDIOOUT),OSS) -objects+=OSSaudiooutput.o -endif - -ifeq ($(AUDIOOUT),VST) -objects+=VSTaudiooutput.o -endif - - -all: $(objects) - --include ../Make.deps - -.PHONY : clean -clean: - rm -f $(objects) - rm -f makeinclude.deps - diff --git a/src/Output/OSSaudiooutput.cpp b/src/Output/OSSaudiooutput.cpp @@ -1,128 +0,0 @@ -/* - ZynAddSubFX - a software synthesizer - - OSSaudiooutput.cpp - 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 <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 "OSSaudiooutput.h" -#include "../Misc/Util.h" -#include "../globals.h" -using namespace std; - -OSSaudiooutput::OSSaudiooutput() -{ - int i; - int snd_bitsize = 16; - snd_fragment = 0x00080009; //fragment size (?) - snd_stereo = 1; //stereo - snd_format = AFMT_S16_LE; - snd_samplerate = SAMPLE_RATE; - playing_until.tv_sec = 0; - playing_until.tv_usec = 0; - - smps = new short int[SOUND_BUFFER_SIZE * 2]; - for(i = 0; i < SOUND_BUFFER_SIZE * 2; i++) - smps[i] = 0; - - snd_handle = open(config.cfg.LinuxOSSWaveOutDev, O_WRONLY, 0); - if(snd_handle == -1) { - cerr << "ERROR - I can't open the "; - cerr << config.cfg.LinuxOSSWaveOutDev << '.' << endl; - return; - } - ioctl(snd_handle, SNDCTL_DSP_RESET, NULL); - - ioctl(snd_handle, SNDCTL_DSP_SETFMT, &snd_format); - ioctl(snd_handle, SNDCTL_DSP_STEREO, &snd_stereo); - ioctl(snd_handle, SNDCTL_DSP_SPEED, &snd_samplerate); - ioctl(snd_handle, SNDCTL_DSP_SAMPLESIZE, &snd_bitsize); - ioctl(snd_handle, SNDCTL_DSP_SETFRAGMENT, &snd_fragment); -} - - -/* - * Output the samples to the soundcard - * The samples are bigger than -1.0 and smaller 1.0 - */ -void OSSaudiooutput::OSSout(REALTYPE *smp_left, REALTYPE *smp_right) -{ - int i; - REALTYPE l, r; - if(snd_handle < 0) { //output could not be opened - struct timeval now; - int remaining; - 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 += SOUND_BUFFER_SIZE * 1000000 / SAMPLE_RATE; - if(remaining < 0) - playing_until.tv_usec -= remaining; - playing_until.tv_sec += playing_until.tv_usec / 1000000; - playing_until.tv_usec %= 1000000; - return; - } - - for(i = 0; i < SOUND_BUFFER_SIZE; i++) { - l = smp_left[i]; - r = smp_right[i]; - - if(l < -1.0) - l = -1.0; - else - if(l > 1.0) - l = 1.0; - if(r < -1.0) - r = -1.0; - else - if(r > 1.0) - r = 1.0; - - smps[i * 2] = (short int) (l * 32767.0); - smps[i * 2 + 1] = (short int) (r * 32767.0); - } - write(snd_handle, smps, SOUND_BUFFER_SIZE * 4); // *2 because is 16 bit, again * 2 because is stereo -} - - -OSSaudiooutput::~OSSaudiooutput() -{ - close(snd_handle); - delete [] smps; -} - diff --git a/src/Output/OSSaudiooutput.h b/src/Output/OSSaudiooutput.h @@ -1,50 +0,0 @@ -/* - 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_AUDIO_OUTPUT_H -#define OSS_AUDIO_OUTPUT_H - -#include <sys/time.h> -#include "../globals.h" - -class OSSaudiooutput -{ - public: - OSSaudiooutput(); - ~OSSaudiooutput(); - - //the out is [-1.0 .. 1.0] - /* smp_left[] and smp_right[] has the size of SOUND_BUFFER_SIZE */ - void OSSout(REALTYPE *smp_left, REALTYPE *smp_right); - private: - int snd_handle; - int snd_fragment; - int snd_stereo; - int snd_format; - int snd_samplerate; - struct timeval playing_until; - - short int *smps; //Samples to be sent to soundcard -}; - -#endif - diff --git a/src/Output/Recorder.cpp b/src/Output/Recorder.cpp @@ -1,113 +0,0 @@ -/* - 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" - -Recorder::Recorder() -{ - recordbuf_16bit = new short int [SOUND_BUFFER_SIZE * 2]; - status = 0; - notetrigger = 0; - for(int i = 0; i < SOUND_BUFFER_SIZE * 2; i++) - recordbuf_16bit[i] = 0; -} - -Recorder::~Recorder() -{ - if(recording() == 1) - stop(); - delete [] recordbuf_16bit; -} - -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; - } - - if(!wav.newfile(filename_, SAMPLE_RATE, 2)) - return 2; - - status = 1; //ready - - return 0; -} - -void Recorder::start() -{ - notetrigger = 0; - status = 2; //recording -} - -void Recorder::stop() -{ - wav.close(); - status = 0; -} - -void Recorder::pause() -{ - status = 0; -} - -int Recorder::recording() -{ - if((status == 2) && (notetrigger != 0)) - return 1; - else - return 0; -} - -void Recorder::recordbuffer(REALTYPE *outl, REALTYPE *outr) -{ - int tmp; - if(status != 2) - return; - for(int i = 0; i < SOUND_BUFFER_SIZE; i++) { - tmp = (int)(outl[i] * 32767.0); - if(tmp < -32768) - tmp = -32768; - if(tmp > 32767) - tmp = 32767; - recordbuf_16bit[i * 2] = tmp; - - tmp = (int)(outr[i] * 32767.0); - if(tmp < -32768) - tmp = -32768; - if(tmp > 32767) - tmp = 32767; - recordbuf_16bit[i * 2 + 1] = tmp; - } - wav.write_stereo_samples(SOUND_BUFFER_SIZE, recordbuf_16bit); -} - -void Recorder::triggernow() -{ - if(status == 2) - notetrigger = 1; -} - diff --git a/src/Output/Recorder.h b/src/Output/Recorder.h @@ -1,57 +0,0 @@ -/* - 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" -#include "WAVaudiooutput.h" - -/**Records sound to a file*/ -class Recorder -{ - public: - - Recorder(); - ~Recorder(); - int preparefile(std::string filename_, int overwrite); //returns 1 if the file exists - void start(); - void stop(); - void pause(); - int recording(); - void triggernow(); - void recordbuffer(REALTYPE *outl, REALTYPE *outr); - - /** Status: - * 0 - not ready(no file selected), - * 1 - ready - * 2 - recording */ - int status; - - private: - WAVaudiooutput wav; - short int *recordbuf_16bit; - int notetrigger; -}; - -#endif - diff --git a/src/Output/WAVaudiooutput.cpp b/src/Output/WAVaudiooutput.cpp @@ -1,101 +0,0 @@ -/* - 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 <stdio.h> -#include <stdlib.h> -#include "WAVaudiooutput.h" -using namespace std; - -WAVaudiooutput::WAVaudiooutput() -{ - file = NULL; - sampleswritten = 0; - samplerate = 44100; -} - -WAVaudiooutput::~WAVaudiooutput() -{ - close(); -} - -bool WAVaudiooutput::newfile(string filename, int samplerate, int channels) -{ - /**\todo Move this into the Constructor*/ - close(); //inchide un posibil fisier existent - file = fopen(filename.c_str(), "w"); - if(!file) - return false; - this->samplerate = samplerate; - this->channels = channels; - sampleswritten = 0; - char tmp[44]; - fwrite(tmp, 1, 44, file); - return true; -} - -void WAVaudiooutput::close() -{ - if(file) { - 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; - } -} - -void WAVaudiooutput::write_stereo_samples(int nsmps, short int *smps) -{ - if(!file) - return; - fwrite(smps, nsmps, 4, file); - sampleswritten += nsmps; -} - -void WAVaudiooutput::write_mono_samples(int nsmps, short int *smps) -{ - if(!file) - return; - fwrite(smps, nsmps, 2, file); - sampleswritten += nsmps; -} - diff --git a/src/Output/WAVaudiooutput.h b/src/Output/WAVaudiooutput.h @@ -1,43 +0,0 @@ -/* - - Copyright (C) 2008 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 -*/ - -#ifndef WAVOUTPUT_H -#define WAVOUTPUT_H -#include <string> - -class WAVaudiooutput -{ - public: - WAVaudiooutput(); - ~WAVaudiooutput(); - - bool newfile(std::string filename, int samplerate, int channels); - void close(); - - void write_mono_samples(int nsmps, short int *smps); - void write_stereo_samples(int nsmps, short int *smps); - - private: - int sampleswritten; - int samplerate; - int channels; - FILE *file; -}; -#endif - diff --git a/src/Params/ADnoteParameters.cpp b/src/Params/ADnoteParameters.cpp @@ -25,6 +25,9 @@ #include <math.h> #include "ADnoteParameters.h" +#include "EnvelopeParams.h" +#include "LFOParams.h" + int ADnote_unison_sizes[] = {1, 2, 3, 4, 5, 6, 8, 10, 12, 15, 20, 25, 30, 40, 50, 0}; diff --git a/src/Params/ADnoteParameters.h b/src/Params/ADnoteParameters.h @@ -25,8 +25,6 @@ #include "../globals.h" -#include "EnvelopeParams.h" -#include "LFOParams.h" #include "FilterParams.h" #include "../Synth/OscilGen.h" #include "../Synth/Resonance.h" @@ -35,6 +33,9 @@ #include "../DSP/FFTwrapper.h" #include "PresetsArray.h" +class EnvelopeParams; +class LFOParams; + enum FMTYPE { NONE, MORPH, RING_MOD, PHASE_MOD, FREQ_MOD, PITCH_MOD }; diff --git a/src/Params/CMakeLists.txt b/src/Params/CMakeLists.txt @@ -16,4 +16,4 @@ add_library(zynaddsubfx_params STATIC ${zynaddsubfx_params_SRCS} ) -target_link_libraries(zynaddsubfx_params)# ${ASOUND_LIBRARY}) +target_link_libraries(zynaddsubfx_params zynaddsubfx_misc) diff --git a/src/Params/Makefile b/src/Params/Makefile @@ -1,15 +0,0 @@ -include ../Makefile.inc - -objects=ADnoteParameters.o EnvelopeParams.o FilterParams.o \ - LFOParams.o SUBnoteParameters.o PADnoteParameters.o Controller.o Presets.o PresetsArray.o PresetsStore.o - - -all: $(objects) - --include ../Make.deps - -.PHONY : clean -clean: - rm -f $(objects) - rm -f makeinclude.deps - diff --git a/src/Params/PADnoteParameters.cpp b/src/Params/PADnoteParameters.cpp @@ -21,7 +21,7 @@ */ #include <math.h> #include "PADnoteParameters.h" -#include "../Output/WAVaudiooutput.h" +#include "../Misc/WavFile.h" using namespace std; PADnoteParameters::PADnoteParameters(FFTwrapper *fft_, @@ -676,14 +676,13 @@ void PADnoteParameters::export2wav(string basefilename) char tmpstr[20]; snprintf(tmpstr, 20, "_%02d", k + 1); string filename = basefilename + string(tmpstr) + ".wav"; - WAVaudiooutput wav; - if(wav.newfile(filename, SAMPLE_RATE, 1)) { + WavFile wav(filename, SAMPLE_RATE, 1); + if(wav.good()) { int nsmps = sample[k].size; short int *smps = new short int[nsmps]; for(int i = 0; i < nsmps; i++) smps[i] = (short int)(sample[k].smp[i] * 32767.0); - wav.write_mono_samples(nsmps, smps); - wav.close(); + wav.writeMonoSamples(nsmps, smps); } } } diff --git a/src/Samples/Makefile b/src/Samples/Makefile @@ -1,14 +0,0 @@ -include ../Makefile.inc - -objects=Sample.o - - -all: $(objects) - --include ../Make.deps - -.PHONY : clean -clean: - rm -f $(objects) - rm -f makeinclude.deps - diff --git a/src/Samples/Sample.cpp b/src/Samples/Sample.cpp @@ -20,6 +20,7 @@ */ #include <cmath> #include <cstring>//for memcpy/memset + #include <iostream> #include "Sample.h" @@ -28,12 +29,17 @@ using namespace std; #warning TODO Think about renaming Sample to Frame /**\TODO start using pointer math here as these will be Frequency called * functions throughout the code*/ +Sample::Sample() + :bufferSize(1),buffer(new REALTYPE[1]) +{ + buffer[0] = 0.0; +} + Sample::Sample(const Sample &smp) :bufferSize(smp.bufferSize) { buffer = new REALTYPE[bufferSize]; - for(int i = 0; i < bufferSize; ++i) - *(i + buffer) = *(i + smp.buffer); + memcpy(buffer, smp.buffer, bufferSize * sizeof(float)); } Sample::Sample(int length, REALTYPE fill) @@ -42,8 +48,7 @@ Sample::Sample(int length, REALTYPE fill) if(length < 1) bufferSize = 1; buffer = new REALTYPE[bufferSize]; - for(int i = 0; i < bufferSize; ++i) - buffer[i] = fill; + memset(buffer, fill, bufferSize * sizeof(float)); } Sample::Sample(int length, const REALTYPE *input) @@ -51,8 +56,7 @@ Sample::Sample(int length, const REALTYPE *input) { if(length > 0) { buffer = new REALTYPE[length]; - for(int i = 0; i < length; ++i) - *(buffer + i) = *(input + i); + memcpy(buffer, input, bufferSize * sizeof(float)); } else { buffer = new REALTYPE[1]; @@ -74,17 +78,12 @@ void Sample::clear() void Sample::operator=(const Sample &smp) { - /**\todo rewrite to be less repetitive*/ - if(bufferSize == smp.bufferSize) - for(int i = 0; i < bufferSize; ++i) - *(i + buffer) = *(i + smp.buffer); - else { + if(bufferSize != smp.bufferSize) { delete[] buffer; buffer = new REALTYPE[smp.bufferSize]; bufferSize = smp.bufferSize; - for(int i = 0; i < bufferSize; ++i) - *(i + buffer) = *(i + smp.buffer); } + memcpy(buffer, smp.buffer, bufferSize * sizeof(float)); } bool Sample::operator==(const Sample &smp) const @@ -106,63 +105,68 @@ bool Sample::operator==(const Sample &smp) const * @param xb X of point b * @return estimated Y of test point */ -inline float linearEstimate(float ya, float yb, float xt, int xa = 0, int xb = 1) +float linearEstimate(float ya, float yb, float xt, float xa = 0.0, float xb =1.0) { - if(xa == xb) - return ya; +#warning TODO this could be done with a good bit less computation + //Lets make this simple by normalizing the x axis + + //Normalize point a + xb -= xa; + xt -= xa; + xa -= xa; - return (yb-ya) * (xt-xa)/(xb-xa) + ya; + //Normalize point b + xt /= xb; + xb /= xb; + + //Now xa=0 xb=1 0<=xt<=1 + //simpily use y=mx+b + return (yb-ya) * xt + ya; } -void Sample::resize(unsigned int nsize) + +void Sample::resample(const unsigned int rate, const unsigned int nrate) { - if(bufferSize == nsize) - return; + if(rate == nrate) + return; //no resampling here else {//resampling occurs here - float ratio = (nsize * 1.0) / (bufferSize * 1.0); + float ratio = (nrate * 1.0) / (rate * 1.0); - int nBufferSize = nsize; + int nBufferSize = (int)bufferSize * ratio; float *nBuffer = new float[nBufferSize]; - //take care of edge cases - *nBuffer = *buffer; - *(nBuffer+nBufferSize-1) = *(buffer+bufferSize-1); - //addition is done to avoid 0 edge case - for(int i = 1; i < nBufferSize - 1; ++i) - { - float left = floor(i/ratio); - float right = ceil((i+1)/ratio); - float test = i/ratio; - if(left > bufferSize - 1) - left = bufferSize - 1; - if(right > bufferSize - 1) - right = bufferSize - 1; - if(left > test) - test = left; - nBuffer[i] = linearEstimate(buffer[(int)left], - buffer[(int)right], - test, (int)left, (int)right); - } + for(int i = 0; i < nBufferSize; ++i) + nBuffer[i] = linearEstimate(buffer[(int)floor(i/ratio)], + buffer[(int)ceil((i+1)/ratio)], + i, + floor(i/ratio), + ceil((i+1)/ratio)); //put the new data in - delete[] buffer; + delete[] buffer; buffer = nBuffer; bufferSize = nBufferSize; } } -void Sample::append(const Sample &smp) +Sample &Sample::append(const Sample &smp) { int nbufferSize = bufferSize + smp.bufferSize; float *nbuffer = new float[nbufferSize]; memcpy(nbuffer, buffer, bufferSize * sizeof(float)); memcpy(nbuffer + bufferSize, smp.buffer, smp.bufferSize * sizeof(float)); - delete buffer; + delete[] buffer; buffer = nbuffer; bufferSize = nbufferSize; + return *this; +} + +Sample Sample::subSample(int a, int b) const +{ + return Sample(b-a, buffer+a); } REALTYPE Sample::max() const diff --git a/src/Samples/Sample.h b/src/Samples/Sample.h @@ -22,11 +22,12 @@ #define SAMPLE_H #include "../globals.h" /** - * Base Class for Samples + * Audio Samples Representation */ class Sample { public: + Sample(); Sample(const Sample &smp); Sample(int length, REALTYPE fill = 0); Sample(int length, const REALTYPE *fill); @@ -57,11 +58,15 @@ class Sample * sparingly*/ const REALTYPE *c_buf() const {return buffer;} - /**Change the size of the sample*/ - void resize(unsigned int nsize); + /**Change the sampling rate of the Sample*/ + void resample(const unsigned int rate, const unsigned int nrate); - /**Appends another Sample to this Sample*/ - void append(const Sample &smp); + /**Appends another Sample to this Sample + * @return this*/ + Sample &append(const Sample &smp); + + /**Gets a subsample from a to b*/ + Sample subSample(int a, int b) const; REALTYPE max() const; REALTYPE min() const; diff --git a/src/Seq/Makefile b/src/Seq/Makefile @@ -1,14 +0,0 @@ -include ../Makefile.inc - -objects=MIDIEvents.o MIDIFile.o Sequencer.o - - -all: $(objects) - --include ../Make.deps - -.PHONY : clean -clean: - rm -f $(objects) - rm -f makeinclude.deps - diff --git a/src/Synth/Makefile b/src/Synth/Makefile @@ -1,14 +0,0 @@ -include ../Makefile.inc - -objects=ADnote.o Envelope.o LFO.o OscilGen.o SUBnote.o Resonance.o PADnote.o - - -all: $(objects) - --include ../Make.deps - -.PHONY : clean -clean: - rm -f $(objects) - rm -f makeinclude.deps - diff --git a/src/Tests/EchoTest.h b/src/Tests/EchoTest.h @@ -21,6 +21,7 @@ */ #include <cxxtest/TestSuite.h> #include <cmath> +#include <cstdlib> #include <iostream> #include "../Effects/Echo.h" #include "../globals.h" diff --git a/src/Tests/SampleTest.h b/src/Tests/SampleTest.h @@ -80,5 +80,23 @@ class SampleTest:public CxxTest::TestSuite for(int i = 0; i < 74; ++i) TS_ASSERT_DELTA(smp1[i], (i < 54 ? 2 : 17), 0.001); } + + void testResample() { + Sample orig(32,2); + Sample cpy(orig); + + //test for no resampleing + orig.resample(128,128); + TS_ASSERT_EQUALS(cpy,orig); + + //test for no bad distortions + orig.resample(128,256); + orig.resample(256,128); + TS_ASSERT_EQUALS(cpy,orig); + + //test for downsample + orig.resample(256,128); + TS_ASSERT_EQUALS(orig.size(),cpy.size()/2); + } }; diff --git a/src/UI/CMakeLists.txt b/src/UI/CMakeLists.txt @@ -27,6 +27,7 @@ fltk_wrap_ui(zynaddsubfx_gui ${UI_fl_files}) add_library(zynaddsubfx_gui STATIC ${UI_objs} ${zynaddsubfx_gui_FLTK_UI_SRCS} + NioUI.cpp ) target_link_libraries(zynaddsubfx_gui ${FLTK_LIBRARIES} ${MYFLTK_LIBRARIES}) diff --git a/src/UI/Makefile b/src/UI/Makefile @@ -1,41 +0,0 @@ -include ../Makefile.inc - -%.cc : %.fl - fluid -c $< - -objects=WidgetPDial.o PresetsUI.o EnvelopeUI.o LFOUI.o FilterUI.o VirKeyboard.o ConfigUI.o\ - SUBnoteUI.o ResonanceUI.o OscilGenUI.o ADnoteUI.o PADnoteUI.o EffUI.o BankUI.o \ - PartUI.o MicrotonalUI.o SeqUI.o MasterUI.o -ifeq ($(DISABLE_GUI),YES) - objects= -endif - -all: $(objects) - -WidgetPDial.o: WidgetPDial.fl WidgetPDial.cc WidgetPDial.h -PresetsUI.o: PresetsUI.fl PresetsUI.cc PresetsUI.h -EnvelopeUI.o: EnvelopeUI.fl EnvelopeUI.cc EnvelopeUI.h -LFOUI.o: LFOUI.fl LFOUI.cc LFOUI.h -FilterUI.o: FilterUI.fl FilterUI.cc FilterUI.h - -ResonanceUI.o:ResonanceUI.fl ResonanceUI.cc ResonanceUI.h -OscilGenUI.o:OscilGenUI.fl OscilGenUI.cc OscilGenUI.h -ADnoteUI.o:ADnoteUI.fl ADnoteUI.cc ADnoteUI.h -SUBnoteUI.o:SUBnoteUI.fl SUBnoteUI.cc SUBnoteUI.h -PADnoteUI.o:PADnoteUI.fl PADnoteUI.cc PADnoteUI.h - -EffUI.o: EffUI.fl EffUI.cc EffUI.h -BankUI.o: BankUI.fl BankUI.cc BankUI.h -PartUI.o: PartUI.fl PartUI.cc PartUI.h - -VirKeyboard.o: VirKeyboard.fl VirKeyboard.cc VirKeyboard.h -ConfigUI.o: ConfigUI.fl ConfigUI.cc ConfigUI.h -MicrotonalUI.o: MicrotonalUI.fl MicrotonalUI.cc MicrotonalUI.h -SeqUI.o: SeqUI.fl SeqUI.cc SeqUI.h -MasterUI.o: MasterUI.fl MasterUI.cc MasterUI.h - -.PHONY : clean -clean: - rm -f $(objects) - rm -f makeinclude.deps - rm -f *.h *.cc diff --git a/src/UI/MasterUI.fl b/src/UI/MasterUI.fl @@ -48,6 +48,9 @@ decl {\#include "SeqUI.h"} {public decl {\#include "PresetsUI.h"} {public } +decl {\#include "NioUI.h"} {public global +} + decl {\#include "../Misc/Master.h"} {public } @@ -60,7 +63,7 @@ decl {\#include "../Misc/Util.h"} {public decl {\#include "../globals.h"} {public } -class VUMeter {: {public Fl_Box} +class VUMeter {selected : {public Fl_Box} } { Function {VUMeter(int x,int y, int w, int h, const char *label=0):Fl_Box(x,y,w,h,label)} {} { code {master=NULL; @@ -81,15 +84,17 @@ oldrmsdbr=0.0;} {} int ox=x(); int oy=y(); int lx=w(); int ly=h(); -pthread_mutex_lock(&master->mutex); -REALTYPE dbl=rap2dB(master->vuoutpeakl); -REALTYPE dbr=rap2dB(master->vuoutpeakr); -REALTYPE rmsdbl=rap2dB(master->vurmspeakl); -REALTYPE rmsdbr=rap2dB(master->vurmspeakr); -REALTYPE maxdbl=rap2dB(master->vumaxoutpeakl); -REALTYPE maxdbr=rap2dB(master->vumaxoutpeakr); -int clipped=master->vuclipped; -pthread_mutex_unlock(&master->mutex); +vuData data = master->getVuData(); + +//pthread_mutex_lock(&master->mutex); +REALTYPE dbl=rap2dB(data.outpeakl); +REALTYPE dbr=rap2dB(data.outpeakr); +REALTYPE rmsdbl=rap2dB(data.rmspeakl); +REALTYPE rmsdbr=rap2dB(data.rmspeakr); +REALTYPE maxdbl=rap2dB(data.maxoutpeakl); +REALTYPE maxdbr=rap2dB(data.maxoutpeakr); +int clipped=data.clipped; +//pthread_mutex_unlock(&master->mutex); dbl=(MIN_DB-dbl)/MIN_DB; if (dbl<0.0) dbl=0.0; @@ -229,7 +234,7 @@ REALTYPE tmp=ly*1.0/MIN_DB; Function {tick(void *v)} {return_type {static void} } { code {tickdraw((VUMeter *) v); -Fl::add_timeout(1.0/25.0,tick,v);//25 fps} {} +Fl::add_timeout(1.0/60.0,tick,v);//60 fps} {} } Function {handle(int event)} {return_type int } { @@ -298,7 +303,7 @@ class Panellistitem {: {public Fl_Group} Function {make_window()} {private } { Fl_Window panellistitem { - private xywh {315 213 70 260} type Double hide + private xywh {315 213 70 260} type Double labeltype NORMAL_LABEL align 80 hide class Fl_Group } { Fl_Group panellistitemgroup { @@ -425,7 +430,7 @@ if (fl_choice("Exit and leave the unsaved data?","No","Yes",NULL)) { *exitprogram=1; }; \#endif} - xywh {31 206 390 465} type Double hide xclass zynaddsubfx + xywh {450 306 390 465} type Double align 80 hide xclass zynaddsubfx } { Fl_Menu_Bar mastermenu { xywh {-5 0 690 25} @@ -506,7 +511,13 @@ updatepanel();} MenuItem {} { label {&Settings...} callback {configui->show();} - xywh {25 25 100 20} divider + xywh {25 25 100 20} + } + MenuItem {} { + label {N&io Settings} + callback {nioui.refresh(); +nioui.show();} + xywh {0 0 36 21} divider } MenuItem {} { label {&Copyright...} @@ -1002,7 +1013,7 @@ panelwindow->show();} } Fl_Window aboutwindow { label {Copyright...} - xywh {411 344 365 280} type Double hide + xywh {411 344 365 280} type Double labeltype NORMAL_LABEL align 80 hide } { Fl_Box {} { label {Copyright (c) 2002-2009 Nasca O. PAUL and others. Please read AUTHORS.txt} @@ -1029,7 +1040,7 @@ GNU General Public License for details.} } Fl_Window syseffsendwindow { label {System Effects Send} - xywh {171 234 120 250} type Double hide resizable + xywh {171 234 120 250} type Double labeltype NORMAL_LABEL align 80 hide resizable } { Fl_Scroll {} {open xywh {0 45 120 170} box FLAT_BOX resizable @@ -1048,7 +1059,7 @@ GNU General Public License for details.} } Fl_Window panelwindow { label {ZynAddSubFX Panel} - xywh {89 59 630 635} type Double hide + xywh {89 59 630 635} type Double labeltype NORMAL_LABEL align 80 hide } { Fl_Scroll {} { xywh {0 5 570 310} type HORIZONTAL box THIN_UP_BOX @@ -1088,7 +1099,7 @@ if (fl_choice("Exit and leave the unsaved data?","No","Yes",NULL)) { *exitprogram=1; }; \#endif} - xywh {400 405 600 335} type Double hide + xywh {400 405 600 335} type Double labeltype NORMAL_LABEL align 80 hide } { Fl_Menu_Bar {} { xywh {0 0 690 25} @@ -1578,7 +1589,7 @@ virkeys->take_focus();} Fl_Window selectuiwindow { label {User Interface mode} callback {*exitprogram=1;} - xywh {342 246 430 250} type Double hide non_modal + xywh {342 246 430 250} type Double labeltype NORMAL_LABEL align 80 hide non_modal } { Fl_Box {} { label {Welcome to ZynAddSubFX} @@ -1738,8 +1749,9 @@ pthread_mutex_lock(&master->mutex); //load the data int result=master->loadXML(filename); -pthread_mutex_unlock(&master->mutex); + master->applyparameters(); +pthread_mutex_unlock(&master->mutex); npartcounter->value(1); refresh_master_ui(); @@ -1825,4 +1837,5 @@ bankui->hide();} {} decl {int swapefftype;} {} decl {char masterwindowlabel[100];} {} decl {Panellistitem *panellistitem[NUM_MIDI_PARTS];} {} + decl {NioUI nioui;} {} } diff --git a/src/UI/NioUI.cpp b/src/UI/NioUI.cpp @@ -0,0 +1,82 @@ +#include "NioUI.h" +#include "../Nio/EngineMgr.h" +#include "../Nio/OutMgr.h" +#include "../Nio/InMgr.h" +#include "../Nio/AudioOut.h" +#include "../Nio/MidiIn.h" +#include <cstdio> +#include <FL/Fl_Tabs.H> +#include <FL/Fl_Group.H> +#include <FL/Fl_Text_Display.H> +#include <iostream> +#include <cstring> + +using namespace std; + +NioUI::NioUI() + :Fl_Window(200,100,400,400,"New IO Controls") +{ + //hm, I appear to be leaking memory + Fl_Tabs *wintabs = new Fl_Tabs(0,0,400,400-15); + { + Fl_Group *gen = new Fl_Group(0,20,400,400-35,"General"); + { + Fl_Text_Buffer *buff = new Fl_Text_Buffer(); + Fl_Text_Display *intro = new Fl_Text_Display(20,40,350,300); + intro->buffer(buff); + buff->text("Thanks For Testing Out the New" + " Input/Output system. " + "Beware of bugs that may exist and" + " enjoy the new system."); + intro->wrap_mode(4, 40); + } + gen->end(); + + Fl_Group *settings = new Fl_Group(0,20,400,400-35,"Settings"); + { + audio = new Fl_Choice(60, 80, 100, 25, "Audio"); + audio->callback(audioCallback); + midi = new Fl_Choice(60, 100, 100, 25, "Midi"); + midi->callback(midiCallback); + } + settings->end(); + + int audioval = 0; + int midival = 0; + for(list<Engine *>::iterator itr = sysEngine->engines.begin(); + itr != sysEngine->engines.end(); ++itr) { + Engine *eng = *itr; + if(dynamic_cast<MidiIn *>(eng)) + midi->add(eng->name.c_str()); + if(dynamic_cast<AudioOut *>(eng)) + audio->add(eng->name.c_str()); + if(eng->name == sysOut->getSink()) + audioval = audio->size() - 2; + if(eng->name == sysIn->getSource()) + midival = midi->size() - 2; + } + audio->value(audioval); + midi->value(midival); + } + wintabs->end(); + + resizable(this); + size_range(400,300); +} + +void NioUI::refresh() +{ +} + +void NioUI::midiCallback(Fl_Widget *c) +{ + bool good = sysIn->setSource(static_cast<Fl_Choice *>(c)->text()); + static_cast<Fl_Choice *>(c)->textcolor(fl_rgb_color(255*!good,0,0)); +} + +void NioUI::audioCallback(Fl_Widget *c) +{ + bool good = sysOut->setSink(static_cast<Fl_Choice *>(c)->text()); + static_cast<Fl_Choice *>(c)->textcolor(fl_rgb_color(255*!good,0,0)); +} + diff --git a/src/UI/NioUI.h b/src/UI/NioUI.h @@ -0,0 +1,29 @@ +#ifndef NIOUI_H +#define NIOUT_H + +#include <FL/Fl.H> +#include <FL/Fl_Light_Button.H> +#include <FL/Fl_Window.H> +#include <FL/Fl_Pack.H> +#include <FL/Fl_Spinner.H> +#include <FL/Enumerations.H> +#include <FL/Fl_Choice.H> +#include <list> +#include <string> + +class NioUI : public Fl_Window +{ + public: + NioUI(); + void refresh(); + private: + //std::list<NioTab *> tabs; + + Fl_Choice *midi; + Fl_Choice *audio; + static void midiCallback(Fl_Widget *c); + static void audioCallback(Fl_Widget *c); +}; + +#endif + diff --git a/src/UI/VirKeyboard.fl b/src/UI/VirKeyboard.fl @@ -1,5 +1,5 @@ # data file for the Fltk User Interface Designer (fluid) -version 1.0107 +version 1.0300 header_name {.h} code_name {.cc} decl {//Copyright (c) 2002-2005 Nasca Octavian Paul} {} @@ -205,7 +205,7 @@ if (rndvelocity!=0){ }; pthread_mutex_lock(&master->mutex); - master->NoteOn(midich,nk+midioct*12,(int)vel); +master->noteOn(midich,nk+midioct*12,(int)vel); pthread_mutex_unlock(&master->mutex);} {} } Function {relasekey(int nk,int type)} {} { @@ -219,7 +219,7 @@ pressed[nk]=0; damage(1); pthread_mutex_lock(&master->mutex); - master->NoteOff(midich,nk+12*midioct); +master->noteOff(midich,nk+12*midioct); pthread_mutex_unlock(&master->mutex);} {} } Function {relaseallkeys(int type)} {} { @@ -293,7 +293,7 @@ virkeyboardwindow->hide();} callback {int ctl=midictl; pthread_mutex_lock(&master->mutex); - master->SetController(virkeys->midich,ctl,(int) o->value()); +master->setController(virkeys->midich,ctl,(int) o->value()); pthread_mutex_unlock(&master->mutex); virkeys->take_focus();} tooltip {Controller value} xywh {605 10 15 115} type {Vert Fill} box ENGRAVED_BOX selection_color 229 labelsize 8 align 5 minimum 127 maximum 0 step 1 value 64 textsize 7 @@ -375,7 +375,7 @@ virkeys->take_focus();} Fl_Roller pitchwheelroller { label Pwh callback {pthread_mutex_lock(&master->mutex); - master->SetController(virkeys->midich,C_pitchwheel,-(int) o->value()); +master->setController(virkeys->midich,C_pitchwheel,-(int) o->value()); pthread_mutex_unlock(&master->mutex); virkeys->take_focus();} tooltip {Pitch Wheel} xywh {625 10 20 95} box PLASTIC_UP_BOX labelsize 8 align 1 when 3 minimum -8192 maximum 8192 step 64 @@ -388,7 +388,7 @@ pitchwheelroller->do_callback();} } Fl_Dial {} { label Vrnd - callback {virkeys->rndvelocity=(int) o->value();} + callback {virkeys->rndvelocity=(int) o->value();} selected tooltip {Velocity Randomness} xywh {205 105 20 20} box ROUND_UP_BOX labelsize 10 align 129 maximum 127 step 1 code0 {o->value(virkeys->rndvelocity);} class WidgetPDial @@ -410,8 +410,7 @@ midictl=75; make_window();} {} } Function {~VirKeyboard()} {} { - code {delete virkeyboardwindow;} {selected - } + code {delete virkeyboardwindow;} {} } Function {show()} {} { code {virkeyboardwindow->show();} {} diff --git a/src/main.cpp b/src/main.cpp @@ -1,7 +1,7 @@ /* ZynAddSubFX - a software synthesizer - main.c - Main file of the synthesizer + main.cpp - Main file of the synthesizer Copyright (C) 2002-2005 Nasca Octavian Paul Author: Nasca Octavian Paul @@ -20,13 +20,15 @@ */ -#include <stdio.h> //remove once iostream is used #include <iostream> +#include <cmath> +#include <cctype> +#include <algorithm> #include <unistd.h> #include <pthread.h> -#ifdef OS_LINUX +#if OS_LINUX || OS_CYGWIN #include <getopt.h> #elif OS_WINDOWS #include <winbase.h> @@ -38,21 +40,12 @@ #include "Misc/Dump.h" extern Dump dump; -#ifdef ALSAMIDIIN -#include "Input/ALSAMidiIn.h" -#endif - -#ifdef OSSMIDIIN -#include "Input/OSSMidiIn.h" -#endif - -#if (defined(NONEMIDIIN) || defined(VSTMIDIIN) || defined(DSSIMIDIIN)) -#include "Input/NULLMidiIn.h" -#endif - -#ifdef WINMIDIIN -#include "Input/WINMidiIn.h" -#endif +//Nio System +#include "Nio/MidiIn.h" +#include "Nio/AudioOut.h" +#include "Nio/OutMgr.h" +#include "Nio/InMgr.h" +#include "Nio/EngineMgr.h" #ifndef DISABLE_GUI #ifdef QT_GUI @@ -72,140 +65,27 @@ MasterUI *ui; using namespace std; -pthread_t thr1, thr2, thr3, thr4; +pthread_t thr3, thr4; Master *master; int swaplr = 0; //1 for left-right swapping -bool usejackit = false; - -#ifdef JACKAUDIOOUT -#include "Output/JACKaudiooutput.h" -#endif - -#ifdef JACK_RTAUDIOOUT -#include "Output/JACKaudiooutput.h" -#endif - -#ifdef PAAUDIOOUT -#include "Output/PAaudiooutput.h" -#endif - -#ifdef OSSAUDIOOUT -#include "Output/OSSaudiooutput.h" -OSSaudiooutput *audioout; -#endif #ifdef USE_LASH #include "Misc/LASHClient.h" LASHClient *lash; #endif -MidiIn *Midi; int Pexitprogram = 0; //if the UI set this to 1, the program will exit /* - * Try to get the realtime priority - */ -void set_realtime() -{ -#ifdef OS_LINUX - sched_param sc; - - sc.sched_priority = 50; - - //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"); -#endif -} - -/* - * Midi input thread - */ -#if !(defined(WINMIDIIN) || defined(VSTMIDIIN)) -void *thread1(void *arg) -{ - MidiCmdType cmdtype = MidiNoteOFF; - unsigned char cmdchan = 0, note = 0, vel = 0; - int cmdparams[MP_MAX_BYTES]; - for(int i = 0; i < MP_MAX_BYTES; ++i) - cmdparams[i] = 0; - - set_realtime(); - while(Pexitprogram == 0) { - Midi->getmidicmd(cmdtype, cmdchan, cmdparams); - note = cmdparams[0]; - vel = cmdparams[1]; - - pthread_mutex_lock(&master->mutex); - - if((cmdtype == MidiNoteON) && (note != 0)) - master->NoteOn(cmdchan, note, vel); - if((cmdtype == MidiNoteOFF) && (note != 0)) - master->NoteOff(cmdchan, note); - if(cmdtype == MidiController) - master->SetController(cmdchan, cmdparams[0], cmdparams[1]); - - pthread_mutex_unlock(&master->mutex); - } - - return 0; -} -#endif - -/* - * Wave output thread (for OSS AUDIO out) - */ -#if defined(OSSAUDIOOUT) -//!(defined(JACKAUDIOOUT)||defined(JACK_RTAUDIOOUT)||defined(PAAUDIOOUT)||defined(VSTAUDIOOUT)) - -void *thread2(void *arg) -{ - REALTYPE outputl[SOUND_BUFFER_SIZE]; - REALTYPE outputr[SOUND_BUFFER_SIZE]; - - set_realtime(); - while(Pexitprogram == 0) { - pthread_mutex_lock(&master->mutex); - master->AudioOut(outputl, outputr); - pthread_mutex_unlock(&master->mutex); - -#ifndef NONEAUDIOOUT - audioout->OSSout(outputl, outputr); -#endif - - /** / int i,x,x2; - REALTYPE xx,xx2; - - short int xsmps[SOUND_BUFFER_SIZE*2]; - for (i=0;i<SOUND_BUFFER_SIZE;i++){//output to stdout - xx=-outputl[i]*32767; - xx2=-outputr[i]*32767; - if (xx<-32768) xx=-32768; - if (xx>32767) xx=32767; - if (xx2<-32768) xx2=-32768; - if (xx2>32767) xx2=32767; - x=(short int) xx; - x2=(short int) xx2; - xsmps[i*2]=x;xsmps[i*2+1]=x2; - }; - write(1,&xsmps,SOUND_BUFFER_SIZE*2*2); - - / * */ - } - return 0; -} -#endif - -/* * User Interface thread */ - - -void *thread3(void *arg) +void *thread3(void *) { #ifndef DISABLE_GUI #ifdef FLTK_GUI + + ui = new MasterUI(master, &Pexitprogram); ui->showUI(); while(Pexitprogram == 0) { @@ -240,6 +120,9 @@ void *thread3(void *arg) return 0; } +//this code is disabled for Nio testing +//it should get moved out of here into the nio system soon +#if 0 /* * Sequencer thread (test) */ @@ -277,36 +160,26 @@ void *thread4(void *arg) } //if (!realtime player) atunci fac asta //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -#ifdef OS_LINUX - usleep(1000); -#elif OS_WINDOWS - Sleep(1); -#endif + os_sleep(1000); } return 0; } +#endif /* * Program initialisation */ - - void initprogram() { cerr.precision(1); cerr << std::fixed; -#ifndef JACKAUDIOOUT -#ifndef JACK_RTAUDIOOUT cerr << "\nSample Rate = \t\t" << SAMPLE_RATE << endl; -#endif -#endif cerr << "Sound Buffer Size = \t" << SOUND_BUFFER_SIZE << " samples" << endl; cerr << "Internal latency = \t" << SOUND_BUFFER_SIZE * 1000.0 / SAMPLE_RATE << " ms" << endl; cerr << "ADsynth Oscil.Size = \t" << OSCIL_SIZE << " samples" << endl; - //fflush(stderr); srand(time(NULL)); denormalkillbuf = new REALTYPE [SOUND_BUFFER_SIZE]; for(int i = 0; i < SOUND_BUFFER_SIZE; i++) @@ -315,42 +188,16 @@ void initprogram() master = new Master(); master->swaplr = swaplr; -#if defined(JACKAUDIOOUT) - if(usejackit) { - bool tmp = JACKaudiooutputinit(master); -#if defined(OSSAUDIOOUT) - if(!tmp) - cout << "\nUsing OSS instead." << endl; -#else - if(!tmp) - exit(1); -#endif - usejackit = tmp; - } -#endif -#if defined(OSSAUDIOOUT) - if(!usejackit) - audioout = new OSSaudiooutput(); - else - audioout = NULL; -#endif + //Nio Initialization -#ifdef JACK_RTAUDIOOUT - JACKaudiooutputinit(master); -#endif -#ifdef PAAUDIOOUT - PAaudiooutputinit(master); -#endif + //Enable input wrapper + sysIn = new InMgr(master); -#ifdef ALSAMIDIIN - Midi = new ALSAMidiIn(); -#endif -#ifdef OSSMIDIIN - Midi = new OSSMidiIn(); -#endif -#if (defined(NONEMIDIIN) || (defined(VSTMIDIIN))) - Midi = new NULLMidiIn(); -#endif + //Initialize the Output Systems + sysOut = new OutMgr(master); + + //Initialize The Engines + sysEngine = new EngineMgr(); } /* @@ -359,35 +206,25 @@ void initprogram() void exitprogram() { pthread_mutex_lock(&master->mutex); -#ifdef OSSAUDIOOUT - delete (audioout); -#endif -#ifdef JACKAUDIOOUT - if(usejackit) - JACKfinish(); -#endif -#ifdef JACK_RTAUDIOOUT - JACKfinish(); -#endif -#ifdef PAAUDIOOUT - PAfinish(); -#endif + pthread_mutex_unlock(&master->mutex); + sysEngine->stop(); + delete sysOut; + delete sysIn; + delete sysEngine; #ifndef DISABLE_GUI - delete (ui); + delete ui; #endif - delete (Midi); - delete (master); + delete master; #ifdef USE_LASH - delete (lash); + delete lash; #endif -// pthread_mutex_unlock(&master->mutex); delete [] denormalkillbuf; } -#ifdef OS_WINDOWS +#if OS_WINDOWS #define ARGSIZE 100 char winoptarguments[ARGSIZE]; char getopt(int argc, char *argv[], const char *shortopts, int *index) @@ -403,9 +240,7 @@ char getopt(int argc, char *argv[], const char *shortopts, int *index) result = argv[*index][1]; if(*index + 1 < argc) snprintf(winoptarguments, ARGSIZE, "%s", argv[*index + 1]); - ; } - ; (*index)++; return result; } @@ -424,91 +259,68 @@ int main(int argc, char *argv[]) config.init(); dump.startnow(); int noui = 0; -#ifdef JACKAUDIOOUT - usejackit = true; //use jack by default -#endif cerr << "\nZynAddSubFX - Copyright (c) 2002-2009 Nasca Octavian Paul and others" << endl; cerr << "Compiled: " << __DATE__ << " " << __TIME__ << endl; cerr << "This program is free software (GNU GPL v.2 or later) and \n"; cerr << "it comes with ABSOLUTELY NO WARRANTY.\n" << endl; -#ifdef OS_LINUX - if(argc == 1) - cerr << "Try 'zynaddsubfx --help' for command-line options." << endl; -#else - if(argc == 1) - cerr << "Try 'zynaddsubfx -h' for command-line options.\n" << endl; + if(argc == 1) +#if OS_LINUX || OS_CYGWIN + cerr << "Try 'zynaddsubfx --help' for command-line options." << endl; +#else //assuming windows + cerr << "Try 'zynaddsubfx -h' for command-line options.\n" << endl; #endif + /* Get the settings from the Config*/ SAMPLE_RATE = config.cfg.SampleRate; SOUND_BUFFER_SIZE = config.cfg.SoundBufferSize; OSCIL_SIZE = config.cfg.OscilSize; swaplr = config.cfg.SwapStereo; + /* Parse command-line options */ -#ifdef OS_LINUX +#if OS_LINUX || OS_CYGWIN struct option opts[] = { - { - "load", 2, NULL, 'l' - }, - { - "load-instrument", 2, NULL, 'L' - }, - { - "sample-rate", 2, NULL, 'r' - }, - { - "buffer-size", 2, NULL, 'b' - }, - { - "oscil-size", 2, NULL, 'o' - }, - { - "dump", 2, NULL, 'D' - }, - { - "swap", 2, NULL, 'S' - }, - { - "no-gui", 2, NULL, 'U' - }, - { - "not-use-jack", 2, NULL, 'A' - }, - { - "dummy", 2, NULL, 'Y' - }, - { - "help", 2, NULL, 'h' - }, - { - 0, 0, 0, 0 - } + {"load", 2, NULL, 'l'}, + {"load-instrument", 2, NULL, 'L'}, + {"sample-rate", 2, NULL, 'r'}, + {"buffer-size", 2, NULL, 'b'}, + {"oscil-size", 2, NULL, 'o'}, + {"dump", 2, NULL, 'D'}, + {"swap", 2, NULL, 'S'}, + {"no-gui", 2, NULL, 'U'}, + {"dummy", 2, NULL, 'Y'}, + {"help", 2, NULL, 'h'}, + {"output", 1, NULL, 'O'}, + {"input", 1, NULL, 'I'}, + {0, 0, 0, 0} }; #endif opterr = 0; int option_index = 0, opt, exitwithhelp = 0; - char loadfile[1001]; - ZERO(loadfile, 1001); - char loadinstrument[1001]; - ZERO(loadinstrument, 1001); + string loadfile, loadinstrument, input, output; while(1) { /**\todo check this process for a small memory leak*/ -#ifdef OS_LINUX - opt = getopt_long(argc, argv, "l:L:r:b:o:hSDUAY", opts, &option_index); +#if OS_LINUX || OS_CYGWIN + opt = getopt_long(argc, argv, "l:L:r:b:o:I:O:hSDUY", opts, &option_index); char *optarguments = optarg; -#else - opt = getopt(argc, argv, "l:L:r:b:o:hSDUAY", &option_index); +#elif OS_WINDOWS + opt = getopt(argc, argv, "l:L:r:b:o:I:O:hSDUY", &option_index); char *optarguments = &winoptarguments[0]; +#else +#error Undefined OS #endif +#define GETOP(x) if(optarguments) x = optarguments +#define GETOPNUM(x) if(optarguments) x = atoi(optarguments) + + if(opt == -1) break; - int tmp; switch(opt) { case 'h': exitwithhelp = 1; @@ -523,60 +335,36 @@ int main(int argc, char *argv[]) case 'U': noui = 1; break; - case 'A': -#ifdef JACKAUDIOOUT -#ifdef OSSAUDIOOUT - usejackit = false; -#endif -#endif - break; case 'l': - tmp = 0; - if(optarguments != NULL) - snprintf(loadfile, 1000, "%s", optarguments); - ; + GETOP(loadfile); break; case 'L': - tmp = 0; - if(optarguments != NULL) - snprintf(loadinstrument, 1000, "%s", optarguments); - ; + GETOP(loadinstrument); break; case 'r': - tmp = 0; - if(optarguments != NULL) - tmp = atoi(optarguments); - if(tmp >= 4000) - SAMPLE_RATE = tmp; - else { + GETOPNUM(SAMPLE_RATE); + if(SAMPLE_RATE < 4000) { cerr << "ERROR:Incorrect sample rate: " << optarguments << endl; exit(1); } break; case 'b': - tmp = 0; - if(optarguments != NULL) - tmp = atoi(optarguments); - if(tmp >= 2) - SOUND_BUFFER_SIZE = tmp; - else { + GETOPNUM(SOUND_BUFFER_SIZE); + if(SOUND_BUFFER_SIZE < 2) { cerr << "ERROR:Incorrect buffer size: " << optarguments << endl; exit(1); } break; case 'o': - tmp = 0; - if(optarguments != NULL) - tmp = atoi(optarguments); - OSCIL_SIZE = tmp; + int tmp; + if(optarguments) + OSCIL_SIZE = tmp = atoi(optarguments); if(OSCIL_SIZE < MAX_AD_HARMONICS * 2) OSCIL_SIZE = MAX_AD_HARMONICS * 2; OSCIL_SIZE = (int) pow(2, ceil(log(OSCIL_SIZE - 1.0) / log(2.0))); if(tmp != OSCIL_SIZE) { - cerr - << - "\nOSCIL_SIZE is wrong (must be 2^n) or too small. Adjusting to "; - cerr << OSCIL_SIZE << "." << endl; + cerr << "OSCIL_SIZE is wrong (must be 2^n) or too small. Adjusting to " + << OSCIL_SIZE << "." << endl; } break; case 'S': @@ -585,6 +373,12 @@ int main(int argc, char *argv[]) case 'D': dump.startnow(); break; + case 'I': + GETOP(input); + break; + case 'O': + GETOP(output); + break; case '?': cerr << "ERROR:Bad option or parameter.\n" << endl; exitwithhelp = 1; @@ -593,44 +387,29 @@ int main(int argc, char *argv[]) } if(exitwithhelp != 0) { - cout << "Usage: zynaddsubfx [OPTION]\n" << endl; - cout << " -h , --help \t\t\t\t display command-line help and exit" - << endl; - cout << " -l file, --load=FILE\t\t\t loads a .xmz file" << endl; - cout << " -L file, --load-instrument=FILE\t\t loads a .xiz file" - << endl; - cout << " -r SR, --sample-rate=SR\t\t set the sample rate SR" << endl; - cout - << " -b BS, --buffer-size=SR\t\t set the buffer size (granularity)" - << endl; - cout << " -o OS, --oscil-size=OS\t\t set the ADsynth oscil. size" - << endl; - cout << " -S , --swap\t\t\t\t swap Left <--> Right" << endl; - cout << " -D , --dump\t\t\t\t Dumps midi note ON/OFF commands" << endl; - cout - << " -U , --no-gui\t\t\t\t Run ZynAddSubFX without user interface" - << endl; -#ifdef JACKAUDIOOUT -#ifdef OSSAUDIOOUT - cout << " -A , --not-use-jack\t\t\t Use OSS/ALSA instead of JACK" - << endl; + cout << "Usage: zynaddsubfx [OPTION]\n\n" + << " -h , --help \t\t\t\t Display command-line help and exit\n" + << " -l file, --load=FILE\t\t\t Loads a .xmz file\n" + << " -L file, --load-instrument=FILE\t Loads a .xiz file\n" + << " -r SR, --sample-rate=SR\t\t Set the sample rate SR\n" + << " -b BS, --buffer-size=SR\t\t Set the buffer size (granularity)\n" + << " -o OS, --oscil-size=OS\t\t Set the ADsynth oscil. size\n" + << " -S , --swap\t\t\t\t Swap Left <--> Right\n" + << " -D , --dump\t\t\t\t Dumps midi note ON/OFF commands\n" + << " -U , --no-gui\t\t\t\t Run ZynAddSubFX without user interface\n" + << " -O , --output\t\t\t\t Set Output Engine\n" + << " -I , --input\t\t\t\t Set Input Engine" << endl; + +#if OS_WINDOWS + cout << "nWARNING: On Windows systems, only short comandline parameters works.\n" + << " eg. instead '--buffer-size=512' use '-b 512'" << endl; #endif -#endif -#ifdef OS_WINDOWS - cout - << - "\nWARNING: On Windows systems, only short comandline parameters works." - << endl; - cout << " eg. instead '--buffer-size=512' use '-b 512'" << endl; -#endif - cout << '\n' << endl; return 0; } - //--------- - initprogram(); +#if 0 //TODO update this code #ifdef USE_LASH #ifdef ALSAMIDIIN ALSAMidiIn *alsamidi = dynamic_cast<ALSAMidiIn *>(Midi); @@ -641,13 +420,14 @@ int main(int argc, char *argv[]) lash->setjackname(JACKgetname()); #endif #endif +#endif - if(strlen(loadfile) > 1) { - int tmp = master->loadXML(loadfile); + + if(!loadfile.empty()) { + int tmp = master->loadXML(loadfile.c_str()); if(tmp < 0) { - fprintf(stderr, - "ERROR:Could not load master file %s .\n", - loadfile); + cerr << "ERROR: Could not load master file " << loadfile + << "." << endl; exit(1); } else { @@ -656,11 +436,11 @@ int main(int argc, char *argv[]) } } - if(strlen(loadinstrument) > 1) { + if(!loadinstrument.empty()) { int loadtopart = 0; - int tmp = master->part[loadtopart]->loadXMLinstrument(loadinstrument); + int tmp = master->part[loadtopart]->loadXMLinstrument(loadinstrument.c_str()); if(tmp < 0) { - cerr << "ERROR:Could not load instrument file " + cerr << "ERROR: Could not load instrument file " << loadinstrument << '.' << endl; exit(1); } @@ -671,25 +451,27 @@ int main(int argc, char *argv[]) } -#if !(defined(NONEMIDIIN) || defined(WINMIDIIN) || defined(VSTMIDIIN)) - pthread_create(&thr1, NULL, thread1, NULL); -#endif + if(!input.empty()) { + if(!sysEngine->setInDefault(input)) { + cerr << "There is no input for " << input << endl; + exit(1); + } + cout << input << " selected." << endl; + } + if(!output.empty()) { + if(!sysEngine->setOutDefault(output)) { + cerr << "There is no output for " << output << endl; + exit(1); + } + cout << output << " selected." << endl; + } -#ifdef OSSAUDIOOUT -//!(defined(JACKAUDIOOUT)||defined(JACK_RTAUDIOOUT)||defined(PAAUDIOOUT)||defined(VSTAUDIOOUT)) - if(!usejackit) - pthread_create(&thr2, NULL, thread2, NULL); -#endif + //Run the Nio system + sysEngine->start(); //Drivers start your engines! + +#warning remove welcome message when system is out of beta + cout << "\nThanks for using the Nio system :)" << endl; - /*It is not working and I don't know why - //drop the suid-root permisions - #if !(defined(JACKAUDIOOUT)||defined(PAAUDIOOUT)||defined(VSTAUDIOOUT)|| (defined (WINMIDIIN)) ) - setuid(getuid()); - seteuid(getuid()); - // setreuid(getuid(),getuid()); - // setregid(getuid(),getuid()); - #endif - */ #ifndef DISABLE_GUI if(noui == 0) { ui = new MasterUI(master, &Pexitprogram); @@ -697,11 +479,12 @@ int main(int argc, char *argv[]) } #endif - pthread_create(&thr4, NULL, thread4, NULL); +// pthread_create(&thr4, NULL, thread4, NULL); #ifdef WINMIDIIN InitWinMidi(master); #endif + //TODO look into a conditional variable here, it seems to match usage while(Pexitprogram == 0) { #ifdef OS_LINUX usleep(100000);