Browse Source

Updated to latest SoLoud.

Brucey 3 years ago
parent
commit
47e1b9d7da
51 changed files with 3038 additions and 1370 deletions
  1. 2 0
      .gitignore
  2. 1 1
      README.md
  3. 1 1
      soloud.mod/common.bmx
  4. 1 1
      soloud.mod/file.bmx
  5. 14 4
      soloud.mod/soloud.bmx
  6. 8 0
      soloud.mod/soloud/AUTHORS
  7. 7 0
      soloud.mod/soloud/contrib/CMakeLists.txt
  8. 9 0
      soloud.mod/soloud/contrib/Configure.cmake
  9. 2 2
      soloud.mod/soloud/contrib/cmake/FindSDL2.cmake
  10. 47 0
      soloud.mod/soloud/contrib/gen_glue.cmake
  11. 28 1
      soloud.mod/soloud/contrib/src.cmake
  12. 1 1
      soloud.mod/soloud/demos/common/stb_image.h
  13. 7 1
      soloud.mod/soloud/demos/megademo/radiogaga.cpp
  14. 273 196
      soloud.mod/soloud/demos/piano/main.cpp
  15. 77 0
      soloud.mod/soloud/demos/piano/soloud_adsr.h
  16. 37 42
      soloud.mod/soloud/demos/piano/soloud_basicwave.cpp
  17. 9 9
      soloud.mod/soloud/demos/piano/soloud_basicwave.h
  18. 2 2
      soloud.mod/soloud/demos/piano/soloud_padsynth.cpp
  19. 12 0
      soloud.mod/soloud/docsrc/faq.mmd
  20. 8 0
      soloud.mod/soloud/include/soloud.h
  21. 2 1
      soloud.mod/soloud/include/soloud_bus.h
  22. 71 0
      soloud.mod/soloud/include/soloud_duckfilter.h
  23. 1 1
      soloud.mod/soloud/include/soloud_file.h
  24. 0 1
      soloud.mod/soloud/include/soloud_openmpt.h
  25. 1 0
      soloud.mod/soloud/include/soloud_wav.h
  26. 175 0
      soloud.mod/soloud/scripts/gen_beef.py
  27. 18 4
      soloud.mod/soloud/scripts/gen_cs.py
  28. 39 16
      soloud.mod/soloud/scripts/gen_python.py
  29. 2 3
      soloud.mod/soloud/src/audiosource/ay/sndbuffer.cpp
  30. 6 13
      soloud.mod/soloud/src/audiosource/ay/soloud_ay.cpp
  31. 3 1
      soloud.mod/soloud/src/audiosource/monotone/soloud_monotone.cpp
  32. 3 12
      soloud.mod/soloud/src/audiosource/openmpt/soloud_openmpt.cpp
  33. 12 1
      soloud.mod/soloud/src/audiosource/openmpt/soloud_openmpt_dll.c
  34. 6 13
      soloud.mod/soloud/src/audiosource/tedsid/soloud_tedsid.cpp
  35. 543 192
      soloud.mod/soloud/src/audiosource/wav/dr_flac.h
  36. 365 265
      soloud.mod/soloud/src/audiosource/wav/dr_mp3.h
  37. 565 299
      soloud.mod/soloud/src/audiosource/wav/dr_wav.h
  38. 2 1
      soloud.mod/soloud/src/audiosource/wav/soloud_wav.cpp
  39. 23 5
      soloud.mod/soloud/src/audiosource/wav/soloud_wavstream.cpp
  40. 82 53
      soloud.mod/soloud/src/audiosource/wav/stb_vorbis.c
  41. 23 1
      soloud.mod/soloud/src/backend/coreaudio/soloud_coreaudio.cpp
  42. 357 199
      soloud.mod/soloud/src/backend/miniaudio/miniaudio.h
  43. 5 2
      soloud.mod/soloud/src/backend/miniaudio/soloud_miniaudio.cpp
  44. 9 16
      soloud.mod/soloud/src/backend/opensles/soloud_opensles.cpp
  45. 22 0
      soloud.mod/soloud/src/core/soloud.cpp
  46. 1 1
      soloud.mod/soloud/src/core/soloud_audiosource.cpp
  47. 1 1
      soloud.mod/soloud/src/core/soloud_core_voicegroup.cpp
  48. 6 6
      soloud.mod/soloud/src/core/soloud_file.cpp
  49. 147 0
      soloud.mod/soloud/src/filter/soloud_duckfilter.cpp
  50. 1 1
      soloud.mod/soloud/src/filter/soloud_eqfilter.cpp
  51. 1 1
      soloud.mod/source.bmx

+ 2 - 0
.gitignore

@@ -4,3 +4,5 @@
 *.i
 *.i2
 commands.html
+
+.DS_Store

+ 1 - 1
README.md

@@ -126,7 +126,7 @@ SuperStrict
 
 Framework audio.AudioMiniAudio
 
-Local sound:TSound = LoadSound("music.ogg" | SOUND_STREAM)
+Local sound:TSound = LoadSound("music.ogg", SOUND_STREAM)
 Local channel:TChannel = PlaySound(sound)
 
 Repeat

+ 1 - 1
soloud.mod/common.bmx

@@ -1,4 +1,4 @@
-' Copyright (c) 2016-2020 Bruce A Henderson
+' Copyright (c) 2016-2022 Bruce A Henderson
 '
 ' This software is provided 'as-is', without any express or implied
 ' warranty. In no event will the authors be held liable for any damages

+ 1 - 1
soloud.mod/file.bmx

@@ -1,4 +1,4 @@
-' Copyright (c) 2016-2020 Bruce A Henderson
+' Copyright (c) 2016-2022 Bruce A Henderson
 '
 ' This software is provided 'as-is', without any express or implied
 ' warranty. In no event will the authors be held liable for any damages

+ 14 - 4
soloud.mod/soloud.bmx

@@ -1,4 +1,4 @@
-' Copyright (c) 2016-2020 Bruce A Henderson
+' Copyright (c) 2016-2022 Bruce A Henderson
 '
 ' This software is provided 'as-is', without any express or implied
 ' warranty. In no event will the authors be held liable for any damages
@@ -26,11 +26,13 @@ bbdoc: SoLoud audio.
 End Rem
 Module Audio.SoLoud
 
-ModuleInfo "Version: 1.02"
+ModuleInfo "Version: 1.03"
 ModuleInfo "License: zlib/libpng"
-ModuleInfo "Copyright: SoLoud - 2013-2020 Jari Komppa"
-ModuleInfo "Copyright: Wrapper - 2016-2020 Bruce A Henderson"
+ModuleInfo "Copyright: SoLoud - 2013-2021 Jari Komppa"
+ModuleInfo "Copyright: Wrapper - 2016-2022 Bruce A Henderson"
 
+ModuleInfo "History: 1.03"
+ModuleInfo "History: Update to latest SoLoud.1157475"
 ModuleInfo "History: 1.02"
 ModuleInfo "History: Update to latest SoLoud."
 ModuleInfo "History: Added TSLAy audio source."
@@ -55,6 +57,14 @@ ModuleInfo "CC_OPTS: -DWITH_MINIAUDIO"
 Import "file.bmx"
 Import "common.bmx"
 
+'
+' Implementation notes :
+'     Ay*() functions added to sound_c.cpp
+'     Openmpt*() functions moved to openmptloader.cpp in modloader.mod allowing us to optionally use openmpt.
+'
+'     soloud_miniaudio.cpp : Added use of our own ma_context instance, instead of NULL
+'
+
 Rem
 bbdoc: An instance of the Soloud player.
 End Rem

+ 8 - 0
soloud.mod/soloud/AUTHORS

@@ -32,3 +32,11 @@ Osman Turan https://osmanturan.com
 Samson Close https://github.com/qwertysam
 Bruce A Henderson https://github.com/woollybah
 Philip Bennefall https://github.com/blastbay/
+Sebastian Hartte https://github.com/shartte/
+Jean-Luc Mackail https://github.com/fuzzyquills
+HumanGamer https://github.com/HumanGamer/
+Ales Mlakar https://github.com/jazzbre
+lexander Yashin https://github.com/yashin-alexander
+Nils Duval https://github.com/nlsdvl
+JackRedstonia [email protected]
+David Bullock https://github.com/dwbullock

+ 7 - 0
soloud.mod/soloud/contrib/CMakeLists.txt

@@ -18,6 +18,9 @@ if (UNIX AND NOT WIN32 AND NOT APPLE)
 		mark_as_advanced (LIB_POSTFIX)
 	endif ()
 endif ()
+if (MSVC)
+	add_definitions (-D_CRT_SECURE_NO_WARNINGS)
+endif()
 if (NOT DEFINED LIB_POSTFIX)
 	set (LIB_POSTFIX "")
 endif ()
@@ -29,4 +32,8 @@ IF (SOLOUD_BUILD_DEMOS)
 	include (demos.cmake)
 endif ()
 
+IF (SOLOUD_GENERATE_GLUE)
+	include (gen_glue.cmake)
+endif ()
+
 include (InstallExport)

+ 9 - 0
soloud.mod/soloud/contrib/Configure.cmake

@@ -7,6 +7,9 @@ print_option_status (SOLOUD_DYNAMIC "Build dynamic library")
 option (SOLOUD_STATIC "Set to ON to build static SoLoud" ON)
 print_option_status (SOLOUD_STATIC "Build static library")
 
+option (SOLOUD_C_API "Set to ON to include the C API" OFF)
+print_option_status (SOLOUD_C_API "Build C API")
+
 # TODO:
 option (SOLOUD_BUILD_DEMOS "Set to ON for building demos" OFF)
 print_option_status (SOLOUD_BUILD_DEMOS "Build demos")
@@ -17,6 +20,9 @@ print_option_status (SOLOUD_BACKEND_NULL "NULL backend")
 option (SOLOUD_BACKEND_SDL2 "Set to ON for building SDL2 backend" ON)
 print_option_status (SOLOUD_BACKEND_SDL2 "SDL2 backend")
 
+option (SOLOUD_BACKEND_ALSA "Set to ON for building ALSA backend" OFF)
+print_option_status (SOLOUD_BACKEND_ALSA "ALSA backend")
+
 option (SOLOUD_BACKEND_COREAUDIO "Set to ON for building CoreAudio backend" OFF)
 print_option_status (SOLOUD_BACKEND_COREAUDIO "CoreAudio backend")
 
@@ -31,3 +37,6 @@ print_option_status (SOLOUD_BACKEND_WINMM "WINMM backend")
 
 option (SOLOUD_BACKEND_WASAPI "Set to ON for building WASAPI backend" OFF)
 print_option_status (SOLOUD_BACKEND_WASAPI "WASAPI backend")
+
+option (SOLOUD_GENERATE_GLUE "Set to ON for generating the Glue APIs" OFF)
+print_option_status (SOLOUD_GENERATE_GLUE "Generate Glue")

+ 2 - 2
soloud.mod/soloud/contrib/cmake/FindSDL2.cmake

@@ -76,10 +76,10 @@ SET(SDL2_SEARCH_PATHS
 	${SDL2_PATH}
 )
 
-FIND_PATH(SDL2_INCLUDE_DIR SDL2/SDL.h
+FIND_PATH(SDL2_INCLUDE_DIR SDL.h
 	HINTS
 	$ENV{SDL2DIR}
-	PATH_SUFFIXES include
+	PATH_SUFFIXES include include/SDL2
 	PATHS ${SDL2_SEARCH_PATHS}
 )
 

+ 47 - 0
soloud.mod/soloud/contrib/gen_glue.cmake

@@ -0,0 +1,47 @@
+
+# We need python to run the glue-code generators
+find_package (Python3 COMPONENTS Interpreter REQUIRED)
+
+# Create the executable that generates soloud_codegen.py
+add_executable (codegen ../src/tools/codegen/main.cpp)
+
+# Add a command to run the executable to generate the python file
+add_custom_command (OUTPUT "${CMAKE_CURRENT_SOURCE_DIR}/../scripts/soloud_codegen.py"
+        COMMAND codegen ARGS go
+        WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
+
+# Now we can run the actual Python code-generators, BUT we need to add a dependency on
+# soloud_codegen.py, or otherwise codegen.exe will not be run beforehand
+
+###############################################################################
+# C# API
+###############################################################################
+add_custom_command (OUTPUT "${CMAKE_CURRENT_SOURCE_DIR}/../glue/soloud.cs"
+        COMMAND "${Python3_EXECUTABLE}" "${CMAKE_CURRENT_SOURCE_DIR}/../scripts/gen_cs.py"
+        DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/../scripts/soloud_codegen.py"
+        DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/../scripts/gen_cs.py"
+        )
+add_custom_target (generate_glue_cs ALL DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/../glue/soloud.cs")
+install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/../glue/soloud.cs" DESTINATION glue)
+
+###############################################################################
+# Python API
+###############################################################################
+add_custom_command (OUTPUT "${CMAKE_CURRENT_SOURCE_DIR}/../glue/soloud.rb"
+        COMMAND "${Python3_EXECUTABLE}" "${CMAKE_CURRENT_SOURCE_DIR}/../scripts/gen_ruby.py"
+        DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/../scripts/soloud_codegen.py"
+        DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/../scripts/gen_ruby.py"
+        )
+add_custom_target (generate_glue_ruby ALL DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/../glue/soloud.rb")
+install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/../glue/soloud.rb" DESTINATION glue)
+
+###############################################################################
+# Ruby API
+###############################################################################
+add_custom_command (OUTPUT "${CMAKE_CURRENT_SOURCE_DIR}/../glue/soloud.py"
+        COMMAND "${Python3_EXECUTABLE}" "${CMAKE_CURRENT_SOURCE_DIR}/../scripts/gen_python.py"
+        DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/../scripts/soloud_codegen.py"
+        DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/../scripts/gen_python.py"
+        )
+add_custom_target(generate_glue_python ALL DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/../glue/soloud.py")
+install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/../glue/soloud.py" DESTINATION glue)

+ 28 - 1
soloud.mod/soloud/contrib/src.cmake

@@ -13,7 +13,6 @@ set (TARGET_HEADERS
 	${HEADER_PATH}/soloud_bassboostfilter.h
 	${HEADER_PATH}/soloud_biquadresonantfilter.h
 	${HEADER_PATH}/soloud_bus.h
-	${HEADER_PATH}/soloud_c.h
 	${HEADER_PATH}/soloud_dcremovalfilter.h
 	${HEADER_PATH}/soloud_echofilter.h
 	${HEADER_PATH}/soloud_error.h
@@ -166,6 +165,22 @@ if (SOLOUD_BACKEND_SDL2)
 
 endif()
 
+if (SOLOUD_BACKEND_ALSA)                     
+    add_definitions (-DWITH_ALSA)                
+                                           
+    set (BACKENDS_SOURCES              
+        ${BACKENDS_SOURCES} 
+        ${BACKENDS_PATH}/alsa/soloud_alsa.cpp
+    )                                              
+
+    find_library (ALSA_LIBRARY asound)
+    set (LINK_LIBRARIES
+        ${LINK_LIBRARIES}
+        ${ALSA_LIBRARY}
+    )
+endif()
+
+
 if (SOLOUD_BACKEND_COREAUDIO)
 	if (NOT APPLE)
 		message (FATAL_ERROR "CoreAudio backend can be enabled only on Apple!")
@@ -259,6 +274,18 @@ set (TARGET_SOURCES
 	${FILTERS_SOURCES}
 )
 
+if (SOLOUD_C_API)
+	set (TARGET_SOURCES
+		${TARGET_SOURCES}
+		${SOURCE_PATH}/c_api/soloud.def
+		${SOURCE_PATH}/c_api/soloud_c.cpp
+	)
+	set (TARGET_HEADERS
+		${TARGET_HEADERS}
+		${HEADER_PATH}/soloud_c.h
+	)
+endif()
+
 if (SOLOUD_DYNAMIC)
 	add_library(${TARGET_NAME} ${TARGET_SOURCES})
 endif ()

+ 1 - 1
soloud.mod/soloud/demos/common/stb_image.h

@@ -6359,7 +6359,7 @@ static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, i
       memset( g->history, 0x00, g->w * g->h );        // pixels that were affected previous frame
       first_frame = 1; 
    } else {
-      // second frame - how do we dispoase of the previous one?
+      // second frame - how do we dispose of the previous one?
       dispose = (g->eflags & 0x1C) >> 2; 
       pcount = g->w * g->h; 
 

+ 7 - 1
soloud.mod/soloud/demos/megademo/radiogaga.cpp

@@ -33,6 +33,7 @@ freely, subject to the following restrictions:
 #include "soloud.h"
 #include "soloud_speech.h"
 #include "soloud_wav.h"
+#include "soloud_duckfilter.h"
 
 namespace radiogaga
 {
@@ -50,6 +51,8 @@ namespace radiogaga
 	SoLoud::handle gSpeechqueuehandle;
 	SoLoud::handle gIntrohandle;
 
+	SoLoud::DuckFilter gDuckFilter;
+
 	const char *phrase[19] =
 	{
 		"............................",
@@ -78,7 +81,7 @@ namespace radiogaga
 		gSoloud.init();
 		gSoloud.setVisualizationEnable(1);
 
-		gSoloud.play(gSpeechBus, 0.5f);
+		SoLoud::handle speechBusHandle = gSoloud.play(gSpeechBus, 0.5f);
 
 		gSoloud.play(gMusicBus);
 
@@ -113,6 +116,9 @@ namespace radiogaga
 		gSpeechBus.setVisualizationEnable(1);
 		gMusicBus.setVisualizationEnable(1);
 
+		gDuckFilter.setParams(&gSoloud, speechBusHandle);
+		gMusicBus.setFilter(0, &gDuckFilter);
+
 		gIntro.setText("Eat, Sleep, Rave, Repeat");
 		gIntro.setLooping(true);
 		gIntro.setLoopPoint(1.45f);

+ 273 - 196
soloud.mod/soloud/demos/piano/main.cpp

@@ -1,6 +1,6 @@
 /*
 SoLoud audio engine
-Copyright (c) 2013-2015 Jari Komppa
+Copyright (c) 2013-2021 Jari Komppa
 
 This software is provided 'as-is', without any express or implied
 warranty. In no event will the authors be held liable for any damages
@@ -32,12 +32,20 @@ freely, subject to the following restrictions:
 #include "soloud_wav.h"
 #include "soloud_padsynth.h"
 #include "soloud_basicwave.h"
+#include "soloud_ay.h"
+
+
+#include "soloud_bassboostfilter.h"
+#include "soloud_biquadresonantfilter.h"
+#include "soloud_dcremovalfilter.h"
 #include "soloud_echofilter.h"
-#include "soloud_speech.h"
 #include "soloud_fftfilter.h"
-#include "soloud_biquadresonantfilter.h"
+#include "soloud_flangerfilter.h"
+#include "soloud_freeverbfilter.h"
 #include "soloud_lofifilter.h"
-#include "soloud_dcremovalfilter.h"
+#include "soloud_robotizefilter.h"
+#include "soloud_waveshaperfilter.h"
+#include "soloud_eqfilter.h"
 
 #include "RtMidi.h"
 
@@ -47,25 +55,35 @@ struct plonked
 	float mRel;
 };
 
-SoLoud::Speech gSpeech;
-SoLoud::Soloud gSoloud;			// SoLoud engine core
-SoLoud::Basicwave gWave;		// Simple wave audio source
+SoLoud::Soloud gSoloud;
+SoLoud::Basicwave gWave;
+SoLoud::Ay gAy;
 SoLoud::Wav gLoadedWave;
 SoLoud::Wav gPadsynth;
-SoLoud::EchoFilter gEchoFilter;		// Simple echo filter
-SoLoud::BiquadResonantFilter gBQRFilter;   // BQR filter
 SoLoud::Bus gBus;
-SoLoud::FFTFilter gFftFilter;
-SoLoud::LofiFilter gLofiFilter;
-SoLoud::DCRemovalFilter gDCRemovalFilter;
+SoLoud::Filter* gFilter[11];
+int gFilterSelect[4] = { 0, 0, 0, 0 };
 
 float gAttack = 0.02f;
 float gRelease = 0.5f;
+int gSynthEngine = 0;
+bool gSynthWindow = true;
+bool gInfoWindow = true;
+bool gFilterWindow = false;
+
+float harm[7] = { 0.7f, 0.3f, 0.2f, 1.7f, 0.4f, 1.3f, 0.2f };
+float bw = 0.25f;
+float bws = 1.0f;
+
+float filter_param0[4] = { 0, 0, 1, 1 };
+float filter_param1[4] = { 8000, 0, 1000, 0 };
+float filter_param2[4] = { 3, 0, 2, 0 };
+
+int bushandle;
+
 
 plonked gPlonked[128] = { 0 };
-int gWaveSelect = 2;
-int gFilterSelect = 0;
-int gEcho = 0;
+int gWaveSelect = SoLoud::Soloud::WAVE_SQUARE;
 char *gInfo = (char*)"";
 
 RtMidiIn *midi = NULL;
@@ -80,21 +98,27 @@ void plonk(float rel, float vol = 0x50)
 	vol *= vol;
 	float pan = (float)sin(DemoTick() * 0.0234);
 	int handle;
-	if (gWaveSelect < 4)
+	switch (gSynthEngine)
 	{
+	default:
+	case 0:
+		gWave.setFreq(440.0f * rel * 2);
 		handle = gBus.play(gWave, 0);
-	}
-	else
-	{
-		if (gWaveSelect == 4)
-			handle = gBus.play(gLoadedWave, 0);
-		if (gWaveSelect == 5)
-		{
-			handle = gBus.play(gPadsynth, 0);
-		}
-	}
+		break;
+	case 1:
+		handle = gBus.play(gPadsynth, 0);
+		gSoloud.setRelativePlaySpeed(handle, 2 * rel);
+		break;
+	case 2:
+		handle = gBus.play(gLoadedWave, 0);
+		gSoloud.setRelativePlaySpeed(handle, 2 * rel);
+		break;
+	case 3:
+		gWave.setFreq(440.0f * rel * 2, true);
+		handle = gBus.play(gWave, 0);
+		break;
+	}	
 	gSoloud.fadeVolume(handle, vol, gAttack);
-	gSoloud.setRelativePlaySpeed(handle, 2 * rel);
 	gPlonked[i].mHandle = handle;
 	gPlonked[i].mRel = rel;
 }
@@ -109,35 +133,12 @@ void unplonk(float rel)
 	gPlonked[i].mHandle = 0;
 }
 
-void replonk(float vol = 0x50)
-{
-	int i = 0;
-	while (i < 128 && gPlonked[i].mHandle != 0) i++;
-	if (i == 128) return;
-
-	vol = (vol + 10) / (float)(0x7f + 10);
-	vol *= vol;
-	for (i = 0; i < 128; i++)
-	{
-		if (gPlonked[i].mHandle != 0)
-		{	
-			gSoloud.fadeVolume(gPlonked[i].mHandle, vol, 0.1);
-		}
-	}
-}
 
 void say(const char *text)
 {
 	gInfo = (char*)text;
-	gSpeech.setText(text);
-	gSoloud.play(gSpeech, 4);
 }
 
-float harm[7] = { 0.7f, 0.3f, 0.2f, 1.7f, 0.4f, 1.3f, 0.2f };
-float bw = 0.25f;
-float bws = 1.0f;
-
-int bushandle;
 
 void midicallback(double deltatime, std::vector< unsigned char > *message, void *userData)
 {
@@ -158,11 +159,6 @@ void midicallback(double deltatime, std::vector< unsigned char > *message, void
 	{
 		unplonk((float)pow(0.943875f, 0x3c - (*message)[1]));
 	}
-	// aftertouch
-	if (((*message)[0] & 0xf0) == 0xd0)
-	{
-		replonk((float)(*message)[1]);
-	}
 }
 
 int DemoEntry(int argc, char *argv[])
@@ -192,34 +188,212 @@ int DemoEntry(int argc, char *argv[])
 	gSoloud.init(SoLoud::Soloud::CLIP_ROUNDOFF | SoLoud::Soloud::ENABLE_VISUALIZATION);
 	gSoloud.setGlobalVolume(0.75);
 	gSoloud.setPostClipScaler(0.75);
-	//	gBus.setFilter(0, &gBQRFilter);
-	//	gBus.setFilter(1, &gFilter);
-	gEchoFilter.setParams(0.5f, 0.5f);
 	bushandle = gSoloud.play(gBus);
 
-	
-
-	gSpeech.setFilter(1, &gFftFilter);
 
-	gSpeech.setText(". . . . . . . . . . . . . . . Use keyboard to play!");
-	gInfo = (char*)"Use keyboard to play!";
-	gSoloud.play(gSpeech, 4);
+	gInfo = (char*)"Use keyboard or midi to play!";
 
 	gLoadedWave.load("audio/AKWF_c604_0024.wav");
 	gLoadedWave.setLooping(1);
 
-	gBus.setFilter(0, &gLofiFilter);
-	gBus.setFilter(1, &gEchoFilter);
-	gBus.setFilter(3, &gDCRemovalFilter);
-
 	SoLoud::generatePadsynth(gPadsynth, 7, harm, bw, bws);
 
+	gFilter[0] = new SoLoud::BassboostFilter;
+	gFilter[1] = new SoLoud::BiquadResonantFilter;
+	gFilter[2] = new SoLoud::DCRemovalFilter;
+	gFilter[3] = new SoLoud::EchoFilter;
+	gFilter[4] = new SoLoud::FFTFilter;
+	gFilter[5] = new SoLoud::FlangerFilter;
+	gFilter[6] = new SoLoud::FreeverbFilter;
+	gFilter[7] = new SoLoud::LofiFilter;
+	gFilter[8] = new SoLoud::RobotizeFilter;
+	gFilter[9] = new SoLoud::WaveShaperFilter;
+	gFilter[10] = new SoLoud::EqFilter;
+
 	return 0;
 }
 
-float filter_param0[4] = { 0, 0, 1, 1 };
-float filter_param1[4] = { 8000, 0, 1000, 0 };
-float filter_param2[4] = { 3, 0, 2, 0 };
+
+void waveform_window()
+{
+	ONCE(ImGui::SetNextWindowPos(ImVec2(320, 20)));
+	ONCE(ImGui::SetNextWindowSize(ImVec2(200, 350)));
+	ImGui::Begin("Waveform");
+
+	if (ImGui::Combo("Wave", &gWaveSelect,
+		"Square wave\x00"
+		"Saw wave\x00"
+		"Sine wave\x00"
+		"Triangle wave\x00"
+		"Bounce wave\x00"
+		"Jaws wave\x00"
+		"Humps wave\x00"
+		"Antialized square wave\x00"
+		"Antialiazed sawe wave\x00"
+		"\x00"))
+	{
+		gWave.setWaveform(gWaveSelect);
+	}
+
+	ImGui::DragFloat("Attack", &gWave.mADSR.mA, 0.01f);
+	ImGui::DragFloat("Decay", &gWave.mADSR.mD, 0.01f);
+	ImGui::DragFloat("Sustain", &gWave.mADSR.mS, 0.01f);
+	ImGui::DragFloat("Release", &gRelease, 0.01f);
+
+	ImGui::End();
+}
+
+void superwave_window()
+{
+	ONCE(ImGui::SetNextWindowPos(ImVec2(320, 20)));
+	ONCE(ImGui::SetNextWindowSize(ImVec2(200, 350)));
+	ImGui::Begin("Superwave");
+
+	ImGui::DragFloat("Scale", &gWave.mSuperwaveScale, 0.01f);
+	ImGui::DragFloat("Detune", &gWave.mSuperwaveDetune, 0.001f);
+
+	if (ImGui::Combo("Wave", &gWaveSelect,
+		"Square wave\x00"
+		"Saw wave\x00"
+		"Sine wave\x00"
+		"Triangle wave\x00"
+		"Bounce wave\x00"
+		"Jaws wave\x00"
+		"Humps wave\x00"
+		"Antialized square wave\x00"
+		"Antialiazed sawe wave\x00"
+		"\x00"))
+	{
+		gWave.setWaveform(gWaveSelect);
+	}
+
+
+	ImGui::DragFloat("Attack", &gWave.mADSR.mA, 0.01f);
+	ImGui::DragFloat("Decay", &gWave.mADSR.mD, 0.01f);
+	ImGui::DragFloat("Sustain", &gWave.mADSR.mS, 0.01f);
+	ImGui::DragFloat("Release", &gRelease, 0.01f);
+
+	ImGui::End();
+}
+
+void info_window()
+{
+	float* buf = gSoloud.getWave();
+	float* fft = gSoloud.calcFFT();
+
+	ONCE(ImGui::SetNextWindowPos(ImVec2(520, 20)));
+	ImGui::Begin("Output");
+	ImGui::PlotLines("##Wave", buf, 256, 0, "Wave", -1, 1, ImVec2(264, 80));
+	ImGui::PlotHistogram("##FFT", fft, 256 / 2, 0, "FFT", 0, 10, ImVec2(264, 80), 8);
+	ImGui::Text("Active voices     : %d", gSoloud.getActiveVoiceCount());
+	ImGui::Text("1 2 3   5 6   8 9 0");
+	ImGui::Text(" Q W E R T Y U I O P");
+	ImGui::Text(gInfo);
+	ImGui::End();
+}
+
+void padsynth_window()
+{
+	ONCE(ImGui::SetNextWindowPos(ImVec2(320, 20)));
+	ONCE(ImGui::SetNextWindowSize(ImVec2(200, 350)));
+	ImGui::Begin("Padsynth");
+	{
+		int changed = 0;
+		if (ImGui::DragFloat("Harmonic 1", &harm[0], 0.1f)) changed = 1;
+		if (ImGui::DragFloat("Harmonic 2", &harm[1], 0.1f)) changed = 1;
+		if (ImGui::DragFloat("Harmonic 3", &harm[2], 0.1f)) changed = 1;
+		if (ImGui::DragFloat("Harmonic 4", &harm[3], 0.1f)) changed = 1;
+		if (ImGui::DragFloat("Harmonic 5", &harm[4], 0.1f)) changed = 1;
+		if (ImGui::DragFloat("Harmonic 6", &harm[5], 0.1f)) changed = 1;
+		if (ImGui::DragFloat("Harmonic 7", &harm[6], 0.1f)) changed = 1;
+		if (ImGui::DragFloat("Bandwidth", &bw, 0.1f)) changed = 1;
+		if (ImGui::DragFloat("Bandwidth scale", &bws, 0.1f)) changed = 1;
+		if (changed)
+			SoLoud::generatePadsynth(gPadsynth, 5, harm, bw, bws);
+	}
+	ImGui::End();
+}
+
+void filter_window()
+{
+	ONCE(ImGui::SetNextWindowPos(ImVec2(320, 20)));
+	ONCE(ImGui::SetNextWindowSize(ImVec2(200, 350)));
+	ImGui::Begin("Filters");
+	for (int filterindex = 0; filterindex < 4; filterindex++)
+	{
+		if (filterindex != 0)
+			ImGui::Separator();
+
+		char* label[4] = { "Filter 1", "Filter 2", "Filter 3", "Filter 4" };
+
+		if (ImGui::Combo(label[filterindex], &gFilterSelect[filterindex],
+			"None\x00"
+			"BassboostFilter\x00"
+			"BiquadResonantFilter\x00"
+			"DCRemovalFilter\x00"
+			"EchoFilter\x00"
+			"FFTFilter\x00"
+			"FlangerFilter\x00"
+			"FreeverbFilter\x00"
+			"LofiFilter\x00"
+			"RobotizeFilter\x00"
+			"WaveShaperFilter\x00"
+			"EqFilter\x00\x00"))
+		{
+			if (gFilterSelect[filterindex])
+				gSoloud.setGlobalFilter(filterindex, gFilter[gFilterSelect[filterindex] - 1]);
+			else
+				gSoloud.setGlobalFilter(filterindex, 0);
+		}
+
+		if (gFilterSelect[filterindex] != 0)
+		{
+			SoLoud::Filter* f = gFilter[gFilterSelect[filterindex] - 1];
+			int count = f->getParamCount();
+			for (int i = 0; i < count; i++)
+			{
+				int filtertype = f->getParamType(i);
+				float filtermin = f->getParamMin(i);
+				float filtermax = f->getParamMax(i);
+
+				if (filtertype == SoLoud::Filter::INT_PARAM)
+				{
+					int v = (int)gSoloud.getFilterParameter(0, filterindex, i);
+					char temp[128];
+					sprintf(temp, "%s##%d-%d", f->getParamName(i), filterindex, i);
+					if (ImGui::SliderInt(temp, &v, (int)filtermin, (int)filtermax))
+					{
+						gSoloud.setFilterParameter(0, filterindex, i, (float)v);
+					}
+				}
+
+				if (filtertype == SoLoud::Filter::FLOAT_PARAM)
+				{
+					float v = gSoloud.getFilterParameter(0, filterindex, i);
+					char temp[128];
+					sprintf(temp, "%s##%d-%d", f->getParamName(i), filterindex, i);
+					if (ImGui::SliderFloat(temp, &v, filtermin, filtermax))
+					{
+						gSoloud.setFilterParameter(0, filterindex, i, v);
+					}
+				}
+
+				if (filtertype == SoLoud::Filter::BOOL_PARAM)
+				{
+					float v = gSoloud.getFilterParameter(0, filterindex, i);
+					bool bv = v > 0.5f;
+					char temp[128];
+					sprintf(temp, "%s##%d-%d", f->getParamName(i), filterindex, i);
+					if (ImGui::Checkbox(temp, &bv))
+					{
+						gSoloud.setFilterParameter(0, filterindex, i, bv ? 1.0f : 0.0f);
+					}
+				}
+			}
+		}
+	}
+	ImGui::End();
+}
 
 void DemoMainloop()
 {
@@ -257,139 +431,42 @@ void DemoMainloop()
 
 	DemoUpdateStart();
 
-	float *buf = gSoloud.getWave();
-	float *fft = gSoloud.calcFFT();
-
-	ONCE(ImGui::SetNextWindowPos(ImVec2(320, 20)));
-	ONCE(ImGui::SetNextWindowSize(ImVec2(200, 350)));
-	ImGui::Begin("Padsynth");
+	ONCE(ImGui::SetNextWindowPos(ImVec2(20, 20)));
+	ONCE(ImGui::SetNextWindowSize(ImVec2(300, 350)));
+	ImGui::Begin("Master Control");	
+	if (ImGui::Combo("Synth engine", &gSynthEngine,
+		"Basic wave\x00"
+		"Padsynth\x00"
+		"Basic sample\x00"
+		"Superwave\x00"
+		"\x00"))
 	{
-		int changed = 0;
-		if (ImGui::DragFloat("Harmonic 1", &harm[0], 0.1f)) changed = 1;
-		if (ImGui::DragFloat("Harmonic 2", &harm[1], 0.1f)) changed = 1;
-		if (ImGui::DragFloat("Harmonic 3", &harm[2], 0.1f)) changed = 1;
-		if (ImGui::DragFloat("Harmonic 4", &harm[3], 0.1f)) changed = 1;
-		if (ImGui::DragFloat("Harmonic 5", &harm[4], 0.1f)) changed = 1;
-		if (ImGui::DragFloat("Harmonic 6", &harm[5], 0.1f)) changed = 1;
-		if (ImGui::DragFloat("Harmonic 7", &harm[6], 0.1f)) changed = 1;
-		if (ImGui::DragFloat("Bandwidth", &bw, 0.1f)) changed = 1;
-		if (ImGui::DragFloat("Bandwidth scale", &bws, 0.1f)) changed = 1;
-		if (changed)
-			SoLoud::generatePadsynth(gPadsynth, 5, harm, bw, bws);
 	}
-	ImGui::End();
 
-	ONCE(ImGui::SetNextWindowPos(ImVec2(500, 20)));
-	ImGui::Begin("Output");
-	ImGui::PlotLines("##Wave", buf, 256, 0, "Wave", -1, 1, ImVec2(264, 80));
-	ImGui::PlotHistogram("##FFT", fft, 256 / 2, 0, "FFT", 0, 10, ImVec2(264, 80), 8);
-	ImGui::Text("Active voices     : %d", gSoloud.getActiveVoiceCount());
-	ImGui::Text("1 2 3   5 6   8 9 0");		
-	ImGui::Text(" Q W E R T Y U I O P");
-	ImGui::Text(gInfo);
-	ImGui::End();
+	ImGui::Checkbox("Synth window", &gSynthWindow);
+	ImGui::Checkbox("Info window", &gInfoWindow);
+	ImGui::Checkbox("Filter window", &gFilterWindow);
 
-	ONCE(ImGui::SetNextWindowPos(ImVec2(20, 20)));
-	ONCE(ImGui::SetNextWindowSize(ImVec2(300, 350)));
-	ImGui::Begin("Control");
+	if (gInfoWindow)
+		info_window();
 
-	if (ImGui::CollapsingHeader("Waveform",0,true,true))
-	{
-		if (ImGui::RadioButton("Sine", gWaveSelect == 0))
-		{
-			gWaveSelect = 0;
-			gWave.setWaveform(SoLoud::Basicwave::SINE);
-			say("Sine wave");
-		}
-		if (ImGui::RadioButton("Triangle", gWaveSelect == 1))
-		{
-			gWaveSelect = 1;
-			gWave.setWaveform(SoLoud::Basicwave::TRIANGLE);
-			say("Triangle wave");
-		}
-		if (ImGui::RadioButton("Square", gWaveSelect == 2))
-		{
-			gWaveSelect = 2;
-			gWave.setWaveform(SoLoud::Basicwave::SQUARE);
-			say("Square wave");
-		}
-		if (ImGui::RadioButton("Saw", gWaveSelect == 3))
-		{
-			gWaveSelect = 3;
-			gWave.setWaveform(SoLoud::Basicwave::SAW);
-			say("Saw wave");
-		}
-		if (ImGui::RadioButton("Looping sample", gWaveSelect == 4))
-		{
-			gWaveSelect = 4;
-			say("Looping sample");
-		}
-		if (ImGui::RadioButton("Padsynth", gWaveSelect == 5))
-		{
-			gWaveSelect = 5;
-			say("Padsynth");
-		}
-	}
-		
-	if (ImGui::CollapsingHeader("BQRFilter",0,true,true))
+	if (gFilterWindow)
+		filter_window();
+
+	if (gSynthWindow)
 	{
-		if (ImGui::RadioButton("None", gFilterSelect == 0))
-		{
-			gFilterSelect = 0;
-			gBus.setFilter(2, 0);
-			say("Filter disabled");
-		}
-		if (ImGui::RadioButton("Lowpass", gFilterSelect == 1))
-		{
-			gFilterSelect = 1;
-			gBQRFilter.setParams(SoLoud::BiquadResonantFilter::LOWPASS, 1000, 2);
-			gBus.setFilter(2, &gBQRFilter);
-			say("Low pass filter");
-		}
-		if (ImGui::RadioButton("Highpass", gFilterSelect == 2))
-		{
-			gFilterSelect = 2;
-			gBQRFilter.setParams(SoLoud::BiquadResonantFilter::HIGHPASS, 1000, 2);
-			gBus.setFilter(2, &gBQRFilter);
-			say("High pass filter");
-		}
-		if (ImGui::RadioButton("Bandpass", gFilterSelect == 3))
-		{
-			gFilterSelect = 3;
-			gBQRFilter.setParams(SoLoud::BiquadResonantFilter::BANDPASS, 1000, 2);
-			gBus.setFilter(2, &gBQRFilter);
-			say("Band pass filter");
-		}
-		ImGui::SliderFloat("Wet##4", &filter_param0[2], 0, 1);
-		filter_param1[2] = gSoloud.getFilterParameter(bushandle, 2, 2);
-		if (ImGui::SliderFloat("Frequency##4", &filter_param1[2], 0, 8000))
+		switch (gSynthEngine)
 		{
-			gSoloud.setFilterParameter(bushandle, 2, 2, filter_param1[2]);
+		case 0:
+			waveform_window();
+			break;
+		case 1:
+			padsynth_window();
+			break;
+		case 3:
+			superwave_window();
+			break;
 		}
-		if (ImGui::SliderFloat("Resonance##4", &filter_param2[2], 1, 20))
-		{
-			gSoloud.setFilterParameter(bushandle, 2, 3, filter_param2[2]);
-		}
-		if (ImGui::Button("Oscillate +/- 1kHz"))
-		{
-			float from = filter_param1[2] - 500;
-			if (from < 0) from = 0;
-			gSoloud.oscillateFilterParameter(bushandle, 2, 2, from, from + 1000, 1);
-		}
-	}
-	if (ImGui::CollapsingHeader("Lofi filter", 0, true, true))
-	{
-		ImGui::SliderFloat("Wet##2", &filter_param0[0], 0, 1);
-		ImGui::SliderFloat("Rate##2", &filter_param1[0], 1000, 8000);
-		ImGui::SliderFloat("Bit depth##2", &filter_param2[0], 0, 8);
-	}
-	if (ImGui::CollapsingHeader("Echo filter", 0, true, true))
-	{
-		ImGui::SliderFloat("Wet##3", &filter_param0[1], 0, 1);
-	}
-	if (ImGui::CollapsingHeader("DC Removal filter", 0, true, true))
-	{
-		ImGui::SliderFloat("Wet##1", &filter_param0[3], 0, 1);
 	}
 
 

+ 77 - 0
soloud.mod/soloud/demos/piano/soloud_adsr.h

@@ -0,0 +1,77 @@
+/*
+SoLoud audio engine
+Copyright (c) 2013-2021 Jari Komppa
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+   1. The origin of this software must not be misrepresented; you must not
+   claim that you wrote the original software. If you use this software
+   in a product, an acknowledgment in the product documentation would be
+   appreciated but is not required.
+
+   2. Altered source versions must be plainly marked as such, and must not be
+   misrepresented as being the original software.
+
+   3. This notice may not be removed or altered from any source
+   distribution.
+*/
+
+#ifndef ADSR_H
+#define ADSR_H
+
+#include "soloud.h"
+
+namespace SoLoud
+{
+	class ADSR
+	{
+	public:
+		float mA, mD, mS, mR;
+
+		ADSR()
+		{
+			mA = 0.0f;
+			mD = 0.0f;
+			mS = 1.0f;
+			mR = 0.0f;
+		}
+
+		ADSR(float aA, float aD, float aS, float aR)
+		{
+			mA = aA;
+			mD = aD;
+			mS = aS;
+			mR = aR;
+		}
+		
+		float val(float aT, float aRelTime)
+		{
+			if (aT < mA)
+			{
+				return aT / mA;
+			}
+			aT -= mA;
+			if (aT < mD)
+			{
+				return 1.0f - ((aT / mD)) * (1.0f - mS);
+			}
+			aT -= mD;
+			if (aT < aRelTime)
+				return mS;
+			aT -= aRelTime;
+			if (aT >= mR)
+			{
+				return 0.0f;
+			}
+			return (1.0f - aT / mR) * mS;
+		}
+	};
+};
+
+#endif

+ 37 - 42
soloud.mod/soloud/demos/piano/soloud_basicwave.cpp

@@ -1,6 +1,6 @@
 /*
 SoLoud audio engine
-Copyright (c) 2013-2014 Jari Komppa
+Copyright (c) 2013-2021 Jari Komppa
 
 This software is provided 'as-is', without any express or implied
 warranty. In no event will the authors be held liable for any damages
@@ -23,13 +23,8 @@ freely, subject to the following restrictions:
 */
 
 #include "soloud_basicwave.h"
+#include "soloud_misc.h"
 
-static float my_fabs(float x)
-{
-    if (x < 0)
-        return -x;
-    return x;
-}
 
 namespace SoLoud
 {
@@ -38,48 +33,39 @@ namespace SoLoud
 	{
 		mParent = aParent;
 		mOffset = 0;
+		mFreq = aParent->mFreq;
+		mT = 0;
 	}
 
 	unsigned int BasicwaveInstance::getAudio(float *aBuffer, unsigned int aSamplesToRead, unsigned int aBufferSize)
 	{
 		unsigned int i;
-		switch (mParent->mWaveform)
+		int waveform = mParent->mWaveform;
+		float d = 1.0f / mSamplerate;
+		if (!mParent->mSuperwave)
 		{
-			case Basicwave::SINE:
-				for (i = 0; i < aSamplesToRead; i++)
-				{
-					aBuffer[i] = (float)sin(mParent->mFreq * mOffset * M_PI * 2);
-					mOffset++;
-				}
-				break;
-			case Basicwave::SAW:
-				for (i = 0; i < aSamplesToRead; i++)
-				{
-					aBuffer[i] = (1 - (float)fmod(mParent->mFreq * mOffset, 1)) * 2 - 1;
-					mOffset++;
-				}
-				break;				
-			case Basicwave::INVERSESAW:
-				for (i = 0; i < aSamplesToRead; i++)
-				{
-					aBuffer[i] = ((float)fmod(mParent->mFreq * mOffset, 1)) * 2 - 1;
-					mOffset++;
-				}
-				break;				
-			case Basicwave::SQUARE:
-				for (i = 0; i < aSamplesToRead; i++)
-				{
-					aBuffer[i] = ((float)fmod(mParent->mFreq * mOffset, 1.0f) > 0.5f) ? -1.0f : 1.0f;
-					mOffset++;
-				}
-				break;
-			case Basicwave::TRIANGLE:
-				for (i = 0; i < aSamplesToRead; i++)
+			for (i = 0; i < aSamplesToRead; i++)
+			{
+				aBuffer[i] = SoLoud::Misc::generateWaveform(waveform, (float)fmod(mFreq * (float)mOffset, 1.0f)) * mParent->mADSR.val(mT, 10000000000000.0f);
+				mOffset++;
+				mT += d;
+			}
+		}
+		else
+		{
+			for (i = 0; i < aSamplesToRead; i++)
+			{
+				aBuffer[i] = SoLoud::Misc::generateWaveform(waveform, (float)fmod(mFreq * (float)mOffset, 1.0f)) * mParent->mADSR.val(mT, 10000000000000.0f);
+				float f = mFreq * (float)mOffset;
+				for (int j = 0; j < 3; j++)
 				{
-					aBuffer[i] = my_fabs(0.5f - (float)fmod(mParent->mFreq * mOffset, 1)) * 4 - 1;
-					mOffset++;
+					f *= 2;
+					aBuffer[i] += SoLoud::Misc::generateWaveform(waveform, (float)fmod(mParent->mSuperwaveDetune * f, 1.0f)) * mParent->mADSR.val(mT, 10000000000000.0f) * mParent->mSuperwaveScale;
 				}
-				break;
+				mOffset++;
+				mT += d;
+			}
+
 		}
 		return aSamplesToRead;
 	}
@@ -93,7 +79,10 @@ namespace SoLoud
 	Basicwave::Basicwave()
 	{
 		setSamplerate(44100);
-		mWaveform = SQUARE;
+		mWaveform = SoLoud::Soloud::WAVE_SQUARE;
+		mSuperwave = false;
+		mSuperwaveScale = 0.25f;
+		mSuperwaveDetune = 1.0f;
 	}
 
 	Basicwave::~Basicwave()
@@ -107,6 +96,12 @@ namespace SoLoud
 		mFreq = (float)(440 / mBaseSamplerate);
 	}
 
+	void Basicwave::setFreq(float aFreq, bool aSuperwave)
+	{
+		mFreq = aFreq / mBaseSamplerate;
+		mSuperwave = aSuperwave;
+	}
+
 	void Basicwave::setWaveform(int aWaveform)
 	{
 		mWaveform = aWaveform;

+ 9 - 9
soloud.mod/soloud/demos/piano/soloud_basicwave.h

@@ -1,6 +1,6 @@
 /*
 SoLoud audio engine
-Copyright (c) 2013 Jari Komppa
+Copyright (c) 2013-2021 Jari Komppa
 
 This software is provided 'as-is', without any express or implied
 warranty. In no event will the authors be held liable for any damages
@@ -26,6 +26,7 @@ freely, subject to the following restrictions:
 #define BASICWAVE_H
 
 #include "soloud.h"
+#include "soloud_adsr.h"
 
 namespace SoLoud
 {
@@ -34,7 +35,9 @@ namespace SoLoud
 	class BasicwaveInstance : public AudioSourceInstance
 	{
 		Basicwave *mParent;
+		float mFreq;
 		int mOffset;
+		float mT;
 	public:
 		BasicwaveInstance(Basicwave *aParent);
 		virtual unsigned int getAudio(float *aBuffer, unsigned int aSamplesToRead, unsigned int aBufferSize);
@@ -44,20 +47,17 @@ namespace SoLoud
 	class Basicwave : public AudioSource
 	{
 	public:
-		enum WAVEFORMS
-		{
-			SINE,
-			TRIANGLE,
-			SQUARE,
-			SAW,
-			INVERSESAW
-		};
+		ADSR mADSR;
 		float mFreq;
+		float mSuperwaveScale;
+		float mSuperwaveDetune;
 		int mWaveform;
+		bool mSuperwave;
 		Basicwave();
 		virtual ~Basicwave();
 		void setSamplerate(float aSamplerate);
 		void setWaveform(int aWaveform);
+		void setFreq(float aFreq, bool aSupewave = false);
 		virtual AudioSourceInstance *createInstance();
 	};
 };

+ 2 - 2
soloud.mod/soloud/demos/piano/soloud_padsynth.cpp

@@ -94,7 +94,7 @@ PADsynth::PADsynth(int aSampleCount, float aSamplerate, int aHarmonicsCount)
 	int i;
 	for (i = 0; i < mHarmonicsCount; i++) 
 		mHarmonics[i] = 0.0f;
-	mHarmonics[1]=1.0;//default, the first harmonic has the amplitude 1.0
+	mHarmonics[1] = 1.0f;//default, the first harmonic has the amplitude 1.0
 
     mFreqAmp = new float[mSampleCount / 2];
 };
@@ -132,7 +132,7 @@ float PADsynth::profile(float fi, float bwi)
     return (float)exp(-x) / bwi;
 };
 
-void PADsynth::synth(float f,float bw,float bwscale,float *smp)
+void PADsynth::synth(float f, float bw, float bwscale, float *smp)
 {
     int i, nh;
     

+ 12 - 0
soloud.mod/soloud/docsrc/faq.mmd

@@ -45,6 +45,15 @@ painless as possible.
 
 Yes! This DLL can be used from non-c++ environments through the "C" interface. SoLoud comes with wrappers for Python, Ruby, c#, BlitzMax and others.
 
+### How do I know if a sound has finished playing?
+
+
+This is actually two separate questions.
+
+First, you may be asking when is SoLoud done with a sound? The default behavior is to deallocate the instance after sound has stopped playing, and thus you can simply check if your audio handle is still valid. If it's not, the sound is done.
+
+Second, you may be asking when the sound has come out of the speakers. This is trickier, because the backend may take a while for the sound to come out. The audio is generally sent to audio hardware in buffers, and those buffers may be relatively large. As a completely random example, let's say we're playing with 4096 sample buffers at 44.1kHz. We get unlucky and we need to wait for the whole buffer to play after SoLoud is done with it. With these assumptions, the audio is out of the system after approximately (4096/44100)*1000 = 93ms.
+
 ### What's the animal in the logo?
 
 
@@ -80,6 +89,7 @@ fork of libmodplug.
 
 ### Why did SoLoud move to libmodplug?
 
+
 Originally SoLoud used a public domain fork of modplug, but as time went on it became
 increasingly clear that instead of supporting SoLoud the author would have had to support
 modplug. At the same time better supported forks of modplug existed, so SoLoud was
@@ -87,6 +97,7 @@ divorced from the modplug code, while making it possible to use modplug if neede
 
 ### Can SoLoud do HRTF?
 
+
 Currently, no. Pull requests are welcome =)
 
 All joking aside, there's no simple place to plug this in currently. It's a TODO item
@@ -94,6 +105,7 @@ for the future.
 
 ### What about surround speakers?
 
+
 Yes. SoLoud supports 1, 2, 4, 5.1 and 7.1 configurations.
 
 The way multi-speaker system is implemented in SoLoud, it would be relatively easy to add any number of speakers,

+ 8 - 0
soloud.mod/soloud/include/soloud.h

@@ -106,6 +106,7 @@ namespace SoLoud
 	typedef void (*mutexCallFunction)(void *aMutexPtr);
 	typedef void (*soloudCallFunction)(Soloud *aSoloud);
 	typedef unsigned int result;
+	typedef result (*soloudResultFunction)(Soloud *aSoloud);
 	typedef unsigned int handle;
 	typedef double time;
 };
@@ -165,6 +166,10 @@ namespace SoLoud
 		// Called by SoLoud to shut down the back-end. If NULL, not called. Should be set by back-end.
 		soloudCallFunction mBackendCleanupFunc;
 
+		// Some backends like CoreAudio on iOS must be paused/resumed in some cases. On incoming call as instance.
+		soloudResultFunction mBackendPauseFunc;
+		soloudResultFunction mBackendResumeFunc;
+
 		// CTor
 		Soloud();
 		// DTor
@@ -224,6 +229,9 @@ namespace SoLoud
 		// Initialize SoLoud. Must be called before SoLoud can be used.
 		result init(unsigned int aFlags = Soloud::CLIP_ROUNDOFF, unsigned int aBackend = Soloud::AUTO, unsigned int aSamplerate = Soloud::AUTO, unsigned int aBufferSize = Soloud::AUTO, unsigned int aChannels = 2);
 
+		result pause();
+		result resume();
+
 		// Deinitialize SoLoud. Must be called before shutting down.
 		void deinit();
 

+ 2 - 1
soloud.mod/soloud/include/soloud_bus.h

@@ -33,10 +33,11 @@ namespace SoLoud
 
 	class BusInstance : public AudioSourceInstance
 	{
+	public:
 		Bus *mParent;
 		unsigned int mScratchSize;
 		AlignedFloatBuffer mScratch;
-	public:
+
 		// Approximate volume for channels.
 		float mVisualizationChannelVolume[MAX_CHANNELS];
 		// Mono-mixed wave data for visualization and for visualization FFT input

+ 71 - 0
soloud.mod/soloud/include/soloud_duckfilter.h

@@ -0,0 +1,71 @@
+/*
+SoLoud audio engine
+Copyright (c) 2013-2021 Jari Komppa
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+   1. The origin of this software must not be misrepresented; you must not
+   claim that you wrote the original software. If you use this software
+   in a product, an acknowledgment in the product documentation would be
+   appreciated but is not required.
+
+   2. Altered source versions must be plainly marked as such, and must not be
+   misrepresented as being the original software.
+
+   3. This notice may not be removed or altered from any source
+   distribution.
+*/
+
+#ifndef SOLOUD_DUCKFILTER_H
+#define SOLOUD_DUCKFILTER_H
+
+#include "soloud.h"
+
+namespace SoLoud
+{
+	class DuckFilter;
+
+	class DuckFilterInstance : public FilterInstance
+	{
+		handle mListenTo;
+		Soloud* mSoloud;
+		float mCurrentLevel;
+	public:
+		virtual void filter(float *aBuffer, unsigned int aSamples, unsigned int aBufferSize, unsigned int aChannels, float aSamplerate, time aTime);
+		virtual ~DuckFilterInstance();
+		DuckFilterInstance(DuckFilter *aParent);
+	};
+
+	class DuckFilter : public Filter
+	{
+	public:
+		enum FILTERATTRIBUTE
+		{
+			WET = 0,
+			ONRAMP,
+			OFFRAMP,
+			LEVEL
+		};
+		Soloud* mSoloud;
+		float mOnRamp;
+		float mOffRamp;
+		float mLevel;
+		handle mListenTo;
+		virtual int getParamCount();
+		virtual const char* getParamName(unsigned int aParamIndex);
+		virtual unsigned int getParamType(unsigned int aParamIndex);
+		virtual float getParamMax(unsigned int aParamIndex);
+		virtual float getParamMin(unsigned int aParamIndex);
+		virtual FilterInstance *createInstance();
+		DuckFilter();
+		result setParams(Soloud* aSoloud, handle aListenTo, float aOnRamp = 0.1f, float aOffRamp = 0.5f, float aLevel = 0.1f);
+	};
+}
+
+#endif

+ 1 - 1
soloud.mod/soloud/include/soloud_file.h

@@ -59,7 +59,7 @@ namespace SoLoud
 		virtual void seek(int aOffset);
 		virtual unsigned int pos();
 		virtual ~DiskFile();
-		DiskFile();
+		DiskFile();
 		DiskFile(FILE *fp);
 		result open(const char *aFilename);
 		virtual FILE * getFilePtr();

+ 0 - 1
soloud.mod/soloud/include/soloud_openmpt.h

@@ -42,7 +42,6 @@ namespace SoLoud
 		OpenmptInstance(Openmpt *aParent);
 		virtual ~OpenmptInstance();
 		virtual unsigned int getAudio(float *aBuffer, unsigned int aSamplesToRead, unsigned int aBufferSize);
-		virtual result rewind();
 		virtual bool hasEnded();
 	};
 

+ 1 - 0
soloud.mod/soloud/include/soloud_wav.h

@@ -62,6 +62,7 @@ namespace SoLoud
 	public:
 		WavStreamInstance(WavStream *aParent);
 		virtual unsigned int getAudio(float *aBuffer, unsigned int aSamplesToRead, unsigned int aBufferSize);
+		virtual result seek(double aSeconds, float* mScratch, unsigned int mScratchSize);
 		virtual result rewind();
 		virtual bool hasEnded();
 		virtual ~WavStreamInstance();

+ 175 - 0
soloud.mod/soloud/scripts/gen_beef.py

@@ -0,0 +1,175 @@
+#!/usr/bin/env python3
+""" SoLoud Beef (bf) wrapper generator """
+
+import soloud_codegen
+
+fo = open("../glue/soloud.bf", "w")
+
+
+C_TO_BF_TYPES = {
+    "string":"String",
+    "int":"int32",
+    "void":"void",
+    "const char *":"char8 *",
+    "char *":"char8 *",
+    "unsigned int":"uint32",
+    "float":"float",
+    "double":"double",
+    "float *":"float *",
+    "File *":"void *",
+    "unsigned char *":"uint8 *",
+    "const unsigned char *":"uint8 *",
+    "unsigned char":"uint8",
+    "short *":"uint16 *"
+}
+
+for soloud_type in soloud_codegen.soloud_type:
+    C_TO_BF_TYPES[soloud_type + " *"] = "SoloudObject"
+
+
+def has_ex_variant(funcname):
+    """ Checks if this function has an "Ex" variant """    
+    if funcname[-2::] == "Ex":
+        # Already an Ex..
+        return False
+    for func in soloud_codegen.soloud_func:
+        if func[1] == (funcname + "Ex"):
+            return True
+    return False
+
+fo.write("""
+// SoLoud wrapper for Beef (bf)
+// This file is autogenerated; any changes will be overwritten
+
+using System;
+
+namespace SoLoud
+{
+
+public class SoloudObject
+{
+    public void* objhandle;
+}
+
+""")
+
+fo.write("\n")
+#################################################################
+
+def fix_default_param(defparam, classname):
+    """ 'fixes' default parameters from C to what python expectes """
+    if defparam == "false":
+        return "0"
+    if defparam == "true":
+        return "1"
+    if (classname + '::') == defparam[0:len(classname)+2:]:
+        return defparam[len(classname)+2::]
+    if 'Soloud::' in defparam:
+        return "Soloud." + defparam[len("Soloud")+2::]
+    return defparam
+
+def external_pointer_fix(param):
+    if param == "SoloudObject":
+        return "void *"
+    if param == "string":
+        return "char8 *"
+    return param
+
+for x in soloud_codegen.soloud_type:
+    first = True
+    for y in soloud_codegen.soloud_func:
+        if (x + "_") == y[1][0:len(x)+1:]:
+            if first:
+                fo.write('\n')
+                fo.write('public class %s : SoloudObject\n{\n'%(x))
+                for z in soloud_codegen.soloud_enum:
+                    if z[0:len(x)+1] == x.upper()+'_':
+                        s = str(soloud_codegen.soloud_enum[z])
+                        fo.write('\tpublic const int %s = %s;\n'%(z[len(x)+1::], s))
+                fo.write('\n\t[LinkName(\"%s_create\")]\n\tprivate static extern void* create();\n'%(x))
+                fo.write('\tpublic this()\n\t{\n')
+                fo.write('\t\tobjhandle = create();\n')
+                fo.write('\t}\n')
+                
+                fo.write('\n\t[LinkName(\"%s_destroy\")]\n\tprivate static extern void* destroy(void* aObjHandle);\n'%(x))                
+                fo.write('\tpublic ~this()\n\t{\n')
+                fo.write('\t\tdestroy(objhandle);\n')
+                fo.write('\t}\n')
+                
+                first = False
+            funcname = y[1][len(x)+1::]
+            # If the function has the name "Ex", remove the subfix
+            if funcname[-2::] == "Ex":
+                funcname = funcname[:len(funcname)-2]
+            # Skip generating functions that have an Ex variant            
+            if funcname == "create" or funcname == "destroy" or has_ex_variant(y[1]):
+                pass # omit create/destroy, handled by __exit__ / close
+            else:
+                charptr = False
+                floatptr = False
+                ret = C_TO_BF_TYPES[y[0]]
+                if y[0] == 'const char *':                    
+                    charptr = True
+                    ret = 'char8 *'
+                if y[0] == 'const unsigned char *':                    
+                    charptr = True
+                    ret = 'uint8 *'
+                if y[0] == 'float *':
+                    floatptr = True
+                    ret = 'float *'
+                fo.write('\n\t[CLink]\n\tprivate static extern %s %s(void* aObjHandle'%(ret, y[1]))
+                for z in y[2]:
+                    if len(z) > 1:
+                        if z[1] == 'a'+x:
+                            pass # skip the 'self' pointer
+                        else:
+                            fo.write(', ')
+                            fo.write(external_pointer_fix(C_TO_BF_TYPES[z[0]]) + ' ' + z[1])
+                fo.write(');\n')
+                
+                fo.write('\tpublic %s %s('%(C_TO_BF_TYPES[y[0]], funcname))
+                firstparm = True
+                for z in y[2]:
+                    if len(z) > 1:
+                        if z[1] == 'a'+x:
+                            pass # skip the 'self' pointer
+                        else:
+                            if firstparm:
+                                firstparm = False
+                            else:
+                                fo.write(', ')
+                            fo.write(C_TO_BF_TYPES[z[0]] + ' ' + z[1])
+                            if len(z) > 2:
+                                fo.write(' = ' + fix_default_param(z[2], x))
+                fo.write(')\n\t{\n')
+                fo.write('\t\t')
+                if y[0] == 'void':
+                    pass
+                elif charptr:
+                    fo.write('return ')
+                elif floatptr:
+                    fo.write('return ')
+                else:
+                    fo.write('return ')
+                fo.write(y[1] + '(objhandle')
+                for z in y[2]:
+                    if len(z) > 1:
+                        if z[1] == 'a'+x:
+                            pass # skip the 'self' pointer
+                        else:
+                            fo.write(', ')
+                            fudged_type = C_TO_BF_TYPES[z[0]]
+                            if fudged_type == 'SoloudObject':
+                                fo.write(z[1] + '.objhandle')
+                            else:
+                                fo.write(z[1])
+                fo.write(');\n')
+                fo.write('\t}\n')
+    if not first:
+        fo.write('}\n')
+
+fo.write('}\n')
+
+print("soloud.bf generated")
+
+fo.close()

+ 18 - 4
soloud.mod/soloud/scripts/gen_cs.py

@@ -108,9 +108,16 @@ def fix_default_param(defparam, classname):
     """ 'fixes' default parameters from C to what python expectes """
     if (classname + '::') == defparam[0:len(classname)+2:]:
         return defparam[len(classname)+2::]
-    #if defparam[len(defparam)-1] == "f":
-    #    return defparam[0:len(defparam)-1]
-    return defparam
+
+    # The C API doesn't use BOOL, so the generated source will use int too,
+    # using a boolean as the default param for an int won't work.
+    if defparam == "true":
+        return "1"
+    elif defparam == "false":
+        return "0"
+
+    # C# uses . as namespace/class separator
+    return defparam.replace("::", ".")
 
 def external_pointer_fix(param):
     if param == "SoloudObject":
@@ -125,7 +132,7 @@ for x in soloud_codegen.soloud_type:
         if (x + "_") == y[1][0:len(x)+1:]:
             if first:
                 fo.write('\n')
-                fo.write('public class %s : SoloudObject\n{\n'%(x))
+                fo.write('public class %s : SoloudObject, IDisposable\n{\n'%(x))
                 for z in soloud_codegen.soloud_enum:
                     if z[0:len(x)+1] == x.upper()+'_':
                         s = str(soloud_codegen.soloud_enum[z])
@@ -138,6 +145,13 @@ for x in soloud_codegen.soloud_type:
                 fo.write('\t~%s()\n\t{\n'%(x))
                 fo.write('\t\t%s_destroy(objhandle);\n'%(x))
                 fo.write('\t}\n')
+                fo.write('\tpublic void Dispose()\n\t{\n')
+                fo.write('\t\tif (objhandle != IntPtr.Zero) {\n')
+                fo.write('\t\t\t%s_destroy(objhandle);\n'%(x))
+                fo.write('\t\t\tobjhandle = IntPtr.Zero;\n')
+                fo.write('\t\t\tGC.SuppressFinalize(this);\n')
+                fo.write('\t\t}\n')
+                fo.write('\t}\n')
                 
                 first = False
             funcname = y[1][len(x)+1::]

+ 39 - 16
soloud.mod/soloud/scripts/gen_python.py

@@ -2,7 +2,6 @@
 """ SoLoud Python wrapper generator """
 
 import soloud_codegen
-
 fo = open("../glue/soloud.py", "w")
 
 #
@@ -62,22 +61,44 @@ def has_ex_variant(funcname):
             return True
     return False
 
-fo.write("# SoLoud wrapper for Python\n")
-fo.write("# This file is autogenerated; any changes will be overwritten\n")
 
-fo.write("\n")
-fo.write('import ctypes\n')
-fo.write('import sys\n')
-fo.write('\n')
-fo.write('try:\n')
-fo.write('\tsoloud_dll = ctypes.CDLL("soloud_x86")\n')
-fo.write('except:\n')
-fo.write('\ttry:\n')
-fo.write('\t\tsoloud_dll = ctypes.CDLL("soloud_x64")\n')
-fo.write('\texcept:\n')
-fo.write('\t\tprint("SoLoud dynamic link library (soloud_x86.dll / soloud_x64.dll on Windows, .so on Linux / OSX) not found. Terminating.")\n')
-fo.write('\t\tsys.exit()')
-fo.write("\n")
+bootstrap = '''\
+# SoLoud wrapper for Python
+# This file is autogenerated; any changes will be overwritten
+# See: http://sol.gfxile.net/soloud/codegen.html
+
+import ctypes
+import sys
+import os
+
+soloud_dll = None
+try:
+
+	for filename in ['soloud_x86','soloud_x64','libsoloud_x64.so']:
+		try:
+			soloud_dll = ctypes.CDLL("%s/%s" %(os.path.dirname(os.path.abspath(__file__)), filename))
+			break
+		except FileNotFoundError:
+			# on windows, we get an explicit FileNotFoundError
+			continue
+		except OSError as e:
+			# but on linux, we don't
+			if 'No such file or directory' in str(e):
+				continue
+			else:
+				raise e
+	if soloud_dll == None:
+		raise Exception("SoLoud dynamic library not found in %s " % (os.path.dirname(os.path.abspath(__file__))))
+
+except BaseException as e:
+	print("Error while loading Soloud dynamic library on %s %sbit" % (sys.platform, sys.maxsize.bit_length()+1))
+	print(e)
+	sys.exit()
+
+'''
+
+
+fo.write(bootstrap)
 
 # Since there's no reason to use the "raw" data anymore,
 # skip generating the enum dictionary
@@ -138,6 +159,8 @@ def fix_default_param(defparam, classname):
         defparam = 'True'
     if (classname + '::') == defparam[0:len(classname)+2:]:
         return defparam[len(classname)+2::]
+    elif defparam.startswith('Soloud::'):
+        return 'Soloud.%s'%(defparam[8:])
     if defparam[len(defparam)-1] == "f":
         return defparam[0:len(defparam)-1]
     return defparam

+ 2 - 3
soloud.mod/soloud/src/audiosource/ay/sndbuffer.cpp

@@ -1,8 +1,7 @@
+#include <stdlib.h>
 #include "sndbuffer.h"
 #include "sndrender.h"
-
-#include "stdlib.h"
-#include "memory.h"
+#include <memory.h>
 
 SNDBUFFER::SNDBUFFER(unsigned aSize) {
         read_position = 0;

+ 6 - 13
soloud.mod/soloud/src/audiosource/ay/soloud_ay.cpp

@@ -101,17 +101,13 @@ namespace SoLoud
 	{
 		if (!aMem || aLength == 0)
 			return INVALID_PARAMETER;
-		MemoryFile *mf = new MemoryFile;
-		if (!mf)
-			return OUT_OF_MEMORY;
-		int res = mf->openMem(aMem, aLength, aCopy, aTakeOwnership);
+		MemoryFile mf;
+		int res = mf.openMem(aMem, aLength, aCopy, aTakeOwnership);
 		if (res != SO_NO_ERROR)
 		{
-			delete mf;
 			return res;
 		}
-		res = loadFile(mf);
-		delete mf;
+		res = loadFile(&mf);
 		return res;
 	}
 
@@ -119,17 +115,14 @@ namespace SoLoud
 	{
 		if (!aFilename)
 			return INVALID_PARAMETER;
-		DiskFile *df = new DiskFile;
-		if (!df) return OUT_OF_MEMORY;
-		int res = df->open(aFilename);
+		DiskFile df;
+		int res = df.open(aFilename);
 		if (res != SO_NO_ERROR)
 		{
-			delete df;
 			return res;
 		}
-		res = loadFile(df);
+		res = loadFile(&df);
 
-		delete df;
 		return res;
 	}
 

+ 3 - 1
soloud.mod/soloud/src/audiosource/monotone/soloud_monotone.cpp

@@ -121,7 +121,7 @@ namespace SoLoud
 							mChannel[j].mVibratoIndex = 0;
 						}
 						else
-						if (note == 0)
+//						if (note == 0)
 						{
 							note = mChannel[j].mLastNote;
 						}
@@ -307,7 +307,9 @@ namespace SoLoud
 		}
 
 		for (i = 0; i < 32; i++)
+		{
 			mVibTable[i] = (int)floor(0.5 + 64 * sin(i * M_PI / 32 * 2));
+		}
 
 		mSong.mTitle = 0;
 		mSong.mComment = 0;

+ 3 - 12
soloud.mod/soloud/src/audiosource/openmpt/soloud_openmpt.cpp

@@ -32,7 +32,7 @@ extern "C"
 	void * openmpt_module_create_from_memory(const void * filedata, size_t filesize, void *logfunc, void * user,void * ctls);
 	void openmpt_module_destroy(void * mod);
 	int openmpt_module_read_float_stereo(void * mod, int samplerate, size_t count, float * left, float * right);
-	double openmpt_module_set_position_seconds(void * mod, double seconds);		
+	void openmpt_module_set_repeat_count(void* mod, int repeat_count);
 }
 
 namespace SoLoud
@@ -41,6 +41,7 @@ namespace SoLoud
 	{
 		mParent = aParent;
 		mModfile = openmpt_module_create_from_memory((const void*)mParent->mData, mParent->mDataLen, NULL, NULL, NULL);		
+		openmpt_module_set_repeat_count(mModfile, -1);
 		mPlaying = mModfile != NULL;		
 	}
 
@@ -68,13 +69,6 @@ namespace SoLoud
 		return outofs;
 	}
 
-	result OpenmptInstance::rewind()
-	{
-		openmpt_module_set_position_seconds(mModfile, 0.0f);
-		mPlaying = 1;
-		return 0;
-	}
-
 	bool OpenmptInstance::hasEnded()
 	{
 		return !mPlaying;
@@ -109,10 +103,7 @@ namespace SoLoud
 
 	result Openmpt::loadFile(File *aFile)
 	{
-		if (mData)
-		{
-			delete[] mData;
-		}
+		delete[] mData;
 
 		mDataLen = aFile->length();
 		mData = new char[mDataLen];

+ 12 - 1
soloud.mod/soloud/src/audiosource/openmpt/soloud_openmpt_dll.c

@@ -30,10 +30,12 @@ freely, subject to the following restrictions:
 typedef void * (*dll_openmpt_module_create_from_memory)(const void * filedata, size_t filesize, void *logfunc, void * user, void * ctls);
 typedef void (*dll_openmpt_module_destroy)(void * mod);
 typedef int (*dll_openmpt_module_read_float_stereo)(void * mod, int samplerate, size_t count, float * left, float * right);
+typedef void (*dll_openmpt_module_set_repeat_count)(void* mod, int repeat_count);
 
 static dll_openmpt_module_create_from_memory d_openmpt_module_create_from_memory = NULL;
 static dll_openmpt_module_destroy d_openmpt_module_destroy = NULL;
 static dll_openmpt_module_read_float_stereo d_openmpt_module_read_float_stereo = NULL;
+static dll_openmpt_module_set_repeat_count d_openmpt_module_set_repeat_count = NULL;
 
 #ifdef WINDOWS_VERSION
 #include <windows.h>
@@ -98,11 +100,14 @@ static int load_dll()
 		d_openmpt_module_create_from_memory = (dll_openmpt_module_create_from_memory)getDllProc(dll, "openmpt_module_create_from_memory");
 		d_openmpt_module_destroy = (dll_openmpt_module_destroy)getDllProc(dll, "openmpt_module_destroy");
 		d_openmpt_module_read_float_stereo = (dll_openmpt_module_read_float_stereo)getDllProc(dll, "openmpt_module_read_float_stereo");
+		d_openmpt_module_set_repeat_count = (dll_openmpt_module_set_repeat_count)getDllProc(dll, "openmpt_module_set_repeat_count");
+
 
 
 		if (d_openmpt_module_create_from_memory &&
 			d_openmpt_module_destroy &&
-			d_openmpt_module_read_float_stereo)
+			d_openmpt_module_read_float_stereo &&
+			d_openmpt_module_set_repeat_count)
 		{
 			return 1;
 		}
@@ -130,3 +135,9 @@ int openmpt_module_read_float_stereo(void * mod, int samplerate, size_t count, f
 		return d_openmpt_module_read_float_stereo(mod, samplerate, count, left, right);
 	return 0;
 }
+
+void openmpt_module_set_repeat_count(void* mod, int repeat_count)
+{
+	if (load_dll())
+		d_openmpt_module_set_repeat_count(mod, repeat_count);
+}

+ 6 - 13
soloud.mod/soloud/src/audiosource/tedsid/soloud_tedsid.cpp

@@ -139,17 +139,13 @@ namespace SoLoud
 	{
 		if (!aMem || aLength == 0)
 			return INVALID_PARAMETER;
-		MemoryFile *mf = new MemoryFile;
-		if (!mf)
-			return OUT_OF_MEMORY;
-		int res = mf->openMem(aMem, aLength, aCopy, aTakeOwnership);
+		MemoryFile mf;
+		int res = mf.openMem(aMem, aLength, aCopy, aTakeOwnership);
 		if (res != SO_NO_ERROR)
 		{
-			delete mf;
 			return res;
 		}
-		res = loadFile(mf);
-		delete mf;
+		res = loadFile(&mf);
 		return res;
 	}
 
@@ -157,16 +153,13 @@ namespace SoLoud
 	{
 		if (!aFilename)
 			return INVALID_PARAMETER;
-		DiskFile *df = new DiskFile;
-		if (!df) return OUT_OF_MEMORY;
-		int res = df->open(aFilename);
+		DiskFile df;
+		int res = df.open(aFilename);
 		if (res != SO_NO_ERROR)
 		{
-			delete df;
 			return res;
 		}
-		res = loadFile(df);
-		delete df;
+		res = loadFile(&df);
 		return res;
 	}
 

File diff suppressed because it is too large
+ 543 - 192
soloud.mod/soloud/src/audiosource/wav/dr_flac.h


File diff suppressed because it is too large
+ 365 - 265
soloud.mod/soloud/src/audiosource/wav/dr_mp3.h


File diff suppressed because it is too large
+ 565 - 299
soloud.mod/soloud/src/audiosource/wav/dr_wav.h


+ 2 - 1
soloud.mod/soloud/src/audiosource/wav/soloud_wav.cpp

@@ -156,6 +156,7 @@ namespace SoLoud
 			mChannels = info.channels;
 		}
 		mData = new float[samples * mChannels];
+		memset(mData, 0, samples * mChannels * sizeof(float));
 		mSampleCount = samples;
 		samples = 0;
 		while(1)
@@ -182,7 +183,7 @@ namespace SoLoud
 	{
 		drmp3 decoder;
 
-		if (!drmp3_init_memory(&decoder, aReader->getMemPtr(), aReader->length(), NULL, NULL))
+		if (!drmp3_init_memory(&decoder, aReader->getMemPtr(), aReader->length(), NULL))
 		{
 			return FILE_LOAD_FAILED;
 		}

+ 23 - 5
soloud.mod/soloud/src/audiosource/wav/soloud_wavstream.cpp

@@ -82,6 +82,7 @@ namespace SoLoud
 
 	WavStreamInstance::WavStreamInstance(WavStream *aParent)
 	{
+		mOggFrameSize = 0;
 		mParent = aParent;
 		mOffset = 0;
 		mCodec.mOgg = 0;
@@ -157,7 +158,7 @@ namespace SoLoud
 			if (mParent->mFiletype == WAVSTREAM_MP3)
 			{
 				mCodec.mMp3 = new drmp3;
-				if (!drmp3_init(mCodec.mMp3, drmp3_read_func, drmp3_seek_func, (void*)mFile, NULL, NULL))
+				if (!drmp3_init(mCodec.mMp3, drmp3_read_func, drmp3_seek_func, (void*)mFile, NULL))
 				{
 					delete mCodec.mMp3;
 					mCodec.mMp3 = 0;
@@ -239,6 +240,7 @@ namespace SoLoud
 	unsigned int WavStreamInstance::getAudio(float *aBuffer, unsigned int aSamplesToRead, unsigned int aBufferSize)
 	{			
 		unsigned int offset = 0;
+		float tmp[512 * MAX_CHANNELS];
 		if (mFile == NULL)
 			return 0;
 		switch (mParent->mFiletype)
@@ -249,7 +251,6 @@ namespace SoLoud
 
 				for (i = 0; i < aSamplesToRead; i += 512)
 				{
-					float tmp[512 * MAX_CHANNELS];
 					unsigned int blockSize = (aSamplesToRead - i) > 512 ? 512 : aSamplesToRead - i;
 					offset += (unsigned int)drflac_read_pcm_frames_f32(mCodec.mFlac, blockSize, tmp);
 
@@ -271,7 +272,6 @@ namespace SoLoud
 
 				for (i = 0; i < aSamplesToRead; i += 512)
 				{
-					float tmp[512 * MAX_CHANNELS];
 					unsigned int blockSize = (aSamplesToRead - i) > 512 ? 512 : aSamplesToRead - i;
 					offset += (unsigned int)drmp3_read_pcm_frames_f32(mCodec.mMp3, blockSize, tmp);
 
@@ -320,7 +320,6 @@ namespace SoLoud
 
 				for (i = 0; i < aSamplesToRead; i += 512)
 				{
-					float tmp[512 * MAX_CHANNELS];
 					unsigned int blockSize = (aSamplesToRead - i) > 512 ? 512 : aSamplesToRead - i;
 					offset += (unsigned int)drwav_read_pcm_frames_f32(mCodec.mWav, blockSize, tmp);
 
@@ -340,6 +339,25 @@ namespace SoLoud
 		return aSamplesToRead;
 	}
 
+	result WavStreamInstance::seek(double aSeconds, float* mScratch, unsigned int mScratchSize)
+	{
+		if (mCodec.mOgg)
+		{
+			int pos = (int)floor(mBaseSamplerate * aSeconds);
+			stb_vorbis_seek(mCodec.mOgg, pos);
+			// Since the position that we just sought to might not be *exactly*
+			// the position we asked for, we're re-calculating the position just
+			// for the sake of correctness.
+			mOffset = stb_vorbis_get_sample_offset(mCodec.mOgg);
+			double newPosition = float(mOffset / mBaseSamplerate);
+			mStreamPosition = newPosition;
+			return 0;
+		}
+		else {
+			return AudioSourceInstance::seek(aSeconds, mScratch, mScratchSize);
+		}
+	}
+
 	result WavStreamInstance::rewind()
 	{
 		switch (mParent->mFiletype)
@@ -473,7 +491,7 @@ namespace SoLoud
 	{
 		fp->seek(0);
 		drmp3 decoder;
-		if (!drmp3_init(&decoder, drmp3_read_func, drmp3_seek_func, (void*)fp, NULL, NULL))
+		if (!drmp3_init(&decoder, drmp3_read_func, drmp3_seek_func, (void*)fp, NULL))
 			return FILE_LOAD_FAILED;
 
 

+ 82 - 53
soloud.mod/soloud/src/audiosource/wav/stb_vorbis.c

@@ -1,4 +1,4 @@
-// Ogg Vorbis audio decoder - v1.19 - public domain
+// Ogg Vorbis audio decoder - v1.22 - public domain
 // http://nothings.org/stb_vorbis/
 //
 // Original version written by Sean Barrett in 2007.
@@ -29,11 +29,16 @@
 //    Bernhard Wodo      Evan Balster        github:alxprd
 //    Tom Beaumont       Ingo Leitgeb        Nicolas Guillemot
 //    Phillip Bennefall  Rohit               Thiago Goulart
-//    github:manxorist   saga musix          github:infatum
+//    github:manxorist   Saga Musix          github:infatum
 //    Timur Gagiev       Maxwell Koo         Peter Waller
-//    github:audinowho   Dougall Johnson
+//    github:audinowho   Dougall Johnson     David Reid
+//    github:Clownacy    Pedro J. Estebanez  Remi Verschelde
+//    AnthoFoxo          github:morlat       Gabriel Ravier
 //
 // Partial history:
+//    1.22    - 2021-07-11 - various small fixes
+//    1.21    - 2021-07-02 - fix bug for files with no comments
+//    1.20    - 2020-07-11 - several small fixes
 //    1.19    - 2020-02-05 - warnings
 //    1.18    - 2020-02-02 - fix seek bugs; parse header comments; misc warnings etc.
 //    1.17    - 2019-07-08 - fix CVE-2019-13217..CVE-2019-13223 (by ForAllSecure)
@@ -220,6 +225,12 @@ extern int stb_vorbis_decode_frame_pushdata(
 // channel. In other words, (*output)[0][0] contains the first sample from
 // the first channel, and (*output)[1][0] contains the first sample from
 // the second channel.
+//
+// *output points into stb_vorbis's internal output buffer storage; these
+// buffers are owned by stb_vorbis and application code should not free
+// them or modify their contents. They are transient and will be overwritten
+// once you ask for more data to get decoded, so be sure to grab any data
+// you need before then.
 
 extern void stb_vorbis_flush_pushdata(stb_vorbis *f);
 // inform stb_vorbis that your next datablock will not be contiguous with
@@ -579,7 +590,7 @@ enum STBVorbisError
    #if defined(_MSC_VER) || defined(__MINGW32__)
       #include <malloc.h>
    #endif
-   #if defined(__linux__) || defined(__linux) || defined(__EMSCRIPTEN__)
+   #if defined(__linux__) || defined(__linux) || defined(__sun__) || defined(__EMSCRIPTEN__) || defined(__NEWLIB__)
       #include <alloca.h>
    #endif
 #else // STB_VORBIS_NO_CRT
@@ -601,7 +612,9 @@ enum STBVorbisError
    #undef __forceinline
    #endif
    #define __forceinline
+   #ifndef alloca
    #define alloca __builtin_alloca
+   #endif
 #elif !defined(_MSC_VER)
    #if __GNUC__
       #define __forceinline inline
@@ -644,6 +657,12 @@ typedef   signed int    int32;
 
 typedef float codetype;
 
+#ifdef _MSC_VER
+#define STBV_NOTUSED(v)  (void)(v)
+#else
+#define STBV_NOTUSED(v)  (void)sizeof(v)
+#endif
+
 // @NOTE
 //
 // Some arrays below are tagged "//varies", which means it's actually
@@ -963,7 +982,7 @@ static void *setup_temp_malloc(vorb *f, int sz)
 static void setup_temp_free(vorb *f, void *p, int sz)
 {
    if (f->alloc.alloc_buffer) {
-      f->temp_offset += (sz+3)&~3;
+      f->temp_offset += (sz+7)&~7;
       return;
    }
    free(p);
@@ -1044,7 +1063,7 @@ static float float32_unpack(uint32 x)
    uint32 sign = x & 0x80000000;
    uint32 exp = (x & 0x7fe00000) >> 21;
    double res = sign ? -(double)mantissa : (double)mantissa;
-   return (float) ldexp((float)res, exp-788);
+   return (float) ldexp((float)res, (int)exp-788);
 }
 
 
@@ -1075,6 +1094,7 @@ static int compute_codewords(Codebook *c, uint8 *len, int n, uint32 *values)
    // find the first entry
    for (k=0; k < n; ++k) if (len[k] < NO_CODE) break;
    if (k == n) { assert(c->sorted_entries == 0); return TRUE; }
+   assert(len[k] < 32); // no error return required, code reading lens checks this
    // add to the list
    add_entry(c, 0, k, m++, len[k], values);
    // add all available leaves
@@ -1088,6 +1108,7 @@ static int compute_codewords(Codebook *c, uint8 *len, int n, uint32 *values)
       uint32 res;
       int z = len[i], y;
       if (z == NO_CODE) continue;
+      assert(z < 32); // no error return required, code reading lens checks this
       // find lowest available leaf (should always be earliest,
       // which is what the specification calls for)
       // note that this property, and the fact we can never have
@@ -1097,12 +1118,10 @@ static int compute_codewords(Codebook *c, uint8 *len, int n, uint32 *values)
       while (z > 0 && !available[z]) --z;
       if (z == 0) { return FALSE; }
       res = available[z];
-      assert(z >= 0 && z < 32);
       available[z] = 0;
       add_entry(c, bit_reverse(res), i, m++, len[i], values);
       // propagate availability up the tree
       if (z != len[i]) {
-         assert(len[i] >= 0 && len[i] < 32);
          for (y=len[i]; y > z; --y) {
             assert(available[y] == 0);
             available[y] = res + (1 << (32-y));
@@ -1602,7 +1621,8 @@ static uint32 get_bits(vorb *f, int n)
          f->valid_bits += 8;
       }
    }
-   if (f->valid_bits < 0) return 0;
+
+   assert(f->valid_bits >= n);
    z = f->acc & ((1 << n)-1);
    f->acc >>= n;
    f->valid_bits -= n;
@@ -2574,34 +2594,33 @@ static void imdct_step3_inner_s_loop_ld654(int n, float *e, int i_off, float *A,
 
    while (z > base) {
       float k00,k11;
-
-      k00   = z[-0] - z[-8];
-      k11   = z[-1] - z[-9];
-      z[-0] = z[-0] + z[-8];
-      z[-1] = z[-1] + z[-9];
-      z[-8] =  k00;
-      z[-9] =  k11 ;
-
-      k00    = z[ -2] - z[-10];
-      k11    = z[ -3] - z[-11];
-      z[ -2] = z[ -2] + z[-10];
-      z[ -3] = z[ -3] + z[-11];
-      z[-10] = (k00+k11) * A2;
-      z[-11] = (k11-k00) * A2;
-
-      k00    = z[-12] - z[ -4];  // reverse to avoid a unary negation
+      float l00,l11;
+
+      k00    = z[-0] - z[ -8];
+      k11    = z[-1] - z[ -9];
+      l00    = z[-2] - z[-10];
+      l11    = z[-3] - z[-11];
+      z[ -0] = z[-0] + z[ -8];
+      z[ -1] = z[-1] + z[ -9];
+      z[ -2] = z[-2] + z[-10];
+      z[ -3] = z[-3] + z[-11];
+      z[ -8] = k00;
+      z[ -9] = k11;
+      z[-10] = (l00+l11) * A2;
+      z[-11] = (l11-l00) * A2;
+
+      k00    = z[ -4] - z[-12];
       k11    = z[ -5] - z[-13];
+      l00    = z[ -6] - z[-14];
+      l11    = z[ -7] - z[-15];
       z[ -4] = z[ -4] + z[-12];
       z[ -5] = z[ -5] + z[-13];
-      z[-12] = k11;
-      z[-13] = k00;
-
-      k00    = z[-14] - z[ -6];  // reverse to avoid a unary negation
-      k11    = z[ -7] - z[-15];
       z[ -6] = z[ -6] + z[-14];
       z[ -7] = z[ -7] + z[-15];
-      z[-14] = (k00+k11) * A2;
-      z[-15] = (k00-k11) * A2;
+      z[-12] = k11;
+      z[-13] = -k00;
+      z[-14] = (l11-l00) * A2;
+      z[-15] = (l00+l11) * -A2;
 
       iter_54(z);
       iter_54(z-8);
@@ -3066,6 +3085,7 @@ static int do_floor(vorb *f, Mapping *map, int i, int n, float *target, YTYPE *f
       for (q=1; q < g->values; ++q) {
          j = g->sorted_order[q];
          #ifndef STB_VORBIS_NO_DEFER_FLOOR
+         STBV_NOTUSED(step2_flag);
          if (finalY[j] >= 0)
          #else
          if (step2_flag[j])
@@ -3168,6 +3188,7 @@ static int vorbis_decode_packet_rest(vorb *f, int *len, Mode *m, int left_start,
 
 // WINDOWING
 
+   STBV_NOTUSED(left_end);
    n = f->blocksize[m->blockflag];
    map = &f->mapping[m->mapping];
 
@@ -3365,7 +3386,7 @@ static int vorbis_decode_packet_rest(vorb *f, int *len, Mode *m, int left_start,
       // this isn't to spec, but spec would require us to read ahead
       // and decode the size of all current frames--could be done,
       // but presumably it's not a commonly used feature
-      f->current_loc = -n2; // start of first frame is positioned for discard
+      f->current_loc = 0u - n2; // start of first frame is positioned for discard (NB this is an intentional unsigned overflow/wrap-around)
       // we might have to discard samples "from" the next frame too,
       // if we're lapping a large block then a small at the start?
       f->discard_samples_deferred = n - right_end;
@@ -3632,17 +3653,24 @@ static int start_decoder(vorb *f)
    //file vendor
    len = get32_packet(f);
    f->vendor = (char*)setup_malloc(f, sizeof(char) * (len+1));
+   if (f->vendor == NULL)                           return error(f, VORBIS_outofmem);
    for(i=0; i < len; ++i) {
       f->vendor[i] = get8_packet(f);
    }
    f->vendor[len] = (char)'\0';
    //user comments
    f->comment_list_length = get32_packet(f);
-   f->comment_list = (char**)setup_malloc(f, sizeof(char*) * (f->comment_list_length));
+   f->comment_list = NULL;
+   if (f->comment_list_length > 0)
+   {
+      f->comment_list = (char**) setup_malloc(f, sizeof(char*) * (f->comment_list_length));
+      if (f->comment_list == NULL)                  return error(f, VORBIS_outofmem);
+   }
 
    for(i=0; i < f->comment_list_length; ++i) {
       len = get32_packet(f);
       f->comment_list[i] = (char*)setup_malloc(f, sizeof(char) * (len+1));
+      if (f->comment_list[i] == NULL)               return error(f, VORBIS_outofmem);
 
       for(j=0; j < len; ++j) {
          f->comment_list[i][j] = get8_packet(f);
@@ -3859,8 +3887,7 @@ static int start_decoder(vorb *f)
                unsigned int div=1;
                for (k=0; k < c->dimensions; ++k) {
                   int off = (z / div) % c->lookup_values;
-                  float val = mults[off];
-                  val = mults[off]*c->delta_value + c->minimum_value + last;
+                  float val = mults[off]*c->delta_value + c->minimum_value + last;
                   c->multiplicands[j*c->dimensions + k] = val;
                   if (c->sequence_p)
                      last = val;
@@ -3943,7 +3970,7 @@ static int start_decoder(vorb *f)
                if (g->class_masterbooks[j] >= f->codebook_count) return error(f, VORBIS_invalid_setup);
             }
             for (k=0; k < 1 << g->class_subclasses[j]; ++k) {
-               g->subclass_books[j][k] = get_bits(f,8)-1;
+               g->subclass_books[j][k] = (int16)get_bits(f,8)-1;
                if (g->subclass_books[j][k] >= f->codebook_count) return error(f, VORBIS_invalid_setup);
             }
          }
@@ -4255,7 +4282,7 @@ static void vorbis_init(stb_vorbis *p, const stb_vorbis_alloc *z)
    memset(p, 0, sizeof(*p)); // NULL out all malloc'd pointers to start
    if (z) {
       p->alloc = *z;
-      p->alloc.alloc_buffer_length_in_bytes = (p->alloc.alloc_buffer_length_in_bytes+3) & ~3;
+      p->alloc.alloc_buffer_length_in_bytes &= ~7;
       p->temp_offset = p->alloc.alloc_buffer_length_in_bytes;
    }
    p->eof = 0;
@@ -4501,6 +4528,7 @@ stb_vorbis *stb_vorbis_open_pushdata(
          *error = VORBIS_need_more_data;
       else
          *error = p.error;
+      vorbis_deinit(&p);
       return NULL;
    }
    f = vorbis_alloc(&p);
@@ -4558,7 +4586,7 @@ static uint32 vorbis_find_page(stb_vorbis *f, uint32 *end, uint32 *last)
                header[i] = get8(f);
             if (f->eof) return 0;
             if (header[4] != 0) goto invalid;
-            goal = header[22] + (header[23] << 8) + (header[24]<<16) + (header[25]<<24);
+            goal = header[22] + (header[23] << 8) + (header[24]<<16) + ((uint32)header[25]<<24);
             for (i=22; i < 26; ++i)
                header[i] = 0;
             crc = 0;
@@ -4962,7 +4990,7 @@ unsigned int stb_vorbis_stream_length_in_samples(stb_vorbis *f)
             // set. whoops!
             break;
          }
-         previous_safe = last_page_loc+1;
+         //previous_safe = last_page_loc+1; // NOTE: not used after this point, but note for debugging
          last_page_loc = stb_vorbis_get_file_offset(f);
       }
 
@@ -5073,7 +5101,10 @@ stb_vorbis * stb_vorbis_open_filename(const char *filename, int *error, const st
 stb_vorbis * stb_vorbis_open_memory(const unsigned char *data, int len, int *error, const stb_vorbis_alloc *alloc)
 {
    stb_vorbis *f, p;
-   if (data == NULL) return NULL;
+   if (!data) {
+      if (error) *error = VORBIS_unexpected_eof;
+      return NULL;
+   }
    vorbis_init(&p, alloc);
    p.stream = (uint8 *) data;
    p.stream_end = (uint8 *) data + len;
@@ -5148,11 +5179,11 @@ static void copy_samples(short *dest, float *src, int len)
 
 static void compute_samples(int mask, short *output, int num_c, float **data, int d_offset, int len)
 {
-   #define BUFFER_SIZE  32
-   float buffer[BUFFER_SIZE];
-   int i,j,o,n = BUFFER_SIZE;
+   #define STB_BUFFER_SIZE  32
+   float buffer[STB_BUFFER_SIZE];
+   int i,j,o,n = STB_BUFFER_SIZE;
    check_endianness();
-   for (o = 0; o < len; o += BUFFER_SIZE) {
+   for (o = 0; o < len; o += STB_BUFFER_SIZE) {
       memset(buffer, 0, sizeof(buffer));
       if (o + n > len) n = len - o;
       for (j=0; j < num_c; ++j) {
@@ -5169,16 +5200,17 @@ static void compute_samples(int mask, short *output, int num_c, float **data, in
          output[o+i] = v;
       }
    }
+   #undef STB_BUFFER_SIZE
 }
 
 static void compute_stereo_samples(short *output, int num_c, float **data, int d_offset, int len)
 {
-   #define BUFFER_SIZE  32
-   float buffer[BUFFER_SIZE];
-   int i,j,o,n = BUFFER_SIZE >> 1;
+   #define STB_BUFFER_SIZE  32
+   float buffer[STB_BUFFER_SIZE];
+   int i,j,o,n = STB_BUFFER_SIZE >> 1;
    // o is the offset in the source data
    check_endianness();
-   for (o = 0; o < len; o += BUFFER_SIZE >> 1) {
+   for (o = 0; o < len; o += STB_BUFFER_SIZE >> 1) {
       // o2 is the offset in the output data
       int o2 = o << 1;
       memset(buffer, 0, sizeof(buffer));
@@ -5208,6 +5240,7 @@ static void compute_stereo_samples(short *output, int num_c, float **data, int d
          output[o2+i] = v;
       }
    }
+   #undef STB_BUFFER_SIZE
 }
 
 static void convert_samples_short(int buf_c, short **buffer, int b_offset, int data_c, float **data, int d_offset, int samples)
@@ -5280,8 +5313,6 @@ int stb_vorbis_get_samples_short_interleaved(stb_vorbis *f, int channels, short
    float **outputs;
    int len = num_shorts / channels;
    int n=0;
-   int z = f->channels;
-   if (z > channels) z = channels;
    while (n < len) {
       int k = f->channel_buffer_end - f->channel_buffer_start;
       if (n+k >= len) k = len - n;
@@ -5300,8 +5331,6 @@ int stb_vorbis_get_samples_short(stb_vorbis *f, int channels, short **buffer, in
 {
    float **outputs;
    int n=0;
-   int z = f->channels;
-   if (z > channels) z = channels;
    while (n < len) {
       int k = f->channel_buffer_end - f->channel_buffer_start;
       if (n+k >= len) k = len - n;

+ 23 - 1
soloud.mod/soloud/src/backend/coreaudio/soloud_coreaudio.cpp

@@ -55,7 +55,27 @@ namespace SoLoud
 		AudioQueueStop(audioQueue, true);
 		AudioQueueDispose(audioQueue, false);
 	}
+
+	result soloud_coreaudio_pause(SoLoud::Soloud *aSoloud)
+	{
+		if (!audioQueue)
+			return UNKNOWN_ERROR;
+
+		AudioQueuePause(audioQueue);			// TODO: Error code
+
+		return 0;
+	}
+
+	result soloud_coreaudio_resume(SoLoud::Soloud *aSoloud)
+	{
+		if (!audioQueue)
+			return UNKNOWN_ERROR;
 	
+		AudioQueueStart(audioQueue, nil);		// TODO: Error code
+
+		return 0;
+	}
+
 	static void coreaudio_mutex_lock(void *mutex)
 	{
 		Thread::lockMutex(mutex);
@@ -77,6 +97,8 @@ namespace SoLoud
 	{
 		aSoloud->postinit_internal(aSamplerate, aBuffer, aFlags, 2);
 		aSoloud->mBackendCleanupFunc = soloud_coreaudio_deinit;
+		aSoloud->mBackendPauseFunc = soloud_coreaudio_pause;
+		aSoloud->mBackendResumeFunc = soloud_coreaudio_resume;
 
 		AudioStreamBasicDescription audioFormat;
 		audioFormat.mSampleRate = aSamplerate;
@@ -122,6 +144,6 @@ namespace SoLoud
 
         aSoloud->mBackendString = "CoreAudio";
 		return 0;
-	}	
+	}
 };
 #endif

File diff suppressed because it is too large
+ 357 - 199
soloud.mod/soloud/src/backend/miniaudio/miniaudio.h


+ 5 - 2
soloud.mod/soloud/src/backend/miniaudio/soloud_miniaudio.cpp

@@ -40,6 +40,9 @@ namespace SoLoud
 #define MINIAUDIO_IMPLEMENTATION
 #define MA_NO_NULL
 #define MA_NO_DECODING
+#define MA_NO_WAV
+#define MA_NO_FLAC
+#define MA_NO_MP3
 #include "miniaudio.h"
 #include <math.h>
 
@@ -63,7 +66,7 @@ namespace SoLoud
     result miniaudio_init(SoLoud::Soloud *aSoloud, unsigned int aFlags, unsigned int aSamplerate, unsigned int aBuffer, unsigned int aChannels)
     {
         ma_device_config config = ma_device_config_init(ma_device_type_playback);
-        config.periodSizeInFrames = 128;
+        //config.periodSizeInFrames = aBuffer; // setting to aBuffer (like 2048) causes miniaudio to crash; let's just use the default.
         config.playback.format    = ma_format_f32;
         config.playback.channels  = aChannels;
         config.sampleRate         = aSamplerate;
@@ -84,4 +87,4 @@ namespace SoLoud
         return 0;
     }
 };
-#endif
+#endif

+ 9 - 16
soloud.mod/soloud/src/backend/opensles/soloud_opensles.cpp

@@ -48,10 +48,10 @@ namespace SoLoud
 // Error logging.
 #if defined( __ANDROID__ ) 
 #  include <android/log.h>
-#  define LOG_ERROR( _msg ) \
-   __android_log_print( ANDROID_LOG_ERROR, "SoLoud", _msg )
-#  define LOG_INFO( _msg ) \
-   __android_log_print( ANDROID_LOG_INFO, "SoLoud", _msg )
+#  define LOG_ERROR(...) \
+   __android_log_print( ANDROID_LOG_ERROR, "SoLoud", __VA_ARGS__ )
+#  define LOG_INFO(...) \
+   __android_log_print( ANDROID_LOG_INFO, "SoLoud", __VA_ARGS__ )
 
 #else
    printf( _msg )
@@ -172,7 +172,7 @@ namespace SoLoud
 	{
 		Soloud *soloud = static_cast<Soloud*>(context);
 		BackendData *data = static_cast<BackendData*>(soloud->mBackendData);
-		if( event & SL_PLAYEVENT_HEADATEND )
+		if (event & SL_PLAYEVENT_HEADATEND && data->buffersQueued > 0)
 		{
 			data->buffersQueued--;
 		}
@@ -275,17 +275,10 @@ namespace SoLoud
 
 		aSoloud->mBackendData = data;		// Must be set before callback
 
-		// Begin playing.
-		{
-			const int bufferSizeBytes = data->bufferSize * data->channels * sizeof(short);
-			(*data->playerBufferQueue)->Enqueue(data->playerBufferQueue, data->outputBuffers[0], bufferSizeBytes);
-			data->activeBuffer = (data->activeBuffer + 1) % NUM_BUFFERS;
-
-			(*data->player)->RegisterCallback(data->player, soloud_opensles_play_callback, aSoloud);
-			(*data->player)->SetCallbackEventsMask(data->player, SL_PLAYEVENT_HEADATEND);
-			(*data->player)->SetPlayState(data->player, SL_PLAYSTATE_PLAYING);
-
-		}
+		// Register callback
+		(*data->player)->RegisterCallback(data->player, soloud_opensles_play_callback, aSoloud);
+		(*data->player)->SetCallbackEventsMask(data->player, SL_PLAYEVENT_HEADATEND);
+		(*data->player)->SetPlayState(data->player, SL_PLAYSTATE_PLAYING);
 
 		//
 		aSoloud->postinit_internal(aSamplerate,data->bufferSize,aFlags,2);

+ 22 - 0
soloud.mod/soloud/src/core/soloud.cpp

@@ -116,6 +116,8 @@ namespace SoLoud
 		mAudioThreadMutex = NULL;
 		mPostClipScaler = 0;
 		mBackendCleanupFunc = NULL;
+		mBackendPauseFunc = NULL;
+		mBackendResumeFunc = NULL;
 		mChannels = 2;		
 		mStreamTime = 0;
 		mLastClockedTime = 0;
@@ -189,6 +191,9 @@ namespace SoLoud
 
 	void Soloud::deinit()
 	{
+		// Make sure no audio operation is currently pending
+		lockAudioMutex_internal();
+		unlockAudioMutex_internal();
 		SOLOUD_ASSERT(!mInsideAudioThreadMutex);
 		stopAll();
 		if (mBackendCleanupFunc)
@@ -566,6 +571,23 @@ namespace SoLoud
 		return 0;
 	}
 
+	result Soloud::pause()
+	{
+		if (mBackendPauseFunc)
+			return mBackendPauseFunc(this);
+
+		return NOT_IMPLEMENTED;
+	}
+
+	result Soloud::resume()
+	{
+		if (mBackendResumeFunc)
+			return mBackendResumeFunc(this);
+
+		return NOT_IMPLEMENTED;
+	}
+
+
 	void Soloud::postinit_internal(unsigned int aSamplerate, unsigned int aBufferSize, unsigned int aFlags, unsigned int aChannels)
 	{		
 		mGlobalVolume = 1;

+ 1 - 1
soloud.mod/soloud/src/core/soloud_audiosource.cpp

@@ -176,7 +176,7 @@ namespace SoLoud
 			getAudio(mScratch, samples, samples);
 			samples_to_discard -= samples;
 		}
-		mStreamPosition = offset;
+		mStreamPosition = aSeconds;
 		return SO_NO_ERROR;
 	}
 

+ 1 - 1
soloud.mod/soloud/src/core/soloud_core_voicegroup.cpp

@@ -39,7 +39,7 @@ namespace SoLoud
 		{
 			if (mVoiceGroup[i] == NULL)
 			{
-				mVoiceGroup[i] = new unsigned int[16];
+				mVoiceGroup[i] = new unsigned int[17];
 				if (mVoiceGroup[i] == NULL)
 				{
 					unlockAudioMutex_internal();

+ 6 - 6
soloud.mod/soloud/src/core/soloud_file.cpp

@@ -53,11 +53,11 @@ namespace SoLoud
 		return d;
 	}
 
-DiskFile::DiskFile(FILE *fp):
-mFileHandle(fp)
-{
-
-}
+DiskFile::DiskFile(FILE *fp):
+mFileHandle(fp)
+{
+
+}
 
 	unsigned int DiskFile::read(unsigned char *aDst, unsigned int aBytes)
 	{
@@ -308,4 +308,4 @@ extern "C"
 		*f = Soloud_Filehack_fopen(aFilename, 0);
 		return 1;
 	}
-}
+}

+ 147 - 0
soloud.mod/soloud/src/filter/soloud_duckfilter.cpp

@@ -0,0 +1,147 @@
+/*
+SoLoud audio engine
+Copyright (c) 2013-2021 Jari Komppa
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+   1. The origin of this software must not be misrepresented; you must not
+   claim that you wrote the original software. If you use this software
+   in a product, an acknowledgment in the product documentation would be
+   appreciated but is not required.
+
+   2. Altered source versions must be plainly marked as such, and must not be
+   misrepresented as being the original software.
+
+   3. This notice may not be removed or altered from any source
+   distribution.
+*/
+
+#include "soloud.h"
+#include "soloud_duckfilter.h"
+
+namespace SoLoud
+{
+	DuckFilterInstance::DuckFilterInstance(DuckFilter *aParent)
+	{
+		initParams(4);
+		mParam[DuckFilter::ONRAMP] = aParent->mOnRamp;
+		mParam[DuckFilter::OFFRAMP] = aParent->mOffRamp;
+		mParam[DuckFilter::LEVEL] = aParent->mLevel;
+		mListenTo = aParent->mListenTo;
+		mSoloud = aParent->mSoloud;
+		mCurrentLevel = 1;
+	}
+
+	void DuckFilterInstance::filter(float *aBuffer, unsigned int aSamples, unsigned int aBufferSize, unsigned int aChannels, float aSamplerate, double aTime)
+	{
+		updateParams(aTime);
+		float onramp_step = 1;
+		if (mParam[DuckFilter::ONRAMP] > 0.01)
+			onramp_step = (1.0f - mParam[DuckFilter::LEVEL]) / (mParam[DuckFilter::ONRAMP] * aSamplerate);
+		float offramp_step = 1;
+		if (mParam[DuckFilter::OFFRAMP] > 0.01)
+			offramp_step = (1.0f - mParam[DuckFilter::LEVEL]) / (mParam[DuckFilter::OFFRAMP] * aSamplerate);
+
+		int soundOn = 0;
+		if (mSoloud)
+		{
+			int voiceno = mSoloud->getVoiceFromHandle_internal(mListenTo);
+			if (voiceno != -1)
+			{
+				BusInstance* bi = (BusInstance*)mSoloud->mVoice[voiceno];
+				float v = 0;
+				for (unsigned int i = 0; i < bi->mChannels; i++)
+					v += bi->mVisualizationChannelVolume[i];
+				if (v > 0.01f)
+					soundOn = 1;
+			}
+		}
+		float level = mCurrentLevel;
+		for (unsigned int j = 0; j < aChannels; j++)
+		{
+			level = mCurrentLevel;
+			int bchofs = j * aBufferSize;
+			for (unsigned int i = 0; i < aSamples; i++)
+			{
+				if (soundOn && level > mParam[DuckFilter::LEVEL])
+					level -= onramp_step;
+				if (!soundOn && level < 1)
+					level += offramp_step;
+				if (level < mParam[DuckFilter::LEVEL]) level = mParam[DuckFilter::LEVEL];
+				if (level > 1) level = 1;
+				aBuffer[i + bchofs] += (-aBuffer[i + bchofs] + aBuffer[i + bchofs] * level) * mParam[DuckFilter::WET];
+			}
+		}
+		mCurrentLevel = level;
+	}
+
+	DuckFilterInstance::~DuckFilterInstance()
+	{
+	}
+
+	DuckFilter::DuckFilter()
+	{
+		mSoloud = 0;
+		mOnRamp = 0.1f;
+		mOffRamp = 0.5f;
+		mLevel = 0.5f;
+	}
+
+	result DuckFilter::setParams(Soloud* aSoloud, handle aListenTo, float aOnRamp, float aOffRamp, float aLevel)
+	{
+		if (aOnRamp < 0.0f || aOffRamp < 0.0f || aLevel < 0.0f || aSoloud == 0 || !aSoloud->isValidVoiceHandle(aListenTo))
+			return INVALID_PARAMETER;
+		
+		mListenTo = aListenTo;
+		mOnRamp = aOnRamp;
+		mOffRamp = aOffRamp;
+		mLevel = aLevel;
+		mSoloud = aSoloud;
+		
+		return 0;
+	}
+
+	int DuckFilter::getParamCount()
+	{
+		return 4;
+	}
+
+	const char* DuckFilter::getParamName(unsigned int aParamIndex)
+	{
+		if (aParamIndex > 3)
+			return 0;
+		const char *names[4] = {
+			"Wet",
+			"OnRamp",
+			"OffRamp",
+			"Level"
+		};
+		return names[aParamIndex];
+	}
+
+	unsigned int DuckFilter::getParamType(unsigned int aParamIndex)
+	{
+		return FLOAT_PARAM;
+	}
+
+	float DuckFilter::getParamMax(unsigned int aParamIndex)
+	{
+		return 1;
+	}
+
+	float DuckFilter::getParamMin(unsigned int aParamIndex)
+	{
+		return 0;
+	}
+
+	FilterInstance *DuckFilter::createInstance()
+	{
+		return new DuckFilterInstance(this);
+	}
+}

+ 1 - 1
soloud.mod/soloud/src/filter/soloud_eqfilter.cpp

@@ -68,7 +68,7 @@ namespace SoLoud
 			if (p1 < 0) p1 = 0;
 			if (p0 < 0) p0 = 0;
 			if (p3 > 7) p3 = 7;
-			float v = (i % (aSamples / 16)) / (float)(aSamples / 16);
+			float v = (float)(i % (aSamples / 16)) / (float)(aSamples / 16);
 			aFFTBuffer[p * 2] *= catmullrom(v, mParam[p0 + 1], mParam[p1 + 1], mParam[p2 + 1], mParam[p3 + 1]);
 		}
 		memset(aFFTBuffer + aSamples, 0, sizeof(float) * aSamples);

+ 1 - 1
soloud.mod/source.bmx

@@ -1,4 +1,4 @@
-' Copyright (c) 2016-2020 Bruce A Henderson
+' Copyright (c) 2016-2022 Bruce A Henderson
 '
 ' This software is provided 'as-is', without any express or implied
 ' warranty. In no event will the authors be held liable for any damages

Some files were not shown because too many files changed in this diff