mirror of
https://github.com/azahar-emu/soundtouch
synced 2025-11-07 15:40:04 +01:00
Compare commits
262 Commits
soundtouch
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9ef8458d85 | ||
|
|
16956b94b9 | ||
|
|
5dede763ff | ||
|
|
e31e1715fb | ||
|
|
bc2a2f73ff | ||
|
|
d3f7e2530b | ||
|
|
ddf28667c9 | ||
|
|
7f35604eda | ||
|
|
4ae091f54f | ||
|
|
e83424d592 | ||
|
|
0095a3d933 | ||
|
|
077e73422f | ||
|
|
f0ef4cd853 | ||
|
|
7dce7268cd | ||
|
|
63002027de | ||
|
|
290b0b13e2 | ||
|
|
2a24e3b454 | ||
|
|
02c22eceea | ||
|
|
ba1cb7727e | ||
|
|
17b63eeb3e | ||
|
|
2e83c770b0 | ||
|
|
5e624fff73 | ||
|
|
1c6a90804b | ||
|
|
f921e5b586 | ||
|
|
6872a2b6d0 | ||
|
|
d90844f67d | ||
|
|
375e6ccfe9 | ||
|
|
74514f5597 | ||
|
|
3781ff5d55 | ||
|
|
e56457728c | ||
|
|
c4c922c7b9 | ||
|
|
28df544c48 | ||
|
|
dd2252e9af | ||
|
|
55bd933dba | ||
|
|
8726394399 | ||
|
|
170349af69 | ||
|
|
b477936716 | ||
|
|
cc24adfc6d | ||
|
|
808bf021e6 | ||
|
|
63db6bf344 | ||
|
|
05d2835f65 | ||
|
|
1eda9c0b01 | ||
|
|
230ae2f9a9 | ||
|
|
a88c82d0ab | ||
|
|
82cb3f99bb | ||
|
|
4070166f4a | ||
|
|
4bcbb3556f | ||
|
|
29fba832a7 | ||
|
|
9e798c0f7f | ||
|
|
ddc351bfb6 | ||
|
|
774257ab5f | ||
|
|
eaa9090f65 | ||
|
|
17925302ae | ||
|
|
8562287944 | ||
|
|
9f14bd8b6e | ||
|
|
64760eb34e | ||
|
|
bb0434dd6e | ||
|
|
1a07a649e4 | ||
|
|
bdafd3b08c | ||
|
|
ab2b8ca91f | ||
|
|
9fedba866e | ||
|
|
0afe414a18 | ||
|
|
b9092339f5 | ||
|
|
85c03d6063 | ||
|
|
7ede48e436 | ||
|
|
8a9d4acb12 | ||
|
|
d063c0d9f9 | ||
|
|
b9afe0ac11 | ||
|
|
572d12c3e9 | ||
|
|
e016ebfcd5 | ||
|
|
4fd8e1acb9 | ||
|
|
afb0e4a73f | ||
|
|
77cbbb2227 | ||
|
|
e1f315f535 | ||
|
|
82e9ebd075 | ||
|
|
fd8e4c6835 | ||
|
|
d7b7a2f3a1 | ||
|
|
7df5617a4b | ||
|
|
2e606befef | ||
|
|
268a91494b | ||
|
|
2adf2ae71d | ||
|
|
9f72a8aa6b | ||
|
|
d11a3adb2d | ||
|
|
847edf4548 | ||
|
|
3148382fa8 | ||
|
|
c65afe49f6 | ||
|
|
28b32c0fbb | ||
|
|
bd2149daf6 | ||
|
|
776443f914 | ||
|
|
65caafdc5f | ||
|
|
6dce1068d9 | ||
|
|
eb6d970970 | ||
|
|
f974b28682 | ||
|
|
220eb7857c | ||
|
|
dae91683bc | ||
|
|
fa223609d2 | ||
|
|
3617bd166b | ||
|
|
d8d86e1a92 | ||
|
|
e0e00878fc | ||
|
|
17a63e99d5 | ||
|
|
6533514372 | ||
|
|
81b0d74727 | ||
|
|
5e76cf2f6d | ||
|
|
f38cfa6850 | ||
|
|
762f56024b | ||
|
|
1d42d899ab | ||
|
|
bf3cec0244 | ||
|
|
a911a1e986 | ||
|
|
3e74d1d18f | ||
|
|
f382149086 | ||
|
|
308c3484f6 | ||
|
|
3d7bf376fd | ||
|
|
1e56c65ea5 | ||
|
|
fe15975a21 | ||
|
|
a046b6971d | ||
|
|
c4f1602474 | ||
|
|
244fbeac24 | ||
|
|
12cb25ed7b | ||
|
|
fb3ea4d9f0 | ||
|
|
2b2585bc74 | ||
|
|
eef1220d72 | ||
|
|
9205fc971e | ||
|
|
b9659b64c6 | ||
|
|
7f594f8b7d | ||
|
|
6d700259b9 | ||
|
|
dad1d566c4 | ||
|
|
41a2cd3e6b | ||
|
|
09e04252dd | ||
|
|
59129fa33d | ||
|
|
a1c400eb2c | ||
|
|
12eaa21e14 | ||
|
|
bdbe1bf551 | ||
|
|
1d63bbf8e1 | ||
|
|
79cbdb1140 | ||
|
|
3ea4f5c7b3 | ||
|
|
68df82bd5b | ||
|
|
00241ebba1 | ||
|
|
1e9ec6f54b | ||
|
|
50348640f7 | ||
|
|
1e9c3bce2d | ||
|
|
5e3ca30225 | ||
|
|
46531e5b92 | ||
|
|
e024068905 | ||
|
|
c38f0506da | ||
|
|
cca9271e98 | ||
|
|
9e02d9b04f | ||
|
|
1ab7e7ccd0 | ||
|
|
4aaac92874 | ||
|
|
107f2c5d20 | ||
|
|
4b6060adfe | ||
|
|
1f7f681f9d | ||
|
|
81cf74cf4c | ||
|
|
5c168a55ff | ||
|
|
f71db0d2c3 | ||
|
|
0093b63141 | ||
|
|
6ee56b1c17 | ||
|
|
6b6c36d3e1 | ||
|
|
8f6f91f9b3 | ||
|
|
007481d711 | ||
|
|
47f74e83ef | ||
|
|
c4154b063f | ||
|
|
3f2ad229bb | ||
|
|
3feea728d5 | ||
|
|
e765f8146f | ||
|
|
669ab8c974 | ||
|
|
7b097533a9 | ||
|
|
30b017d112 | ||
|
|
20e4bf0b04 | ||
|
|
ec9ba968f5 | ||
|
|
407f516e0d | ||
|
|
05a3403137 | ||
|
|
80281c8e1b | ||
|
|
5f8720dae6 | ||
|
|
6e8d58cbcc | ||
|
|
b56859a3fe | ||
|
|
bbeab39f0a | ||
|
|
cd74dccaf1 | ||
|
|
bd0a806285 | ||
|
|
59e6726118 | ||
|
|
92bfdd1e8d | ||
|
|
c31fca9c9f | ||
|
|
6f82bdebdb | ||
|
|
5d9bc2fdf3 | ||
|
|
1049304b5d | ||
|
|
e302cd7123 | ||
|
|
753848865d | ||
|
|
ac03757ec8 | ||
|
|
62d63e7881 | ||
|
|
f574c92dcf | ||
|
|
8023db738f | ||
|
|
882f248a0c | ||
|
|
9287800b65 | ||
|
|
e1c7cffbcd | ||
|
|
81cb7a3406 | ||
|
|
089edd12f4 | ||
|
|
fe44590ab6 | ||
|
|
5f84fe3eb7 | ||
|
|
18a230a54c | ||
|
|
04b96e6b52 | ||
|
|
8c06711f86 | ||
|
|
db04025351 | ||
|
|
c9507ff7f1 | ||
|
|
da748228b9 | ||
|
|
2e8250d03a | ||
|
|
1a3c1cd50e | ||
|
|
2f2b3d756a | ||
|
|
2cbd68c32b | ||
|
|
9ff52beba7 | ||
|
|
55dcf4a956 | ||
|
|
76f76ffb84 | ||
|
|
83e46b5644 | ||
|
|
5ad8994798 | ||
|
|
4bc115df86 | ||
|
|
d44723ea57 | ||
|
|
92973bc18e | ||
|
|
1040bd1d28 | ||
|
|
708f1d7e0b | ||
|
|
cbfec4188e | ||
|
|
32dcebc1d7 | ||
|
|
c36e2fa958 | ||
|
|
3e9cc3fd4b | ||
|
|
123e3299fe | ||
|
|
6935032a52 | ||
|
|
d7d0a5c0f9 | ||
|
|
126d1ac41d | ||
|
|
bfc89b45a9 | ||
|
|
5100cefbb0 | ||
|
|
0715880b1f | ||
|
|
099a6240eb | ||
|
|
1da2f8e700 | ||
|
|
e23bd6d093 | ||
|
|
f68f8e9e09 | ||
|
|
01ba661351 | ||
|
|
9406cf5b28 | ||
|
|
b2ff6711d0 | ||
|
|
33638a2243 | ||
|
|
4da3b1eaf9 | ||
|
|
2d91306ac0 | ||
|
|
1f6391a9ca | ||
|
|
afdfb293f6 | ||
|
|
746a90d610 | ||
|
|
a61c28e36a | ||
|
|
026ebe3841 | ||
|
|
f16b062219 | ||
|
|
a09135884a | ||
|
|
abfeb3fcc9 | ||
|
|
8174f6bc10 | ||
|
|
510a94e990 | ||
|
|
510ac08657 | ||
|
|
70d7518295 | ||
|
|
55aa6f15e9 | ||
|
|
8c65661b91 | ||
|
|
9bb265e3cd | ||
|
|
c6bf1c7585 | ||
|
|
7a0a940953 | ||
|
|
4d8825ef6d | ||
|
|
7dea63e0e1 | ||
|
|
46a7dc3c39 | ||
|
|
9b902ef3b7 | ||
|
|
c3f4ff9532 | ||
|
|
91305a5806 | ||
|
|
fbc2ace440 |
55
.gitignore
vendored
Normal file
55
.gitignore
vendored
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
bin
|
||||||
|
lib
|
||||||
|
|
||||||
|
# Win build files
|
||||||
|
*.dll
|
||||||
|
*.exe
|
||||||
|
*.lib
|
||||||
|
|
||||||
|
# GNU build files
|
||||||
|
*.o
|
||||||
|
*.so*
|
||||||
|
*.lo
|
||||||
|
*.P*
|
||||||
|
*.la*
|
||||||
|
*.a
|
||||||
|
*.pc
|
||||||
|
*config*
|
||||||
|
Makefile
|
||||||
|
Makefile.in
|
||||||
|
.libs
|
||||||
|
aclocal.m4
|
||||||
|
autom4te.cache
|
||||||
|
stamp-*
|
||||||
|
libtool
|
||||||
|
soundstretch
|
||||||
|
|
||||||
|
# Files generated by MSVC
|
||||||
|
*.bsc
|
||||||
|
*.suo
|
||||||
|
*.sdf
|
||||||
|
*.filters
|
||||||
|
*.user
|
||||||
|
source/SoundTouch/Win32/
|
||||||
|
source/SoundTouch/x64/
|
||||||
|
source/SoundStretch/Win32/
|
||||||
|
source/SoundStretch/x64/
|
||||||
|
source/SoundTouchDll/Win32/
|
||||||
|
source/SoundTouchDll/x64/
|
||||||
|
source/SoundTouchDll/DllTest/Win32/
|
||||||
|
source/SoundTouchDll/DllTest/x64/
|
||||||
|
.vs
|
||||||
|
|
||||||
|
# Files generated by Android Studio
|
||||||
|
source/android-lib/.gradle
|
||||||
|
source/android-lib/.idea
|
||||||
|
**/*.iml
|
||||||
|
source/android-lib/local.properties
|
||||||
|
source/android-lib/build
|
||||||
|
source/android-lib/.externalNativeBuild
|
||||||
|
|
||||||
|
# CMake build directory
|
||||||
|
build*
|
||||||
|
CMakeFiles
|
||||||
|
CMakeCache.txt
|
||||||
|
*.cmake
|
||||||
32
.vscode/tasks.json
vendored
Normal file
32
.vscode/tasks.json
vendored
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
{
|
||||||
|
// This is build task definition file for MS VisualStudio Code.
|
||||||
|
// See https://go.microsoft.com/fwlink/?LinkId=733558
|
||||||
|
// for the documentation about the tasks.json format
|
||||||
|
"version": "2.0.0",
|
||||||
|
"tasks": [
|
||||||
|
{
|
||||||
|
"label": "echo",
|
||||||
|
"type": "shell",
|
||||||
|
"command": "echo Hello"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "configure",
|
||||||
|
"type": "shell",
|
||||||
|
"command": "./bootstrap && ./configure"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "build",
|
||||||
|
"type": "shell",
|
||||||
|
"command": "make -j4",
|
||||||
|
"problemMatcher": [
|
||||||
|
"$gcc"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "clean",
|
||||||
|
"type": "shell",
|
||||||
|
"command": "make clean",
|
||||||
|
"problemMatcher": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
191
CMakeLists.txt
Normal file
191
CMakeLists.txt
Normal file
@ -0,0 +1,191 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.5)
|
||||||
|
project(SoundTouch VERSION 2.3.3 LANGUAGES CXX)
|
||||||
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
|
|
||||||
|
include(GNUInstallDirs)
|
||||||
|
|
||||||
|
set(COMPILE_OPTIONS)
|
||||||
|
|
||||||
|
if(MSVC)
|
||||||
|
set(COMPILE_DEFINITIONS /O2 /fp:fast)
|
||||||
|
else()
|
||||||
|
list(APPEND COMPILE_OPTIONS -Wall -Wextra -Wzero-as-null-pointer-constant -Wno-unknown-pragmas)
|
||||||
|
if(EMSCRIPTEN)
|
||||||
|
list(APPEND COMPILE_OPTIONS -O3)
|
||||||
|
else()
|
||||||
|
# Apply -ffast-math to allow compiler autovectorization generate effective SIMD code for arm compilation
|
||||||
|
list(APPEND COMPILE_OPTIONS -O3 -ffast-math)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
#####################
|
||||||
|
# SoundTouch library
|
||||||
|
|
||||||
|
add_library(SoundTouch
|
||||||
|
source/SoundTouch/AAFilter.cpp
|
||||||
|
source/SoundTouch/BPMDetect.cpp
|
||||||
|
source/SoundTouch/cpu_detect_x86.cpp
|
||||||
|
source/SoundTouch/FIFOSampleBuffer.cpp
|
||||||
|
source/SoundTouch/FIRFilter.cpp
|
||||||
|
source/SoundTouch/InterpolateCubic.cpp
|
||||||
|
source/SoundTouch/InterpolateLinear.cpp
|
||||||
|
source/SoundTouch/InterpolateShannon.cpp
|
||||||
|
source/SoundTouch/mmx_optimized.cpp
|
||||||
|
source/SoundTouch/PeakFinder.cpp
|
||||||
|
source/SoundTouch/RateTransposer.cpp
|
||||||
|
source/SoundTouch/SoundTouch.cpp
|
||||||
|
source/SoundTouch/sse_optimized.cpp
|
||||||
|
source/SoundTouch/TDStretch.cpp
|
||||||
|
)
|
||||||
|
target_include_directories(SoundTouch PUBLIC
|
||||||
|
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
|
||||||
|
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
|
||||||
|
)
|
||||||
|
|
||||||
|
target_compile_definitions(SoundTouch PRIVATE ${COMPILE_DEFINITIONS})
|
||||||
|
target_compile_options(SoundTouch PRIVATE ${COMPILE_OPTIONS})
|
||||||
|
|
||||||
|
if(BUILD_SHARED_LIBS)
|
||||||
|
set_target_properties(SoundTouch PROPERTIES
|
||||||
|
VERSION ${CMAKE_PROJECT_VERSION}
|
||||||
|
)
|
||||||
|
if(WIN32)
|
||||||
|
set_target_properties(SoundTouch PROPERTIES
|
||||||
|
WINDOWS_EXPORT_ALL_SYMBOLS TRUE
|
||||||
|
)
|
||||||
|
else()
|
||||||
|
set_target_properties(SoundTouch PROPERTIES
|
||||||
|
SOVERSION ${PROJECT_VERSION_MAJOR}
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
option(INTEGER_SAMPLES "Use integers instead of floats for samples" OFF)
|
||||||
|
if(INTEGER_SAMPLES)
|
||||||
|
target_compile_definitions(SoundTouch PRIVATE SOUNDTOUCH_INTEGER_SAMPLES)
|
||||||
|
else()
|
||||||
|
target_compile_definitions(SoundTouch PRIVATE SOUNDTOUCH_FLOAT_SAMPLES)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(CMAKE_SYSTEM_PROCESSOR MATCHES "^(armv7.*|armv8.*|aarch64.*)$")
|
||||||
|
set(NEON_CPU ON)
|
||||||
|
else()
|
||||||
|
set(NEON_CPU OFF)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
option(NEON "Use ARM Neon SIMD instructions if in ARM CPU" ON)
|
||||||
|
if(${NEON} AND ${NEON_CPU})
|
||||||
|
target_compile_definitions(SoundTouch PRIVATE SOUNDTOUCH_USE_NEON)
|
||||||
|
if(NOT CMAKE_SYSTEM_PROCESSOR MATCHES "^aarch64.*$")
|
||||||
|
target_compile_options(SoundTouch PRIVATE -mfpu=neon)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
find_package(OpenMP)
|
||||||
|
option(OPENMP "Use parallel multicore calculation through OpenMP" OFF)
|
||||||
|
if(OPENMP AND OPENMP_FOUND)
|
||||||
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
install(
|
||||||
|
FILES
|
||||||
|
include/BPMDetect.h
|
||||||
|
include/FIFOSampleBuffer.h
|
||||||
|
include/FIFOSamplePipe.h
|
||||||
|
include/STTypes.h
|
||||||
|
include/SoundTouch.h
|
||||||
|
include/soundtouch_config.h
|
||||||
|
DESTINATION
|
||||||
|
"${CMAKE_INSTALL_INCLUDEDIR}/soundtouch"
|
||||||
|
COMPONENT SoundTouch
|
||||||
|
)
|
||||||
|
|
||||||
|
install(TARGETS SoundTouch
|
||||||
|
EXPORT SoundTouchTargets
|
||||||
|
ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}"
|
||||||
|
LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}"
|
||||||
|
RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}"
|
||||||
|
INCLUDES DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}"
|
||||||
|
COMPONENT SoundTouch
|
||||||
|
)
|
||||||
|
|
||||||
|
#######################
|
||||||
|
# soundstretch utility
|
||||||
|
|
||||||
|
option(SOUNDSTRETCH "Build soundstretch command line utility." ON)
|
||||||
|
if(SOUNDSTRETCH)
|
||||||
|
add_executable(soundstretch
|
||||||
|
source/SoundStretch/main.cpp
|
||||||
|
source/SoundStretch/RunParameters.cpp
|
||||||
|
source/SoundStretch/WavFile.cpp
|
||||||
|
)
|
||||||
|
target_include_directories(soundstretch PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>)
|
||||||
|
target_compile_definitions(soundstretch PRIVATE ${COMPILE_DEFINITIONS})
|
||||||
|
target_compile_options(soundstretch PRIVATE ${COMPILE_OPTIONS})
|
||||||
|
target_link_libraries(soundstretch PRIVATE SoundTouch)
|
||||||
|
if(INTEGER_SAMPLES)
|
||||||
|
target_compile_definitions(soundstretch PRIVATE SOUNDTOUCH_INTEGER_SAMPLES)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
install(TARGETS soundstretch
|
||||||
|
DESTINATION bin
|
||||||
|
COMPONENT soundstretch
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
########################
|
||||||
|
# SoundTouchDll library
|
||||||
|
|
||||||
|
option(SOUNDTOUCH_DLL "Build SoundTouchDLL C wrapper library" OFF)
|
||||||
|
if(SOUNDTOUCH_DLL)
|
||||||
|
add_library(SoundTouchDLL SHARED
|
||||||
|
source/SoundTouchDLL/SoundTouchDLL.cpp
|
||||||
|
source/SoundTouchDLL/SoundTouchDLL.rc
|
||||||
|
)
|
||||||
|
set_target_properties(SoundTouch PROPERTIES POSITION_INDEPENDENT_CODE TRUE)
|
||||||
|
target_compile_options(SoundTouchDLL PRIVATE ${COMPILE_OPTIONS})
|
||||||
|
set_target_properties(SoundTouchDLL PROPERTIES CXX_VISIBILITY_PRESET hidden)
|
||||||
|
target_compile_definitions(SoundTouchDLL PRIVATE DLL_EXPORTS)
|
||||||
|
target_include_directories(SoundTouchDLL PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>)
|
||||||
|
target_link_libraries(SoundTouchDLL PRIVATE SoundTouch)
|
||||||
|
install(FILES source/SoundTouchDLL/SoundTouchDLL.h DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/soundtouch" COMPONENT SoundTouchDLL)
|
||||||
|
install(TARGETS SoundTouchDLL EXPORT SoundTouchTargets COMPONENT SoundTouchDLL)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
########################
|
||||||
|
|
||||||
|
# pkgconfig
|
||||||
|
set(prefix "${CMAKE_INSTALL_PREFIX}")
|
||||||
|
set(execprefix "\${prefix}")
|
||||||
|
set(libdir "\${prefix}/${CMAKE_INSTALL_LIBDIR}")
|
||||||
|
set(includedir "\${prefix}/${CMAKE_INSTALL_INCLUDEDIR}")
|
||||||
|
set(VERSION "${CMAKE_PROJECT_VERSION}")
|
||||||
|
configure_file(soundtouch.pc.in "${CMAKE_CURRENT_BINARY_DIR}/soundtouch.pc" @ONLY)
|
||||||
|
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/soundtouch.pc" DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig" COMPONENT SoundTouch)
|
||||||
|
|
||||||
|
# CMake config
|
||||||
|
include(CMakePackageConfigHelpers)
|
||||||
|
set(SOUNDTOUCH_INSTALL_CMAKEDIR "${CMAKE_INSTALL_LIBDIR}/cmake/SoundTouch")
|
||||||
|
install(
|
||||||
|
EXPORT SoundTouchTargets
|
||||||
|
FILE SoundTouchTargets.cmake
|
||||||
|
NAMESPACE SoundTouch::
|
||||||
|
DESTINATION "${SOUNDTOUCH_INSTALL_CMAKEDIR}"
|
||||||
|
COMPONENT SoundTouch
|
||||||
|
)
|
||||||
|
configure_package_config_file(SoundTouchConfig.cmake.in
|
||||||
|
"${CMAKE_CURRENT_BINARY_DIR}/SoundTouchConfig.cmake"
|
||||||
|
INSTALL_DESTINATION "${SOUNDTOUCH_INSTALL_CMAKEDIR}"
|
||||||
|
)
|
||||||
|
write_basic_package_version_file(
|
||||||
|
"${CMAKE_CURRENT_BINARY_DIR}/SoundTouchConfigVersion.cmake"
|
||||||
|
VERSION "${CMAKE_PROJECT_VERSION}"
|
||||||
|
COMPATIBILITY SameMajorVersion
|
||||||
|
)
|
||||||
|
install(
|
||||||
|
FILES
|
||||||
|
"${CMAKE_CURRENT_BINARY_DIR}/SoundTouchConfig.cmake"
|
||||||
|
"${CMAKE_CURRENT_BINARY_DIR}/SoundTouchConfigVersion.cmake"
|
||||||
|
DESTINATION "${SOUNDTOUCH_INSTALL_CMAKEDIR}"
|
||||||
|
COMPONENT SoundTouch
|
||||||
|
)
|
||||||
@ -2,7 +2,7 @@
|
|||||||
Version 2.1, February 1999
|
Version 2.1, February 1999
|
||||||
|
|
||||||
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
|
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
|
||||||
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
Everyone is permitted to copy and distribute verbatim copies
|
Everyone is permitted to copy and distribute verbatim copies
|
||||||
of this license document, but changing it is not allowed.
|
of this license document, but changing it is not allowed.
|
||||||
|
|
||||||
@ -117,7 +117,7 @@ be combined with the library in order to run.
|
|||||||
|
|
||||||
0. This License Agreement applies to any software library or other
|
0. This License Agreement applies to any software library or other
|
||||||
program which contains a notice placed by the copyright holder or
|
program which contains a notice placed by the copyright holder or
|
||||||
other authoried party saying it may be distributed under the terms of
|
other authorized party saying it may be distributed under the terms of
|
||||||
this Lesser General Public License (also called "this License").
|
this Lesser General Public License (also called "this License").
|
||||||
Each licensee is addressed as "you".
|
Each licensee is addressed as "you".
|
||||||
|
|
||||||
|
|||||||
@ -1,9 +1,5 @@
|
|||||||
## Process this file with automake to create Makefile.in
|
## Process this file with automake to create Makefile.in
|
||||||
##
|
##
|
||||||
## $Id$
|
|
||||||
##
|
|
||||||
## Copyright (C) 2003 - David W. Durham
|
|
||||||
##
|
|
||||||
## This file is part of SoundTouch, an audio processing library for pitch/time adjustments
|
## This file is part of SoundTouch, an audio processing library for pitch/time adjustments
|
||||||
##
|
##
|
||||||
## SoundTouch is free software; you can redistribute it and/or modify it under the
|
## SoundTouch is free software; you can redistribute it and/or modify it under the
|
||||||
@ -26,9 +22,8 @@ include $(top_srcdir)/config/am_include.mk
|
|||||||
## subdirectories Start at frontend_fox/Makefile.am to see how everything works.
|
## subdirectories Start at frontend_fox/Makefile.am to see how everything works.
|
||||||
SUBDIRS=include source
|
SUBDIRS=include source
|
||||||
|
|
||||||
|
|
||||||
# list files that are documentation to be packaged in a release tarball and installed
|
# list files that are documentation to be packaged in a release tarball and installed
|
||||||
pkgdoc_DATA=COPYING.TXT README.html
|
dist_doc_DATA=COPYING.TXT README.html
|
||||||
|
|
||||||
# extra data files that are to be pacakged in a release tarball and installed into the data directory
|
# extra data files that are to be pacakged in a release tarball and installed into the data directory
|
||||||
#pkgdata_DATA=
|
#pkgdata_DATA=
|
||||||
|
|||||||
461
README.html
461
README.html
@ -1,22 +1,22 @@
|
|||||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
|
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
|
||||||
<html>
|
<html>
|
||||||
|
|
||||||
<head>
|
<head>
|
||||||
<title>SoundTouch library README</title>
|
<title>SoundTouch library README</title>
|
||||||
<meta http-equiv="Content-Type"
|
|
||||||
content="text/html; charset=windows-1252">
|
|
||||||
<meta http-equiv="Content-Language" content="en-us">
|
<meta http-equiv="Content-Language" content="en-us">
|
||||||
<meta name="author" content="Olli Parviainen">
|
<meta name="author" content="Olli Parviainen">
|
||||||
<meta name="description"
|
<meta name="description" content="Readme file for SoundTouch audio processing library">
|
||||||
content="Readme file for SoundTouch audio processing library">
|
<style>
|
||||||
<meta name="GENERATOR" content="Microsoft FrontPage 4.0">
|
body {
|
||||||
<meta name="ProgId" content="FrontPage.Editor.Document">
|
font-family: Arial, Helvetica;
|
||||||
<style> <!-- .normal { font-family: Arial }
|
}
|
||||||
--></style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body class="normal">
|
<body class="normal">
|
||||||
<hr>
|
<hr>
|
||||||
<h1>SoundTouch audio processing library v1.7.0</h1>
|
<h1>SoundTouch audio processing library v2.3.3</h1>
|
||||||
<p class="normal">SoundTouch library Copyright © Olli Parviainen 2001-2012 </p>
|
<p class="normal">SoundTouch library Copyright © Olli Parviainen 2001-2024</p>
|
||||||
<hr>
|
<hr>
|
||||||
<h2>1. Introduction </h2>
|
<h2>1. Introduction </h2>
|
||||||
<p>SoundTouch is an open-source audio processing library that allows
|
<p>SoundTouch is an open-source audio processing library that allows
|
||||||
@ -33,26 +33,24 @@ same time </li>
|
|||||||
</ul>
|
</ul>
|
||||||
<h3>1.1 Contact information </h3>
|
<h3>1.1 Contact information </h3>
|
||||||
<p>Author email: oparviai 'at' iki.fi </p>
|
<p>Author email: oparviai 'at' iki.fi </p>
|
||||||
<p>SoundTouch WWW page: <a href="http://www.surina.net/soundtouch">http://www.surina.net/soundtouch</a></p>
|
<p>SoundTouch WWW page: <a href="http://soundtouch.surina.net">http://soundtouch.surina.net</a></p>
|
||||||
|
<p>SoundTouch git repository: <a
|
||||||
|
href="https://codeberg.org/soundtouch/soundtouch.git">https://codeberg.org/soundtouch/soundtouch.git</a></p>
|
||||||
<hr>
|
<hr>
|
||||||
<h2>2. Compiling SoundTouch</h2>
|
<h2>2. Compiling SoundTouch</h2>
|
||||||
<p>Before compiling, notice that you can choose the sample data format
|
<p>Before compiling, notice that you can choose the sample data format if it's
|
||||||
if it's desirable to use floating point sample data instead of 16bit
|
desirable to use 16bit integer sample data instead of floating point samples. See
|
||||||
integers. See section "sample data format" for more information.</p>
|
section "sample data format" for more information.</p>
|
||||||
|
<p>Also notice that SoundTouch can use OpenMP instructions for parallel
|
||||||
|
computation to accelerate the runtime processing speed in multi-core systems,
|
||||||
|
however, these improvements need to be separately enabled before compiling. See
|
||||||
|
OpenMP notes in Chapter 3 below.</p>
|
||||||
<h3>2.1. Building in Microsoft Windows</h3>
|
<h3>2.1. Building in Microsoft Windows</h3>
|
||||||
<p>Project files for Microsoft Visual C++ 6.0 and Visual C++ .NET are
|
<p>Project files for Microsoft Visual C++ are supplied with the source
|
||||||
supplied with the source code package.<br>
|
code package. Go to Microsoft WWW page to download
|
||||||
|
<a href="http://www.visualstudio.com/en-US/products/visual-studio-express-vs">
|
||||||
|
Microsoft Visual Studio Express version for free</a>.
|
||||||
</p>
|
</p>
|
||||||
<p> Please notice that SoundTouch library uses processor-specific
|
|
||||||
optimizations for Pentium III and AMD processors. Visual Studio .NET
|
|
||||||
and later versions supports the required instructions by default, but
|
|
||||||
Visual Studio 6.0 requires a processor pack upgrade to be installed in
|
|
||||||
order to support these optimizations. The processor pack upgrade can be
|
|
||||||
downloaded from Microsoft site at this URL:</p>
|
|
||||||
<p><a href="http://msdn.microsoft.com/en-us/vstudio/aa718349.aspx">http://msdn.microsoft.com/en-us/vstudio/aa718349.aspx</a></p>
|
|
||||||
<p>If the above URL is unavailable or removed, go to <a
|
|
||||||
href="http://msdn.microsoft.com/"> http://msdn.microsoft.com</a> and
|
|
||||||
perform a search with keywords "processor pack". </p>
|
|
||||||
<p>To build the binaries with Visual C++ compiler, either run
|
<p>To build the binaries with Visual C++ compiler, either run
|
||||||
"make-win.bat" script, or open the appropriate project files in source
|
"make-win.bat" script, or open the appropriate project files in source
|
||||||
code directories with Visual Studio. The final executable will appear
|
code directories with Visual Studio. The final executable will appear
|
||||||
@ -61,9 +59,30 @@ instead of the make-win.bat script, directories bin and lib may need to
|
|||||||
be created manually to the SoundTouch package root for the final
|
be created manually to the SoundTouch package root for the final
|
||||||
executables. The make-win.bat script creates these directories
|
executables. The make-win.bat script creates these directories
|
||||||
automatically. </p>
|
automatically. </p>
|
||||||
|
<p><strong>C# example</strong>: The source code package includes also a C# example
|
||||||
|
application for Windows that shows how to invoke SoundTouch.dll
|
||||||
|
dynamic-load library for processing mp3 audio.
|
||||||
|
<p><strong>OpenMP NOTE</strong>: If activating the OpenMP parallel computing in
|
||||||
|
the compilation, the target program will require additional vcomp dll library to
|
||||||
|
properly run. In Visual C++ 9.0 these libraries can be found in the following
|
||||||
|
folders.</p>
|
||||||
|
<ul>
|
||||||
|
<li>x86 32bit: C:\Program Files (x86)\Microsoft Visual Studio
|
||||||
|
9.0\VC\redist\x86\Microsoft.VC90.OPENMP\vcomp90.dll</li>
|
||||||
|
<li>x64 64bit: C:\Program Files (x86)\Microsoft Visual Studio
|
||||||
|
9.0\VC\redist\amd64\Microsoft.VC90.OPENMP\vcomp90.dll</li>
|
||||||
|
</ul>
|
||||||
|
<p>In other VC++ versions the required library will be expectedly found in similar
|
||||||
|
"redist" location.</p>
|
||||||
|
<p>Notice that as minor demonstration of a "dll hell" phenomenon both the 32-bit
|
||||||
|
and 64-bit version of vcomp90.dll have the same filename but different contents,
|
||||||
|
thus choose the proper version to allow the program to start.</p>
|
||||||
<h3>2.2. Building in Gnu platforms</h3>
|
<h3>2.2. Building in Gnu platforms</h3>
|
||||||
<p>The SoundTouch library compiles in practically any platform
|
<p>The SoundTouch library compiles in practically any platform
|
||||||
supporting GNU compiler (GCC) tools. SoundTouch requires GCC version 4.3 or later.</p>
|
supporting GNU compiler (GCC) tools.
|
||||||
|
<h4>2.2.1 Compiling with autotools</h4>
|
||||||
|
<p>To install build prerequisites for 'autotools' tool chain:</p>
|
||||||
|
<pre> sudo apt-get install automake autoconf libtool build-essential</pre>
|
||||||
<p>To build and install the binaries, run the following commands in
|
<p>To build and install the binaries, run the following commands in
|
||||||
/soundtouch directory:</p>
|
/soundtouch directory:</p>
|
||||||
<table border="0" cellpadding="0" cellspacing="4">
|
<table border="0" cellpadding="0" cellspacing="4">
|
||||||
@ -92,7 +111,9 @@ Notice that "configure" file is not available before running the
|
|||||||
<pre>make -</pre>
|
<pre>make -</pre>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<p>Builds the SoundTouch library & SoundStretch utility.</p>
|
<p>Builds the SoundTouch library & SoundStretch utility. You can
|
||||||
|
optionally add "-j" switch after "make" to speed up the compilation in
|
||||||
|
multi-core systems.</p>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr valign="top">
|
<tr valign="top">
|
||||||
@ -108,43 +129,66 @@ destination locations.</p>
|
|||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
<h4><b>2.2.1 Required GNU tools</b> </h4>
|
|
||||||
<p> <span style="font-weight: bold;">Bash shell</span>, <span
|
|
||||||
style="font-weight: bold;">GNU C++ compiler</span>, <span
|
|
||||||
style="font-weight: bold;">libtool</span>, <span
|
|
||||||
style="font-weight: bold;">autoconf</span> and <span
|
|
||||||
style="font-weight: bold;">automake</span> tools
|
|
||||||
are required for compiling the SoundTouch library. These are usually
|
|
||||||
included with the GNU/Linux distribution, but if not, install these
|
|
||||||
packages first. For example, Ubuntu Linux can acquire and install
|
|
||||||
these with the following command:</p>
|
|
||||||
<pre><b>sudo apt-get install automake autoconf libtool build-essential</b></pre>
|
|
||||||
<h4><b>2.2.2 Problems with GCC compiler compatibility</b></h4>
|
|
||||||
<p>At the release time the SoundTouch package has been tested to
|
|
||||||
compile in GNU/Linux platform. However, If you have problems getting the
|
|
||||||
SoundTouch library compiled, try disabling optimizations that are specific for
|
|
||||||
x86 processors by running <b>./configure</b> script with switch
|
|
||||||
<blockquote>
|
|
||||||
<pre>--enable-x86-optimizations=no</pre>
|
|
||||||
</blockquote>
|
|
||||||
|
|
||||||
Alternatively, if you don't use GNU Configure system, edit file "include/STTypes.h"
|
<b>Compiling portable Shared Library / DLL version</b>
|
||||||
directly and remove the following definition:<blockquote>
|
<p> The GNU autotools compilation automatically builds an additional dynamic-link version
|
||||||
<pre>#define SOUNDTOUCH_ALLOW_X86_OPTIMIZATIONS 1</pre>
|
of SoundTouch library that features position-independent code and "C"-style API that is
|
||||||
</blockquote>
|
more suitable for calling the SoundTouch routines from other programming languages.</p>
|
||||||
|
<p>This dynamic-link library is built under source/SoundTouchDLL directory, whose
|
||||||
|
subdirectories also comtain simple example apps that use the dynamic-link library.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h4><b>2.2.2 Compiling with cmake</b></h4>
|
||||||
|
<p>'cmake' build scripts are provided as an alternative to the autotools toolchain.</p>
|
||||||
|
<p>To install cmake build prerequisites:</p>
|
||||||
|
<pre> sudo apt-get install libtool build-essential cmake</pre>
|
||||||
|
<p>To build:</p>
|
||||||
|
<pre>
|
||||||
|
cmake .
|
||||||
|
make -j
|
||||||
|
make install</pre>
|
||||||
|
<p>To list available build options:</p>
|
||||||
|
<pre>
|
||||||
|
cmake -LH</pre>
|
||||||
|
<p>To compile the additional portable Shared Library / DLL version with the native C-language API:</p>
|
||||||
|
<pre>
|
||||||
|
cmake . -DSOUNDTOUCH_DLL=ON
|
||||||
|
make -j
|
||||||
|
make install</pre>
|
||||||
|
|
||||||
|
<h3>2.3. Building in Android</h3>
|
||||||
|
<p>Android compilation instructions are within the
|
||||||
|
source code package, see file "<b>source/Android-lib/README-SoundTouch-Android.html</b>"
|
||||||
|
in the source code package. </p>
|
||||||
|
<p>The Android compilation automatically builds separate .so library binaries
|
||||||
|
for ARM, X86 and MIPS processor architectures. For optimal device support,
|
||||||
|
include all these .so library binaries into the Android .apk application
|
||||||
|
package, so the target Android device can automatically choose the proper
|
||||||
|
library binary version to use.</p>
|
||||||
|
<p>The <strong>source/Android-lib</strong> folder includes also an Android
|
||||||
|
example application that processes WAV audio files using SoundTouch library in
|
||||||
|
Android devices.</p>
|
||||||
|
|
||||||
|
<h3>2.4. Building in Mac</h3>
|
||||||
|
<p>Install autoconf tool as instructed in <a
|
||||||
|
href="http://macappstore.org/autoconf/">http://macappstore.org/autoconf/</a>, or alternatively the 'cmake' toolchain.</p>
|
||||||
|
<p>Then, build as described above in section "Building in Gnu platforms".</p>
|
||||||
|
|
||||||
<hr>
|
<hr>
|
||||||
<h2>3. About implementation & Usage tips</h2>
|
<h2>3. About implementation & Usage tips <h3>3.1. Supported sample data formats</h3>
|
||||||
<h3>3.1. Supported sample data formats</h3>
|
|
||||||
<p>The sample data format can be chosen between 16bit signed integer
|
<p>The sample data format can be chosen between 16bit signed integer
|
||||||
and 32bit floating point values, the default is 32bit floating point. </p>
|
and 32bit floating point values.</p>
|
||||||
|
</p> The default sample type is 32bit floating point format,
|
||||||
|
which also provides better sound quality than integer format because
|
||||||
|
integer algorithms need to scale already intermediate calculation results to
|
||||||
|
avoid integer overflows. These early integer scalings can slightly degrade
|
||||||
|
output quality.</p>
|
||||||
<p> In Windows environment, the sample data format is chosen in file
|
<p> In Windows environment, the sample data format is chosen in file
|
||||||
"STTypes.h" by choosing one of the following defines:</p>
|
"STTypes.h" by choosing one of the following defines:</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li> <span style="font-weight: bold;">#define
|
<li> <span style="font-weight: bold;">#define
|
||||||
SOUNDTOUCH_INTEGER_SAMPLES</span> for 16bit signed integer</li>
|
SOUNDTOUCH_INTEGER_SAMPLES</span> for 16bit signed integer</li>
|
||||||
<li> <span style="font-weight: bold;">#define </span><span
|
<li> <span style="font-weight: bold;">#define </span><span style="font-weight: bold;">SOUNDTOUCH_</span><span
|
||||||
style="font-weight: bold;">SOUNDTOUCH_</span><span
|
|
||||||
style="font-weight: bold;">FLOAT_SAMPLES</span> for 32bit floating
|
style="font-weight: bold;">FLOAT_SAMPLES</span> for 32bit floating
|
||||||
point</li>
|
point</li>
|
||||||
</ul>
|
</ul>
|
||||||
@ -161,7 +205,7 @@ that while it'd be possible in theory to process stereo sound as two
|
|||||||
separate mono channels, this isn't recommended because processing the
|
separate mono channels, this isn't recommended because processing the
|
||||||
channels separately would result in losing the phase coherency between
|
channels separately would result in losing the phase coherency between
|
||||||
the channels, which consequently would ruin the stereo effect.</p>
|
the channels, which consequently would ruin the stereo effect.</p>
|
||||||
<p>Sample rates between 8000-48000H are supported.</p>
|
<p>Sample rates between 8000-48000Hz are supported.</p>
|
||||||
<h3>3.2. Processing latency</h3>
|
<h3>3.2. Processing latency</h3>
|
||||||
<p>The processing and latency constraints of the SoundTouch library are:</p>
|
<p>The processing and latency constraints of the SoundTouch library are:</p>
|
||||||
<ul>
|
<ul>
|
||||||
@ -183,7 +227,7 @@ as combination of two primary effects, <em>sample rate transposing</em>
|
|||||||
and <em>time-stretching</em>.</p>
|
and <em>time-stretching</em>.</p>
|
||||||
<p><em>Sample rate transposing</em> affects both the audio stream
|
<p><em>Sample rate transposing</em> affects both the audio stream
|
||||||
duration and pitch. It's implemented simply by converting the original
|
duration and pitch. It's implemented simply by converting the original
|
||||||
audio sample stream to the desired duration by interpolating from
|
audio sample stream to the desired duration by interpolating from
|
||||||
the original audio samples. In SoundTouch, linear interpolation with
|
the original audio samples. In SoundTouch, linear interpolation with
|
||||||
anti-alias filtering is used. Theoretically a higher-order
|
anti-alias filtering is used. Theoretically a higher-order
|
||||||
interpolation provide better result than 1st order linear
|
interpolation provide better result than 1st order linear
|
||||||
@ -232,7 +276,7 @@ length of a single processing sequence in milliseconds which determines
|
|||||||
the how the original sound is chopped in the time-stretch algorithm.
|
the how the original sound is chopped in the time-stretch algorithm.
|
||||||
Larger values mean fewer sequences are used in processing. In principle
|
Larger values mean fewer sequences are used in processing. In principle
|
||||||
a larger value sounds better when slowing down the tempo, but worse
|
a larger value sounds better when slowing down the tempo, but worse
|
||||||
when increasing the tempo and vice versa. <br>
|
when increasing the tempo and vice versa.<br>
|
||||||
<br>
|
<br>
|
||||||
By default, this setting value is calculated automatically according to
|
By default, this setting value is calculated automatically according to
|
||||||
tempo value.<br>
|
tempo value.<br>
|
||||||
@ -241,7 +285,7 @@ tempo value.<br>
|
|||||||
default length in milliseconds is for the algorithm that seeks the best
|
default length in milliseconds is for the algorithm that seeks the best
|
||||||
possible overlapping location. This determines from how wide a sample
|
possible overlapping location. This determines from how wide a sample
|
||||||
"window" the algorithm can use to find an optimal mixing location when
|
"window" the algorithm can use to find an optimal mixing location when
|
||||||
the sound sequences are to be linked back together. <br>
|
the sound sequences are to be linked back together.<br>
|
||||||
<br>
|
<br>
|
||||||
The bigger this window setting is, the higher the possibility to find a
|
The bigger this window setting is, the higher the possibility to find a
|
||||||
better mixing position becomes, but at the same time large values may
|
better mixing position becomes, but at the same time large values may
|
||||||
@ -260,7 +304,8 @@ ends of the consecutive sequences will overlap with each other.<br>
|
|||||||
<br>
|
<br>
|
||||||
This shouldn't be that critical parameter. If you reduce the
|
This shouldn't be that critical parameter. If you reduce the
|
||||||
DEFAULT_SEQUENCE_MS setting by a large amount, you might wish to try a
|
DEFAULT_SEQUENCE_MS setting by a large amount, you might wish to try a
|
||||||
smaller value on this.</li>
|
smaller value on this.
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p>Notice that these parameters can also be set during execution time
|
<p>Notice that these parameters can also be set during execution time
|
||||||
with functions "<strong>TDStretch::setParameters()</strong>" and "<strong>SoundTouch::setSetting()</strong>".</p>
|
with functions "<strong>TDStretch::setParameters()</strong>" and "<strong>SoundTouch::setSetting()</strong>".</p>
|
||||||
@ -309,7 +354,7 @@ computation burden</td>
|
|||||||
</td>
|
</td>
|
||||||
<td valign="top">Default value is relatively large, chosen to
|
<td valign="top">Default value is relatively large, chosen to
|
||||||
suit with above parameters.</td>
|
suit with above parameters.</td>
|
||||||
<td valign="top"> </td>
|
<td valign="top"></td>
|
||||||
<td valign="top">If you reduce the "sequence ms" setting, you
|
<td valign="top">If you reduce the "sequence ms" setting, you
|
||||||
might wish to try a smaller value.</td>
|
might wish to try a smaller value.</td>
|
||||||
<td valign="top">Increasing the parameter value increases
|
<td valign="top">Increasing the parameter value increases
|
||||||
@ -318,38 +363,97 @@ computation burden</td>
|
|||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
<h3>3.5 Performance Optimizations </h3>
|
<h3>3.5 Performance Optimizations </h3>
|
||||||
|
<p><strong>Integer vs floating point:</strong></p>
|
||||||
|
<p>Floating point sample type is generally recommended because it provides
|
||||||
|
better sound quality.</p>
|
||||||
|
|
||||||
|
<p>However, execution speed difference between integer and floating point processing
|
||||||
|
depends on the CPU architecture. As rule of thumb,
|
||||||
|
<ul>
|
||||||
|
<li>in 32-bit x86 floating point and integer are roughly equally fast</li>
|
||||||
|
<li>in 64-bit x86/x64 floating point can be significantly faster than integer
|
||||||
|
version, because MMX integer optimizations are not available in the x64 architecture.
|
||||||
|
That depends on the compiler however, so that gcc can autovectorize integer routines
|
||||||
|
to work equally fast as floating point, where as Visual C++ (2017) does not
|
||||||
|
perform equally well and produces integer code that runs some 3x slower than
|
||||||
|
SSE-optimized floating poing code.
|
||||||
|
</li>
|
||||||
|
<li>in ARMv7 integer routines are twice as fast as floating point. Their
|
||||||
|
relative difference is roughly the same both with and without NEON; NEON
|
||||||
|
vfpu can however bring 2.4x speed improvement.
|
||||||
|
</li>
|
||||||
|
<li>in other platforms: try out if the execution time performance makes a
|
||||||
|
big difference</li>
|
||||||
|
</ul>
|
||||||
|
</p>
|
||||||
<p><strong>General optimizations:</strong></p>
|
<p><strong>General optimizations:</strong></p>
|
||||||
<p>The time-stretch routine has a 'quick' mode that substantially
|
<p>The time-stretch routine has a 'quick' mode that substantially
|
||||||
speeds up the algorithm but may degrade the sound quality by a small
|
speeds up the algorithm but may slightly compromise the sound quality.
|
||||||
amount. This mode is activated by calling SoundTouch::setSetting()
|
This mode is activated by calling SoundTouch::setSetting()
|
||||||
function with parameter id of SETTING_USE_QUICKSEEK and value
|
function with parameter id of SETTING_USE_QUICKSEEK and value
|
||||||
"1", i.e. </p>
|
"1", i.e. </p>
|
||||||
<blockquote>
|
<blockquote>
|
||||||
<p>setSetting(SETTING_USE_QUICKSEEK, 1);</p>
|
<p>setSetting(SETTING_USE_QUICKSEEK, 1);</p>
|
||||||
</blockquote>
|
</blockquote>
|
||||||
<p><strong>CPU-specific optimizations:</strong></p>
|
<p><strong>CPU-specific optimizations:</strong></p>
|
||||||
|
<p>Intel x86 specific SIMD optimizations are implemented using compiler
|
||||||
|
intrinsics, providing about a 3x processing speedup for x86 compatible
|
||||||
|
processors vs. non-SIMD implementation:</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li> Intel MMX optimized routines are used with compatible CPUs when
|
<li> MMX optimized routines are used in 32-bit x86 build when 16bit integer
|
||||||
16bit integer sample type is used. MMX optimizations are available both
|
sample type is used</li>
|
||||||
in Win32 and Gnu/x86 platforms. Compatible processors are Intel
|
<li> SSE optimized routines are used in 32- and 64-bit x86 CPUs when 32bit
|
||||||
PentiumMMX and later; AMD K6-2, Athlon and later. </li>
|
floating point sample type is used</li>
|
||||||
<li> Intel SSE optimized routines are used with compatible CPUs when
|
</ul>
|
||||||
floating point sample type is used. SSE optimizations are currently
|
<p>The algorithms are tuned to utilize autovectorization efficiently
|
||||||
implemented for Win32 platform only. Processors compatible with SSE
|
also in other CPU architectures, for example ARM cpus see approx 2.4x processing
|
||||||
extension are Intel processors starting from Pentium-III, and AMD
|
speedup when NEON SIMD support is present.
|
||||||
processors starting from Athlon XP. </li>
|
</p>
|
||||||
<li> AMD 3DNow! optimized routines are used with compatible CPUs when
|
<h3>3.5 OpenMP parallel computation</h3>
|
||||||
floating point sample type is used, but SSE extension isn't supported .
|
<p>SoundTouch 1.9 onwards support running the algorithms parallel in several CPU
|
||||||
3DNow! optimizations are currently implemented for Win32 platform only.
|
cores. Based on benchmark the experienced multi-core processing speed-up gain
|
||||||
These optimizations are used in AMD K6-2 and Athlon (classic) CPU's;
|
ranges between +30% (on a high-spec dual-core x86 Windows PC) to 215% (on a moderately low-spec
|
||||||
better performing SSE routines are used with AMD processor starting
|
quad-core ARM of Raspberry Pi2). </p>
|
||||||
from Athlon XP. </li>
|
<p>See an external blog article with more detailed discussion about the
|
||||||
|
<a href="http://www.softwarecoven.com/parallel-computing-in-embedded-mobile-devices/">
|
||||||
|
SoundTouch OpenMP optimization</a>.
|
||||||
|
</p>
|
||||||
|
<p>The parallel computing support is implemented using OpenMP spec 3.0
|
||||||
|
instructions. These instructions are supported by Visual C++ 2008 and later, and
|
||||||
|
GCC v4.2 and later. Compilers that do not supporting OpenMP will ignore these
|
||||||
|
optimizations and routines will still work properly. Possible warnings about
|
||||||
|
unknown #pragmas are related to OpenMP support and can be safely ignored.</p>
|
||||||
|
<p>The OpenMP improvements are disabled by default, and need to be enabled by
|
||||||
|
developer during compile-time. Reason for this is that parallel processing adds
|
||||||
|
moderate runtime overhead in managing the multi-threading, so it may not be
|
||||||
|
necessary nor desirable in all applications. For example real-time processing
|
||||||
|
that is not constrained by CPU power will not benefit of speed-up provided by
|
||||||
|
the parallel processing, in the contrary it may increase power consumption due
|
||||||
|
to the increased overhead.</p>
|
||||||
|
<p>However, applications that run on low-spec multi-core CPUs and may otherwise
|
||||||
|
have possibly constrained performance will benefit of the OpenMP improvements.
|
||||||
|
This include for example multi-core embedded devices.</p>
|
||||||
|
<p>OpenMP parallel computation can be enabled before compiling SoundTouch
|
||||||
|
library as follows:</p>
|
||||||
|
<ul>
|
||||||
|
<li><strong>Visual Studio</strong>: Open properties for the <strong>SoundTouch
|
||||||
|
</strong>sub-project, browse to <strong>C/C++</strong> and <strong>Language
|
||||||
|
</strong>settings. Set
|
||||||
|
there "<strong>OpenMP support</strong>" to "<strong>Yes</strong>". Alternatively add
|
||||||
|
<strong>/openmp</strong> switch to command-line
|
||||||
|
parameters
|
||||||
|
</li>
|
||||||
|
<li><strong>GNU</strong>: Run the configure script with "<strong>./configure
|
||||||
|
--enable-openmp</strong>" switch, then run make as usually</li>
|
||||||
|
<li><strong>Android</strong>: Add "<strong>-fopenmp</strong>" switches to compiler & linker
|
||||||
|
options, see README-SoundTouch-Android.html in the source code package for
|
||||||
|
more detailed instructions.</li>
|
||||||
</ul>
|
</ul>
|
||||||
<hr>
|
<hr>
|
||||||
<h2><a name="SoundStretch"></a>4. SoundStretch audio processing utility
|
<h2><a name="SoundStretch"></a>4. SoundStretch audio processing utility
|
||||||
</h2>
|
</h2>
|
||||||
<p>SoundStretch audio processing utility<br>
|
<p>SoundStretch audio processing utility<br>
|
||||||
Copyright (c) Olli Parviainen 2002-2012</p>
|
Copyright (c) Olli Parviainen 2002-2024</p>
|
||||||
<p>SoundStretch is a simple command-line application that can change
|
<p>SoundStretch is a simple command-line application that can change
|
||||||
tempo, pitch and playback rates of WAV sound files. This program is
|
tempo, pitch and playback rates of WAV sound files. This program is
|
||||||
intended primarily to demonstrate how the "SoundTouch" library can be
|
intended primarily to demonstrate how the "SoundTouch" library can be
|
||||||
@ -376,13 +480,13 @@ file format). Give "stdin" as filename to use standard input pipe. </td>
|
|||||||
</td>
|
</td>
|
||||||
<td valign="top">Name of the output sound file where the
|
<td valign="top">Name of the output sound file where the
|
||||||
resulting sound is saved (in .WAV audio file format). This parameter
|
resulting sound is saved (in .WAV audio file format). This parameter
|
||||||
may be omitted if you don't want to save the output (e.g. when
|
may be omitted if you don't want to save the output (e.g. when
|
||||||
only calculating BPM rate with '-bpm' switch). Give "stdout" as
|
only calculating BPM rate with '-bpm' switch). Give "stdout" as
|
||||||
filename to use standard output pipe.</td>
|
filename to use standard output pipe.</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td valign="top">
|
<td valign="top">
|
||||||
<pre> [switches]</pre>
|
<pre>[switches]</pre>
|
||||||
</td>
|
</td>
|
||||||
<td valign="top">Are one or more control switches.</td>
|
<td valign="top">Are one or more control switches.</td>
|
||||||
</tr>
|
</tr>
|
||||||
@ -495,12 +599,150 @@ and estimates the BPM rate:</p>
|
|||||||
<blockquote>
|
<blockquote>
|
||||||
<pre>soundstretch stdin -bpm</pre>
|
<pre>soundstretch stdin -bpm</pre>
|
||||||
</blockquote>
|
</blockquote>
|
||||||
|
<p><strong>Example 6</strong></p>
|
||||||
|
<p>The following command tunes song from original 440Hz tuning to 432Hz tuning:
|
||||||
|
this corresponds to lowering the pitch by -0.318 semitones:</p>
|
||||||
|
<blockquote>
|
||||||
|
<pre>soundstretch original.wav output.wav -pitch=-0.318</pre>
|
||||||
|
</blockquote>
|
||||||
<hr>
|
<hr>
|
||||||
<h2>5. Change History</h2>
|
<h2>5. Change History</h2>
|
||||||
<h3>5.1. SoundTouch library Change History </h3>
|
<h3>5.1. SoundTouch library Change History </h3>
|
||||||
|
<p><b>2.3.3:</b></p>
|
||||||
|
<ul class="current">
|
||||||
|
<li>Fixing compiler warnings, maintenance fixes to make/build files for various systems
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<p><b>2.3.2:</b></p>
|
||||||
|
<ul>
|
||||||
|
<li>Improve autotools makefiles to build the `SoundTouchDLL` dynamic-link link library with
|
||||||
|
C-style API. This library variation is easier to import and use from other programming
|
||||||
|
languages than the default C++ library.
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<p><b>2.3.1:</b></p>
|
||||||
|
<ul>
|
||||||
|
<li>Adjusted cmake build settings and header files that cmake installs</li>
|
||||||
|
</ul>
|
||||||
|
<p><b>2.3.0:</b></p>
|
||||||
|
<ul>
|
||||||
|
<li>Disable setting "SOUNDTOUCH_ALLOW_NONEXACT_SIMD_OPTIMIZATION" by default. The original
|
||||||
|
purpose of this setting was to avoid performance penalty due to unaligned SIMD memory
|
||||||
|
accesses in old CPUs, but that is not any more issue in concurrent CPU SIMD implementations
|
||||||
|
and having this setting enabled can cause slight compromise in result quality.
|
||||||
|
</li>
|
||||||
|
<li>Bugfix: soundtouch.clear() to really clear whole processing pipeline state. Earlier
|
||||||
|
individual variables were left uncleared, which caused slightly different result if
|
||||||
|
the same audio stream were processed again after calling clear().
|
||||||
|
</li>
|
||||||
|
<li>Bugfix: TDstretch to align initial offset position to be in middle of correlation search
|
||||||
|
window. This ensures that with zero tempo change the output will be same as input.
|
||||||
|
</li>
|
||||||
|
<li>Bugfix: Fix a bug in TDstrectch with too small initial skipFract value that occurred
|
||||||
|
with certain processing parameter settings: Replace assert with assignment that
|
||||||
|
corrects the situation.
|
||||||
|
</li>
|
||||||
|
<li>Remove OpenMP "_init_threading" workaround from Android build as it's not needed with concurrent
|
||||||
|
Android SDKs any more.</li>
|
||||||
|
</ul>
|
||||||
|
<p><b>2.2:</b></p>
|
||||||
|
<ul>
|
||||||
|
<li>Improved source codes so that compiler can autovectorize them more effectively.
|
||||||
|
This brings remarkable improvement e.g. ARM cpus equipped with NEON vfpu: Bencmarked
|
||||||
|
2.4x improvement in execution speed in ARMv7l vs the previous SoundTouch version
|
||||||
|
for both integer and floating point sample types.
|
||||||
|
</li>
|
||||||
|
<li>Bugfix: Resolved bad sound quality when using integer sample types in non-x86 CPU</li>
|
||||||
|
<li>Bugfix: Fixed possible reading past end of array in BPM peak detection algorithm</li>
|
||||||
|
</ul>
|
||||||
|
<p><b>2.1.2:</b></p>
|
||||||
|
<ul>
|
||||||
|
<li>Bump version to 2.1.2 also in configure.ac. The earlier release had old version info for GNU autotools.</li>
|
||||||
|
</ul>
|
||||||
|
<p><b>2.1.1:</b></p>
|
||||||
|
<ul>
|
||||||
|
<li>Bugfixes: Fixed potential buffer overwrite bugs in WavFile routines. Replaced asserts with runtime exceptions.
|
||||||
|
</li>
|
||||||
|
<li>Android: Migrated the SoundTouch Android example to new Android Studio</li>
|
||||||
|
<li>Automake: unset ACLOCAL in bootstrap script in case earlier build script has set it</li>
|
||||||
|
|
||||||
|
</ul>
|
||||||
|
<p><b>2.1:</b></p>
|
||||||
|
<ul>
|
||||||
|
<li>Refactored C# interface example</li>
|
||||||
|
<li>Disable anti-alias filter when switch
|
||||||
|
SOUNDTOUCH_PREVENT_CLICK_AT_RATE_CROSSOVER defined because anti-alias
|
||||||
|
filter cause slight click if the rate change crosses zero during
|
||||||
|
processing</li>
|
||||||
|
<li>Added script for building SoundTouchDll dynamic-link-library for GNU platforms</li>
|
||||||
|
<li>Rewrote Beats-per-Minute analysis algorithm for more reliable BPM detection</li>
|
||||||
|
<li>Added BPM functions to SoundTouchDll API</li>
|
||||||
|
<li>Migrated Visual Studio project files to MSVC 201x format</li>
|
||||||
|
<li>Replaced function parameter value asserts with runtime exceptions</li>
|
||||||
|
<li>Code maintenance & style cleanup</li>
|
||||||
|
</ul>
|
||||||
|
<p><b>2.0:</b></p>
|
||||||
|
<ul>
|
||||||
|
<li>Added functions to get initial processing latency, duration ratio between the original input and processed
|
||||||
|
output tracks, and clarified reporting of input/output batch sizes</li>
|
||||||
|
<li>Fixed issue that added brief sequence of silence to beginning of output audio</li>
|
||||||
|
<li>Adjusted algorithm parameters to reduce reverberating effect at tempo slowdown</li>
|
||||||
|
<li>Bugfix: Fixed a glitch that could cause negative array indexing in quick seek algorithm</li>
|
||||||
|
<li>Bugfix: flush() didn't properly flush final samples from the pipeline on 2nd time in case that soundtouch
|
||||||
|
object instance was recycled and used for processing a second audio stream.</li>
|
||||||
|
<li>Bugfix: Pi value had incorrect 9th/10th decimals</li>
|
||||||
|
<li>Added C# example application that uses SoundTouch dll library for processing MP3 files</li>
|
||||||
|
</ul>
|
||||||
|
<p><b>1.9.2:</b></p>
|
||||||
|
<ul>
|
||||||
|
<li>Fix in GNU package configuration</li>
|
||||||
|
</ul>
|
||||||
|
<p><b>1.9.1:</b></p>
|
||||||
|
<ul>
|
||||||
|
<li>Improved SoundTouch::flush() function so that it returns precisely the desired amount of samples for exact
|
||||||
|
output duration control</li>
|
||||||
|
<li>Redesigned quickseek algorithm for improved sound quality when using the quickseek mode. The new quickseek
|
||||||
|
algorithm can find 99% as good results as the
|
||||||
|
default full-scan mode, while the quickseek algorithm is remarkable less
|
||||||
|
CPU intensive.</li>
|
||||||
|
<li>Added adaptive integer divider scaling for improved sound quality when using integer processing algorithm
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<p><b>1.9:</b></p>
|
||||||
|
<ul>
|
||||||
|
<li>Added support for parallel computation support via OpenMP primitives for better performance in multicore
|
||||||
|
systems.
|
||||||
|
Benchmarks show that achieved parallel processing speedup improvement
|
||||||
|
typically range from +30% (x86 dual-core) to +180% (ARM quad-core). The
|
||||||
|
OpenMP optimizations are disabled by default, see OpenMP notes above in this
|
||||||
|
readme file how to enabled these optimizations.</li>
|
||||||
|
<li>Android: Added support for Android devices featuring X86 and MIPS CPUs,
|
||||||
|
in addition to ARM CPUs.</li>
|
||||||
|
<li>Android: More versatile Android example application that processes WAV
|
||||||
|
audio files with SoundTouch library</li>
|
||||||
|
<li>Replaced Windows-like 'BOOL' types with native 'bool'</li>
|
||||||
|
<li>Changed documentation token to "dist_doc_DATA" in Makefile.am file</li>
|
||||||
|
<li>Miscellaneous small fixes and improvements</li>
|
||||||
|
</ul>
|
||||||
|
<p><b>1.8.0:</b></p>
|
||||||
|
<ul>
|
||||||
|
<li>Added support for multi-channel audio processing</li>
|
||||||
|
<li>Added support for <b>cubic</b> and <b>shannon</b> interpolation for rate and pitch shift effects besides
|
||||||
|
the original <b>linear</b> interpolation, to reduce aliasing at high frequencies due to interpolation.
|
||||||
|
Cubic interpolation is used as default for floating point processing, and linear interpolation for integer
|
||||||
|
processing.</li>
|
||||||
|
<li>Fixed bug in anti-alias filtering that limited stop-band attenuation to -10 dB instead of <-50dB, and
|
||||||
|
increased filter length from 32 to 64 taps to further reduce aliasing due to frequency folding.</li>
|
||||||
|
<li>Performance improvements in cross-correlation algorithm</li>
|
||||||
|
<li>Other bug and compatibility fixes</li>
|
||||||
|
</ul>
|
||||||
|
<p><b>1.7.1:</b></p>
|
||||||
|
<ul>
|
||||||
|
<li>Added files for Android compilation
|
||||||
|
</ul>
|
||||||
<p><b>1.7.0:</b></p>
|
<p><b>1.7.0:</b></p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>Sound quality improvements</li>
|
<li>Sound quality improvements/li>
|
||||||
<li>Improved flush() to adjust output sound stream duration to match better with
|
<li>Improved flush() to adjust output sound stream duration to match better with
|
||||||
ideal duration</li>
|
ideal duration</li>
|
||||||
<li>Rewrote x86 cpu feature check to resolve compatibility problems</li>
|
<li>Rewrote x86 cpu feature check to resolve compatibility problems</li>
|
||||||
@ -534,13 +776,13 @@ sample batch sizes</li>
|
|||||||
<ul>
|
<ul>
|
||||||
<li> Added normalization to correlation calculation and improvement
|
<li> Added normalization to correlation calculation and improvement
|
||||||
automatic seek/sequence parameter calculation to improve sound quality</li>
|
automatic seek/sequence parameter calculation to improve sound quality</li>
|
||||||
<li> Bugfixes:
|
<li> Bugfixes:
|
||||||
<ul>
|
<ul>
|
||||||
<li> Fixed negative array indexing in quick seek algorithm</li>
|
<li> Fixed negative array indexing in quick seek algorithm</li>
|
||||||
<li> FIR autoalias filter running too far in processing buffer</li>
|
<li> FIR autoalias filter running too far in processing buffer</li>
|
||||||
<li> Check against zero sample count in rate transposing</li>
|
<li> Check against zero sample count in rate transposing</li>
|
||||||
<li> Fix for x86-64 support: Removed pop/push instructions from
|
<li> Fix for x86-64 support: Removed pop/push instructions from
|
||||||
the cpu detection algorithm. </li>
|
the cpu detection algorithm.</li>
|
||||||
<li> Check against empty buffers in FIFOSampleBuffer</li>
|
<li> Check against empty buffers in FIFOSampleBuffer</li>
|
||||||
<li> Other minor fixes & code cleanup</li>
|
<li> Other minor fixes & code cleanup</li>
|
||||||
</ul>
|
</ul>
|
||||||
@ -556,7 +798,7 @@ negative side or vice versa</li>
|
|||||||
<p><strong>1.4.1:</strong></p>
|
<p><strong>1.4.1:</strong></p>
|
||||||
<ul>
|
<ul>
|
||||||
<li> Fixed a buffer overflow bug in BPM detect algorithm routines if
|
<li> Fixed a buffer overflow bug in BPM detect algorithm routines if
|
||||||
processing more than 2048 samples at one call </li>
|
processing more than 2048 samples at one call</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p><strong>1.4.0:</strong></p>
|
<p><strong>1.4.0:</strong></p>
|
||||||
<ul>
|
<ul>
|
||||||
@ -639,8 +881,15 @@ accessing the FIFOSampleBuffer class from external files. </li>
|
|||||||
<ul>
|
<ul>
|
||||||
<li> Initial release</li>
|
<li> Initial release</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p> </p>
|
|
||||||
<h3>5.2. SoundStretch application Change History </h3>
|
<h3>5.2. SoundStretch application Change History </h3>
|
||||||
|
<p><b>2.3.3:</b></p>
|
||||||
|
<ul class="current_soundstretch">
|
||||||
|
<li>Added support for Asian / non-latin filenames in Windows. Gnu platform has supported them already earlier.</li>
|
||||||
|
</ul>
|
||||||
|
<p><b>1.9:</b></p>
|
||||||
|
<ul>
|
||||||
|
<li>Added support for WAV file 'fact' information chunk.</li>
|
||||||
|
</ul>
|
||||||
<p><b>1.7.0:</b></p>
|
<p><b>1.7.0:</b></p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>Bugfixes in Wavfile: exception string formatting, avoid getLengthMs() integer
|
<li>Bugfixes in Wavfile: exception string formatting, avoid getLengthMs() integer
|
||||||
@ -699,27 +948,54 @@ switch "-bpm" </li>
|
|||||||
<hr>
|
<hr>
|
||||||
<h2>6. Acknowledgements </h2>
|
<h2>6. Acknowledgements </h2>
|
||||||
<p>Kudos for these people who have contributed to development or
|
<p>Kudos for these people who have contributed to development or
|
||||||
submitted bugfixes since SoundTouch v1.3.1: </p>
|
submitted bugfixes:</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li> Arthur A</li>
|
<li> Arthur A</li>
|
||||||
|
<li> Paul Adenot</li>
|
||||||
<li> Richard Ash</li>
|
<li> Richard Ash</li>
|
||||||
<li> Stanislav Brabec</li>
|
<li> Stanislav Brabec</li>
|
||||||
<li> Christian Budde</li>
|
<li> Christian Budde</li>
|
||||||
|
<li> Jamie Bullock</li>
|
||||||
|
<li> Chris Bryan</li>
|
||||||
|
<li> Jacek Caban</li>
|
||||||
|
<li> Marketa Calabkova</li>
|
||||||
<li> Brian Cameron</li>
|
<li> Brian Cameron</li>
|
||||||
<li> Jason Champion</li>
|
<li> Jason Champion</li>
|
||||||
|
<li> Giuseppe Cigala</li>
|
||||||
<li> David Clark</li>
|
<li> David Clark</li>
|
||||||
<li> Patrick Colis</li>
|
<li> Patrick Colis</li>
|
||||||
<li> Miquel Colon</li>
|
<li> Miquel Colon</li>
|
||||||
|
<li> Jim Credland</li>
|
||||||
|
<li> Sandro Cumerlato</li>
|
||||||
|
<li> Gerry Fan</li>
|
||||||
<li> Justin Frankel</li>
|
<li> Justin Frankel</li>
|
||||||
|
<li> Masa H.</li>
|
||||||
<li> Jason Garland</li>
|
<li> Jason Garland</li>
|
||||||
<li> Takashi Iwai</li>
|
<li> Takashi Iwai</li>
|
||||||
|
<li> Thomas Klausner</li>
|
||||||
|
<li> Lu Zhihe</li>
|
||||||
|
<li> Luzpaz</li>
|
||||||
|
<li> Tony Mechelynck </li>
|
||||||
|
<li> Mathias Möhl</li>
|
||||||
<li> Yuval Naveh</li>
|
<li> Yuval Naveh</li>
|
||||||
|
<li> Mats Palmgren </li>
|
||||||
|
<li> Chandni Patel</li>
|
||||||
<li> Paulo Pizarro</li>
|
<li> Paulo Pizarro</li>
|
||||||
|
<li> Andrey Ponomarenko</li>
|
||||||
<li> Blaise Potard</li>
|
<li> Blaise Potard</li>
|
||||||
|
<li> Michael Pruett</li>
|
||||||
|
<li> Rajeev Puran</li>
|
||||||
<li> RJ Ryan</li>
|
<li> RJ Ryan</li>
|
||||||
|
<li> Serge Sans Paille</li>
|
||||||
<li> John Sheehy</li>
|
<li> John Sheehy</li>
|
||||||
<li> Tim Shuttleworth</li>
|
<li> Tim Shuttleworth</li>
|
||||||
|
<li> Albert Sirvent</li>
|
||||||
|
<li> Tyson Smith</li>
|
||||||
|
<li> John Stumpo</li>
|
||||||
|
<li> Mario di Vece</li>
|
||||||
|
<li> Rémi Verschelde</li>
|
||||||
<li> Katja Vetter</li>
|
<li> Katja Vetter</li>
|
||||||
|
<li> Wu Q.</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p>Moral greetings to all other contributors and users also!</p>
|
<p>Moral greetings to all other contributors and users also!</p>
|
||||||
<hr>
|
<hr>
|
||||||
@ -736,8 +1012,9 @@ General Public License for more details.</p>
|
|||||||
<p>You should have received a copy of the GNU Lesser General Public
|
<p>You should have received a copy of the GNU Lesser General Public
|
||||||
License along with this library; if not, write to the Free Software
|
License along with this library; if not, write to the Free Software
|
||||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA</p>
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA</p>
|
||||||
<hr><!--
|
<p>---</p>
|
||||||
$Id$
|
<p>commercial license alternative also available, contact author for details.</p>
|
||||||
-->
|
<hr>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
14
SoundTouchConfig.cmake.in
Normal file
14
SoundTouchConfig.cmake.in
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
@PACKAGE_INIT@
|
||||||
|
|
||||||
|
include("${CMAKE_CURRENT_LIST_DIR}/SoundTouchTargets.cmake")
|
||||||
|
|
||||||
|
check_required_components(SoundTouch)
|
||||||
|
|
||||||
|
get_target_property(SoundTouch_LOCATION SoundTouch::SoundTouch LOCATION)
|
||||||
|
message(STATUS "Found SoundTouch: ${SoundTouch_LOCATION}")
|
||||||
|
|
||||||
|
if(@SOUNDTOUCH_DLL@)
|
||||||
|
check_required_components(SoundTouchDLL)
|
||||||
|
get_target_property(SoundTouchDLL_LOCATION SoundTouch::SoundTouchDLL LOCATION)
|
||||||
|
message(STATUS "Found SoundTouchDLL: ${SoundTouchDLL_LOCATION}")
|
||||||
|
endif()
|
||||||
@ -1,8 +1,8 @@
|
|||||||
set SOUND_DIR=d:\dev\test_sounds
|
set SOUND_DIR=c:\dev\test_sounds
|
||||||
set OUT_DIR=.
|
set OUT_DIR=.
|
||||||
set TEST_NAME=semmari
|
set TEST_NAME=semmari
|
||||||
set OUT_NAME=out
|
set OUT_NAME=out
|
||||||
set SS=soundstretch
|
set SS=soundstretch_x64
|
||||||
set TEST_PARAM=-pitch=-3 -bpm
|
set TEST_PARAM=-pitch=-3 -bpm
|
||||||
|
|
||||||
call %SS% %SOUND_DIR%\%TEST_NAME%-8b1.wav %OUT_DIR%\%OUT_NAME%-8b1.wav %TEST_PARAM%
|
call %SS% %SOUND_DIR%\%TEST_NAME%-8b1.wav %OUT_DIR%\%OUT_NAME%-8b1.wav %TEST_PARAM%
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
# $Id$
|
|
||||||
|
|
||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
|
||||||
|
unset ACLOCAL
|
||||||
|
|
||||||
if [ "$1" = "--clean" ]
|
if [ "$1" = "--clean" ]
|
||||||
then
|
then
|
||||||
if [ -a Makefile ]
|
if [ -a Makefile ]
|
||||||
@ -16,9 +16,6 @@ then
|
|||||||
|
|
||||||
rm -rf configure libtool aclocal.m4 `find . -name Makefile.in` autom4te*.cache config/config.guess config/config.h.in config/config.sub config/depcomp config/install-sh config/ltmain.sh config/missing config/mkinstalldirs config/stamp-h config/stamp-h.in
|
rm -rf configure libtool aclocal.m4 `find . -name Makefile.in` autom4te*.cache config/config.guess config/config.h.in config/config.sub config/depcomp config/install-sh config/ltmain.sh config/missing config/mkinstalldirs config/stamp-h config/stamp-h.in
|
||||||
|
|
||||||
#gettextie files
|
|
||||||
#rm -f ABOUT-NLS config/config.rpath config/m4/codeset.m4 config/m4/gettext.m4 config/m4/glibc21.m4 config/m4/iconv.m4 config/m4/intdiv0.m4 config/m4/inttypes-pri.m4 config/m4/inttypes.m4 config/m4/inttypes_h.m4 config/m4/isc-posix.m4 config/m4/lcmessage.m4 config/m4/lib-ld.m4 config/m4/lib-link.m4 config/m4/lib-prefix.m4 config/m4/progtest.m4 config/m4/stdint_h.m4 config/m4/uintmax_t.m4 config/m4/ulonglong.m4 po/Makefile.in.in po/Rules-quot po/boldquot.sed po/en@boldquot.header po/en@quot.header po/insert-header.sin po/quot.sed po/remove-potcdate.sin
|
|
||||||
|
|
||||||
else
|
else
|
||||||
export AUTOMAKE="automake --add-missing --foreign --copy"
|
export AUTOMAKE="automake --add-missing --foreign --copy"
|
||||||
autoreconf -fisv && rm -f `find . -name "*~"` && rm -f ChangeLog
|
autoreconf -fisv && rm -f `find . -name "*~"` && rm -f ChangeLog
|
||||||
|
|||||||
@ -1,8 +1,6 @@
|
|||||||
## vim:tw=78
|
## vim:tw=78
|
||||||
## Process this file with automake to create Makefile.in
|
## Process this file with automake to create Makefile.in
|
||||||
##
|
##
|
||||||
## $Id$
|
|
||||||
##
|
|
||||||
## This file is part of SoundTouch, an audio processing library for pitch/time adjustments
|
## This file is part of SoundTouch, an audio processing library for pitch/time adjustments
|
||||||
##
|
##
|
||||||
## SoundTouch is free software; you can redistribute it and/or modify it under the
|
## SoundTouch is free software; you can redistribute it and/or modify it under the
|
||||||
@ -19,16 +17,10 @@
|
|||||||
## Place - Suite 330, Boston, MA 02111-1307, USA
|
## Place - Suite 330, Boston, MA 02111-1307, USA
|
||||||
|
|
||||||
## These are common definitions used in all Makefiles
|
## These are common definitions used in all Makefiles
|
||||||
## It is actually included when a makefile.am is coverted to Makefile.in
|
## It is actually included when a makefile.am is converted to Makefile.in
|
||||||
## by automake, so it's ok to have @MACROS@ that will be set by configure
|
## by automake, so it's ok to have @MACROS@ that will be set by configure
|
||||||
|
|
||||||
|
AM_CPPFLAGS=-I$(top_srcdir)/include
|
||||||
## INCLUDES is automatically added to CXXFLAGS at compile time. The
|
|
||||||
## $(top_srcdir) macro is set by configure. It's important to use $(top_srcdir)
|
|
||||||
## in case a user decides to build in a separate directory from the base package
|
|
||||||
## directory. Using absolute, or relative paths is a bad idea.
|
|
||||||
INCLUDES=-I$(top_srcdir)/include
|
|
||||||
|
|
||||||
|
|
||||||
# doc directory
|
# doc directory
|
||||||
pkgdocdir=$(prefix)/doc/@PACKAGE@
|
pkgdocdir=$(prefix)/doc/@PACKAGE@
|
||||||
|
|||||||
@ -1,3 +0,0 @@
|
|||||||
Starting from SoundTouch 1.6.0, the "configure" file is removed from the source code package due to autoconf/automake version conflicts.
|
|
||||||
|
|
||||||
Instead, generate the "configure" file using local tools by invoking "./bootstrap" script, then configure & compile as usual.
|
|
||||||
130
configure.ac
130
configure.ac
@ -1,7 +1,3 @@
|
|||||||
dnl SoundTouch configure.ac, by David W. Durham
|
|
||||||
dnl
|
|
||||||
dnl $Id$
|
|
||||||
dnl
|
|
||||||
dnl This file is part of SoundTouch, an audio processing library for pitch/time adjustments
|
dnl This file is part of SoundTouch, an audio processing library for pitch/time adjustments
|
||||||
dnl
|
dnl
|
||||||
dnl SoundTouch is free software; you can redistribute it and/or modify it under the
|
dnl SoundTouch is free software; you can redistribute it and/or modify it under the
|
||||||
@ -19,18 +15,27 @@ dnl this program; if not, write to the Free Software Foundation, Inc., 59 Temple
|
|||||||
dnl Place - Suite 330, Boston, MA 02111-1307, USA
|
dnl Place - Suite 330, Boston, MA 02111-1307, USA
|
||||||
# Process this file with autoconf to produce a configure script.
|
# Process this file with autoconf to produce a configure script.
|
||||||
|
|
||||||
AC_INIT(SoundTouch, 1.7.0, [http://www.surina.net/soundtouch])
|
AC_INIT([SoundTouch],[2.3.2],[http://www.surina.net/soundtouch])
|
||||||
|
dnl Default to libSoundTouch.so.$LIB_SONAME.0.0
|
||||||
|
LIB_SONAME=1
|
||||||
|
AC_SUBST(LIB_SONAME)
|
||||||
|
|
||||||
AC_CONFIG_AUX_DIR(config)
|
AC_CONFIG_AUX_DIR(config)
|
||||||
AM_CONFIG_HEADER([include/soundtouch_config.h])
|
AC_CONFIG_MACRO_DIR([config/m4])
|
||||||
|
AC_CONFIG_HEADERS([config.h include/soundtouch_config.h])
|
||||||
AM_INIT_AUTOMAKE
|
AM_INIT_AUTOMAKE
|
||||||
|
AM_SILENT_RULES([yes])
|
||||||
#AC_DISABLE_SHARED dnl This makes libtool only build static libs
|
#AC_DISABLE_SHARED dnl This makes libtool only build static libs
|
||||||
AC_DISABLE_STATIC dnl This makes libtool only build shared libs
|
AC_DISABLE_STATIC dnl This makes libtool only build shared libs
|
||||||
#AC_GNU_SOURCE dnl enable posix extensions in glibc
|
#AC_USE_SYSTEM_EXTENSIONS dnl enable posix extensions in glibc
|
||||||
|
|
||||||
AC_LANG(C++)
|
AC_LANG(C++)
|
||||||
|
|
||||||
|
# Compiler flags. Apply -ffast-math to allow compiler autovectorization generate effective SIMD code for arm compilation
|
||||||
|
CXXFLAGS="${CXXFLAGS} -O3 -ffast-math -Wall -Wextra -Wzero-as-null-pointer-constant -Wno-unknown-pragmas"
|
||||||
|
|
||||||
|
# Set AR_FLAGS to avoid build warning "ar: `u' modifier ignored since `D' is the default (see `U')"
|
||||||
|
AR_FLAGS='cr'
|
||||||
|
|
||||||
|
|
||||||
dnl ############################################################################
|
dnl ############################################################################
|
||||||
@ -45,7 +50,7 @@ AC_PROG_INSTALL
|
|||||||
#AC_PROG_LN_S
|
#AC_PROG_LN_S
|
||||||
AC_PROG_MAKE_SET
|
AC_PROG_MAKE_SET
|
||||||
|
|
||||||
AM_PROG_LIBTOOL dnl turn on using libtool
|
LT_INIT dnl turn on using libtool
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -53,10 +58,11 @@ AM_PROG_LIBTOOL dnl turn on using libtool
|
|||||||
dnl ############################################################################
|
dnl ############################################################################
|
||||||
dnl # Checks for header files #
|
dnl # Checks for header files #
|
||||||
dnl ############################################################################
|
dnl ############################################################################
|
||||||
AC_HEADER_STDC
|
|
||||||
#AC_HEADER_SYS_WAIT
|
#AC_HEADER_SYS_WAIT
|
||||||
# add any others you want to check for here
|
# add any others you want to check for here
|
||||||
AC_CHECK_HEADERS([cpuid.h])
|
AC_CHECK_HEADERS([cpuid.h])
|
||||||
|
AC_CHECK_HEADERS([arm_neon.h])
|
||||||
|
|
||||||
if test "x$ac_cv_header_cpuid_h" = "xno"; then
|
if test "x$ac_cv_header_cpuid_h" = "xno"; then
|
||||||
AC_MSG_WARN([The cpuid.h file was not found therefore the x86 optimizations will be disabled.])
|
AC_MSG_WARN([The cpuid.h file was not found therefore the x86 optimizations will be disabled.])
|
||||||
@ -75,26 +81,34 @@ AC_C_INLINE
|
|||||||
|
|
||||||
|
|
||||||
AC_ARG_ENABLE(integer-samples,
|
AC_ARG_ENABLE(integer-samples,
|
||||||
[AC_HELP_STRING([--enable-integer-samples],
|
[AS_HELP_STRING([--enable-integer-samples],[use integer samples instead of floats [default=no]])],,
|
||||||
[use integer samples instead of floats
|
|
||||||
[default=yes]])],,
|
|
||||||
[enable_integer_samples=no])
|
[enable_integer_samples=no])
|
||||||
|
|
||||||
|
|
||||||
|
AC_ARG_ENABLE(openmp,
|
||||||
|
[AS_HELP_STRING([--enable-openmp],[use parallel multicore calculation through OpenMP [default=no]])],,
|
||||||
|
[enable_openmp=no])
|
||||||
|
|
||||||
# Let the user enable/disable the x86 optimizations.
|
# Let the user enable/disable the x86 optimizations.
|
||||||
# Useful when compiling on non-x86 architectures.
|
# Useful when compiling on non-x86 architectures.
|
||||||
AC_ARG_ENABLE([x86-optimizations],
|
AC_ARG_ENABLE([x86-optimizations],
|
||||||
[AS_HELP_STRING([--enable-x86-optimizations],
|
[AS_HELP_STRING([--enable-x86-optimizations],
|
||||||
[use MMX or SSE optimization
|
[use MMX or SSE optimization [default=yes]])],[enable_x86_optimizations="${enableval}"],
|
||||||
[default=yes]])],[enable_x86_optimizations="${enableval}"],
|
|
||||||
[enable_x86_optimizations=yes])
|
[enable_x86_optimizations=yes])
|
||||||
|
|
||||||
|
# Let the user enable/disable the x86 optimizations.
|
||||||
|
# Useful when compiling on non-x86 architectures.
|
||||||
|
AC_ARG_ENABLE([neon-optimizations],
|
||||||
|
[AS_HELP_STRING([--enable-neon-optimizations],
|
||||||
|
[use ARM NEON optimization [default=yes]])],[enable_neon_optimizations="${enableval}"],
|
||||||
|
[enable_neon_optimizations=yes])
|
||||||
|
|
||||||
|
|
||||||
# Tell the Makefile.am if the user wants to disable optimizations.
|
# Tell the Makefile.am if the user wants to disable optimizations.
|
||||||
# Makefile.am will enable them by default if support is available.
|
# Makefile.am will enable them by default if support is available.
|
||||||
# Note: We check if optimizations are supported a few lines down.
|
# Note: We check if optimizations are supported a few lines down.
|
||||||
AM_CONDITIONAL([X86_OPTIMIZATIONS], [test "x$enable_x86_optimizations" = "xyes"])
|
AM_CONDITIONAL([X86_OPTIMIZATIONS], [test "x$enable_x86_optimizations" = "xyes"])
|
||||||
|
|
||||||
|
|
||||||
if test "x$enable_integer_samples" = "xyes"; then
|
if test "x$enable_integer_samples" = "xyes"; then
|
||||||
echo "****** Integer sample type enabled ******"
|
echo "****** Integer sample type enabled ******"
|
||||||
AC_DEFINE(SOUNDTOUCH_INTEGER_SAMPLES,1,[Use Integer as Sample type])
|
AC_DEFINE(SOUNDTOUCH_INTEGER_SAMPLES,1,[Use Integer as Sample type])
|
||||||
@ -102,6 +116,14 @@ else
|
|||||||
echo "****** Float sample type enabled ******"
|
echo "****** Float sample type enabled ******"
|
||||||
AC_DEFINE(SOUNDTOUCH_FLOAT_SAMPLES,1,[Use Float as Sample type])
|
AC_DEFINE(SOUNDTOUCH_FLOAT_SAMPLES,1,[Use Float as Sample type])
|
||||||
fi
|
fi
|
||||||
|
AM_CONDITIONAL([SOUNDTOUCH_FLOAT_SAMPLES], [test "x$enable_integer_samples" != "xyes"])
|
||||||
|
|
||||||
|
if test "x$enable_openmp" = "xyes"; then
|
||||||
|
echo "****** openmp optimizations enabled ******"
|
||||||
|
AM_CXXFLAGS="-fopenmp $AM_CXXFLAGS"
|
||||||
|
else
|
||||||
|
echo "****** openmp optimizations disabled ******"
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
# Check if optimizations are supported in the system at build time.
|
# Check if optimizations are supported in the system at build time.
|
||||||
@ -110,8 +132,7 @@ if test "x$enable_x86_optimizations" = "xyes" -a "x$ac_cv_header_cpuid_h" = "xye
|
|||||||
|
|
||||||
original_saved_CXXFLAGS=$CXXFLAGS
|
original_saved_CXXFLAGS=$CXXFLAGS
|
||||||
have_mmx_intrinsics=no
|
have_mmx_intrinsics=no
|
||||||
OPT_CXXFLAGS="-mmmx -Winline"
|
CXXFLAGS="-mmmx -Winline $CXXFLAGS"
|
||||||
CXXFLAGS="$OPT_CXXFLAGS $CXXFLAGS"
|
|
||||||
|
|
||||||
# Check if the user can compile MMX code using intrinsics.
|
# Check if the user can compile MMX code using intrinsics.
|
||||||
# GCC supports MMX intrinsics since version 3.3
|
# GCC supports MMX intrinsics since version 3.3
|
||||||
@ -137,14 +158,15 @@ if test "x$enable_x86_optimizations" = "xyes" -a "x$ac_cv_header_cpuid_h" = "xye
|
|||||||
echo "****** No MMX support found ******"
|
echo "****** No MMX support found ******"
|
||||||
if test "x$enable_integer_samples" = "xyes"; then
|
if test "x$enable_integer_samples" = "xyes"; then
|
||||||
echo "****** Disabling optimizations. Using integer samples with no MMX support ******"
|
echo "****** Disabling optimizations. Using integer samples with no MMX support ******"
|
||||||
AC_DEFINE([SOUNDTOUCH_DISABLE_X86_OPTIMIZATIONS],[1],[Do not use x86 optimizations])
|
CPPFLAGS="-DSOUNDTOUCH_DISABLE_X86_OPTIMIZATIONS $CPPFLAGS"
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
# SSE support
|
||||||
|
original_saved_CXXFLAGS=$CXXFLAGS
|
||||||
have_sse_intrinsics=no
|
have_sse_intrinsics=no
|
||||||
OPT_CXXFLAGS="-msse -Winline"
|
CXXFLAGS="-msse -Winline $CXXFLAGS"
|
||||||
CXXFLAGS="$OPT_CXXFLAGS $CXXFLAGS"
|
|
||||||
|
|
||||||
# Check if the user can compile SSE code using intrinsics.
|
# Check if the user can compile SSE code using intrinsics.
|
||||||
# GCC supports SSE intrinsics since version 3.3
|
# GCC supports SSE intrinsics since version 3.3
|
||||||
@ -170,16 +192,69 @@ if test "x$enable_x86_optimizations" = "xyes" -a "x$ac_cv_header_cpuid_h" = "xye
|
|||||||
echo "****** No SSE support found ******"
|
echo "****** No SSE support found ******"
|
||||||
if test "x$enable_integer_samples" != "xyes"; then
|
if test "x$enable_integer_samples" != "xyes"; then
|
||||||
echo "****** Disabling optimizations. Using float samples with no SSE support ******"
|
echo "****** Disabling optimizations. Using float samples with no SSE support ******"
|
||||||
AC_DEFINE([SOUNDTOUCH_DISABLE_X86_OPTIMIZATIONS],[1],[Do not use x86 optimizations])
|
CPPFLAGS="-DSOUNDTOUCH_DISABLE_X86_OPTIMIZATIONS $CPPFLAGS"
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
else
|
else
|
||||||
# Disable optimizations in SSTypes.h since the user requested it.
|
# Disable optimizations in SSTypes.h since the user requested it.
|
||||||
echo "****** x86 optimizations disabled ******"
|
echo "****** x86 optimizations disabled ******"
|
||||||
AC_DEFINE([SOUNDTOUCH_DISABLE_X86_OPTIMIZATIONS],[1],[Do not use x86 optimizations])
|
CPPFLAGS="-DSOUNDTOUCH_DISABLE_X86_OPTIMIZATIONS $CPPFLAGS"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
if test "x$enable_neon_optimizations" = "xyes" -a "x$ac_cv_header_arm_neon_h" = "xyes"; then
|
||||||
|
|
||||||
|
# Check for ARM NEON support
|
||||||
|
original_saved_CXXFLAGS=$CXXFLAGS
|
||||||
|
have_neon=no
|
||||||
|
CXXFLAGS="-mfpu=neon -march=native $CXXFLAGS"
|
||||||
|
|
||||||
|
# Check if can compile neon code using intrinsics, require GCC >= 4.3 for autovectorization.
|
||||||
|
AC_COMPILE_IFELSE([AC_LANG_SOURCE([[
|
||||||
|
#if defined(__GNUC__) && (__GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 3))
|
||||||
|
#error "Need GCC >= 4.3 for neon autovectorization"
|
||||||
|
#endif
|
||||||
|
#include <arm_neon.h>
|
||||||
|
int main () {
|
||||||
|
int32x4_t t = {1};
|
||||||
|
return vaddq_s32(t,t)[0] == 2;
|
||||||
|
}]])],[have_neon=yes])
|
||||||
|
CXXFLAGS=$original_saved_CXXFLAGS
|
||||||
|
if test "x$have_neon" = "xyes" ; then
|
||||||
|
echo "****** NEON support enabled ******"
|
||||||
|
CPPFLAGS="-mfpu=neon -march=native -mtune=native $CPPFLAGS"
|
||||||
|
AC_DEFINE(SOUNDTOUCH_USE_NEON,1,[Use ARM NEON extension])
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
AC_CANONICAL_HOST
|
||||||
|
HOST_OS=""
|
||||||
|
AS_CASE([$host_cpu],
|
||||||
|
[x86_64],
|
||||||
|
[
|
||||||
|
x86_64=true
|
||||||
|
x86=true
|
||||||
|
],
|
||||||
|
[i?86],
|
||||||
|
[
|
||||||
|
x86=true
|
||||||
|
])
|
||||||
|
|
||||||
|
AM_CONDITIONAL([X86], [test "$x86" = true])
|
||||||
|
AM_CONDITIONAL([X86_64], [test "$x86_64" = true])
|
||||||
|
|
||||||
|
AC_SUBST([HOST_OS])
|
||||||
|
|
||||||
|
|
||||||
|
# Set AM_CXXFLAGS
|
||||||
|
AC_SUBST([AM_CXXFLAGS], [$AM_CXXFLAGS])
|
||||||
|
|
||||||
|
# Empty default CXXFLAGS so user can set them if desirable
|
||||||
|
#AC_SUBST([CXXFLAGS], [ ])
|
||||||
|
|
||||||
|
|
||||||
# SSTypes.h by default enables optimizations. Those already got disabled if
|
# SSTypes.h by default enables optimizations. Those already got disabled if
|
||||||
# the user requested for it or if the system does not support them.
|
# the user requested for it or if the system does not support them.
|
||||||
#
|
#
|
||||||
@ -195,8 +270,6 @@ AM_CONDITIONAL([HAVE_SSE], [test "x$have_sse_intrinsics" = "xyes"])
|
|||||||
dnl ############################################################################
|
dnl ############################################################################
|
||||||
dnl # Checks for library functions/classes #
|
dnl # Checks for library functions/classes #
|
||||||
dnl ############################################################################
|
dnl ############################################################################
|
||||||
AC_FUNC_MALLOC
|
|
||||||
AC_TYPE_SIGNAL
|
|
||||||
|
|
||||||
dnl make -lm get added to the LIBS
|
dnl make -lm get added to the LIBS
|
||||||
AC_CHECK_LIB(m, sqrt,,AC_MSG_ERROR([compatible libc math library not found]))
|
AC_CHECK_LIB(m, sqrt,,AC_MSG_ERROR([compatible libc math library not found]))
|
||||||
@ -229,11 +302,12 @@ AC_CONFIG_FILES([
|
|||||||
source/Makefile
|
source/Makefile
|
||||||
source/SoundTouch/Makefile
|
source/SoundTouch/Makefile
|
||||||
source/SoundStretch/Makefile
|
source/SoundStretch/Makefile
|
||||||
|
source/SoundTouchDLL/Makefile
|
||||||
include/Makefile
|
include/Makefile
|
||||||
])
|
])
|
||||||
|
|
||||||
AC_OUTPUT(
|
AC_CONFIG_FILES([soundtouch.pc
|
||||||
soundtouch.pc
|
])
|
||||||
)
|
AC_OUTPUT
|
||||||
|
|
||||||
dnl use 'echo' to put stuff here if you want a message to the builder
|
dnl use 'echo' to put stuff here if you want a message to the builder
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
# Helper script for building a source code release package
|
# Helper script for building a source code release package
|
||||||
# $Id$
|
|
||||||
|
|
||||||
rm -Rf soundtouch
|
rm -Rf soundtouch
|
||||||
rm soundtouch.zip
|
rm soundtouch.zip
|
||||||
|
|||||||
@ -26,13 +26,6 @@
|
|||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Last changed : $Date$
|
|
||||||
// File revision : $Revision: 4 $
|
|
||||||
//
|
|
||||||
// $Id$
|
|
||||||
//
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// License :
|
// License :
|
||||||
//
|
//
|
||||||
// SoundTouch audio processing library
|
// SoundTouch audio processing library
|
||||||
@ -57,6 +50,7 @@
|
|||||||
#ifndef _BPMDetect_H_
|
#ifndef _BPMDetect_H_
|
||||||
#define _BPMDetect_H_
|
#define _BPMDetect_H_
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
#include "STTypes.h"
|
#include "STTypes.h"
|
||||||
#include "FIFOSampleBuffer.h"
|
#include "FIFOSampleBuffer.h"
|
||||||
|
|
||||||
@ -64,10 +58,32 @@ namespace soundtouch
|
|||||||
{
|
{
|
||||||
|
|
||||||
/// Minimum allowed BPM rate. Used to restrict accepted result above a reasonable limit.
|
/// Minimum allowed BPM rate. Used to restrict accepted result above a reasonable limit.
|
||||||
#define MIN_BPM 29
|
#define MIN_BPM 45
|
||||||
|
|
||||||
/// Maximum allowed BPM rate. Used to restrict accepted result below a reasonable limit.
|
/// Maximum allowed BPM rate range. Used for calculating algorithm parametrs
|
||||||
#define MAX_BPM 200
|
#define MAX_BPM_RANGE 200
|
||||||
|
|
||||||
|
/// Maximum allowed BPM rate range. Used to restrict accepted result below a reasonable limit.
|
||||||
|
#define MAX_BPM_VALID 190
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
float pos;
|
||||||
|
float strength;
|
||||||
|
} BEAT;
|
||||||
|
|
||||||
|
|
||||||
|
class IIR2_filter
|
||||||
|
{
|
||||||
|
double coeffs[5];
|
||||||
|
double prev[5];
|
||||||
|
|
||||||
|
public:
|
||||||
|
IIR2_filter(const double *lpf_coeffs);
|
||||||
|
float update(float x);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
/// Class for calculating BPM rate for audio data.
|
/// Class for calculating BPM rate for audio data.
|
||||||
@ -77,12 +93,6 @@ protected:
|
|||||||
/// Auto-correlation accumulator bins.
|
/// Auto-correlation accumulator bins.
|
||||||
float *xcorr;
|
float *xcorr;
|
||||||
|
|
||||||
/// Amplitude envelope sliding average approximation level accumulator
|
|
||||||
double envelopeAccu;
|
|
||||||
|
|
||||||
/// RMS volume sliding average approximation level accumulator
|
|
||||||
double RMSVolumeAccu;
|
|
||||||
|
|
||||||
/// Sample average counter.
|
/// Sample average counter.
|
||||||
int decimateCount;
|
int decimateCount;
|
||||||
|
|
||||||
@ -105,9 +115,28 @@ protected:
|
|||||||
/// the first these many correlation bins.
|
/// the first these many correlation bins.
|
||||||
int windowStart;
|
int windowStart;
|
||||||
|
|
||||||
|
/// window functions for data preconditioning
|
||||||
|
float *hamw;
|
||||||
|
float *hamw2;
|
||||||
|
|
||||||
|
// beat detection variables
|
||||||
|
int pos;
|
||||||
|
int peakPos;
|
||||||
|
int beatcorr_ringbuffpos;
|
||||||
|
int init_scaler;
|
||||||
|
float peakVal;
|
||||||
|
float *beatcorr_ringbuff;
|
||||||
|
|
||||||
/// FIFO-buffer for decimated processing samples.
|
/// FIFO-buffer for decimated processing samples.
|
||||||
soundtouch::FIFOSampleBuffer *buffer;
|
soundtouch::FIFOSampleBuffer *buffer;
|
||||||
|
|
||||||
|
/// Collection of detected beat positions
|
||||||
|
//BeatCollection beats;
|
||||||
|
std::vector<BEAT> beats;
|
||||||
|
|
||||||
|
// 2nd order low-pass-filter
|
||||||
|
IIR2_filter beat_lpf;
|
||||||
|
|
||||||
/// Updates auto-correlation function for given number of decimated samples that
|
/// Updates auto-correlation function for given number of decimated samples that
|
||||||
/// are read from the internal 'buffer' pipe (samples aren't removed from the pipe
|
/// are read from the internal 'buffer' pipe (samples aren't removed from the pipe
|
||||||
/// though).
|
/// though).
|
||||||
@ -131,6 +160,10 @@ protected:
|
|||||||
/// remove constant bias from xcorr data
|
/// remove constant bias from xcorr data
|
||||||
void removeBias();
|
void removeBias();
|
||||||
|
|
||||||
|
// Detect individual beat positions
|
||||||
|
void updateBeatPos(int process_samples);
|
||||||
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/// Constructor.
|
/// Constructor.
|
||||||
BPMDetect(int numChannels, ///< Number of channels in sample data.
|
BPMDetect(int numChannels, ///< Number of channels in sample data.
|
||||||
@ -150,15 +183,23 @@ public:
|
|||||||
int numSamples ///< Number of samples in buffer
|
int numSamples ///< Number of samples in buffer
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
/// Analyzes the results and returns the BPM rate. Use this function to read result
|
/// Analyzes the results and returns the BPM rate. Use this function to read result
|
||||||
/// after whole song data has been input to the class by consecutive calls of
|
/// after whole song data has been input to the class by consecutive calls of
|
||||||
/// 'inputSamples' function.
|
/// 'inputSamples' function.
|
||||||
///
|
///
|
||||||
/// \return Beats-per-minute rate, or zero if detection failed.
|
/// \return Beats-per-minute rate, or zero if detection failed.
|
||||||
float getBpm();
|
float getBpm();
|
||||||
|
|
||||||
|
/// Get beat position arrays. Note: The array includes also really low beat detection values
|
||||||
|
/// in absence of clear strong beats. Consumer may wish to filter low values away.
|
||||||
|
/// - "pos" receive array of beat positions
|
||||||
|
/// - "values" receive array of beat detection strengths
|
||||||
|
/// - max_num indicates max.size of "pos" and "values" array.
|
||||||
|
///
|
||||||
|
/// You can query a suitable array sized by calling this with nullptr in "pos" & "values".
|
||||||
|
///
|
||||||
|
/// \return number of beats in the arrays.
|
||||||
|
int getBeats(float *pos, float *strength, int max_num);
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // _BPMDetect_H_
|
#endif // _BPMDetect_H_
|
||||||
|
|||||||
@ -15,13 +15,6 @@
|
|||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Last changed : $Date$
|
|
||||||
// File revision : $Revision: 4 $
|
|
||||||
//
|
|
||||||
// $Id$
|
|
||||||
//
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// License :
|
// License :
|
||||||
//
|
//
|
||||||
// SoundTouch audio processing library
|
// SoundTouch audio processing library
|
||||||
@ -98,7 +91,7 @@ public:
|
|||||||
);
|
);
|
||||||
|
|
||||||
/// destructor
|
/// destructor
|
||||||
~FIFOSampleBuffer();
|
~FIFOSampleBuffer() override;
|
||||||
|
|
||||||
/// Returns a pointer to the beginning of the output samples.
|
/// Returns a pointer to the beginning of the output samples.
|
||||||
/// This function is provided for accessing the output samples directly.
|
/// This function is provided for accessing the output samples directly.
|
||||||
@ -107,7 +100,7 @@ public:
|
|||||||
/// When using this function to output samples, also remember to 'remove' the
|
/// When using this function to output samples, also remember to 'remove' the
|
||||||
/// output samples from the buffer by calling the
|
/// output samples from the buffer by calling the
|
||||||
/// 'receiveSamples(numSamples)' function
|
/// 'receiveSamples(numSamples)' function
|
||||||
virtual SAMPLETYPE *ptrBegin();
|
virtual SAMPLETYPE *ptrBegin() override;
|
||||||
|
|
||||||
/// Returns a pointer to the end of the used part of the sample buffer (i.e.
|
/// Returns a pointer to the end of the used part of the sample buffer (i.e.
|
||||||
/// where the new samples are to be inserted). This function may be used for
|
/// where the new samples are to be inserted). This function may be used for
|
||||||
@ -119,7 +112,7 @@ public:
|
|||||||
/// 'putSamples(numSamples)' function.
|
/// 'putSamples(numSamples)' function.
|
||||||
SAMPLETYPE *ptrEnd(
|
SAMPLETYPE *ptrEnd(
|
||||||
uint slackCapacity ///< How much free capacity (in samples) there _at least_
|
uint slackCapacity ///< How much free capacity (in samples) there _at least_
|
||||||
///< should be so that the caller can succesfully insert the
|
///< should be so that the caller can successfully insert the
|
||||||
///< desired samples to the buffer. If necessary, the function
|
///< desired samples to the buffer. If necessary, the function
|
||||||
///< grows the buffer size to comply with this requirement.
|
///< grows the buffer size to comply with this requirement.
|
||||||
);
|
);
|
||||||
@ -128,7 +121,7 @@ public:
|
|||||||
/// the sample buffer.
|
/// the sample buffer.
|
||||||
virtual void putSamples(const SAMPLETYPE *samples, ///< Pointer to samples.
|
virtual void putSamples(const SAMPLETYPE *samples, ///< Pointer to samples.
|
||||||
uint numSamples ///< Number of samples to insert.
|
uint numSamples ///< Number of samples to insert.
|
||||||
);
|
) override;
|
||||||
|
|
||||||
/// Adjusts the book-keeping to increase number of samples in the buffer without
|
/// Adjusts the book-keeping to increase number of samples in the buffer without
|
||||||
/// copying any actual samples.
|
/// copying any actual samples.
|
||||||
@ -146,7 +139,7 @@ public:
|
|||||||
/// \return Number of samples returned.
|
/// \return Number of samples returned.
|
||||||
virtual uint receiveSamples(SAMPLETYPE *output, ///< Buffer where to copy output samples.
|
virtual uint receiveSamples(SAMPLETYPE *output, ///< Buffer where to copy output samples.
|
||||||
uint maxSamples ///< How many samples to receive at max.
|
uint maxSamples ///< How many samples to receive at max.
|
||||||
);
|
) override;
|
||||||
|
|
||||||
/// Adjusts book-keeping so that given number of samples are removed from beginning of the
|
/// Adjusts book-keeping so that given number of samples are removed from beginning of the
|
||||||
/// sample buffer without copying them anywhere.
|
/// sample buffer without copying them anywhere.
|
||||||
@ -154,23 +147,32 @@ public:
|
|||||||
/// Used to reduce the number of samples in the buffer when accessing the sample buffer directly
|
/// Used to reduce the number of samples in the buffer when accessing the sample buffer directly
|
||||||
/// with 'ptrBegin' function.
|
/// with 'ptrBegin' function.
|
||||||
virtual uint receiveSamples(uint maxSamples ///< Remove this many samples from the beginning of pipe.
|
virtual uint receiveSamples(uint maxSamples ///< Remove this many samples from the beginning of pipe.
|
||||||
);
|
) override;
|
||||||
|
|
||||||
/// Returns number of samples currently available.
|
/// Returns number of samples currently available.
|
||||||
virtual uint numSamples() const;
|
virtual uint numSamples() const override;
|
||||||
|
|
||||||
/// Sets number of channels, 1 = mono, 2 = stereo.
|
/// Sets number of channels, 1 = mono, 2 = stereo.
|
||||||
void setChannels(int numChannels);
|
void setChannels(int numChannels);
|
||||||
|
|
||||||
|
/// Get number of channels
|
||||||
|
int getChannels()
|
||||||
|
{
|
||||||
|
return channels;
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns nonzero if there aren't any samples available for outputting.
|
/// Returns nonzero if there aren't any samples available for outputting.
|
||||||
virtual int isEmpty() const;
|
virtual int isEmpty() const override;
|
||||||
|
|
||||||
/// Clears all the samples.
|
/// Clears all the samples.
|
||||||
virtual void clear();
|
virtual void clear() override;
|
||||||
|
|
||||||
/// allow trimming (downwards) amount of samples in pipeline.
|
/// allow trimming (downwards) amount of samples in pipeline.
|
||||||
/// Returns adjusted amount of samples
|
/// Returns adjusted amount of samples
|
||||||
uint adjustAmountOfSamples(uint numSamples);
|
uint adjustAmountOfSamples(uint numSamples) override;
|
||||||
|
|
||||||
|
/// Add silence to end of buffer
|
||||||
|
void addSilent(uint nSamples);
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -17,13 +17,6 @@
|
|||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Last changed : $Date$
|
|
||||||
// File revision : $Revision: 4 $
|
|
||||||
//
|
|
||||||
// $Id$
|
|
||||||
//
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// License :
|
// License :
|
||||||
//
|
//
|
||||||
// SoundTouch audio processing library
|
// SoundTouch audio processing library
|
||||||
@ -58,6 +51,18 @@ namespace soundtouch
|
|||||||
/// Abstract base class for FIFO (first-in-first-out) sample processing classes.
|
/// Abstract base class for FIFO (first-in-first-out) sample processing classes.
|
||||||
class FIFOSamplePipe
|
class FIFOSamplePipe
|
||||||
{
|
{
|
||||||
|
protected:
|
||||||
|
|
||||||
|
bool verifyNumberOfChannels(int nChannels) const
|
||||||
|
{
|
||||||
|
if ((nChannels > 0) && (nChannels <= SOUNDTOUCH_MAX_CHANNELS))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
ST_THROW_RT_ERROR("Error: Illegal number of channels");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// virtual default destructor
|
// virtual default destructor
|
||||||
virtual ~FIFOSamplePipe() {}
|
virtual ~FIFOSamplePipe() {}
|
||||||
@ -83,11 +88,11 @@ public:
|
|||||||
void moveSamples(FIFOSamplePipe &other ///< Other pipe instance where from the receive the data.
|
void moveSamples(FIFOSamplePipe &other ///< Other pipe instance where from the receive the data.
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
int oNumSamples = other.numSamples();
|
const uint oNumSamples = other.numSamples();
|
||||||
|
|
||||||
putSamples(other.ptrBegin(), oNumSamples);
|
putSamples(other.ptrBegin(), oNumSamples);
|
||||||
other.receiveSamples(oNumSamples);
|
other.receiveSamples(oNumSamples);
|
||||||
};
|
}
|
||||||
|
|
||||||
/// Output samples from beginning of the sample buffer. Copies requested samples to
|
/// Output samples from beginning of the sample buffer. Copies requested samples to
|
||||||
/// output buffer and removes them from the sample buffer. If there are less than
|
/// output buffer and removes them from the sample buffer. If there are less than
|
||||||
@ -122,7 +127,6 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// Base-class for sound processing routines working in FIFO principle. With this base
|
/// Base-class for sound processing routines working in FIFO principle. With this base
|
||||||
/// class it's easy to implement sound processing stages that can be chained together,
|
/// class it's easy to implement sound processing stages that can be chained together,
|
||||||
/// so that samples that are fed into beginning of the pipe automatically go through
|
/// so that samples that are fed into beginning of the pipe automatically go through
|
||||||
@ -140,20 +144,18 @@ protected:
|
|||||||
/// Sets output pipe.
|
/// Sets output pipe.
|
||||||
void setOutPipe(FIFOSamplePipe *pOutput)
|
void setOutPipe(FIFOSamplePipe *pOutput)
|
||||||
{
|
{
|
||||||
assert(output == NULL);
|
assert(output == nullptr);
|
||||||
assert(pOutput != NULL);
|
assert(pOutput != nullptr);
|
||||||
output = pOutput;
|
output = pOutput;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Constructor. Doesn't define output pipe; it has to be set be
|
/// Constructor. Doesn't define output pipe; it has to be set be
|
||||||
/// 'setOutPipe' function.
|
/// 'setOutPipe' function.
|
||||||
FIFOProcessor()
|
FIFOProcessor()
|
||||||
{
|
{
|
||||||
output = NULL;
|
output = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Constructor. Configures output pipe.
|
/// Constructor. Configures output pipe.
|
||||||
FIFOProcessor(FIFOSamplePipe *pOutput ///< Output pipe.
|
FIFOProcessor(FIFOSamplePipe *pOutput ///< Output pipe.
|
||||||
)
|
)
|
||||||
@ -161,13 +163,11 @@ protected:
|
|||||||
output = pOutput;
|
output = pOutput;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Destructor.
|
/// Destructor.
|
||||||
virtual ~FIFOProcessor()
|
virtual ~FIFOProcessor() override
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Returns a pointer to the beginning of the output samples.
|
/// Returns a pointer to the beginning of the output samples.
|
||||||
/// This function is provided for accessing the output samples directly.
|
/// This function is provided for accessing the output samples directly.
|
||||||
/// Please be careful for not to corrupt the book-keeping!
|
/// Please be careful for not to corrupt the book-keeping!
|
||||||
@ -175,7 +175,7 @@ protected:
|
|||||||
/// When using this function to output samples, also remember to 'remove' the
|
/// When using this function to output samples, also remember to 'remove' the
|
||||||
/// output samples from the buffer by calling the
|
/// output samples from the buffer by calling the
|
||||||
/// 'receiveSamples(numSamples)' function
|
/// 'receiveSamples(numSamples)' function
|
||||||
virtual SAMPLETYPE *ptrBegin()
|
virtual SAMPLETYPE *ptrBegin() override
|
||||||
{
|
{
|
||||||
return output->ptrBegin();
|
return output->ptrBegin();
|
||||||
}
|
}
|
||||||
@ -189,44 +189,40 @@ public:
|
|||||||
/// \return Number of samples returned.
|
/// \return Number of samples returned.
|
||||||
virtual uint receiveSamples(SAMPLETYPE *outBuffer, ///< Buffer where to copy output samples.
|
virtual uint receiveSamples(SAMPLETYPE *outBuffer, ///< Buffer where to copy output samples.
|
||||||
uint maxSamples ///< How many samples to receive at max.
|
uint maxSamples ///< How many samples to receive at max.
|
||||||
)
|
) override
|
||||||
{
|
{
|
||||||
return output->receiveSamples(outBuffer, maxSamples);
|
return output->receiveSamples(outBuffer, maxSamples);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Adjusts book-keeping so that given number of samples are removed from beginning of the
|
/// Adjusts book-keeping so that given number of samples are removed from beginning of the
|
||||||
/// sample buffer without copying them anywhere.
|
/// sample buffer without copying them anywhere.
|
||||||
///
|
///
|
||||||
/// Used to reduce the number of samples in the buffer when accessing the sample buffer directly
|
/// Used to reduce the number of samples in the buffer when accessing the sample buffer directly
|
||||||
/// with 'ptrBegin' function.
|
/// with 'ptrBegin' function.
|
||||||
virtual uint receiveSamples(uint maxSamples ///< Remove this many samples from the beginning of pipe.
|
virtual uint receiveSamples(uint maxSamples ///< Remove this many samples from the beginning of pipe.
|
||||||
)
|
) override
|
||||||
{
|
{
|
||||||
return output->receiveSamples(maxSamples);
|
return output->receiveSamples(maxSamples);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Returns number of samples currently available.
|
/// Returns number of samples currently available.
|
||||||
virtual uint numSamples() const
|
virtual uint numSamples() const override
|
||||||
{
|
{
|
||||||
return output->numSamples();
|
return output->numSamples();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Returns nonzero if there aren't any samples available for outputting.
|
/// Returns nonzero if there aren't any samples available for outputting.
|
||||||
virtual int isEmpty() const
|
virtual int isEmpty() const override
|
||||||
{
|
{
|
||||||
return output->isEmpty();
|
return output->isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// allow trimming (downwards) amount of samples in pipeline.
|
/// allow trimming (downwards) amount of samples in pipeline.
|
||||||
/// Returns adjusted amount of samples
|
/// Returns adjusted amount of samples
|
||||||
virtual uint adjustAmountOfSamples(uint numSamples)
|
virtual uint adjustAmountOfSamples(uint numSamples) override
|
||||||
{
|
{
|
||||||
return output->adjustAmountOfSamples(numSamples);
|
return output->adjustAmountOfSamples(numSamples);
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,9 +1,5 @@
|
|||||||
## Process this file with automake to create Makefile.in
|
## Process this file with automake to create Makefile.in
|
||||||
##
|
##
|
||||||
## $Id$
|
|
||||||
##
|
|
||||||
## Copyright (C) 2003 - David W. Durham
|
|
||||||
##
|
|
||||||
## This file is part of SoundTouch, an audio processing library for pitch/time adjustments
|
## This file is part of SoundTouch, an audio processing library for pitch/time adjustments
|
||||||
##
|
##
|
||||||
## SoundTouch is free software; you can redistribute it and/or modify it under the
|
## SoundTouch is free software; you can redistribute it and/or modify it under the
|
||||||
|
|||||||
@ -8,13 +8,6 @@
|
|||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Last changed : $Date$
|
|
||||||
// File revision : $Revision: 3 $
|
|
||||||
//
|
|
||||||
// $Id$
|
|
||||||
//
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// License :
|
// License :
|
||||||
//
|
//
|
||||||
// SoundTouch audio processing library
|
// SoundTouch audio processing library
|
||||||
@ -42,29 +35,50 @@
|
|||||||
typedef unsigned int uint;
|
typedef unsigned int uint;
|
||||||
typedef unsigned long ulong;
|
typedef unsigned long ulong;
|
||||||
|
|
||||||
#ifdef __GNUC__
|
// Patch for MinGW: on Win64 long is 32-bit
|
||||||
// In GCC, include soundtouch_config.h made by config scritps
|
#ifdef _WIN64
|
||||||
#include "soundtouch_config.h"
|
typedef unsigned long long ulongptr;
|
||||||
|
#else
|
||||||
|
typedef ulong ulongptr;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef _WINDEF_
|
|
||||||
// if these aren't defined already by Windows headers, define now
|
|
||||||
|
|
||||||
typedef int BOOL;
|
// Helper macro for aligning pointer up to next 16-byte boundary
|
||||||
|
#define SOUNDTOUCH_ALIGN_POINTER_16(x) ( ( (ulongptr)(x) + 15 ) & ~(ulongptr)15 )
|
||||||
|
|
||||||
#define FALSE 0
|
|
||||||
#define TRUE 1
|
|
||||||
|
|
||||||
#endif // _WINDEF_
|
#if (defined(__GNUC__) && !defined(ANDROID))
|
||||||
|
// In GCC, include soundtouch_config.h made by config scritps.
|
||||||
|
// Skip this in Android compilation that uses GCC but without configure scripts.
|
||||||
|
#include "soundtouch_config.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
namespace soundtouch
|
namespace soundtouch
|
||||||
{
|
{
|
||||||
|
/// Max allowed number of channels. This is not a hard limit but to have some
|
||||||
|
/// maximum value for argument sanity checks -- can be increased if necessary
|
||||||
|
#define SOUNDTOUCH_MAX_CHANNELS 32
|
||||||
|
|
||||||
/// Activate these undef's to overrule the possible sampletype
|
/// Activate these undef's to overrule the possible sampletype
|
||||||
/// setting inherited from some other header file:
|
/// setting inherited from some other header file:
|
||||||
//#undef SOUNDTOUCH_INTEGER_SAMPLES
|
//#undef SOUNDTOUCH_INTEGER_SAMPLES
|
||||||
//#undef SOUNDTOUCH_FLOAT_SAMPLES
|
//#undef SOUNDTOUCH_FLOAT_SAMPLES
|
||||||
|
|
||||||
|
/// If following flag is defined, always uses multichannel processing
|
||||||
|
/// routines also for mono and stero sound. This is for routine testing
|
||||||
|
/// purposes; output should be same with either routines, yet disabling
|
||||||
|
/// the dedicated mono/stereo processing routines will result in slower
|
||||||
|
/// runtime performance so recommendation is to keep this off.
|
||||||
|
// #define USE_MULTICH_ALWAYS
|
||||||
|
|
||||||
|
#if (defined(__SOFTFP__) && defined(ANDROID))
|
||||||
|
// For Android compilation: Force use of Integer samples in case that
|
||||||
|
// compilation uses soft-floating point emulation - soft-fp is way too slow
|
||||||
|
#undef SOUNDTOUCH_FLOAT_SAMPLES
|
||||||
|
#define SOUNDTOUCH_INTEGER_SAMPLES 1
|
||||||
|
#endif
|
||||||
|
|
||||||
#if !(SOUNDTOUCH_INTEGER_SAMPLES || SOUNDTOUCH_FLOAT_SAMPLES)
|
#if !(SOUNDTOUCH_INTEGER_SAMPLES || SOUNDTOUCH_FLOAT_SAMPLES)
|
||||||
|
|
||||||
/// Choose either 32bit floating point or 16bit integer sampletype
|
/// Choose either 32bit floating point or 16bit integer sampletype
|
||||||
@ -108,10 +122,10 @@ namespace soundtouch
|
|||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// If defined, allows the SIMD-optimized routines to take minor shortcuts
|
// If defined, allows the SIMD-optimized routines to skip unevenly aligned
|
||||||
// for improved performance. Undefine to require faithfully similar SIMD
|
// memory offsets that can cause performance penalty in some SIMD implementations.
|
||||||
// calculations as in normal C implementation.
|
// Causes slight compromise in sound quality.
|
||||||
#define SOUNDTOUCH_ALLOW_NONEXACT_SIMD_OPTIMIZATION 1
|
// #define SOUNDTOUCH_ALLOW_NONEXACT_SIMD_OPTIMIZATION 1
|
||||||
|
|
||||||
|
|
||||||
#ifdef SOUNDTOUCH_INTEGER_SAMPLES
|
#ifdef SOUNDTOUCH_INTEGER_SAMPLES
|
||||||
@ -126,16 +140,19 @@ namespace soundtouch
|
|||||||
#endif // SOUNDTOUCH_FLOAT_SAMPLES
|
#endif // SOUNDTOUCH_FLOAT_SAMPLES
|
||||||
|
|
||||||
#ifdef SOUNDTOUCH_ALLOW_X86_OPTIMIZATIONS
|
#ifdef SOUNDTOUCH_ALLOW_X86_OPTIMIZATIONS
|
||||||
// Allow MMX optimizations
|
// Allow MMX optimizations (not available in X64 mode)
|
||||||
|
#if (!_M_X64)
|
||||||
#define SOUNDTOUCH_ALLOW_MMX 1
|
#define SOUNDTOUCH_ALLOW_MMX 1
|
||||||
#endif
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
// floating point samples
|
// floating point samples
|
||||||
typedef float SAMPLETYPE;
|
typedef float SAMPLETYPE;
|
||||||
// data type for sample accumulation: Use double to utilize full precision.
|
// data type for sample accumulation: Use float also here to enable
|
||||||
typedef double LONG_SAMPLETYPE;
|
// efficient autovectorization
|
||||||
|
typedef float LONG_SAMPLETYPE;
|
||||||
|
|
||||||
#ifdef SOUNDTOUCH_ALLOW_X86_OPTIMIZATIONS
|
#ifdef SOUNDTOUCH_ALLOW_X86_OPTIMIZATIONS
|
||||||
// Allow SSE optimizations
|
// Allow SSE optimizations
|
||||||
@ -144,7 +161,13 @@ namespace soundtouch
|
|||||||
|
|
||||||
#endif // SOUNDTOUCH_INTEGER_SAMPLES
|
#endif // SOUNDTOUCH_INTEGER_SAMPLES
|
||||||
|
|
||||||
};
|
#if ((SOUNDTOUCH_ALLOW_SSE) || (__SSE__) || (SOUNDTOUCH_USE_NEON))
|
||||||
|
#if SOUNDTOUCH_ALLOW_NONEXACT_SIMD_OPTIMIZATION
|
||||||
|
#define ST_SIMD_AVOID_UNALIGNED
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
// define ST_NO_EXCEPTION_HANDLING switch to disable throwing std exceptions:
|
// define ST_NO_EXCEPTION_HANDLING switch to disable throwing std exceptions:
|
||||||
// #define ST_NO_EXCEPTION_HANDLING 1
|
// #define ST_NO_EXCEPTION_HANDLING 1
|
||||||
@ -155,6 +178,7 @@ namespace soundtouch
|
|||||||
#else
|
#else
|
||||||
// use c++ standard exceptions
|
// use c++ standard exceptions
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
#include <string>
|
||||||
#define ST_THROW_RT_ERROR(x) {throw std::runtime_error(x);}
|
#define ST_THROW_RT_ERROR(x) {throw std::runtime_error(x);}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
@ -41,13 +41,6 @@
|
|||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Last changed : $Date$
|
|
||||||
// File revision : $Revision: 4 $
|
|
||||||
//
|
|
||||||
// $Id$
|
|
||||||
//
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// License :
|
// License :
|
||||||
//
|
//
|
||||||
// SoundTouch audio processing library
|
// SoundTouch audio processing library
|
||||||
@ -79,10 +72,10 @@ namespace soundtouch
|
|||||||
{
|
{
|
||||||
|
|
||||||
/// Soundtouch library version string
|
/// Soundtouch library version string
|
||||||
#define SOUNDTOUCH_VERSION "1.7.0"
|
#define SOUNDTOUCH_VERSION "2.3.3"
|
||||||
|
|
||||||
/// SoundTouch library version id
|
/// SoundTouch library version id
|
||||||
#define SOUNDTOUCH_VERSION_ID (10700)
|
#define SOUNDTOUCH_VERSION_ID (20303)
|
||||||
|
|
||||||
//
|
//
|
||||||
// Available setting IDs for the 'setSetting' & 'get_setting' functions:
|
// Available setting IDs for the 'setSetting' & 'get_setting' functions:
|
||||||
@ -116,15 +109,17 @@ namespace soundtouch
|
|||||||
#define SETTING_OVERLAP_MS 5
|
#define SETTING_OVERLAP_MS 5
|
||||||
|
|
||||||
|
|
||||||
/// Call "getSetting" with this ID to query nominal average processing sequence
|
/// Call "getSetting" with this ID to query processing sequence size in samples.
|
||||||
/// size in samples. This value tells approcimate value how many input samples
|
/// This value gives approximate value of how many input samples you'll need to
|
||||||
/// SoundTouch needs to gather before it does DSP processing run for the sample batch.
|
/// feed into SoundTouch after initial buffering to get out a new batch of
|
||||||
|
/// output samples.
|
||||||
|
///
|
||||||
|
/// This value does not include initial buffering at beginning of a new processing
|
||||||
|
/// stream, use SETTING_INITIAL_LATENCY to get the initial buffering size.
|
||||||
///
|
///
|
||||||
/// Notices:
|
/// Notices:
|
||||||
/// - This is read-only parameter, i.e. setSetting ignores this parameter
|
/// - This is read-only parameter, i.e. setSetting ignores this parameter
|
||||||
/// - Returned value is approximate average value, exact processing batch
|
/// - This parameter value is not constant but change depending on
|
||||||
/// size may wary from time to time
|
|
||||||
/// - This parameter value is not constant but may change depending on
|
|
||||||
/// tempo/pitch/rate/samplerate settings.
|
/// tempo/pitch/rate/samplerate settings.
|
||||||
#define SETTING_NOMINAL_INPUT_SEQUENCE 6
|
#define SETTING_NOMINAL_INPUT_SEQUENCE 6
|
||||||
|
|
||||||
@ -135,12 +130,41 @@ namespace soundtouch
|
|||||||
///
|
///
|
||||||
/// Notices:
|
/// Notices:
|
||||||
/// - This is read-only parameter, i.e. setSetting ignores this parameter
|
/// - This is read-only parameter, i.e. setSetting ignores this parameter
|
||||||
/// - Returned value is approximate average value, exact processing batch
|
/// - This parameter value is not constant but change depending on
|
||||||
/// size may wary from time to time
|
|
||||||
/// - This parameter value is not constant but may change depending on
|
|
||||||
/// tempo/pitch/rate/samplerate settings.
|
/// tempo/pitch/rate/samplerate settings.
|
||||||
#define SETTING_NOMINAL_OUTPUT_SEQUENCE 7
|
#define SETTING_NOMINAL_OUTPUT_SEQUENCE 7
|
||||||
|
|
||||||
|
|
||||||
|
/// Call "getSetting" with this ID to query initial processing latency, i.e.
|
||||||
|
/// approx. how many samples you'll need to enter to SoundTouch pipeline before
|
||||||
|
/// you can expect to get first batch of ready output samples out.
|
||||||
|
///
|
||||||
|
/// After the first output batch, you can then expect to get approx.
|
||||||
|
/// SETTING_NOMINAL_OUTPUT_SEQUENCE ready samples out for every
|
||||||
|
/// SETTING_NOMINAL_INPUT_SEQUENCE samples that you enter into SoundTouch.
|
||||||
|
///
|
||||||
|
/// Example:
|
||||||
|
/// processing with parameter -tempo=5
|
||||||
|
/// => initial latency = 5509 samples
|
||||||
|
/// input sequence = 4167 samples
|
||||||
|
/// output sequence = 3969 samples
|
||||||
|
///
|
||||||
|
/// Accordingly, you can expect to feed in approx. 5509 samples at beginning of
|
||||||
|
/// the stream, and then you'll get out the first 3969 samples. After that, for
|
||||||
|
/// every approx. 4167 samples that you'll put in, you'll receive again approx.
|
||||||
|
/// 3969 samples out.
|
||||||
|
///
|
||||||
|
/// This also means that average latency during stream processing is
|
||||||
|
/// INITIAL_LATENCY-OUTPUT_SEQUENCE/2, in the above example case 5509-3969/2
|
||||||
|
/// = 3524 samples
|
||||||
|
///
|
||||||
|
/// Notices:
|
||||||
|
/// - This is read-only parameter, i.e. setSetting ignores this parameter
|
||||||
|
/// - This parameter value is not constant but change depending on
|
||||||
|
/// tempo/pitch/rate/samplerate settings.
|
||||||
|
#define SETTING_INITIAL_LATENCY 8
|
||||||
|
|
||||||
|
|
||||||
class SoundTouch : public FIFOProcessor
|
class SoundTouch : public FIFOProcessor
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
@ -151,16 +175,23 @@ private:
|
|||||||
class TDStretch *pTDStretch;
|
class TDStretch *pTDStretch;
|
||||||
|
|
||||||
/// Virtual pitch parameter. Effective rate & tempo are calculated from these parameters.
|
/// Virtual pitch parameter. Effective rate & tempo are calculated from these parameters.
|
||||||
float virtualRate;
|
double virtualRate;
|
||||||
|
|
||||||
/// Virtual pitch parameter. Effective rate & tempo are calculated from these parameters.
|
/// Virtual pitch parameter. Effective rate & tempo are calculated from these parameters.
|
||||||
float virtualTempo;
|
double virtualTempo;
|
||||||
|
|
||||||
/// Virtual pitch parameter. Effective rate & tempo are calculated from these parameters.
|
/// Virtual pitch parameter. Effective rate & tempo are calculated from these parameters.
|
||||||
float virtualPitch;
|
double virtualPitch;
|
||||||
|
|
||||||
/// Flag: Has sample rate been set?
|
/// Flag: Has sample rate been set?
|
||||||
BOOL bSrateSet;
|
bool bSrateSet;
|
||||||
|
|
||||||
|
/// Accumulator for how many samples in total will be expected as output vs. samples put in,
|
||||||
|
/// considering current processing settings.
|
||||||
|
double samplesExpectedOut;
|
||||||
|
|
||||||
|
/// Accumulator for how many samples in total have been read out from the processing so far
|
||||||
|
long samplesOutput;
|
||||||
|
|
||||||
/// Calculates effective rate & tempo valuescfrom 'virtualRate', 'virtualTempo' and
|
/// Calculates effective rate & tempo valuescfrom 'virtualRate', 'virtualTempo' and
|
||||||
/// 'virtualPitch' parameters.
|
/// 'virtualPitch' parameters.
|
||||||
@ -171,14 +202,14 @@ protected :
|
|||||||
uint channels;
|
uint channels;
|
||||||
|
|
||||||
/// Effective 'rate' value calculated from 'virtualRate', 'virtualTempo' and 'virtualPitch'
|
/// Effective 'rate' value calculated from 'virtualRate', 'virtualTempo' and 'virtualPitch'
|
||||||
float rate;
|
double rate;
|
||||||
|
|
||||||
/// Effective 'tempo' value calculated from 'virtualRate', 'virtualTempo' and 'virtualPitch'
|
/// Effective 'tempo' value calculated from 'virtualRate', 'virtualTempo' and 'virtualPitch'
|
||||||
float tempo;
|
double tempo;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
SoundTouch();
|
SoundTouch();
|
||||||
virtual ~SoundTouch();
|
virtual ~SoundTouch() override;
|
||||||
|
|
||||||
/// Get SoundTouch library version string
|
/// Get SoundTouch library version string
|
||||||
static const char *getVersionString();
|
static const char *getVersionString();
|
||||||
@ -188,32 +219,32 @@ public:
|
|||||||
|
|
||||||
/// Sets new rate control value. Normal rate = 1.0, smaller values
|
/// Sets new rate control value. Normal rate = 1.0, smaller values
|
||||||
/// represent slower rate, larger faster rates.
|
/// represent slower rate, larger faster rates.
|
||||||
void setRate(float newRate);
|
void setRate(double newRate);
|
||||||
|
|
||||||
/// Sets new tempo control value. Normal tempo = 1.0, smaller values
|
/// Sets new tempo control value. Normal tempo = 1.0, smaller values
|
||||||
/// represent slower tempo, larger faster tempo.
|
/// represent slower tempo, larger faster tempo.
|
||||||
void setTempo(float newTempo);
|
void setTempo(double newTempo);
|
||||||
|
|
||||||
/// Sets new rate control value as a difference in percents compared
|
/// Sets new rate control value as a difference in percents compared
|
||||||
/// to the original rate (-50 .. +100 %)
|
/// to the original rate (-50 .. +100 %)
|
||||||
void setRateChange(float newRate);
|
void setRateChange(double newRate);
|
||||||
|
|
||||||
/// Sets new tempo control value as a difference in percents compared
|
/// Sets new tempo control value as a difference in percents compared
|
||||||
/// to the original tempo (-50 .. +100 %)
|
/// to the original tempo (-50 .. +100 %)
|
||||||
void setTempoChange(float newTempo);
|
void setTempoChange(double newTempo);
|
||||||
|
|
||||||
/// Sets new pitch control value. Original pitch = 1.0, smaller values
|
/// Sets new pitch control value. Original pitch = 1.0, smaller values
|
||||||
/// represent lower pitches, larger values higher pitch.
|
/// represent lower pitches, larger values higher pitch.
|
||||||
void setPitch(float newPitch);
|
void setPitch(double newPitch);
|
||||||
|
|
||||||
/// Sets pitch change in octaves compared to the original pitch
|
/// Sets pitch change in octaves compared to the original pitch
|
||||||
/// (-1.00 .. +1.00)
|
/// (-1.00 .. +1.00)
|
||||||
void setPitchOctaves(float newPitch);
|
void setPitchOctaves(double newPitch);
|
||||||
|
|
||||||
/// Sets pitch change in semi-tones compared to the original pitch
|
/// Sets pitch change in semi-tones compared to the original pitch
|
||||||
/// (-12 .. +12)
|
/// (-12 .. +12)
|
||||||
void setPitchSemiTones(int newPitch);
|
void setPitchSemiTones(int newPitch);
|
||||||
void setPitchSemiTones(float newPitch);
|
void setPitchSemiTones(double newPitch);
|
||||||
|
|
||||||
/// Sets the number of channels, 1 = mono, 2 = stereo
|
/// Sets the number of channels, 1 = mono, 2 = stereo
|
||||||
void setChannels(uint numChannels);
|
void setChannels(uint numChannels);
|
||||||
@ -221,6 +252,24 @@ public:
|
|||||||
/// Sets sample rate.
|
/// Sets sample rate.
|
||||||
void setSampleRate(uint srate);
|
void setSampleRate(uint srate);
|
||||||
|
|
||||||
|
/// Get ratio between input and output audio durations, useful for calculating
|
||||||
|
/// processed output duration: if you'll process a stream of N samples, then
|
||||||
|
/// you can expect to get out N * getInputOutputSampleRatio() samples.
|
||||||
|
///
|
||||||
|
/// This ratio will give accurate target duration ratio for a full audio track,
|
||||||
|
/// given that the the whole track is processed with same processing parameters.
|
||||||
|
///
|
||||||
|
/// If this ratio is applied to calculate intermediate offsets inside a processing
|
||||||
|
/// stream, then this ratio is approximate and can deviate +- some tens of milliseconds
|
||||||
|
/// from ideal offset, yet by end of the audio stream the duration ratio will become
|
||||||
|
/// exact.
|
||||||
|
///
|
||||||
|
/// Example: if processing with parameters "-tempo=15 -pitch=-3", the function
|
||||||
|
/// will return value 0.8695652... Now, if processing an audio stream whose duration
|
||||||
|
/// is exactly one million audio samples, then you can expect the processed
|
||||||
|
/// output duration be 0.869565 * 1000000 = 869565 samples.
|
||||||
|
double getInputOutputSampleRatio();
|
||||||
|
|
||||||
/// Flushes the last samples from the processing pipeline to the output.
|
/// Flushes the last samples from the processing pipeline to the output.
|
||||||
/// Clears also the internal processing buffers.
|
/// Clears also the internal processing buffers.
|
||||||
//
|
//
|
||||||
@ -238,17 +287,34 @@ public:
|
|||||||
uint numSamples ///< Number of samples in buffer. Notice
|
uint numSamples ///< Number of samples in buffer. Notice
|
||||||
///< that in case of stereo-sound a single sample
|
///< that in case of stereo-sound a single sample
|
||||||
///< contains data for both channels.
|
///< contains data for both channels.
|
||||||
);
|
) override;
|
||||||
|
|
||||||
|
/// Output samples from beginning of the sample buffer. Copies requested samples to
|
||||||
|
/// output buffer and removes them from the sample buffer. If there are less than
|
||||||
|
/// 'numsample' samples in the buffer, returns all that available.
|
||||||
|
///
|
||||||
|
/// \return Number of samples returned.
|
||||||
|
virtual uint receiveSamples(SAMPLETYPE *output, ///< Buffer where to copy output samples.
|
||||||
|
uint maxSamples ///< How many samples to receive at max.
|
||||||
|
) override;
|
||||||
|
|
||||||
|
/// Adjusts book-keeping so that given number of samples are removed from beginning of the
|
||||||
|
/// sample buffer without copying them anywhere.
|
||||||
|
///
|
||||||
|
/// Used to reduce the number of samples in the buffer when accessing the sample buffer directly
|
||||||
|
/// with 'ptrBegin' function.
|
||||||
|
virtual uint receiveSamples(uint maxSamples ///< Remove this many samples from the beginning of pipe.
|
||||||
|
) override;
|
||||||
|
|
||||||
/// Clears all the samples in the object's output and internal processing
|
/// Clears all the samples in the object's output and internal processing
|
||||||
/// buffers.
|
/// buffers.
|
||||||
virtual void clear();
|
virtual void clear() override;
|
||||||
|
|
||||||
/// Changes a setting controlling the processing system behaviour. See the
|
/// Changes a setting controlling the processing system behaviour. See the
|
||||||
/// 'SETTING_...' defines for available setting ID's.
|
/// 'SETTING_...' defines for available setting ID's.
|
||||||
///
|
///
|
||||||
/// \return 'TRUE' if the setting was succesfully changed
|
/// \return 'true' if the setting was successfully changed
|
||||||
BOOL setSetting(int settingId, ///< Setting ID number. see SETTING_... defines.
|
bool setSetting(int settingId, ///< Setting ID number. see SETTING_... defines.
|
||||||
int value ///< New setting value.
|
int value ///< New setting value.
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -262,6 +328,11 @@ public:
|
|||||||
/// Returns number of samples currently unprocessed.
|
/// Returns number of samples currently unprocessed.
|
||||||
virtual uint numUnprocessedSamples() const;
|
virtual uint numUnprocessedSamples() const;
|
||||||
|
|
||||||
|
/// Return number of channels
|
||||||
|
uint numChannels() const
|
||||||
|
{
|
||||||
|
return channels;
|
||||||
|
}
|
||||||
|
|
||||||
/// Other handy functions that are implemented in the ancestor classes (see
|
/// Other handy functions that are implemented in the ancestor classes (see
|
||||||
/// classes 'FIFOProcessor' and 'FIFOSamplePipe')
|
/// classes 'FIFOProcessor' and 'FIFOSamplePipe')
|
||||||
|
|||||||
3
include/soundtouch_config.h
Normal file
3
include/soundtouch_config.h
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
// autotools configuration step replaces this file with a configured version.
|
||||||
|
// this empty file stub is provided to avoid error about missing include file
|
||||||
|
// when not using autotools build
|
||||||
8
include/soundtouch_config.h.in
Normal file
8
include/soundtouch_config.h.in
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
/* Use Float as Sample type */
|
||||||
|
#undef SOUNDTOUCH_FLOAT_SAMPLES
|
||||||
|
|
||||||
|
/* Use Integer as Sample type */
|
||||||
|
#undef SOUNDTOUCH_INTEGER_SAMPLES
|
||||||
|
|
||||||
|
/* Use ARM NEON extension */
|
||||||
|
#undef SOUNDTOUCH_USE_NEON
|
||||||
50
make-win.bat
50
make-win.bat
@ -5,42 +5,21 @@
|
|||||||
@REM "vcvars32.bat" in VC install directotry before running this one.
|
@REM "vcvars32.bat" in VC install directotry before running this one.
|
||||||
@REM
|
@REM
|
||||||
@REM Copyright (c) Olli Parviainen
|
@REM Copyright (c) Olli Parviainen
|
||||||
@REM File Created: 09/Sep/2003
|
|
||||||
@REM
|
@REM
|
||||||
@REM $Id$
|
|
||||||
|
|
||||||
@rem ****************************
|
|
||||||
@rem try first for VS6.0 support
|
|
||||||
|
|
||||||
@if "%MsDevDir%"=="" goto nomsdevdir
|
|
||||||
|
|
||||||
md bin
|
|
||||||
md lib
|
|
||||||
msdev source\SoundTouch\SoundTouch.dsw /MAKE ALL
|
|
||||||
msdev source\SoundStretch\SoundStretch.dsw /MAKE ALL
|
|
||||||
|
|
||||||
goto end
|
|
||||||
|
|
||||||
:nomsdevdir
|
|
||||||
|
|
||||||
@rem **********************************
|
|
||||||
@rem try with devenv for VS2003 support
|
|
||||||
|
|
||||||
@if "%DevEnvDir%"=="" goto nodevdir
|
@if "%DevEnvDir%"=="" goto nodevdir
|
||||||
|
|
||||||
md bin
|
@rem devenv source\SoundStretch\SoundStretch.sln /upgrade
|
||||||
md lib
|
devenv source\SoundStretch\SoundStretch.sln /build "Debug|Win32"
|
||||||
devenv source\SoundTouch\SoundTouch.vcproj /upgrade
|
devenv source\SoundStretch\SoundStretch.sln /build "Release|Win32"
|
||||||
devenv source\SoundTouch\SoundTouch.vcproj /build debug
|
devenv source\SoundStretch\SoundStretch.sln /build "Debug|x64"
|
||||||
devenv source\SoundTouch\SoundTouch.vcproj /build release
|
devenv source\SoundStretch\SoundStretch.sln /build "Release|x64"
|
||||||
|
|
||||||
devenv source\SoundStretch\SoundStretch.vcproj /upgrade
|
@rem devenv source\SoundTouchDll\SoundTouchDll.sln /upgrade
|
||||||
devenv source\SoundStretch\SoundStretch.vcproj /build debug
|
devenv source\SoundTouchDll\SoundTouchDll.sln /build "Debug|Win32"
|
||||||
devenv source\SoundStretch\SoundStretch.vcproj /build release
|
devenv source\SoundTouchDll\SoundTouchDll.sln /build "Release|Win32"
|
||||||
|
devenv source\SoundTouchDll\SoundTouchDll.sln /build "Debug|x64"
|
||||||
devenv source\SoundTouchDll\SoundTouchDll.vcproj /upgrade
|
devenv source\SoundTouchDll\SoundTouchDll.sln /build "Release|x64"
|
||||||
devenv source\SoundTouchDll\SoundTouchDll.vcproj /build debug
|
|
||||||
devenv source\SoundTouchDll\SoundTouchDll.vcproj /build release
|
|
||||||
|
|
||||||
@goto end
|
@goto end
|
||||||
|
|
||||||
@ -48,14 +27,15 @@ devenv source\SoundTouchDll\SoundTouchDll.vcproj /build release
|
|||||||
:nodevdir
|
:nodevdir
|
||||||
|
|
||||||
@echo off
|
@echo off
|
||||||
echo *********************************************************************
|
echo ****************************************************************************
|
||||||
echo **
|
echo **
|
||||||
echo ** ERROR: Visual Studio path not set.
|
echo ** ERROR: Visual Studio path not set.
|
||||||
echo **
|
echo **
|
||||||
echo ** Run "vsvars32.bat" or "vcvars32.bat" from Visual Studio installation
|
echo ** Open "tools"->"Developer Command Line" from Visual Studio IDE, or
|
||||||
echo ** directory, e.g. "\Program Files\Microsoft Visual Studio\VC98\Bin",
|
echo ** run "vcvars32.bat" from Visual Studio installation dir, e.g.
|
||||||
|
echo ** "C:\Program Files (x86)\Microsoft Visual Studio xxx\VC\bin",
|
||||||
echo ** then try again.
|
echo ** then try again.
|
||||||
echo **
|
echo **
|
||||||
echo *********************************************************************
|
echo ****************************************************************************
|
||||||
|
|
||||||
:end
|
:end
|
||||||
|
|||||||
66
readme.md
Normal file
66
readme.md
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
# SoundTouch library
|
||||||
|
|
||||||
|
## About
|
||||||
|
|
||||||
|
SoundTouch is an open-source audio processing library that allows changing the sound tempo, pitch and playback rate parameters independently from each other:
|
||||||
|
* Change **tempo** while maintaining the original pitch
|
||||||
|
* Change **pitch** while maintaining the original tempo
|
||||||
|
* Change **playback rate** that affects both tempo and pitch at the
|
||||||
|
same time
|
||||||
|
* Change any combination of tempo/pitch/rate
|
||||||
|
|
||||||
|
Visit [SoundTouch website](https://www.surina.net/soundtouch) and see the [README file](https://www.surina.net/soundtouch/readme.html) for more information and audio examples.
|
||||||
|
|
||||||
|
### The latest stable release is 2.3.3
|
||||||
|
|
||||||
|
## Example
|
||||||
|
|
||||||
|
Use SoundStretch example app for modifying wav audio files, for example as follows:
|
||||||
|
|
||||||
|
```
|
||||||
|
soundstretch my_original_file.wav output_file.wav -tempo=+15 -pitch=-3
|
||||||
|
```
|
||||||
|
|
||||||
|
See the [README file](http://soundtouch.surina.net/README.html) for more usage examples and instructions how to build SoundTouch + SoundStretch.
|
||||||
|
|
||||||
|
Ready [SoundStretch application executables](https://www.surina.net/soundtouch/download.html) are available for download for Windows and Mac OS.
|
||||||
|
|
||||||
|
## Language & Platforms
|
||||||
|
|
||||||
|
SoundTouch is written in C++ and compiles in virtually any platform:
|
||||||
|
* Windows
|
||||||
|
* Mac OS
|
||||||
|
* Linux & Unices (including also Raspberry, Beaglebone, Yocto etc embedded Linux flavors)
|
||||||
|
* Android
|
||||||
|
* iOS
|
||||||
|
* embedded systems
|
||||||
|
|
||||||
|
The source code package includes dynamic library import modules for C#, Java and Pascal/Delphi languages.
|
||||||
|
|
||||||
|
## Tarballs
|
||||||
|
|
||||||
|
Source code release tarballs:
|
||||||
|
* https://www.surina.net/soundtouch/soundtouch-2.3.3.tar.gz
|
||||||
|
* https://www.surina.net/soundtouch/soundtouch-2.3.2.tar.gz
|
||||||
|
* https://www.surina.net/soundtouch/soundtouch-2.3.1.tar.gz
|
||||||
|
* https://www.surina.net/soundtouch/soundtouch-2.3.0.tar.gz
|
||||||
|
* https://www.surina.net/soundtouch/soundtouch-2.2.0.tar.gz
|
||||||
|
* https://www.surina.net/soundtouch/soundtouch-2.1.2.tar.gz
|
||||||
|
* https://www.surina.net/soundtouch/soundtouch-2.1.1.tar.gz
|
||||||
|
* https://www.surina.net/soundtouch/soundtouch-2.0.0.tar.gz
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
SoundTouch is released under LGPL v2.1:
|
||||||
|
|
||||||
|
This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License version 2.1 as published by the Free Software Foundation.
|
||||||
|
|
||||||
|
This library 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 Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
|
||||||
|
See [LGPL v2.1 full license text ](https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html) for details.
|
||||||
|
|
||||||
|
--
|
||||||
|
|
||||||
|
Also commercial license free of GPL limitations available upon request
|
||||||
@ -1,14 +0,0 @@
|
|||||||
# This file is obsoleted but provided for backwards compatibility
|
|
||||||
# with legacy package environments
|
|
||||||
|
|
||||||
prefix=@prefix@
|
|
||||||
exec_prefix=@exec_prefix@
|
|
||||||
libdir=@libdir@
|
|
||||||
includedir=@includedir@
|
|
||||||
|
|
||||||
Name: SoundTouch
|
|
||||||
Description: SoundTouch is an open-source audio processing library for changing the Tempo, Pitch and Playback Rates of audio streams or files
|
|
||||||
Version: @VERSION@
|
|
||||||
Libs: -L${libdir} -lSoundTouch
|
|
||||||
Cflags: -I${includedir}/soundtouch
|
|
||||||
|
|
||||||
@ -1,7 +1,4 @@
|
|||||||
# m4 configure test script for the SoundTouch library
|
# m4 configure test script for the SoundTouch library
|
||||||
# (c)2003 David W. Durham
|
|
||||||
#
|
|
||||||
# $Id$
|
|
||||||
#
|
#
|
||||||
# This file can be included with other packages that need to test
|
# This file can be included with other packages that need to test
|
||||||
# for libSoundTouch.
|
# for libSoundTouch.
|
||||||
|
|||||||
9
source/Android-lib/.classpath
Normal file
9
source/Android-lib/.classpath
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<classpath>
|
||||||
|
<classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
|
||||||
|
<classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/>
|
||||||
|
<classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.DEPENDENCIES"/>
|
||||||
|
<classpathentry kind="src" path="src"/>
|
||||||
|
<classpathentry kind="src" path="gen"/>
|
||||||
|
<classpathentry kind="output" path="bin/classes"/>
|
||||||
|
</classpath>
|
||||||
33
source/Android-lib/.project
Normal file
33
source/Android-lib/.project
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<projectDescription>
|
||||||
|
<name>ExampleActivity</name>
|
||||||
|
<comment></comment>
|
||||||
|
<projects>
|
||||||
|
</projects>
|
||||||
|
<buildSpec>
|
||||||
|
<buildCommand>
|
||||||
|
<name>com.android.ide.eclipse.adt.ResourceManagerBuilder</name>
|
||||||
|
<arguments>
|
||||||
|
</arguments>
|
||||||
|
</buildCommand>
|
||||||
|
<buildCommand>
|
||||||
|
<name>com.android.ide.eclipse.adt.PreCompilerBuilder</name>
|
||||||
|
<arguments>
|
||||||
|
</arguments>
|
||||||
|
</buildCommand>
|
||||||
|
<buildCommand>
|
||||||
|
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||||
|
<arguments>
|
||||||
|
</arguments>
|
||||||
|
</buildCommand>
|
||||||
|
<buildCommand>
|
||||||
|
<name>com.android.ide.eclipse.adt.ApkBuilder</name>
|
||||||
|
<arguments>
|
||||||
|
</arguments>
|
||||||
|
</buildCommand>
|
||||||
|
</buildSpec>
|
||||||
|
<natures>
|
||||||
|
<nature>com.android.ide.eclipse.adt.AndroidNature</nature>
|
||||||
|
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||||
|
</natures>
|
||||||
|
</projectDescription>
|
||||||
30
source/Android-lib/AndroidManifest.xml
Normal file
30
source/Android-lib/AndroidManifest.xml
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
package="net.surina.soundtouchexample"
|
||||||
|
android:versionCode="1"
|
||||||
|
android:versionName="1.0" >
|
||||||
|
|
||||||
|
<uses-sdk
|
||||||
|
android:minSdkVersion="11"
|
||||||
|
android:targetSdkVersion="21" />
|
||||||
|
|
||||||
|
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||||
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||||
|
|
||||||
|
<application
|
||||||
|
android:allowBackup="true"
|
||||||
|
android:icon="@drawable/ic_launcher"
|
||||||
|
android:label="@string/app_name"
|
||||||
|
android:theme="@style/AppTheme" >
|
||||||
|
<activity
|
||||||
|
android:name="net.surina.ExampleActivity"
|
||||||
|
android:label="@string/app_name" >
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
|
||||||
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
|
</intent-filter>
|
||||||
|
</activity>
|
||||||
|
</application>
|
||||||
|
|
||||||
|
</manifest>
|
||||||
128
source/Android-lib/README-SoundTouch-Android.html
Normal file
128
source/Android-lib/README-SoundTouch-Android.html
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>SoundTouch in Android</title>
|
||||||
|
<meta http-equiv="Content-Type"
|
||||||
|
content="text/html; charset=windows-1252">
|
||||||
|
<meta http-equiv="Content-Language" content="en-us">
|
||||||
|
<meta name="author" content="Olli Parviainen">
|
||||||
|
<meta name="description"
|
||||||
|
content="Readme file for SoundTouch library Android compilation">
|
||||||
|
<style> <!-- .normal { font-family: Arial }
|
||||||
|
--></style>
|
||||||
|
</head>
|
||||||
|
<body class="normal">
|
||||||
|
<hr>
|
||||||
|
<h1>SoundTouch in Android</h1>
|
||||||
|
<hr>
|
||||||
|
<h2>Compiling SoundTouch for Android</h2>
|
||||||
|
<p>SoundTouch source code package contains "Android-lib" example project that compiles SoundTouch
|
||||||
|
source codes into Android native library, and gives an example of JNI interface
|
||||||
|
for invoking
|
||||||
|
the native SoundTouch routines from an Android application written in Java.</p>
|
||||||
|
<p style="font-weight: 700">Software prerequisites:</p>
|
||||||
|
<ul>
|
||||||
|
<li>Android SDK environment for developing your own Android application. Visit the <a href="http://developer.android.com/index.html">Android developers' site</a>
|
||||||
|
for more information about the Android SDK and developing Android applications.</li>
|
||||||
|
<li>Android NDK compiler kit for compiling native library binaries. The Android NDK
|
||||||
|
is <a href="http://developer.android.com/tools/sdk/ndk/index.html">
|
||||||
|
available for download</a> at the Android developer tools site.</li>
|
||||||
|
<li>In case you're working in Windows environment, install
|
||||||
|
<a href="http://cygwin.com/install.html">
|
||||||
|
Cygwin</a> to run the Android NDK/SDK compiler scripts</li>
|
||||||
|
<li>Latest SoundTouch source code package available at <a href="http://soundtouch.surina.net/sourcecode.html">
|
||||||
|
soundtouch.surina.net</a>.</li>
|
||||||
|
</ul>
|
||||||
|
<p><b>Hint: </b>As installing and configuring all the components for an Android SDK/NDK
|
||||||
|
environment requires fair effort, it's good idea to create a dedicated Virtual
|
||||||
|
Machine environment for the Android development environment installation.
|
||||||
|
Having the Android developer environment setup in dedicated Virtual Machine
|
||||||
|
allows keeping all these settings isolated from your other PC operations, and
|
||||||
|
eases taking backup snapshots of your full development environment.</p>
|
||||||
|
<p><b>Compiling</b></p>
|
||||||
|
<p>
|
||||||
|
To compile the SoundTouch library source codes into an Android native library,
|
||||||
|
open Cygwin/bash shell, go to directory <b>"soundtouch/source/Android-lib/jni"</b> and invoke the NDK
|
||||||
|
compiler with following command:</p>
|
||||||
|
<pre> $NDK/ndk-build</pre>
|
||||||
|
<p>This will build binaries for all the supported Android platforms (arm-v5, arm-v7, X86, MIPS etc) of SoundTouch library, plus the JNI wrapper interface as discussed below. The target binaries will be built into the "libs" subdirectory. As long as all these .so binary library versions are included in the APK Application delivery package, the target Android device can choose the correct library version to use. </p>
|
||||||
|
<p>Notice that to allow Cygwin/bash to locate the NDK compile scripts, you
|
||||||
|
need to define the location of the NDK installation defined in environment
|
||||||
|
variable "NDK". That's easiest done by adding the NDK path definition at end of
|
||||||
|
your <b>~/.bash_profile</b> file, for instance as follows:</p>
|
||||||
|
<pre> NDK=/cygdrive/d/Android/android-ndk-r6</pre>
|
||||||
|
<p><b>Enabling OpenMP parallel computing mode</b></p>
|
||||||
|
<p>
|
||||||
|
SoundTouch supports OpenMP for parallel computing in multi-core
|
||||||
|
environments, and these improvements can be enabled also in the Android
|
||||||
|
build. See the SoundTouch main README.html file for generic notes about the
|
||||||
|
OpenMP implementation.</p>
|
||||||
|
<p>
|
||||||
|
To enable OpenMP mode in Android compilation, edit file <strong>Android.mk</strong>
|
||||||
|
and enable the "-fopenmp" flag in LOCAL_CFLAGS and LOCAL_LDFLAGS variables.
|
||||||
|
This is done by removing hash # from before the following lines in the
|
||||||
|
Android.mk file, before compiling the library:</p>
|
||||||
|
<pre> LOCAL_CFLAGS += -fopenmp
|
||||||
|
LOCAL_LDFLAGS += -fopenmp</pre>
|
||||||
|
<p><strong>OpenMP COMPATIBILITY NOTE: </strong>Android NDK has a threading issue
|
||||||
|
(at least until NDK v10) that causes the native library crash with fatal signal
|
||||||
|
11 if calling OpenMP-improved routines from a background thread. SoundTouch has
|
||||||
|
a workaround for this issue in soundtouch-jni.cpp, and this workaround requires
|
||||||
|
calling function <strong>SoundTouch.getVersionString() </strong>from the Android
|
||||||
|
application's main thread at least once before calling other SoundTouch
|
||||||
|
processing routines. See the SoundTouch Android example application and comments
|
||||||
|
in the <strong>soundtouch-jni.cpp </strong>source code file for more details.</p>
|
||||||
|
<p>
|
||||||
|
<strong>SoundTouch performance in Android</strong></p>
|
||||||
|
<p>
|
||||||
|
See external blog articles for more discussion about the
|
||||||
|
<a href="http://www.softwarecoven.com/parallel-computing-in-embedded-mobile-devices/">
|
||||||
|
SoundTouch OpenMP implementation</a> and the
|
||||||
|
<a href="http://www.softwarecoven.com/parallel-computing-with-openmp-in-android/">
|
||||||
|
SoundTouch performance benchmark tests in Android environment</a>.</p>
|
||||||
|
<hr />
|
||||||
|
<h2>
|
||||||
|
Calling SoundTouch native routines from Android application</h2>
|
||||||
|
<p>The NDK tools build the SoundTouch c++ routines into a native binary library, while
|
||||||
|
Android applications are written in Java language. To call the SoundTouch and other c/c++
|
||||||
|
routines from an Android java application code, you'll need to use Java Native
|
||||||
|
Interface (JNI).</p>
|
||||||
|
<p>
|
||||||
|
The SoundTouch source code package provides source code example how to
|
||||||
|
use JNI to call native c++ routines from a Java class, and provides source codes also for
|
||||||
|
a simple example Android application:<ul>
|
||||||
|
<li><b>ExampleActivity</b>: This is simple Android example application that
|
||||||
|
utilizes SoundTouch native routines for processing WAV audio files. To build the example
|
||||||
|
application, use Eclipse Android SDK environment to import the "ExampleActivity" project in the "Android-lib" folder into the Eclipse workspace.
|
||||||
|
<li><b>Android-lib/jni/soundtouch-jni.cpp</b>: This file contains c/c++ wrapper routines
|
||||||
|
for performing elementary audio file processing with adjusted tempo/pitch/speed parameters
|
||||||
|
from the Android application. The wrapper interface is not complete, but provides example
|
||||||
|
that is easy to extend when necessary. The NDK compiles this file along with the SoundTouch
|
||||||
|
routines into the native binary library.</li>
|
||||||
|
<li><b>Android-lib/src/net/surina/soundtouch/SoundTouch.java</b>: This file implements
|
||||||
|
the Java interface class that loasd & accesses the JNI routines in the natively compiled library.
|
||||||
|
The example Android application uses this class as interface for processing audio files
|
||||||
|
with SoundTouch.</li>
|
||||||
|
<li><b>Android-lib/build.gradle</b>: Top level build script file for Android Studio 3.1.4+</li>
|
||||||
|
</ul>
|
||||||
|
<p>
|
||||||
|
Feel free to examine and extend the provided cpp/java source code example file pair to
|
||||||
|
implement and integrate the desired SoundTouch library capabilities into your own Android application.</p>
|
||||||
|
<hr />
|
||||||
|
<h2>
|
||||||
|
Android floating-point performance considerations</h2>
|
||||||
|
<p>
|
||||||
|
The make process will build dedicated binaries for each supported Android CPU hardware platform type.
|
||||||
|
</p><p>SoundTouch uses floating-point algorithms for ideal sound quality on all other platform than in the lowest-end ARMv5. That is because lowest-end Android devices are not guaranteed to
|
||||||
|
have floating-point hardware in their CPUs, so that the ARMv5 compilation uses by default software-emulation for floating-point calculations to allow running the binary executables also in low-end devices without floating-point hardware.<p>
|
||||||
|
As floating point software-emulation is however several tens of times slower
|
||||||
|
than real hardware-level floating-point calculations, that would make running
|
||||||
|
floating-point-intensive applications such as SoundTouch infeasible in these low-end
|
||||||
|
devices. As workaround, the SoundTouch Android compilation builds the ARMv5 version using integer algorithm versions. The integer
|
||||||
|
algorithm version compromises the sound quality but provides good performance also
|
||||||
|
with low-end devices without hardware floating-point support in the CPU level.</p>
|
||||||
|
<p>When Android devices with more capable device is used, the device will automatically choose a proper library version for ideal sound quality.</p>
|
||||||
|
<hr />
|
||||||
|
<p style="text-align: center"><i>Copyright © Olli Parviainen</i></p>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
55
source/Android-lib/build.gradle
Normal file
55
source/Android-lib/build.gradle
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||||
|
buildscript {
|
||||||
|
repositories {
|
||||||
|
jcenter()
|
||||||
|
google()
|
||||||
|
}
|
||||||
|
dependencies {
|
||||||
|
classpath 'com.android.tools.build:gradle:3.1.4'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
allprojects {
|
||||||
|
repositories {
|
||||||
|
jcenter()
|
||||||
|
google()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
apply plugin: 'com.android.application'
|
||||||
|
|
||||||
|
android {
|
||||||
|
compileSdkVersion 28
|
||||||
|
|
||||||
|
defaultConfig {
|
||||||
|
applicationId "net.surina.soundtouchexample"
|
||||||
|
minSdkVersion 14
|
||||||
|
targetSdkVersion 21
|
||||||
|
|
||||||
|
externalNativeBuild.ndkBuild {
|
||||||
|
arguments "NDK_APPLICATION=jni/Application.mk",
|
||||||
|
"APP_ALLOW_MISSING_DEPS:=true"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sourceSets {
|
||||||
|
main {
|
||||||
|
manifest.srcFile "./AndroidManifest.xml"
|
||||||
|
java.srcDirs = ["./src"]
|
||||||
|
res.srcDirs = ["res"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
externalNativeBuild {
|
||||||
|
ndkBuild {
|
||||||
|
path 'jni/Android.mk'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
buildTypes {
|
||||||
|
release {
|
||||||
|
minifyEnabled false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
source/Android-lib/gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
BIN
source/Android-lib/gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
Binary file not shown.
6
source/Android-lib/gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
6
source/Android-lib/gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
#Sat Jan 13 09:12:34 PST 2018
|
||||||
|
distributionBase=GRADLE_USER_HOME
|
||||||
|
distributionPath=wrapper/dists
|
||||||
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
zipStorePath=wrapper/dists
|
||||||
|
distributionUrl=https\://services.gradle.org/distributions/gradle-4.4.1-all.zip
|
||||||
172
source/Android-lib/gradlew
vendored
Executable file
172
source/Android-lib/gradlew
vendored
Executable file
@ -0,0 +1,172 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
##############################################################################
|
||||||
|
##
|
||||||
|
## Gradle start up script for UN*X
|
||||||
|
##
|
||||||
|
##############################################################################
|
||||||
|
|
||||||
|
# Attempt to set APP_HOME
|
||||||
|
# Resolve links: $0 may be a link
|
||||||
|
PRG="$0"
|
||||||
|
# Need this for relative symlinks.
|
||||||
|
while [ -h "$PRG" ] ; do
|
||||||
|
ls=`ls -ld "$PRG"`
|
||||||
|
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||||
|
if expr "$link" : '/.*' > /dev/null; then
|
||||||
|
PRG="$link"
|
||||||
|
else
|
||||||
|
PRG=`dirname "$PRG"`"/$link"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
SAVED="`pwd`"
|
||||||
|
cd "`dirname \"$PRG\"`/" >/dev/null
|
||||||
|
APP_HOME="`pwd -P`"
|
||||||
|
cd "$SAVED" >/dev/null
|
||||||
|
|
||||||
|
APP_NAME="Gradle"
|
||||||
|
APP_BASE_NAME=`basename "$0"`
|
||||||
|
|
||||||
|
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
|
DEFAULT_JVM_OPTS=""
|
||||||
|
|
||||||
|
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||||
|
MAX_FD="maximum"
|
||||||
|
|
||||||
|
warn () {
|
||||||
|
echo "$*"
|
||||||
|
}
|
||||||
|
|
||||||
|
die () {
|
||||||
|
echo
|
||||||
|
echo "$*"
|
||||||
|
echo
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# OS specific support (must be 'true' or 'false').
|
||||||
|
cygwin=false
|
||||||
|
msys=false
|
||||||
|
darwin=false
|
||||||
|
nonstop=false
|
||||||
|
case "`uname`" in
|
||||||
|
CYGWIN* )
|
||||||
|
cygwin=true
|
||||||
|
;;
|
||||||
|
Darwin* )
|
||||||
|
darwin=true
|
||||||
|
;;
|
||||||
|
MINGW* )
|
||||||
|
msys=true
|
||||||
|
;;
|
||||||
|
NONSTOP* )
|
||||||
|
nonstop=true
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||||
|
|
||||||
|
# Determine the Java command to use to start the JVM.
|
||||||
|
if [ -n "$JAVA_HOME" ] ; then
|
||||||
|
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||||
|
# IBM's JDK on AIX uses strange locations for the executables
|
||||||
|
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||||
|
else
|
||||||
|
JAVACMD="$JAVA_HOME/bin/java"
|
||||||
|
fi
|
||||||
|
if [ ! -x "$JAVACMD" ] ; then
|
||||||
|
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||||
|
|
||||||
|
Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
location of your Java installation."
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
JAVACMD="java"
|
||||||
|
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
|
|
||||||
|
Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
location of your Java installation."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Increase the maximum file descriptors if we can.
|
||||||
|
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
|
||||||
|
MAX_FD_LIMIT=`ulimit -H -n`
|
||||||
|
if [ $? -eq 0 ] ; then
|
||||||
|
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||||
|
MAX_FD="$MAX_FD_LIMIT"
|
||||||
|
fi
|
||||||
|
ulimit -n $MAX_FD
|
||||||
|
if [ $? -ne 0 ] ; then
|
||||||
|
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# For Darwin, add options to specify how the application appears in the dock
|
||||||
|
if $darwin; then
|
||||||
|
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||||
|
fi
|
||||||
|
|
||||||
|
# For Cygwin, switch paths to Windows format before running java
|
||||||
|
if $cygwin ; then
|
||||||
|
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||||
|
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||||
|
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||||
|
|
||||||
|
# We build the pattern for arguments to be converted via cygpath
|
||||||
|
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||||
|
SEP=""
|
||||||
|
for dir in $ROOTDIRSRAW ; do
|
||||||
|
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
||||||
|
SEP="|"
|
||||||
|
done
|
||||||
|
OURCYGPATTERN="(^($ROOTDIRS))"
|
||||||
|
# Add a user-defined pattern to the cygpath arguments
|
||||||
|
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
||||||
|
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
||||||
|
fi
|
||||||
|
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||||
|
i=0
|
||||||
|
for arg in "$@" ; do
|
||||||
|
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
||||||
|
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
||||||
|
|
||||||
|
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
||||||
|
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
||||||
|
else
|
||||||
|
eval `echo args$i`="\"$arg\""
|
||||||
|
fi
|
||||||
|
i=$((i+1))
|
||||||
|
done
|
||||||
|
case $i in
|
||||||
|
(0) set -- ;;
|
||||||
|
(1) set -- "$args0" ;;
|
||||||
|
(2) set -- "$args0" "$args1" ;;
|
||||||
|
(3) set -- "$args0" "$args1" "$args2" ;;
|
||||||
|
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||||
|
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||||
|
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||||
|
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||||
|
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||||
|
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Escape application args
|
||||||
|
save () {
|
||||||
|
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||||
|
echo " "
|
||||||
|
}
|
||||||
|
APP_ARGS=$(save "$@")
|
||||||
|
|
||||||
|
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
||||||
|
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
||||||
|
|
||||||
|
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
|
||||||
|
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
|
||||||
|
cd "$(dirname "$0")"
|
||||||
|
fi
|
||||||
|
|
||||||
|
exec "$JAVACMD" "$@"
|
||||||
84
source/Android-lib/gradlew.bat
vendored
Normal file
84
source/Android-lib/gradlew.bat
vendored
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
@if "%DEBUG%" == "" @echo off
|
||||||
|
@rem ##########################################################################
|
||||||
|
@rem
|
||||||
|
@rem Gradle startup script for Windows
|
||||||
|
@rem
|
||||||
|
@rem ##########################################################################
|
||||||
|
|
||||||
|
@rem Set local scope for the variables with windows NT shell
|
||||||
|
if "%OS%"=="Windows_NT" setlocal
|
||||||
|
|
||||||
|
set DIRNAME=%~dp0
|
||||||
|
if "%DIRNAME%" == "" set DIRNAME=.
|
||||||
|
set APP_BASE_NAME=%~n0
|
||||||
|
set APP_HOME=%DIRNAME%
|
||||||
|
|
||||||
|
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
|
set DEFAULT_JVM_OPTS=
|
||||||
|
|
||||||
|
@rem Find java.exe
|
||||||
|
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||||
|
|
||||||
|
set JAVA_EXE=java.exe
|
||||||
|
%JAVA_EXE% -version >NUL 2>&1
|
||||||
|
if "%ERRORLEVEL%" == "0" goto init
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
|
echo.
|
||||||
|
echo Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
echo location of your Java installation.
|
||||||
|
|
||||||
|
goto fail
|
||||||
|
|
||||||
|
:findJavaFromJavaHome
|
||||||
|
set JAVA_HOME=%JAVA_HOME:"=%
|
||||||
|
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||||
|
|
||||||
|
if exist "%JAVA_EXE%" goto init
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||||
|
echo.
|
||||||
|
echo Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
echo location of your Java installation.
|
||||||
|
|
||||||
|
goto fail
|
||||||
|
|
||||||
|
:init
|
||||||
|
@rem Get command-line arguments, handling Windows variants
|
||||||
|
|
||||||
|
if not "%OS%" == "Windows_NT" goto win9xME_args
|
||||||
|
|
||||||
|
:win9xME_args
|
||||||
|
@rem Slurp the command line arguments.
|
||||||
|
set CMD_LINE_ARGS=
|
||||||
|
set _SKIP=2
|
||||||
|
|
||||||
|
:win9xME_args_slurp
|
||||||
|
if "x%~1" == "x" goto execute
|
||||||
|
|
||||||
|
set CMD_LINE_ARGS=%*
|
||||||
|
|
||||||
|
:execute
|
||||||
|
@rem Setup the command line
|
||||||
|
|
||||||
|
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||||
|
|
||||||
|
@rem Execute Gradle
|
||||||
|
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
|
||||||
|
|
||||||
|
:end
|
||||||
|
@rem End local scope for the variables with windows NT shell
|
||||||
|
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||||
|
|
||||||
|
:fail
|
||||||
|
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||||
|
rem the _cmd.exe /c_ return code!
|
||||||
|
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||||
|
exit /b 1
|
||||||
|
|
||||||
|
:mainEnd
|
||||||
|
if "%OS%"=="Windows_NT" endlocal
|
||||||
|
|
||||||
|
:omega
|
||||||
37
source/Android-lib/jni/Android.mk
Normal file
37
source/Android-lib/jni/Android.mk
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
LOCAL_PATH := $(call my-dir)
|
||||||
|
|
||||||
|
include $(CLEAR_VARS)
|
||||||
|
|
||||||
|
LOCAL_C_INCLUDES += $(LOCAL_PATH)/../../../include $(LOCAL_PATH)/../../SoundStretch
|
||||||
|
# *** Remember: Change -O0 into -O2 in add-applications.mk ***
|
||||||
|
|
||||||
|
LOCAL_MODULE := soundtouch
|
||||||
|
LOCAL_SRC_FILES := soundtouch-jni.cpp ../../SoundTouch/AAFilter.cpp ../../SoundTouch/FIFOSampleBuffer.cpp \
|
||||||
|
../../SoundTouch/FIRFilter.cpp ../../SoundTouch/cpu_detect_x86.cpp \
|
||||||
|
../../SoundTouch/sse_optimized.cpp ../../SoundStretch/WavFile.cpp \
|
||||||
|
../../SoundTouch/RateTransposer.cpp ../../SoundTouch/SoundTouch.cpp \
|
||||||
|
../../SoundTouch/InterpolateCubic.cpp ../../SoundTouch/InterpolateLinear.cpp \
|
||||||
|
../../SoundTouch/InterpolateShannon.cpp ../../SoundTouch/TDStretch.cpp \
|
||||||
|
../../SoundTouch/BPMDetect.cpp ../../SoundTouch/PeakFinder.cpp
|
||||||
|
|
||||||
|
# for native audio
|
||||||
|
LOCAL_SHARED_LIBRARIES += -lgcc
|
||||||
|
# --whole-archive -lgcc
|
||||||
|
# for logging
|
||||||
|
LOCAL_LDLIBS += -llog
|
||||||
|
# for native asset manager
|
||||||
|
#LOCAL_LDLIBS += -landroid
|
||||||
|
|
||||||
|
# Custom Flags:
|
||||||
|
# -fvisibility=hidden : don't export all symbols
|
||||||
|
LOCAL_CFLAGS += -fvisibility=hidden -fdata-sections -ffunction-sections -ffast-math
|
||||||
|
|
||||||
|
# OpenMP mode : enable these flags to enable using OpenMP for parallel computation
|
||||||
|
#LOCAL_CFLAGS += -fopenmp
|
||||||
|
#LOCAL_LDFLAGS += -fopenmp
|
||||||
|
|
||||||
|
|
||||||
|
# Use ARM instruction set instead of Thumb for improved calculation performance in ARM CPUs
|
||||||
|
LOCAL_ARM_MODE := arm
|
||||||
|
|
||||||
|
include $(BUILD_SHARED_LIBRARY)
|
||||||
9
source/Android-lib/jni/Application.mk
Normal file
9
source/Android-lib/jni/Application.mk
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
#
|
||||||
|
# Build library bilaries for all supported architectures
|
||||||
|
#
|
||||||
|
|
||||||
|
APP_ABI := all #armeabi-v7a armeabi
|
||||||
|
APP_OPTIM := release
|
||||||
|
APP_STL := c++_static
|
||||||
|
APP_CPPFLAGS := -fexceptions # -D SOUNDTOUCH_DISABLE_X86_OPTIMIZATIONS
|
||||||
|
|
||||||
258
source/Android-lib/jni/soundtouch-jni.cpp
Normal file
258
source/Android-lib/jni/soundtouch-jni.cpp
Normal file
@ -0,0 +1,258 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
///
|
||||||
|
/// Example Interface class for SoundTouch native compilation
|
||||||
|
///
|
||||||
|
/// Author : Copyright (c) Olli Parviainen
|
||||||
|
/// Author e-mail : oparviai 'at' iki.fi
|
||||||
|
/// WWW : http://www.surina.net
|
||||||
|
///
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#include <jni.h>
|
||||||
|
#include <android/log.h>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
#include "../../../include/SoundTouch.h"
|
||||||
|
#include "../source/SoundStretch/WavFile.h"
|
||||||
|
|
||||||
|
#define LOGV(...) __android_log_print((int)ANDROID_LOG_INFO, "SOUNDTOUCH", __VA_ARGS__)
|
||||||
|
//#define LOGV(...)
|
||||||
|
|
||||||
|
|
||||||
|
// String for keeping possible c++ exception error messages. Notice that this isn't
|
||||||
|
// thread-safe but it's expected that exceptions are special situations that won't
|
||||||
|
// occur in several threads in parallel.
|
||||||
|
static string _errMsg = "";
|
||||||
|
|
||||||
|
|
||||||
|
#define DLL_PUBLIC __attribute__ ((visibility ("default")))
|
||||||
|
#define BUFF_SIZE 4096
|
||||||
|
|
||||||
|
|
||||||
|
using namespace soundtouch;
|
||||||
|
|
||||||
|
|
||||||
|
// Set error message to return
|
||||||
|
static void _setErrmsg(const char *msg)
|
||||||
|
{
|
||||||
|
_errMsg = msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if 0 // apparently following workaround not needed any more with concurrent Android SDKs
|
||||||
|
#ifdef _OPENMP
|
||||||
|
|
||||||
|
#include <pthread.h>
|
||||||
|
extern pthread_key_t gomp_tls_key;
|
||||||
|
static void * _p_gomp_tls = nullptr;
|
||||||
|
|
||||||
|
/// Function to initialize threading for OpenMP.
|
||||||
|
///
|
||||||
|
/// This is a workaround for bug in Android NDK v10 regarding OpenMP: OpenMP works only if
|
||||||
|
/// called from the Android App main thread because in the main thread the gomp_tls storage is
|
||||||
|
/// properly set, however, Android does not properly initialize gomp_tls storage for other threads.
|
||||||
|
/// Thus if OpenMP routines are invoked from some other thread than the main thread,
|
||||||
|
/// the OpenMP routine will crash the application due to nullptr access on uninitialized storage.
|
||||||
|
///
|
||||||
|
/// This workaround stores the gomp_tls storage from main thread, and copies to other threads.
|
||||||
|
/// In order this to work, the Application main thread needws to call at least "getVersionString"
|
||||||
|
/// routine.
|
||||||
|
static int _init_threading(bool warn)
|
||||||
|
{
|
||||||
|
void *ptr = pthread_getspecific(gomp_tls_key);
|
||||||
|
LOGV("JNI thread-specific TLS storage %ld", (long)ptr);
|
||||||
|
if (ptr == nullptr)
|
||||||
|
{
|
||||||
|
LOGV("JNI set missing TLS storage to %ld", (long)_p_gomp_tls);
|
||||||
|
pthread_setspecific(gomp_tls_key, _p_gomp_tls);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LOGV("JNI store this TLS storage");
|
||||||
|
_p_gomp_tls = ptr;
|
||||||
|
}
|
||||||
|
// Where critical, show warning if storage still not properly initialized
|
||||||
|
if ((warn) && (_p_gomp_tls == nullptr))
|
||||||
|
{
|
||||||
|
_setErrmsg("Error - OpenMP threading not properly initialized: Call SoundTouch.getVersionString() from the App main thread!");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
static int _init_threading(bool warn)
|
||||||
|
{
|
||||||
|
// do nothing if not OpenMP build
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Processes the sound file
|
||||||
|
static void _processFile(SoundTouch *pSoundTouch, const char *inFileName, const char *outFileName)
|
||||||
|
{
|
||||||
|
int nSamples;
|
||||||
|
int nChannels;
|
||||||
|
int buffSizeSamples;
|
||||||
|
SAMPLETYPE sampleBuffer[BUFF_SIZE];
|
||||||
|
|
||||||
|
// open input file
|
||||||
|
WavInFile inFile(inFileName);
|
||||||
|
int sampleRate = inFile.getSampleRate();
|
||||||
|
int bits = inFile.getNumBits();
|
||||||
|
nChannels = inFile.getNumChannels();
|
||||||
|
|
||||||
|
// create output file
|
||||||
|
WavOutFile outFile(outFileName, sampleRate, bits, nChannels);
|
||||||
|
|
||||||
|
pSoundTouch->setSampleRate(sampleRate);
|
||||||
|
pSoundTouch->setChannels(nChannels);
|
||||||
|
|
||||||
|
assert(nChannels > 0);
|
||||||
|
buffSizeSamples = BUFF_SIZE / nChannels;
|
||||||
|
|
||||||
|
// Process samples read from the input file
|
||||||
|
while (inFile.eof() == 0)
|
||||||
|
{
|
||||||
|
int num;
|
||||||
|
|
||||||
|
// Read a chunk of samples from the input file
|
||||||
|
num = inFile.read(sampleBuffer, BUFF_SIZE);
|
||||||
|
nSamples = num / nChannels;
|
||||||
|
|
||||||
|
// Feed the samples into SoundTouch processor
|
||||||
|
pSoundTouch->putSamples(sampleBuffer, nSamples);
|
||||||
|
|
||||||
|
// Read ready samples from SoundTouch processor & write them output file.
|
||||||
|
// NOTES:
|
||||||
|
// - 'receiveSamples' doesn't necessarily return any samples at all
|
||||||
|
// during some rounds!
|
||||||
|
// - On the other hand, during some round 'receiveSamples' may have more
|
||||||
|
// ready samples than would fit into 'sampleBuffer', and for this reason
|
||||||
|
// the 'receiveSamples' call is iterated for as many times as it
|
||||||
|
// outputs samples.
|
||||||
|
do
|
||||||
|
{
|
||||||
|
nSamples = pSoundTouch->receiveSamples(sampleBuffer, buffSizeSamples);
|
||||||
|
outFile.write(sampleBuffer, nSamples * nChannels);
|
||||||
|
} while (nSamples != 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now the input file is processed, yet 'flush' few last samples that are
|
||||||
|
// hiding in the SoundTouch's internal processing pipeline.
|
||||||
|
pSoundTouch->flush();
|
||||||
|
do
|
||||||
|
{
|
||||||
|
nSamples = pSoundTouch->receiveSamples(sampleBuffer, buffSizeSamples);
|
||||||
|
outFile.write(sampleBuffer, nSamples * nChannels);
|
||||||
|
} while (nSamples != 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
extern "C" DLL_PUBLIC jstring Java_net_surina_soundtouch_SoundTouch_getVersionString(JNIEnv *env, jobject thiz)
|
||||||
|
{
|
||||||
|
const char *verStr;
|
||||||
|
|
||||||
|
LOGV("JNI call SoundTouch.getVersionString");
|
||||||
|
|
||||||
|
// Call example SoundTouch routine
|
||||||
|
verStr = SoundTouch::getVersionString();
|
||||||
|
|
||||||
|
// gomp_tls storage bug workaround - see comments in _init_threading() function!
|
||||||
|
// update: apparently this is not needed any more with concurrent Android SDKs
|
||||||
|
// _init_threading(false);
|
||||||
|
|
||||||
|
int threads = 0;
|
||||||
|
#pragma omp parallel
|
||||||
|
{
|
||||||
|
#pragma omp atomic
|
||||||
|
threads ++;
|
||||||
|
}
|
||||||
|
LOGV("JNI thread count %d", threads);
|
||||||
|
|
||||||
|
// return version as string
|
||||||
|
return env->NewStringUTF(verStr);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
extern "C" DLL_PUBLIC jlong Java_net_surina_soundtouch_SoundTouch_newInstance(JNIEnv *env, jobject thiz)
|
||||||
|
{
|
||||||
|
return (jlong)(new SoundTouch());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
extern "C" DLL_PUBLIC void Java_net_surina_soundtouch_SoundTouch_deleteInstance(JNIEnv *env, jobject thiz, jlong handle)
|
||||||
|
{
|
||||||
|
SoundTouch *ptr = (SoundTouch*)handle;
|
||||||
|
delete ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
extern "C" DLL_PUBLIC void Java_net_surina_soundtouch_SoundTouch_setTempo(JNIEnv *env, jobject thiz, jlong handle, jfloat tempo)
|
||||||
|
{
|
||||||
|
SoundTouch *ptr = (SoundTouch*)handle;
|
||||||
|
ptr->setTempo(tempo);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
extern "C" DLL_PUBLIC void Java_net_surina_soundtouch_SoundTouch_setPitchSemiTones(JNIEnv *env, jobject thiz, jlong handle, jfloat pitch)
|
||||||
|
{
|
||||||
|
SoundTouch *ptr = (SoundTouch*)handle;
|
||||||
|
ptr->setPitchSemiTones(pitch);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
extern "C" DLL_PUBLIC void Java_net_surina_soundtouch_SoundTouch_setSpeed(JNIEnv *env, jobject thiz, jlong handle, jfloat speed)
|
||||||
|
{
|
||||||
|
SoundTouch *ptr = (SoundTouch*)handle;
|
||||||
|
ptr->setRate(speed);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
extern "C" DLL_PUBLIC jstring Java_net_surina_soundtouch_SoundTouch_getErrorString(JNIEnv *env, jobject thiz)
|
||||||
|
{
|
||||||
|
jstring result = env->NewStringUTF(_errMsg.c_str());
|
||||||
|
_errMsg.clear();
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
extern "C" DLL_PUBLIC int Java_net_surina_soundtouch_SoundTouch_processFile(JNIEnv *env, jobject thiz, jlong handle, jstring jinputFile, jstring joutputFile)
|
||||||
|
{
|
||||||
|
SoundTouch *ptr = (SoundTouch*)handle;
|
||||||
|
|
||||||
|
const char *inputFile = env->GetStringUTFChars(jinputFile, 0);
|
||||||
|
const char *outputFile = env->GetStringUTFChars(joutputFile, 0);
|
||||||
|
|
||||||
|
LOGV("JNI process file %s", inputFile);
|
||||||
|
|
||||||
|
/// gomp_tls storage bug workaround - see comments in _init_threading() function!
|
||||||
|
// update: apparently this is not needed any more with concurrent Android SDKs
|
||||||
|
// if (_init_threading(true)) return -1;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_processFile(ptr, inputFile, outputFile);
|
||||||
|
}
|
||||||
|
catch (const runtime_error &e)
|
||||||
|
{
|
||||||
|
const char *err = e.what();
|
||||||
|
// An exception occurred during processing, return the error message
|
||||||
|
LOGV("JNI exception in SoundTouch::processFile: %s", err);
|
||||||
|
_setErrmsg(err);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
env->ReleaseStringUTFChars(jinputFile, inputFile);
|
||||||
|
env->ReleaseStringUTFChars(joutputFile, outputFile);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
3
source/Android-lib/lint.xml
Normal file
3
source/Android-lib/lint.xml
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<lint>
|
||||||
|
</lint>
|
||||||
20
source/Android-lib/proguard-project.txt
Normal file
20
source/Android-lib/proguard-project.txt
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
# To enable ProGuard in your project, edit project.properties
|
||||||
|
# to define the proguard.config property as described in that file.
|
||||||
|
#
|
||||||
|
# Add project specific ProGuard rules here.
|
||||||
|
# By default, the flags in this file are appended to flags specified
|
||||||
|
# in ${sdk.dir}/tools/proguard/proguard-android.txt
|
||||||
|
# You can edit the include path and order by changing the ProGuard
|
||||||
|
# include property in project.properties.
|
||||||
|
#
|
||||||
|
# For more details, see
|
||||||
|
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||||
|
|
||||||
|
# Add any project specific keep options here:
|
||||||
|
|
||||||
|
# If your project uses WebView with JS, uncomment the following
|
||||||
|
# and specify the fully qualified class name to the JavaScript interface
|
||||||
|
# class:
|
||||||
|
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||||
|
# public *;
|
||||||
|
#}
|
||||||
14
source/Android-lib/project.properties
Normal file
14
source/Android-lib/project.properties
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
# This file is automatically generated by Android Tools.
|
||||||
|
# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
|
||||||
|
#
|
||||||
|
# This file must be checked in Version Control Systems.
|
||||||
|
#
|
||||||
|
# To customize properties used by the Ant build system edit
|
||||||
|
# "ant.properties", and override values to adapt the script to your
|
||||||
|
# project structure.
|
||||||
|
#
|
||||||
|
# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
|
||||||
|
#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
|
||||||
|
|
||||||
|
# Project target.
|
||||||
|
target=android-19
|
||||||
BIN
source/Android-lib/res/drawable-hdpi/ic_launcher.png
Normal file
BIN
source/Android-lib/res/drawable-hdpi/ic_launcher.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 7.5 KiB |
BIN
source/Android-lib/res/drawable-mdpi/ic_launcher.png
Normal file
BIN
source/Android-lib/res/drawable-mdpi/ic_launcher.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.7 KiB |
BIN
source/Android-lib/res/drawable-xhdpi/ic_launcher.png
Normal file
BIN
source/Android-lib/res/drawable-xhdpi/ic_launcher.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 12 KiB |
BIN
source/Android-lib/res/drawable-xxhdpi/ic_launcher.png
Normal file
BIN
source/Android-lib/res/drawable-xxhdpi/ic_launcher.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 24 KiB |
140
source/Android-lib/res/layout/activity_example.xml
Normal file
140
source/Android-lib/res/layout/activity_example.xml
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:id="@+id/linearLayout1"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="fill_parent"
|
||||||
|
android:orientation="vertical" >
|
||||||
|
|
||||||
|
<TableLayout
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content" >
|
||||||
|
|
||||||
|
<TableRow
|
||||||
|
android:id="@+id/tableRow1"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content" >
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Tempo %:"
|
||||||
|
android:textAppearance="?android:attr/textAppearanceMedium" />
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/editTextTempo"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:ems="5"
|
||||||
|
android:inputType="text"
|
||||||
|
android:text="100" >
|
||||||
|
</EditText>
|
||||||
|
</TableRow>
|
||||||
|
|
||||||
|
<TableRow
|
||||||
|
android:id="@+id/tableRow2"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content" >
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Pitch half-steps:"
|
||||||
|
android:textAppearance="?android:attr/textAppearanceMedium" />
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/editTextPitch"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:ems="5"
|
||||||
|
android:inputType="text"
|
||||||
|
android:text="-0.318" >
|
||||||
|
</EditText>
|
||||||
|
</TableRow>
|
||||||
|
</TableLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="10dp" >
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Source file:"
|
||||||
|
android:textAppearance="?android:attr/textAppearanceMedium" />
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/editTextSrcFileName"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="/sdcard/Download/test.wav"
|
||||||
|
android:layout_weight="1" />
|
||||||
|
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/buttonSelectSrcFile"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Select" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content" >
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Output file:"
|
||||||
|
android:textAppearance="?android:attr/textAppearanceMedium" />
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/editTextOutFileName"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="/sdcard/Download/soundtouch-output.wav"
|
||||||
|
android:layout_weight="1" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/buttonSelectOutFile"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Select" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<CheckBox
|
||||||
|
android:id="@+id/checkBoxPlay"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="10dp"
|
||||||
|
android:text="Play the output file after processing!" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/buttonProcess"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:text="Process file!" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="10dp"
|
||||||
|
android:text="Status console:"
|
||||||
|
android:textAppearance="?android:attr/textAppearanceMedium" />
|
||||||
|
|
||||||
|
<ScrollView
|
||||||
|
android:id="@+id/scrollView1"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content" >
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/textViewResult"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="fill_parent"
|
||||||
|
android:text="@string/hello_world" />
|
||||||
|
|
||||||
|
|
||||||
|
</ScrollView>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
7
source/Android-lib/res/values/strings.xml
Normal file
7
source/Android-lib/res/values/strings.xml
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
|
||||||
|
<string name="app_name">SoundTouch Example</string>
|
||||||
|
<string name="hello_world">Hello world!</string>
|
||||||
|
|
||||||
|
</resources>
|
||||||
20
source/Android-lib/res/values/styles.xml
Normal file
20
source/Android-lib/res/values/styles.xml
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
<resources>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Base application theme, dependent on API level. This theme is replaced
|
||||||
|
by AppBaseTheme from res/values-vXX/styles.xml on newer devices.
|
||||||
|
-->
|
||||||
|
<style name="AppBaseTheme" parent="android:Theme.Holo.Light">
|
||||||
|
<!--
|
||||||
|
Theme customizations available in newer API levels can go in
|
||||||
|
res/values-vXX/styles.xml, while customizations related to
|
||||||
|
backward-compatibility can go here.
|
||||||
|
-->
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<!-- Application theme. -->
|
||||||
|
<style name="AppTheme" parent="AppBaseTheme">
|
||||||
|
<!-- All customizations that are NOT specific to a particular API-level can go here. -->
|
||||||
|
</style>
|
||||||
|
|
||||||
|
</resources>
|
||||||
219
source/Android-lib/src/net/surina/ExampleActivity.java
Normal file
219
source/Android-lib/src/net/surina/ExampleActivity.java
Normal file
@ -0,0 +1,219 @@
|
|||||||
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
///
|
||||||
|
/// Example Android Application/Activity that allows processing WAV
|
||||||
|
/// audio files with SoundTouch library
|
||||||
|
///
|
||||||
|
/// Copyright (c) Olli Parviainen
|
||||||
|
///
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
package net.surina;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
|
import net.surina.soundtouch.SoundTouch;
|
||||||
|
import net.surina.soundtouchexample.R;
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.AsyncTask;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.View.OnClickListener;
|
||||||
|
import android.widget.Button;
|
||||||
|
import android.widget.CheckBox;
|
||||||
|
import android.widget.EditText;
|
||||||
|
import android.widget.TextView;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
public class ExampleActivity extends Activity implements OnClickListener
|
||||||
|
{
|
||||||
|
TextView textViewConsole = null;
|
||||||
|
EditText editSourceFile = null;
|
||||||
|
EditText editOutputFile = null;
|
||||||
|
EditText editTempo = null;
|
||||||
|
EditText editPitch = null;
|
||||||
|
CheckBox checkBoxPlay = null;
|
||||||
|
|
||||||
|
StringBuilder consoleText = new StringBuilder();
|
||||||
|
|
||||||
|
|
||||||
|
/// Called when the activity is created
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState)
|
||||||
|
{
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
setContentView(R.layout.activity_example);
|
||||||
|
|
||||||
|
textViewConsole = (TextView)findViewById(R.id.textViewResult);
|
||||||
|
editSourceFile = (EditText)findViewById(R.id.editTextSrcFileName);
|
||||||
|
editOutputFile = (EditText)findViewById(R.id.editTextOutFileName);
|
||||||
|
|
||||||
|
editTempo = (EditText)findViewById(R.id.editTextTempo);
|
||||||
|
editPitch = (EditText)findViewById(R.id.editTextPitch);
|
||||||
|
|
||||||
|
Button buttonFileSrc = (Button)findViewById(R.id.buttonSelectSrcFile);
|
||||||
|
Button buttonFileOutput = (Button)findViewById(R.id.buttonSelectOutFile);
|
||||||
|
Button buttonProcess = (Button)findViewById(R.id.buttonProcess);
|
||||||
|
buttonFileSrc.setOnClickListener(this);
|
||||||
|
buttonFileOutput.setOnClickListener(this);
|
||||||
|
buttonProcess.setOnClickListener(this);
|
||||||
|
|
||||||
|
checkBoxPlay = (CheckBox)findViewById(R.id.checkBoxPlay);
|
||||||
|
|
||||||
|
// Check soundtouch library presence & version
|
||||||
|
checkLibVersion();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/// Function to append status text onto "console box" on the Activity
|
||||||
|
public void appendToConsole(final String text)
|
||||||
|
{
|
||||||
|
// run on UI thread to avoid conflicts
|
||||||
|
runOnUiThread(new Runnable()
|
||||||
|
{
|
||||||
|
public void run()
|
||||||
|
{
|
||||||
|
consoleText.append(text);
|
||||||
|
consoleText.append("\n");
|
||||||
|
textViewConsole.setText(consoleText);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/// print SoundTouch native library version onto console
|
||||||
|
protected void checkLibVersion()
|
||||||
|
{
|
||||||
|
String ver = SoundTouch.getVersionString();
|
||||||
|
appendToConsole("SoundTouch native library version = " + ver);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/// Button click handler
|
||||||
|
@Override
|
||||||
|
public void onClick(View arg0)
|
||||||
|
{
|
||||||
|
switch (arg0.getId())
|
||||||
|
{
|
||||||
|
case R.id.buttonSelectSrcFile:
|
||||||
|
case R.id.buttonSelectOutFile:
|
||||||
|
// one of the file select buttons clicked ... we've not just implemented them ;-)
|
||||||
|
Toast.makeText(this, "File selector not implemented, sorry! Enter the file path manually ;-)", Toast.LENGTH_LONG).show();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case R.id.buttonProcess:
|
||||||
|
// button "process" pushed
|
||||||
|
process();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Play audio file
|
||||||
|
protected void playWavFile(String fileName)
|
||||||
|
{
|
||||||
|
File file2play = new File(fileName);
|
||||||
|
Intent i = new Intent();
|
||||||
|
i.setAction(android.content.Intent.ACTION_VIEW);
|
||||||
|
i.setDataAndType(Uri.fromFile(file2play), "audio/wav");
|
||||||
|
startActivity(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/// Helper class that will execute the SoundTouch processing. As the processing may take
|
||||||
|
/// some time, run it in background thread to avoid hanging of the UI.
|
||||||
|
protected class ProcessTask extends AsyncTask<ProcessTask.Parameters, Integer, Long>
|
||||||
|
{
|
||||||
|
/// Helper class to store the SoundTouch file processing parameters
|
||||||
|
public final class Parameters
|
||||||
|
{
|
||||||
|
String inFileName;
|
||||||
|
String outFileName;
|
||||||
|
float tempo;
|
||||||
|
float pitch;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/// Function that does the SoundTouch processing
|
||||||
|
public final long doSoundTouchProcessing(Parameters params)
|
||||||
|
{
|
||||||
|
|
||||||
|
SoundTouch st = new SoundTouch();
|
||||||
|
st.setTempo(params.tempo);
|
||||||
|
st.setPitchSemiTones(params.pitch);
|
||||||
|
Log.i("SoundTouch", "process file " + params.inFileName);
|
||||||
|
long startTime = System.currentTimeMillis();
|
||||||
|
int res = st.processFile(params.inFileName, params.outFileName);
|
||||||
|
long endTime = System.currentTimeMillis();
|
||||||
|
float duration = (endTime - startTime) * 0.001f;
|
||||||
|
|
||||||
|
Log.i("SoundTouch", "process file done, duration = " + duration);
|
||||||
|
appendToConsole("Processing done, duration " + duration + " sec.");
|
||||||
|
if (res != 0)
|
||||||
|
{
|
||||||
|
String err = SoundTouch.getErrorString();
|
||||||
|
appendToConsole("Failure: " + err);
|
||||||
|
return -1L;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Play file if so is desirable
|
||||||
|
if (checkBoxPlay.isChecked())
|
||||||
|
{
|
||||||
|
playWavFile(params.outFileName);
|
||||||
|
}
|
||||||
|
return 0L;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/// Overloaded function that get called by the system to perform the background processing
|
||||||
|
@Override
|
||||||
|
protected Long doInBackground(Parameters... aparams)
|
||||||
|
{
|
||||||
|
return doSoundTouchProcessing(aparams[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// process a file with SoundTouch. Do the processing using a background processing
|
||||||
|
/// task to avoid hanging of the UI
|
||||||
|
protected void process()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ProcessTask task = new ProcessTask();
|
||||||
|
ProcessTask.Parameters params = task.new Parameters();
|
||||||
|
// parse processing parameters
|
||||||
|
params.inFileName = editSourceFile.getText().toString();
|
||||||
|
params.outFileName = editOutputFile.getText().toString();
|
||||||
|
params.tempo = 0.01f * Float.parseFloat(editTempo.getText().toString());
|
||||||
|
params.pitch = Float.parseFloat(editPitch.getText().toString());
|
||||||
|
|
||||||
|
// update UI about status
|
||||||
|
appendToConsole("Process audio file :" + params.inFileName +" => " + params.outFileName);
|
||||||
|
appendToConsole("Tempo = " + params.tempo);
|
||||||
|
appendToConsole("Pitch adjust = " + params.pitch);
|
||||||
|
|
||||||
|
Toast.makeText(this, "Starting to process file " + params.inFileName + "...", Toast.LENGTH_SHORT).show();
|
||||||
|
|
||||||
|
// start SoundTouch processing in a background thread
|
||||||
|
task.execute(params);
|
||||||
|
// task.doSoundTouchProcessing(params); // this would run processing in main thread
|
||||||
|
|
||||||
|
}
|
||||||
|
catch (Exception exp)
|
||||||
|
{
|
||||||
|
exp.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
79
source/Android-lib/src/net/surina/soundtouch/SoundTouch.java
Normal file
79
source/Android-lib/src/net/surina/soundtouch/SoundTouch.java
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
///
|
||||||
|
/// Example class that invokes native SoundTouch routines through the JNI
|
||||||
|
/// interface.
|
||||||
|
///
|
||||||
|
/// Author : Copyright (c) Olli Parviainen
|
||||||
|
/// Author e-mail : oparviai 'at' iki.fi
|
||||||
|
/// WWW : http://www.surina.net
|
||||||
|
///
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
package net.surina.soundtouch;
|
||||||
|
|
||||||
|
public final class SoundTouch
|
||||||
|
{
|
||||||
|
// Native interface function that returns SoundTouch version string.
|
||||||
|
// This invokes the native c++ routine defined in "soundtouch-jni.cpp".
|
||||||
|
public native final static String getVersionString();
|
||||||
|
|
||||||
|
private native final void setTempo(long handle, float tempo);
|
||||||
|
|
||||||
|
private native final void setPitchSemiTones(long handle, float pitch);
|
||||||
|
|
||||||
|
private native final void setSpeed(long handle, float speed);
|
||||||
|
|
||||||
|
private native final int processFile(long handle, String inputFile, String outputFile);
|
||||||
|
|
||||||
|
public native final static String getErrorString();
|
||||||
|
|
||||||
|
private native final static long newInstance();
|
||||||
|
|
||||||
|
private native final void deleteInstance(long handle);
|
||||||
|
|
||||||
|
long handle = 0;
|
||||||
|
|
||||||
|
|
||||||
|
public SoundTouch()
|
||||||
|
{
|
||||||
|
handle = newInstance();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void close()
|
||||||
|
{
|
||||||
|
deleteInstance(handle);
|
||||||
|
handle = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void setTempo(float tempo)
|
||||||
|
{
|
||||||
|
setTempo(handle, tempo);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void setPitchSemiTones(float pitch)
|
||||||
|
{
|
||||||
|
setPitchSemiTones(handle, pitch);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void setSpeed(float speed)
|
||||||
|
{
|
||||||
|
setSpeed(handle, speed);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public int processFile(String inputFile, String outputFile)
|
||||||
|
{
|
||||||
|
return processFile(handle, inputFile, outputFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Load the native library upon startup
|
||||||
|
static
|
||||||
|
{
|
||||||
|
System.loadLibrary("soundtouch");
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,9 +1,5 @@
|
|||||||
## Process this file with automake to create Makefile.in
|
## Process this file with automake to create Makefile.in
|
||||||
##
|
##
|
||||||
## $Id$
|
|
||||||
##
|
|
||||||
## Copyright (C) 2003 - David W. Durham
|
|
||||||
##
|
|
||||||
## This file is part of SoundTouch, an audio processing library for pitch/time adjustments
|
## This file is part of SoundTouch, an audio processing library for pitch/time adjustments
|
||||||
##
|
##
|
||||||
## SoundTouch is free software; you can redistribute it and/or modify it under the
|
## SoundTouch is free software; you can redistribute it and/or modify it under the
|
||||||
@ -21,8 +17,9 @@
|
|||||||
|
|
||||||
include $(top_srcdir)/config/am_include.mk
|
include $(top_srcdir)/config/am_include.mk
|
||||||
|
|
||||||
|
if SOUNDTOUCH_FLOAT_SAMPLES
|
||||||
|
# build SoundTouchDLL only if float samples used
|
||||||
|
SUBDIRS=SoundTouch SoundStretch SoundTouchDLL
|
||||||
|
else
|
||||||
SUBDIRS=SoundTouch SoundStretch
|
SUBDIRS=SoundTouch SoundStretch
|
||||||
|
endif
|
||||||
# set to something if you want other stuff to be included in the distribution tarball
|
|
||||||
#EXTRA_DIST=
|
|
||||||
|
|
||||||
|
|||||||
@ -1,9 +1,5 @@
|
|||||||
## Process this file with automake to create Makefile.in
|
## Process this file with automake to create Makefile.in
|
||||||
##
|
##
|
||||||
## $Id$
|
|
||||||
##
|
|
||||||
## Copyright (C) 2003 - David W. Durham
|
|
||||||
##
|
|
||||||
## This file is part of SoundTouch, an audio processing library for pitch/time adjustments
|
## This file is part of SoundTouch, an audio processing library for pitch/time adjustments
|
||||||
##
|
##
|
||||||
## SoundTouch is free software; you can redistribute it and/or modify it under the
|
## SoundTouch is free software; you can redistribute it and/or modify it under the
|
||||||
@ -30,8 +26,8 @@ bin_PROGRAMS=soundstretch
|
|||||||
|
|
||||||
noinst_HEADERS=RunParameters.h WavFile.h
|
noinst_HEADERS=RunParameters.h WavFile.h
|
||||||
|
|
||||||
# extra files to include in distrubution tarball
|
# extra files to include in distribution tarball
|
||||||
EXTRA_DIST=soundstretch.dsp soundstretch.dsw soundstretch.sln soundstretch.vcproj
|
EXTRA_DIST=soundstretch.sln soundstretch.vcxproj
|
||||||
|
|
||||||
## for every name listed under bin_PROGRAMS, you have a <prog>_SOURCES. This lists
|
## for every name listed under bin_PROGRAMS, you have a <prog>_SOURCES. This lists
|
||||||
## all the sources in the current directory that are used to build soundstretch.
|
## all the sources in the current directory that are used to build soundstretch.
|
||||||
@ -44,11 +40,11 @@ soundstretch_SOURCES=main.cpp RunParameters.cpp WavFile.cpp
|
|||||||
soundstretch_LDADD=../SoundTouch/libSoundTouch.la -lm
|
soundstretch_LDADD=../SoundTouch/libSoundTouch.la -lm
|
||||||
|
|
||||||
## linker flags.
|
## linker flags.
|
||||||
# OP 2011-7-17 Linker flags disabled to prevent stripping symbols by default
|
# Linker flag -s disabled to prevent stripping symbols by default
|
||||||
#soundstretch_LDFLAGS=-s
|
#soundstretch_LDFLAGS=-s
|
||||||
|
|
||||||
## additional compiler flags
|
## additional compiler flags
|
||||||
soundstretch_CXXFLAGS=-O3
|
soundstretch_CXXFLAGS=$(AM_CXXFLAGS)
|
||||||
|
|
||||||
#clean-local:
|
#clean-local:
|
||||||
# -rm -f additional-files-to-remove-on-make-clean
|
# -rm -f additional-files-to-remove-on-make-clean
|
||||||
|
|||||||
@ -8,13 +8,6 @@
|
|||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Last changed : $Date$
|
|
||||||
// File revision : $Revision: 4 $
|
|
||||||
//
|
|
||||||
// $Id$
|
|
||||||
//
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// License :
|
// License :
|
||||||
//
|
//
|
||||||
// SoundTouch audio processing library
|
// SoundTouch audio processing library
|
||||||
@ -37,12 +30,15 @@
|
|||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <stdlib.h>
|
#include <cstdlib>
|
||||||
|
|
||||||
#include "RunParameters.h"
|
#include "RunParameters.h"
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
|
namespace soundstretch
|
||||||
|
{
|
||||||
|
|
||||||
// Program usage instructions
|
// Program usage instructions
|
||||||
|
|
||||||
static const char licenseText[] =
|
static const char licenseText[] =
|
||||||
@ -101,9 +97,8 @@ static int _toLowerCase(int c)
|
|||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Constructor
|
// Constructor
|
||||||
RunParameters::RunParameters(const int nParams, const char * const paramStr[])
|
RunParameters::RunParameters(int nParams, const CHARTYPE* paramStr[])
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
int nFirstParam;
|
int nFirstParam;
|
||||||
@ -119,28 +114,17 @@ RunParameters::RunParameters(const int nParams, const char * const paramStr[])
|
|||||||
}
|
}
|
||||||
string msg = whatText;
|
string msg = whatText;
|
||||||
msg += usage;
|
msg += usage;
|
||||||
ST_THROW_RT_ERROR(msg.c_str());
|
throw(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
inFileName = NULL;
|
|
||||||
outFileName = NULL;
|
|
||||||
tempoDelta = 0;
|
|
||||||
pitchDelta = 0;
|
|
||||||
rateDelta = 0;
|
|
||||||
quick = 0;
|
|
||||||
noAntiAlias = 0;
|
|
||||||
goalBPM = 0;
|
|
||||||
speech = FALSE;
|
|
||||||
detectBPM = FALSE;
|
|
||||||
|
|
||||||
// Get input & output file names
|
// Get input & output file names
|
||||||
inFileName = (char*)paramStr[1];
|
inFileName = paramStr[1];
|
||||||
outFileName = (char*)paramStr[2];
|
outFileName = paramStr[2];
|
||||||
|
|
||||||
if (outFileName[0] == '-')
|
if (outFileName[0] == '-')
|
||||||
{
|
{
|
||||||
// no outputfile name was given but parameters
|
// outputfile name was omitted but other parameter switches given instead
|
||||||
outFileName = NULL;
|
outFileName = STRING_CONST("");
|
||||||
nFirstParam = 2;
|
nFirstParam = 2;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -158,7 +142,6 @@ RunParameters::RunParameters(const int nParams, const char * const paramStr[])
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Checks parameter limits
|
// Checks parameter limits
|
||||||
void RunParameters::checkLimits()
|
void RunParameters::checkLimits()
|
||||||
{
|
{
|
||||||
@ -190,27 +173,33 @@ void RunParameters::checkLimits()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Convert STRING to std::string. Actually needed only if STRING is std::wstring, but conversion penalty is negligible
|
||||||
|
std::string convertString(const STRING& str)
|
||||||
// Unknown switch parameter -- throws an exception with an error message
|
|
||||||
void RunParameters::throwIllegalParamExp(const string &str) const
|
|
||||||
{
|
{
|
||||||
string msg = "ERROR : Illegal parameter \"";
|
std::string res;
|
||||||
msg += str;
|
for (auto c : str)
|
||||||
msg += "\".\n\n";
|
{
|
||||||
msg += usage;
|
res += (char)c;
|
||||||
ST_THROW_RT_ERROR(msg.c_str());
|
}
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Unknown switch parameter -- throws an exception with an error message
|
||||||
|
void RunParameters::throwIllegalParamExp(const STRING &str) const
|
||||||
|
{
|
||||||
|
string msg = "ERROR : Illegal parameter \"";
|
||||||
|
msg += convertString(str);
|
||||||
|
msg += "\".\n\n";
|
||||||
|
msg += usage;
|
||||||
|
ST_THROW_RT_ERROR(msg);
|
||||||
|
}
|
||||||
|
|
||||||
void RunParameters::throwLicense() const
|
void RunParameters::throwLicense() const
|
||||||
{
|
{
|
||||||
ST_THROW_RT_ERROR(licenseText);
|
ST_THROW_RT_ERROR(licenseText);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
double RunParameters::parseSwitchValue(const STRING& str) const
|
||||||
float RunParameters::parseSwitchValue(const string &str) const
|
|
||||||
{
|
{
|
||||||
int pos;
|
int pos;
|
||||||
|
|
||||||
@ -222,14 +211,14 @@ float RunParameters::parseSwitchValue(const string &str) const
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Read numerical parameter value after '='
|
// Read numerical parameter value after '='
|
||||||
return (float)atof(str.substr(pos + 1).c_str());
|
return stof(str.substr(pos + 1).c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Interprets a single switch parameter string of format "-switch=xx"
|
// Interprets a single switch parameter string of format "-switch=xx"
|
||||||
// Valid switches are "-tempo=xx", "-pitch=xx" and "-rate=xx". Stores
|
// Valid switches are "-tempo=xx", "-pitch=xx" and "-rate=xx". Stores
|
||||||
// switch values into 'params' structure.
|
// switch values into 'params' structure.
|
||||||
void RunParameters::parseSwitchParam(const string &str)
|
void RunParameters::parseSwitchParam(const STRING& str)
|
||||||
{
|
{
|
||||||
int upS;
|
int upS;
|
||||||
|
|
||||||
@ -262,12 +251,12 @@ void RunParameters::parseSwitchParam(const string &str)
|
|||||||
|
|
||||||
case 'b' :
|
case 'b' :
|
||||||
// switch '-bpm=xx'
|
// switch '-bpm=xx'
|
||||||
detectBPM = TRUE;
|
detectBPM = true;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
goalBPM = parseSwitchValue(str);
|
goalBPM = parseSwitchValue(str);
|
||||||
}
|
}
|
||||||
catch (const runtime_error)
|
catch (const runtime_error &)
|
||||||
{
|
{
|
||||||
// illegal or missing bpm value => just calculate bpm
|
// illegal or missing bpm value => just calculate bpm
|
||||||
goalBPM = 0;
|
goalBPM = 0;
|
||||||
@ -291,7 +280,7 @@ void RunParameters::parseSwitchParam(const string &str)
|
|||||||
|
|
||||||
case 's' :
|
case 's' :
|
||||||
// switch '-speech'
|
// switch '-speech'
|
||||||
speech = TRUE;
|
speech = true;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@ -299,3 +288,5 @@ void RunParameters::parseSwitchParam(const string &str)
|
|||||||
throwIllegalParamExp(str);
|
throwIllegalParamExp(str);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|||||||
@ -8,13 +8,6 @@
|
|||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Last changed : $Date$
|
|
||||||
// File revision : $Revision: 4 $
|
|
||||||
//
|
|
||||||
// $Id$
|
|
||||||
//
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// License :
|
// License :
|
||||||
//
|
//
|
||||||
// SoundTouch audio processing library
|
// SoundTouch audio processing library
|
||||||
@ -39,34 +32,39 @@
|
|||||||
#ifndef RUNPARAMETERS_H
|
#ifndef RUNPARAMETERS_H
|
||||||
#define RUNPARAMETERS_H
|
#define RUNPARAMETERS_H
|
||||||
|
|
||||||
#include "STTypes.h"
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include "STTypes.h"
|
||||||
|
#include "SS_CharTypes.h"
|
||||||
|
#include "WavFile.h"
|
||||||
|
|
||||||
using namespace std;
|
namespace soundstretch
|
||||||
|
{
|
||||||
|
|
||||||
/// Parses command line parameters into program parameters
|
/// Parses command line parameters into program parameters
|
||||||
class RunParameters
|
class RunParameters
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
void throwIllegalParamExp(const string &str) const;
|
void throwIllegalParamExp(const STRING& str) const;
|
||||||
void throwLicense() const;
|
void throwLicense() const;
|
||||||
void parseSwitchParam(const string &str);
|
void parseSwitchParam(const STRING& str);
|
||||||
void checkLimits();
|
void checkLimits();
|
||||||
float parseSwitchValue(const string &str) const;
|
double parseSwitchValue(const STRING& tr) const;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
char *inFileName;
|
STRING inFileName;
|
||||||
char *outFileName;
|
STRING outFileName;
|
||||||
float tempoDelta;
|
double tempoDelta{ 0 };
|
||||||
float pitchDelta;
|
double pitchDelta{ 0 };
|
||||||
float rateDelta;
|
double rateDelta{ 0 };
|
||||||
int quick;
|
int quick{ 0 };
|
||||||
int noAntiAlias;
|
int noAntiAlias{ 0 };
|
||||||
float goalBPM;
|
double goalBPM{ 0 };
|
||||||
BOOL detectBPM;
|
bool detectBPM{ false };
|
||||||
BOOL speech;
|
bool speech{ false };
|
||||||
|
|
||||||
RunParameters(const int nParams, const char * const paramStr[]);
|
RunParameters(int nParams, const CHARTYPE* paramStr[]);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
52
source/SoundStretch/SS_CharTypes.h
Normal file
52
source/SoundStretch/SS_CharTypes.h
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
///
|
||||||
|
/// Char type for SoundStretch
|
||||||
|
///
|
||||||
|
/// Author : Copyright (c) Olli Parviainen
|
||||||
|
/// Author e-mail : oparviai 'at' iki.fi
|
||||||
|
/// SoundTouch WWW: http://www.surina.net/soundtouch
|
||||||
|
///
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// License :
|
||||||
|
//
|
||||||
|
// SoundTouch audio processing library
|
||||||
|
// Copyright (c) Olli Parviainen
|
||||||
|
//
|
||||||
|
// This library is free software; you can redistribute it and/or
|
||||||
|
// modify it under the terms of the GNU Lesser General Public
|
||||||
|
// License as published by the Free Software Foundation; either
|
||||||
|
// version 2.1 of the License, or (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This library 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
|
||||||
|
// Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public
|
||||||
|
// License along with this library; if not, write to the Free Software
|
||||||
|
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
//
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#ifndef SS_CHARTYPE_H
|
||||||
|
#define SS_CHARTYPE_H
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace soundstretch
|
||||||
|
{
|
||||||
|
#if _WIN32
|
||||||
|
// wide-char types for supporting non-latin file paths in Windows
|
||||||
|
using CHARTYPE = wchar_t;
|
||||||
|
using STRING = std::wstring;
|
||||||
|
#define STRING_CONST(x) (L"" x)
|
||||||
|
#else
|
||||||
|
// gnu platform can natively support UTF-8 paths using "char*" set
|
||||||
|
using CHARTYPE = char;
|
||||||
|
using STRING = std::string;
|
||||||
|
#define STRING_CONST(x) (x)
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif //SS_CHARTYPE_H
|
||||||
@ -17,13 +17,6 @@
|
|||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Last changed : $Date$
|
|
||||||
// File revision : $Revision: 4 $
|
|
||||||
//
|
|
||||||
// $Id$
|
|
||||||
//
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// License :
|
// License :
|
||||||
//
|
//
|
||||||
// SoundTouch audio processing library
|
// SoundTouch audio processing library
|
||||||
@ -49,20 +42,29 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <assert.h>
|
#include <cassert>
|
||||||
#include <limits.h>
|
#include <climits>
|
||||||
|
|
||||||
#include "WavFile.h"
|
#include "WavFile.h"
|
||||||
#include "STTypes.h"
|
#include "STTypes.h"
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
|
namespace soundstretch
|
||||||
|
{
|
||||||
|
|
||||||
|
#if _WIN32
|
||||||
|
#define FOPEN(name, mode) _wfopen(name, STRING_CONST(mode))
|
||||||
|
#else
|
||||||
|
#define FOPEN(name, mode) fopen(name, mode)
|
||||||
|
#endif
|
||||||
|
|
||||||
static const char riffStr[] = "RIFF";
|
static const char riffStr[] = "RIFF";
|
||||||
static const char waveStr[] = "WAVE";
|
static const char waveStr[] = "WAVE";
|
||||||
static const char fmtStr[] = "fmt ";
|
static const char fmtStr[] = "fmt ";
|
||||||
|
static const char factStr[] = "fact";
|
||||||
static const char dataStr[] = "data";
|
static const char dataStr[] = "data";
|
||||||
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Helper functions for swapping byte order to correctly read/write WAV files
|
// Helper functions for swapping byte order to correctly read/write WAV files
|
||||||
@ -130,7 +132,7 @@ static const char dataStr[] = "data";
|
|||||||
}
|
}
|
||||||
|
|
||||||
// dummy helper-function
|
// dummy helper-function
|
||||||
static inline void _swap16Buffer(short *pData, int numBytes)
|
static inline void _swap16Buffer(short*, int)
|
||||||
{
|
{
|
||||||
// do nothing
|
// do nothing
|
||||||
}
|
}
|
||||||
@ -145,7 +147,7 @@ static const char dataStr[] = "data";
|
|||||||
|
|
||||||
WavFileBase::WavFileBase()
|
WavFileBase::WavFileBase()
|
||||||
{
|
{
|
||||||
convBuff = NULL;
|
convBuff = nullptr;
|
||||||
convBuffSize = 0;
|
convBuffSize = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -176,17 +178,13 @@ void *WavFileBase::getConvBuffer(int sizeBytes)
|
|||||||
// Class WavInFile
|
// Class WavInFile
|
||||||
//
|
//
|
||||||
|
|
||||||
WavInFile::WavInFile(const char *fileName)
|
WavInFile::WavInFile(const STRING& fileName)
|
||||||
{
|
{
|
||||||
// Try to open the file for reading
|
// Try to open the file for reading
|
||||||
fptr = fopen(fileName, "rb");
|
fptr = FOPEN(fileName.c_str(), "rb");
|
||||||
if (fptr == NULL)
|
if (fptr == nullptr)
|
||||||
{
|
{
|
||||||
// didn't succeed
|
ST_THROW_RT_ERROR("Error : Unable to open file for reading.");
|
||||||
string msg = "Error : Unable to open file \"";
|
|
||||||
msg += fileName;
|
|
||||||
msg += "\" for reading.";
|
|
||||||
ST_THROW_RT_ERROR(msg.c_str());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
init();
|
init();
|
||||||
@ -199,9 +197,7 @@ WavInFile::WavInFile(FILE *file)
|
|||||||
fptr = file;
|
fptr = file;
|
||||||
if (!file)
|
if (!file)
|
||||||
{
|
{
|
||||||
// didn't succeed
|
ST_THROW_RT_ERROR("Error : Unable to access input stream for reading");
|
||||||
string msg = "Error : Unable to access input stream for reading";
|
|
||||||
ST_THROW_RT_ERROR(msg.c_str());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
init();
|
init();
|
||||||
@ -220,32 +216,29 @@ void WavInFile::init()
|
|||||||
hdrsOk = readWavHeaders();
|
hdrsOk = readWavHeaders();
|
||||||
if (hdrsOk != 0)
|
if (hdrsOk != 0)
|
||||||
{
|
{
|
||||||
// Something didn't match in the wav file headers
|
ST_THROW_RT_ERROR("Input file is corrupt or not a WAV file");
|
||||||
string msg = "Input file is corrupt or not a WAV file";
|
|
||||||
ST_THROW_RT_ERROR(msg.c_str());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Ignore 'fixed' field value as 32bit signed linear data can have other value than 1.
|
// sanity check for format parameters
|
||||||
if (header.format.fixed != 1)
|
if ((header.format.channel_number < 1) || (header.format.channel_number > 9) ||
|
||||||
|
(header.format.sample_rate < 4000) || (header.format.sample_rate > 192000) ||
|
||||||
|
(header.format.byte_per_sample < 1) || (header.format.byte_per_sample > 320) ||
|
||||||
|
(header.format.bits_per_sample < 8) || (header.format.bits_per_sample > 32))
|
||||||
{
|
{
|
||||||
string msg = "Input file uses unsupported encoding.";
|
ST_THROW_RT_ERROR("Error: Illegal wav file header format parameters.");
|
||||||
ST_THROW_RT_ERROR(msg.c_str());
|
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
|
||||||
dataRead = 0;
|
dataRead = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
WavInFile::~WavInFile()
|
WavInFile::~WavInFile()
|
||||||
{
|
{
|
||||||
if (fptr) fclose(fptr);
|
if (fptr) fclose(fptr);
|
||||||
fptr = NULL;
|
fptr = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void WavInFile::rewind()
|
void WavInFile::rewind()
|
||||||
{
|
{
|
||||||
int hdrsOk;
|
int hdrsOk;
|
||||||
@ -458,11 +451,10 @@ int WavInFile::read(float *buffer, int maxElems)
|
|||||||
int WavInFile::eof() const
|
int WavInFile::eof() const
|
||||||
{
|
{
|
||||||
// return true if all data has been read or file eof has reached
|
// return true if all data has been read or file eof has reached
|
||||||
return (dataRead == header.data.data_len || feof(fptr));
|
return ((uint)dataRead == header.data.data_len || feof(fptr));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// test if character code is between a white space ' ' and little 'z'
|
// test if character code is between a white space ' ' and little 'z'
|
||||||
static int isAlpha(char c)
|
static int isAlpha(char c)
|
||||||
{
|
{
|
||||||
@ -503,8 +495,6 @@ int WavInFile::readRIFFBlock()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int WavInFile::readHeaderBlock()
|
int WavInFile::readHeaderBlock()
|
||||||
{
|
{
|
||||||
char label[5];
|
char label[5];
|
||||||
@ -527,12 +517,16 @@ int WavInFile::readHeaderBlock()
|
|||||||
// read length of the format field
|
// read length of the format field
|
||||||
if (fread(&nLen, sizeof(int), 1, fptr) != 1) return -1;
|
if (fread(&nLen, sizeof(int), 1, fptr) != 1) return -1;
|
||||||
// swap byte order if necessary
|
// swap byte order if necessary
|
||||||
_swap32(nLen); // int format_len;
|
_swap32(nLen);
|
||||||
header.format.format_len = nLen;
|
|
||||||
|
|
||||||
// calculate how much length differs from expected
|
// calculate how much length differs from expected
|
||||||
nDump = nLen - ((int)sizeof(header.format) - 8);
|
nDump = nLen - ((int)sizeof(header.format) - 8);
|
||||||
|
|
||||||
|
// verify that header length isn't smaller than expected structure
|
||||||
|
if ((nLen < 0) || (nDump < 0)) return -1;
|
||||||
|
|
||||||
|
header.format.format_len = nLen;
|
||||||
|
|
||||||
// if format_len is larger than expected, read only as much data as we've space for
|
// if format_len is larger than expected, read only as much data as we've space for
|
||||||
if (nDump > 0)
|
if (nDump > 0)
|
||||||
{
|
{
|
||||||
@ -543,12 +537,12 @@ int WavInFile::readHeaderBlock()
|
|||||||
if (fread(&(header.format.fixed), nLen, 1, fptr) != 1) return -1;
|
if (fread(&(header.format.fixed), nLen, 1, fptr) != 1) return -1;
|
||||||
|
|
||||||
// swap byte order if necessary
|
// swap byte order if necessary
|
||||||
_swap16(header.format.fixed); // short int fixed;
|
_swap16((short&)header.format.fixed); // short int fixed;
|
||||||
_swap16(header.format.channel_number); // short int channel_number;
|
_swap16((short&)header.format.channel_number); // short int channel_number;
|
||||||
_swap32((int&)header.format.sample_rate); // int sample_rate;
|
_swap32((int&)header.format.sample_rate); // int sample_rate;
|
||||||
_swap32((int&)header.format.byte_rate); // int byte_rate;
|
_swap32((int&)header.format.byte_rate); // int byte_rate;
|
||||||
_swap16(header.format.byte_per_sample); // short int byte_per_sample;
|
_swap16((short&)header.format.byte_per_sample); // short int byte_per_sample;
|
||||||
_swap16(header.format.bits_per_sample); // short int bits_per_sample;
|
_swap16((short&)header.format.bits_per_sample); // short int bits_per_sample;
|
||||||
|
|
||||||
// if format_len is larger than expected, skip the extra data
|
// if format_len is larger than expected, skip the extra data
|
||||||
if (nDump > 0)
|
if (nDump > 0)
|
||||||
@ -558,6 +552,46 @@ int WavInFile::readHeaderBlock()
|
|||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
else if (strcmp(label, factStr) == 0)
|
||||||
|
{
|
||||||
|
int nLen, nDump;
|
||||||
|
|
||||||
|
// 'fact' block
|
||||||
|
memcpy(header.fact.fact_field, factStr, 4);
|
||||||
|
|
||||||
|
// read length of the fact field
|
||||||
|
if (fread(&nLen, sizeof(int), 1, fptr) != 1) return -1;
|
||||||
|
// swap byte order if necessary
|
||||||
|
_swap32(nLen);
|
||||||
|
|
||||||
|
// calculate how much length differs from expected
|
||||||
|
nDump = nLen - ((int)sizeof(header.fact) - 8);
|
||||||
|
|
||||||
|
// verify that fact length isn't smaller than expected structure
|
||||||
|
if ((nLen < 0) || (nDump < 0)) return -1;
|
||||||
|
|
||||||
|
header.fact.fact_len = nLen;
|
||||||
|
|
||||||
|
// if format_len is larger than expected, read only as much data as we've space for
|
||||||
|
if (nDump > 0)
|
||||||
|
{
|
||||||
|
nLen = sizeof(header.fact) - 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
// read data
|
||||||
|
if (fread(&(header.fact.fact_sample_len), nLen, 1, fptr) != 1) return -1;
|
||||||
|
|
||||||
|
// swap byte order if necessary
|
||||||
|
_swap32((int&)header.fact.fact_sample_len); // int sample_length;
|
||||||
|
|
||||||
|
// if fact_len is larger than expected, skip the extra data
|
||||||
|
if (nDump > 0)
|
||||||
|
{
|
||||||
|
fseek(fptr, nDump, SEEK_CUR);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
else if (strcmp(label, dataStr) == 0)
|
else if (strcmp(label, dataStr) == 0)
|
||||||
{
|
{
|
||||||
// 'data' block
|
// 'data' block
|
||||||
@ -632,7 +666,6 @@ uint WavInFile::getSampleRate() const
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
uint WavInFile::getDataSizeInBytes() const
|
uint WavInFile::getDataSizeInBytes() const
|
||||||
{
|
{
|
||||||
return header.data.data_len;
|
return header.data.data_len;
|
||||||
@ -642,6 +675,7 @@ uint WavInFile::getDataSizeInBytes() const
|
|||||||
uint WavInFile::getNumSamples() const
|
uint WavInFile::getNumSamples() const
|
||||||
{
|
{
|
||||||
if (header.format.byte_per_sample == 0) return 0;
|
if (header.format.byte_per_sample == 0) return 0;
|
||||||
|
if (header.format.fixed > 1) return header.fact.fact_sample_len;
|
||||||
return header.data.data_len / (unsigned short)header.format.byte_per_sample;
|
return header.data.data_len / (unsigned short)header.format.byte_per_sample;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -665,23 +699,18 @@ uint WavInFile::getElapsedMS() const
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Class WavOutFile
|
// Class WavOutFile
|
||||||
//
|
//
|
||||||
|
|
||||||
WavOutFile::WavOutFile(const char *fileName, int sampleRate, int bits, int channels)
|
WavOutFile::WavOutFile(const STRING& fileName, int sampleRate, int bits, int channels)
|
||||||
{
|
{
|
||||||
bytesWritten = 0;
|
bytesWritten = 0;
|
||||||
fptr = fopen(fileName, "wb");
|
fptr = FOPEN(fileName.c_str(), "wb");
|
||||||
if (fptr == NULL)
|
if (fptr == nullptr)
|
||||||
{
|
{
|
||||||
string msg = "Error : Unable to open file \"";
|
ST_THROW_RT_ERROR("Error : Unable to open file for writing.");
|
||||||
msg += fileName;
|
|
||||||
msg += "\" for writing.";
|
|
||||||
//pmsg = msg.c_str;
|
|
||||||
ST_THROW_RT_ERROR(msg.c_str());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fillInHeader(sampleRate, bits, channels);
|
fillInHeader(sampleRate, bits, channels);
|
||||||
@ -693,10 +722,9 @@ WavOutFile::WavOutFile(FILE *file, int sampleRate, int bits, int channels)
|
|||||||
{
|
{
|
||||||
bytesWritten = 0;
|
bytesWritten = 0;
|
||||||
fptr = file;
|
fptr = file;
|
||||||
if (fptr == NULL)
|
if (fptr == nullptr)
|
||||||
{
|
{
|
||||||
string msg = "Error : Unable to access output file stream.";
|
ST_THROW_RT_ERROR("Error : Unable to access output file stream.");
|
||||||
ST_THROW_RT_ERROR(msg.c_str());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fillInHeader(sampleRate, bits, channels);
|
fillInHeader(sampleRate, bits, channels);
|
||||||
@ -704,16 +732,14 @@ WavOutFile::WavOutFile(FILE *file, int sampleRate, int bits, int channels)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
WavOutFile::~WavOutFile()
|
WavOutFile::~WavOutFile()
|
||||||
{
|
{
|
||||||
finishHeader();
|
finishHeader();
|
||||||
if (fptr) fclose(fptr);
|
if (fptr) fclose(fptr);
|
||||||
fptr = NULL;
|
fptr = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void WavOutFile::fillInHeader(uint sampleRate, uint bits, uint channels)
|
void WavOutFile::fillInHeader(uint sampleRate, uint bits, uint channels)
|
||||||
{
|
{
|
||||||
// fill in the 'riff' part..
|
// fill in the 'riff' part..
|
||||||
@ -739,6 +765,11 @@ void WavOutFile::fillInHeader(uint sampleRate, uint bits, uint channels)
|
|||||||
header.format.byte_rate = header.format.byte_per_sample * (int)sampleRate;
|
header.format.byte_rate = header.format.byte_per_sample * (int)sampleRate;
|
||||||
header.format.sample_rate = (int)sampleRate;
|
header.format.sample_rate = (int)sampleRate;
|
||||||
|
|
||||||
|
// fill in the 'fact' part...
|
||||||
|
memcpy(&(header.fact.fact_field), factStr, 4);
|
||||||
|
header.fact.fact_len = 4;
|
||||||
|
header.fact.fact_sample_len = 0;
|
||||||
|
|
||||||
// fill in the 'data' part..
|
// fill in the 'data' part..
|
||||||
|
|
||||||
// copy string 'data' to data_field
|
// copy string 'data' to data_field
|
||||||
@ -751,14 +782,14 @@ void WavOutFile::fillInHeader(uint sampleRate, uint bits, uint channels)
|
|||||||
void WavOutFile::finishHeader()
|
void WavOutFile::finishHeader()
|
||||||
{
|
{
|
||||||
// supplement the file length into the header structure
|
// supplement the file length into the header structure
|
||||||
header.riff.package_len = bytesWritten + 36;
|
header.riff.package_len = bytesWritten + sizeof(WavHeader) - sizeof(WavRiff) + 4;
|
||||||
header.data.data_len = bytesWritten;
|
header.data.data_len = bytesWritten;
|
||||||
|
header.fact.fact_sample_len = bytesWritten / header.format.byte_per_sample;
|
||||||
|
|
||||||
writeHeader();
|
writeHeader();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void WavOutFile::writeHeader()
|
void WavOutFile::writeHeader()
|
||||||
{
|
{
|
||||||
WavHeader hdrTemp;
|
WavHeader hdrTemp;
|
||||||
@ -775,6 +806,8 @@ void WavOutFile::writeHeader()
|
|||||||
_swap16((short&)hdrTemp.format.byte_per_sample);
|
_swap16((short&)hdrTemp.format.byte_per_sample);
|
||||||
_swap16((short&)hdrTemp.format.bits_per_sample);
|
_swap16((short&)hdrTemp.format.bits_per_sample);
|
||||||
_swap32((int&)hdrTemp.data.data_len);
|
_swap32((int&)hdrTemp.data.data_len);
|
||||||
|
_swap32((int&)hdrTemp.fact.fact_len);
|
||||||
|
_swap32((int&)hdrTemp.fact.fact_sample_len);
|
||||||
|
|
||||||
// write the supplemented header in the beginning of the file
|
// write the supplemented header in the beginning of the file
|
||||||
fseek(fptr, 0, SEEK_SET);
|
fseek(fptr, 0, SEEK_SET);
|
||||||
@ -789,7 +822,6 @@ void WavOutFile::writeHeader()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void WavOutFile::write(const unsigned char* buffer, int numElems)
|
void WavOutFile::write(const unsigned char* buffer, int numElems)
|
||||||
{
|
{
|
||||||
int res;
|
int res;
|
||||||
@ -810,7 +842,6 @@ void WavOutFile::write(const unsigned char *buffer, int numElems)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void WavOutFile::write(const short* buffer, int numElems)
|
void WavOutFile::write(const short* buffer, int numElems)
|
||||||
{
|
{
|
||||||
int res;
|
int res;
|
||||||
@ -840,7 +871,7 @@ void WavOutFile::write(const short *buffer, int numElems)
|
|||||||
|
|
||||||
// use temp buffer to swap byte order if necessary
|
// use temp buffer to swap byte order if necessary
|
||||||
short* pTemp = (short*)getConvBuffer(numElems * sizeof(short));
|
short* pTemp = (short*)getConvBuffer(numElems * sizeof(short));
|
||||||
memcpy(pTemp, buffer, numElems * 2);
|
memcpy(pTemp, buffer, (size_t)numElems * 2L);
|
||||||
_swap16Buffer(pTemp, numElems);
|
_swap16Buffer(pTemp, numElems);
|
||||||
|
|
||||||
res = (int)fwrite(pTemp, 2, numElems, fptr);
|
res = (int)fwrite(pTemp, 2, numElems, fptr);
|
||||||
@ -889,7 +920,7 @@ void WavOutFile::write(const float *buffer, int numElems)
|
|||||||
|
|
||||||
bytesPerSample = header.format.bits_per_sample / 8;
|
bytesPerSample = header.format.bits_per_sample / 8;
|
||||||
numBytes = numElems * bytesPerSample;
|
numBytes = numElems * bytesPerSample;
|
||||||
short *temp = (short*)getConvBuffer(numBytes);
|
void* temp = getConvBuffer(numBytes + 7); // round bit up to avoid buffer overrun with 24bit-value assignment
|
||||||
|
|
||||||
switch (bytesPerSample)
|
switch (bytesPerSample)
|
||||||
{
|
{
|
||||||
@ -949,3 +980,5 @@ void WavOutFile::write(const float *buffer, int numElems)
|
|||||||
}
|
}
|
||||||
bytesWritten += numBytes;
|
bytesWritten += numBytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|||||||
@ -16,13 +16,6 @@
|
|||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Last changed : $Date$
|
|
||||||
// File revision : $Revision: 4 $
|
|
||||||
//
|
|
||||||
// $Id$
|
|
||||||
//
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// License :
|
// License :
|
||||||
//
|
//
|
||||||
// SoundTouch audio processing library
|
// SoundTouch audio processing library
|
||||||
@ -47,7 +40,12 @@
|
|||||||
#ifndef WAVFILE_H
|
#ifndef WAVFILE_H
|
||||||
#define WAVFILE_H
|
#define WAVFILE_H
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <cstdio>
|
||||||
|
#include <string>
|
||||||
|
#include "SS_CharTypes.h"
|
||||||
|
|
||||||
|
namespace soundstretch
|
||||||
|
{
|
||||||
|
|
||||||
#ifndef uint
|
#ifndef uint
|
||||||
typedef unsigned int uint;
|
typedef unsigned int uint;
|
||||||
@ -58,7 +56,7 @@ typedef unsigned int uint;
|
|||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
char riff_char[4];
|
char riff_char[4];
|
||||||
int package_len;
|
uint package_len;
|
||||||
char wave[4];
|
char wave[4];
|
||||||
} WavRiff;
|
} WavRiff;
|
||||||
|
|
||||||
@ -66,15 +64,23 @@ typedef struct
|
|||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
char fmt[4];
|
char fmt[4];
|
||||||
int format_len;
|
unsigned int format_len;
|
||||||
short fixed;
|
unsigned short fixed;
|
||||||
short channel_number;
|
unsigned short channel_number;
|
||||||
int sample_rate;
|
unsigned int sample_rate;
|
||||||
int byte_rate;
|
unsigned int byte_rate;
|
||||||
short byte_per_sample;
|
unsigned short byte_per_sample;
|
||||||
short bits_per_sample;
|
unsigned short bits_per_sample;
|
||||||
} WavFormat;
|
} WavFormat;
|
||||||
|
|
||||||
|
/// WAV audio file 'fact' section header
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
char fact_field[4];
|
||||||
|
uint fact_len;
|
||||||
|
uint fact_sample_len;
|
||||||
|
} WavFact;
|
||||||
|
|
||||||
/// WAV audio file 'data' section header
|
/// WAV audio file 'data' section header
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
@ -88,6 +94,7 @@ typedef struct
|
|||||||
{
|
{
|
||||||
WavRiff riff;
|
WavRiff riff;
|
||||||
WavFormat format;
|
WavFormat format;
|
||||||
|
WavFact fact;
|
||||||
WavData data;
|
WavData data;
|
||||||
} WavHeader;
|
} WavHeader;
|
||||||
|
|
||||||
@ -116,9 +123,6 @@ private:
|
|||||||
/// File pointer.
|
/// File pointer.
|
||||||
FILE *fptr;
|
FILE *fptr;
|
||||||
|
|
||||||
/// Position within the audio stream
|
|
||||||
long position;
|
|
||||||
|
|
||||||
/// Counter of how many bytes of sample data have been read from the file.
|
/// Counter of how many bytes of sample data have been read from the file.
|
||||||
long dataRead;
|
long dataRead;
|
||||||
|
|
||||||
@ -146,7 +150,7 @@ private:
|
|||||||
public:
|
public:
|
||||||
/// Constructor: Opens the given WAV file. If the file can't be opened,
|
/// Constructor: Opens the given WAV file. If the file can't be opened,
|
||||||
/// throws 'runtime_error' exception.
|
/// throws 'runtime_error' exception.
|
||||||
WavInFile(const char *filename);
|
WavInFile(const STRING& filename);
|
||||||
|
|
||||||
WavInFile(FILE *file);
|
WavInFile(FILE *file);
|
||||||
|
|
||||||
@ -216,7 +220,6 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// Class for writing WAV audio files.
|
/// Class for writing WAV audio files.
|
||||||
class WavOutFile : protected WavFileBase
|
class WavOutFile : protected WavFileBase
|
||||||
{
|
{
|
||||||
@ -243,7 +246,7 @@ private:
|
|||||||
public:
|
public:
|
||||||
/// Constructor: Creates a new WAV file. Throws a 'runtime_error' exception
|
/// Constructor: Creates a new WAV file. Throws a 'runtime_error' exception
|
||||||
/// if file creation fails.
|
/// if file creation fails.
|
||||||
WavOutFile(const char *fileName, ///< Filename
|
WavOutFile(const STRING& fileName, ///< Filename
|
||||||
int sampleRate, ///< Sample rate (e.g. 44100 etc)
|
int sampleRate, ///< Sample rate (e.g. 44100 etc)
|
||||||
int bits, ///< Bits per sample (8 or 16 bits)
|
int bits, ///< Bits per sample (8 or 16 bits)
|
||||||
int channels ///< Number of channels (1=mono, 2=stereo)
|
int channels ///< Number of channels (1=mono, 2=stereo)
|
||||||
@ -273,4 +276,6 @@ public:
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -8,13 +8,6 @@
|
|||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Last changed : $Date$
|
|
||||||
// File revision : $Revision: 4 $
|
|
||||||
//
|
|
||||||
// $Id$
|
|
||||||
//
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// License :
|
// License :
|
||||||
//
|
//
|
||||||
// SoundTouch audio processing library
|
// SoundTouch audio processing library
|
||||||
@ -36,10 +29,12 @@
|
|||||||
//
|
//
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <memory>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <stdio.h>
|
#include <string>
|
||||||
#include <string.h>
|
#include <cstdio>
|
||||||
#include <time.h>
|
#include <ctime>
|
||||||
#include "RunParameters.h"
|
#include "RunParameters.h"
|
||||||
#include "WavFile.h"
|
#include "WavFile.h"
|
||||||
#include "SoundTouch.h"
|
#include "SoundTouch.h"
|
||||||
@ -48,8 +43,11 @@
|
|||||||
using namespace soundtouch;
|
using namespace soundtouch;
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
// Processing chunk size
|
namespace soundstretch
|
||||||
#define BUFF_SIZE 2048
|
{
|
||||||
|
|
||||||
|
// Processing chunk size (size chosen to be divisible by 2, 4, 6, 8, 10, 12, 14, 16 channels ...)
|
||||||
|
#define BUFF_SIZE 6720
|
||||||
|
|
||||||
#if _WIN32
|
#if _WIN32
|
||||||
#include <io.h>
|
#include <io.h>
|
||||||
@ -65,8 +63,8 @@ using namespace std;
|
|||||||
|
|
||||||
static const char _helloText[] =
|
static const char _helloText[] =
|
||||||
"\n"
|
"\n"
|
||||||
" SoundStretch v%s - Written by Olli Parviainen 2001 - 2012\n"
|
" SoundStretch v%s - Copyright (c) Olli Parviainen\n"
|
||||||
"==================================================================\n"
|
"=========================================================\n"
|
||||||
"author e-mail: <oparviai"
|
"author e-mail: <oparviai"
|
||||||
"@"
|
"@"
|
||||||
"iki.fi> - WWW: http://www.surina.net/soundtouch\n"
|
"iki.fi> - WWW: http://www.surina.net/soundtouch\n"
|
||||||
@ -75,77 +73,67 @@ static const char _helloText[] =
|
|||||||
"more information.\n"
|
"more information.\n"
|
||||||
"\n";
|
"\n";
|
||||||
|
|
||||||
static void openFiles(WavInFile **inFile, WavOutFile **outFile, const RunParameters *params)
|
static void openFiles(unique_ptr<WavInFile>& inFile, unique_ptr<WavOutFile>& outFile, const RunParameters& params)
|
||||||
{
|
{
|
||||||
int bits, samplerate, channels;
|
if (params.inFileName == STRING_CONST("stdin"))
|
||||||
|
|
||||||
if (strcmp(params->inFileName, "stdin") == 0)
|
|
||||||
{
|
{
|
||||||
// used 'stdin' as input file
|
// used 'stdin' as input file
|
||||||
SET_STREAM_TO_BIN_MODE(stdin);
|
SET_STREAM_TO_BIN_MODE(stdin);
|
||||||
*inFile = new WavInFile(stdin);
|
inFile = make_unique<WavInFile>(stdin);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// open input file...
|
// open input file...
|
||||||
*inFile = new WavInFile(params->inFileName);
|
inFile = make_unique<WavInFile>(params.inFileName.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
// ... open output file with same sound parameters
|
// ... open output file with same sound parameters
|
||||||
bits = (int)(*inFile)->getNumBits();
|
const int bits = (int)inFile->getNumBits();
|
||||||
samplerate = (int)(*inFile)->getSampleRate();
|
const int samplerate = (int)inFile->getSampleRate();
|
||||||
channels = (int)(*inFile)->getNumChannels();
|
const int channels = (int)inFile->getNumChannels();
|
||||||
|
|
||||||
if (params->outFileName)
|
if (!params.outFileName.empty())
|
||||||
{
|
{
|
||||||
if (strcmp(params->outFileName, "stdout") == 0)
|
if (params.outFileName == STRING_CONST("stdout"))
|
||||||
{
|
{
|
||||||
SET_STREAM_TO_BIN_MODE(stdout);
|
SET_STREAM_TO_BIN_MODE(stdout);
|
||||||
*outFile = new WavOutFile(stdout, samplerate, bits, channels);
|
outFile = make_unique<WavOutFile>(stdout, samplerate, bits, channels);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
*outFile = new WavOutFile(params->outFileName, samplerate, bits, channels);
|
outFile = make_unique<WavOutFile>(params.outFileName.c_str(), samplerate, bits, channels);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
*outFile = NULL;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Sets the 'SoundTouch' object up according to input file sound format &
|
// Sets the 'SoundTouch' object up according to input file sound format &
|
||||||
// command line parameters
|
// command line parameters
|
||||||
static void setup(SoundTouch *pSoundTouch, const WavInFile *inFile, const RunParameters *params)
|
static void setup(SoundTouch& soundTouch, const WavInFile& inFile, const RunParameters& params)
|
||||||
{
|
{
|
||||||
int sampleRate;
|
const int sampleRate = (int)inFile.getSampleRate();
|
||||||
int channels;
|
const int channels = (int)inFile.getNumChannels();
|
||||||
|
soundTouch.setSampleRate(sampleRate);
|
||||||
|
soundTouch.setChannels(channels);
|
||||||
|
|
||||||
sampleRate = (int)inFile->getSampleRate();
|
soundTouch.setTempoChange(params.tempoDelta);
|
||||||
channels = (int)inFile->getNumChannels();
|
soundTouch.setPitchSemiTones(params.pitchDelta);
|
||||||
pSoundTouch->setSampleRate(sampleRate);
|
soundTouch.setRateChange(params.rateDelta);
|
||||||
pSoundTouch->setChannels(channels);
|
|
||||||
|
|
||||||
pSoundTouch->setTempoChange(params->tempoDelta);
|
soundTouch.setSetting(SETTING_USE_QUICKSEEK, params.quick);
|
||||||
pSoundTouch->setPitchSemiTones(params->pitchDelta);
|
soundTouch.setSetting(SETTING_USE_AA_FILTER, !(params.noAntiAlias));
|
||||||
pSoundTouch->setRateChange(params->rateDelta);
|
|
||||||
|
|
||||||
pSoundTouch->setSetting(SETTING_USE_QUICKSEEK, params->quick);
|
if (params.speech)
|
||||||
pSoundTouch->setSetting(SETTING_USE_AA_FILTER, !(params->noAntiAlias));
|
|
||||||
|
|
||||||
if (params->speech)
|
|
||||||
{
|
{
|
||||||
// use settings for speech processing
|
// use settings for speech processing
|
||||||
pSoundTouch->setSetting(SETTING_SEQUENCE_MS, 40);
|
soundTouch.setSetting(SETTING_SEQUENCE_MS, 40);
|
||||||
pSoundTouch->setSetting(SETTING_SEEKWINDOW_MS, 15);
|
soundTouch.setSetting(SETTING_SEEKWINDOW_MS, 15);
|
||||||
pSoundTouch->setSetting(SETTING_OVERLAP_MS, 8);
|
soundTouch.setSetting(SETTING_OVERLAP_MS, 8);
|
||||||
fprintf(stderr, "Tune processing parameters for speech processing.\n");
|
fprintf(stderr, "Tune processing parameters for speech processing.\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
// print processing information
|
// print processing information
|
||||||
if (params->outFileName)
|
if (!params.outFileName.empty())
|
||||||
{
|
{
|
||||||
#ifdef SOUNDTOUCH_INTEGER_SAMPLES
|
#ifdef SOUNDTOUCH_INTEGER_SAMPLES
|
||||||
fprintf(stderr, "Uses 16bit integer sample type in processing.\n\n");
|
fprintf(stderr, "Uses 16bit integer sample type in processing.\n\n");
|
||||||
@ -157,9 +145,9 @@ static void setup(SoundTouch *pSoundTouch, const WavInFile *inFile, const RunPar
|
|||||||
#endif
|
#endif
|
||||||
// print processing information only if outFileName given i.e. some processing will happen
|
// print processing information only if outFileName given i.e. some processing will happen
|
||||||
fprintf(stderr, "Processing the file with the following changes:\n");
|
fprintf(stderr, "Processing the file with the following changes:\n");
|
||||||
fprintf(stderr, " tempo change = %+g %%\n", params->tempoDelta);
|
fprintf(stderr, " tempo change = %+lg %%\n", params.tempoDelta);
|
||||||
fprintf(stderr, " pitch change = %+g semitones\n", params->pitchDelta);
|
fprintf(stderr, " pitch change = %+lg semitones\n", params.pitchDelta);
|
||||||
fprintf(stderr, " rate change = %+g %%\n\n", params->rateDelta);
|
fprintf(stderr, " rate change = %+lg %%\n\n", params.rateDelta);
|
||||||
fprintf(stderr, "Working...");
|
fprintf(stderr, "Working...");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -172,32 +160,25 @@ static void setup(SoundTouch *pSoundTouch, const WavInFile *inFile, const RunPar
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Processes the sound
|
// Processes the sound
|
||||||
static void process(SoundTouch *pSoundTouch, WavInFile *inFile, WavOutFile *outFile)
|
static void process(SoundTouch& soundTouch, WavInFile& inFile, WavOutFile& outFile)
|
||||||
{
|
{
|
||||||
int nSamples;
|
|
||||||
int nChannels;
|
|
||||||
int buffSizeSamples;
|
|
||||||
SAMPLETYPE sampleBuffer[BUFF_SIZE];
|
SAMPLETYPE sampleBuffer[BUFF_SIZE];
|
||||||
|
int nSamples;
|
||||||
|
|
||||||
if ((inFile == NULL) || (outFile == NULL)) return; // nothing to do.
|
const int nChannels = (int)inFile.getNumChannels();
|
||||||
|
|
||||||
nChannels = (int)inFile->getNumChannels();
|
|
||||||
assert(nChannels > 0);
|
assert(nChannels > 0);
|
||||||
buffSizeSamples = BUFF_SIZE / nChannels;
|
const int buffSizeSamples = BUFF_SIZE / nChannels;
|
||||||
|
|
||||||
// Process samples read from the input file
|
// Process samples read from the input file
|
||||||
while (inFile->eof() == 0)
|
while (inFile.eof() == 0)
|
||||||
{
|
{
|
||||||
int num;
|
|
||||||
|
|
||||||
// Read a chunk of samples from the input file
|
// Read a chunk of samples from the input file
|
||||||
num = inFile->read(sampleBuffer, BUFF_SIZE);
|
const int num = inFile.read(sampleBuffer, BUFF_SIZE);
|
||||||
nSamples = num / (int)inFile->getNumChannels();
|
int nSamples = num / (int)inFile.getNumChannels();
|
||||||
|
|
||||||
// Feed the samples into SoundTouch processor
|
// Feed the samples into SoundTouch processor
|
||||||
pSoundTouch->putSamples(sampleBuffer, nSamples);
|
soundTouch.putSamples(sampleBuffer, nSamples);
|
||||||
|
|
||||||
// Read ready samples from SoundTouch processor & write them output file.
|
// Read ready samples from SoundTouch processor & write them output file.
|
||||||
// NOTES:
|
// NOTES:
|
||||||
@ -209,62 +190,57 @@ static void process(SoundTouch *pSoundTouch, WavInFile *inFile, WavOutFile *outF
|
|||||||
// outputs samples.
|
// outputs samples.
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
nSamples = pSoundTouch->receiveSamples(sampleBuffer, buffSizeSamples);
|
nSamples = soundTouch.receiveSamples(sampleBuffer, buffSizeSamples);
|
||||||
outFile->write(sampleBuffer, nSamples * nChannels);
|
outFile.write(sampleBuffer, nSamples * nChannels);
|
||||||
} while (nSamples != 0);
|
} while (nSamples != 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now the input file is processed, yet 'flush' few last samples that are
|
// Now the input file is processed, yet 'flush' few last samples that are
|
||||||
// hiding in the SoundTouch's internal processing pipeline.
|
// hiding in the SoundTouch's internal processing pipeline.
|
||||||
pSoundTouch->flush();
|
soundTouch.flush();
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
nSamples = pSoundTouch->receiveSamples(sampleBuffer, buffSizeSamples);
|
nSamples = soundTouch.receiveSamples(sampleBuffer, buffSizeSamples);
|
||||||
outFile->write(sampleBuffer, nSamples * nChannels);
|
outFile.write(sampleBuffer, nSamples * nChannels);
|
||||||
} while (nSamples != 0);
|
} while (nSamples != 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Detect BPM rate of inFile and adjust tempo setting accordingly if necessary
|
// Detect BPM rate of inFile and adjust tempo setting accordingly if necessary
|
||||||
static void detectBPM(WavInFile *inFile, RunParameters *params)
|
static void detectBPM(WavInFile& inFile, RunParameters& params)
|
||||||
{
|
{
|
||||||
float bpmValue;
|
BPMDetect bpm(inFile.getNumChannels(), inFile.getSampleRate());
|
||||||
int nChannels;
|
|
||||||
BPMDetect bpm(inFile->getNumChannels(), inFile->getSampleRate());
|
|
||||||
SAMPLETYPE sampleBuffer[BUFF_SIZE];
|
SAMPLETYPE sampleBuffer[BUFF_SIZE];
|
||||||
|
|
||||||
// detect bpm rate
|
// detect bpm rate
|
||||||
fprintf(stderr, "Detecting BPM rate...");
|
fprintf(stderr, "Detecting BPM rate...");
|
||||||
fflush(stderr);
|
fflush(stderr);
|
||||||
|
|
||||||
nChannels = (int)inFile->getNumChannels();
|
const int nChannels = (int)inFile.getNumChannels();
|
||||||
assert(BUFF_SIZE % nChannels == 0);
|
int readSize = BUFF_SIZE - BUFF_SIZE % nChannels; // round read size down to multiple of num.channels
|
||||||
|
|
||||||
// Process the 'inFile' in small blocks, repeat until whole file has
|
// Process the 'inFile' in small blocks, repeat until whole file has
|
||||||
// been processed
|
// been processed
|
||||||
while (inFile->eof() == 0)
|
while (inFile.eof() == 0)
|
||||||
{
|
{
|
||||||
int num, samples;
|
|
||||||
|
|
||||||
// Read sample data from input file
|
// Read sample data from input file
|
||||||
num = inFile->read(sampleBuffer, BUFF_SIZE);
|
const int num = inFile.read(sampleBuffer, readSize);
|
||||||
|
|
||||||
// Enter the new samples to the bpm analyzer class
|
// Enter the new samples to the bpm analyzer class
|
||||||
samples = num / nChannels;
|
const int samples = num / nChannels;
|
||||||
bpm.inputSamples(sampleBuffer, samples);
|
bpm.inputSamples(sampleBuffer, samples);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now the whole song data has been analyzed. Read the resulting bpm.
|
// Now the whole song data has been analyzed. Read the resulting bpm.
|
||||||
bpmValue = bpm.getBpm();
|
const float bpmValue = bpm.getBpm();
|
||||||
fprintf(stderr, "Done!\n");
|
fprintf(stderr, "Done!\n");
|
||||||
|
|
||||||
// rewind the file after bpm detection
|
// rewind the file after bpm detection
|
||||||
inFile->rewind();
|
inFile.rewind();
|
||||||
|
|
||||||
if (bpmValue > 0)
|
if (bpmValue > 0)
|
||||||
{
|
{
|
||||||
fprintf(stderr, "Detected BPM rate %.1f\n\n", bpmValue);
|
fprintf(stderr, "Detected BPM rate %.1lf\n\n", bpmValue);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -272,62 +248,74 @@ static void detectBPM(WavInFile *inFile, RunParameters *params)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (params->goalBPM > 0)
|
if (params.goalBPM > 0)
|
||||||
{
|
{
|
||||||
// adjust tempo to given bpm
|
// adjust tempo to given bpm
|
||||||
params->tempoDelta = (params->goalBPM / bpmValue - 1.0f) * 100.0f;
|
params.tempoDelta = (params.goalBPM / bpmValue - 1.0f) * 100.0f;
|
||||||
fprintf(stderr, "The file will be converted to %.1f BPM\n\n", params->goalBPM);
|
fprintf(stderr, "The file will be converted to %.1lf BPM\n\n", params.goalBPM);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void printHelloText()
|
||||||
|
|
||||||
int main(const int nParams, const char * const paramStr[])
|
|
||||||
{
|
{
|
||||||
WavInFile *inFile;
|
SoundTouch soundTouch;
|
||||||
WavOutFile *outFile;
|
fprintf(stderr, _helloText, soundTouch.getVersionString());
|
||||||
RunParameters *params;
|
}
|
||||||
|
|
||||||
|
void ss_main(RunParameters& params)
|
||||||
|
{
|
||||||
|
unique_ptr<WavInFile> inFile;
|
||||||
|
unique_ptr<WavOutFile> outFile;
|
||||||
SoundTouch soundTouch;
|
SoundTouch soundTouch;
|
||||||
|
|
||||||
fprintf(stderr, _helloText, SoundTouch::getVersionString());
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// Parse command line parameters
|
|
||||||
params = new RunParameters(nParams, paramStr);
|
|
||||||
|
|
||||||
// Open input & output files
|
// Open input & output files
|
||||||
openFiles(&inFile, &outFile, params);
|
openFiles(inFile, outFile, params);
|
||||||
|
|
||||||
if (params->detectBPM == TRUE)
|
if (params.detectBPM == true)
|
||||||
{
|
{
|
||||||
// detect sound BPM (and adjust processing parameters
|
// detect sound BPM (and adjust processing parameters
|
||||||
// accordingly if necessary)
|
// accordingly if necessary)
|
||||||
detectBPM(inFile, params);
|
detectBPM(*inFile, params);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Setup the 'SoundTouch' object for processing the sound
|
// Setup the 'SoundTouch' object for processing the sound
|
||||||
setup(&soundTouch, inFile, params);
|
setup(soundTouch, *inFile, params);
|
||||||
|
|
||||||
// clock_t cs = clock(); // for benchmarking processing duration
|
// clock_t cs = clock(); // for benchmarking processing duration
|
||||||
// Process the sound
|
// Process the sound
|
||||||
process(&soundTouch, inFile, outFile);
|
if (inFile && outFile)
|
||||||
|
{
|
||||||
|
process(soundTouch, *inFile, *outFile);
|
||||||
|
}
|
||||||
// clock_t ce = clock(); // for benchmarking processing duration
|
// clock_t ce = clock(); // for benchmarking processing duration
|
||||||
// printf("duration: %lf\n", (double)(ce-cs)/CLOCKS_PER_SEC);
|
// printf("duration: %lf\n", (double)(ce-cs)/CLOCKS_PER_SEC);
|
||||||
|
|
||||||
// Close WAV file handles & dispose of the objects
|
|
||||||
delete inFile;
|
|
||||||
delete outFile;
|
|
||||||
delete params;
|
|
||||||
|
|
||||||
fprintf(stderr, "Done!\n");
|
fprintf(stderr, "Done!\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#if _WIN32
|
||||||
|
int wmain(int argc, const wchar_t* args[])
|
||||||
|
#else
|
||||||
|
int main(int argc, const char* args[])
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
soundstretch::printHelloText();
|
||||||
|
soundstretch::RunParameters params(argc, args);
|
||||||
|
soundstretch::ss_main(params);
|
||||||
|
}
|
||||||
catch (const runtime_error& e)
|
catch (const runtime_error& e)
|
||||||
{
|
{
|
||||||
// An exception occurred during processing, display an error message
|
|
||||||
fprintf(stderr, "%s\n", e.what());
|
fprintf(stderr, "%s\n", e.what());
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
catch (const string& e)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%s\n", e.c_str());
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,137 +0,0 @@
|
|||||||
# Microsoft Developer Studio Project File - Name="soundstretch" - Package Owner=<4>
|
|
||||||
# Microsoft Developer Studio Generated Build File, Format Version 6.00
|
|
||||||
# ** DO NOT EDIT **
|
|
||||||
|
|
||||||
# TARGTYPE "Win32 (x86) Console Application" 0x0103
|
|
||||||
|
|
||||||
CFG=soundstretch - Win32 Debug
|
|
||||||
!MESSAGE This is not a valid makefile. To build this project using NMAKE,
|
|
||||||
!MESSAGE use the Export Makefile command and run
|
|
||||||
!MESSAGE
|
|
||||||
!MESSAGE NMAKE /f "soundstretch.mak".
|
|
||||||
!MESSAGE
|
|
||||||
!MESSAGE You can specify a configuration when running NMAKE
|
|
||||||
!MESSAGE by defining the macro CFG on the command line. For example:
|
|
||||||
!MESSAGE
|
|
||||||
!MESSAGE NMAKE /f "soundstretch.mak" CFG="soundstretch - Win32 Debug"
|
|
||||||
!MESSAGE
|
|
||||||
!MESSAGE Possible choices for configuration are:
|
|
||||||
!MESSAGE
|
|
||||||
!MESSAGE "soundstretch - Win32 Release" (based on "Win32 (x86) Console Application")
|
|
||||||
!MESSAGE "soundstretch - Win32 Debug" (based on "Win32 (x86) Console Application")
|
|
||||||
!MESSAGE
|
|
||||||
|
|
||||||
# Begin Project
|
|
||||||
# PROP AllowPerConfigDependencies 0
|
|
||||||
# PROP Scc_ProjName ""
|
|
||||||
# PROP Scc_LocalPath ""
|
|
||||||
CPP=cl.exe
|
|
||||||
RSC=rc.exe
|
|
||||||
|
|
||||||
!IF "$(CFG)" == "soundstretch - Win32 Release"
|
|
||||||
|
|
||||||
# PROP BASE Use_MFC 0
|
|
||||||
# PROP BASE Use_Debug_Libraries 0
|
|
||||||
# PROP BASE Output_Dir "Release"
|
|
||||||
# PROP BASE Intermediate_Dir "Release"
|
|
||||||
# PROP BASE Target_Dir ""
|
|
||||||
# PROP Use_MFC 0
|
|
||||||
# PROP Use_Debug_Libraries 0
|
|
||||||
# PROP Output_Dir "Release"
|
|
||||||
# PROP Intermediate_Dir "Release"
|
|
||||||
# PROP Ignore_Export_Lib 0
|
|
||||||
# PROP Target_Dir ""
|
|
||||||
# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
|
|
||||||
# ADD CPP /nologo /W3 /GX /Zi /O2 /I "..\..\include" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /FD /c
|
|
||||||
# SUBTRACT CPP /YX
|
|
||||||
# ADD BASE RSC /l 0x40b /d "NDEBUG"
|
|
||||||
# ADD RSC /l 0x40b /d "NDEBUG"
|
|
||||||
BSC32=bscmake.exe
|
|
||||||
# ADD BASE BSC32 /nologo
|
|
||||||
# ADD BSC32 /nologo
|
|
||||||
LINK32=link.exe
|
|
||||||
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
|
|
||||||
# ADD LINK32 SoundTouch.lib /nologo /subsystem:console /profile /map /debug /machine:I386 /libpath:"..\..\lib"
|
|
||||||
# Begin Special Build Tool
|
|
||||||
SOURCE="$(InputPath)"
|
|
||||||
PostBuild_Cmds=copy Release\soundstretch.exe ..\..\bin\
|
|
||||||
# End Special Build Tool
|
|
||||||
|
|
||||||
!ELSEIF "$(CFG)" == "soundstretch - Win32 Debug"
|
|
||||||
|
|
||||||
# PROP BASE Use_MFC 0
|
|
||||||
# PROP BASE Use_Debug_Libraries 1
|
|
||||||
# PROP BASE Output_Dir "Debug"
|
|
||||||
# PROP BASE Intermediate_Dir "Debug"
|
|
||||||
# PROP BASE Target_Dir ""
|
|
||||||
# PROP Use_MFC 0
|
|
||||||
# PROP Use_Debug_Libraries 1
|
|
||||||
# PROP Output_Dir "Debug"
|
|
||||||
# PROP Intermediate_Dir "Debug"
|
|
||||||
# PROP Ignore_Export_Lib 0
|
|
||||||
# PROP Target_Dir ""
|
|
||||||
# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
|
|
||||||
# ADD CPP /nologo /Zp16 /W3 /Gm /GX /ZI /Od /I "..\..\include" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /FR /FD /GZ /c
|
|
||||||
# SUBTRACT CPP /YX
|
|
||||||
# ADD BASE RSC /l 0x40b /d "_DEBUG"
|
|
||||||
# ADD RSC /l 0x40b /d "_DEBUG"
|
|
||||||
BSC32=bscmake.exe
|
|
||||||
# ADD BASE BSC32 /nologo
|
|
||||||
# ADD BSC32 /nologo
|
|
||||||
LINK32=link.exe
|
|
||||||
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
|
|
||||||
# ADD LINK32 SoundTouchD.lib /nologo /subsystem:console /map /debug /machine:I386 /nodefaultlib:"libc" /out:"Debug/soundstretchD.exe" /pdbtype:sept /libpath:"..\..\lib"
|
|
||||||
# SUBTRACT LINK32 /pdb:none
|
|
||||||
# Begin Special Build Tool
|
|
||||||
SOURCE="$(InputPath)"
|
|
||||||
PostBuild_Cmds=copy Debug\soundstretchD.exe ..\..\bin\
|
|
||||||
# End Special Build Tool
|
|
||||||
|
|
||||||
!ENDIF
|
|
||||||
|
|
||||||
# Begin Target
|
|
||||||
|
|
||||||
# Name "soundstretch - Win32 Release"
|
|
||||||
# Name "soundstretch - Win32 Debug"
|
|
||||||
# Begin Group "Source Files"
|
|
||||||
|
|
||||||
# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
|
|
||||||
# Begin Source File
|
|
||||||
|
|
||||||
SOURCE=.\main.cpp
|
|
||||||
# End Source File
|
|
||||||
# Begin Source File
|
|
||||||
|
|
||||||
SOURCE=.\RunParameters.cpp
|
|
||||||
# End Source File
|
|
||||||
# Begin Source File
|
|
||||||
|
|
||||||
SOURCE=.\WavFile.cpp
|
|
||||||
# End Source File
|
|
||||||
# End Group
|
|
||||||
# Begin Group "Header Files"
|
|
||||||
|
|
||||||
# PROP Default_Filter "h;hpp;hxx;hm;inl"
|
|
||||||
# Begin Source File
|
|
||||||
|
|
||||||
SOURCE=.\RunParameters.h
|
|
||||||
# End Source File
|
|
||||||
# Begin Source File
|
|
||||||
|
|
||||||
SOURCE=..\..\..\include\SoundTouch.h
|
|
||||||
# End Source File
|
|
||||||
# Begin Source File
|
|
||||||
|
|
||||||
SOURCE=..\..\..\include\STTypes.h
|
|
||||||
# End Source File
|
|
||||||
# Begin Source File
|
|
||||||
|
|
||||||
SOURCE=.\WavFile.h
|
|
||||||
# End Source File
|
|
||||||
# End Group
|
|
||||||
# Begin Group "Resource Files"
|
|
||||||
|
|
||||||
# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
|
|
||||||
# End Group
|
|
||||||
# End Target
|
|
||||||
# End Project
|
|
||||||
@ -1,44 +0,0 @@
|
|||||||
Microsoft Developer Studio Workspace File, Format Version 6.00
|
|
||||||
# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
|
|
||||||
|
|
||||||
###############################################################################
|
|
||||||
|
|
||||||
Project: "SoundTouch"=..\SoundTouch\SoundTouch.dsp - Package Owner=<4>
|
|
||||||
|
|
||||||
Package=<5>
|
|
||||||
{{{
|
|
||||||
}}}
|
|
||||||
|
|
||||||
Package=<4>
|
|
||||||
{{{
|
|
||||||
}}}
|
|
||||||
|
|
||||||
###############################################################################
|
|
||||||
|
|
||||||
Project: "soundstretch"=.\soundstretch.dsp - Package Owner=<4>
|
|
||||||
|
|
||||||
Package=<5>
|
|
||||||
{{{
|
|
||||||
}}}
|
|
||||||
|
|
||||||
Package=<4>
|
|
||||||
{{{
|
|
||||||
Begin Project Dependency
|
|
||||||
Project_Dep_Name SoundTouch
|
|
||||||
End Project Dependency
|
|
||||||
}}}
|
|
||||||
|
|
||||||
###############################################################################
|
|
||||||
|
|
||||||
Global:
|
|
||||||
|
|
||||||
Package=<5>
|
|
||||||
{{{
|
|
||||||
}}}
|
|
||||||
|
|
||||||
Package=<3>
|
|
||||||
{{{
|
|
||||||
}}}
|
|
||||||
|
|
||||||
###############################################################################
|
|
||||||
|
|
||||||
@ -1,32 +1,37 @@
|
|||||||
Microsoft Visual Studio Solution File, Format Version 8.00
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "soundstretch", "soundstretch.vcproj", "{5AACDFFA-D491-44B8-A332-DA7ACCAAF2AF}"
|
# Visual Studio 14
|
||||||
ProjectSection(ProjectDependencies) = postProject
|
VisualStudioVersion = 14.0.23107.0
|
||||||
{68A5DD20-7057-448B-8FE0-B6AC8D205509} = {68A5DD20-7057-448B-8FE0-B6AC8D205509}
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
EndProjectSection
|
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "soundstretch", "soundstretch.vcxproj", "{5AACDFFA-D491-44B8-A332-DA7ACCAAF2AF}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SoundTouch", "..\SoundTouch\SoundTouch.vcproj", "{68A5DD20-7057-448B-8FE0-B6AC8D205509}"
|
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SoundTouch", "..\SoundTouch\SoundTouch.vcxproj", "{68A5DD20-7057-448B-8FE0-B6AC8D205509}"
|
||||||
ProjectSection(ProjectDependencies) = postProject
|
|
||||||
EndProjectSection
|
|
||||||
EndProject
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfiguration) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug = Debug
|
Debug|Win32 = Debug|Win32
|
||||||
Release = Release
|
Debug|x64 = Debug|x64
|
||||||
|
Release|Win32 = Release|Win32
|
||||||
|
Release|x64 = Release|x64
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(ProjectDependencies) = postSolution
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
|
{5AACDFFA-D491-44B8-A332-DA7ACCAAF2AF}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||||
|
{5AACDFFA-D491-44B8-A332-DA7ACCAAF2AF}.Debug|Win32.Build.0 = Debug|Win32
|
||||||
|
{5AACDFFA-D491-44B8-A332-DA7ACCAAF2AF}.Debug|x64.ActiveCfg = Debug|x64
|
||||||
|
{5AACDFFA-D491-44B8-A332-DA7ACCAAF2AF}.Debug|x64.Build.0 = Debug|x64
|
||||||
|
{5AACDFFA-D491-44B8-A332-DA7ACCAAF2AF}.Release|Win32.ActiveCfg = Release|Win32
|
||||||
|
{5AACDFFA-D491-44B8-A332-DA7ACCAAF2AF}.Release|Win32.Build.0 = Release|Win32
|
||||||
|
{5AACDFFA-D491-44B8-A332-DA7ACCAAF2AF}.Release|x64.ActiveCfg = Release|x64
|
||||||
|
{5AACDFFA-D491-44B8-A332-DA7ACCAAF2AF}.Release|x64.Build.0 = Release|x64
|
||||||
|
{68A5DD20-7057-448B-8FE0-B6AC8D205509}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||||
|
{68A5DD20-7057-448B-8FE0-B6AC8D205509}.Debug|Win32.Build.0 = Debug|Win32
|
||||||
|
{68A5DD20-7057-448B-8FE0-B6AC8D205509}.Debug|x64.ActiveCfg = Debug|x64
|
||||||
|
{68A5DD20-7057-448B-8FE0-B6AC8D205509}.Debug|x64.Build.0 = Debug|x64
|
||||||
|
{68A5DD20-7057-448B-8FE0-B6AC8D205509}.Release|Win32.ActiveCfg = Release|Win32
|
||||||
|
{68A5DD20-7057-448B-8FE0-B6AC8D205509}.Release|Win32.Build.0 = Release|Win32
|
||||||
|
{68A5DD20-7057-448B-8FE0-B6AC8D205509}.Release|x64.ActiveCfg = Release|x64
|
||||||
|
{68A5DD20-7057-448B-8FE0-B6AC8D205509}.Release|x64.Build.0 = Release|x64
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(ProjectConfiguration) = postSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
{5AACDFFA-D491-44B8-A332-DA7ACCAAF2AF}.Debug.ActiveCfg = Debug|Win32
|
HideSolutionNode = FALSE
|
||||||
{5AACDFFA-D491-44B8-A332-DA7ACCAAF2AF}.Debug.Build.0 = Debug|Win32
|
|
||||||
{5AACDFFA-D491-44B8-A332-DA7ACCAAF2AF}.Release.ActiveCfg = Release|Win32
|
|
||||||
{5AACDFFA-D491-44B8-A332-DA7ACCAAF2AF}.Release.Build.0 = Release|Win32
|
|
||||||
{68A5DD20-7057-448B-8FE0-B6AC8D205509}.Debug.ActiveCfg = Debug|Win32
|
|
||||||
{68A5DD20-7057-448B-8FE0-B6AC8D205509}.Debug.Build.0 = Debug|Win32
|
|
||||||
{68A5DD20-7057-448B-8FE0-B6AC8D205509}.Release.ActiveCfg = Release|Win32
|
|
||||||
{68A5DD20-7057-448B-8FE0-B6AC8D205509}.Release.Build.0 = Release|Win32
|
|
||||||
EndGlobalSection
|
|
||||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
|
||||||
EndGlobalSection
|
|
||||||
GlobalSection(ExtensibilityAddIns) = postSolution
|
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
EndGlobal
|
EndGlobal
|
||||||
|
|||||||
@ -1,235 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="Windows-1252"?>
|
|
||||||
<VisualStudioProject
|
|
||||||
ProjectType="Visual C++"
|
|
||||||
Version="7.10"
|
|
||||||
Name="soundstretch"
|
|
||||||
SccProjectName=""
|
|
||||||
SccLocalPath="">
|
|
||||||
<Platforms>
|
|
||||||
<Platform
|
|
||||||
Name="Win32"/>
|
|
||||||
</Platforms>
|
|
||||||
<Configurations>
|
|
||||||
<Configuration
|
|
||||||
Name="Debug|Win32"
|
|
||||||
OutputDirectory=".\Debug"
|
|
||||||
IntermediateDirectory=".\Debug"
|
|
||||||
ConfigurationType="1"
|
|
||||||
UseOfMFC="0"
|
|
||||||
ATLMinimizesCRunTimeLibraryUsage="FALSE"
|
|
||||||
CharacterSet="2">
|
|
||||||
<Tool
|
|
||||||
Name="VCCLCompilerTool"
|
|
||||||
Optimization="0"
|
|
||||||
AdditionalIncludeDirectories="..\..\include"
|
|
||||||
PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS"
|
|
||||||
BasicRuntimeChecks="3"
|
|
||||||
RuntimeLibrary="5"
|
|
||||||
StructMemberAlignment="5"
|
|
||||||
PrecompiledHeaderFile=".\Debug/soundstretch.pch"
|
|
||||||
AssemblerListingLocation=".\Debug/"
|
|
||||||
ObjectFile=".\Debug/"
|
|
||||||
ProgramDataBaseFileName=".\Debug/"
|
|
||||||
BrowseInformation="1"
|
|
||||||
WarningLevel="3"
|
|
||||||
SuppressStartupBanner="TRUE"
|
|
||||||
DebugInformationFormat="4"
|
|
||||||
CompileAs="0"/>
|
|
||||||
<Tool
|
|
||||||
Name="VCCustomBuildTool"/>
|
|
||||||
<Tool
|
|
||||||
Name="VCLinkerTool"
|
|
||||||
AdditionalDependencies="SoundTouchD.lib"
|
|
||||||
OutputFile="Debug/soundstretchD.exe"
|
|
||||||
LinkIncremental="2"
|
|
||||||
SuppressStartupBanner="TRUE"
|
|
||||||
AdditionalLibraryDirectories="..\..\lib"
|
|
||||||
IgnoreDefaultLibraryNames="libc"
|
|
||||||
GenerateDebugInformation="TRUE"
|
|
||||||
ProgramDatabaseFile=".\Debug/soundstretchD.pdb"
|
|
||||||
GenerateMapFile="TRUE"
|
|
||||||
MapFileName=".\Debug/soundstretchD.map"
|
|
||||||
SubSystem="1"
|
|
||||||
TargetMachine="1"/>
|
|
||||||
<Tool
|
|
||||||
Name="VCMIDLTool"
|
|
||||||
TypeLibraryName=".\Debug/soundstretch.tlb"
|
|
||||||
HeaderFileName=""/>
|
|
||||||
<Tool
|
|
||||||
Name="VCPostBuildEventTool"
|
|
||||||
CommandLine="copy Debug\soundstretchD.exe ..\..\bin\"/>
|
|
||||||
<Tool
|
|
||||||
Name="VCPreBuildEventTool"/>
|
|
||||||
<Tool
|
|
||||||
Name="VCPreLinkEventTool"/>
|
|
||||||
<Tool
|
|
||||||
Name="VCResourceCompilerTool"
|
|
||||||
PreprocessorDefinitions="_DEBUG"
|
|
||||||
Culture="1035"/>
|
|
||||||
<Tool
|
|
||||||
Name="VCWebServiceProxyGeneratorTool"/>
|
|
||||||
<Tool
|
|
||||||
Name="VCXMLDataGeneratorTool"/>
|
|
||||||
<Tool
|
|
||||||
Name="VCWebDeploymentTool"/>
|
|
||||||
<Tool
|
|
||||||
Name="VCManagedWrapperGeneratorTool"/>
|
|
||||||
<Tool
|
|
||||||
Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
|
|
||||||
</Configuration>
|
|
||||||
<Configuration
|
|
||||||
Name="Release|Win32"
|
|
||||||
OutputDirectory=".\Release"
|
|
||||||
IntermediateDirectory=".\Release"
|
|
||||||
ConfigurationType="1"
|
|
||||||
UseOfMFC="0"
|
|
||||||
ATLMinimizesCRunTimeLibraryUsage="FALSE"
|
|
||||||
CharacterSet="2">
|
|
||||||
<Tool
|
|
||||||
Name="VCCLCompilerTool"
|
|
||||||
Optimization="3"
|
|
||||||
GlobalOptimizations="FALSE"
|
|
||||||
InlineFunctionExpansion="2"
|
|
||||||
EnableIntrinsicFunctions="TRUE"
|
|
||||||
AdditionalIncludeDirectories="..\..\include"
|
|
||||||
PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS"
|
|
||||||
StringPooling="TRUE"
|
|
||||||
RuntimeLibrary="4"
|
|
||||||
EnableFunctionLevelLinking="TRUE"
|
|
||||||
PrecompiledHeaderFile=".\Release/soundstretch.pch"
|
|
||||||
AssemblerListingLocation=".\Release/"
|
|
||||||
ObjectFile=".\Release/"
|
|
||||||
ProgramDataBaseFileName=".\Release/"
|
|
||||||
WarningLevel="3"
|
|
||||||
SuppressStartupBanner="TRUE"
|
|
||||||
DebugInformationFormat="0"
|
|
||||||
CompileAs="0"/>
|
|
||||||
<Tool
|
|
||||||
Name="VCCustomBuildTool"/>
|
|
||||||
<Tool
|
|
||||||
Name="VCLinkerTool"
|
|
||||||
AdditionalDependencies="SoundTouch.lib"
|
|
||||||
OutputFile=".\Release/soundstretch.exe"
|
|
||||||
LinkIncremental="1"
|
|
||||||
SuppressStartupBanner="TRUE"
|
|
||||||
AdditionalLibraryDirectories="..\..\lib"
|
|
||||||
GenerateDebugInformation="FALSE"
|
|
||||||
GenerateMapFile="TRUE"
|
|
||||||
MapFileName=".\Release/soundstretch.map"
|
|
||||||
SubSystem="1"
|
|
||||||
TargetMachine="1"/>
|
|
||||||
<Tool
|
|
||||||
Name="VCMIDLTool"
|
|
||||||
TypeLibraryName=".\Release/soundstretch.tlb"
|
|
||||||
HeaderFileName=""/>
|
|
||||||
<Tool
|
|
||||||
Name="VCPostBuildEventTool"
|
|
||||||
CommandLine="copy Release\soundstretch.exe ..\..\bin\"/>
|
|
||||||
<Tool
|
|
||||||
Name="VCPreBuildEventTool"/>
|
|
||||||
<Tool
|
|
||||||
Name="VCPreLinkEventTool"/>
|
|
||||||
<Tool
|
|
||||||
Name="VCResourceCompilerTool"
|
|
||||||
PreprocessorDefinitions="NDEBUG"
|
|
||||||
Culture="1035"/>
|
|
||||||
<Tool
|
|
||||||
Name="VCWebServiceProxyGeneratorTool"/>
|
|
||||||
<Tool
|
|
||||||
Name="VCXMLDataGeneratorTool"/>
|
|
||||||
<Tool
|
|
||||||
Name="VCWebDeploymentTool"/>
|
|
||||||
<Tool
|
|
||||||
Name="VCManagedWrapperGeneratorTool"/>
|
|
||||||
<Tool
|
|
||||||
Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
|
|
||||||
</Configuration>
|
|
||||||
</Configurations>
|
|
||||||
<References>
|
|
||||||
</References>
|
|
||||||
<Files>
|
|
||||||
<Filter
|
|
||||||
Name="Source Files"
|
|
||||||
Filter="cpp;c;cxx;rc;def;r;odl;idl;hpj;bat">
|
|
||||||
<File
|
|
||||||
RelativePath="main.cpp">
|
|
||||||
<FileConfiguration
|
|
||||||
Name="Debug|Win32">
|
|
||||||
<Tool
|
|
||||||
Name="VCCLCompilerTool"
|
|
||||||
Optimization="0"
|
|
||||||
AdditionalIncludeDirectories=""
|
|
||||||
PreprocessorDefinitions=""
|
|
||||||
BasicRuntimeChecks="3"
|
|
||||||
BrowseInformation="1"/>
|
|
||||||
</FileConfiguration>
|
|
||||||
<FileConfiguration
|
|
||||||
Name="Release|Win32">
|
|
||||||
<Tool
|
|
||||||
Name="VCCLCompilerTool"
|
|
||||||
Optimization="2"
|
|
||||||
AdditionalIncludeDirectories=""
|
|
||||||
PreprocessorDefinitions=""/>
|
|
||||||
</FileConfiguration>
|
|
||||||
</File>
|
|
||||||
<File
|
|
||||||
RelativePath="RunParameters.cpp">
|
|
||||||
<FileConfiguration
|
|
||||||
Name="Debug|Win32">
|
|
||||||
<Tool
|
|
||||||
Name="VCCLCompilerTool"
|
|
||||||
Optimization="0"
|
|
||||||
AdditionalIncludeDirectories=""
|
|
||||||
PreprocessorDefinitions=""
|
|
||||||
BasicRuntimeChecks="3"
|
|
||||||
BrowseInformation="1"/>
|
|
||||||
</FileConfiguration>
|
|
||||||
<FileConfiguration
|
|
||||||
Name="Release|Win32">
|
|
||||||
<Tool
|
|
||||||
Name="VCCLCompilerTool"
|
|
||||||
Optimization="2"
|
|
||||||
AdditionalIncludeDirectories=""
|
|
||||||
PreprocessorDefinitions=""/>
|
|
||||||
</FileConfiguration>
|
|
||||||
</File>
|
|
||||||
<File
|
|
||||||
RelativePath="WavFile.cpp">
|
|
||||||
<FileConfiguration
|
|
||||||
Name="Debug|Win32">
|
|
||||||
<Tool
|
|
||||||
Name="VCCLCompilerTool"
|
|
||||||
Optimization="0"
|
|
||||||
AdditionalIncludeDirectories=""
|
|
||||||
PreprocessorDefinitions=""
|
|
||||||
BasicRuntimeChecks="3"
|
|
||||||
BrowseInformation="1"/>
|
|
||||||
</FileConfiguration>
|
|
||||||
<FileConfiguration
|
|
||||||
Name="Release|Win32">
|
|
||||||
<Tool
|
|
||||||
Name="VCCLCompilerTool"
|
|
||||||
Optimization="2"
|
|
||||||
AdditionalIncludeDirectories=""
|
|
||||||
PreprocessorDefinitions=""/>
|
|
||||||
</FileConfiguration>
|
|
||||||
</File>
|
|
||||||
</Filter>
|
|
||||||
<Filter
|
|
||||||
Name="Header Files"
|
|
||||||
Filter="h;hpp;hxx;hm;inl">
|
|
||||||
<File
|
|
||||||
RelativePath="RunParameters.h">
|
|
||||||
</File>
|
|
||||||
<File
|
|
||||||
RelativePath="WavFile.h">
|
|
||||||
</File>
|
|
||||||
</Filter>
|
|
||||||
<Filter
|
|
||||||
Name="Resource Files"
|
|
||||||
Filter="ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe">
|
|
||||||
</Filter>
|
|
||||||
</Files>
|
|
||||||
<Globals>
|
|
||||||
</Globals>
|
|
||||||
</VisualStudioProject>
|
|
||||||
333
source/SoundStretch/soundstretch.vcxproj
Normal file
333
source/SoundStretch/soundstretch.vcxproj
Normal file
@ -0,0 +1,333 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<ItemGroup Label="ProjectConfigurations">
|
||||||
|
<ProjectConfiguration Include="Debug|Win32">
|
||||||
|
<Configuration>Debug</Configuration>
|
||||||
|
<Platform>Win32</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
<ProjectConfiguration Include="Debug|x64">
|
||||||
|
<Configuration>Debug</Configuration>
|
||||||
|
<Platform>x64</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
<ProjectConfiguration Include="Release|Win32">
|
||||||
|
<Configuration>Release</Configuration>
|
||||||
|
<Platform>Win32</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
<ProjectConfiguration Include="Release|x64">
|
||||||
|
<Configuration>Release</Configuration>
|
||||||
|
<Platform>x64</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
</ItemGroup>
|
||||||
|
<PropertyGroup Label="Globals">
|
||||||
|
<ProjectGuid>{5AACDFFA-D491-44B8-A332-DA7ACCAAF2AF}</ProjectGuid>
|
||||||
|
<RootNamespace>soundstretch</RootNamespace>
|
||||||
|
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||||
|
<ConfigurationType>Application</ConfigurationType>
|
||||||
|
<PlatformToolset>v142</PlatformToolset>
|
||||||
|
<UseOfMfc>false</UseOfMfc>
|
||||||
|
<CharacterSet>Unicode</CharacterSet>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||||
|
<ConfigurationType>Application</ConfigurationType>
|
||||||
|
<PlatformToolset>v142</PlatformToolset>
|
||||||
|
<UseOfMfc>false</UseOfMfc>
|
||||||
|
<CharacterSet>Unicode</CharacterSet>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||||
|
<ConfigurationType>Application</ConfigurationType>
|
||||||
|
<PlatformToolset>v142</PlatformToolset>
|
||||||
|
<UseOfMfc>false</UseOfMfc>
|
||||||
|
<CharacterSet>Unicode</CharacterSet>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||||
|
<ConfigurationType>Application</ConfigurationType>
|
||||||
|
<PlatformToolset>v142</PlatformToolset>
|
||||||
|
<UseOfMfc>false</UseOfMfc>
|
||||||
|
<CharacterSet>Unicode</CharacterSet>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||||
|
<ImportGroup Label="ExtensionSettings">
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
|
||||||
|
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||||
|
<Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC71.props" />
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
|
||||||
|
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||||
|
<Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC71.props" />
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
|
||||||
|
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||||
|
<Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC71.props" />
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
|
||||||
|
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||||
|
<Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC71.props" />
|
||||||
|
</ImportGroup>
|
||||||
|
<PropertyGroup Label="UserMacros" />
|
||||||
|
<PropertyGroup>
|
||||||
|
<_ProjectFileVersion>14.0.23107.0</_ProjectFileVersion>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||||
|
<OutDir>$(Platform)\$(Configuration)\</OutDir>
|
||||||
|
<IntDir>$(Platform)\$(Configuration)\</IntDir>
|
||||||
|
<LinkIncremental>true</LinkIncremental>
|
||||||
|
<TargetName>$(ProjectName)D</TargetName>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||||
|
<OutDir>$(Platform)\$(Configuration)\</OutDir>
|
||||||
|
<IntDir>$(Platform)\$(Configuration)\</IntDir>
|
||||||
|
<LinkIncremental>false</LinkIncremental>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||||
|
<OutDir>$(Platform)\$(Configuration)\</OutDir>
|
||||||
|
<IntDir>$(Platform)\$(Configuration)\</IntDir>
|
||||||
|
<LinkIncremental>true</LinkIncremental>
|
||||||
|
<TargetName>$(ProjectName)D_x64</TargetName>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||||
|
<OutDir>$(Platform)\$(Configuration)\</OutDir>
|
||||||
|
<IntDir>$(Platform)\$(Configuration)\</IntDir>
|
||||||
|
<LinkIncremental>false</LinkIncremental>
|
||||||
|
<TargetName>$(ProjectName)_x64</TargetName>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||||
|
<Midl>
|
||||||
|
<TypeLibraryName>.\Debug/soundstretch.tlb</TypeLibraryName>
|
||||||
|
<HeaderFileName />
|
||||||
|
</Midl>
|
||||||
|
<ClCompile>
|
||||||
|
<Optimization>Disabled</Optimization>
|
||||||
|
<AdditionalIncludeDirectories>..\..\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
|
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
|
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
|
||||||
|
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
|
||||||
|
<StructMemberAlignment>16Bytes</StructMemberAlignment>
|
||||||
|
<FloatingPointModel>Fast</FloatingPointModel>
|
||||||
|
<PrecompiledHeaderOutputFile>$(OutDir)$(TargetName).pch</PrecompiledHeaderOutputFile>
|
||||||
|
<AssemblerListingLocation>$(OutDir)</AssemblerListingLocation>
|
||||||
|
<ObjectFileName>$(OutDir)</ObjectFileName>
|
||||||
|
<ProgramDataBaseFileName>$(OutDir)</ProgramDataBaseFileName>
|
||||||
|
<BrowseInformation>true</BrowseInformation>
|
||||||
|
<WarningLevel>Level3</WarningLevel>
|
||||||
|
<SuppressStartupBanner>true</SuppressStartupBanner>
|
||||||
|
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||||
|
<CompileAs>Default</CompileAs>
|
||||||
|
<EnableEnhancedInstructionSet>StreamingSIMDExtensions2</EnableEnhancedInstructionSet>
|
||||||
|
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||||
|
</ClCompile>
|
||||||
|
<ResourceCompile>
|
||||||
|
<PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
|
<Culture>0x040b</Culture>
|
||||||
|
</ResourceCompile>
|
||||||
|
<Link>
|
||||||
|
<AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
|
||||||
|
<OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
|
||||||
|
<SuppressStartupBanner>true</SuppressStartupBanner>
|
||||||
|
<AdditionalLibraryDirectories>..\..\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||||
|
<IgnoreSpecificDefaultLibraries>libc;%(IgnoreSpecificDefaultLibraries)</IgnoreSpecificDefaultLibraries>
|
||||||
|
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||||
|
<ProgramDatabaseFile>$(OutDir)$(TargetName).pdb</ProgramDatabaseFile>
|
||||||
|
<GenerateMapFile>true</GenerateMapFile>
|
||||||
|
<MapFileName>$(OutDir)$(TargetName).map</MapFileName>
|
||||||
|
<SubSystem>Console</SubSystem>
|
||||||
|
<DataExecutionPrevention />
|
||||||
|
</Link>
|
||||||
|
<PostBuildEvent>
|
||||||
|
<Command>if not exist ..\..\bin mkdir ..\..\bin
|
||||||
|
copy $(OutDir)$(TargetName)$(TargetExt) ..\..\bin\</Command>
|
||||||
|
</PostBuildEvent>
|
||||||
|
</ItemDefinitionGroup>
|
||||||
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||||
|
<Midl>
|
||||||
|
<TypeLibraryName>.\Release/soundstretch.tlb</TypeLibraryName>
|
||||||
|
<HeaderFileName />
|
||||||
|
</Midl>
|
||||||
|
<ClCompile>
|
||||||
|
<Optimization>Full</Optimization>
|
||||||
|
<InlineFunctionExpansion>AnySuitable</InlineFunctionExpansion>
|
||||||
|
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||||
|
<AdditionalIncludeDirectories>..\..\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
|
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
|
<StringPooling>true</StringPooling>
|
||||||
|
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||||
|
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||||
|
<FloatingPointModel>Fast</FloatingPointModel>
|
||||||
|
<PrecompiledHeaderOutputFile>$(OutDir)$(TargetName).pch</PrecompiledHeaderOutputFile>
|
||||||
|
<AssemblerListingLocation>$(OutDir)</AssemblerListingLocation>
|
||||||
|
<ObjectFileName>$(OutDir)</ObjectFileName>
|
||||||
|
<ProgramDataBaseFileName>$(OutDir)</ProgramDataBaseFileName>
|
||||||
|
<WarningLevel>Level3</WarningLevel>
|
||||||
|
<SuppressStartupBanner>true</SuppressStartupBanner>
|
||||||
|
<DebugInformationFormat />
|
||||||
|
<CompileAs>Default</CompileAs>
|
||||||
|
<EnableEnhancedInstructionSet>StreamingSIMDExtensions2</EnableEnhancedInstructionSet>
|
||||||
|
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||||
|
</ClCompile>
|
||||||
|
<ResourceCompile>
|
||||||
|
<PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
|
<Culture>0x040b</Culture>
|
||||||
|
</ResourceCompile>
|
||||||
|
<Link>
|
||||||
|
<AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
|
||||||
|
<OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
|
||||||
|
<SuppressStartupBanner>true</SuppressStartupBanner>
|
||||||
|
<AdditionalLibraryDirectories>..\..\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||||
|
<GenerateDebugInformation>false</GenerateDebugInformation>
|
||||||
|
<GenerateMapFile>true</GenerateMapFile>
|
||||||
|
<MapFileName>$(OutDir)$(TargetName).map</MapFileName>
|
||||||
|
<SubSystem>Console</SubSystem>
|
||||||
|
<DataExecutionPrevention />
|
||||||
|
</Link>
|
||||||
|
<PostBuildEvent>
|
||||||
|
<Command>if not exist ..\..\bin mkdir ..\..\bin
|
||||||
|
copy $(OutDir)$(TargetName)$(TargetExt) ..\..\bin\</Command>
|
||||||
|
</PostBuildEvent>
|
||||||
|
</ItemDefinitionGroup>
|
||||||
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||||
|
<Midl>
|
||||||
|
<TargetEnvironment>X64</TargetEnvironment>
|
||||||
|
<TypeLibraryName>.\Debug/soundstretch.tlb</TypeLibraryName>
|
||||||
|
<HeaderFileName />
|
||||||
|
</Midl>
|
||||||
|
<ClCompile>
|
||||||
|
<Optimization>Disabled</Optimization>
|
||||||
|
<AdditionalIncludeDirectories>..\..\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
|
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
|
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
|
||||||
|
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
|
||||||
|
<StructMemberAlignment>16Bytes</StructMemberAlignment>
|
||||||
|
<FloatingPointModel>Fast</FloatingPointModel>
|
||||||
|
<PrecompiledHeaderOutputFile>$(OutDir)$(TargetName).pch</PrecompiledHeaderOutputFile>
|
||||||
|
<AssemblerListingLocation>$(OutDir)</AssemblerListingLocation>
|
||||||
|
<ObjectFileName>$(OutDir)</ObjectFileName>
|
||||||
|
<ProgramDataBaseFileName>$(OutDir)</ProgramDataBaseFileName>
|
||||||
|
<BrowseInformation>true</BrowseInformation>
|
||||||
|
<WarningLevel>Level3</WarningLevel>
|
||||||
|
<SuppressStartupBanner>true</SuppressStartupBanner>
|
||||||
|
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||||
|
<CompileAs>Default</CompileAs>
|
||||||
|
<EnableEnhancedInstructionSet>
|
||||||
|
</EnableEnhancedInstructionSet>
|
||||||
|
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||||
|
</ClCompile>
|
||||||
|
<ResourceCompile>
|
||||||
|
<PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
|
<Culture>0x040b</Culture>
|
||||||
|
</ResourceCompile>
|
||||||
|
<Link>
|
||||||
|
<AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
|
||||||
|
<OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
|
||||||
|
<SuppressStartupBanner>true</SuppressStartupBanner>
|
||||||
|
<AdditionalLibraryDirectories>..\..\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||||
|
<IgnoreSpecificDefaultLibraries>libc;%(IgnoreSpecificDefaultLibraries)</IgnoreSpecificDefaultLibraries>
|
||||||
|
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||||
|
<ProgramDatabaseFile>$(OutDir)$(TargetName).pdb</ProgramDatabaseFile>
|
||||||
|
<GenerateMapFile>true</GenerateMapFile>
|
||||||
|
<MapFileName>$(OutDir)$(TargetName).map</MapFileName>
|
||||||
|
<SubSystem>Console</SubSystem>
|
||||||
|
<DataExecutionPrevention />
|
||||||
|
</Link>
|
||||||
|
<PostBuildEvent>
|
||||||
|
<Command>if not exist ..\..\bin mkdir ..\..\bin
|
||||||
|
copy $(OutDir)$(TargetName)$(TargetExt) ..\..\bin\</Command>
|
||||||
|
</PostBuildEvent>
|
||||||
|
</ItemDefinitionGroup>
|
||||||
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||||
|
<Midl>
|
||||||
|
<TargetEnvironment>X64</TargetEnvironment>
|
||||||
|
<TypeLibraryName>.\Release/soundstretch.tlb</TypeLibraryName>
|
||||||
|
<HeaderFileName />
|
||||||
|
</Midl>
|
||||||
|
<ClCompile>
|
||||||
|
<Optimization>Full</Optimization>
|
||||||
|
<InlineFunctionExpansion>AnySuitable</InlineFunctionExpansion>
|
||||||
|
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||||
|
<AdditionalIncludeDirectories>..\..\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
|
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
|
<StringPooling>true</StringPooling>
|
||||||
|
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||||
|
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||||
|
<FloatingPointModel>Fast</FloatingPointModel>
|
||||||
|
<PrecompiledHeaderOutputFile>$(OutDir)$(TargetName).pch</PrecompiledHeaderOutputFile>
|
||||||
|
<AssemblerListingLocation>$(OutDir)</AssemblerListingLocation>
|
||||||
|
<ObjectFileName>$(OutDir)</ObjectFileName>
|
||||||
|
<ProgramDataBaseFileName>$(OutDir)</ProgramDataBaseFileName>
|
||||||
|
<WarningLevel>Level3</WarningLevel>
|
||||||
|
<SuppressStartupBanner>true</SuppressStartupBanner>
|
||||||
|
<DebugInformationFormat />
|
||||||
|
<CompileAs>Default</CompileAs>
|
||||||
|
<EnableEnhancedInstructionSet>
|
||||||
|
</EnableEnhancedInstructionSet>
|
||||||
|
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||||
|
</ClCompile>
|
||||||
|
<ResourceCompile>
|
||||||
|
<PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
|
<Culture>0x040b</Culture>
|
||||||
|
</ResourceCompile>
|
||||||
|
<Link>
|
||||||
|
<AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
|
||||||
|
<OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
|
||||||
|
<SuppressStartupBanner>true</SuppressStartupBanner>
|
||||||
|
<AdditionalLibraryDirectories>..\..\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||||
|
<GenerateDebugInformation>false</GenerateDebugInformation>
|
||||||
|
<GenerateMapFile>true</GenerateMapFile>
|
||||||
|
<MapFileName>$(OutDir)$(TargetName).map</MapFileName>
|
||||||
|
<SubSystem>Console</SubSystem>
|
||||||
|
<DataExecutionPrevention />
|
||||||
|
</Link>
|
||||||
|
<PostBuildEvent>
|
||||||
|
<Command>if not exist ..\..\bin mkdir ..\..\bin
|
||||||
|
copy $(OutDir)$(TargetName)$(TargetExt) ..\..\bin\</Command>
|
||||||
|
</PostBuildEvent>
|
||||||
|
</ItemDefinitionGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ClCompile Include="main.cpp">
|
||||||
|
<Optimization Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Disabled</Optimization>
|
||||||
|
<BasicRuntimeChecks Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">EnableFastChecks</BasicRuntimeChecks>
|
||||||
|
<BrowseInformation Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</BrowseInformation>
|
||||||
|
<Optimization Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Disabled</Optimization>
|
||||||
|
<BasicRuntimeChecks Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">EnableFastChecks</BasicRuntimeChecks>
|
||||||
|
<BrowseInformation Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</BrowseInformation>
|
||||||
|
<Optimization Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">MaxSpeed</Optimization>
|
||||||
|
<Optimization Condition="'$(Configuration)|$(Platform)'=='Release|x64'">MaxSpeed</Optimization>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="RunParameters.cpp">
|
||||||
|
<Optimization Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Disabled</Optimization>
|
||||||
|
<BasicRuntimeChecks Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">EnableFastChecks</BasicRuntimeChecks>
|
||||||
|
<BrowseInformation Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</BrowseInformation>
|
||||||
|
<Optimization Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Disabled</Optimization>
|
||||||
|
<BasicRuntimeChecks Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">EnableFastChecks</BasicRuntimeChecks>
|
||||||
|
<BrowseInformation Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</BrowseInformation>
|
||||||
|
<Optimization Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">MaxSpeed</Optimization>
|
||||||
|
<Optimization Condition="'$(Configuration)|$(Platform)'=='Release|x64'">MaxSpeed</Optimization>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="WavFile.cpp">
|
||||||
|
<Optimization Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Disabled</Optimization>
|
||||||
|
<BasicRuntimeChecks Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">EnableFastChecks</BasicRuntimeChecks>
|
||||||
|
<BrowseInformation Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</BrowseInformation>
|
||||||
|
<Optimization Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Disabled</Optimization>
|
||||||
|
<BasicRuntimeChecks Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">EnableFastChecks</BasicRuntimeChecks>
|
||||||
|
<BrowseInformation Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</BrowseInformation>
|
||||||
|
<Optimization Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">MaxSpeed</Optimization>
|
||||||
|
<Optimization Condition="'$(Configuration)|$(Platform)'=='Release|x64'">MaxSpeed</Optimization>
|
||||||
|
</ClCompile>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ClInclude Include="RunParameters.h" />
|
||||||
|
<ClInclude Include="SS_CharTypes.h" />
|
||||||
|
<ClInclude Include="WavFile.h" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\SoundTouch\SoundTouch.vcxproj">
|
||||||
|
<Project>{68a5dd20-7057-448b-8fe0-b6ac8d205509}</Project>
|
||||||
|
<ReferenceOutputAssembly>false</ReferenceOutputAssembly>
|
||||||
|
</ProjectReference>
|
||||||
|
</ItemGroup>
|
||||||
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||||
|
<ImportGroup Label="ExtensionTargets">
|
||||||
|
</ImportGroup>
|
||||||
|
</Project>
|
||||||
@ -12,13 +12,6 @@
|
|||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Last changed : $Date$
|
|
||||||
// File revision : $Revision: 4 $
|
|
||||||
//
|
|
||||||
// $Id$
|
|
||||||
//
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// License :
|
// License :
|
||||||
//
|
//
|
||||||
// SoundTouch audio processing library
|
// SoundTouch audio processing library
|
||||||
@ -49,9 +42,32 @@
|
|||||||
|
|
||||||
using namespace soundtouch;
|
using namespace soundtouch;
|
||||||
|
|
||||||
#define PI 3.141592655357989
|
#define PI 3.14159265358979323846
|
||||||
#define TWOPI (2 * PI)
|
#define TWOPI (2 * PI)
|
||||||
|
|
||||||
|
// define this to save AA filter coefficients to a file
|
||||||
|
// #define _DEBUG_SAVE_AAFILTER_COEFFICIENTS 1
|
||||||
|
|
||||||
|
#ifdef _DEBUG_SAVE_AAFILTER_COEFFICIENTS
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
static void _DEBUG_SAVE_AAFIR_COEFFS(SAMPLETYPE *coeffs, int len)
|
||||||
|
{
|
||||||
|
FILE *fptr = fopen("aa_filter_coeffs.txt", "wt");
|
||||||
|
if (fptr == nullptr) return;
|
||||||
|
|
||||||
|
for (int i = 0; i < len; i ++)
|
||||||
|
{
|
||||||
|
double temp = coeffs[i];
|
||||||
|
fprintf(fptr, "%lf\n", temp);
|
||||||
|
}
|
||||||
|
fclose(fptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
#define _DEBUG_SAVE_AAFIR_COEFFS(x, y)
|
||||||
|
#endif
|
||||||
|
|
||||||
/*****************************************************************************
|
/*****************************************************************************
|
||||||
*
|
*
|
||||||
* Implementation of the class 'AAFilter'
|
* Implementation of the class 'AAFilter'
|
||||||
@ -66,14 +82,12 @@ AAFilter::AAFilter(uint len)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
AAFilter::~AAFilter()
|
AAFilter::~AAFilter()
|
||||||
{
|
{
|
||||||
delete pFIR;
|
delete pFIR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Sets new anti-alias filter cut-off edge frequency, scaled to
|
// Sets new anti-alias filter cut-off edge frequency, scaled to
|
||||||
// sampling frequency (nyquist frequency = 0.5).
|
// sampling frequency (nyquist frequency = 0.5).
|
||||||
// The filter will cut frequencies higher than the given frequency.
|
// The filter will cut frequencies higher than the given frequency.
|
||||||
@ -84,7 +98,6 @@ void AAFilter::setCutoffFreq(double newCutoffFreq)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Sets number of FIR filter taps
|
// Sets number of FIR filter taps
|
||||||
void AAFilter::setLength(uint newLength)
|
void AAFilter::setLength(uint newLength)
|
||||||
{
|
{
|
||||||
@ -93,13 +106,12 @@ void AAFilter::setLength(uint newLength)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Calculates coefficients for a low-pass FIR filter using Hamming window
|
// Calculates coefficients for a low-pass FIR filter using Hamming window
|
||||||
void AAFilter::calculateCoeffs()
|
void AAFilter::calculateCoeffs()
|
||||||
{
|
{
|
||||||
uint i;
|
uint i;
|
||||||
double cntTemp, temp, tempCoeff,h, w;
|
double cntTemp, temp, tempCoeff,h, w;
|
||||||
double fc2, wc;
|
double wc;
|
||||||
double scaleCoeff, sum;
|
double scaleCoeff, sum;
|
||||||
double *work;
|
double *work;
|
||||||
SAMPLETYPE *coeffs;
|
SAMPLETYPE *coeffs;
|
||||||
@ -112,8 +124,7 @@ void AAFilter::calculateCoeffs()
|
|||||||
work = new double[length];
|
work = new double[length];
|
||||||
coeffs = new SAMPLETYPE[length];
|
coeffs = new SAMPLETYPE[length];
|
||||||
|
|
||||||
fc2 = 2.0 * cutoffFreq;
|
wc = 2.0 * PI * cutoffFreq;
|
||||||
wc = PI * fc2;
|
|
||||||
tempCoeff = TWOPI / (double)length;
|
tempCoeff = TWOPI / (double)length;
|
||||||
|
|
||||||
sum = 0;
|
sum = 0;
|
||||||
@ -124,7 +135,7 @@ void AAFilter::calculateCoeffs()
|
|||||||
temp = cntTemp * wc;
|
temp = cntTemp * wc;
|
||||||
if (temp != 0)
|
if (temp != 0)
|
||||||
{
|
{
|
||||||
h = fc2 * sin(temp) / temp; // sinc function
|
h = sin(temp) / temp; // sinc function
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -153,8 +164,8 @@ void AAFilter::calculateCoeffs()
|
|||||||
|
|
||||||
for (i = 0; i < length; i ++)
|
for (i = 0; i < length; i ++)
|
||||||
{
|
{
|
||||||
// scale & round to nearest integer
|
|
||||||
temp = work[i] * scaleCoeff;
|
temp = work[i] * scaleCoeff;
|
||||||
|
// scale & round to nearest integer
|
||||||
temp += (temp >= 0) ? 0.5 : -0.5;
|
temp += (temp >= 0) ? 0.5 : -0.5;
|
||||||
// ensure no overfloods
|
// ensure no overfloods
|
||||||
assert(temp >= -32768 && temp <= 32767);
|
assert(temp >= -32768 && temp <= 32767);
|
||||||
@ -164,6 +175,8 @@ void AAFilter::calculateCoeffs()
|
|||||||
// Set coefficients. Use divide factor 14 => divide result by 2^14 = 16384
|
// Set coefficients. Use divide factor 14 => divide result by 2^14 = 16384
|
||||||
pFIR->setCoefficients(coeffs, length, 14);
|
pFIR->setCoefficients(coeffs, length, 14);
|
||||||
|
|
||||||
|
_DEBUG_SAVE_AAFIR_COEFFS(coeffs, length);
|
||||||
|
|
||||||
delete[] work;
|
delete[] work;
|
||||||
delete[] coeffs;
|
delete[] coeffs;
|
||||||
}
|
}
|
||||||
@ -178,6 +191,31 @@ uint AAFilter::evaluate(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Applies the filter to the given src & dest pipes, so that processed amount of
|
||||||
|
/// samples get removed from src, and produced amount added to dest
|
||||||
|
/// Note : The amount of outputted samples is by value of 'filter length'
|
||||||
|
/// smaller than the amount of input samples.
|
||||||
|
uint AAFilter::evaluate(FIFOSampleBuffer &dest, FIFOSampleBuffer &src) const
|
||||||
|
{
|
||||||
|
SAMPLETYPE *pdest;
|
||||||
|
const SAMPLETYPE *psrc;
|
||||||
|
uint numSrcSamples;
|
||||||
|
uint result;
|
||||||
|
int numChannels = src.getChannels();
|
||||||
|
|
||||||
|
assert(numChannels == dest.getChannels());
|
||||||
|
|
||||||
|
numSrcSamples = src.numSamples();
|
||||||
|
psrc = src.ptrBegin();
|
||||||
|
pdest = dest.ptrEnd(numSrcSamples);
|
||||||
|
result = pFIR->evaluate(pdest, psrc, numSrcSamples, numChannels);
|
||||||
|
src.receiveSamples(result);
|
||||||
|
dest.putSamples(result);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
uint AAFilter::getLength() const
|
uint AAFilter::getLength() const
|
||||||
{
|
{
|
||||||
return pFIR->getLength();
|
return pFIR->getLength();
|
||||||
|
|||||||
@ -13,13 +13,6 @@
|
|||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Last changed : $Date$
|
|
||||||
// File revision : $Revision: 4 $
|
|
||||||
//
|
|
||||||
// $Id$
|
|
||||||
//
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// License :
|
// License :
|
||||||
//
|
//
|
||||||
// SoundTouch audio processing library
|
// SoundTouch audio processing library
|
||||||
@ -45,6 +38,7 @@
|
|||||||
#define AAFilter_H
|
#define AAFilter_H
|
||||||
|
|
||||||
#include "STTypes.h"
|
#include "STTypes.h"
|
||||||
|
#include "FIFOSampleBuffer.h"
|
||||||
|
|
||||||
namespace soundtouch
|
namespace soundtouch
|
||||||
{
|
{
|
||||||
@ -84,6 +78,14 @@ public:
|
|||||||
const SAMPLETYPE *src,
|
const SAMPLETYPE *src,
|
||||||
uint numSamples,
|
uint numSamples,
|
||||||
uint numChannels) const;
|
uint numChannels) const;
|
||||||
|
|
||||||
|
/// Applies the filter to the given src & dest pipes, so that processed amount of
|
||||||
|
/// samples get removed from src, and produced amount added to dest
|
||||||
|
/// Note : The amount of outputted samples is by value of 'filter length'
|
||||||
|
/// smaller than the amount of input samples.
|
||||||
|
uint evaluate(FIFOSampleBuffer &dest,
|
||||||
|
FIFOSampleBuffer &src) const;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -26,13 +26,6 @@
|
|||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Last changed : $Date$
|
|
||||||
// File revision : $Revision: 4 $
|
|
||||||
//
|
|
||||||
// $Id$
|
|
||||||
//
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// License :
|
// License :
|
||||||
//
|
//
|
||||||
// SoundTouch audio processing library
|
// SoundTouch audio processing library
|
||||||
@ -54,26 +47,45 @@
|
|||||||
//
|
//
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#define _USE_MATH_DEFINES
|
||||||
|
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <cfloat>
|
||||||
#include "FIFOSampleBuffer.h"
|
#include "FIFOSampleBuffer.h"
|
||||||
#include "PeakFinder.h"
|
#include "PeakFinder.h"
|
||||||
#include "BPMDetect.h"
|
#include "BPMDetect.h"
|
||||||
|
|
||||||
using namespace soundtouch;
|
using namespace soundtouch;
|
||||||
|
|
||||||
#define INPUT_BLOCK_SAMPLES 2048
|
// algorithm input sample block size
|
||||||
#define DECIMATED_BLOCK_SAMPLES 256
|
static const int INPUT_BLOCK_SIZE = 2048;
|
||||||
|
|
||||||
/// decay constant for calculating RMS volume sliding average approximation
|
// decimated sample block size
|
||||||
/// (time constant is about 10 sec)
|
static const int DECIMATED_BLOCK_SIZE = 256;
|
||||||
const float avgdecay = 0.99986f;
|
|
||||||
|
|
||||||
/// Normalization coefficient for calculating RMS sliding average approximation.
|
/// Target sample rate after decimation
|
||||||
const float avgnorm = (1 - avgdecay);
|
static const int TARGET_SRATE = 1000;
|
||||||
|
|
||||||
|
/// XCorr update sequence size, update in about 200msec chunks
|
||||||
|
static const int XCORR_UPDATE_SEQUENCE = (int)(TARGET_SRATE / 5);
|
||||||
|
|
||||||
|
/// Moving average N size
|
||||||
|
static const int MOVING_AVERAGE_N = 15;
|
||||||
|
|
||||||
|
/// XCorr decay time constant, decay to half in 30 seconds
|
||||||
|
/// If it's desired to have the system adapt quicker to beat rate
|
||||||
|
/// changes within a continuing music stream, then the
|
||||||
|
/// 'xcorr_decay_time_constant' value can be reduced, yet that
|
||||||
|
/// can increase possibility of glitches in bpm detection.
|
||||||
|
static const double XCORR_DECAY_TIME_CONSTANT = 30.0;
|
||||||
|
|
||||||
|
/// Data overlap factor for beat detection algorithm
|
||||||
|
static const int OVERLAP_FACTOR = 4;
|
||||||
|
|
||||||
|
static const double TWOPI = (2 * M_PI);
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
@ -83,16 +95,14 @@ const float avgnorm = (1 - avgdecay);
|
|||||||
|
|
||||||
#ifdef _CREATE_BPM_DEBUG_FILE
|
#ifdef _CREATE_BPM_DEBUG_FILE
|
||||||
|
|
||||||
#define DEBUGFILE_NAME "c:\\temp\\soundtouch-bpm-debug.txt"
|
static void _SaveDebugData(const char *name, const float *data, int minpos, int maxpos, double coeff)
|
||||||
|
|
||||||
static void _SaveDebugData(const float *data, int minpos, int maxpos, double coeff)
|
|
||||||
{
|
{
|
||||||
FILE *fptr = fopen(DEBUGFILE_NAME, "wt");
|
FILE *fptr = fopen(name, "wt");
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
if (fptr)
|
if (fptr)
|
||||||
{
|
{
|
||||||
printf("\n\nWriting BPM debug data into file " DEBUGFILE_NAME "\n\n");
|
printf("\nWriting BPM debug data into file %s\n", name);
|
||||||
for (i = minpos; i < maxpos; i ++)
|
for (i = minpos; i < maxpos; i ++)
|
||||||
{
|
{
|
||||||
fprintf(fptr, "%d\t%.1lf\t%f\n", i, coeff / (double)i, data[i]);
|
fprintf(fptr, "%d\t%.1lf\t%f\n", i, coeff / (double)i, data[i]);
|
||||||
@ -100,42 +110,90 @@ const float avgnorm = (1 - avgdecay);
|
|||||||
fclose(fptr);
|
fclose(fptr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _SaveDebugBeatPos(const char *name, const std::vector<BEAT> &beats)
|
||||||
|
{
|
||||||
|
printf("\nWriting beat detections data into file %s\n", name);
|
||||||
|
|
||||||
|
FILE *fptr = fopen(name, "wt");
|
||||||
|
if (fptr)
|
||||||
|
{
|
||||||
|
for (uint i = 0; i < beats.size(); i++)
|
||||||
|
{
|
||||||
|
BEAT b = beats[i];
|
||||||
|
fprintf(fptr, "%lf\t%lf\n", b.pos, b.strength);
|
||||||
|
}
|
||||||
|
fclose(fptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
#else
|
#else
|
||||||
#define _SaveDebugData(a,b,c,d)
|
#define _SaveDebugData(name, a,b,c,d)
|
||||||
|
#define _SaveDebugBeatPos(name, b)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// Hamming window
|
||||||
|
void hamming(float *w, int N)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < N; i++)
|
||||||
|
{
|
||||||
|
w[i] = (float)(0.54 - 0.46 * cos(TWOPI * i / (N - 1)));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// IIR2_filter - 2nd order IIR filter
|
||||||
|
|
||||||
|
IIR2_filter::IIR2_filter(const double *lpf_coeffs)
|
||||||
|
{
|
||||||
|
memcpy(coeffs, lpf_coeffs, 5 * sizeof(double));
|
||||||
|
memset(prev, 0, sizeof(prev));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
float IIR2_filter::update(float x)
|
||||||
|
{
|
||||||
|
prev[0] = x;
|
||||||
|
double y = x * coeffs[0];
|
||||||
|
|
||||||
|
for (int i = 4; i >= 1; i--)
|
||||||
|
{
|
||||||
|
y += coeffs[i] * prev[i];
|
||||||
|
prev[i] = prev[i - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
prev[3] = y;
|
||||||
|
return (float)y;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// IIR low-pass filter coefficients, calculated with matlab/octave cheby2(2,40,0.05)
|
||||||
|
const double _LPF_coeffs[5] = { 0.00996655391939, -0.01944529148401, 0.00996655391939, 1.96867605796247, -0.96916387431724 };
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
BPMDetect::BPMDetect(int numChannels, int aSampleRate) :
|
||||||
BPMDetect::BPMDetect(int numChannels, int aSampleRate)
|
beat_lpf(_LPF_coeffs)
|
||||||
{
|
{
|
||||||
|
beats.reserve(250); // initial reservation to prevent frequent reallocation
|
||||||
|
|
||||||
this->sampleRate = aSampleRate;
|
this->sampleRate = aSampleRate;
|
||||||
this->channels = numChannels;
|
this->channels = numChannels;
|
||||||
|
|
||||||
decimateSum = 0;
|
decimateSum = 0;
|
||||||
decimateCount = 0;
|
decimateCount = 0;
|
||||||
|
|
||||||
envelopeAccu = 0;
|
|
||||||
|
|
||||||
// Initialize RMS volume accumulator to RMS level of 1500 (out of 32768) that's
|
|
||||||
// safe initial RMS signal level value for song data. This value is then adapted
|
|
||||||
// to the actual level during processing.
|
|
||||||
#ifdef SOUNDTOUCH_INTEGER_SAMPLES
|
|
||||||
// integer samples
|
|
||||||
RMSVolumeAccu = (1500 * 1500) / avgnorm;
|
|
||||||
#else
|
|
||||||
// float samples, scaled to range [-1..+1[
|
|
||||||
RMSVolumeAccu = (0.045f * 0.045f) / avgnorm;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// choose decimation factor so that result is approx. 1000 Hz
|
// choose decimation factor so that result is approx. 1000 Hz
|
||||||
decimateBy = sampleRate / 1000;
|
decimateBy = sampleRate / TARGET_SRATE;
|
||||||
assert(decimateBy > 0);
|
if ((decimateBy <= 0) || (decimateBy * DECIMATED_BLOCK_SIZE < INPUT_BLOCK_SIZE))
|
||||||
assert(INPUT_BLOCK_SAMPLES < decimateBy * DECIMATED_BLOCK_SAMPLES);
|
{
|
||||||
|
ST_THROW_RT_ERROR("Too small samplerate");
|
||||||
|
}
|
||||||
|
|
||||||
// Calculate window length & starting item according to desired min & max bpms
|
// Calculate window length & starting item according to desired min & max bpms
|
||||||
windowLen = (60 * sampleRate) / (decimateBy * MIN_BPM);
|
windowLen = (60 * sampleRate) / (decimateBy * MIN_BPM);
|
||||||
windowStart = (60 * sampleRate) / (decimateBy * MAX_BPM);
|
windowStart = (60 * sampleRate) / (decimateBy * MAX_BPM_RANGE);
|
||||||
|
|
||||||
assert(windowLen > windowStart);
|
assert(windowLen > windowStart);
|
||||||
|
|
||||||
@ -143,23 +201,38 @@ BPMDetect::BPMDetect(int numChannels, int aSampleRate)
|
|||||||
xcorr = new float[windowLen];
|
xcorr = new float[windowLen];
|
||||||
memset(xcorr, 0, windowLen * sizeof(float));
|
memset(xcorr, 0, windowLen * sizeof(float));
|
||||||
|
|
||||||
|
pos = 0;
|
||||||
|
peakPos = 0;
|
||||||
|
peakVal = 0;
|
||||||
|
init_scaler = 1;
|
||||||
|
beatcorr_ringbuffpos = 0;
|
||||||
|
beatcorr_ringbuff = new float[windowLen];
|
||||||
|
memset(beatcorr_ringbuff, 0, windowLen * sizeof(float));
|
||||||
|
|
||||||
// allocate processing buffer
|
// allocate processing buffer
|
||||||
buffer = new FIFOSampleBuffer();
|
buffer = new FIFOSampleBuffer();
|
||||||
// we do processing in mono mode
|
// we do processing in mono mode
|
||||||
buffer->setChannels(1);
|
buffer->setChannels(1);
|
||||||
buffer->clear();
|
buffer->clear();
|
||||||
}
|
|
||||||
|
|
||||||
|
// calculate hamming windows
|
||||||
|
hamw = new float[XCORR_UPDATE_SEQUENCE];
|
||||||
|
hamming(hamw, XCORR_UPDATE_SEQUENCE);
|
||||||
|
hamw2 = new float[XCORR_UPDATE_SEQUENCE / 2];
|
||||||
|
hamming(hamw2, XCORR_UPDATE_SEQUENCE / 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
BPMDetect::~BPMDetect()
|
BPMDetect::~BPMDetect()
|
||||||
{
|
{
|
||||||
delete[] xcorr;
|
delete[] xcorr;
|
||||||
|
delete[] beatcorr_ringbuff;
|
||||||
|
delete[] hamw;
|
||||||
|
delete[] hamw2;
|
||||||
delete buffer;
|
delete buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// convert to mono, low-pass filter & decimate to about 500 Hz.
|
/// convert to mono, low-pass filter & decimate to about 500 Hz.
|
||||||
/// return number of outputted samples.
|
/// return number of outputted samples.
|
||||||
///
|
///
|
||||||
@ -216,7 +289,6 @@ int BPMDetect::decimate(SAMPLETYPE *dest, const SAMPLETYPE *src, int numsamples)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Calculates autocorrelation function of the sample history buffer
|
// Calculates autocorrelation function of the sample history buffer
|
||||||
void BPMDetect::updateXCorr(int process_samples)
|
void BPMDetect::updateXCorr(int process_samples)
|
||||||
{
|
{
|
||||||
@ -224,71 +296,122 @@ void BPMDetect::updateXCorr(int process_samples)
|
|||||||
SAMPLETYPE *pBuffer;
|
SAMPLETYPE *pBuffer;
|
||||||
|
|
||||||
assert(buffer->numSamples() >= (uint)(process_samples + windowLen));
|
assert(buffer->numSamples() >= (uint)(process_samples + windowLen));
|
||||||
|
assert(process_samples == XCORR_UPDATE_SEQUENCE);
|
||||||
|
|
||||||
pBuffer = buffer->ptrBegin();
|
pBuffer = buffer->ptrBegin();
|
||||||
|
|
||||||
|
// calculate decay factor for xcorr filtering
|
||||||
|
float xcorr_decay = (float)pow(0.5, process_samples / (XCORR_DECAY_TIME_CONSTANT * TARGET_SRATE));
|
||||||
|
|
||||||
|
// prescale pbuffer
|
||||||
|
float tmp[XCORR_UPDATE_SEQUENCE];
|
||||||
|
for (int i = 0; i < process_samples; i++)
|
||||||
|
{
|
||||||
|
tmp[i] = hamw[i] * hamw[i] * pBuffer[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma omp parallel for
|
||||||
for (offs = windowStart; offs < windowLen; offs ++)
|
for (offs = windowStart; offs < windowLen; offs ++)
|
||||||
{
|
{
|
||||||
LONG_SAMPLETYPE sum;
|
float sum;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
sum = 0;
|
sum = 0;
|
||||||
for (i = 0; i < process_samples; i ++)
|
for (i = 0; i < process_samples; i ++)
|
||||||
{
|
{
|
||||||
sum += pBuffer[i] * pBuffer[i + offs]; // scaling the sub-result shouldn't be necessary
|
sum += tmp[i] * pBuffer[i + offs]; // scaling the sub-result shouldn't be necessary
|
||||||
}
|
}
|
||||||
// xcorr[offs] *= xcorr_decay; // decay 'xcorr' here with suitable coefficients
|
xcorr[offs] *= xcorr_decay; // decay 'xcorr' here with suitable time constant.
|
||||||
// if it's desired that the system adapts automatically to
|
|
||||||
// various bpms, e.g. in processing continouos music stream.
|
|
||||||
// The 'xcorr_decay' should be a value that's smaller than but
|
|
||||||
// close to one, and should also depend on 'process_samples' value.
|
|
||||||
|
|
||||||
xcorr[offs] += (float)sum;
|
xcorr[offs] += (float)fabs(sum);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Calculates envelope of the sample data
|
// Detect individual beat positions
|
||||||
void BPMDetect::calcEnvelope(SAMPLETYPE *samples, int numsamples)
|
void BPMDetect::updateBeatPos(int process_samples)
|
||||||
{
|
{
|
||||||
const static double decay = 0.7f; // decay constant for smoothing the envelope
|
SAMPLETYPE *pBuffer;
|
||||||
const static double norm = (1 - decay);
|
|
||||||
|
|
||||||
int i;
|
assert(buffer->numSamples() >= (uint)(process_samples + windowLen));
|
||||||
LONG_SAMPLETYPE out;
|
|
||||||
double val;
|
|
||||||
|
|
||||||
for (i = 0; i < numsamples; i ++)
|
pBuffer = buffer->ptrBegin();
|
||||||
|
assert(process_samples == XCORR_UPDATE_SEQUENCE / 2);
|
||||||
|
|
||||||
|
// static double thr = 0.0003;
|
||||||
|
double posScale = (double)this->decimateBy / (double)this->sampleRate;
|
||||||
|
int resetDur = (int)(0.12 / posScale + 0.5);
|
||||||
|
|
||||||
|
// prescale pbuffer
|
||||||
|
float tmp[XCORR_UPDATE_SEQUENCE / 2];
|
||||||
|
for (int i = 0; i < process_samples; i++)
|
||||||
{
|
{
|
||||||
// calc average RMS volume
|
tmp[i] = hamw2[i] * hamw2[i] * pBuffer[i];
|
||||||
RMSVolumeAccu *= avgdecay;
|
}
|
||||||
val = (float)fabs((float)samples[i]);
|
|
||||||
RMSVolumeAccu += val * val;
|
|
||||||
|
|
||||||
// cut amplitudes that are below cutoff ~2 times RMS volume
|
#pragma omp parallel for
|
||||||
// (we're interested in peak values, not the silent moments)
|
for (int offs = windowStart; offs < windowLen; offs++)
|
||||||
if (val < 0.5 * sqrt(RMSVolumeAccu * avgnorm))
|
|
||||||
{
|
{
|
||||||
val = 0;
|
float sum = 0;
|
||||||
|
for (int i = 0; i < process_samples; i++)
|
||||||
|
{
|
||||||
|
sum += tmp[i] * pBuffer[offs + i];
|
||||||
|
}
|
||||||
|
beatcorr_ringbuff[(beatcorr_ringbuffpos + offs) % windowLen] += (float)((sum > 0) ? sum : 0); // accumulate only positive correlations
|
||||||
}
|
}
|
||||||
|
|
||||||
// smooth amplitude envelope
|
int skipstep = XCORR_UPDATE_SEQUENCE / OVERLAP_FACTOR;
|
||||||
envelopeAccu *= decay;
|
|
||||||
envelopeAccu += val;
|
|
||||||
out = (LONG_SAMPLETYPE)(envelopeAccu * norm);
|
|
||||||
|
|
||||||
#ifdef SOUNDTOUCH_INTEGER_SAMPLES
|
// compensate empty buffer at beginning by scaling coefficient
|
||||||
// cut peaks (shouldn't be necessary though)
|
float scale = (float)windowLen / (float)(skipstep * init_scaler);
|
||||||
if (out > 32767) out = 32767;
|
if (scale > 1.0f)
|
||||||
#endif // SOUNDTOUCH_INTEGER_SAMPLES
|
{
|
||||||
samples[i] = (SAMPLETYPE)out;
|
init_scaler++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
scale = 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
// detect beats
|
||||||
|
for (int i = 0; i < skipstep; i++)
|
||||||
|
{
|
||||||
|
float sum = beatcorr_ringbuff[beatcorr_ringbuffpos];
|
||||||
|
sum -= beat_lpf.update(sum);
|
||||||
|
|
||||||
|
if (sum > peakVal)
|
||||||
|
{
|
||||||
|
// found new local largest value
|
||||||
|
peakVal = sum;
|
||||||
|
peakPos = pos;
|
||||||
|
}
|
||||||
|
if (pos > peakPos + resetDur)
|
||||||
|
{
|
||||||
|
// largest value not updated for 200msec => accept as beat
|
||||||
|
peakPos += skipstep;
|
||||||
|
if (peakVal > 0)
|
||||||
|
{
|
||||||
|
// add detected beat to end of "beats" vector
|
||||||
|
BEAT temp = { (float)(peakPos * posScale), (float)(peakVal * scale) };
|
||||||
|
beats.push_back(temp);
|
||||||
|
}
|
||||||
|
|
||||||
|
peakVal = 0;
|
||||||
|
peakPos = pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
beatcorr_ringbuff[beatcorr_ringbuffpos] = 0;
|
||||||
|
pos++;
|
||||||
|
beatcorr_ringbuffpos = (beatcorr_ringbuffpos + 1) % windowLen;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#define max(x,y) ((x) > (y) ? (x) : (y))
|
||||||
|
|
||||||
void BPMDetect::inputSamples(const SAMPLETYPE *samples, int numSamples)
|
void BPMDetect::inputSamples(const SAMPLETYPE *samples, int numSamples)
|
||||||
{
|
{
|
||||||
SAMPLETYPE decimated[DECIMATED_BLOCK_SAMPLES];
|
SAMPLETYPE decimated[DECIMATED_BLOCK_SIZE];
|
||||||
|
|
||||||
// iterate so that max INPUT_BLOCK_SAMPLES processed per iteration
|
// iterate so that max INPUT_BLOCK_SAMPLES processed per iteration
|
||||||
while (numSamples > 0)
|
while (numSamples > 0)
|
||||||
@ -296,48 +419,70 @@ void BPMDetect::inputSamples(const SAMPLETYPE *samples, int numSamples)
|
|||||||
int block;
|
int block;
|
||||||
int decSamples;
|
int decSamples;
|
||||||
|
|
||||||
block = (numSamples > INPUT_BLOCK_SAMPLES) ? INPUT_BLOCK_SAMPLES : numSamples;
|
block = (numSamples > INPUT_BLOCK_SIZE) ? INPUT_BLOCK_SIZE : numSamples;
|
||||||
|
|
||||||
// decimate. note that converts to mono at the same time
|
// decimate. note that converts to mono at the same time
|
||||||
decSamples = decimate(decimated, samples, block);
|
decSamples = decimate(decimated, samples, block);
|
||||||
samples += block * channels;
|
samples += block * channels;
|
||||||
numSamples -= block;
|
numSamples -= block;
|
||||||
|
|
||||||
// envelope new samples and add them to buffer
|
|
||||||
calcEnvelope(decimated, decSamples);
|
|
||||||
buffer->putSamples(decimated, decSamples);
|
buffer->putSamples(decimated, decSamples);
|
||||||
}
|
}
|
||||||
|
|
||||||
// when the buffer has enought samples for processing...
|
// when the buffer has enough samples for processing...
|
||||||
if ((int)buffer->numSamples() > windowLen)
|
int req = max(windowLen + XCORR_UPDATE_SEQUENCE, 2 * XCORR_UPDATE_SEQUENCE);
|
||||||
|
while ((int)buffer->numSamples() >= req)
|
||||||
{
|
{
|
||||||
int processLength;
|
// ... update autocorrelations...
|
||||||
|
updateXCorr(XCORR_UPDATE_SEQUENCE);
|
||||||
// how many samples are processed
|
// ...update beat position calculation...
|
||||||
processLength = (int)buffer->numSamples() - windowLen;
|
updateBeatPos(XCORR_UPDATE_SEQUENCE / 2);
|
||||||
|
// ... and remove proceessed samples from the buffer
|
||||||
// ... calculate autocorrelations for oldest samples...
|
int n = XCORR_UPDATE_SEQUENCE / OVERLAP_FACTOR;
|
||||||
updateXCorr(processLength);
|
buffer->receiveSamples(n);
|
||||||
// ... and remove them from the buffer
|
|
||||||
buffer->receiveSamples(processLength);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void BPMDetect::removeBias()
|
void BPMDetect::removeBias()
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
float minval = 1e12f; // arbitrary large number
|
|
||||||
|
|
||||||
|
// Remove linear bias: calculate linear regression coefficient
|
||||||
|
// 1. calc mean of 'xcorr' and 'i'
|
||||||
|
double mean_i = 0;
|
||||||
|
double mean_x = 0;
|
||||||
for (i = windowStart; i < windowLen; i++)
|
for (i = windowStart; i < windowLen; i++)
|
||||||
{
|
{
|
||||||
|
mean_x += xcorr[i];
|
||||||
|
}
|
||||||
|
mean_x /= (windowLen - windowStart);
|
||||||
|
mean_i = 0.5 * (windowLen - 1 + windowStart);
|
||||||
|
|
||||||
|
// 2. calculate linear regression coefficient
|
||||||
|
double b = 0;
|
||||||
|
double div = 0;
|
||||||
|
for (i = windowStart; i < windowLen; i++)
|
||||||
|
{
|
||||||
|
double xt = xcorr[i] - mean_x;
|
||||||
|
double xi = i - mean_i;
|
||||||
|
b += xt * xi;
|
||||||
|
div += xi * xi;
|
||||||
|
}
|
||||||
|
b /= div;
|
||||||
|
|
||||||
|
// subtract linear regression and resolve min. value bias
|
||||||
|
float minval = FLT_MAX; // arbitrary large number
|
||||||
|
for (i = windowStart; i < windowLen; i ++)
|
||||||
|
{
|
||||||
|
xcorr[i] -= (float)(b * i);
|
||||||
if (xcorr[i] < minval)
|
if (xcorr[i] < minval)
|
||||||
{
|
{
|
||||||
minval = xcorr[i];
|
minval = xcorr[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// subtract min.value
|
||||||
for (i = windowStart; i < windowLen; i ++)
|
for (i = windowStart; i < windowLen; i ++)
|
||||||
{
|
{
|
||||||
xcorr[i] -= minval;
|
xcorr[i] -= minval;
|
||||||
@ -345,26 +490,82 @@ void BPMDetect::removeBias()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Calculate N-point moving average for "source" values
|
||||||
|
void MAFilter(float *dest, const float *source, int start, int end, int N)
|
||||||
|
{
|
||||||
|
for (int i = start; i < end; i++)
|
||||||
|
{
|
||||||
|
int i1 = i - N / 2;
|
||||||
|
int i2 = i + N / 2 + 1;
|
||||||
|
if (i1 < start) i1 = start;
|
||||||
|
if (i2 > end) i2 = end;
|
||||||
|
|
||||||
|
double sum = 0;
|
||||||
|
for (int j = i1; j < i2; j ++)
|
||||||
|
{
|
||||||
|
sum += source[j];
|
||||||
|
}
|
||||||
|
dest[i] = (float)(sum / (i2 - i1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
float BPMDetect::getBpm()
|
float BPMDetect::getBpm()
|
||||||
{
|
{
|
||||||
double peakPos;
|
double peakPos;
|
||||||
double coeff;
|
double coeff;
|
||||||
PeakFinder peakFinder;
|
PeakFinder peakFinder;
|
||||||
|
|
||||||
coeff = 60.0 * ((double)sampleRate / (double)decimateBy);
|
|
||||||
|
|
||||||
// save bpm debug analysis data if debug data enabled
|
|
||||||
_SaveDebugData(xcorr, windowStart, windowLen, coeff);
|
|
||||||
|
|
||||||
// remove bias from xcorr data
|
// remove bias from xcorr data
|
||||||
removeBias();
|
removeBias();
|
||||||
|
|
||||||
|
coeff = 60.0 * ((double)sampleRate / (double)decimateBy);
|
||||||
|
|
||||||
|
// save bpm debug data if debug data writing enabled
|
||||||
|
_SaveDebugData("soundtouch-bpm-xcorr.txt", xcorr, windowStart, windowLen, coeff);
|
||||||
|
|
||||||
|
// Smoothen by N-point moving-average
|
||||||
|
float *data = new float[windowLen];
|
||||||
|
memset(data, 0, sizeof(float) * windowLen);
|
||||||
|
MAFilter(data, xcorr, windowStart, windowLen, MOVING_AVERAGE_N);
|
||||||
|
|
||||||
// find peak position
|
// find peak position
|
||||||
peakPos = peakFinder.detectPeak(xcorr, windowStart, windowLen);
|
peakPos = peakFinder.detectPeak(data, windowStart, windowLen);
|
||||||
|
|
||||||
|
// save bpm debug data if debug data writing enabled
|
||||||
|
_SaveDebugData("soundtouch-bpm-smoothed.txt", data, windowStart, windowLen, coeff);
|
||||||
|
|
||||||
|
delete[] data;
|
||||||
|
|
||||||
assert(decimateBy != 0);
|
assert(decimateBy != 0);
|
||||||
if (peakPos < 1e-9) return 0.0; // detection failed.
|
if (peakPos < 1e-9) return 0.0; // detection failed.
|
||||||
|
|
||||||
|
_SaveDebugBeatPos("soundtouch-detected-beats.txt", beats);
|
||||||
|
|
||||||
// calculate BPM
|
// calculate BPM
|
||||||
return (float) (coeff / peakPos);
|
float bpm = (float)(coeff / peakPos);
|
||||||
|
return (bpm >= MIN_BPM && bpm <= MAX_BPM_VALID) ? bpm : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Get beat position arrays. Note: The array includes also really low beat detection values
|
||||||
|
/// in absence of clear strong beats. Consumer may wish to filter low values away.
|
||||||
|
/// - "pos" receive array of beat positions
|
||||||
|
/// - "values" receive array of beat detection strengths
|
||||||
|
/// - max_num indicates max.size of "pos" and "values" array.
|
||||||
|
///
|
||||||
|
/// You can query a suitable array sized by calling this with nullptr in "pos" & "values".
|
||||||
|
///
|
||||||
|
/// \return number of beats in the arrays.
|
||||||
|
int BPMDetect::getBeats(float *pos, float *values, int max_num)
|
||||||
|
{
|
||||||
|
int num = (int)beats.size();
|
||||||
|
if ((!pos) || (!values)) return num; // pos or values nullptr, return just size
|
||||||
|
|
||||||
|
for (int i = 0; (i < num) && (i < max_num); i++)
|
||||||
|
{
|
||||||
|
pos[i] = beats[i].pos;
|
||||||
|
values[i] = beats[i].strength;
|
||||||
|
}
|
||||||
|
return num;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,13 +15,6 @@
|
|||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Last changed : $Date$
|
|
||||||
// File revision : $Revision: 4 $
|
|
||||||
//
|
|
||||||
// $Id$
|
|
||||||
//
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// License :
|
// License :
|
||||||
//
|
//
|
||||||
// SoundTouch audio processing library
|
// SoundTouch audio processing library
|
||||||
@ -57,8 +50,8 @@ FIFOSampleBuffer::FIFOSampleBuffer(int numChannels)
|
|||||||
{
|
{
|
||||||
assert(numChannels > 0);
|
assert(numChannels > 0);
|
||||||
sizeInBytes = 0; // reasonable initial value
|
sizeInBytes = 0; // reasonable initial value
|
||||||
buffer = NULL;
|
buffer = nullptr;
|
||||||
bufferUnaligned = NULL;
|
bufferUnaligned = nullptr;
|
||||||
samplesInBuffer = 0;
|
samplesInBuffer = 0;
|
||||||
bufferPos = 0;
|
bufferPos = 0;
|
||||||
channels = (uint)numChannels;
|
channels = (uint)numChannels;
|
||||||
@ -70,8 +63,8 @@ FIFOSampleBuffer::FIFOSampleBuffer(int numChannels)
|
|||||||
FIFOSampleBuffer::~FIFOSampleBuffer()
|
FIFOSampleBuffer::~FIFOSampleBuffer()
|
||||||
{
|
{
|
||||||
delete[] bufferUnaligned;
|
delete[] bufferUnaligned;
|
||||||
bufferUnaligned = NULL;
|
bufferUnaligned = nullptr;
|
||||||
buffer = NULL;
|
buffer = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -80,7 +73,8 @@ void FIFOSampleBuffer::setChannels(int numChannels)
|
|||||||
{
|
{
|
||||||
uint usedBytes;
|
uint usedBytes;
|
||||||
|
|
||||||
assert(numChannels > 0);
|
if (!verifyNumberOfChannels(numChannels)) return;
|
||||||
|
|
||||||
usedBytes = channels * samplesInBuffer;
|
usedBytes = channels * samplesInBuffer;
|
||||||
channels = (uint)numChannels;
|
channels = (uint)numChannels;
|
||||||
samplesInBuffer = usedBytes / channels;
|
samplesInBuffer = usedBytes / channels;
|
||||||
@ -131,7 +125,7 @@ void FIFOSampleBuffer::putSamples(uint nSamples)
|
|||||||
//
|
//
|
||||||
// Parameter 'slackCapacity' tells the function how much free capacity (in
|
// Parameter 'slackCapacity' tells the function how much free capacity (in
|
||||||
// terms of samples) there _at least_ should be, in order to the caller to
|
// terms of samples) there _at least_ should be, in order to the caller to
|
||||||
// succesfully insert all the required samples to the buffer. When necessary,
|
// successfully insert all the required samples to the buffer. When necessary,
|
||||||
// the function grows the buffer size to comply with this requirement.
|
// the function grows the buffer size to comply with this requirement.
|
||||||
//
|
//
|
||||||
// When using this function as means for inserting new samples, also remember
|
// When using this function as means for inserting new samples, also remember
|
||||||
@ -158,7 +152,7 @@ SAMPLETYPE *FIFOSampleBuffer::ptrBegin()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Ensures that the buffer has enought capacity, i.e. space for _at least_
|
// Ensures that the buffer has enough capacity, i.e. space for _at least_
|
||||||
// 'capacityRequirement' number of samples. The buffer is grown in steps of
|
// 'capacityRequirement' number of samples. The buffer is grown in steps of
|
||||||
// 4 kilobytes to eliminate the need for frequently growing up the buffer,
|
// 4 kilobytes to eliminate the need for frequently growing up the buffer,
|
||||||
// as well as to round the buffer size up to the virtual memory page size.
|
// as well as to round the buffer size up to the virtual memory page size.
|
||||||
@ -172,12 +166,12 @@ void FIFOSampleBuffer::ensureCapacity(uint capacityRequirement)
|
|||||||
sizeInBytes = (capacityRequirement * channels * sizeof(SAMPLETYPE) + 4095) & (uint)-4096;
|
sizeInBytes = (capacityRequirement * channels * sizeof(SAMPLETYPE) + 4095) & (uint)-4096;
|
||||||
assert(sizeInBytes % 2 == 0);
|
assert(sizeInBytes % 2 == 0);
|
||||||
tempUnaligned = new SAMPLETYPE[sizeInBytes / sizeof(SAMPLETYPE) + 16 / sizeof(SAMPLETYPE)];
|
tempUnaligned = new SAMPLETYPE[sizeInBytes / sizeof(SAMPLETYPE) + 16 / sizeof(SAMPLETYPE)];
|
||||||
if (tempUnaligned == NULL)
|
if (tempUnaligned == nullptr)
|
||||||
{
|
{
|
||||||
ST_THROW_RT_ERROR("Couldn't allocate memory!\n");
|
ST_THROW_RT_ERROR("Couldn't allocate memory!\n");
|
||||||
}
|
}
|
||||||
// Align the buffer to begin at 16byte cache line boundary for optimal performance
|
// Align the buffer to begin at 16byte cache line boundary for optimal performance
|
||||||
temp = (SAMPLETYPE *)(((ulong)tempUnaligned + 15) & (ulong)-16);
|
temp = (SAMPLETYPE *)SOUNDTOUCH_ALIGN_POINTER_16(tempUnaligned);
|
||||||
if (samplesInBuffer)
|
if (samplesInBuffer)
|
||||||
{
|
{
|
||||||
memcpy(temp, ptrBegin(), samplesInBuffer * channels * sizeof(SAMPLETYPE));
|
memcpy(temp, ptrBegin(), samplesInBuffer * channels * sizeof(SAMPLETYPE));
|
||||||
@ -272,3 +266,10 @@ uint FIFOSampleBuffer::adjustAmountOfSamples(uint numSamples)
|
|||||||
return samplesInBuffer;
|
return samplesInBuffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Add silence to end of buffer
|
||||||
|
void FIFOSampleBuffer::addSilent(uint nSamples)
|
||||||
|
{
|
||||||
|
memset(ptrEnd(nSamples), 0, sizeof(SAMPLETYPE) * nSamples * channels);
|
||||||
|
samplesInBuffer += nSamples;
|
||||||
|
}
|
||||||
|
|||||||
@ -2,22 +2,21 @@
|
|||||||
///
|
///
|
||||||
/// General FIR digital filter routines with MMX optimization.
|
/// General FIR digital filter routines with MMX optimization.
|
||||||
///
|
///
|
||||||
/// Note : MMX optimized functions reside in a separate, platform-specific file,
|
/// Notes : MMX optimized functions reside in a separate, platform-specific file,
|
||||||
/// e.g. 'mmx_win.cpp' or 'mmx_gcc.cpp'
|
/// e.g. 'mmx_win.cpp' or 'mmx_gcc.cpp'
|
||||||
///
|
///
|
||||||
|
/// This source file contains OpenMP optimizations that allow speeding up the
|
||||||
|
/// corss-correlation algorithm by executing it in several threads / CPU cores
|
||||||
|
/// in parallel. See the following article link for more detailed discussion
|
||||||
|
/// about SoundTouch OpenMP optimizations:
|
||||||
|
/// http://www.softwarecoven.com/parallel-computing-in-embedded-mobile-devices
|
||||||
|
///
|
||||||
/// Author : Copyright (c) Olli Parviainen
|
/// Author : Copyright (c) Olli Parviainen
|
||||||
/// Author e-mail : oparviai 'at' iki.fi
|
/// Author e-mail : oparviai 'at' iki.fi
|
||||||
/// SoundTouch WWW: http://www.surina.net/soundtouch
|
/// SoundTouch WWW: http://www.surina.net/soundtouch
|
||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Last changed : $Date$
|
|
||||||
// File revision : $Revision: 4 $
|
|
||||||
//
|
|
||||||
// $Id$
|
|
||||||
//
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// License :
|
// License :
|
||||||
//
|
//
|
||||||
// SoundTouch audio processing library
|
// SoundTouch audio processing library
|
||||||
@ -57,54 +56,45 @@ using namespace soundtouch;
|
|||||||
FIRFilter::FIRFilter()
|
FIRFilter::FIRFilter()
|
||||||
{
|
{
|
||||||
resultDivFactor = 0;
|
resultDivFactor = 0;
|
||||||
resultDivider = 0;
|
|
||||||
length = 0;
|
length = 0;
|
||||||
lengthDiv8 = 0;
|
lengthDiv8 = 0;
|
||||||
filterCoeffs = NULL;
|
filterCoeffs = nullptr;
|
||||||
|
filterCoeffsStereo = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
FIRFilter::~FIRFilter()
|
FIRFilter::~FIRFilter()
|
||||||
{
|
{
|
||||||
delete[] filterCoeffs;
|
delete[] filterCoeffs;
|
||||||
|
delete[] filterCoeffsStereo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Usual C-version of the filter routine for stereo sound
|
// Usual C-version of the filter routine for stereo sound
|
||||||
uint FIRFilter::evaluateFilterStereo(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples) const
|
uint FIRFilter::evaluateFilterStereo(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples) const
|
||||||
{
|
{
|
||||||
uint i, j, end;
|
int j, end;
|
||||||
LONG_SAMPLETYPE suml, sumr;
|
// hint compiler autovectorization that loop length is divisible by 8
|
||||||
#ifdef SOUNDTOUCH_FLOAT_SAMPLES
|
uint ilength = length & -8;
|
||||||
// when using floating point samples, use a scaler instead of a divider
|
|
||||||
// because division is much slower operation than multiplying.
|
|
||||||
double dScaler = 1.0 / (double)resultDivider;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
assert(length != 0);
|
assert((length != 0) && (length == ilength) && (src != nullptr) && (dest != nullptr) && (filterCoeffs != nullptr));
|
||||||
assert(src != NULL);
|
assert(numSamples > ilength);
|
||||||
assert(dest != NULL);
|
|
||||||
assert(filterCoeffs != NULL);
|
|
||||||
|
|
||||||
end = 2 * (numSamples - length);
|
end = 2 * (numSamples - ilength);
|
||||||
|
|
||||||
|
#pragma omp parallel for
|
||||||
for (j = 0; j < end; j += 2)
|
for (j = 0; j < end; j += 2)
|
||||||
{
|
{
|
||||||
const SAMPLETYPE *ptr;
|
const SAMPLETYPE *ptr;
|
||||||
|
LONG_SAMPLETYPE suml, sumr;
|
||||||
|
|
||||||
suml = sumr = 0;
|
suml = sumr = 0;
|
||||||
ptr = src + j;
|
ptr = src + j;
|
||||||
|
|
||||||
for (i = 0; i < length; i += 4)
|
for (uint i = 0; i < ilength; i ++)
|
||||||
{
|
{
|
||||||
// loop is unrolled by factor of 4 here for efficiency
|
suml += ptr[2 * i] * filterCoeffsStereo[2 * i];
|
||||||
suml += ptr[2 * i + 0] * filterCoeffs[i + 0] +
|
sumr += ptr[2 * i + 1] * filterCoeffsStereo[2 * i + 1];
|
||||||
ptr[2 * i + 2] * filterCoeffs[i + 1] +
|
|
||||||
ptr[2 * i + 4] * filterCoeffs[i + 2] +
|
|
||||||
ptr[2 * i + 6] * filterCoeffs[i + 3];
|
|
||||||
sumr += ptr[2 * i + 1] * filterCoeffs[i + 0] +
|
|
||||||
ptr[2 * i + 3] * filterCoeffs[i + 1] +
|
|
||||||
ptr[2 * i + 5] * filterCoeffs[i + 2] +
|
|
||||||
ptr[2 * i + 7] * filterCoeffs[i + 3];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef SOUNDTOUCH_INTEGER_SAMPLES
|
#ifdef SOUNDTOUCH_INTEGER_SAMPLES
|
||||||
@ -114,59 +104,100 @@ uint FIRFilter::evaluateFilterStereo(SAMPLETYPE *dest, const SAMPLETYPE *src, ui
|
|||||||
suml = (suml < -32768) ? -32768 : (suml > 32767) ? 32767 : suml;
|
suml = (suml < -32768) ? -32768 : (suml > 32767) ? 32767 : suml;
|
||||||
// saturate to 16 bit integer limits
|
// saturate to 16 bit integer limits
|
||||||
sumr = (sumr < -32768) ? -32768 : (sumr > 32767) ? 32767 : sumr;
|
sumr = (sumr < -32768) ? -32768 : (sumr > 32767) ? 32767 : sumr;
|
||||||
#else
|
|
||||||
suml *= dScaler;
|
|
||||||
sumr *= dScaler;
|
|
||||||
#endif // SOUNDTOUCH_INTEGER_SAMPLES
|
#endif // SOUNDTOUCH_INTEGER_SAMPLES
|
||||||
dest[j] = (SAMPLETYPE)suml;
|
dest[j] = (SAMPLETYPE)suml;
|
||||||
dest[j + 1] = (SAMPLETYPE)sumr;
|
dest[j + 1] = (SAMPLETYPE)sumr;
|
||||||
}
|
}
|
||||||
return numSamples - length;
|
return numSamples - ilength;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Usual C-version of the filter routine for mono sound
|
// Usual C-version of the filter routine for mono sound
|
||||||
uint FIRFilter::evaluateFilterMono(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples) const
|
uint FIRFilter::evaluateFilterMono(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples) const
|
||||||
{
|
{
|
||||||
uint i, j, end;
|
int j, end;
|
||||||
LONG_SAMPLETYPE sum;
|
|
||||||
#ifdef SOUNDTOUCH_FLOAT_SAMPLES
|
|
||||||
// when using floating point samples, use a scaler instead of a divider
|
|
||||||
// because division is much slower operation than multiplying.
|
|
||||||
double dScaler = 1.0 / (double)resultDivider;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
// hint compiler autovectorization that loop length is divisible by 8
|
||||||
|
int ilength = length & -8;
|
||||||
|
|
||||||
assert(length != 0);
|
assert(ilength != 0);
|
||||||
|
|
||||||
end = numSamples - length;
|
end = numSamples - ilength;
|
||||||
|
#pragma omp parallel for
|
||||||
for (j = 0; j < end; j ++)
|
for (j = 0; j < end; j ++)
|
||||||
{
|
{
|
||||||
|
const SAMPLETYPE *pSrc = src + j;
|
||||||
|
LONG_SAMPLETYPE sum;
|
||||||
|
int i;
|
||||||
|
|
||||||
sum = 0;
|
sum = 0;
|
||||||
for (i = 0; i < length; i += 4)
|
for (i = 0; i < ilength; i ++)
|
||||||
{
|
{
|
||||||
// loop is unrolled by factor of 4 here for efficiency
|
sum += pSrc[i] * filterCoeffs[i];
|
||||||
sum += src[i + 0] * filterCoeffs[i + 0] +
|
|
||||||
src[i + 1] * filterCoeffs[i + 1] +
|
|
||||||
src[i + 2] * filterCoeffs[i + 2] +
|
|
||||||
src[i + 3] * filterCoeffs[i + 3];
|
|
||||||
}
|
}
|
||||||
#ifdef SOUNDTOUCH_INTEGER_SAMPLES
|
#ifdef SOUNDTOUCH_INTEGER_SAMPLES
|
||||||
sum >>= resultDivFactor;
|
sum >>= resultDivFactor;
|
||||||
// saturate to 16 bit integer limits
|
// saturate to 16 bit integer limits
|
||||||
sum = (sum < -32768) ? -32768 : (sum > 32767) ? 32767 : sum;
|
sum = (sum < -32768) ? -32768 : (sum > 32767) ? 32767 : sum;
|
||||||
#else
|
|
||||||
sum *= dScaler;
|
|
||||||
#endif // SOUNDTOUCH_INTEGER_SAMPLES
|
#endif // SOUNDTOUCH_INTEGER_SAMPLES
|
||||||
dest[j] = (SAMPLETYPE)sum;
|
dest[j] = (SAMPLETYPE)sum;
|
||||||
src ++;
|
|
||||||
}
|
}
|
||||||
return end;
|
return end;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
uint FIRFilter::evaluateFilterMulti(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples, uint numChannels)
|
||||||
|
{
|
||||||
|
int j, end;
|
||||||
|
|
||||||
|
assert(length != 0);
|
||||||
|
assert(src != nullptr);
|
||||||
|
assert(dest != nullptr);
|
||||||
|
assert(filterCoeffs != nullptr);
|
||||||
|
assert(numChannels <= SOUNDTOUCH_MAX_CHANNELS);
|
||||||
|
|
||||||
|
// hint compiler autovectorization that loop length is divisible by 8
|
||||||
|
int ilength = length & -8;
|
||||||
|
|
||||||
|
end = numChannels * (numSamples - ilength);
|
||||||
|
|
||||||
|
#pragma omp parallel for
|
||||||
|
for (j = 0; j < end; j += numChannels)
|
||||||
|
{
|
||||||
|
const SAMPLETYPE *ptr;
|
||||||
|
LONG_SAMPLETYPE sums[16];
|
||||||
|
uint c;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (c = 0; c < numChannels; c ++)
|
||||||
|
{
|
||||||
|
sums[c] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ptr = src + j;
|
||||||
|
|
||||||
|
for (i = 0; i < ilength; i ++)
|
||||||
|
{
|
||||||
|
SAMPLETYPE coef=filterCoeffs[i];
|
||||||
|
for (c = 0; c < numChannels; c ++)
|
||||||
|
{
|
||||||
|
sums[c] += ptr[0] * coef;
|
||||||
|
ptr ++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (c = 0; c < numChannels; c ++)
|
||||||
|
{
|
||||||
|
#ifdef SOUNDTOUCH_INTEGER_SAMPLES
|
||||||
|
sums[c] >>= resultDivFactor;
|
||||||
|
#endif // SOUNDTOUCH_INTEGER_SAMPLES
|
||||||
|
dest[j+c] = (SAMPLETYPE)sums[c];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return numSamples - ilength;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Set filter coeffiecients and length.
|
// Set filter coeffiecients and length.
|
||||||
//
|
//
|
||||||
// Throws an exception if filter length isn't divisible by 8
|
// Throws an exception if filter length isn't divisible by 8
|
||||||
@ -180,11 +211,27 @@ void FIRFilter::setCoefficients(const SAMPLETYPE *coeffs, uint newLength, uint u
|
|||||||
assert(length == newLength);
|
assert(length == newLength);
|
||||||
|
|
||||||
resultDivFactor = uResultDivFactor;
|
resultDivFactor = uResultDivFactor;
|
||||||
resultDivider = (SAMPLETYPE)::pow(2.0, (int)resultDivFactor);
|
|
||||||
|
|
||||||
delete[] filterCoeffs;
|
delete[] filterCoeffs;
|
||||||
filterCoeffs = new SAMPLETYPE[length];
|
filterCoeffs = new SAMPLETYPE[length];
|
||||||
memcpy(filterCoeffs, coeffs, length * sizeof(SAMPLETYPE));
|
delete[] filterCoeffsStereo;
|
||||||
|
filterCoeffsStereo = new SAMPLETYPE[length*2];
|
||||||
|
|
||||||
|
#ifdef SOUNDTOUCH_FLOAT_SAMPLES
|
||||||
|
// scale coefficients already here if using floating samples
|
||||||
|
const double scale = ::pow(0.5, (int)resultDivFactor);;
|
||||||
|
#else
|
||||||
|
const short scale = 1;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
for (uint i = 0; i < length; i ++)
|
||||||
|
{
|
||||||
|
filterCoeffs[i] = (SAMPLETYPE)(coeffs[i] * scale);
|
||||||
|
// create also stereo set of filter coefficients: this allows compiler
|
||||||
|
// to autovectorize filter evaluation much more efficiently
|
||||||
|
filterCoeffsStereo[2 * i] = (SAMPLETYPE)(coeffs[i] * scale);
|
||||||
|
filterCoeffsStereo[2 * i + 1] = (SAMPLETYPE)(coeffs[i] * scale);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -194,31 +241,38 @@ uint FIRFilter::getLength() const
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Applies the filter to the given sequence of samples.
|
// Applies the filter to the given sequence of samples.
|
||||||
//
|
//
|
||||||
// Note : The amount of outputted samples is by value of 'filter_length'
|
// Note : The amount of outputted samples is by value of 'filter_length'
|
||||||
// smaller than the amount of input samples.
|
// smaller than the amount of input samples.
|
||||||
uint FIRFilter::evaluate(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples, uint numChannels) const
|
uint FIRFilter::evaluate(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples, uint numChannels)
|
||||||
{
|
{
|
||||||
assert(numChannels == 1 || numChannels == 2);
|
|
||||||
|
|
||||||
assert(length > 0);
|
assert(length > 0);
|
||||||
assert(lengthDiv8 * 8 == length);
|
assert(lengthDiv8 * 8 == length);
|
||||||
|
|
||||||
if (numSamples < length) return 0;
|
if (numSamples < length) return 0;
|
||||||
if (numChannels == 2)
|
|
||||||
|
#ifndef USE_MULTICH_ALWAYS
|
||||||
|
if (numChannels == 1)
|
||||||
{
|
{
|
||||||
return evaluateFilterStereo(dest, src, numSamples);
|
|
||||||
} else {
|
|
||||||
return evaluateFilterMono(dest, src, numSamples);
|
return evaluateFilterMono(dest, src, numSamples);
|
||||||
}
|
}
|
||||||
|
else if (numChannels == 2)
|
||||||
|
{
|
||||||
|
return evaluateFilterStereo(dest, src, numSamples);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
#endif // USE_MULTICH_ALWAYS
|
||||||
|
{
|
||||||
|
assert(numChannels > 0);
|
||||||
|
return evaluateFilterMulti(dest, src, numSamples, numChannels);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Operator 'new' is overloaded so that it automatically creates a suitable instance
|
// Operator 'new' is overloaded so that it automatically creates a suitable instance
|
||||||
// depending on if we've a MMX-capable CPU available or not.
|
// depending on if we've a MMX-capable CPU available or not.
|
||||||
void * FIRFilter::operator new(size_t s)
|
void * FIRFilter::operator new(size_t)
|
||||||
{
|
{
|
||||||
// Notice! don't use "new FIRFilter" directly, use "newInstance" to create a new instance instead!
|
// Notice! don't use "new FIRFilter" directly, use "newInstance" to create a new instance instead!
|
||||||
ST_THROW_RT_ERROR("Error in FIRFilter::new: Don't use 'new FIRFilter', use 'newInstance' member instead!");
|
ST_THROW_RT_ERROR("Error in FIRFilter::new: Don't use 'new FIRFilter', use 'newInstance' member instead!");
|
||||||
@ -231,6 +285,7 @@ FIRFilter * FIRFilter::newInstance()
|
|||||||
uint uExtensions;
|
uint uExtensions;
|
||||||
|
|
||||||
uExtensions = detectCPUextensions();
|
uExtensions = detectCPUextensions();
|
||||||
|
(void)uExtensions;
|
||||||
|
|
||||||
// Check if MMX/SSE instruction set extensions supported by CPU
|
// Check if MMX/SSE instruction set extensions supported by CPU
|
||||||
|
|
||||||
|
|||||||
@ -11,13 +11,6 @@
|
|||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Last changed : $Date$
|
|
||||||
// File revision : $Revision: 4 $
|
|
||||||
//
|
|
||||||
// $Id$
|
|
||||||
//
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// License :
|
// License :
|
||||||
//
|
//
|
||||||
// SoundTouch audio processing library
|
// SoundTouch audio processing library
|
||||||
@ -59,11 +52,9 @@ protected:
|
|||||||
// Result divider factor in 2^k format
|
// Result divider factor in 2^k format
|
||||||
uint resultDivFactor;
|
uint resultDivFactor;
|
||||||
|
|
||||||
// Result divider value.
|
|
||||||
SAMPLETYPE resultDivider;
|
|
||||||
|
|
||||||
// Memory for filter coefficients
|
// Memory for filter coefficients
|
||||||
SAMPLETYPE *filterCoeffs;
|
SAMPLETYPE *filterCoeffs;
|
||||||
|
SAMPLETYPE *filterCoeffsStereo;
|
||||||
|
|
||||||
virtual uint evaluateFilterStereo(SAMPLETYPE *dest,
|
virtual uint evaluateFilterStereo(SAMPLETYPE *dest,
|
||||||
const SAMPLETYPE *src,
|
const SAMPLETYPE *src,
|
||||||
@ -71,6 +62,7 @@ protected:
|
|||||||
virtual uint evaluateFilterMono(SAMPLETYPE *dest,
|
virtual uint evaluateFilterMono(SAMPLETYPE *dest,
|
||||||
const SAMPLETYPE *src,
|
const SAMPLETYPE *src,
|
||||||
uint numSamples) const;
|
uint numSamples) const;
|
||||||
|
virtual uint evaluateFilterMulti(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples, uint numChannels);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
FIRFilter();
|
FIRFilter();
|
||||||
@ -90,7 +82,7 @@ public:
|
|||||||
uint evaluate(SAMPLETYPE *dest,
|
uint evaluate(SAMPLETYPE *dest,
|
||||||
const SAMPLETYPE *src,
|
const SAMPLETYPE *src,
|
||||||
uint numSamples,
|
uint numSamples,
|
||||||
uint numChannels) const;
|
uint numChannels);
|
||||||
|
|
||||||
uint getLength() const;
|
uint getLength() const;
|
||||||
|
|
||||||
@ -111,12 +103,12 @@ public:
|
|||||||
short *filterCoeffsUnalign;
|
short *filterCoeffsUnalign;
|
||||||
short *filterCoeffsAlign;
|
short *filterCoeffsAlign;
|
||||||
|
|
||||||
virtual uint evaluateFilterStereo(short *dest, const short *src, uint numSamples) const;
|
virtual uint evaluateFilterStereo(short *dest, const short *src, uint numSamples) const override;
|
||||||
public:
|
public:
|
||||||
FIRFilterMMX();
|
FIRFilterMMX();
|
||||||
~FIRFilterMMX();
|
~FIRFilterMMX();
|
||||||
|
|
||||||
virtual void setCoefficients(const short *coeffs, uint newLength, uint uResultDivFactor);
|
virtual void setCoefficients(const short *coeffs, uint newLength, uint uResultDivFactor) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // SOUNDTOUCH_ALLOW_MMX
|
#endif // SOUNDTOUCH_ALLOW_MMX
|
||||||
@ -130,12 +122,12 @@ public:
|
|||||||
float *filterCoeffsUnalign;
|
float *filterCoeffsUnalign;
|
||||||
float *filterCoeffsAlign;
|
float *filterCoeffsAlign;
|
||||||
|
|
||||||
virtual uint evaluateFilterStereo(float *dest, const float *src, uint numSamples) const;
|
virtual uint evaluateFilterStereo(float *dest, const float *src, uint numSamples) const override;
|
||||||
public:
|
public:
|
||||||
FIRFilterSSE();
|
FIRFilterSSE();
|
||||||
~FIRFilterSSE();
|
~FIRFilterSSE();
|
||||||
|
|
||||||
virtual void setCoefficients(const float *coeffs, uint newLength, uint uResultDivFactor);
|
virtual void setCoefficients(const float *coeffs, uint newLength, uint uResultDivFactor) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // SOUNDTOUCH_ALLOW_SSE
|
#endif // SOUNDTOUCH_ALLOW_SSE
|
||||||
|
|||||||
196
source/SoundTouch/InterpolateCubic.cpp
Normal file
196
source/SoundTouch/InterpolateCubic.cpp
Normal file
@ -0,0 +1,196 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
///
|
||||||
|
/// Cubic interpolation routine.
|
||||||
|
///
|
||||||
|
/// Author : Copyright (c) Olli Parviainen
|
||||||
|
/// Author e-mail : oparviai 'at' iki.fi
|
||||||
|
/// SoundTouch WWW: http://www.surina.net/soundtouch
|
||||||
|
///
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// License :
|
||||||
|
//
|
||||||
|
// SoundTouch audio processing library
|
||||||
|
// Copyright (c) Olli Parviainen
|
||||||
|
//
|
||||||
|
// This library is free software; you can redistribute it and/or
|
||||||
|
// modify it under the terms of the GNU Lesser General Public
|
||||||
|
// License as published by the Free Software Foundation; either
|
||||||
|
// version 2.1 of the License, or (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This library 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
|
||||||
|
// Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public
|
||||||
|
// License along with this library; if not, write to the Free Software
|
||||||
|
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
//
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include "InterpolateCubic.h"
|
||||||
|
#include "STTypes.h"
|
||||||
|
|
||||||
|
using namespace soundtouch;
|
||||||
|
|
||||||
|
// cubic interpolation coefficients
|
||||||
|
static const float _coeffs[]=
|
||||||
|
{ -0.5f, 1.0f, -0.5f, 0.0f,
|
||||||
|
1.5f, -2.5f, 0.0f, 1.0f,
|
||||||
|
-1.5f, 2.0f, 0.5f, 0.0f,
|
||||||
|
0.5f, -0.5f, 0.0f, 0.0f};
|
||||||
|
|
||||||
|
|
||||||
|
InterpolateCubic::InterpolateCubic()
|
||||||
|
{
|
||||||
|
fract = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void InterpolateCubic::resetRegisters()
|
||||||
|
{
|
||||||
|
fract = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Transpose mono audio. Returns number of produced output samples, and
|
||||||
|
/// updates "srcSamples" to amount of consumed source samples
|
||||||
|
int InterpolateCubic::transposeMono(SAMPLETYPE *pdest,
|
||||||
|
const SAMPLETYPE *psrc,
|
||||||
|
int &srcSamples)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
int srcSampleEnd = srcSamples - 4;
|
||||||
|
int srcCount = 0;
|
||||||
|
|
||||||
|
i = 0;
|
||||||
|
while (srcCount < srcSampleEnd)
|
||||||
|
{
|
||||||
|
float out;
|
||||||
|
const float x3 = 1.0f;
|
||||||
|
const float x2 = (float)fract; // x
|
||||||
|
const float x1 = x2*x2; // x^2
|
||||||
|
const float x0 = x1*x2; // x^3
|
||||||
|
float y0, y1, y2, y3;
|
||||||
|
|
||||||
|
assert(fract < 1.0);
|
||||||
|
|
||||||
|
y0 = _coeffs[0] * x0 + _coeffs[1] * x1 + _coeffs[2] * x2 + _coeffs[3] * x3;
|
||||||
|
y1 = _coeffs[4] * x0 + _coeffs[5] * x1 + _coeffs[6] * x2 + _coeffs[7] * x3;
|
||||||
|
y2 = _coeffs[8] * x0 + _coeffs[9] * x1 + _coeffs[10] * x2 + _coeffs[11] * x3;
|
||||||
|
y3 = _coeffs[12] * x0 + _coeffs[13] * x1 + _coeffs[14] * x2 + _coeffs[15] * x3;
|
||||||
|
|
||||||
|
out = y0 * psrc[0] + y1 * psrc[1] + y2 * psrc[2] + y3 * psrc[3];
|
||||||
|
|
||||||
|
pdest[i] = (SAMPLETYPE)out;
|
||||||
|
i ++;
|
||||||
|
|
||||||
|
// update position fraction
|
||||||
|
fract += rate;
|
||||||
|
// update whole positions
|
||||||
|
int whole = (int)fract;
|
||||||
|
fract -= whole;
|
||||||
|
psrc += whole;
|
||||||
|
srcCount += whole;
|
||||||
|
}
|
||||||
|
srcSamples = srcCount;
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Transpose stereo audio. Returns number of produced output samples, and
|
||||||
|
/// updates "srcSamples" to amount of consumed source samples
|
||||||
|
int InterpolateCubic::transposeStereo(SAMPLETYPE *pdest,
|
||||||
|
const SAMPLETYPE *psrc,
|
||||||
|
int &srcSamples)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
int srcSampleEnd = srcSamples - 4;
|
||||||
|
int srcCount = 0;
|
||||||
|
|
||||||
|
i = 0;
|
||||||
|
while (srcCount < srcSampleEnd)
|
||||||
|
{
|
||||||
|
const float x3 = 1.0f;
|
||||||
|
const float x2 = (float)fract; // x
|
||||||
|
const float x1 = x2*x2; // x^2
|
||||||
|
const float x0 = x1*x2; // x^3
|
||||||
|
float y0, y1, y2, y3;
|
||||||
|
float out0, out1;
|
||||||
|
|
||||||
|
assert(fract < 1.0);
|
||||||
|
|
||||||
|
y0 = _coeffs[0] * x0 + _coeffs[1] * x1 + _coeffs[2] * x2 + _coeffs[3] * x3;
|
||||||
|
y1 = _coeffs[4] * x0 + _coeffs[5] * x1 + _coeffs[6] * x2 + _coeffs[7] * x3;
|
||||||
|
y2 = _coeffs[8] * x0 + _coeffs[9] * x1 + _coeffs[10] * x2 + _coeffs[11] * x3;
|
||||||
|
y3 = _coeffs[12] * x0 + _coeffs[13] * x1 + _coeffs[14] * x2 + _coeffs[15] * x3;
|
||||||
|
|
||||||
|
out0 = y0 * psrc[0] + y1 * psrc[2] + y2 * psrc[4] + y3 * psrc[6];
|
||||||
|
out1 = y0 * psrc[1] + y1 * psrc[3] + y2 * psrc[5] + y3 * psrc[7];
|
||||||
|
|
||||||
|
pdest[2*i] = (SAMPLETYPE)out0;
|
||||||
|
pdest[2*i+1] = (SAMPLETYPE)out1;
|
||||||
|
i ++;
|
||||||
|
|
||||||
|
// update position fraction
|
||||||
|
fract += rate;
|
||||||
|
// update whole positions
|
||||||
|
int whole = (int)fract;
|
||||||
|
fract -= whole;
|
||||||
|
psrc += 2*whole;
|
||||||
|
srcCount += whole;
|
||||||
|
}
|
||||||
|
srcSamples = srcCount;
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Transpose multi-channel audio. Returns number of produced output samples, and
|
||||||
|
/// updates "srcSamples" to amount of consumed source samples
|
||||||
|
int InterpolateCubic::transposeMulti(SAMPLETYPE *pdest,
|
||||||
|
const SAMPLETYPE *psrc,
|
||||||
|
int &srcSamples)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
int srcSampleEnd = srcSamples - 4;
|
||||||
|
int srcCount = 0;
|
||||||
|
|
||||||
|
i = 0;
|
||||||
|
while (srcCount < srcSampleEnd)
|
||||||
|
{
|
||||||
|
const float x3 = 1.0f;
|
||||||
|
const float x2 = (float)fract; // x
|
||||||
|
const float x1 = x2*x2; // x^2
|
||||||
|
const float x0 = x1*x2; // x^3
|
||||||
|
float y0, y1, y2, y3;
|
||||||
|
|
||||||
|
assert(fract < 1.0);
|
||||||
|
|
||||||
|
y0 = _coeffs[0] * x0 + _coeffs[1] * x1 + _coeffs[2] * x2 + _coeffs[3] * x3;
|
||||||
|
y1 = _coeffs[4] * x0 + _coeffs[5] * x1 + _coeffs[6] * x2 + _coeffs[7] * x3;
|
||||||
|
y2 = _coeffs[8] * x0 + _coeffs[9] * x1 + _coeffs[10] * x2 + _coeffs[11] * x3;
|
||||||
|
y3 = _coeffs[12] * x0 + _coeffs[13] * x1 + _coeffs[14] * x2 + _coeffs[15] * x3;
|
||||||
|
|
||||||
|
for (int c = 0; c < numChannels; c ++)
|
||||||
|
{
|
||||||
|
float out;
|
||||||
|
out = y0 * psrc[c] + y1 * psrc[c + numChannels] + y2 * psrc[c + 2 * numChannels] + y3 * psrc[c + 3 * numChannels];
|
||||||
|
pdest[0] = (SAMPLETYPE)out;
|
||||||
|
pdest ++;
|
||||||
|
}
|
||||||
|
i ++;
|
||||||
|
|
||||||
|
// update position fraction
|
||||||
|
fract += rate;
|
||||||
|
// update whole positions
|
||||||
|
int whole = (int)fract;
|
||||||
|
fract -= whole;
|
||||||
|
psrc += numChannels*whole;
|
||||||
|
srcCount += whole;
|
||||||
|
}
|
||||||
|
srcSamples = srcCount;
|
||||||
|
return i;
|
||||||
|
}
|
||||||
69
source/SoundTouch/InterpolateCubic.h
Normal file
69
source/SoundTouch/InterpolateCubic.h
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
///
|
||||||
|
/// Cubic interpolation routine.
|
||||||
|
///
|
||||||
|
/// Author : Copyright (c) Olli Parviainen
|
||||||
|
/// Author e-mail : oparviai 'at' iki.fi
|
||||||
|
/// SoundTouch WWW: http://www.surina.net/soundtouch
|
||||||
|
///
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// License :
|
||||||
|
//
|
||||||
|
// SoundTouch audio processing library
|
||||||
|
// Copyright (c) Olli Parviainen
|
||||||
|
//
|
||||||
|
// This library is free software; you can redistribute it and/or
|
||||||
|
// modify it under the terms of the GNU Lesser General Public
|
||||||
|
// License as published by the Free Software Foundation; either
|
||||||
|
// version 2.1 of the License, or (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This library 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
|
||||||
|
// Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public
|
||||||
|
// License along with this library; if not, write to the Free Software
|
||||||
|
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
//
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#ifndef _InterpolateCubic_H_
|
||||||
|
#define _InterpolateCubic_H_
|
||||||
|
|
||||||
|
#include "RateTransposer.h"
|
||||||
|
#include "STTypes.h"
|
||||||
|
|
||||||
|
namespace soundtouch
|
||||||
|
{
|
||||||
|
|
||||||
|
class InterpolateCubic : public TransposerBase
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
virtual int transposeMono(SAMPLETYPE *dest,
|
||||||
|
const SAMPLETYPE *src,
|
||||||
|
int &srcSamples) override;
|
||||||
|
virtual int transposeStereo(SAMPLETYPE *dest,
|
||||||
|
const SAMPLETYPE *src,
|
||||||
|
int &srcSamples) override;
|
||||||
|
virtual int transposeMulti(SAMPLETYPE *dest,
|
||||||
|
const SAMPLETYPE *src,
|
||||||
|
int &srcSamples) override;
|
||||||
|
|
||||||
|
double fract;
|
||||||
|
|
||||||
|
public:
|
||||||
|
InterpolateCubic();
|
||||||
|
|
||||||
|
virtual void resetRegisters() override;
|
||||||
|
|
||||||
|
virtual int getLatency() const override
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
296
source/SoundTouch/InterpolateLinear.cpp
Normal file
296
source/SoundTouch/InterpolateLinear.cpp
Normal file
@ -0,0 +1,296 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
///
|
||||||
|
/// Linear interpolation algorithm.
|
||||||
|
///
|
||||||
|
/// Author : Copyright (c) Olli Parviainen
|
||||||
|
/// Author e-mail : oparviai 'at' iki.fi
|
||||||
|
/// SoundTouch WWW: http://www.surina.net/soundtouch
|
||||||
|
///
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// License :
|
||||||
|
//
|
||||||
|
// SoundTouch audio processing library
|
||||||
|
// Copyright (c) Olli Parviainen
|
||||||
|
//
|
||||||
|
// This library is free software; you can redistribute it and/or
|
||||||
|
// modify it under the terms of the GNU Lesser General Public
|
||||||
|
// License as published by the Free Software Foundation; either
|
||||||
|
// version 2.1 of the License, or (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This library 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
|
||||||
|
// Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public
|
||||||
|
// License along with this library; if not, write to the Free Software
|
||||||
|
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
//
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include "InterpolateLinear.h"
|
||||||
|
|
||||||
|
using namespace soundtouch;
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// InterpolateLinearInteger - integer arithmetic implementation
|
||||||
|
//
|
||||||
|
|
||||||
|
/// fixed-point interpolation routine precision
|
||||||
|
#define SCALE 65536
|
||||||
|
|
||||||
|
|
||||||
|
// Constructor
|
||||||
|
InterpolateLinearInteger::InterpolateLinearInteger() : TransposerBase()
|
||||||
|
{
|
||||||
|
// Notice: use local function calling syntax for sake of clarity,
|
||||||
|
// to indicate the fact that C++ constructor can't call virtual functions.
|
||||||
|
resetRegisters();
|
||||||
|
setRate(1.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void InterpolateLinearInteger::resetRegisters()
|
||||||
|
{
|
||||||
|
iFract = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Transposes the sample rate of the given samples using linear interpolation.
|
||||||
|
// 'Mono' version of the routine. Returns the number of samples returned in
|
||||||
|
// the "dest" buffer
|
||||||
|
int InterpolateLinearInteger::transposeMono(SAMPLETYPE *dest, const SAMPLETYPE *src, int &srcSamples)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
int srcSampleEnd = srcSamples - 1;
|
||||||
|
int srcCount = 0;
|
||||||
|
|
||||||
|
i = 0;
|
||||||
|
while (srcCount < srcSampleEnd)
|
||||||
|
{
|
||||||
|
LONG_SAMPLETYPE temp;
|
||||||
|
|
||||||
|
assert(iFract < SCALE);
|
||||||
|
|
||||||
|
temp = (SCALE - iFract) * src[0] + iFract * src[1];
|
||||||
|
dest[i] = (SAMPLETYPE)(temp / SCALE);
|
||||||
|
i++;
|
||||||
|
|
||||||
|
iFract += iRate;
|
||||||
|
|
||||||
|
int iWhole = iFract / SCALE;
|
||||||
|
iFract -= iWhole * SCALE;
|
||||||
|
srcCount += iWhole;
|
||||||
|
src += iWhole;
|
||||||
|
}
|
||||||
|
srcSamples = srcCount;
|
||||||
|
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Transposes the sample rate of the given samples using linear interpolation.
|
||||||
|
// 'Stereo' version of the routine. Returns the number of samples returned in
|
||||||
|
// the "dest" buffer
|
||||||
|
int InterpolateLinearInteger::transposeStereo(SAMPLETYPE *dest, const SAMPLETYPE *src, int &srcSamples)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
int srcSampleEnd = srcSamples - 1;
|
||||||
|
int srcCount = 0;
|
||||||
|
|
||||||
|
i = 0;
|
||||||
|
while (srcCount < srcSampleEnd)
|
||||||
|
{
|
||||||
|
LONG_SAMPLETYPE temp0;
|
||||||
|
LONG_SAMPLETYPE temp1;
|
||||||
|
|
||||||
|
assert(iFract < SCALE);
|
||||||
|
|
||||||
|
temp0 = (SCALE - iFract) * src[0] + iFract * src[2];
|
||||||
|
temp1 = (SCALE - iFract) * src[1] + iFract * src[3];
|
||||||
|
dest[0] = (SAMPLETYPE)(temp0 / SCALE);
|
||||||
|
dest[1] = (SAMPLETYPE)(temp1 / SCALE);
|
||||||
|
dest += 2;
|
||||||
|
i++;
|
||||||
|
|
||||||
|
iFract += iRate;
|
||||||
|
|
||||||
|
int iWhole = iFract / SCALE;
|
||||||
|
iFract -= iWhole * SCALE;
|
||||||
|
srcCount += iWhole;
|
||||||
|
src += 2*iWhole;
|
||||||
|
}
|
||||||
|
srcSamples = srcCount;
|
||||||
|
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int InterpolateLinearInteger::transposeMulti(SAMPLETYPE *dest, const SAMPLETYPE *src, int &srcSamples)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
int srcSampleEnd = srcSamples - 1;
|
||||||
|
int srcCount = 0;
|
||||||
|
|
||||||
|
i = 0;
|
||||||
|
while (srcCount < srcSampleEnd)
|
||||||
|
{
|
||||||
|
LONG_SAMPLETYPE temp, vol1;
|
||||||
|
|
||||||
|
assert(iFract < SCALE);
|
||||||
|
vol1 = (LONG_SAMPLETYPE)(SCALE - iFract);
|
||||||
|
for (int c = 0; c < numChannels; c ++)
|
||||||
|
{
|
||||||
|
temp = vol1 * src[c] + iFract * src[c + numChannels];
|
||||||
|
dest[0] = (SAMPLETYPE)(temp / SCALE);
|
||||||
|
dest ++;
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
|
||||||
|
iFract += iRate;
|
||||||
|
|
||||||
|
int iWhole = iFract / SCALE;
|
||||||
|
iFract -= iWhole * SCALE;
|
||||||
|
srcCount += iWhole;
|
||||||
|
src += iWhole * numChannels;
|
||||||
|
}
|
||||||
|
srcSamples = srcCount;
|
||||||
|
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Sets new target iRate. Normal iRate = 1.0, smaller values represent slower
|
||||||
|
// iRate, larger faster iRates.
|
||||||
|
void InterpolateLinearInteger::setRate(double newRate)
|
||||||
|
{
|
||||||
|
iRate = (int)(newRate * SCALE + 0.5);
|
||||||
|
TransposerBase::setRate(newRate);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// InterpolateLinearFloat - floating point arithmetic implementation
|
||||||
|
//
|
||||||
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
|
||||||
|
// Constructor
|
||||||
|
InterpolateLinearFloat::InterpolateLinearFloat() : TransposerBase()
|
||||||
|
{
|
||||||
|
// Notice: use local function calling syntax for sake of clarity,
|
||||||
|
// to indicate the fact that C++ constructor can't call virtual functions.
|
||||||
|
resetRegisters();
|
||||||
|
setRate(1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void InterpolateLinearFloat::resetRegisters()
|
||||||
|
{
|
||||||
|
fract = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Transposes the sample rate of the given samples using linear interpolation.
|
||||||
|
// 'Mono' version of the routine. Returns the number of samples returned in
|
||||||
|
// the "dest" buffer
|
||||||
|
int InterpolateLinearFloat::transposeMono(SAMPLETYPE *dest, const SAMPLETYPE *src, int &srcSamples)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
int srcSampleEnd = srcSamples - 1;
|
||||||
|
int srcCount = 0;
|
||||||
|
|
||||||
|
i = 0;
|
||||||
|
while (srcCount < srcSampleEnd)
|
||||||
|
{
|
||||||
|
double out;
|
||||||
|
assert(fract < 1.0);
|
||||||
|
|
||||||
|
out = (1.0 - fract) * src[0] + fract * src[1];
|
||||||
|
dest[i] = (SAMPLETYPE)out;
|
||||||
|
i ++;
|
||||||
|
|
||||||
|
// update position fraction
|
||||||
|
fract += rate;
|
||||||
|
// update whole positions
|
||||||
|
int whole = (int)fract;
|
||||||
|
fract -= whole;
|
||||||
|
src += whole;
|
||||||
|
srcCount += whole;
|
||||||
|
}
|
||||||
|
srcSamples = srcCount;
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Transposes the sample rate of the given samples using linear interpolation.
|
||||||
|
// 'Mono' version of the routine. Returns the number of samples returned in
|
||||||
|
// the "dest" buffer
|
||||||
|
int InterpolateLinearFloat::transposeStereo(SAMPLETYPE *dest, const SAMPLETYPE *src, int &srcSamples)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
int srcSampleEnd = srcSamples - 1;
|
||||||
|
int srcCount = 0;
|
||||||
|
|
||||||
|
i = 0;
|
||||||
|
while (srcCount < srcSampleEnd)
|
||||||
|
{
|
||||||
|
double out0, out1;
|
||||||
|
assert(fract < 1.0);
|
||||||
|
|
||||||
|
out0 = (1.0 - fract) * src[0] + fract * src[2];
|
||||||
|
out1 = (1.0 - fract) * src[1] + fract * src[3];
|
||||||
|
dest[2*i] = (SAMPLETYPE)out0;
|
||||||
|
dest[2*i+1] = (SAMPLETYPE)out1;
|
||||||
|
i ++;
|
||||||
|
|
||||||
|
// update position fraction
|
||||||
|
fract += rate;
|
||||||
|
// update whole positions
|
||||||
|
int whole = (int)fract;
|
||||||
|
fract -= whole;
|
||||||
|
src += 2*whole;
|
||||||
|
srcCount += whole;
|
||||||
|
}
|
||||||
|
srcSamples = srcCount;
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int InterpolateLinearFloat::transposeMulti(SAMPLETYPE *dest, const SAMPLETYPE *src, int &srcSamples)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
int srcSampleEnd = srcSamples - 1;
|
||||||
|
int srcCount = 0;
|
||||||
|
|
||||||
|
i = 0;
|
||||||
|
while (srcCount < srcSampleEnd)
|
||||||
|
{
|
||||||
|
float temp, vol1, fract_float;
|
||||||
|
|
||||||
|
vol1 = (float)(1.0 - fract);
|
||||||
|
fract_float = (float)fract;
|
||||||
|
for (int c = 0; c < numChannels; c ++)
|
||||||
|
{
|
||||||
|
temp = vol1 * src[c] + fract_float * src[c + numChannels];
|
||||||
|
*dest = (SAMPLETYPE)temp;
|
||||||
|
dest ++;
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
|
||||||
|
fract += rate;
|
||||||
|
|
||||||
|
int iWhole = (int)fract;
|
||||||
|
fract -= iWhole;
|
||||||
|
srcCount += iWhole;
|
||||||
|
src += iWhole * numChannels;
|
||||||
|
}
|
||||||
|
srcSamples = srcCount;
|
||||||
|
|
||||||
|
return i;
|
||||||
|
}
|
||||||
98
source/SoundTouch/InterpolateLinear.h
Normal file
98
source/SoundTouch/InterpolateLinear.h
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
///
|
||||||
|
/// Linear interpolation routine.
|
||||||
|
///
|
||||||
|
/// Author : Copyright (c) Olli Parviainen
|
||||||
|
/// Author e-mail : oparviai 'at' iki.fi
|
||||||
|
/// SoundTouch WWW: http://www.surina.net/soundtouch
|
||||||
|
///
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// License :
|
||||||
|
//
|
||||||
|
// SoundTouch audio processing library
|
||||||
|
// Copyright (c) Olli Parviainen
|
||||||
|
//
|
||||||
|
// This library is free software; you can redistribute it and/or
|
||||||
|
// modify it under the terms of the GNU Lesser General Public
|
||||||
|
// License as published by the Free Software Foundation; either
|
||||||
|
// version 2.1 of the License, or (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This library 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
|
||||||
|
// Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public
|
||||||
|
// License along with this library; if not, write to the Free Software
|
||||||
|
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
//
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#ifndef _InterpolateLinear_H_
|
||||||
|
#define _InterpolateLinear_H_
|
||||||
|
|
||||||
|
#include "RateTransposer.h"
|
||||||
|
#include "STTypes.h"
|
||||||
|
|
||||||
|
namespace soundtouch
|
||||||
|
{
|
||||||
|
|
||||||
|
/// Linear transposer class that uses integer arithmetic
|
||||||
|
class InterpolateLinearInteger : public TransposerBase
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
int iFract;
|
||||||
|
int iRate;
|
||||||
|
|
||||||
|
virtual int transposeMono(SAMPLETYPE *dest,
|
||||||
|
const SAMPLETYPE *src,
|
||||||
|
int &srcSamples) override;
|
||||||
|
virtual int transposeStereo(SAMPLETYPE *dest,
|
||||||
|
const SAMPLETYPE *src,
|
||||||
|
int &srcSamples) override;
|
||||||
|
virtual int transposeMulti(SAMPLETYPE *dest, const SAMPLETYPE *src, int &srcSamples) override;
|
||||||
|
public:
|
||||||
|
InterpolateLinearInteger();
|
||||||
|
|
||||||
|
/// Sets new target rate. Normal rate = 1.0, smaller values represent slower
|
||||||
|
/// rate, larger faster rates.
|
||||||
|
virtual void setRate(double newRate) override;
|
||||||
|
|
||||||
|
virtual void resetRegisters() override;
|
||||||
|
|
||||||
|
virtual int getLatency() const override
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/// Linear transposer class that uses floating point arithmetic
|
||||||
|
class InterpolateLinearFloat : public TransposerBase
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
double fract;
|
||||||
|
|
||||||
|
virtual int transposeMono(SAMPLETYPE *dest,
|
||||||
|
const SAMPLETYPE *src,
|
||||||
|
int &srcSamples);
|
||||||
|
virtual int transposeStereo(SAMPLETYPE *dest,
|
||||||
|
const SAMPLETYPE *src,
|
||||||
|
int &srcSamples);
|
||||||
|
virtual int transposeMulti(SAMPLETYPE *dest, const SAMPLETYPE *src, int &srcSamples);
|
||||||
|
|
||||||
|
public:
|
||||||
|
InterpolateLinearFloat();
|
||||||
|
|
||||||
|
virtual void resetRegisters();
|
||||||
|
|
||||||
|
int getLatency() const
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
181
source/SoundTouch/InterpolateShannon.cpp
Normal file
181
source/SoundTouch/InterpolateShannon.cpp
Normal file
@ -0,0 +1,181 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
///
|
||||||
|
/// Sample interpolation routine using 8-tap band-limited Shannon interpolation
|
||||||
|
/// with kaiser window.
|
||||||
|
///
|
||||||
|
/// Notice. This algorithm is remarkably much heavier than linear or cubic
|
||||||
|
/// interpolation, and not remarkably better than cubic algorithm. Thus mostly
|
||||||
|
/// for experimental purposes
|
||||||
|
///
|
||||||
|
/// Author : Copyright (c) Olli Parviainen
|
||||||
|
/// Author e-mail : oparviai 'at' iki.fi
|
||||||
|
/// SoundTouch WWW: http://www.surina.net/soundtouch
|
||||||
|
///
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// License :
|
||||||
|
//
|
||||||
|
// SoundTouch audio processing library
|
||||||
|
// Copyright (c) Olli Parviainen
|
||||||
|
//
|
||||||
|
// This library is free software; you can redistribute it and/or
|
||||||
|
// modify it under the terms of the GNU Lesser General Public
|
||||||
|
// License as published by the Free Software Foundation; either
|
||||||
|
// version 2.1 of the License, or (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This library 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
|
||||||
|
// Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public
|
||||||
|
// License along with this library; if not, write to the Free Software
|
||||||
|
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
//
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#include <math.h>
|
||||||
|
#include "InterpolateShannon.h"
|
||||||
|
#include "STTypes.h"
|
||||||
|
|
||||||
|
using namespace soundtouch;
|
||||||
|
|
||||||
|
|
||||||
|
/// Kaiser window with beta = 2.0
|
||||||
|
/// Values scaled down by 5% to avoid overflows
|
||||||
|
static const double _kaiser8[8] =
|
||||||
|
{
|
||||||
|
0.41778693317814,
|
||||||
|
0.64888025049173,
|
||||||
|
0.83508562409944,
|
||||||
|
0.93887857733412,
|
||||||
|
0.93887857733412,
|
||||||
|
0.83508562409944,
|
||||||
|
0.64888025049173,
|
||||||
|
0.41778693317814
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
InterpolateShannon::InterpolateShannon()
|
||||||
|
{
|
||||||
|
fract = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void InterpolateShannon::resetRegisters()
|
||||||
|
{
|
||||||
|
fract = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#define PI 3.1415926536
|
||||||
|
#define sinc(x) (sin(PI * (x)) / (PI * (x)))
|
||||||
|
|
||||||
|
/// Transpose mono audio. Returns number of produced output samples, and
|
||||||
|
/// updates "srcSamples" to amount of consumed source samples
|
||||||
|
int InterpolateShannon::transposeMono(SAMPLETYPE *pdest,
|
||||||
|
const SAMPLETYPE *psrc,
|
||||||
|
int &srcSamples)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
int srcSampleEnd = srcSamples - 8;
|
||||||
|
int srcCount = 0;
|
||||||
|
|
||||||
|
i = 0;
|
||||||
|
while (srcCount < srcSampleEnd)
|
||||||
|
{
|
||||||
|
double out;
|
||||||
|
assert(fract < 1.0);
|
||||||
|
|
||||||
|
out = psrc[0] * sinc(-3.0 - fract) * _kaiser8[0];
|
||||||
|
out += psrc[1] * sinc(-2.0 - fract) * _kaiser8[1];
|
||||||
|
out += psrc[2] * sinc(-1.0 - fract) * _kaiser8[2];
|
||||||
|
if (fract < 1e-6)
|
||||||
|
{
|
||||||
|
out += psrc[3] * _kaiser8[3]; // sinc(0) = 1
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
out += psrc[3] * sinc(- fract) * _kaiser8[3];
|
||||||
|
}
|
||||||
|
out += psrc[4] * sinc( 1.0 - fract) * _kaiser8[4];
|
||||||
|
out += psrc[5] * sinc( 2.0 - fract) * _kaiser8[5];
|
||||||
|
out += psrc[6] * sinc( 3.0 - fract) * _kaiser8[6];
|
||||||
|
out += psrc[7] * sinc( 4.0 - fract) * _kaiser8[7];
|
||||||
|
|
||||||
|
pdest[i] = (SAMPLETYPE)out;
|
||||||
|
i ++;
|
||||||
|
|
||||||
|
// update position fraction
|
||||||
|
fract += rate;
|
||||||
|
// update whole positions
|
||||||
|
int whole = (int)fract;
|
||||||
|
fract -= whole;
|
||||||
|
psrc += whole;
|
||||||
|
srcCount += whole;
|
||||||
|
}
|
||||||
|
srcSamples = srcCount;
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Transpose stereo audio. Returns number of produced output samples, and
|
||||||
|
/// updates "srcSamples" to amount of consumed source samples
|
||||||
|
int InterpolateShannon::transposeStereo(SAMPLETYPE *pdest,
|
||||||
|
const SAMPLETYPE *psrc,
|
||||||
|
int &srcSamples)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
int srcSampleEnd = srcSamples - 8;
|
||||||
|
int srcCount = 0;
|
||||||
|
|
||||||
|
i = 0;
|
||||||
|
while (srcCount < srcSampleEnd)
|
||||||
|
{
|
||||||
|
double out0, out1, w;
|
||||||
|
assert(fract < 1.0);
|
||||||
|
|
||||||
|
w = sinc(-3.0 - fract) * _kaiser8[0];
|
||||||
|
out0 = psrc[0] * w; out1 = psrc[1] * w;
|
||||||
|
w = sinc(-2.0 - fract) * _kaiser8[1];
|
||||||
|
out0 += psrc[2] * w; out1 += psrc[3] * w;
|
||||||
|
w = sinc(-1.0 - fract) * _kaiser8[2];
|
||||||
|
out0 += psrc[4] * w; out1 += psrc[5] * w;
|
||||||
|
w = _kaiser8[3] * ((fract < 1e-5) ? 1.0 : sinc(- fract)); // sinc(0) = 1
|
||||||
|
out0 += psrc[6] * w; out1 += psrc[7] * w;
|
||||||
|
w = sinc( 1.0 - fract) * _kaiser8[4];
|
||||||
|
out0 += psrc[8] * w; out1 += psrc[9] * w;
|
||||||
|
w = sinc( 2.0 - fract) * _kaiser8[5];
|
||||||
|
out0 += psrc[10] * w; out1 += psrc[11] * w;
|
||||||
|
w = sinc( 3.0 - fract) * _kaiser8[6];
|
||||||
|
out0 += psrc[12] * w; out1 += psrc[13] * w;
|
||||||
|
w = sinc( 4.0 - fract) * _kaiser8[7];
|
||||||
|
out0 += psrc[14] * w; out1 += psrc[15] * w;
|
||||||
|
|
||||||
|
pdest[2*i] = (SAMPLETYPE)out0;
|
||||||
|
pdest[2*i+1] = (SAMPLETYPE)out1;
|
||||||
|
i ++;
|
||||||
|
|
||||||
|
// update position fraction
|
||||||
|
fract += rate;
|
||||||
|
// update whole positions
|
||||||
|
int whole = (int)fract;
|
||||||
|
fract -= whole;
|
||||||
|
psrc += 2*whole;
|
||||||
|
srcCount += whole;
|
||||||
|
}
|
||||||
|
srcSamples = srcCount;
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Transpose stereo audio. Returns number of produced output samples, and
|
||||||
|
/// updates "srcSamples" to amount of consumed source samples
|
||||||
|
int InterpolateShannon::transposeMulti(SAMPLETYPE *,
|
||||||
|
const SAMPLETYPE *,
|
||||||
|
int &)
|
||||||
|
{
|
||||||
|
// not implemented
|
||||||
|
assert(false);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
74
source/SoundTouch/InterpolateShannon.h
Normal file
74
source/SoundTouch/InterpolateShannon.h
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
///
|
||||||
|
/// Sample interpolation routine using 8-tap band-limited Shannon interpolation
|
||||||
|
/// with kaiser window.
|
||||||
|
///
|
||||||
|
/// Notice. This algorithm is remarkably much heavier than linear or cubic
|
||||||
|
/// interpolation, and not remarkably better than cubic algorithm. Thus mostly
|
||||||
|
/// for experimental purposes
|
||||||
|
///
|
||||||
|
/// Author : Copyright (c) Olli Parviainen
|
||||||
|
/// Author e-mail : oparviai 'at' iki.fi
|
||||||
|
/// SoundTouch WWW: http://www.surina.net/soundtouch
|
||||||
|
///
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// License :
|
||||||
|
//
|
||||||
|
// SoundTouch audio processing library
|
||||||
|
// Copyright (c) Olli Parviainen
|
||||||
|
//
|
||||||
|
// This library is free software; you can redistribute it and/or
|
||||||
|
// modify it under the terms of the GNU Lesser General Public
|
||||||
|
// License as published by the Free Software Foundation; either
|
||||||
|
// version 2.1 of the License, or (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This library 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
|
||||||
|
// Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public
|
||||||
|
// License along with this library; if not, write to the Free Software
|
||||||
|
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
//
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#ifndef _InterpolateShannon_H_
|
||||||
|
#define _InterpolateShannon_H_
|
||||||
|
|
||||||
|
#include "RateTransposer.h"
|
||||||
|
#include "STTypes.h"
|
||||||
|
|
||||||
|
namespace soundtouch
|
||||||
|
{
|
||||||
|
|
||||||
|
class InterpolateShannon : public TransposerBase
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
int transposeMono(SAMPLETYPE *dest,
|
||||||
|
const SAMPLETYPE *src,
|
||||||
|
int &srcSamples) override;
|
||||||
|
int transposeStereo(SAMPLETYPE *dest,
|
||||||
|
const SAMPLETYPE *src,
|
||||||
|
int &srcSamples) override;
|
||||||
|
int transposeMulti(SAMPLETYPE *dest,
|
||||||
|
const SAMPLETYPE *src,
|
||||||
|
int &srcSamples) override;
|
||||||
|
|
||||||
|
double fract;
|
||||||
|
|
||||||
|
public:
|
||||||
|
InterpolateShannon();
|
||||||
|
|
||||||
|
void resetRegisters() override;
|
||||||
|
|
||||||
|
virtual int getLatency() const override
|
||||||
|
{
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
@ -1,7 +1,5 @@
|
|||||||
## Process this file with automake to create Makefile.in
|
## Process this file with automake to create Makefile.in
|
||||||
##
|
##
|
||||||
## $Id$
|
|
||||||
##
|
|
||||||
## This file is part of SoundTouch, an audio processing library for pitch/time adjustments
|
## This file is part of SoundTouch, an audio processing library for pitch/time adjustments
|
||||||
##
|
##
|
||||||
## SoundTouch is free software; you can redistribute it and/or modify it under the
|
## SoundTouch is free software; you can redistribute it and/or modify it under the
|
||||||
@ -22,17 +20,20 @@ include $(top_srcdir)/config/am_include.mk
|
|||||||
|
|
||||||
|
|
||||||
# set to something if you want other stuff to be included in the distribution tarball
|
# set to something if you want other stuff to be included in the distribution tarball
|
||||||
EXTRA_DIST=SoundTouch.dsp SoundTouch.dsw SoundTouch.sln SoundTouch.vcproj
|
EXTRA_DIST=SoundTouch.sln SoundTouch.vcxproj
|
||||||
|
|
||||||
noinst_HEADERS=AAFilter.h cpu_detect.h cpu_detect_x86.cpp FIRFilter.h RateTransposer.h TDStretch.h PeakFinder.h
|
noinst_HEADERS=AAFilter.h cpu_detect.h cpu_detect_x86.cpp FIRFilter.h RateTransposer.h TDStretch.h PeakFinder.h \
|
||||||
|
InterpolateCubic.h InterpolateLinear.h InterpolateShannon.h
|
||||||
|
|
||||||
lib_LTLIBRARIES=libSoundTouch.la
|
lib_LTLIBRARIES=libSoundTouch.la
|
||||||
#
|
#
|
||||||
libSoundTouch_la_SOURCES=AAFilter.cpp FIRFilter.cpp FIFOSampleBuffer.cpp RateTransposer.cpp SoundTouch.cpp TDStretch.cpp cpu_detect_x86.cpp BPMDetect.cpp PeakFinder.cpp
|
libSoundTouch_la_SOURCES=AAFilter.cpp FIRFilter.cpp FIFOSampleBuffer.cpp \
|
||||||
|
RateTransposer.cpp SoundTouch.cpp TDStretch.cpp cpu_detect_x86.cpp \
|
||||||
|
BPMDetect.cpp PeakFinder.cpp InterpolateLinear.cpp InterpolateCubic.cpp \
|
||||||
|
InterpolateShannon.cpp
|
||||||
|
|
||||||
# Compiler flags
|
# Compiler flags
|
||||||
AM_CXXFLAGS=-O3 -fcheck-new -I../../include
|
#AM_CXXFLAGS+=
|
||||||
|
|
||||||
# Compile the files that need MMX and SSE individually.
|
# Compile the files that need MMX and SSE individually.
|
||||||
libSoundTouch_la_LIBADD=libSoundTouchMMX.la libSoundTouchSSE.la
|
libSoundTouch_la_LIBADD=libSoundTouchMMX.la libSoundTouchSSE.la
|
||||||
@ -63,6 +64,8 @@ libSoundTouchMMX_la_CXXFLAGS = $(AM_CXXFLAGS)
|
|||||||
libSoundTouchSSE_la_CXXFLAGS = $(AM_CXXFLAGS)
|
libSoundTouchSSE_la_CXXFLAGS = $(AM_CXXFLAGS)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
# Modify the default 0.0.0 to LIB_SONAME.0.0
|
||||||
|
libSoundTouch_la_LDFLAGS=-version-info @LIB_SONAME@
|
||||||
|
|
||||||
# other linking flags to add
|
# other linking flags to add
|
||||||
# noinst_LTLIBRARIES = libSoundTouchOpt.la
|
# noinst_LTLIBRARIES = libSoundTouchOpt.la
|
||||||
|
|||||||
@ -11,13 +11,6 @@
|
|||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Last changed : $Date$
|
|
||||||
// File revision : $Revision: 4 $
|
|
||||||
//
|
|
||||||
// $Id$
|
|
||||||
//
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// License :
|
// License :
|
||||||
//
|
//
|
||||||
// SoundTouch audio processing library
|
// SoundTouch audio processing library
|
||||||
@ -64,7 +57,7 @@ int PeakFinder::findTop(const float *data, int peakpos) const
|
|||||||
|
|
||||||
refvalue = data[peakpos];
|
refvalue = data[peakpos];
|
||||||
|
|
||||||
// seek within ±10 points
|
// seek within ±10 points
|
||||||
start = peakpos - 10;
|
start = peakpos - 10;
|
||||||
if (start < minPos) start = minPos;
|
if (start < minPos) start = minPos;
|
||||||
end = peakpos + 10;
|
end = peakpos + 10;
|
||||||
@ -149,7 +142,7 @@ int PeakFinder::findCrossingLevel(const float *data, float level, int peakpos, i
|
|||||||
peaklevel = data[peakpos];
|
peaklevel = data[peakpos];
|
||||||
assert(peaklevel >= level);
|
assert(peaklevel >= level);
|
||||||
pos = peakpos;
|
pos = peakpos;
|
||||||
while ((pos >= minPos) && (pos < maxPos))
|
while ((pos >= minPos) && (pos + direction < maxPos))
|
||||||
{
|
{
|
||||||
if (data[pos + direction] < level) return pos; // crossing found
|
if (data[pos + direction] < level) return pos; // crossing found
|
||||||
pos += direction;
|
pos += direction;
|
||||||
@ -178,7 +171,6 @@ double PeakFinder::calcMassCenter(const float *data, int firstPos, int lastPos)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// get exact center of peak near given position by calculating local mass of center
|
/// get exact center of peak near given position by calculating local mass of center
|
||||||
double PeakFinder::getPeakCenter(const float *data, int peakpos) const
|
double PeakFinder::getPeakCenter(const float *data, int peakpos) const
|
||||||
{
|
{
|
||||||
@ -192,11 +184,21 @@ double PeakFinder::getPeakCenter(const float *data, int peakpos) const
|
|||||||
gp1 = findGround(data, peakpos, -1);
|
gp1 = findGround(data, peakpos, -1);
|
||||||
gp2 = findGround(data, peakpos, 1);
|
gp2 = findGround(data, peakpos, 1);
|
||||||
|
|
||||||
groundLevel = 0.5f * (data[gp1] + data[gp2]);
|
|
||||||
peakLevel = data[peakpos];
|
peakLevel = data[peakpos];
|
||||||
|
|
||||||
|
if (gp1 == gp2)
|
||||||
|
{
|
||||||
|
// avoid rounding errors when all are equal
|
||||||
|
assert(gp1 == peakpos);
|
||||||
|
cutLevel = groundLevel = peakLevel;
|
||||||
|
} else {
|
||||||
|
// get average of the ground levels
|
||||||
|
groundLevel = 0.5f * (data[gp1] + data[gp2]);
|
||||||
|
|
||||||
// calculate 70%-level of the peak
|
// calculate 70%-level of the peak
|
||||||
cutLevel = 0.70f * peakLevel + 0.30f * groundLevel;
|
cutLevel = 0.70f * peakLevel + 0.30f * groundLevel;
|
||||||
|
}
|
||||||
|
|
||||||
// find mid-level crossings
|
// find mid-level crossings
|
||||||
crosspos1 = findCrossingLevel(data, cutLevel, peakpos, -1);
|
crosspos1 = findCrossingLevel(data, cutLevel, peakpos, -1);
|
||||||
crosspos2 = findCrossingLevel(data, cutLevel, peakpos, 1);
|
crosspos2 = findCrossingLevel(data, cutLevel, peakpos, 1);
|
||||||
@ -208,7 +210,6 @@ double PeakFinder::getPeakCenter(const float *data, int peakpos) const
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
double PeakFinder::detectPeak(const float *data, int aminPos, int amaxPos)
|
double PeakFinder::detectPeak(const float *data, int aminPos, int amaxPos)
|
||||||
{
|
{
|
||||||
|
|
||||||
@ -239,14 +240,12 @@ double PeakFinder::detectPeak(const float *data, int aminPos, int amaxPos)
|
|||||||
// - sometimes the highest peak can be Nth harmonic of the true base peak yet
|
// - sometimes the highest peak can be Nth harmonic of the true base peak yet
|
||||||
// just a slightly higher than the true base
|
// just a slightly higher than the true base
|
||||||
|
|
||||||
int hp = (int)(highPeak + 0.5);
|
for (i = 1; i < 3; i ++)
|
||||||
|
|
||||||
for (i = 3; i < 10; i ++)
|
|
||||||
{
|
{
|
||||||
double peaktmp, harmonic;
|
double peaktmp, harmonic;
|
||||||
int i1,i2;
|
int i1,i2;
|
||||||
|
|
||||||
harmonic = (double)i * 0.5;
|
harmonic = (double)pow(2.0, i);
|
||||||
peakpos = (int)(highPeak / harmonic + 0.5f);
|
peakpos = (int)(highPeak / harmonic + 0.5f);
|
||||||
if (peakpos < minPos) break;
|
if (peakpos < minPos) break;
|
||||||
peakpos = findTop(data, peakpos); // seek true local maximum index
|
peakpos = findTop(data, peakpos); // seek true local maximum index
|
||||||
@ -257,7 +256,7 @@ double PeakFinder::detectPeak(const float *data, int aminPos, int amaxPos)
|
|||||||
|
|
||||||
// accept harmonic peak if
|
// accept harmonic peak if
|
||||||
// (a) it is found
|
// (a) it is found
|
||||||
// (b) is within ±4% of the expected harmonic interval
|
// (b) is within ±4% of the expected harmonic interval
|
||||||
// (c) has at least half x-corr value of the max. peak
|
// (c) has at least half x-corr value of the max. peak
|
||||||
|
|
||||||
double diff = harmonic * peaktmp / highPeak;
|
double diff = harmonic * peaktmp / highPeak;
|
||||||
|
|||||||
@ -9,13 +9,6 @@
|
|||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Last changed : $Date$
|
|
||||||
// File revision : $Revision: 4 $
|
|
||||||
//
|
|
||||||
// $Id$
|
|
||||||
//
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// License :
|
// License :
|
||||||
//
|
//
|
||||||
// SoundTouch audio processing library
|
// SoundTouch audio processing library
|
||||||
@ -51,8 +44,8 @@ protected:
|
|||||||
|
|
||||||
/// Calculates the mass center between given vector items.
|
/// Calculates the mass center between given vector items.
|
||||||
double calcMassCenter(const float *data, ///< Data vector.
|
double calcMassCenter(const float *data, ///< Data vector.
|
||||||
int firstPos, ///< Index of first vector item beloging to the peak.
|
int firstPos, ///< Index of first vector item belonging to the peak.
|
||||||
int lastPos ///< Index of last vector item beloging to the peak.
|
int lastPos ///< Index of last vector item belonging to the peak.
|
||||||
) const;
|
) const;
|
||||||
|
|
||||||
/// Finds the data vector index where the monotoniously decreasing signal crosses the
|
/// Finds the data vector index where the monotoniously decreasing signal crosses the
|
||||||
|
|||||||
@ -10,13 +10,6 @@
|
|||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Last changed : $Date$
|
|
||||||
// File revision : $Revision: 4 $
|
|
||||||
//
|
|
||||||
// $Id$
|
|
||||||
//
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// License :
|
// License :
|
||||||
//
|
//
|
||||||
// SoundTouch audio processing library
|
// SoundTouch audio processing library
|
||||||
@ -43,114 +36,55 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include "RateTransposer.h"
|
#include "RateTransposer.h"
|
||||||
|
#include "InterpolateLinear.h"
|
||||||
|
#include "InterpolateCubic.h"
|
||||||
|
#include "InterpolateShannon.h"
|
||||||
#include "AAFilter.h"
|
#include "AAFilter.h"
|
||||||
|
|
||||||
using namespace soundtouch;
|
using namespace soundtouch;
|
||||||
|
|
||||||
|
// Define default interpolation algorithm here
|
||||||
/// A linear samplerate transposer class that uses integer arithmetics.
|
TransposerBase::ALGORITHM TransposerBase::algorithm = TransposerBase::CUBIC;
|
||||||
/// for the transposing.
|
|
||||||
class RateTransposerInteger : public RateTransposer
|
|
||||||
{
|
|
||||||
protected:
|
|
||||||
int iSlopeCount;
|
|
||||||
int iRate;
|
|
||||||
SAMPLETYPE sPrevSampleL, sPrevSampleR;
|
|
||||||
|
|
||||||
virtual void resetRegisters();
|
|
||||||
|
|
||||||
virtual uint transposeStereo(SAMPLETYPE *dest,
|
|
||||||
const SAMPLETYPE *src,
|
|
||||||
uint numSamples);
|
|
||||||
virtual uint transposeMono(SAMPLETYPE *dest,
|
|
||||||
const SAMPLETYPE *src,
|
|
||||||
uint numSamples);
|
|
||||||
|
|
||||||
public:
|
|
||||||
RateTransposerInteger();
|
|
||||||
virtual ~RateTransposerInteger();
|
|
||||||
|
|
||||||
/// Sets new target rate. Normal rate = 1.0, smaller values represent slower
|
|
||||||
/// rate, larger faster rates.
|
|
||||||
virtual void setRate(float newRate);
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/// A linear samplerate transposer class that uses floating point arithmetics
|
|
||||||
/// for the transposing.
|
|
||||||
class RateTransposerFloat : public RateTransposer
|
|
||||||
{
|
|
||||||
protected:
|
|
||||||
float fSlopeCount;
|
|
||||||
SAMPLETYPE sPrevSampleL, sPrevSampleR;
|
|
||||||
|
|
||||||
virtual void resetRegisters();
|
|
||||||
|
|
||||||
virtual uint transposeStereo(SAMPLETYPE *dest,
|
|
||||||
const SAMPLETYPE *src,
|
|
||||||
uint numSamples);
|
|
||||||
virtual uint transposeMono(SAMPLETYPE *dest,
|
|
||||||
const SAMPLETYPE *src,
|
|
||||||
uint numSamples);
|
|
||||||
|
|
||||||
public:
|
|
||||||
RateTransposerFloat();
|
|
||||||
virtual ~RateTransposerFloat();
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Operator 'new' is overloaded so that it automatically creates a suitable instance
|
|
||||||
// depending on if we've a MMX/SSE/etc-capable CPU available or not.
|
|
||||||
void * RateTransposer::operator new(size_t s)
|
|
||||||
{
|
|
||||||
ST_THROW_RT_ERROR("Error in RateTransoser::new: don't use \"new TDStretch\" directly, use \"newInstance\" to create a new instance instead!");
|
|
||||||
return newInstance();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
RateTransposer *RateTransposer::newInstance()
|
|
||||||
{
|
|
||||||
#ifdef SOUNDTOUCH_INTEGER_SAMPLES
|
|
||||||
return ::new RateTransposerInteger;
|
|
||||||
#else
|
|
||||||
return ::new RateTransposerFloat;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Constructor
|
// Constructor
|
||||||
RateTransposer::RateTransposer() : FIFOProcessor(&outputBuffer)
|
RateTransposer::RateTransposer() : FIFOProcessor(&outputBuffer)
|
||||||
{
|
{
|
||||||
numChannels = 2;
|
bUseAAFilter =
|
||||||
bUseAAFilter = TRUE;
|
#ifndef SOUNDTOUCH_PREVENT_CLICK_AT_RATE_CROSSOVER
|
||||||
fRate = 0;
|
true;
|
||||||
|
#else
|
||||||
|
// Disable Anti-alias filter if desirable to avoid click at rate change zero value crossover
|
||||||
|
false;
|
||||||
|
#endif
|
||||||
|
|
||||||
// Instantiates the anti-alias filter with default tap length
|
// Instantiates the anti-alias filter
|
||||||
// of 32
|
pAAFilter = new AAFilter(64);
|
||||||
pAAFilter = new AAFilter(32);
|
pTransposer = TransposerBase::newInstance();
|
||||||
|
clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
RateTransposer::~RateTransposer()
|
RateTransposer::~RateTransposer()
|
||||||
{
|
{
|
||||||
delete pAAFilter;
|
delete pAAFilter;
|
||||||
|
delete pTransposer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// Enables/disables the anti-alias filter. Zero to disable, nonzero to enable
|
/// Enables/disables the anti-alias filter. Zero to disable, nonzero to enable
|
||||||
void RateTransposer::enableAAFilter(BOOL newMode)
|
void RateTransposer::enableAAFilter(bool newMode)
|
||||||
{
|
{
|
||||||
|
#ifndef SOUNDTOUCH_PREVENT_CLICK_AT_RATE_CROSSOVER
|
||||||
|
// Disable Anti-alias filter if desirable to avoid click at rate change zero value crossover
|
||||||
bUseAAFilter = newMode;
|
bUseAAFilter = newMode;
|
||||||
|
clear();
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Returns nonzero if anti-alias filter is enabled.
|
/// Returns nonzero if anti-alias filter is enabled.
|
||||||
BOOL RateTransposer::isAAFilterEnabled() const
|
bool RateTransposer::isAAFilterEnabled() const
|
||||||
{
|
{
|
||||||
return bUseAAFilter;
|
return bUseAAFilter;
|
||||||
}
|
}
|
||||||
@ -162,44 +96,27 @@ AAFilter *RateTransposer::getAAFilter()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Sets new target iRate. Normal iRate = 1.0, smaller values represent slower
|
// Sets new target iRate. Normal iRate = 1.0, smaller values represent slower
|
||||||
// iRate, larger faster iRates.
|
// iRate, larger faster iRates.
|
||||||
void RateTransposer::setRate(float newRate)
|
void RateTransposer::setRate(double newRate)
|
||||||
{
|
{
|
||||||
double fCutoff;
|
double fCutoff;
|
||||||
|
|
||||||
fRate = newRate;
|
pTransposer->setRate(newRate);
|
||||||
|
|
||||||
// design a new anti-alias filter
|
// design a new anti-alias filter
|
||||||
if (newRate > 1.0f)
|
if (newRate > 1.0)
|
||||||
{
|
{
|
||||||
fCutoff = 0.5f / newRate;
|
fCutoff = 0.5 / newRate;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
fCutoff = 0.5f * newRate;
|
fCutoff = 0.5 * newRate;
|
||||||
}
|
}
|
||||||
pAAFilter->setCutoffFreq(fCutoff);
|
pAAFilter->setCutoffFreq(fCutoff);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Outputs as many samples of the 'outputBuffer' as possible, and if there's
|
|
||||||
// any room left, outputs also as many of the incoming samples as possible.
|
|
||||||
// The goal is to drive the outputBuffer empty.
|
|
||||||
//
|
|
||||||
// It's allowed for 'output' and 'input' parameters to point to the same
|
|
||||||
// memory position.
|
|
||||||
/*
|
|
||||||
void RateTransposer::flushStoreBuffer()
|
|
||||||
{
|
|
||||||
if (storeBuffer.isEmpty()) return;
|
|
||||||
|
|
||||||
outputBuffer.moveSamples(storeBuffer);
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
// Adds 'nSamples' pcs of samples from the 'samples' memory position into
|
// Adds 'nSamples' pcs of samples from the 'samples' memory position into
|
||||||
// the input of the object.
|
// the input of the object.
|
||||||
void RateTransposer::putSamples(const SAMPLETYPE *samples, uint nSamples)
|
void RateTransposer::putSamples(const SAMPLETYPE *samples, uint nSamples)
|
||||||
@ -208,115 +125,50 @@ void RateTransposer::putSamples(const SAMPLETYPE *samples, uint nSamples)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Transposes up the sample rate, causing the observed playback 'rate' of the
|
|
||||||
// sound to decrease
|
|
||||||
void RateTransposer::upsample(const SAMPLETYPE *src, uint nSamples)
|
|
||||||
{
|
|
||||||
uint count, sizeTemp, num;
|
|
||||||
|
|
||||||
// If the parameter 'uRate' value is smaller than 'SCALE', first transpose
|
|
||||||
// the samples and then apply the anti-alias filter to remove aliasing.
|
|
||||||
|
|
||||||
// First check that there's enough room in 'storeBuffer'
|
|
||||||
// (+16 is to reserve some slack in the destination buffer)
|
|
||||||
sizeTemp = (uint)((float)nSamples / fRate + 16.0f);
|
|
||||||
|
|
||||||
// Transpose the samples, store the result into the end of "storeBuffer"
|
|
||||||
count = transpose(storeBuffer.ptrEnd(sizeTemp), src, nSamples);
|
|
||||||
storeBuffer.putSamples(count);
|
|
||||||
|
|
||||||
// Apply the anti-alias filter to samples in "store output", output the
|
|
||||||
// result to "dest"
|
|
||||||
num = storeBuffer.numSamples();
|
|
||||||
count = pAAFilter->evaluate(outputBuffer.ptrEnd(num),
|
|
||||||
storeBuffer.ptrBegin(), num, (uint)numChannels);
|
|
||||||
outputBuffer.putSamples(count);
|
|
||||||
|
|
||||||
// Remove the processed samples from "storeBuffer"
|
|
||||||
storeBuffer.receiveSamples(count);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Transposes down the sample rate, causing the observed playback 'rate' of the
|
|
||||||
// sound to increase
|
|
||||||
void RateTransposer::downsample(const SAMPLETYPE *src, uint nSamples)
|
|
||||||
{
|
|
||||||
uint count, sizeTemp;
|
|
||||||
|
|
||||||
// If the parameter 'uRate' value is larger than 'SCALE', first apply the
|
|
||||||
// anti-alias filter to remove high frequencies (prevent them from folding
|
|
||||||
// over the lover frequencies), then transpose.
|
|
||||||
|
|
||||||
// Add the new samples to the end of the storeBuffer
|
|
||||||
storeBuffer.putSamples(src, nSamples);
|
|
||||||
|
|
||||||
// Anti-alias filter the samples to prevent folding and output the filtered
|
|
||||||
// data to tempBuffer. Note : because of the FIR filter length, the
|
|
||||||
// filtering routine takes in 'filter_length' more samples than it outputs.
|
|
||||||
assert(tempBuffer.isEmpty());
|
|
||||||
sizeTemp = storeBuffer.numSamples();
|
|
||||||
|
|
||||||
count = pAAFilter->evaluate(tempBuffer.ptrEnd(sizeTemp),
|
|
||||||
storeBuffer.ptrBegin(), sizeTemp, (uint)numChannels);
|
|
||||||
|
|
||||||
if (count == 0) return;
|
|
||||||
|
|
||||||
// Remove the filtered samples from 'storeBuffer'
|
|
||||||
storeBuffer.receiveSamples(count);
|
|
||||||
|
|
||||||
// Transpose the samples (+16 is to reserve some slack in the destination buffer)
|
|
||||||
sizeTemp = (uint)((float)nSamples / fRate + 16.0f);
|
|
||||||
count = transpose(outputBuffer.ptrEnd(sizeTemp), tempBuffer.ptrBegin(), count);
|
|
||||||
outputBuffer.putSamples(count);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Transposes sample rate by applying anti-alias filter to prevent folding.
|
// Transposes sample rate by applying anti-alias filter to prevent folding.
|
||||||
// Returns amount of samples returned in the "dest" buffer.
|
// Returns amount of samples returned in the "dest" buffer.
|
||||||
// The maximum amount of samples that can be returned at a time is set by
|
// The maximum amount of samples that can be returned at a time is set by
|
||||||
// the 'set_returnBuffer_size' function.
|
// the 'set_returnBuffer_size' function.
|
||||||
void RateTransposer::processSamples(const SAMPLETYPE *src, uint nSamples)
|
void RateTransposer::processSamples(const SAMPLETYPE *src, uint nSamples)
|
||||||
{
|
{
|
||||||
uint count;
|
|
||||||
uint sizeReq;
|
|
||||||
|
|
||||||
if (nSamples == 0) return;
|
if (nSamples == 0) return;
|
||||||
assert(pAAFilter);
|
|
||||||
|
// Store samples to input buffer
|
||||||
|
inputBuffer.putSamples(src, nSamples);
|
||||||
|
|
||||||
// If anti-alias filter is turned off, simply transpose without applying
|
// If anti-alias filter is turned off, simply transpose without applying
|
||||||
// the filter
|
// the filter
|
||||||
if (bUseAAFilter == FALSE)
|
if (bUseAAFilter == false)
|
||||||
{
|
{
|
||||||
sizeReq = (uint)((float)nSamples / fRate + 1.0f);
|
(void)pTransposer->transpose(outputBuffer, inputBuffer);
|
||||||
count = transpose(outputBuffer.ptrEnd(sizeReq), src, nSamples);
|
|
||||||
outputBuffer.putSamples(count);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
assert(pAAFilter);
|
||||||
|
|
||||||
// Transpose with anti-alias filter
|
// Transpose with anti-alias filter
|
||||||
if (fRate < 1.0f)
|
if (pTransposer->rate < 1.0f)
|
||||||
{
|
{
|
||||||
upsample(src, nSamples);
|
// If the parameter 'Rate' value is smaller than 1, first transpose
|
||||||
|
// the samples and then apply the anti-alias filter to remove aliasing.
|
||||||
|
|
||||||
|
// Transpose the samples, store the result to end of "midBuffer"
|
||||||
|
pTransposer->transpose(midBuffer, inputBuffer);
|
||||||
|
|
||||||
|
// Apply the anti-alias filter for transposed samples in midBuffer
|
||||||
|
pAAFilter->evaluate(outputBuffer, midBuffer);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
downsample(src, nSamples);
|
// If the parameter 'Rate' value is larger than 1, first apply the
|
||||||
}
|
// anti-alias filter to remove high frequencies (prevent them from folding
|
||||||
}
|
// over the lover frequencies), then transpose.
|
||||||
|
|
||||||
|
// Apply the anti-alias filter for samples in inputBuffer
|
||||||
|
pAAFilter->evaluate(midBuffer, inputBuffer);
|
||||||
|
|
||||||
// Transposes the sample rate of the given samples using linear interpolation.
|
// Transpose the AA-filtered samples in "midBuffer"
|
||||||
// Returns the number of samples returned in the "dest" buffer
|
pTransposer->transpose(outputBuffer, midBuffer);
|
||||||
inline uint RateTransposer::transpose(SAMPLETYPE *dest, const SAMPLETYPE *src, uint nSamples)
|
|
||||||
{
|
|
||||||
if (numChannels == 2)
|
|
||||||
{
|
|
||||||
return transposeStereo(dest, src, nSamples);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return transposeMono(dest, src, nSamples);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -324,18 +176,13 @@ inline uint RateTransposer::transpose(SAMPLETYPE *dest, const SAMPLETYPE *src, u
|
|||||||
// Sets the number of channels, 1 = mono, 2 = stereo
|
// Sets the number of channels, 1 = mono, 2 = stereo
|
||||||
void RateTransposer::setChannels(int nChannels)
|
void RateTransposer::setChannels(int nChannels)
|
||||||
{
|
{
|
||||||
assert(nChannels > 0);
|
if (!verifyNumberOfChannels(nChannels) ||
|
||||||
if (numChannels == nChannels) return;
|
(pTransposer->numChannels == nChannels)) return;
|
||||||
|
|
||||||
assert(nChannels == 1 || nChannels == 2);
|
pTransposer->setChannels(nChannels);
|
||||||
numChannels = nChannels;
|
inputBuffer.setChannels(nChannels);
|
||||||
|
midBuffer.setChannels(nChannels);
|
||||||
storeBuffer.setChannels(numChannels);
|
outputBuffer.setChannels(nChannels);
|
||||||
tempBuffer.setChannels(numChannels);
|
|
||||||
outputBuffer.setChannels(numChannels);
|
|
||||||
|
|
||||||
// Inits the linear interpolation registers
|
|
||||||
resetRegisters();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -343,7 +190,13 @@ void RateTransposer::setChannels(int nChannels)
|
|||||||
void RateTransposer::clear()
|
void RateTransposer::clear()
|
||||||
{
|
{
|
||||||
outputBuffer.clear();
|
outputBuffer.clear();
|
||||||
storeBuffer.clear();
|
midBuffer.clear();
|
||||||
|
inputBuffer.clear();
|
||||||
|
pTransposer->resetRegisters();
|
||||||
|
|
||||||
|
// prefill buffer to avoid losing first samples at beginning of stream
|
||||||
|
int prefill = getLatency();
|
||||||
|
inputBuffer.addSilent(prefill);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -354,273 +207,107 @@ int RateTransposer::isEmpty() const
|
|||||||
|
|
||||||
res = FIFOProcessor::isEmpty();
|
res = FIFOProcessor::isEmpty();
|
||||||
if (res == 0) return 0;
|
if (res == 0) return 0;
|
||||||
return storeBuffer.isEmpty();
|
return inputBuffer.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Return approximate initial input-output latency
|
||||||
|
int RateTransposer::getLatency() const
|
||||||
|
{
|
||||||
|
return pTransposer->getLatency() +
|
||||||
|
((bUseAAFilter) ? (pAAFilter->getLength() / 2) : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// RateTransposerInteger - integer arithmetic implementation
|
// TransposerBase - Base class for interpolation
|
||||||
//
|
//
|
||||||
|
|
||||||
/// fixed-point interpolation routine precision
|
// static function to set interpolation algorithm
|
||||||
#define SCALE 65536
|
void TransposerBase::setAlgorithm(TransposerBase::ALGORITHM a)
|
||||||
|
|
||||||
// Constructor
|
|
||||||
RateTransposerInteger::RateTransposerInteger() : RateTransposer()
|
|
||||||
{
|
{
|
||||||
// Notice: use local function calling syntax for sake of clarity,
|
TransposerBase::algorithm = a;
|
||||||
// to indicate the fact that C++ constructor can't call virtual functions.
|
|
||||||
RateTransposerInteger::resetRegisters();
|
|
||||||
RateTransposerInteger::setRate(1.0f);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
RateTransposerInteger::~RateTransposerInteger()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void RateTransposerInteger::resetRegisters()
|
|
||||||
{
|
|
||||||
iSlopeCount = 0;
|
|
||||||
sPrevSampleL =
|
|
||||||
sPrevSampleR = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Transposes the sample rate of the given samples using linear interpolation.
|
|
||||||
// 'Mono' version of the routine. Returns the number of samples returned in
|
|
||||||
// the "dest" buffer
|
|
||||||
uint RateTransposerInteger::transposeMono(SAMPLETYPE *dest, const SAMPLETYPE *src, uint nSamples)
|
|
||||||
{
|
|
||||||
unsigned int i, used;
|
|
||||||
LONG_SAMPLETYPE temp, vol1;
|
|
||||||
|
|
||||||
if (nSamples == 0) return 0; // no samples, no work
|
|
||||||
|
|
||||||
used = 0;
|
|
||||||
i = 0;
|
|
||||||
|
|
||||||
// Process the last sample saved from the previous call first...
|
|
||||||
while (iSlopeCount <= SCALE)
|
|
||||||
{
|
|
||||||
vol1 = (LONG_SAMPLETYPE)(SCALE - iSlopeCount);
|
|
||||||
temp = vol1 * sPrevSampleL + iSlopeCount * src[0];
|
|
||||||
dest[i] = (SAMPLETYPE)(temp / SCALE);
|
|
||||||
i++;
|
|
||||||
iSlopeCount += iRate;
|
|
||||||
}
|
|
||||||
// now always (iSlopeCount > SCALE)
|
|
||||||
iSlopeCount -= SCALE;
|
|
||||||
|
|
||||||
while (1)
|
|
||||||
{
|
|
||||||
while (iSlopeCount > SCALE)
|
|
||||||
{
|
|
||||||
iSlopeCount -= SCALE;
|
|
||||||
used ++;
|
|
||||||
if (used >= nSamples - 1) goto end;
|
|
||||||
}
|
|
||||||
vol1 = (LONG_SAMPLETYPE)(SCALE - iSlopeCount);
|
|
||||||
temp = src[used] * vol1 + iSlopeCount * src[used + 1];
|
|
||||||
dest[i] = (SAMPLETYPE)(temp / SCALE);
|
|
||||||
|
|
||||||
i++;
|
|
||||||
iSlopeCount += iRate;
|
|
||||||
}
|
|
||||||
end:
|
|
||||||
// Store the last sample for the next round
|
|
||||||
sPrevSampleL = src[nSamples - 1];
|
|
||||||
|
|
||||||
return i;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Transposes the sample rate of the given samples using linear interpolation.
|
// Transposes the sample rate of the given samples using linear interpolation.
|
||||||
// 'Stereo' version of the routine. Returns the number of samples returned in
|
// Returns the number of samples returned in the "dest" buffer
|
||||||
// the "dest" buffer
|
int TransposerBase::transpose(FIFOSampleBuffer &dest, FIFOSampleBuffer &src)
|
||||||
uint RateTransposerInteger::transposeStereo(SAMPLETYPE *dest, const SAMPLETYPE *src, uint nSamples)
|
|
||||||
{
|
{
|
||||||
unsigned int srcPos, i, used;
|
int numSrcSamples = src.numSamples();
|
||||||
LONG_SAMPLETYPE temp, vol1;
|
int sizeDemand = (int)((double)numSrcSamples / rate) + 8;
|
||||||
|
int numOutput;
|
||||||
|
SAMPLETYPE *psrc = src.ptrBegin();
|
||||||
|
SAMPLETYPE *pdest = dest.ptrEnd(sizeDemand);
|
||||||
|
|
||||||
if (nSamples == 0) return 0; // no samples, no work
|
#ifndef USE_MULTICH_ALWAYS
|
||||||
|
if (numChannels == 1)
|
||||||
used = 0;
|
|
||||||
i = 0;
|
|
||||||
|
|
||||||
// Process the last sample saved from the sPrevSampleLious call first...
|
|
||||||
while (iSlopeCount <= SCALE)
|
|
||||||
{
|
{
|
||||||
vol1 = (LONG_SAMPLETYPE)(SCALE - iSlopeCount);
|
numOutput = transposeMono(pdest, psrc, numSrcSamples);
|
||||||
temp = vol1 * sPrevSampleL + iSlopeCount * src[0];
|
|
||||||
dest[2 * i] = (SAMPLETYPE)(temp / SCALE);
|
|
||||||
temp = vol1 * sPrevSampleR + iSlopeCount * src[1];
|
|
||||||
dest[2 * i + 1] = (SAMPLETYPE)(temp / SCALE);
|
|
||||||
i++;
|
|
||||||
iSlopeCount += iRate;
|
|
||||||
}
|
}
|
||||||
// now always (iSlopeCount > SCALE)
|
else if (numChannels == 2)
|
||||||
iSlopeCount -= SCALE;
|
|
||||||
|
|
||||||
while (1)
|
|
||||||
{
|
{
|
||||||
while (iSlopeCount > SCALE)
|
numOutput = transposeStereo(pdest, psrc, numSrcSamples);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
#endif // USE_MULTICH_ALWAYS
|
||||||
{
|
{
|
||||||
iSlopeCount -= SCALE;
|
assert(numChannels > 0);
|
||||||
used ++;
|
numOutput = transposeMulti(pdest, psrc, numSrcSamples);
|
||||||
if (used >= nSamples - 1) goto end;
|
|
||||||
}
|
}
|
||||||
srcPos = 2 * used;
|
dest.putSamples(numOutput);
|
||||||
vol1 = (LONG_SAMPLETYPE)(SCALE - iSlopeCount);
|
src.receiveSamples(numSrcSamples);
|
||||||
temp = src[srcPos] * vol1 + iSlopeCount * src[srcPos + 2];
|
return numOutput;
|
||||||
dest[2 * i] = (SAMPLETYPE)(temp / SCALE);
|
|
||||||
temp = src[srcPos + 1] * vol1 + iSlopeCount * src[srcPos + 3];
|
|
||||||
dest[2 * i + 1] = (SAMPLETYPE)(temp / SCALE);
|
|
||||||
|
|
||||||
i++;
|
|
||||||
iSlopeCount += iRate;
|
|
||||||
}
|
|
||||||
end:
|
|
||||||
// Store the last sample for the next round
|
|
||||||
sPrevSampleL = src[2 * nSamples - 2];
|
|
||||||
sPrevSampleR = src[2 * nSamples - 1];
|
|
||||||
|
|
||||||
return i;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Sets new target iRate. Normal iRate = 1.0, smaller values represent slower
|
TransposerBase::TransposerBase()
|
||||||
// iRate, larger faster iRates.
|
|
||||||
void RateTransposerInteger::setRate(float newRate)
|
|
||||||
{
|
{
|
||||||
iRate = (int)(newRate * SCALE + 0.5f);
|
numChannels = 0;
|
||||||
RateTransposer::setRate(newRate);
|
rate = 1.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
TransposerBase::~TransposerBase()
|
||||||
//
|
|
||||||
// RateTransposerFloat - floating point arithmetic implementation
|
|
||||||
//
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
// Constructor
|
|
||||||
RateTransposerFloat::RateTransposerFloat() : RateTransposer()
|
|
||||||
{
|
|
||||||
// Notice: use local function calling syntax for sake of clarity,
|
|
||||||
// to indicate the fact that C++ constructor can't call virtual functions.
|
|
||||||
RateTransposerFloat::resetRegisters();
|
|
||||||
RateTransposerFloat::setRate(1.0f);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
RateTransposerFloat::~RateTransposerFloat()
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void RateTransposerFloat::resetRegisters()
|
void TransposerBase::setChannels(int channels)
|
||||||
{
|
{
|
||||||
fSlopeCount = 0;
|
numChannels = channels;
|
||||||
sPrevSampleL =
|
resetRegisters();
|
||||||
sPrevSampleR = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void TransposerBase::setRate(double newRate)
|
||||||
// Transposes the sample rate of the given samples using linear interpolation.
|
|
||||||
// 'Mono' version of the routine. Returns the number of samples returned in
|
|
||||||
// the "dest" buffer
|
|
||||||
uint RateTransposerFloat::transposeMono(SAMPLETYPE *dest, const SAMPLETYPE *src, uint nSamples)
|
|
||||||
{
|
{
|
||||||
unsigned int i, used;
|
rate = newRate;
|
||||||
|
|
||||||
used = 0;
|
|
||||||
i = 0;
|
|
||||||
|
|
||||||
// Process the last sample saved from the previous call first...
|
|
||||||
while (fSlopeCount <= 1.0f)
|
|
||||||
{
|
|
||||||
dest[i] = (SAMPLETYPE)((1.0f - fSlopeCount) * sPrevSampleL + fSlopeCount * src[0]);
|
|
||||||
i++;
|
|
||||||
fSlopeCount += fRate;
|
|
||||||
}
|
|
||||||
fSlopeCount -= 1.0f;
|
|
||||||
|
|
||||||
if (nSamples > 1)
|
|
||||||
{
|
|
||||||
while (1)
|
|
||||||
{
|
|
||||||
while (fSlopeCount > 1.0f)
|
|
||||||
{
|
|
||||||
fSlopeCount -= 1.0f;
|
|
||||||
used ++;
|
|
||||||
if (used >= nSamples - 1) goto end;
|
|
||||||
}
|
|
||||||
dest[i] = (SAMPLETYPE)((1.0f - fSlopeCount) * src[used] + fSlopeCount * src[used + 1]);
|
|
||||||
i++;
|
|
||||||
fSlopeCount += fRate;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
end:
|
|
||||||
// Store the last sample for the next round
|
|
||||||
sPrevSampleL = src[nSamples - 1];
|
|
||||||
|
|
||||||
return i;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Transposes the sample rate of the given samples using linear interpolation.
|
// static factory function
|
||||||
// 'Mono' version of the routine. Returns the number of samples returned in
|
TransposerBase *TransposerBase::newInstance()
|
||||||
// the "dest" buffer
|
|
||||||
uint RateTransposerFloat::transposeStereo(SAMPLETYPE *dest, const SAMPLETYPE *src, uint nSamples)
|
|
||||||
{
|
{
|
||||||
unsigned int srcPos, i, used;
|
#ifdef SOUNDTOUCH_INTEGER_SAMPLES
|
||||||
|
// Notice: For integer arithmetic support only linear algorithm (due to simplest calculus)
|
||||||
if (nSamples == 0) return 0; // no samples, no work
|
return ::new InterpolateLinearInteger;
|
||||||
|
#else
|
||||||
used = 0;
|
switch (algorithm)
|
||||||
i = 0;
|
|
||||||
|
|
||||||
// Process the last sample saved from the sPrevSampleLious call first...
|
|
||||||
while (fSlopeCount <= 1.0f)
|
|
||||||
{
|
{
|
||||||
dest[2 * i] = (SAMPLETYPE)((1.0f - fSlopeCount) * sPrevSampleL + fSlopeCount * src[0]);
|
case LINEAR:
|
||||||
dest[2 * i + 1] = (SAMPLETYPE)((1.0f - fSlopeCount) * sPrevSampleR + fSlopeCount * src[1]);
|
return new InterpolateLinearFloat;
|
||||||
i++;
|
|
||||||
fSlopeCount += fRate;
|
|
||||||
}
|
|
||||||
// now always (iSlopeCount > 1.0f)
|
|
||||||
fSlopeCount -= 1.0f;
|
|
||||||
|
|
||||||
if (nSamples > 1)
|
case CUBIC:
|
||||||
{
|
return new InterpolateCubic;
|
||||||
while (1)
|
|
||||||
{
|
|
||||||
while (fSlopeCount > 1.0f)
|
|
||||||
{
|
|
||||||
fSlopeCount -= 1.0f;
|
|
||||||
used ++;
|
|
||||||
if (used >= nSamples - 1) goto end;
|
|
||||||
}
|
|
||||||
srcPos = 2 * used;
|
|
||||||
|
|
||||||
dest[2 * i] = (SAMPLETYPE)((1.0f - fSlopeCount) * src[srcPos]
|
case SHANNON:
|
||||||
+ fSlopeCount * src[srcPos + 2]);
|
return new InterpolateShannon;
|
||||||
dest[2 * i + 1] = (SAMPLETYPE)((1.0f - fSlopeCount) * src[srcPos + 1]
|
|
||||||
+ fSlopeCount * src[srcPos + 3]);
|
|
||||||
|
|
||||||
i++;
|
default:
|
||||||
fSlopeCount += fRate;
|
assert(false);
|
||||||
|
return nullptr;
|
||||||
}
|
}
|
||||||
}
|
#endif
|
||||||
end:
|
|
||||||
// Store the last sample for the next round
|
|
||||||
sPrevSampleL = src[2 * nSamples - 2];
|
|
||||||
sPrevSampleR = src[2 * nSamples - 1];
|
|
||||||
|
|
||||||
return i;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -14,13 +14,6 @@
|
|||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Last changed : $Date$
|
|
||||||
// File revision : $Revision: 4 $
|
|
||||||
//
|
|
||||||
// $Id$
|
|
||||||
//
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// License :
|
// License :
|
||||||
//
|
//
|
||||||
// SoundTouch audio processing library
|
// SoundTouch audio processing library
|
||||||
@ -55,50 +48,72 @@
|
|||||||
namespace soundtouch
|
namespace soundtouch
|
||||||
{
|
{
|
||||||
|
|
||||||
|
/// Abstract base class for transposer implementations (linear, advanced vs integer, float etc)
|
||||||
|
class TransposerBase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum ALGORITHM {
|
||||||
|
LINEAR = 0,
|
||||||
|
CUBIC,
|
||||||
|
SHANNON
|
||||||
|
};
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual int transposeMono(SAMPLETYPE *dest,
|
||||||
|
const SAMPLETYPE *src,
|
||||||
|
int &srcSamples) = 0;
|
||||||
|
virtual int transposeStereo(SAMPLETYPE *dest,
|
||||||
|
const SAMPLETYPE *src,
|
||||||
|
int &srcSamples) = 0;
|
||||||
|
virtual int transposeMulti(SAMPLETYPE *dest,
|
||||||
|
const SAMPLETYPE *src,
|
||||||
|
int &srcSamples) = 0;
|
||||||
|
|
||||||
|
static ALGORITHM algorithm;
|
||||||
|
|
||||||
|
public:
|
||||||
|
double rate;
|
||||||
|
int numChannels;
|
||||||
|
|
||||||
|
TransposerBase();
|
||||||
|
virtual ~TransposerBase();
|
||||||
|
|
||||||
|
virtual int transpose(FIFOSampleBuffer &dest, FIFOSampleBuffer &src);
|
||||||
|
virtual void setRate(double newRate);
|
||||||
|
virtual void setChannels(int channels);
|
||||||
|
virtual int getLatency() const = 0;
|
||||||
|
|
||||||
|
virtual void resetRegisters() = 0;
|
||||||
|
|
||||||
|
// static factory function
|
||||||
|
static TransposerBase *newInstance();
|
||||||
|
|
||||||
|
// static function to set interpolation algorithm
|
||||||
|
static void setAlgorithm(ALGORITHM a);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
/// A common linear samplerate transposer class.
|
/// A common linear samplerate transposer class.
|
||||||
///
|
///
|
||||||
/// Note: Use function "RateTransposer::newInstance()" to create a new class
|
|
||||||
/// instance instead of the "new" operator; that function automatically
|
|
||||||
/// chooses a correct implementation depending on if integer or floating
|
|
||||||
/// arithmetics are to be used.
|
|
||||||
class RateTransposer : public FIFOProcessor
|
class RateTransposer : public FIFOProcessor
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
/// Anti-alias filter object
|
/// Anti-alias filter object
|
||||||
AAFilter *pAAFilter;
|
AAFilter *pAAFilter;
|
||||||
|
TransposerBase *pTransposer;
|
||||||
float fRate;
|
|
||||||
|
|
||||||
int numChannels;
|
|
||||||
|
|
||||||
/// Buffer for collecting samples to feed the anti-alias filter between
|
/// Buffer for collecting samples to feed the anti-alias filter between
|
||||||
/// two batches
|
/// two batches
|
||||||
FIFOSampleBuffer storeBuffer;
|
FIFOSampleBuffer inputBuffer;
|
||||||
|
|
||||||
/// Buffer for keeping samples between transposing & anti-alias filter
|
/// Buffer for keeping samples between transposing & anti-alias filter
|
||||||
FIFOSampleBuffer tempBuffer;
|
FIFOSampleBuffer midBuffer;
|
||||||
|
|
||||||
/// Output sample buffer
|
/// Output sample buffer
|
||||||
FIFOSampleBuffer outputBuffer;
|
FIFOSampleBuffer outputBuffer;
|
||||||
|
|
||||||
BOOL bUseAAFilter;
|
bool bUseAAFilter;
|
||||||
|
|
||||||
virtual void resetRegisters() = 0;
|
|
||||||
|
|
||||||
virtual uint transposeStereo(SAMPLETYPE *dest,
|
|
||||||
const SAMPLETYPE *src,
|
|
||||||
uint numSamples) = 0;
|
|
||||||
virtual uint transposeMono(SAMPLETYPE *dest,
|
|
||||||
const SAMPLETYPE *src,
|
|
||||||
uint numSamples) = 0;
|
|
||||||
inline uint transpose(SAMPLETYPE *dest,
|
|
||||||
const SAMPLETYPE *src,
|
|
||||||
uint numSamples);
|
|
||||||
|
|
||||||
void downsample(const SAMPLETYPE *src,
|
|
||||||
uint numSamples);
|
|
||||||
void upsample(const SAMPLETYPE *src,
|
|
||||||
uint numSamples);
|
|
||||||
|
|
||||||
/// Transposes sample rate by applying anti-alias filter to prevent folding.
|
/// Transposes sample rate by applying anti-alias filter to prevent folding.
|
||||||
/// Returns amount of samples returned in the "dest" buffer.
|
/// Returns amount of samples returned in the "dest" buffer.
|
||||||
@ -107,51 +122,41 @@ protected:
|
|||||||
void processSamples(const SAMPLETYPE *src,
|
void processSamples(const SAMPLETYPE *src,
|
||||||
uint numSamples);
|
uint numSamples);
|
||||||
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
RateTransposer();
|
RateTransposer();
|
||||||
virtual ~RateTransposer();
|
virtual ~RateTransposer() override;
|
||||||
|
|
||||||
/// Operator 'new' is overloaded so that it automatically creates a suitable instance
|
|
||||||
/// depending on if we're to use integer or floating point arithmetics.
|
|
||||||
static void *operator new(size_t s);
|
|
||||||
|
|
||||||
/// Use this function instead of "new" operator to create a new instance of this class.
|
|
||||||
/// This function automatically chooses a correct implementation, depending on if
|
|
||||||
/// integer ot floating point arithmetics are to be used.
|
|
||||||
static RateTransposer *newInstance();
|
|
||||||
|
|
||||||
/// Returns the output buffer object
|
/// Returns the output buffer object
|
||||||
FIFOSamplePipe *getOutput() { return &outputBuffer; };
|
FIFOSamplePipe *getOutput() { return &outputBuffer; };
|
||||||
|
|
||||||
/// Returns the store buffer object
|
|
||||||
FIFOSamplePipe *getStore() { return &storeBuffer; };
|
|
||||||
|
|
||||||
/// Return anti-alias filter object
|
/// Return anti-alias filter object
|
||||||
AAFilter *getAAFilter();
|
AAFilter *getAAFilter();
|
||||||
|
|
||||||
/// Enables/disables the anti-alias filter. Zero to disable, nonzero to enable
|
/// Enables/disables the anti-alias filter. Zero to disable, nonzero to enable
|
||||||
void enableAAFilter(BOOL newMode);
|
void enableAAFilter(bool newMode);
|
||||||
|
|
||||||
/// Returns nonzero if anti-alias filter is enabled.
|
/// Returns nonzero if anti-alias filter is enabled.
|
||||||
BOOL isAAFilterEnabled() const;
|
bool isAAFilterEnabled() const;
|
||||||
|
|
||||||
/// Sets new target rate. Normal rate = 1.0, smaller values represent slower
|
/// Sets new target rate. Normal rate = 1.0, smaller values represent slower
|
||||||
/// rate, larger faster rates.
|
/// rate, larger faster rates.
|
||||||
virtual void setRate(float newRate);
|
virtual void setRate(double newRate);
|
||||||
|
|
||||||
/// Sets the number of channels, 1 = mono, 2 = stereo
|
/// Sets the number of channels, 1 = mono, 2 = stereo
|
||||||
void setChannels(int channels);
|
void setChannels(int channels);
|
||||||
|
|
||||||
/// Adds 'numSamples' pcs of samples from the 'samples' memory position into
|
/// Adds 'numSamples' pcs of samples from the 'samples' memory position into
|
||||||
/// the input of the object.
|
/// the input of the object.
|
||||||
void putSamples(const SAMPLETYPE *samples, uint numSamples);
|
void putSamples(const SAMPLETYPE *samples, uint numSamples) override;
|
||||||
|
|
||||||
/// Clears all the samples in the object
|
/// Clears all the samples in the object
|
||||||
void clear();
|
void clear() override;
|
||||||
|
|
||||||
/// Returns nonzero if there aren't any samples available for outputting.
|
/// Returns nonzero if there aren't any samples available for outputting.
|
||||||
int isEmpty() const;
|
int isEmpty() const override;
|
||||||
|
|
||||||
|
/// Return approximate initial input-output latency
|
||||||
|
int getLatency() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -41,13 +41,6 @@
|
|||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Last changed : $Date$
|
|
||||||
// File revision : $Revision: 4 $
|
|
||||||
//
|
|
||||||
// $Id$
|
|
||||||
//
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// License :
|
// License :
|
||||||
//
|
//
|
||||||
// SoundTouch audio processing library
|
// SoundTouch audio processing library
|
||||||
@ -97,7 +90,7 @@ SoundTouch::SoundTouch()
|
|||||||
{
|
{
|
||||||
// Initialize rate transposer and tempo changer instances
|
// Initialize rate transposer and tempo changer instances
|
||||||
|
|
||||||
pRateTransposer = RateTransposer::newInstance();
|
pRateTransposer = new RateTransposer();
|
||||||
pTDStretch = TDStretch::newInstance();
|
pTDStretch = TDStretch::newInstance();
|
||||||
|
|
||||||
setOutPipe(pTDStretch);
|
setOutPipe(pTDStretch);
|
||||||
@ -110,10 +103,12 @@ SoundTouch::SoundTouch()
|
|||||||
|
|
||||||
calcEffectiveRateAndTempo();
|
calcEffectiveRateAndTempo();
|
||||||
|
|
||||||
channels = 0;
|
samplesExpectedOut = 0;
|
||||||
bSrateSet = FALSE;
|
samplesOutput = 0;
|
||||||
}
|
|
||||||
|
|
||||||
|
channels = 0;
|
||||||
|
bSrateSet = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
SoundTouch::~SoundTouch()
|
SoundTouch::~SoundTouch()
|
||||||
@ -123,7 +118,6 @@ SoundTouch::~SoundTouch()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// Get SoundTouch library version string
|
/// Get SoundTouch library version string
|
||||||
const char *SoundTouch::getVersionString()
|
const char *SoundTouch::getVersionString()
|
||||||
{
|
{
|
||||||
@ -143,89 +137,79 @@ uint SoundTouch::getVersionId()
|
|||||||
// Sets the number of channels, 1 = mono, 2 = stereo
|
// Sets the number of channels, 1 = mono, 2 = stereo
|
||||||
void SoundTouch::setChannels(uint numChannels)
|
void SoundTouch::setChannels(uint numChannels)
|
||||||
{
|
{
|
||||||
if (numChannels != 1 && numChannels != 2)
|
if (!verifyNumberOfChannels(numChannels)) return;
|
||||||
{
|
|
||||||
ST_THROW_RT_ERROR("Illegal number of channels");
|
|
||||||
}
|
|
||||||
channels = numChannels;
|
channels = numChannels;
|
||||||
pRateTransposer->setChannels((int)numChannels);
|
pRateTransposer->setChannels((int)numChannels);
|
||||||
pTDStretch->setChannels((int)numChannels);
|
pTDStretch->setChannels((int)numChannels);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Sets new rate control value. Normal rate = 1.0, smaller values
|
// Sets new rate control value. Normal rate = 1.0, smaller values
|
||||||
// represent slower rate, larger faster rates.
|
// represent slower rate, larger faster rates.
|
||||||
void SoundTouch::setRate(float newRate)
|
void SoundTouch::setRate(double newRate)
|
||||||
{
|
{
|
||||||
virtualRate = newRate;
|
virtualRate = newRate;
|
||||||
calcEffectiveRateAndTempo();
|
calcEffectiveRateAndTempo();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Sets new rate control value as a difference in percents compared
|
// Sets new rate control value as a difference in percents compared
|
||||||
// to the original rate (-50 .. +100 %)
|
// to the original rate (-50 .. +100 %)
|
||||||
void SoundTouch::setRateChange(float newRate)
|
void SoundTouch::setRateChange(double newRate)
|
||||||
{
|
{
|
||||||
virtualRate = 1.0f + 0.01f * newRate;
|
virtualRate = 1.0 + 0.01 * newRate;
|
||||||
calcEffectiveRateAndTempo();
|
calcEffectiveRateAndTempo();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Sets new tempo control value. Normal tempo = 1.0, smaller values
|
// Sets new tempo control value. Normal tempo = 1.0, smaller values
|
||||||
// represent slower tempo, larger faster tempo.
|
// represent slower tempo, larger faster tempo.
|
||||||
void SoundTouch::setTempo(float newTempo)
|
void SoundTouch::setTempo(double newTempo)
|
||||||
{
|
{
|
||||||
virtualTempo = newTempo;
|
virtualTempo = newTempo;
|
||||||
calcEffectiveRateAndTempo();
|
calcEffectiveRateAndTempo();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Sets new tempo control value as a difference in percents compared
|
// Sets new tempo control value as a difference in percents compared
|
||||||
// to the original tempo (-50 .. +100 %)
|
// to the original tempo (-50 .. +100 %)
|
||||||
void SoundTouch::setTempoChange(float newTempo)
|
void SoundTouch::setTempoChange(double newTempo)
|
||||||
{
|
{
|
||||||
virtualTempo = 1.0f + 0.01f * newTempo;
|
virtualTempo = 1.0 + 0.01 * newTempo;
|
||||||
calcEffectiveRateAndTempo();
|
calcEffectiveRateAndTempo();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Sets new pitch control value. Original pitch = 1.0, smaller values
|
// Sets new pitch control value. Original pitch = 1.0, smaller values
|
||||||
// represent lower pitches, larger values higher pitch.
|
// represent lower pitches, larger values higher pitch.
|
||||||
void SoundTouch::setPitch(float newPitch)
|
void SoundTouch::setPitch(double newPitch)
|
||||||
{
|
{
|
||||||
virtualPitch = newPitch;
|
virtualPitch = newPitch;
|
||||||
calcEffectiveRateAndTempo();
|
calcEffectiveRateAndTempo();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Sets pitch change in octaves compared to the original pitch
|
// Sets pitch change in octaves compared to the original pitch
|
||||||
// (-1.00 .. +1.00)
|
// (-1.00 .. +1.00)
|
||||||
void SoundTouch::setPitchOctaves(float newPitch)
|
void SoundTouch::setPitchOctaves(double newPitch)
|
||||||
{
|
{
|
||||||
virtualPitch = (float)exp(0.69314718056f * newPitch);
|
virtualPitch = exp(0.69314718056 * newPitch);
|
||||||
calcEffectiveRateAndTempo();
|
calcEffectiveRateAndTempo();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Sets pitch change in semi-tones compared to the original pitch
|
// Sets pitch change in semi-tones compared to the original pitch
|
||||||
// (-12 .. +12)
|
// (-12 .. +12)
|
||||||
void SoundTouch::setPitchSemiTones(int newPitch)
|
void SoundTouch::setPitchSemiTones(int newPitch)
|
||||||
{
|
{
|
||||||
setPitchOctaves((float)newPitch / 12.0f);
|
setPitchOctaves((double)newPitch / 12.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void SoundTouch::setPitchSemiTones(double newPitch)
|
||||||
void SoundTouch::setPitchSemiTones(float newPitch)
|
|
||||||
{
|
{
|
||||||
setPitchOctaves(newPitch / 12.0f);
|
setPitchOctaves(newPitch / 12.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -233,8 +217,8 @@ void SoundTouch::setPitchSemiTones(float newPitch)
|
|||||||
// nominal control values.
|
// nominal control values.
|
||||||
void SoundTouch::calcEffectiveRateAndTempo()
|
void SoundTouch::calcEffectiveRateAndTempo()
|
||||||
{
|
{
|
||||||
float oldTempo = tempo;
|
double oldTempo = tempo;
|
||||||
float oldRate = rate;
|
double oldRate = rate;
|
||||||
|
|
||||||
tempo = virtualTempo / virtualPitch;
|
tempo = virtualTempo / virtualPitch;
|
||||||
rate = virtualPitch * virtualRate;
|
rate = virtualPitch * virtualRate;
|
||||||
@ -254,7 +238,7 @@ void SoundTouch::calcEffectiveRateAndTempo()
|
|||||||
tempoOut = pTDStretch->getOutput();
|
tempoOut = pTDStretch->getOutput();
|
||||||
tempoOut->moveSamples(*output);
|
tempoOut->moveSamples(*output);
|
||||||
// move samples in pitch transposer's store buffer to tempo changer's input
|
// move samples in pitch transposer's store buffer to tempo changer's input
|
||||||
pTDStretch->moveSamples(*pRateTransposer->getStore());
|
// deprecated : pTDStretch->moveSamples(*pRateTransposer->getStore());
|
||||||
|
|
||||||
output = pTDStretch;
|
output = pTDStretch;
|
||||||
}
|
}
|
||||||
@ -282,9 +266,9 @@ void SoundTouch::calcEffectiveRateAndTempo()
|
|||||||
// Sets sample rate.
|
// Sets sample rate.
|
||||||
void SoundTouch::setSampleRate(uint srate)
|
void SoundTouch::setSampleRate(uint srate)
|
||||||
{
|
{
|
||||||
bSrateSet = TRUE;
|
|
||||||
// set sample rate, leave other tempo changer parameters as they are.
|
// set sample rate, leave other tempo changer parameters as they are.
|
||||||
pTDStretch->setParameters((int)srate);
|
pTDStretch->setParameters((int)srate);
|
||||||
|
bSrateSet = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -292,7 +276,7 @@ void SoundTouch::setSampleRate(uint srate)
|
|||||||
// the input of the object.
|
// the input of the object.
|
||||||
void SoundTouch::putSamples(const SAMPLETYPE *samples, uint nSamples)
|
void SoundTouch::putSamples(const SAMPLETYPE *samples, uint nSamples)
|
||||||
{
|
{
|
||||||
if (bSrateSet == FALSE)
|
if (bSrateSet == false)
|
||||||
{
|
{
|
||||||
ST_THROW_RT_ERROR("SoundTouch : Sample rate not defined");
|
ST_THROW_RT_ERROR("SoundTouch : Sample rate not defined");
|
||||||
}
|
}
|
||||||
@ -301,23 +285,12 @@ void SoundTouch::putSamples(const SAMPLETYPE *samples, uint nSamples)
|
|||||||
ST_THROW_RT_ERROR("SoundTouch : Number of channels not defined");
|
ST_THROW_RT_ERROR("SoundTouch : Number of channels not defined");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Transpose the rate of the new samples if necessary
|
// accumulate how many samples are expected out from processing, given the current
|
||||||
/* Bypass the nominal setting - can introduce a click in sound when tempo/pitch control crosses the nominal value...
|
// processing setting
|
||||||
if (rate == 1.0f)
|
samplesExpectedOut += (double)nSamples / ((double)rate * (double)tempo);
|
||||||
{
|
|
||||||
// The rate value is same as the original, simply evaluate the tempo changer.
|
|
||||||
assert(output == pTDStretch);
|
|
||||||
if (pRateTransposer->isEmpty() == 0)
|
|
||||||
{
|
|
||||||
// yet flush the last samples in the pitch transposer buffer
|
|
||||||
// (may happen if 'rate' changes from a non-zero value to zero)
|
|
||||||
pTDStretch->moveSamples(*pRateTransposer);
|
|
||||||
}
|
|
||||||
pTDStretch->putSamples(samples, nSamples);
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
#ifndef SOUNDTOUCH_PREVENT_CLICK_AT_RATE_CROSSOVER
|
#ifndef SOUNDTOUCH_PREVENT_CLICK_AT_RATE_CROSSOVER
|
||||||
else if (rate <= 1.0f)
|
if (rate <= 1.0f)
|
||||||
{
|
{
|
||||||
// transpose the rate down, output the transposed sound to tempo changer buffer
|
// transpose the rate down, output the transposed sound to tempo changer buffer
|
||||||
assert(output == pTDStretch);
|
assert(output == pTDStretch);
|
||||||
@ -345,49 +318,37 @@ void SoundTouch::putSamples(const SAMPLETYPE *samples, uint nSamples)
|
|||||||
void SoundTouch::flush()
|
void SoundTouch::flush()
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
int nUnprocessed;
|
int numStillExpected;
|
||||||
int nOut;
|
SAMPLETYPE *buff = new SAMPLETYPE[128 * channels];
|
||||||
SAMPLETYPE buff[64*2]; // note: allocate 2*64 to cater 64 sample frames of stereo sound
|
|
||||||
|
|
||||||
// check how many samples still await processing, and scale
|
// how many samples are still expected to output
|
||||||
// that by tempo & rate to get expected output sample count
|
numStillExpected = (int)((long)(samplesExpectedOut + 0.5) - samplesOutput);
|
||||||
nUnprocessed = numUnprocessedSamples();
|
if (numStillExpected < 0) numStillExpected = 0;
|
||||||
nUnprocessed = (int)((double)nUnprocessed / (tempo * rate) + 0.5);
|
|
||||||
|
|
||||||
nOut = numSamples(); // ready samples currently in buffer ...
|
memset(buff, 0, 128 * channels * sizeof(SAMPLETYPE));
|
||||||
nOut += nUnprocessed; // ... and how many we expect there to be in the end
|
|
||||||
|
|
||||||
memset(buff, 0, 64 * channels * sizeof(SAMPLETYPE));
|
|
||||||
// "Push" the last active samples out from the processing pipeline by
|
// "Push" the last active samples out from the processing pipeline by
|
||||||
// feeding blank samples into the processing pipeline until new,
|
// feeding blank samples into the processing pipeline until new,
|
||||||
// processed samples appear in the output (not however, more than
|
// processed samples appear in the output (not however, more than
|
||||||
// 8ksamples in any case)
|
// 24ksamples in any case)
|
||||||
for (i = 0; i < 128; i ++)
|
for (i = 0; (numStillExpected > (int)numSamples()) && (i < 200); i ++)
|
||||||
{
|
{
|
||||||
putSamples(buff, 64);
|
putSamples(buff, 128);
|
||||||
if ((int)numSamples() >= nOut)
|
|
||||||
{
|
|
||||||
// Enough new samples have appeared into the output!
|
|
||||||
// As samples come from processing with bigger chunks, now truncate it
|
|
||||||
// back to maximum "nOut" samples to improve duration accuracy
|
|
||||||
adjustAmountOfSamples(nOut);
|
|
||||||
|
|
||||||
// finish
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clear working buffers
|
adjustAmountOfSamples(numStillExpected);
|
||||||
pRateTransposer->clear();
|
|
||||||
|
delete[] buff;
|
||||||
|
|
||||||
|
// Clear input buffers
|
||||||
pTDStretch->clearInput();
|
pTDStretch->clearInput();
|
||||||
// yet leave the 'tempoChanger' output intouched as that's where the
|
// yet leave the output intouched as that's where the
|
||||||
// flushed samples are!
|
// flushed samples are!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Changes a setting controlling the processing system behaviour. See the
|
// Changes a setting controlling the processing system behaviour. See the
|
||||||
// 'SETTING_...' defines for available setting ID's.
|
// 'SETTING_...' defines for available setting ID's.
|
||||||
BOOL SoundTouch::setSetting(int settingId, int value)
|
bool SoundTouch::setSetting(int settingId, int value)
|
||||||
{
|
{
|
||||||
int sampleRate, sequenceMs, seekWindowMs, overlapMs;
|
int sampleRate, sequenceMs, seekWindowMs, overlapMs;
|
||||||
|
|
||||||
@ -398,36 +359,36 @@ BOOL SoundTouch::setSetting(int settingId, int value)
|
|||||||
{
|
{
|
||||||
case SETTING_USE_AA_FILTER :
|
case SETTING_USE_AA_FILTER :
|
||||||
// enables / disabless anti-alias filter
|
// enables / disabless anti-alias filter
|
||||||
pRateTransposer->enableAAFilter((value != 0) ? TRUE : FALSE);
|
pRateTransposer->enableAAFilter((value != 0) ? true : false);
|
||||||
return TRUE;
|
return true;
|
||||||
|
|
||||||
case SETTING_AA_FILTER_LENGTH :
|
case SETTING_AA_FILTER_LENGTH :
|
||||||
// sets anti-alias filter length
|
// sets anti-alias filter length
|
||||||
pRateTransposer->getAAFilter()->setLength(value);
|
pRateTransposer->getAAFilter()->setLength(value);
|
||||||
return TRUE;
|
return true;
|
||||||
|
|
||||||
case SETTING_USE_QUICKSEEK :
|
case SETTING_USE_QUICKSEEK :
|
||||||
// enables / disables tempo routine quick seeking algorithm
|
// enables / disables tempo routine quick seeking algorithm
|
||||||
pTDStretch->enableQuickSeek((value != 0) ? TRUE : FALSE);
|
pTDStretch->enableQuickSeek((value != 0) ? true : false);
|
||||||
return TRUE;
|
return true;
|
||||||
|
|
||||||
case SETTING_SEQUENCE_MS:
|
case SETTING_SEQUENCE_MS:
|
||||||
// change time-stretch sequence duration parameter
|
// change time-stretch sequence duration parameter
|
||||||
pTDStretch->setParameters(sampleRate, value, seekWindowMs, overlapMs);
|
pTDStretch->setParameters(sampleRate, value, seekWindowMs, overlapMs);
|
||||||
return TRUE;
|
return true;
|
||||||
|
|
||||||
case SETTING_SEEKWINDOW_MS:
|
case SETTING_SEEKWINDOW_MS:
|
||||||
// change time-stretch seek window length parameter
|
// change time-stretch seek window length parameter
|
||||||
pTDStretch->setParameters(sampleRate, sequenceMs, value, overlapMs);
|
pTDStretch->setParameters(sampleRate, sequenceMs, value, overlapMs);
|
||||||
return TRUE;
|
return true;
|
||||||
|
|
||||||
case SETTING_OVERLAP_MS:
|
case SETTING_OVERLAP_MS:
|
||||||
// change time-stretch overlap length parameter
|
// change time-stretch overlap length parameter
|
||||||
pTDStretch->setParameters(sampleRate, sequenceMs, seekWindowMs, value);
|
pTDStretch->setParameters(sampleRate, sequenceMs, seekWindowMs, value);
|
||||||
return TRUE;
|
return true;
|
||||||
|
|
||||||
default :
|
default :
|
||||||
return FALSE;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -452,22 +413,62 @@ int SoundTouch::getSetting(int settingId) const
|
|||||||
return (uint)pTDStretch->isQuickSeekEnabled();
|
return (uint)pTDStretch->isQuickSeekEnabled();
|
||||||
|
|
||||||
case SETTING_SEQUENCE_MS:
|
case SETTING_SEQUENCE_MS:
|
||||||
pTDStretch->getParameters(NULL, &temp, NULL, NULL);
|
pTDStretch->getParameters(nullptr, &temp, nullptr, nullptr);
|
||||||
return temp;
|
return temp;
|
||||||
|
|
||||||
case SETTING_SEEKWINDOW_MS:
|
case SETTING_SEEKWINDOW_MS:
|
||||||
pTDStretch->getParameters(NULL, NULL, &temp, NULL);
|
pTDStretch->getParameters(nullptr, nullptr, &temp, nullptr);
|
||||||
return temp;
|
return temp;
|
||||||
|
|
||||||
case SETTING_OVERLAP_MS:
|
case SETTING_OVERLAP_MS:
|
||||||
pTDStretch->getParameters(NULL, NULL, NULL, &temp);
|
pTDStretch->getParameters(nullptr, nullptr, nullptr, &temp);
|
||||||
return temp;
|
return temp;
|
||||||
|
|
||||||
case SETTING_NOMINAL_INPUT_SEQUENCE :
|
case SETTING_NOMINAL_INPUT_SEQUENCE :
|
||||||
return pTDStretch->getInputSampleReq();
|
{
|
||||||
|
int size = pTDStretch->getInputSampleReq();
|
||||||
|
|
||||||
|
#ifndef SOUNDTOUCH_PREVENT_CLICK_AT_RATE_CROSSOVER
|
||||||
|
if (rate <= 1.0)
|
||||||
|
{
|
||||||
|
// transposing done before timestretch, which impacts latency
|
||||||
|
return (int)(size * rate + 0.5);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
case SETTING_NOMINAL_OUTPUT_SEQUENCE :
|
case SETTING_NOMINAL_OUTPUT_SEQUENCE :
|
||||||
return pTDStretch->getOutputBatchSize();
|
{
|
||||||
|
int size = pTDStretch->getOutputBatchSize();
|
||||||
|
|
||||||
|
if (rate > 1.0)
|
||||||
|
{
|
||||||
|
// transposing done after timestretch, which impacts latency
|
||||||
|
return (int)(size / rate + 0.5);
|
||||||
|
}
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
case SETTING_INITIAL_LATENCY:
|
||||||
|
{
|
||||||
|
double latency = pTDStretch->getLatency();
|
||||||
|
int latency_tr = pRateTransposer->getLatency();
|
||||||
|
|
||||||
|
#ifndef SOUNDTOUCH_PREVENT_CLICK_AT_RATE_CROSSOVER
|
||||||
|
if (rate <= 1.0)
|
||||||
|
{
|
||||||
|
// transposing done before timestretch, which impacts latency
|
||||||
|
latency = (latency + latency_tr) * rate;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
latency += (double)latency_tr / rate;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (int)(latency + 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
default :
|
default :
|
||||||
return 0;
|
return 0;
|
||||||
@ -479,12 +480,13 @@ int SoundTouch::getSetting(int settingId) const
|
|||||||
// buffers.
|
// buffers.
|
||||||
void SoundTouch::clear()
|
void SoundTouch::clear()
|
||||||
{
|
{
|
||||||
|
samplesExpectedOut = 0;
|
||||||
|
samplesOutput = 0;
|
||||||
pRateTransposer->clear();
|
pRateTransposer->clear();
|
||||||
pTDStretch->clear();
|
pTDStretch->clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// Returns number of samples currently unprocessed.
|
/// Returns number of samples currently unprocessed.
|
||||||
uint SoundTouch::numUnprocessedSamples() const
|
uint SoundTouch::numUnprocessedSamples() const
|
||||||
{
|
{
|
||||||
@ -499,3 +501,38 @@ uint SoundTouch::numUnprocessedSamples() const
|
|||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Output samples from beginning of the sample buffer. Copies requested samples to
|
||||||
|
/// output buffer and removes them from the sample buffer. If there are less than
|
||||||
|
/// 'numsample' samples in the buffer, returns all that available.
|
||||||
|
///
|
||||||
|
/// \return Number of samples returned.
|
||||||
|
uint SoundTouch::receiveSamples(SAMPLETYPE *output, uint maxSamples)
|
||||||
|
{
|
||||||
|
uint ret = FIFOProcessor::receiveSamples(output, maxSamples);
|
||||||
|
samplesOutput += (long)ret;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Adjusts book-keeping so that given number of samples are removed from beginning of the
|
||||||
|
/// sample buffer without copying them anywhere.
|
||||||
|
///
|
||||||
|
/// Used to reduce the number of samples in the buffer when accessing the sample buffer directly
|
||||||
|
/// with 'ptrBegin' function.
|
||||||
|
uint SoundTouch::receiveSamples(uint maxSamples)
|
||||||
|
{
|
||||||
|
uint ret = FIFOProcessor::receiveSamples(maxSamples);
|
||||||
|
samplesOutput += (long)ret;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Get ratio between input and output audio durations, useful for calculating
|
||||||
|
/// processed output duration: if you'll process a stream of N samples, then
|
||||||
|
/// you can expect to get out N * getInputOutputSampleRatio() samples.
|
||||||
|
double SoundTouch::getInputOutputSampleRatio()
|
||||||
|
{
|
||||||
|
return 1.0 / (tempo * rate);
|
||||||
|
}
|
||||||
|
|||||||
@ -1,188 +0,0 @@
|
|||||||
# Microsoft Developer Studio Project File - Name="SoundTouch" - Package Owner=<4>
|
|
||||||
# Microsoft Developer Studio Generated Build File, Format Version 6.00
|
|
||||||
# ** DO NOT EDIT **
|
|
||||||
|
|
||||||
# TARGTYPE "Win32 (x86) Static Library" 0x0104
|
|
||||||
|
|
||||||
CFG=SoundTouch - Win32 Debug
|
|
||||||
!MESSAGE This is not a valid makefile. To build this project using NMAKE,
|
|
||||||
!MESSAGE use the Export Makefile command and run
|
|
||||||
!MESSAGE
|
|
||||||
!MESSAGE NMAKE /f "SoundTouch.mak".
|
|
||||||
!MESSAGE
|
|
||||||
!MESSAGE You can specify a configuration when running NMAKE
|
|
||||||
!MESSAGE by defining the macro CFG on the command line. For example:
|
|
||||||
!MESSAGE
|
|
||||||
!MESSAGE NMAKE /f "SoundTouch.mak" CFG="SoundTouch - Win32 Debug"
|
|
||||||
!MESSAGE
|
|
||||||
!MESSAGE Possible choices for configuration are:
|
|
||||||
!MESSAGE
|
|
||||||
!MESSAGE "SoundTouch - Win32 Release" (based on "Win32 (x86) Static Library")
|
|
||||||
!MESSAGE "SoundTouch - Win32 Debug" (based on "Win32 (x86) Static Library")
|
|
||||||
!MESSAGE
|
|
||||||
|
|
||||||
# Begin Project
|
|
||||||
# PROP AllowPerConfigDependencies 0
|
|
||||||
# PROP Scc_ProjName ""
|
|
||||||
# PROP Scc_LocalPath ""
|
|
||||||
CPP=cl.exe
|
|
||||||
RSC=rc.exe
|
|
||||||
|
|
||||||
!IF "$(CFG)" == "SoundTouch - Win32 Release"
|
|
||||||
|
|
||||||
# PROP BASE Use_MFC 0
|
|
||||||
# PROP BASE Use_Debug_Libraries 0
|
|
||||||
# PROP BASE Output_Dir "Release"
|
|
||||||
# PROP BASE Intermediate_Dir "Release"
|
|
||||||
# PROP BASE Target_Dir ""
|
|
||||||
# PROP Use_MFC 0
|
|
||||||
# PROP Use_Debug_Libraries 0
|
|
||||||
# PROP Output_Dir "Release"
|
|
||||||
# PROP Intermediate_Dir "Release"
|
|
||||||
# PROP Target_Dir ""
|
|
||||||
# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c
|
|
||||||
# ADD CPP /nologo /W3 /GX /Zi /O2 /I "..\..\include" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c
|
|
||||||
# ADD BASE RSC /l 0x40b /d "NDEBUG"
|
|
||||||
# ADD RSC /l 0x40b /d "NDEBUG"
|
|
||||||
BSC32=bscmake.exe
|
|
||||||
# ADD BASE BSC32 /nologo
|
|
||||||
# ADD BSC32 /nologo
|
|
||||||
LIB32=link.exe -lib
|
|
||||||
# ADD BASE LIB32 /nologo
|
|
||||||
# ADD LIB32 /nologo
|
|
||||||
# Begin Special Build Tool
|
|
||||||
SOURCE="$(InputPath)"
|
|
||||||
PostBuild_Cmds=copy .\Release\SoundTouch.lib ..\..\lib\
|
|
||||||
# End Special Build Tool
|
|
||||||
|
|
||||||
!ELSEIF "$(CFG)" == "SoundTouch - Win32 Debug"
|
|
||||||
|
|
||||||
# PROP BASE Use_MFC 0
|
|
||||||
# PROP BASE Use_Debug_Libraries 1
|
|
||||||
# PROP BASE Output_Dir "Debug"
|
|
||||||
# PROP BASE Intermediate_Dir "Debug"
|
|
||||||
# PROP BASE Target_Dir ""
|
|
||||||
# PROP Use_MFC 0
|
|
||||||
# PROP Use_Debug_Libraries 1
|
|
||||||
# PROP Output_Dir "Debug"
|
|
||||||
# PROP Intermediate_Dir "Debug"
|
|
||||||
# PROP Target_Dir ""
|
|
||||||
# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c
|
|
||||||
# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /I "..\..\include" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FR /YX /FD /GZ /c
|
|
||||||
# ADD BASE RSC /l 0x40b /d "_DEBUG"
|
|
||||||
# ADD RSC /l 0x40b /d "_DEBUG"
|
|
||||||
BSC32=bscmake.exe
|
|
||||||
# ADD BASE BSC32 /nologo
|
|
||||||
# ADD BSC32 /nologo
|
|
||||||
LIB32=link.exe -lib
|
|
||||||
# ADD BASE LIB32 /nologo
|
|
||||||
# ADD LIB32 /nologo /out:"Debug\SoundTouchD.lib"
|
|
||||||
# Begin Special Build Tool
|
|
||||||
SOURCE="$(InputPath)"
|
|
||||||
PostBuild_Cmds=copy .\Debug\SoundTouchD.lib ..\..\lib\
|
|
||||||
# End Special Build Tool
|
|
||||||
|
|
||||||
!ENDIF
|
|
||||||
|
|
||||||
# Begin Target
|
|
||||||
|
|
||||||
# Name "SoundTouch - Win32 Release"
|
|
||||||
# Name "SoundTouch - Win32 Debug"
|
|
||||||
# Begin Group "Source Files"
|
|
||||||
|
|
||||||
# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
|
|
||||||
# Begin Group "bpm"
|
|
||||||
|
|
||||||
# PROP Default_Filter ""
|
|
||||||
# Begin Source File
|
|
||||||
|
|
||||||
SOURCE=.\BPMDetect.cpp
|
|
||||||
# End Source File
|
|
||||||
# Begin Source File
|
|
||||||
|
|
||||||
SOURCE=.\PeakFinder.cpp
|
|
||||||
# End Source File
|
|
||||||
# End Group
|
|
||||||
# Begin Source File
|
|
||||||
|
|
||||||
SOURCE=.\3dnow_win.cpp
|
|
||||||
# End Source File
|
|
||||||
# Begin Source File
|
|
||||||
|
|
||||||
SOURCE=.\AAFilter.cpp
|
|
||||||
# End Source File
|
|
||||||
# Begin Source File
|
|
||||||
|
|
||||||
SOURCE=.\cpu_detect_x86_win.cpp
|
|
||||||
# End Source File
|
|
||||||
# Begin Source File
|
|
||||||
|
|
||||||
SOURCE=.\FIFOSampleBuffer.cpp
|
|
||||||
# End Source File
|
|
||||||
# Begin Source File
|
|
||||||
|
|
||||||
SOURCE=.\FIRFilter.cpp
|
|
||||||
# End Source File
|
|
||||||
# Begin Source File
|
|
||||||
|
|
||||||
SOURCE=.\mmx_optimized.cpp
|
|
||||||
# End Source File
|
|
||||||
# Begin Source File
|
|
||||||
|
|
||||||
SOURCE=.\RateTransposer.cpp
|
|
||||||
# End Source File
|
|
||||||
# Begin Source File
|
|
||||||
|
|
||||||
SOURCE=.\SoundTouch.cpp
|
|
||||||
# End Source File
|
|
||||||
# Begin Source File
|
|
||||||
|
|
||||||
SOURCE=.\sse_optimized.cpp
|
|
||||||
# End Source File
|
|
||||||
# Begin Source File
|
|
||||||
|
|
||||||
SOURCE=.\TDStretch.cpp
|
|
||||||
# End Source File
|
|
||||||
# End Group
|
|
||||||
# Begin Group "Header Files"
|
|
||||||
|
|
||||||
# PROP Default_Filter "h;hpp;hxx;hm;inl"
|
|
||||||
# Begin Source File
|
|
||||||
|
|
||||||
SOURCE=.\AAFilter.h
|
|
||||||
# End Source File
|
|
||||||
# Begin Source File
|
|
||||||
|
|
||||||
SOURCE=.\cpu_detect.h
|
|
||||||
# End Source File
|
|
||||||
# Begin Source File
|
|
||||||
|
|
||||||
SOURCE=..\..\include\FIFOSampleBuffer.h
|
|
||||||
# End Source File
|
|
||||||
# Begin Source File
|
|
||||||
|
|
||||||
SOURCE=..\..\include\FIFOSamplePipe.h
|
|
||||||
# End Source File
|
|
||||||
# Begin Source File
|
|
||||||
|
|
||||||
SOURCE=.\FIRFilter.h
|
|
||||||
# End Source File
|
|
||||||
# Begin Source File
|
|
||||||
|
|
||||||
SOURCE=.\RateTransposer.h
|
|
||||||
# End Source File
|
|
||||||
# Begin Source File
|
|
||||||
|
|
||||||
SOURCE=..\..\include\SoundTouch.h
|
|
||||||
# End Source File
|
|
||||||
# Begin Source File
|
|
||||||
|
|
||||||
SOURCE=..\..\include\STTypes.h
|
|
||||||
# End Source File
|
|
||||||
# Begin Source File
|
|
||||||
|
|
||||||
SOURCE=.\TDStretch.h
|
|
||||||
# End Source File
|
|
||||||
# End Group
|
|
||||||
# End Target
|
|
||||||
# End Project
|
|
||||||
@ -1,31 +0,0 @@
|
|||||||
Microsoft Developer Studio Workspace File, Format Version 6.00
|
|
||||||
# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
|
|
||||||
#
|
|
||||||
# $Id$
|
|
||||||
|
|
||||||
###############################################################################
|
|
||||||
|
|
||||||
Project: "SoundTouch"=.\SoundTouch.dsp - Package Owner=<4>
|
|
||||||
|
|
||||||
Package=<5>
|
|
||||||
{{{
|
|
||||||
}}}
|
|
||||||
|
|
||||||
Package=<4>
|
|
||||||
{{{
|
|
||||||
}}}
|
|
||||||
|
|
||||||
###############################################################################
|
|
||||||
|
|
||||||
Global:
|
|
||||||
|
|
||||||
Package=<5>
|
|
||||||
{{{
|
|
||||||
}}}
|
|
||||||
|
|
||||||
Package=<3>
|
|
||||||
{{{
|
|
||||||
}}}
|
|
||||||
|
|
||||||
###############################################################################
|
|
||||||
|
|
||||||
@ -1,21 +1,29 @@
|
|||||||
Microsoft Visual Studio Solution File, Format Version 8.00
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SoundTouch", "SoundTouch.vcproj", "{0A626C77-0515-4131-AA80-E0BFFC479FEB}"
|
# Visual Studio 14
|
||||||
ProjectSection(ProjectDependencies) = postProject
|
VisualStudioVersion = 14.0.23107.0
|
||||||
EndProjectSection
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
|
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SoundTouch", "SoundTouch.vcxproj", "{68A5DD20-7057-448B-8FE0-B6AC8D205509}"
|
||||||
EndProject
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfiguration) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug = Debug
|
Debug|Win32 = Debug|Win32
|
||||||
Release = Release
|
Debug|x64 = Debug|x64
|
||||||
|
Release|Win32 = Release|Win32
|
||||||
|
Release|x64 = Release|x64
|
||||||
|
ReleaseX64|Win32 = ReleaseX64|Win32
|
||||||
|
ReleaseX64|x64 = ReleaseX64|x64
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(ProjectConfiguration) = postSolution
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
{0A626C77-0515-4131-AA80-E0BFFC479FEB}.Debug.ActiveCfg = Debug|Win32
|
{68A5DD20-7057-448B-8FE0-B6AC8D205509}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||||
{0A626C77-0515-4131-AA80-E0BFFC479FEB}.Debug.Build.0 = Debug|Win32
|
{68A5DD20-7057-448B-8FE0-B6AC8D205509}.Debug|x64.ActiveCfg = Debug|x64
|
||||||
{0A626C77-0515-4131-AA80-E0BFFC479FEB}.Release.ActiveCfg = Release|Win32
|
{68A5DD20-7057-448B-8FE0-B6AC8D205509}.Release|Win32.ActiveCfg = Release|Win32
|
||||||
{0A626C77-0515-4131-AA80-E0BFFC479FEB}.Release.Build.0 = Release|Win32
|
{68A5DD20-7057-448B-8FE0-B6AC8D205509}.Release|x64.ActiveCfg = Release|x64
|
||||||
|
{68A5DD20-7057-448B-8FE0-B6AC8D205509}.ReleaseX64|Win32.ActiveCfg = ReleaseX64|Win32
|
||||||
|
{68A5DD20-7057-448B-8FE0-B6AC8D205509}.ReleaseX64|Win32.Build.0 = ReleaseX64|Win32
|
||||||
|
{68A5DD20-7057-448B-8FE0-B6AC8D205509}.ReleaseX64|x64.ActiveCfg = ReleaseX64|x64
|
||||||
|
{68A5DD20-7057-448B-8FE0-B6AC8D205509}.ReleaseX64|x64.Build.0 = ReleaseX64|x64
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
EndGlobalSection
|
HideSolutionNode = FALSE
|
||||||
GlobalSection(ExtensibilityAddIns) = postSolution
|
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
EndGlobal
|
EndGlobal
|
||||||
|
|||||||
@ -1,315 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="Windows-1252"?>
|
|
||||||
<VisualStudioProject
|
|
||||||
ProjectType="Visual C++"
|
|
||||||
Version="7.10"
|
|
||||||
Name="SoundTouch"
|
|
||||||
ProjectGUID="{68A5DD20-7057-448B-8FE0-B6AC8D205509}"
|
|
||||||
SccProjectName=""
|
|
||||||
SccLocalPath="">
|
|
||||||
<Platforms>
|
|
||||||
<Platform
|
|
||||||
Name="Win32"/>
|
|
||||||
</Platforms>
|
|
||||||
<Configurations>
|
|
||||||
<Configuration
|
|
||||||
Name="Release|Win32"
|
|
||||||
OutputDirectory=".\Release"
|
|
||||||
IntermediateDirectory=".\Release"
|
|
||||||
ConfigurationType="4"
|
|
||||||
UseOfMFC="0"
|
|
||||||
ATLMinimizesCRunTimeLibraryUsage="FALSE"
|
|
||||||
CharacterSet="2">
|
|
||||||
<Tool
|
|
||||||
Name="VCCLCompilerTool"
|
|
||||||
Optimization="3"
|
|
||||||
InlineFunctionExpansion="2"
|
|
||||||
EnableIntrinsicFunctions="TRUE"
|
|
||||||
AdditionalIncludeDirectories="..\..\include"
|
|
||||||
PreprocessorDefinitions="WIN32;NDEBUG;_LIB"
|
|
||||||
StringPooling="TRUE"
|
|
||||||
RuntimeLibrary="4"
|
|
||||||
EnableFunctionLevelLinking="TRUE"
|
|
||||||
UsePrecompiledHeader="2"
|
|
||||||
PrecompiledHeaderFile=".\Release/SoundTouch.pch"
|
|
||||||
AssemblerListingLocation=".\Release/"
|
|
||||||
ObjectFile=".\Release/"
|
|
||||||
ProgramDataBaseFileName=".\Release/"
|
|
||||||
WarningLevel="3"
|
|
||||||
SuppressStartupBanner="TRUE"
|
|
||||||
DebugInformationFormat="0"
|
|
||||||
CompileAs="0"/>
|
|
||||||
<Tool
|
|
||||||
Name="VCCustomBuildTool"/>
|
|
||||||
<Tool
|
|
||||||
Name="VCLibrarianTool"
|
|
||||||
OutputFile=".\Release\SoundTouch.lib"
|
|
||||||
SuppressStartupBanner="TRUE"/>
|
|
||||||
<Tool
|
|
||||||
Name="VCMIDLTool"/>
|
|
||||||
<Tool
|
|
||||||
Name="VCPostBuildEventTool"
|
|
||||||
CommandLine="copy .\Release\SoundTouch.lib ..\..\lib\"/>
|
|
||||||
<Tool
|
|
||||||
Name="VCPreBuildEventTool"/>
|
|
||||||
<Tool
|
|
||||||
Name="VCPreLinkEventTool"/>
|
|
||||||
<Tool
|
|
||||||
Name="VCResourceCompilerTool"
|
|
||||||
PreprocessorDefinitions="NDEBUG"
|
|
||||||
Culture="1035"/>
|
|
||||||
<Tool
|
|
||||||
Name="VCWebServiceProxyGeneratorTool"/>
|
|
||||||
<Tool
|
|
||||||
Name="VCXMLDataGeneratorTool"/>
|
|
||||||
<Tool
|
|
||||||
Name="VCManagedWrapperGeneratorTool"/>
|
|
||||||
<Tool
|
|
||||||
Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
|
|
||||||
</Configuration>
|
|
||||||
<Configuration
|
|
||||||
Name="Debug|Win32"
|
|
||||||
OutputDirectory=".\Debug"
|
|
||||||
IntermediateDirectory=".\Debug"
|
|
||||||
ConfigurationType="4"
|
|
||||||
UseOfMFC="0"
|
|
||||||
ATLMinimizesCRunTimeLibraryUsage="FALSE"
|
|
||||||
CharacterSet="2">
|
|
||||||
<Tool
|
|
||||||
Name="VCCLCompilerTool"
|
|
||||||
Optimization="0"
|
|
||||||
AdditionalIncludeDirectories="..\..\include"
|
|
||||||
PreprocessorDefinitions="WIN32;_DEBUG;_LIB"
|
|
||||||
BasicRuntimeChecks="3"
|
|
||||||
RuntimeLibrary="5"
|
|
||||||
UsePrecompiledHeader="2"
|
|
||||||
PrecompiledHeaderFile=".\Debug/SoundTouch.pch"
|
|
||||||
AssemblerListingLocation=".\Debug/"
|
|
||||||
ObjectFile=".\Debug/"
|
|
||||||
ProgramDataBaseFileName=".\Debug/"
|
|
||||||
BrowseInformation="1"
|
|
||||||
WarningLevel="3"
|
|
||||||
SuppressStartupBanner="TRUE"
|
|
||||||
DebugInformationFormat="4"
|
|
||||||
CompileAs="0"/>
|
|
||||||
<Tool
|
|
||||||
Name="VCCustomBuildTool"/>
|
|
||||||
<Tool
|
|
||||||
Name="VCLibrarianTool"
|
|
||||||
OutputFile="Debug\SoundTouchD.lib"
|
|
||||||
SuppressStartupBanner="TRUE"/>
|
|
||||||
<Tool
|
|
||||||
Name="VCMIDLTool"/>
|
|
||||||
<Tool
|
|
||||||
Name="VCPostBuildEventTool"
|
|
||||||
CommandLine="copy .\Debug\SoundTouchD.lib ..\..\lib\"/>
|
|
||||||
<Tool
|
|
||||||
Name="VCPreBuildEventTool"/>
|
|
||||||
<Tool
|
|
||||||
Name="VCPreLinkEventTool"/>
|
|
||||||
<Tool
|
|
||||||
Name="VCResourceCompilerTool"
|
|
||||||
PreprocessorDefinitions="_DEBUG"
|
|
||||||
Culture="1035"/>
|
|
||||||
<Tool
|
|
||||||
Name="VCWebServiceProxyGeneratorTool"/>
|
|
||||||
<Tool
|
|
||||||
Name="VCXMLDataGeneratorTool"/>
|
|
||||||
<Tool
|
|
||||||
Name="VCManagedWrapperGeneratorTool"/>
|
|
||||||
<Tool
|
|
||||||
Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
|
|
||||||
</Configuration>
|
|
||||||
</Configurations>
|
|
||||||
<References>
|
|
||||||
</References>
|
|
||||||
<Files>
|
|
||||||
<Filter
|
|
||||||
Name="Source Files"
|
|
||||||
Filter="cpp;c;cxx;rc;def;r;odl;idl;hpj;bat">
|
|
||||||
<File
|
|
||||||
RelativePath="AAFilter.cpp">
|
|
||||||
<FileConfiguration
|
|
||||||
Name="Release|Win32">
|
|
||||||
<Tool
|
|
||||||
Name="VCCLCompilerTool"
|
|
||||||
Optimization="2"
|
|
||||||
AdditionalIncludeDirectories=""
|
|
||||||
PreprocessorDefinitions=""/>
|
|
||||||
</FileConfiguration>
|
|
||||||
<FileConfiguration
|
|
||||||
Name="Debug|Win32">
|
|
||||||
<Tool
|
|
||||||
Name="VCCLCompilerTool"
|
|
||||||
Optimization="0"
|
|
||||||
AdditionalIncludeDirectories=""
|
|
||||||
PreprocessorDefinitions=""
|
|
||||||
BasicRuntimeChecks="3"
|
|
||||||
BrowseInformation="1"/>
|
|
||||||
</FileConfiguration>
|
|
||||||
</File>
|
|
||||||
<File
|
|
||||||
RelativePath=".\cpu_detect_x86.cpp">
|
|
||||||
</File>
|
|
||||||
<File
|
|
||||||
RelativePath="FIFOSampleBuffer.cpp">
|
|
||||||
<FileConfiguration
|
|
||||||
Name="Release|Win32">
|
|
||||||
<Tool
|
|
||||||
Name="VCCLCompilerTool"
|
|
||||||
Optimization="2"
|
|
||||||
AdditionalIncludeDirectories=""
|
|
||||||
PreprocessorDefinitions=""/>
|
|
||||||
</FileConfiguration>
|
|
||||||
<FileConfiguration
|
|
||||||
Name="Debug|Win32">
|
|
||||||
<Tool
|
|
||||||
Name="VCCLCompilerTool"
|
|
||||||
Optimization="0"
|
|
||||||
AdditionalIncludeDirectories=""
|
|
||||||
PreprocessorDefinitions=""
|
|
||||||
BasicRuntimeChecks="3"
|
|
||||||
BrowseInformation="1"/>
|
|
||||||
</FileConfiguration>
|
|
||||||
</File>
|
|
||||||
<File
|
|
||||||
RelativePath="FIRFilter.cpp">
|
|
||||||
<FileConfiguration
|
|
||||||
Name="Release|Win32">
|
|
||||||
<Tool
|
|
||||||
Name="VCCLCompilerTool"
|
|
||||||
Optimization="2"
|
|
||||||
AdditionalIncludeDirectories=""
|
|
||||||
PreprocessorDefinitions=""/>
|
|
||||||
</FileConfiguration>
|
|
||||||
<FileConfiguration
|
|
||||||
Name="Debug|Win32">
|
|
||||||
<Tool
|
|
||||||
Name="VCCLCompilerTool"
|
|
||||||
Optimization="0"
|
|
||||||
AdditionalIncludeDirectories=""
|
|
||||||
PreprocessorDefinitions=""
|
|
||||||
BasicRuntimeChecks="3"
|
|
||||||
BrowseInformation="1"/>
|
|
||||||
</FileConfiguration>
|
|
||||||
</File>
|
|
||||||
<File
|
|
||||||
RelativePath=".\mmx_optimized.cpp">
|
|
||||||
</File>
|
|
||||||
<File
|
|
||||||
RelativePath="RateTransposer.cpp">
|
|
||||||
<FileConfiguration
|
|
||||||
Name="Release|Win32">
|
|
||||||
<Tool
|
|
||||||
Name="VCCLCompilerTool"
|
|
||||||
Optimization="2"
|
|
||||||
AdditionalIncludeDirectories=""
|
|
||||||
PreprocessorDefinitions=""/>
|
|
||||||
</FileConfiguration>
|
|
||||||
<FileConfiguration
|
|
||||||
Name="Debug|Win32">
|
|
||||||
<Tool
|
|
||||||
Name="VCCLCompilerTool"
|
|
||||||
Optimization="0"
|
|
||||||
AdditionalIncludeDirectories=""
|
|
||||||
PreprocessorDefinitions=""
|
|
||||||
BasicRuntimeChecks="3"
|
|
||||||
BrowseInformation="1"/>
|
|
||||||
</FileConfiguration>
|
|
||||||
</File>
|
|
||||||
<File
|
|
||||||
RelativePath="SoundTouch.cpp">
|
|
||||||
<FileConfiguration
|
|
||||||
Name="Release|Win32">
|
|
||||||
<Tool
|
|
||||||
Name="VCCLCompilerTool"
|
|
||||||
Optimization="2"
|
|
||||||
AdditionalIncludeDirectories=""
|
|
||||||
PreprocessorDefinitions=""/>
|
|
||||||
</FileConfiguration>
|
|
||||||
<FileConfiguration
|
|
||||||
Name="Debug|Win32">
|
|
||||||
<Tool
|
|
||||||
Name="VCCLCompilerTool"
|
|
||||||
Optimization="0"
|
|
||||||
AdditionalIncludeDirectories=""
|
|
||||||
PreprocessorDefinitions=""
|
|
||||||
BasicRuntimeChecks="3"
|
|
||||||
BrowseInformation="1"/>
|
|
||||||
</FileConfiguration>
|
|
||||||
</File>
|
|
||||||
<File
|
|
||||||
RelativePath=".\sse_optimized.cpp">
|
|
||||||
</File>
|
|
||||||
<File
|
|
||||||
RelativePath="TDStretch.cpp">
|
|
||||||
<FileConfiguration
|
|
||||||
Name="Release|Win32">
|
|
||||||
<Tool
|
|
||||||
Name="VCCLCompilerTool"
|
|
||||||
Optimization="2"
|
|
||||||
AdditionalIncludeDirectories=""
|
|
||||||
PreprocessorDefinitions=""/>
|
|
||||||
</FileConfiguration>
|
|
||||||
<FileConfiguration
|
|
||||||
Name="Debug|Win32">
|
|
||||||
<Tool
|
|
||||||
Name="VCCLCompilerTool"
|
|
||||||
Optimization="0"
|
|
||||||
AdditionalIncludeDirectories=""
|
|
||||||
PreprocessorDefinitions=""
|
|
||||||
BasicRuntimeChecks="3"
|
|
||||||
BrowseInformation="1"/>
|
|
||||||
</FileConfiguration>
|
|
||||||
</File>
|
|
||||||
<Filter
|
|
||||||
Name="bpm"
|
|
||||||
Filter="">
|
|
||||||
<File
|
|
||||||
RelativePath=".\BPMDetect.cpp">
|
|
||||||
</File>
|
|
||||||
<File
|
|
||||||
RelativePath=".\PeakFinder.cpp">
|
|
||||||
</File>
|
|
||||||
</Filter>
|
|
||||||
</Filter>
|
|
||||||
<Filter
|
|
||||||
Name="Header Files"
|
|
||||||
Filter="h;hpp;hxx;hm;inl">
|
|
||||||
<File
|
|
||||||
RelativePath="AAFilter.h">
|
|
||||||
</File>
|
|
||||||
<File
|
|
||||||
RelativePath="..\..\include\BPMDetect.h">
|
|
||||||
</File>
|
|
||||||
<File
|
|
||||||
RelativePath="cpu_detect.h">
|
|
||||||
</File>
|
|
||||||
<File
|
|
||||||
RelativePath="..\..\include\FIFOSampleBuffer.h">
|
|
||||||
</File>
|
|
||||||
<File
|
|
||||||
RelativePath="..\..\include\FIFOSamplePipe.h">
|
|
||||||
</File>
|
|
||||||
<File
|
|
||||||
RelativePath="FIRFilter.h">
|
|
||||||
</File>
|
|
||||||
<File
|
|
||||||
RelativePath=".\PeakFinder.h">
|
|
||||||
</File>
|
|
||||||
<File
|
|
||||||
RelativePath="RateTransposer.h">
|
|
||||||
</File>
|
|
||||||
<File
|
|
||||||
RelativePath="..\..\include\SoundTouch.h">
|
|
||||||
</File>
|
|
||||||
<File
|
|
||||||
RelativePath="..\..\include\STTypes.h">
|
|
||||||
</File>
|
|
||||||
<File
|
|
||||||
RelativePath="TDStretch.h">
|
|
||||||
</File>
|
|
||||||
</Filter>
|
|
||||||
</Files>
|
|
||||||
<Globals>
|
|
||||||
</Globals>
|
|
||||||
</VisualStudioProject>
|
|
||||||
342
source/SoundTouch/SoundTouch.vcxproj
Normal file
342
source/SoundTouch/SoundTouch.vcxproj
Normal file
@ -0,0 +1,342 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<ItemGroup Label="ProjectConfigurations">
|
||||||
|
<ProjectConfiguration Include="Debug|Win32">
|
||||||
|
<Configuration>Debug</Configuration>
|
||||||
|
<Platform>Win32</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
<ProjectConfiguration Include="Debug|x64">
|
||||||
|
<Configuration>Debug</Configuration>
|
||||||
|
<Platform>x64</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
<ProjectConfiguration Include="Release|Win32">
|
||||||
|
<Configuration>Release</Configuration>
|
||||||
|
<Platform>Win32</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
<ProjectConfiguration Include="Release|x64">
|
||||||
|
<Configuration>Release</Configuration>
|
||||||
|
<Platform>x64</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
</ItemGroup>
|
||||||
|
<PropertyGroup Label="Globals">
|
||||||
|
<ProjectGuid>{68A5DD20-7057-448B-8FE0-B6AC8D205509}</ProjectGuid>
|
||||||
|
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||||
|
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||||
|
<PlatformToolset>v142</PlatformToolset>
|
||||||
|
<UseOfMfc>false</UseOfMfc>
|
||||||
|
<CharacterSet>Unicode</CharacterSet>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||||
|
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||||
|
<PlatformToolset>v142</PlatformToolset>
|
||||||
|
<UseOfMfc>false</UseOfMfc>
|
||||||
|
<CharacterSet>Unicode</CharacterSet>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||||
|
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||||
|
<PlatformToolset>v142</PlatformToolset>
|
||||||
|
<UseOfMfc>false</UseOfMfc>
|
||||||
|
<CharacterSet>Unicode</CharacterSet>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||||
|
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||||
|
<PlatformToolset>v142</PlatformToolset>
|
||||||
|
<UseOfMfc>false</UseOfMfc>
|
||||||
|
<CharacterSet>Unicode</CharacterSet>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||||
|
<ImportGroup Label="ExtensionSettings">
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
|
||||||
|
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||||
|
<Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC71.props" />
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
|
||||||
|
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||||
|
<Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC71.props" />
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
|
||||||
|
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||||
|
<Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC71.props" />
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
|
||||||
|
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||||
|
<Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC71.props" />
|
||||||
|
</ImportGroup>
|
||||||
|
<PropertyGroup Label="UserMacros" />
|
||||||
|
<PropertyGroup>
|
||||||
|
<_ProjectFileVersion>14.0.23107.0</_ProjectFileVersion>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||||
|
<OutDir>$(Platform)\$(Configuration)\</OutDir>
|
||||||
|
<IntDir>$(Platform)\$(Configuration)\</IntDir>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||||
|
<OutDir>$(Platform)\$(Configuration)\</OutDir>
|
||||||
|
<IntDir>$(Platform)\$(Configuration)\</IntDir>
|
||||||
|
<TargetName>$(ProjectName)_x64</TargetName>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||||
|
<OutDir>$(Platform)\$(Configuration)\</OutDir>
|
||||||
|
<IntDir>$(Platform)\$(Configuration)\</IntDir>
|
||||||
|
<TargetName>$(ProjectName)D</TargetName>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||||
|
<OutDir>$(Platform)\$(Configuration)\</OutDir>
|
||||||
|
<IntDir>$(Platform)\$(Configuration)\</IntDir>
|
||||||
|
<TargetName>$(ProjectName)D_x64</TargetName>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||||
|
<ClCompile>
|
||||||
|
<Optimization>Full</Optimization>
|
||||||
|
<InlineFunctionExpansion>AnySuitable</InlineFunctionExpansion>
|
||||||
|
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||||
|
<AdditionalIncludeDirectories>..\..\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
|
<PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
|
<StringPooling>true</StringPooling>
|
||||||
|
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||||
|
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||||
|
<FloatingPointModel>Fast</FloatingPointModel>
|
||||||
|
<PrecompiledHeader />
|
||||||
|
<PrecompiledHeaderOutputFile>$(OutDir)$(TargetName).pch</PrecompiledHeaderOutputFile>
|
||||||
|
<AssemblerListingLocation>$(OutDir)</AssemblerListingLocation>
|
||||||
|
<ObjectFileName>$(OutDir)</ObjectFileName>
|
||||||
|
<ProgramDataBaseFileName>$(OutDir)</ProgramDataBaseFileName>
|
||||||
|
<WarningLevel>Level3</WarningLevel>
|
||||||
|
<SuppressStartupBanner>true</SuppressStartupBanner>
|
||||||
|
<DebugInformationFormat />
|
||||||
|
<CompileAs>Default</CompileAs>
|
||||||
|
<EnableEnhancedInstructionSet>StreamingSIMDExtensions2</EnableEnhancedInstructionSet>
|
||||||
|
<XMLDocumentationFileName>$(IntDir)</XMLDocumentationFileName>
|
||||||
|
<BrowseInformationFile>$(IntDir)</BrowseInformationFile>
|
||||||
|
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||||
|
</ClCompile>
|
||||||
|
<ResourceCompile>
|
||||||
|
<PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
|
<Culture>0x040b</Culture>
|
||||||
|
</ResourceCompile>
|
||||||
|
<Lib>
|
||||||
|
<OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
|
||||||
|
<SuppressStartupBanner>true</SuppressStartupBanner>
|
||||||
|
</Lib>
|
||||||
|
<PostBuildEvent>
|
||||||
|
<Command>if not exist ..\..\lib mkdir ..\..\lib
|
||||||
|
copy $(OutDir)$(TargetName)$(TargetExt) ..\..\lib</Command>
|
||||||
|
</PostBuildEvent>
|
||||||
|
</ItemDefinitionGroup>
|
||||||
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||||
|
<Midl>
|
||||||
|
<TargetEnvironment>X64</TargetEnvironment>
|
||||||
|
</Midl>
|
||||||
|
<ClCompile>
|
||||||
|
<Optimization>Full</Optimization>
|
||||||
|
<InlineFunctionExpansion>AnySuitable</InlineFunctionExpansion>
|
||||||
|
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||||
|
<AdditionalIncludeDirectories>..\..\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
|
<PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
|
<StringPooling>true</StringPooling>
|
||||||
|
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||||
|
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||||
|
<FloatingPointModel>Fast</FloatingPointModel>
|
||||||
|
<PrecompiledHeader />
|
||||||
|
<PrecompiledHeaderOutputFile>$(OutDir)$(TargetName).pch</PrecompiledHeaderOutputFile>
|
||||||
|
<AssemblerListingLocation>$(OutDir)</AssemblerListingLocation>
|
||||||
|
<ObjectFileName>$(OutDir)</ObjectFileName>
|
||||||
|
<ProgramDataBaseFileName>$(OutDir)</ProgramDataBaseFileName>
|
||||||
|
<WarningLevel>Level3</WarningLevel>
|
||||||
|
<SuppressStartupBanner>true</SuppressStartupBanner>
|
||||||
|
<DebugInformationFormat />
|
||||||
|
<CompileAs>Default</CompileAs>
|
||||||
|
<EnableEnhancedInstructionSet>
|
||||||
|
</EnableEnhancedInstructionSet>
|
||||||
|
<XMLDocumentationFileName>$(IntDir)</XMLDocumentationFileName>
|
||||||
|
<BrowseInformationFile>$(IntDir)</BrowseInformationFile>
|
||||||
|
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||||
|
</ClCompile>
|
||||||
|
<ResourceCompile>
|
||||||
|
<PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
|
<Culture>0x040b</Culture>
|
||||||
|
</ResourceCompile>
|
||||||
|
<Lib>
|
||||||
|
<OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
|
||||||
|
<SuppressStartupBanner>true</SuppressStartupBanner>
|
||||||
|
</Lib>
|
||||||
|
<PostBuildEvent>
|
||||||
|
<Command>if not exist ..\..\lib mkdir ..\..\lib
|
||||||
|
copy $(OutDir)$(TargetName)$(TargetExt) ..\..\lib</Command>
|
||||||
|
</PostBuildEvent>
|
||||||
|
</ItemDefinitionGroup>
|
||||||
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||||
|
<ClCompile>
|
||||||
|
<Optimization>Disabled</Optimization>
|
||||||
|
<AdditionalIncludeDirectories>..\..\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
|
<PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
|
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
|
||||||
|
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
|
||||||
|
<FloatingPointModel>Fast</FloatingPointModel>
|
||||||
|
<PrecompiledHeader />
|
||||||
|
<PrecompiledHeaderOutputFile>$(OutDir)$(TargetName).pch</PrecompiledHeaderOutputFile>
|
||||||
|
<AssemblerListingLocation>$(OutDir)</AssemblerListingLocation>
|
||||||
|
<ObjectFileName>$(OutDir)</ObjectFileName>
|
||||||
|
<ProgramDataBaseFileName>$(OutDir)</ProgramDataBaseFileName>
|
||||||
|
<BrowseInformation>true</BrowseInformation>
|
||||||
|
<WarningLevel>Level3</WarningLevel>
|
||||||
|
<SuppressStartupBanner>true</SuppressStartupBanner>
|
||||||
|
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||||
|
<CompileAs>Default</CompileAs>
|
||||||
|
<EnableEnhancedInstructionSet>StreamingSIMDExtensions2</EnableEnhancedInstructionSet>
|
||||||
|
<XMLDocumentationFileName>$(IntDir)</XMLDocumentationFileName>
|
||||||
|
<BrowseInformationFile>$(IntDir)</BrowseInformationFile>
|
||||||
|
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||||
|
</ClCompile>
|
||||||
|
<ResourceCompile>
|
||||||
|
<PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
|
<Culture>0x040b</Culture>
|
||||||
|
</ResourceCompile>
|
||||||
|
<Lib>
|
||||||
|
<OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
|
||||||
|
<SuppressStartupBanner>true</SuppressStartupBanner>
|
||||||
|
</Lib>
|
||||||
|
<PostBuildEvent>
|
||||||
|
<Command>if not exist ..\..\lib mkdir ..\..\lib
|
||||||
|
copy $(OutDir)$(TargetName)$(TargetExt) ..\..\lib</Command>
|
||||||
|
</PostBuildEvent>
|
||||||
|
</ItemDefinitionGroup>
|
||||||
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||||
|
<Midl>
|
||||||
|
<TargetEnvironment>X64</TargetEnvironment>
|
||||||
|
</Midl>
|
||||||
|
<ClCompile>
|
||||||
|
<Optimization>Disabled</Optimization>
|
||||||
|
<AdditionalIncludeDirectories>..\..\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
|
<PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
|
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
|
||||||
|
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
|
||||||
|
<FloatingPointModel>Fast</FloatingPointModel>
|
||||||
|
<PrecompiledHeader />
|
||||||
|
<PrecompiledHeaderOutputFile>$(OutDir)$(TargetName).pch</PrecompiledHeaderOutputFile>
|
||||||
|
<AssemblerListingLocation>$(OutDir)</AssemblerListingLocation>
|
||||||
|
<ObjectFileName>$(OutDir)</ObjectFileName>
|
||||||
|
<ProgramDataBaseFileName>$(OutDir)</ProgramDataBaseFileName>
|
||||||
|
<BrowseInformation>true</BrowseInformation>
|
||||||
|
<WarningLevel>Level3</WarningLevel>
|
||||||
|
<SuppressStartupBanner>true</SuppressStartupBanner>
|
||||||
|
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||||
|
<CompileAs>Default</CompileAs>
|
||||||
|
<EnableEnhancedInstructionSet>
|
||||||
|
</EnableEnhancedInstructionSet>
|
||||||
|
<XMLDocumentationFileName>$(IntDir)</XMLDocumentationFileName>
|
||||||
|
<BrowseInformationFile>$(IntDir)</BrowseInformationFile>
|
||||||
|
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||||
|
</ClCompile>
|
||||||
|
<ResourceCompile>
|
||||||
|
<PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
|
<Culture>0x040b</Culture>
|
||||||
|
</ResourceCompile>
|
||||||
|
<Lib>
|
||||||
|
<OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
|
||||||
|
<SuppressStartupBanner>true</SuppressStartupBanner>
|
||||||
|
</Lib>
|
||||||
|
<PostBuildEvent>
|
||||||
|
<Command>if not exist ..\..\lib mkdir ..\..\lib
|
||||||
|
copy $(OutDir)$(TargetName)$(TargetExt) ..\..\lib</Command>
|
||||||
|
</PostBuildEvent>
|
||||||
|
</ItemDefinitionGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ClCompile Include="AAFilter.cpp">
|
||||||
|
<Optimization Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Disabled</Optimization>
|
||||||
|
<BasicRuntimeChecks Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">EnableFastChecks</BasicRuntimeChecks>
|
||||||
|
<BrowseInformation Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</BrowseInformation>
|
||||||
|
<Optimization Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Disabled</Optimization>
|
||||||
|
<BasicRuntimeChecks Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">EnableFastChecks</BasicRuntimeChecks>
|
||||||
|
<BrowseInformation Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</BrowseInformation>
|
||||||
|
<Optimization Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">MaxSpeed</Optimization>
|
||||||
|
<Optimization Condition="'$(Configuration)|$(Platform)'=='Release|x64'">MaxSpeed</Optimization>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="BPMDetect.cpp">
|
||||||
|
<DisableSpecificWarnings Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">4996</DisableSpecificWarnings>
|
||||||
|
<DisableSpecificWarnings Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">4996</DisableSpecificWarnings>
|
||||||
|
<DisableSpecificWarnings Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">4996</DisableSpecificWarnings>
|
||||||
|
<DisableSpecificWarnings Condition="'$(Configuration)|$(Platform)'=='Release|x64'">4996</DisableSpecificWarnings>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="cpu_detect_x86.cpp" />
|
||||||
|
<ClCompile Include="FIFOSampleBuffer.cpp">
|
||||||
|
<Optimization Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Disabled</Optimization>
|
||||||
|
<BasicRuntimeChecks Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">EnableFastChecks</BasicRuntimeChecks>
|
||||||
|
<BrowseInformation Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</BrowseInformation>
|
||||||
|
<Optimization Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Disabled</Optimization>
|
||||||
|
<BasicRuntimeChecks Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">EnableFastChecks</BasicRuntimeChecks>
|
||||||
|
<BrowseInformation Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</BrowseInformation>
|
||||||
|
<Optimization Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">MaxSpeed</Optimization>
|
||||||
|
<Optimization Condition="'$(Configuration)|$(Platform)'=='Release|x64'">MaxSpeed</Optimization>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="FIRFilter.cpp">
|
||||||
|
<Optimization Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Disabled</Optimization>
|
||||||
|
<BasicRuntimeChecks Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">EnableFastChecks</BasicRuntimeChecks>
|
||||||
|
<BrowseInformation Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</BrowseInformation>
|
||||||
|
<Optimization Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Disabled</Optimization>
|
||||||
|
<BasicRuntimeChecks Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">EnableFastChecks</BasicRuntimeChecks>
|
||||||
|
<BrowseInformation Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</BrowseInformation>
|
||||||
|
<Optimization Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">MaxSpeed</Optimization>
|
||||||
|
<Optimization Condition="'$(Configuration)|$(Platform)'=='Release|x64'">MaxSpeed</Optimization>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="InterpolateCubic.cpp" />
|
||||||
|
<ClCompile Include="InterpolateLinear.cpp" />
|
||||||
|
<ClCompile Include="InterpolateShannon.cpp" />
|
||||||
|
<ClCompile Include="mmx_optimized.cpp" />
|
||||||
|
<ClCompile Include="PeakFinder.cpp" />
|
||||||
|
<ClCompile Include="RateTransposer.cpp">
|
||||||
|
<Optimization Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Disabled</Optimization>
|
||||||
|
<BasicRuntimeChecks Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">EnableFastChecks</BasicRuntimeChecks>
|
||||||
|
<BrowseInformation Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</BrowseInformation>
|
||||||
|
<Optimization Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Disabled</Optimization>
|
||||||
|
<BasicRuntimeChecks Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">EnableFastChecks</BasicRuntimeChecks>
|
||||||
|
<BrowseInformation Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</BrowseInformation>
|
||||||
|
<Optimization Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">MaxSpeed</Optimization>
|
||||||
|
<Optimization Condition="'$(Configuration)|$(Platform)'=='Release|x64'">MaxSpeed</Optimization>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="SoundTouch.cpp">
|
||||||
|
<Optimization Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Disabled</Optimization>
|
||||||
|
<BasicRuntimeChecks Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">EnableFastChecks</BasicRuntimeChecks>
|
||||||
|
<BrowseInformation Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</BrowseInformation>
|
||||||
|
<Optimization Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Disabled</Optimization>
|
||||||
|
<BasicRuntimeChecks Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">EnableFastChecks</BasicRuntimeChecks>
|
||||||
|
<BrowseInformation Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</BrowseInformation>
|
||||||
|
<Optimization Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">MaxSpeed</Optimization>
|
||||||
|
<Optimization Condition="'$(Configuration)|$(Platform)'=='Release|x64'">MaxSpeed</Optimization>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="sse_optimized.cpp" />
|
||||||
|
<ClCompile Include="TDStretch.cpp">
|
||||||
|
<Optimization Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Disabled</Optimization>
|
||||||
|
<BasicRuntimeChecks Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">EnableFastChecks</BasicRuntimeChecks>
|
||||||
|
<BrowseInformation Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</BrowseInformation>
|
||||||
|
<Optimization Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Disabled</Optimization>
|
||||||
|
<BasicRuntimeChecks Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">EnableFastChecks</BasicRuntimeChecks>
|
||||||
|
<BrowseInformation Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</BrowseInformation>
|
||||||
|
<Optimization Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">MaxSpeed</Optimization>
|
||||||
|
<Optimization Condition="'$(Configuration)|$(Platform)'=='Release|x64'">MaxSpeed</Optimization>
|
||||||
|
</ClCompile>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ClInclude Include="..\..\include\BPMDetect.h" />
|
||||||
|
<ClInclude Include="..\..\include\FIFOSampleBuffer.h" />
|
||||||
|
<ClInclude Include="..\..\include\FIFOSamplePipe.h" />
|
||||||
|
<ClInclude Include="..\..\include\SoundTouch.h" />
|
||||||
|
<ClInclude Include="..\..\include\STTypes.h" />
|
||||||
|
<ClInclude Include="AAFilter.h" />
|
||||||
|
<ClInclude Include="cpu_detect.h" />
|
||||||
|
<ClInclude Include="FIRFilter.h" />
|
||||||
|
<ClInclude Include="InterpolateCubic.h" />
|
||||||
|
<ClInclude Include="InterpolateLinear.h" />
|
||||||
|
<ClInclude Include="InterpolateShannon.h" />
|
||||||
|
<ClInclude Include="PeakFinder.h" />
|
||||||
|
<ClInclude Include="RateTransposer.h" />
|
||||||
|
<ClInclude Include="TDStretch.h" />
|
||||||
|
</ItemGroup>
|
||||||
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||||
|
<ImportGroup Label="ExtensionTargets">
|
||||||
|
</ImportGroup>
|
||||||
|
</Project>
|
||||||
@ -1,11 +1,17 @@
|
|||||||
////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
///
|
///
|
||||||
/// Sampled sound tempo changer/time stretch algorithm. Changes the sound tempo
|
/// Sampled sound tempo changer/time stretch algorithm. Changes the sound tempo
|
||||||
/// while maintaining the original pitch by using a time domain WSOLA-like
|
/// while maintaining the original pitch by using a time domain WSOLA-like
|
||||||
/// method with several performance-increasing tweaks.
|
/// method with several performance-increasing tweaks.
|
||||||
///
|
///
|
||||||
/// Note : MMX optimized functions reside in a separate, platform-specific
|
/// Notes : MMX optimized functions reside in a separate, platform-specific
|
||||||
/// file, e.g. 'mmx_win.cpp' or 'mmx_gcc.cpp'
|
/// file, e.g. 'mmx_win.cpp' or 'mmx_gcc.cpp'.
|
||||||
|
///
|
||||||
|
/// This source file contains OpenMP optimizations that allow speeding up the
|
||||||
|
/// corss-correlation algorithm by executing it in several threads / CPU cores
|
||||||
|
/// in parallel. See the following article link for more detailed discussion
|
||||||
|
/// about SoundTouch OpenMP optimizations:
|
||||||
|
/// http://www.softwarecoven.com/parallel-computing-in-embedded-mobile-devices
|
||||||
///
|
///
|
||||||
/// Author : Copyright (c) Olli Parviainen
|
/// Author : Copyright (c) Olli Parviainen
|
||||||
/// Author e-mail : oparviai 'at' iki.fi
|
/// Author e-mail : oparviai 'at' iki.fi
|
||||||
@ -13,13 +19,6 @@
|
|||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Last changed : $Date$
|
|
||||||
// File revision : $Revision: 1.12 $
|
|
||||||
//
|
|
||||||
// $Id$
|
|
||||||
//
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// License :
|
// License :
|
||||||
//
|
//
|
||||||
// SoundTouch audio processing library
|
// SoundTouch audio processing library
|
||||||
@ -51,32 +50,10 @@
|
|||||||
#include "cpu_detect.h"
|
#include "cpu_detect.h"
|
||||||
#include "TDStretch.h"
|
#include "TDStretch.h"
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
using namespace soundtouch;
|
using namespace soundtouch;
|
||||||
|
|
||||||
#define max(x, y) (((x) > (y)) ? (x) : (y))
|
#define max(x, y) (((x) > (y)) ? (x) : (y))
|
||||||
|
|
||||||
|
|
||||||
/*****************************************************************************
|
|
||||||
*
|
|
||||||
* Constant definitions
|
|
||||||
*
|
|
||||||
*****************************************************************************/
|
|
||||||
|
|
||||||
// Table for the hierarchical mixing position seeking algorithm
|
|
||||||
static const short _scanOffsets[5][24]={
|
|
||||||
{ 124, 186, 248, 310, 372, 434, 496, 558, 620, 682, 744, 806,
|
|
||||||
868, 930, 992, 1054, 1116, 1178, 1240, 1302, 1364, 1426, 1488, 0},
|
|
||||||
{-100, -75, -50, -25, 25, 50, 75, 100, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
|
||||||
{ -20, -15, -10, -5, 5, 10, 15, 20, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
|
||||||
{ -4, -3, -2, -1, 1, 2, 3, 4, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
|
||||||
{ 121, 114, 97, 114, 98, 105, 108, 32, 104, 99, 117, 111,
|
|
||||||
116, 100, 110, 117, 111, 115, 0, 0, 0, 0, 0, 0}};
|
|
||||||
|
|
||||||
/*****************************************************************************
|
/*****************************************************************************
|
||||||
*
|
*
|
||||||
* Implementation of the class 'TDStretch'
|
* Implementation of the class 'TDStretch'
|
||||||
@ -86,18 +63,15 @@ static const short _scanOffsets[5][24]={
|
|||||||
|
|
||||||
TDStretch::TDStretch() : FIFOProcessor(&outputBuffer)
|
TDStretch::TDStretch() : FIFOProcessor(&outputBuffer)
|
||||||
{
|
{
|
||||||
bQuickSeek = FALSE;
|
bQuickSeek = false;
|
||||||
channels = 2;
|
channels = 2;
|
||||||
|
|
||||||
pMidBuffer = NULL;
|
pMidBuffer = nullptr;
|
||||||
pMidBufferUnaligned = NULL;
|
pMidBufferUnaligned = nullptr;
|
||||||
overlapLength = 0;
|
overlapLength = 0;
|
||||||
|
|
||||||
bAutoSeqSetting = TRUE;
|
bAutoSeqSetting = true;
|
||||||
bAutoSeekSetting = TRUE;
|
bAutoSeekSetting = true;
|
||||||
|
|
||||||
// outDebt = 0;
|
|
||||||
skipFract = 0;
|
|
||||||
|
|
||||||
tempo = 1.0f;
|
tempo = 1.0f;
|
||||||
setParameters(44100, DEFAULT_SEQUENCE_MS, DEFAULT_SEEKWINDOW_MS, DEFAULT_OVERLAP_MS);
|
setParameters(44100, DEFAULT_SEQUENCE_MS, DEFAULT_SEEKWINDOW_MS, DEFAULT_OVERLAP_MS);
|
||||||
@ -128,29 +102,34 @@ void TDStretch::setParameters(int aSampleRate, int aSequenceMS,
|
|||||||
int aSeekWindowMS, int aOverlapMS)
|
int aSeekWindowMS, int aOverlapMS)
|
||||||
{
|
{
|
||||||
// accept only positive parameter values - if zero or negative, use old values instead
|
// accept only positive parameter values - if zero or negative, use old values instead
|
||||||
if (aSampleRate > 0) this->sampleRate = aSampleRate;
|
if (aSampleRate > 0)
|
||||||
|
{
|
||||||
|
if (aSampleRate > 192000) ST_THROW_RT_ERROR("Error: Excessive samplerate");
|
||||||
|
this->sampleRate = aSampleRate;
|
||||||
|
}
|
||||||
|
|
||||||
if (aOverlapMS > 0) this->overlapMs = aOverlapMS;
|
if (aOverlapMS > 0) this->overlapMs = aOverlapMS;
|
||||||
|
|
||||||
if (aSequenceMS > 0)
|
if (aSequenceMS > 0)
|
||||||
{
|
{
|
||||||
this->sequenceMs = aSequenceMS;
|
this->sequenceMs = aSequenceMS;
|
||||||
bAutoSeqSetting = FALSE;
|
bAutoSeqSetting = false;
|
||||||
}
|
}
|
||||||
else if (aSequenceMS == 0)
|
else if (aSequenceMS == 0)
|
||||||
{
|
{
|
||||||
// if zero, use automatic setting
|
// if zero, use automatic setting
|
||||||
bAutoSeqSetting = TRUE;
|
bAutoSeqSetting = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (aSeekWindowMS > 0)
|
if (aSeekWindowMS > 0)
|
||||||
{
|
{
|
||||||
this->seekWindowMs = aSeekWindowMS;
|
this->seekWindowMs = aSeekWindowMS;
|
||||||
bAutoSeekSetting = FALSE;
|
bAutoSeekSetting = false;
|
||||||
}
|
}
|
||||||
else if (aSeekWindowMS == 0)
|
else if (aSeekWindowMS == 0)
|
||||||
{
|
{
|
||||||
// if zero, use automatic setting
|
// if zero, use automatic setting
|
||||||
bAutoSeekSetting = TRUE;
|
bAutoSeekSetting = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
calcSeqParameters();
|
calcSeqParameters();
|
||||||
@ -159,13 +138,12 @@ void TDStretch::setParameters(int aSampleRate, int aSequenceMS,
|
|||||||
|
|
||||||
// set tempo to recalculate 'sampleReq'
|
// set tempo to recalculate 'sampleReq'
|
||||||
setTempo(tempo);
|
setTempo(tempo);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// Get routine control parameters, see setParameters() function.
|
/// Get routine control parameters, see setParameters() function.
|
||||||
/// Any of the parameters to this function can be NULL, in such case corresponding parameter
|
/// Any of the parameters to this function can be nullptr, in such case corresponding parameter
|
||||||
/// value isn't returned.
|
/// value isn't returned.
|
||||||
void TDStretch::getParameters(int *pSampleRate, int *pSequenceMs, int *pSeekWindowMs, int *pOverlapMs) const
|
void TDStretch::getParameters(int *pSampleRate, int *pSequenceMs, int *pSeekWindowMs, int *pOverlapMs) const
|
||||||
{
|
{
|
||||||
@ -212,7 +190,7 @@ void TDStretch::overlapMono(SAMPLETYPE *pOutput, const SAMPLETYPE *pInput) const
|
|||||||
|
|
||||||
void TDStretch::clearMidBuffer()
|
void TDStretch::clearMidBuffer()
|
||||||
{
|
{
|
||||||
memset(pMidBuffer, 0, 2 * sizeof(SAMPLETYPE) * overlapLength);
|
memset(pMidBuffer, 0, channels * sizeof(SAMPLETYPE) * overlapLength);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -220,6 +198,10 @@ void TDStretch::clearInput()
|
|||||||
{
|
{
|
||||||
inputBuffer.clear();
|
inputBuffer.clear();
|
||||||
clearMidBuffer();
|
clearMidBuffer();
|
||||||
|
isBeginning = true;
|
||||||
|
maxnorm = 0;
|
||||||
|
maxnormf = 1e8;
|
||||||
|
skipFract = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -234,14 +216,14 @@ void TDStretch::clear()
|
|||||||
|
|
||||||
// Enables/disables the quick position seeking algorithm. Zero to disable, nonzero
|
// Enables/disables the quick position seeking algorithm. Zero to disable, nonzero
|
||||||
// to enable
|
// to enable
|
||||||
void TDStretch::enableQuickSeek(BOOL enable)
|
void TDStretch::enableQuickSeek(bool enable)
|
||||||
{
|
{
|
||||||
bQuickSeek = enable;
|
bQuickSeek = enable;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Returns nonzero if the quick seeking algorithm is enabled.
|
// Returns nonzero if the quick seeking algorithm is enabled.
|
||||||
BOOL TDStretch::isQuickSeekEnabled() const
|
bool TDStretch::isQuickSeekEnabled() const
|
||||||
{
|
{
|
||||||
return bQuickSeek;
|
return bQuickSeek;
|
||||||
}
|
}
|
||||||
@ -265,16 +247,24 @@ int TDStretch::seekBestOverlapPosition(const SAMPLETYPE *refPos)
|
|||||||
// of 'ovlPos'.
|
// of 'ovlPos'.
|
||||||
inline void TDStretch::overlap(SAMPLETYPE *pOutput, const SAMPLETYPE *pInput, uint ovlPos) const
|
inline void TDStretch::overlap(SAMPLETYPE *pOutput, const SAMPLETYPE *pInput, uint ovlPos) const
|
||||||
{
|
{
|
||||||
if (channels == 2)
|
#ifndef USE_MULTICH_ALWAYS
|
||||||
|
if (channels == 1)
|
||||||
{
|
{
|
||||||
// stereo sound
|
|
||||||
overlapStereo(pOutput, pInput + 2 * ovlPos);
|
|
||||||
} else {
|
|
||||||
// mono sound.
|
// mono sound.
|
||||||
overlapMono(pOutput, pInput + ovlPos);
|
overlapMono(pOutput, pInput + ovlPos);
|
||||||
}
|
}
|
||||||
|
else if (channels == 2)
|
||||||
|
{
|
||||||
|
// stereo sound
|
||||||
|
overlapStereo(pOutput, pInput + 2 * ovlPos);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
#endif // USE_MULTICH_ALWAYS
|
||||||
|
{
|
||||||
|
assert(channels > 0);
|
||||||
|
overlapMulti(pOutput, pInput + channels * ovlPos);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Seeks for the optimal overlap-mixing position. The 'stereo' version of the
|
// Seeks for the optimal overlap-mixing position. The 'stereo' version of the
|
||||||
@ -286,23 +276,144 @@ inline void TDStretch::overlap(SAMPLETYPE *pOutput, const SAMPLETYPE *pInput, ui
|
|||||||
int TDStretch::seekBestOverlapPositionFull(const SAMPLETYPE *refPos)
|
int TDStretch::seekBestOverlapPositionFull(const SAMPLETYPE *refPos)
|
||||||
{
|
{
|
||||||
int bestOffs;
|
int bestOffs;
|
||||||
double bestCorr, corr;
|
double bestCorr;
|
||||||
int i;
|
int i;
|
||||||
|
double norm;
|
||||||
|
|
||||||
bestCorr = FLT_MIN;
|
bestCorr = -FLT_MAX;
|
||||||
bestOffs = 0;
|
bestOffs = 0;
|
||||||
|
|
||||||
// Scans for the best correlation value by testing each possible position
|
// Scans for the best correlation value by testing each possible position
|
||||||
// over the permitted range.
|
// over the permitted range.
|
||||||
for (i = 0; i < seekLength; i ++)
|
bestCorr = calcCrossCorr(refPos, pMidBuffer, norm);
|
||||||
|
bestCorr = (bestCorr + 0.1) * 0.75;
|
||||||
|
|
||||||
|
#pragma omp parallel for
|
||||||
|
for (i = 1; i < seekLength; i ++)
|
||||||
{
|
{
|
||||||
// Calculates correlation value for the mixing position corresponding
|
double corr;
|
||||||
// to 'i'
|
// Calculates correlation value for the mixing position corresponding to 'i'
|
||||||
corr = calcCrossCorr(refPos + channels * i, pMidBuffer);
|
#if defined(_OPENMP) || defined(ST_SIMD_AVOID_UNALIGNED)
|
||||||
|
// in parallel OpenMP mode, can't use norm accumulator version as parallel executor won't
|
||||||
|
// iterate the loop in sequential order
|
||||||
|
// in SIMD mode, avoid accumulator version to allow avoiding unaligned positions
|
||||||
|
corr = calcCrossCorr(refPos + channels * i, pMidBuffer, norm);
|
||||||
|
#else
|
||||||
|
// In non-parallel version call "calcCrossCorrAccumulate" that is otherwise same
|
||||||
|
// as "calcCrossCorr", but saves time by reusing & updating previously stored
|
||||||
|
// "norm" value
|
||||||
|
corr = calcCrossCorrAccumulate(refPos + channels * i, pMidBuffer, norm);
|
||||||
|
#endif
|
||||||
// heuristic rule to slightly favour values close to mid of the range
|
// heuristic rule to slightly favour values close to mid of the range
|
||||||
double tmp = (double)(2 * i - seekLength) / (double)seekLength;
|
double tmp = (double)(2 * i - seekLength) / (double)seekLength;
|
||||||
corr = ((corr + 0.1) * (1.0 - 0.25 * tmp * tmp));
|
corr = ((corr + 0.1) * (1.0 - 0.25 * tmp * tmp));
|
||||||
|
|
||||||
|
// Checks for the highest correlation value
|
||||||
|
if (corr > bestCorr)
|
||||||
|
{
|
||||||
|
// For optimal performance, enter critical section only in case that best value found.
|
||||||
|
// in such case repeat 'if' condition as it's possible that parallel execution may have
|
||||||
|
// updated the bestCorr value in the mean time
|
||||||
|
#pragma omp critical
|
||||||
|
if (corr > bestCorr)
|
||||||
|
{
|
||||||
|
bestCorr = corr;
|
||||||
|
bestOffs = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef SOUNDTOUCH_INTEGER_SAMPLES
|
||||||
|
adaptNormalizer();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// clear cross correlation routine state if necessary (is so e.g. in MMX routines).
|
||||||
|
clearCrossCorrState();
|
||||||
|
|
||||||
|
return bestOffs;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Quick seek algorithm for improved runtime-performance: First roughly scans through the
|
||||||
|
// correlation area, and then scan surroundings of two best preliminary correlation candidates
|
||||||
|
// with improved precision
|
||||||
|
//
|
||||||
|
// Based on testing:
|
||||||
|
// - This algorithm gives on average 99% as good match as the full algorithm
|
||||||
|
// - this quick seek algorithm finds the best match on ~90% of cases
|
||||||
|
// - on those 10% of cases when this algorithm doesn't find best match,
|
||||||
|
// it still finds on average ~90% match vs. the best possible match
|
||||||
|
int TDStretch::seekBestOverlapPositionQuick(const SAMPLETYPE *refPos)
|
||||||
|
{
|
||||||
|
#define _MIN(a, b) (((a) < (b)) ? (a) : (b))
|
||||||
|
#define SCANSTEP 16
|
||||||
|
#define SCANWIND 8
|
||||||
|
|
||||||
|
int bestOffs;
|
||||||
|
int i;
|
||||||
|
int bestOffs2;
|
||||||
|
float bestCorr, corr;
|
||||||
|
float bestCorr2;
|
||||||
|
double norm;
|
||||||
|
|
||||||
|
// note: 'float' types used in this function in case that the platform would need to use software-fp
|
||||||
|
|
||||||
|
bestCorr =
|
||||||
|
bestCorr2 = -FLT_MAX;
|
||||||
|
bestOffs =
|
||||||
|
bestOffs2 = SCANWIND;
|
||||||
|
|
||||||
|
// Scans for the best correlation value by testing each possible position
|
||||||
|
// over the permitted range. Look for two best matches on the first pass to
|
||||||
|
// increase possibility of ideal match.
|
||||||
|
//
|
||||||
|
// Begin from "SCANSTEP" instead of SCANWIND to make the calculation
|
||||||
|
// catch the 'middlepoint' of seekLength vector as that's the a-priori
|
||||||
|
// expected best match position
|
||||||
|
//
|
||||||
|
// Roughly:
|
||||||
|
// - 15% of cases find best result directly on the first round,
|
||||||
|
// - 75% cases find better match on 2nd round around the best match from 1st round
|
||||||
|
// - 10% cases find better match on 2nd round around the 2nd-best-match from 1st round
|
||||||
|
for (i = SCANSTEP; i < seekLength - SCANWIND - 1; i += SCANSTEP)
|
||||||
|
{
|
||||||
|
// Calculates correlation value for the mixing position corresponding
|
||||||
|
// to 'i'
|
||||||
|
corr = (float)calcCrossCorr(refPos + channels*i, pMidBuffer, norm);
|
||||||
|
// heuristic rule to slightly favour values close to mid of the seek range
|
||||||
|
float tmp = (float)(2 * i - seekLength - 1) / (float)seekLength;
|
||||||
|
corr = ((corr + 0.1f) * (1.0f - 0.25f * tmp * tmp));
|
||||||
|
|
||||||
|
// Checks for the highest correlation value
|
||||||
|
if (corr > bestCorr)
|
||||||
|
{
|
||||||
|
// found new best match. keep the previous best as 2nd best match
|
||||||
|
bestCorr2 = bestCorr;
|
||||||
|
bestOffs2 = bestOffs;
|
||||||
|
bestCorr = corr;
|
||||||
|
bestOffs = i;
|
||||||
|
}
|
||||||
|
else if (corr > bestCorr2)
|
||||||
|
{
|
||||||
|
// not new best, but still new 2nd best match
|
||||||
|
bestCorr2 = corr;
|
||||||
|
bestOffs2 = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scans surroundings of the found best match with small stepping
|
||||||
|
int end = _MIN(bestOffs + SCANWIND + 1, seekLength);
|
||||||
|
for (i = bestOffs - SCANWIND; i < end; i++)
|
||||||
|
{
|
||||||
|
if (i == bestOffs) continue; // this offset already calculated, thus skip
|
||||||
|
|
||||||
|
// Calculates correlation value for the mixing position corresponding
|
||||||
|
// to 'i'
|
||||||
|
corr = (float)calcCrossCorr(refPos + channels*i, pMidBuffer, norm);
|
||||||
|
// heuristic rule to slightly favour values close to mid of the range
|
||||||
|
float tmp = (float)(2 * i - seekLength - 1) / (float)seekLength;
|
||||||
|
corr = ((corr + 0.1f) * (1.0f - 0.25f * tmp * tmp));
|
||||||
|
|
||||||
// Checks for the highest correlation value
|
// Checks for the highest correlation value
|
||||||
if (corr > bestCorr)
|
if (corr > bestCorr)
|
||||||
{
|
{
|
||||||
@ -310,70 +421,70 @@ int TDStretch::seekBestOverlapPositionFull(const SAMPLETYPE *refPos)
|
|||||||
bestOffs = i;
|
bestOffs = i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// clear cross correlation routine state if necessary (is so e.g. in MMX routines).
|
|
||||||
clearCrossCorrState();
|
|
||||||
|
|
||||||
return bestOffs;
|
// Scans surroundings of the 2nd best match with small stepping
|
||||||
}
|
end = _MIN(bestOffs2 + SCANWIND + 1, seekLength);
|
||||||
|
for (i = bestOffs2 - SCANWIND; i < end; i++)
|
||||||
|
|
||||||
// Seeks for the optimal overlap-mixing position. The 'stereo' version of the
|
|
||||||
// routine
|
|
||||||
//
|
|
||||||
// The best position is determined as the position where the two overlapped
|
|
||||||
// sample sequences are 'most alike', in terms of the highest cross-correlation
|
|
||||||
// value over the overlapping period
|
|
||||||
int TDStretch::seekBestOverlapPositionQuick(const SAMPLETYPE *refPos)
|
|
||||||
{
|
{
|
||||||
int j;
|
if (i == bestOffs2) continue; // this offset already calculated, thus skip
|
||||||
int bestOffs;
|
|
||||||
double bestCorr, corr;
|
|
||||||
int scanCount, corrOffset, tempOffset;
|
|
||||||
|
|
||||||
bestCorr = FLT_MIN;
|
|
||||||
bestOffs = _scanOffsets[0][0];
|
|
||||||
corrOffset = 0;
|
|
||||||
tempOffset = 0;
|
|
||||||
|
|
||||||
// Scans for the best correlation value using four-pass hierarchical search.
|
|
||||||
//
|
|
||||||
// The look-up table 'scans' has hierarchical position adjusting steps.
|
|
||||||
// In first pass the routine searhes for the highest correlation with
|
|
||||||
// relatively coarse steps, then rescans the neighbourhood of the highest
|
|
||||||
// correlation with better resolution and so on.
|
|
||||||
for (scanCount = 0;scanCount < 4; scanCount ++)
|
|
||||||
{
|
|
||||||
j = 0;
|
|
||||||
while (_scanOffsets[scanCount][j])
|
|
||||||
{
|
|
||||||
tempOffset = corrOffset + _scanOffsets[scanCount][j];
|
|
||||||
if (tempOffset >= seekLength) break;
|
|
||||||
|
|
||||||
// Calculates correlation value for the mixing position corresponding
|
// Calculates correlation value for the mixing position corresponding
|
||||||
// to 'tempOffset'
|
// to 'i'
|
||||||
corr = (double)calcCrossCorr(refPos + channels * tempOffset, pMidBuffer);
|
corr = (float)calcCrossCorr(refPos + channels*i, pMidBuffer, norm);
|
||||||
// heuristic rule to slightly favour values close to mid of the range
|
// heuristic rule to slightly favour values close to mid of the range
|
||||||
double tmp = (double)(2 * tempOffset - seekLength) / seekLength;
|
float tmp = (float)(2 * i - seekLength - 1) / (float)seekLength;
|
||||||
corr = ((corr + 0.1) * (1.0 - 0.25 * tmp * tmp));
|
corr = ((corr + 0.1f) * (1.0f - 0.25f * tmp * tmp));
|
||||||
|
|
||||||
// Checks for the highest correlation value
|
// Checks for the highest correlation value
|
||||||
if (corr > bestCorr)
|
if (corr > bestCorr)
|
||||||
{
|
{
|
||||||
bestCorr = corr;
|
bestCorr = corr;
|
||||||
bestOffs = tempOffset;
|
bestOffs = i;
|
||||||
}
|
}
|
||||||
j ++;
|
|
||||||
}
|
|
||||||
corrOffset = bestOffs;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// clear cross correlation routine state if necessary (is so e.g. in MMX routines).
|
// clear cross correlation routine state if necessary (is so e.g. in MMX routines).
|
||||||
clearCrossCorrState();
|
clearCrossCorrState();
|
||||||
|
|
||||||
|
#ifdef SOUNDTOUCH_INTEGER_SAMPLES
|
||||||
|
adaptNormalizer();
|
||||||
|
#endif
|
||||||
|
|
||||||
return bestOffs;
|
return bestOffs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/// For integer algorithm: adapt normalization factor divider with music so that
|
||||||
|
/// it'll not be pessimistically restrictive that can degrade quality on quieter sections
|
||||||
|
/// yet won't cause integer overflows either
|
||||||
|
void TDStretch::adaptNormalizer()
|
||||||
|
{
|
||||||
|
// Do not adapt normalizer over too silent sequences to avoid averaging filter depleting to
|
||||||
|
// too low values during pauses in music
|
||||||
|
if ((maxnorm > 1000) || (maxnormf > 40000000))
|
||||||
|
{
|
||||||
|
//norm averaging filter
|
||||||
|
maxnormf = 0.9f * maxnormf + 0.1f * (float)maxnorm;
|
||||||
|
|
||||||
|
if ((maxnorm > 800000000) && (overlapDividerBitsNorm < 16))
|
||||||
|
{
|
||||||
|
// large values, so increase divider
|
||||||
|
overlapDividerBitsNorm++;
|
||||||
|
if (maxnorm > 1600000000) overlapDividerBitsNorm++; // extra large value => extra increase
|
||||||
|
}
|
||||||
|
else if ((maxnormf < 1000000) && (overlapDividerBitsNorm > 0))
|
||||||
|
{
|
||||||
|
// extra small values, decrease divider
|
||||||
|
overlapDividerBitsNorm--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
maxnorm = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/// clear cross correlation routine state if necessary
|
/// clear cross correlation routine state if necessary
|
||||||
void TDStretch::clearCrossCorrState()
|
void TDStretch::clearCrossCorrState()
|
||||||
{
|
{
|
||||||
@ -385,18 +496,18 @@ void TDStretch::clearCrossCorrState()
|
|||||||
void TDStretch::calcSeqParameters()
|
void TDStretch::calcSeqParameters()
|
||||||
{
|
{
|
||||||
// Adjust tempo param according to tempo, so that variating processing sequence length is used
|
// Adjust tempo param according to tempo, so that variating processing sequence length is used
|
||||||
// at varius tempo settings, between the given low...top limits
|
// at various tempo settings, between the given low...top limits
|
||||||
#define AUTOSEQ_TEMPO_LOW 0.5 // auto setting low tempo range (-50%)
|
#define AUTOSEQ_TEMPO_LOW 0.5 // auto setting low tempo range (-50%)
|
||||||
#define AUTOSEQ_TEMPO_TOP 2.0 // auto setting top tempo range (+100%)
|
#define AUTOSEQ_TEMPO_TOP 2.0 // auto setting top tempo range (+100%)
|
||||||
|
|
||||||
// sequence-ms setting values at above low & top tempo
|
// sequence-ms setting values at above low & top tempo
|
||||||
#define AUTOSEQ_AT_MIN 125.0
|
#define AUTOSEQ_AT_MIN 90.0
|
||||||
#define AUTOSEQ_AT_MAX 50.0
|
#define AUTOSEQ_AT_MAX 40.0
|
||||||
#define AUTOSEQ_K ((AUTOSEQ_AT_MAX - AUTOSEQ_AT_MIN) / (AUTOSEQ_TEMPO_TOP - AUTOSEQ_TEMPO_LOW))
|
#define AUTOSEQ_K ((AUTOSEQ_AT_MAX - AUTOSEQ_AT_MIN) / (AUTOSEQ_TEMPO_TOP - AUTOSEQ_TEMPO_LOW))
|
||||||
#define AUTOSEQ_C (AUTOSEQ_AT_MIN - (AUTOSEQ_K) * (AUTOSEQ_TEMPO_LOW))
|
#define AUTOSEQ_C (AUTOSEQ_AT_MIN - (AUTOSEQ_K) * (AUTOSEQ_TEMPO_LOW))
|
||||||
|
|
||||||
// seek-window-ms setting values at above low & top tempo
|
// seek-window-ms setting values at above low & top tempoq
|
||||||
#define AUTOSEEK_AT_MIN 25.0
|
#define AUTOSEEK_AT_MIN 20.0
|
||||||
#define AUTOSEEK_AT_MAX 15.0
|
#define AUTOSEEK_AT_MAX 15.0
|
||||||
#define AUTOSEEK_K ((AUTOSEEK_AT_MAX - AUTOSEEK_AT_MIN) / (AUTOSEQ_TEMPO_TOP - AUTOSEQ_TEMPO_LOW))
|
#define AUTOSEEK_K ((AUTOSEEK_AT_MAX - AUTOSEEK_AT_MIN) / (AUTOSEQ_TEMPO_TOP - AUTOSEQ_TEMPO_LOW))
|
||||||
#define AUTOSEEK_C (AUTOSEEK_AT_MIN - (AUTOSEEK_K) * (AUTOSEQ_TEMPO_LOW))
|
#define AUTOSEEK_C (AUTOSEEK_AT_MIN - (AUTOSEEK_K) * (AUTOSEQ_TEMPO_LOW))
|
||||||
@ -432,7 +543,7 @@ void TDStretch::calcSeqParameters()
|
|||||||
|
|
||||||
// Sets new target tempo. Normal tempo = 'SCALE', smaller values represent slower
|
// Sets new target tempo. Normal tempo = 'SCALE', smaller values represent slower
|
||||||
// tempo, larger faster tempo.
|
// tempo, larger faster tempo.
|
||||||
void TDStretch::setTempo(float newTempo)
|
void TDStretch::setTempo(double newTempo)
|
||||||
{
|
{
|
||||||
int intskip;
|
int intskip;
|
||||||
|
|
||||||
@ -443,7 +554,7 @@ void TDStretch::setTempo(float newTempo)
|
|||||||
|
|
||||||
// Calculate ideal skip length (according to tempo value)
|
// Calculate ideal skip length (according to tempo value)
|
||||||
nominalSkip = tempo * (seekWindowLength - overlapLength);
|
nominalSkip = tempo * (seekWindowLength - overlapLength);
|
||||||
intskip = (int)(nominalSkip + 0.5f);
|
intskip = (int)(nominalSkip + 0.5);
|
||||||
|
|
||||||
// Calculate how many samples are needed in the 'inputBuffer' to
|
// Calculate how many samples are needed in the 'inputBuffer' to
|
||||||
// process another batch of samples
|
// process another batch of samples
|
||||||
@ -456,13 +567,16 @@ void TDStretch::setTempo(float newTempo)
|
|||||||
// Sets the number of channels, 1 = mono, 2 = stereo
|
// Sets the number of channels, 1 = mono, 2 = stereo
|
||||||
void TDStretch::setChannels(int numChannels)
|
void TDStretch::setChannels(int numChannels)
|
||||||
{
|
{
|
||||||
assert(numChannels > 0);
|
if (!verifyNumberOfChannels(numChannels) ||
|
||||||
if (channels == numChannels) return;
|
(channels == numChannels)) return;
|
||||||
assert(numChannels == 1 || numChannels == 2);
|
|
||||||
|
|
||||||
channels = numChannels;
|
channels = numChannels;
|
||||||
inputBuffer.setChannels(channels);
|
inputBuffer.setChannels(channels);
|
||||||
outputBuffer.setChannels(channels);
|
outputBuffer.setChannels(channels);
|
||||||
|
|
||||||
|
// re-init overlap/buffer
|
||||||
|
overlapLength=0;
|
||||||
|
setParameters(sampleRate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -498,13 +612,13 @@ void TDStretch::processNominalTempo()
|
|||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
// Processes as many processing frames of the samples 'inputBuffer', store
|
// Processes as many processing frames of the samples 'inputBuffer', store
|
||||||
// the result into 'outputBuffer'
|
// the result into 'outputBuffer'
|
||||||
void TDStretch::processSamples()
|
void TDStretch::processSamples()
|
||||||
{
|
{
|
||||||
int ovlSkip, offset;
|
int ovlSkip;
|
||||||
|
int offset = 0;
|
||||||
int temp;
|
int temp;
|
||||||
|
|
||||||
/* Removed this small optimization - can introduce a click to sound when tempo setting
|
/* Removed this small optimization - can introduce a click to sound when tempo setting
|
||||||
@ -521,8 +635,10 @@ void TDStretch::processSamples()
|
|||||||
// to form a processing frame.
|
// to form a processing frame.
|
||||||
while ((int)inputBuffer.numSamples() >= sampleReq)
|
while ((int)inputBuffer.numSamples() >= sampleReq)
|
||||||
{
|
{
|
||||||
// If tempo differs from the normal ('SCALE'), scan for the best overlapping
|
if (isBeginning == false)
|
||||||
// position
|
{
|
||||||
|
// apart from the very beginning of the track,
|
||||||
|
// scan for the best overlapping position & do overlap-add
|
||||||
offset = seekBestOverlapPosition(inputBuffer.ptrBegin());
|
offset = seekBestOverlapPosition(inputBuffer.ptrBegin());
|
||||||
|
|
||||||
// Mix the samples in the 'inputBuffer' at position of 'offset' with the
|
// Mix the samples in the 'inputBuffer' at position of 'offset' with the
|
||||||
@ -531,25 +647,50 @@ void TDStretch::processSamples()
|
|||||||
// (that's in 'midBuffer')
|
// (that's in 'midBuffer')
|
||||||
overlap(outputBuffer.ptrEnd((uint)overlapLength), inputBuffer.ptrBegin(), (uint)offset);
|
overlap(outputBuffer.ptrEnd((uint)overlapLength), inputBuffer.ptrBegin(), (uint)offset);
|
||||||
outputBuffer.putSamples((uint)overlapLength);
|
outputBuffer.putSamples((uint)overlapLength);
|
||||||
|
offset += overlapLength;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Adjust processing offset at beginning of track by not perform initial overlapping
|
||||||
|
// and compensating that in the 'input buffer skip' calculation
|
||||||
|
isBeginning = false;
|
||||||
|
int skip = (int)(tempo * overlapLength + 0.5 * seekLength + 0.5);
|
||||||
|
|
||||||
|
#ifdef ST_SIMD_AVOID_UNALIGNED
|
||||||
|
// in SIMD mode, round the skip amount to value corresponding to aligned memory address
|
||||||
|
if (channels == 1)
|
||||||
|
{
|
||||||
|
skip &= -4;
|
||||||
|
}
|
||||||
|
else if (channels == 2)
|
||||||
|
{
|
||||||
|
skip &= -2;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
skipFract -= skip;
|
||||||
|
if (skipFract <= -nominalSkip)
|
||||||
|
{
|
||||||
|
skipFract = -nominalSkip;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ... then copy sequence samples from 'inputBuffer' to output:
|
// ... then copy sequence samples from 'inputBuffer' to output:
|
||||||
|
|
||||||
// length of sequence
|
|
||||||
temp = (seekWindowLength - 2 * overlapLength);
|
|
||||||
|
|
||||||
// crosscheck that we don't have buffer overflow...
|
// crosscheck that we don't have buffer overflow...
|
||||||
if ((int)inputBuffer.numSamples() < (offset + temp + overlapLength * 2))
|
if ((int)inputBuffer.numSamples() < (offset + seekWindowLength - overlapLength))
|
||||||
{
|
{
|
||||||
continue; // just in case, shouldn't really happen
|
continue; // just in case, shouldn't really happen
|
||||||
}
|
}
|
||||||
|
|
||||||
outputBuffer.putSamples(inputBuffer.ptrBegin() + channels * (offset + overlapLength), (uint)temp);
|
// length of sequence
|
||||||
|
temp = (seekWindowLength - 2 * overlapLength);
|
||||||
|
outputBuffer.putSamples(inputBuffer.ptrBegin() + channels * offset, (uint)temp);
|
||||||
|
|
||||||
// Copies the end of the current sequence from 'inputBuffer' to
|
// Copies the end of the current sequence from 'inputBuffer' to
|
||||||
// 'midBuffer' for being mixed with the beginning of the next
|
// 'midBuffer' for being mixed with the beginning of the next
|
||||||
// processing sequence and so on
|
// processing sequence and so on
|
||||||
assert((offset + temp + overlapLength * 2) <= (int)inputBuffer.numSamples());
|
assert((offset + temp + overlapLength) <= (int)inputBuffer.numSamples());
|
||||||
memcpy(pMidBuffer, inputBuffer.ptrBegin() + channels * (offset + temp + overlapLength),
|
memcpy(pMidBuffer, inputBuffer.ptrBegin() + channels * (offset + temp),
|
||||||
channels * sizeof(SAMPLETYPE) * overlapLength);
|
channels * sizeof(SAMPLETYPE) * overlapLength);
|
||||||
|
|
||||||
// Remove the processed samples from the input buffer. Update
|
// Remove the processed samples from the input buffer. Update
|
||||||
@ -588,9 +729,9 @@ void TDStretch::acceptNewOverlapLength(int newOverlapLength)
|
|||||||
{
|
{
|
||||||
delete[] pMidBufferUnaligned;
|
delete[] pMidBufferUnaligned;
|
||||||
|
|
||||||
pMidBufferUnaligned = new SAMPLETYPE[overlapLength * 2 + 16 / sizeof(SAMPLETYPE)];
|
pMidBufferUnaligned = new SAMPLETYPE[overlapLength * channels + 16 / sizeof(SAMPLETYPE)];
|
||||||
// ensure that 'pMidBuffer' is aligned to 16 byte boundary for efficiency
|
// ensure that 'pMidBuffer' is aligned to 16 byte boundary for efficiency
|
||||||
pMidBuffer = (SAMPLETYPE *)((((ulong)pMidBufferUnaligned) + 15) & (ulong)-16);
|
pMidBuffer = (SAMPLETYPE *)SOUNDTOUCH_ALIGN_POINTER_16(pMidBufferUnaligned);
|
||||||
|
|
||||||
clearMidBuffer();
|
clearMidBuffer();
|
||||||
}
|
}
|
||||||
@ -599,7 +740,7 @@ void TDStretch::acceptNewOverlapLength(int newOverlapLength)
|
|||||||
|
|
||||||
// Operator 'new' is overloaded so that it automatically creates a suitable instance
|
// Operator 'new' is overloaded so that it automatically creates a suitable instance
|
||||||
// depending on if we've a MMX/SSE/etc-capable CPU available or not.
|
// depending on if we've a MMX/SSE/etc-capable CPU available or not.
|
||||||
void * TDStretch::operator new(size_t s)
|
void * TDStretch::operator new(size_t)
|
||||||
{
|
{
|
||||||
// Notice! don't use "new TDStretch" directly, use "newInstance" to create a new instance instead!
|
// Notice! don't use "new TDStretch" directly, use "newInstance" to create a new instance instead!
|
||||||
ST_THROW_RT_ERROR("Error in TDStretch::new: Don't use 'new TDStretch' directly, use 'newInstance' member instead!");
|
ST_THROW_RT_ERROR("Error in TDStretch::new: Don't use 'new TDStretch' directly, use 'newInstance' member instead!");
|
||||||
@ -612,6 +753,7 @@ TDStretch * TDStretch::newInstance()
|
|||||||
uint uExtensions;
|
uint uExtensions;
|
||||||
|
|
||||||
uExtensions = detectCPUextensions();
|
uExtensions = detectCPUextensions();
|
||||||
|
(void)uExtensions;
|
||||||
|
|
||||||
// Check if MMX/SSE instruction set extensions supported by CPU
|
// Check if MMX/SSE instruction set extensions supported by CPU
|
||||||
|
|
||||||
@ -643,7 +785,7 @@ TDStretch * TDStretch::newInstance()
|
|||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Integer arithmetics specific algorithm implementations.
|
// Integer arithmetic specific algorithm implementations.
|
||||||
//
|
//
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
@ -666,6 +808,25 @@ void TDStretch::overlapStereo(short *poutput, const short *input) const
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Overlaps samples in 'midBuffer' with the samples in 'input'. The 'Multi'
|
||||||
|
// version of the routine.
|
||||||
|
void TDStretch::overlapMulti(short *poutput, const short *input) const
|
||||||
|
{
|
||||||
|
short m1;
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
for (m1 = 0; m1 < overlapLength; m1 ++)
|
||||||
|
{
|
||||||
|
short m2 = (short)(overlapLength - m1);
|
||||||
|
for (int c = 0; c < channels; c ++)
|
||||||
|
{
|
||||||
|
poutput[i] = (input[i] * m1 + pMidBuffer[i] * m2) / overlapLength;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Calculates the x having the closest 2^x value for the given value
|
// Calculates the x having the closest 2^x value for the given value
|
||||||
static int _getClosest2Power(double value)
|
static int _getClosest2Power(double value)
|
||||||
{
|
{
|
||||||
@ -685,13 +846,15 @@ void TDStretch::calculateOverlapLength(int aoverlapMs)
|
|||||||
// calculate overlap length so that it's power of 2 - thus it's easy to do
|
// calculate overlap length so that it's power of 2 - thus it's easy to do
|
||||||
// integer division by right-shifting. Term "-1" at end is to account for
|
// integer division by right-shifting. Term "-1" at end is to account for
|
||||||
// the extra most significatnt bit left unused in result by signed multiplication
|
// the extra most significatnt bit left unused in result by signed multiplication
|
||||||
overlapDividerBits = _getClosest2Power((sampleRate * aoverlapMs) / 1000.0) - 1;
|
overlapDividerBitsPure = _getClosest2Power((sampleRate * aoverlapMs) / 1000.0) - 1;
|
||||||
if (overlapDividerBits > 9) overlapDividerBits = 9;
|
if (overlapDividerBitsPure > 9) overlapDividerBitsPure = 9;
|
||||||
if (overlapDividerBits < 3) overlapDividerBits = 3;
|
if (overlapDividerBitsPure < 3) overlapDividerBitsPure = 3;
|
||||||
newOvl = (int)pow(2.0, (int)overlapDividerBits + 1); // +1 => account for -1 above
|
newOvl = (int)pow(2.0, (int)overlapDividerBitsPure + 1); // +1 => account for -1 above
|
||||||
|
|
||||||
acceptNewOverlapLength(newOvl);
|
acceptNewOverlapLength(newOvl);
|
||||||
|
|
||||||
|
overlapDividerBitsNorm = overlapDividerBitsPure;
|
||||||
|
|
||||||
// calculate sloping divider so that crosscorrelation operation won't
|
// calculate sloping divider so that crosscorrelation operation won't
|
||||||
// overflow 32-bit register. Max. sum of the crosscorrelation sum without
|
// overflow 32-bit register. Max. sum of the crosscorrelation sum without
|
||||||
// divider would be 2^30*(N^3-N)/3, where N = overlap length
|
// divider would be 2^30*(N^3-N)/3, where N = overlap length
|
||||||
@ -699,39 +862,95 @@ void TDStretch::calculateOverlapLength(int aoverlapMs)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
double TDStretch::calcCrossCorr(const short *mixingPos, const short *compare) const
|
double TDStretch::calcCrossCorr(const short *mixingPos, const short *compare, double &norm)
|
||||||
{
|
{
|
||||||
long corr;
|
long corr;
|
||||||
long norm;
|
unsigned long lnorm;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
corr = norm = 0;
|
#ifdef ST_SIMD_AVOID_UNALIGNED
|
||||||
// Same routine for stereo and mono. For stereo, unroll loop for better
|
// in SIMD mode skip 'mixingPos' positions that aren't aligned to 16-byte boundary
|
||||||
// efficiency and gives slightly better resolution against rounding.
|
if (((ulongptr)mixingPos) & 15) return -1e50;
|
||||||
// For mono it same routine, just unrolls loop by factor of 4
|
#endif
|
||||||
for (i = 0; i < channels * overlapLength; i += 4)
|
|
||||||
|
// hint compiler autovectorization that loop length is divisible by 8
|
||||||
|
int ilength = (channels * overlapLength) & -8;
|
||||||
|
|
||||||
|
corr = lnorm = 0;
|
||||||
|
// Same routine for stereo and mono
|
||||||
|
for (i = 0; i < ilength; i += 2)
|
||||||
{
|
{
|
||||||
corr += (mixingPos[i] * compare[i] +
|
corr += (mixingPos[i] * compare[i] +
|
||||||
mixingPos[i + 1] * compare[i + 1] +
|
mixingPos[i + 1] * compare[i + 1]) >> overlapDividerBitsNorm;
|
||||||
mixingPos[i + 2] * compare[i + 2] +
|
lnorm += (mixingPos[i] * mixingPos[i] +
|
||||||
mixingPos[i + 3] * compare[i + 3]) >> overlapDividerBits;
|
mixingPos[i + 1] * mixingPos[i + 1]) >> overlapDividerBitsNorm;
|
||||||
norm += (mixingPos[i] * mixingPos[i] +
|
// do intermediate scalings to avoid integer overflow
|
||||||
mixingPos[i + 1] * mixingPos[i + 1] +
|
}
|
||||||
mixingPos[i + 2] * mixingPos[i + 2] +
|
|
||||||
mixingPos[i + 3] * mixingPos[i + 3]) >> overlapDividerBits;
|
if (lnorm > maxnorm)
|
||||||
|
{
|
||||||
|
// modify 'maxnorm' inside critical section to avoid multi-access conflict if in OpenMP mode
|
||||||
|
#pragma omp critical
|
||||||
|
if (lnorm > maxnorm)
|
||||||
|
{
|
||||||
|
maxnorm = lnorm;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Normalize result by dividing by sqrt(norm) - this step is easiest
|
||||||
|
// done using floating point operation
|
||||||
|
norm = (double)lnorm;
|
||||||
|
return (double)corr / sqrt((norm < 1e-9) ? 1.0 : norm);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Update cross-correlation by accumulating "norm" coefficient by previously calculated value
|
||||||
|
double TDStretch::calcCrossCorrAccumulate(const short *mixingPos, const short *compare, double &norm)
|
||||||
|
{
|
||||||
|
long corr;
|
||||||
|
long lnorm;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
// hint compiler autovectorization that loop length is divisible by 8
|
||||||
|
int ilength = (channels * overlapLength) & -8;
|
||||||
|
|
||||||
|
// cancel first normalizer tap from previous round
|
||||||
|
lnorm = 0;
|
||||||
|
for (i = 1; i <= channels; i ++)
|
||||||
|
{
|
||||||
|
lnorm -= (mixingPos[-i] * mixingPos[-i]) >> overlapDividerBitsNorm;
|
||||||
|
}
|
||||||
|
|
||||||
|
corr = 0;
|
||||||
|
// Same routine for stereo and mono.
|
||||||
|
for (i = 0; i < ilength; i += 2)
|
||||||
|
{
|
||||||
|
corr += (mixingPos[i] * compare[i] +
|
||||||
|
mixingPos[i + 1] * compare[i + 1]) >> overlapDividerBitsNorm;
|
||||||
|
}
|
||||||
|
|
||||||
|
// update normalizer with last samples of this round
|
||||||
|
for (int j = 0; j < channels; j ++)
|
||||||
|
{
|
||||||
|
i --;
|
||||||
|
lnorm += (mixingPos[i] * mixingPos[i]) >> overlapDividerBitsNorm;
|
||||||
|
}
|
||||||
|
|
||||||
|
norm += (double)lnorm;
|
||||||
|
if (norm > maxnorm)
|
||||||
|
{
|
||||||
|
maxnorm = (unsigned long)norm;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Normalize result by dividing by sqrt(norm) - this step is easiest
|
// Normalize result by dividing by sqrt(norm) - this step is easiest
|
||||||
// done using floating point operation
|
// done using floating point operation
|
||||||
if (norm == 0) norm = 1; // to avoid div by zero
|
return (double)corr / sqrt((norm < 1e-9) ? 1.0 : norm);
|
||||||
return (double)corr / sqrt((double)norm);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // SOUNDTOUCH_INTEGER_SAMPLES
|
#endif // SOUNDTOUCH_INTEGER_SAMPLES
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Floating point arithmetics specific algorithm implementations.
|
// Floating point arithmetic specific algorithm implementations.
|
||||||
//
|
//
|
||||||
|
|
||||||
#ifdef SOUNDTOUCH_FLOAT_SAMPLES
|
#ifdef SOUNDTOUCH_FLOAT_SAMPLES
|
||||||
@ -760,6 +979,34 @@ void TDStretch::overlapStereo(float *pOutput, const float *pInput) const
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Overlaps samples in 'midBuffer' with the samples in 'input'.
|
||||||
|
void TDStretch::overlapMulti(float *pOutput, const float *pInput) const
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
float fScale;
|
||||||
|
float f1;
|
||||||
|
float f2;
|
||||||
|
|
||||||
|
fScale = 1.0f / (float)overlapLength;
|
||||||
|
|
||||||
|
f1 = 0;
|
||||||
|
f2 = 1.0f;
|
||||||
|
|
||||||
|
i=0;
|
||||||
|
for (int i2 = 0; i2 < overlapLength; i2 ++)
|
||||||
|
{
|
||||||
|
// note: Could optimize this slightly by taking into account that always channels > 2
|
||||||
|
for (int c = 0; c < channels; c ++)
|
||||||
|
{
|
||||||
|
pOutput[i] = pInput[i] * f1 + pMidBuffer[i] * f2;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
f1 += fScale;
|
||||||
|
f2 -= fScale;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Calculates overlapInMsec period length in samples.
|
/// Calculates overlapInMsec period length in samples.
|
||||||
void TDStretch::calculateOverlapLength(int overlapInMsec)
|
void TDStretch::calculateOverlapLength(int overlapInMsec)
|
||||||
{
|
{
|
||||||
@ -776,33 +1023,66 @@ void TDStretch::calculateOverlapLength(int overlapInMsec)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
double TDStretch::calcCrossCorr(const float *mixingPos, const float *compare) const
|
/// Calculate cross-correlation
|
||||||
|
double TDStretch::calcCrossCorr(const float *mixingPos, const float *compare, double &anorm)
|
||||||
{
|
{
|
||||||
double corr;
|
float corr;
|
||||||
double norm;
|
float norm;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
|
#ifdef ST_SIMD_AVOID_UNALIGNED
|
||||||
|
// in SIMD mode skip 'mixingPos' positions that aren't aligned to 16-byte boundary
|
||||||
|
if (((ulongptr)mixingPos) & 15) return -1e50;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// hint compiler autovectorization that loop length is divisible by 8
|
||||||
|
int ilength = (channels * overlapLength) & -8;
|
||||||
|
|
||||||
corr = norm = 0;
|
corr = norm = 0;
|
||||||
// Same routine for stereo and mono. For Stereo, unroll by factor of 2.
|
// Same routine for stereo and mono
|
||||||
// For mono it's same routine yet unrollsd by factor of 4.
|
for (i = 0; i < ilength; i ++)
|
||||||
for (i = 0; i < channels * overlapLength; i += 4)
|
|
||||||
{
|
{
|
||||||
corr += mixingPos[i] * compare[i] +
|
corr += mixingPos[i] * compare[i];
|
||||||
mixingPos[i + 1] * compare[i + 1];
|
norm += mixingPos[i] * mixingPos[i];
|
||||||
|
|
||||||
norm += mixingPos[i] * mixingPos[i] +
|
|
||||||
mixingPos[i + 1] * mixingPos[i + 1];
|
|
||||||
|
|
||||||
// unroll the loop for better CPU efficiency:
|
|
||||||
corr += mixingPos[i + 2] * compare[i + 2] +
|
|
||||||
mixingPos[i + 3] * compare[i + 3];
|
|
||||||
|
|
||||||
norm += mixingPos[i + 2] * mixingPos[i + 2] +
|
|
||||||
mixingPos[i + 3] * mixingPos[i + 3];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (norm < 1e-9) norm = 1.0; // to avoid div by zero
|
anorm = norm;
|
||||||
return corr / sqrt(norm);
|
return corr / sqrt((norm < 1e-9 ? 1.0 : norm));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Update cross-correlation by accumulating "norm" coefficient by previously calculated value
|
||||||
|
double TDStretch::calcCrossCorrAccumulate(const float *mixingPos, const float *compare, double &norm)
|
||||||
|
{
|
||||||
|
float corr;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
corr = 0;
|
||||||
|
|
||||||
|
// cancel first normalizer tap from previous round
|
||||||
|
for (i = 1; i <= channels; i ++)
|
||||||
|
{
|
||||||
|
norm -= mixingPos[-i] * mixingPos[-i];
|
||||||
|
}
|
||||||
|
|
||||||
|
// hint compiler autovectorization that loop length is divisible by 8
|
||||||
|
int ilength = (channels * overlapLength) & -8;
|
||||||
|
|
||||||
|
// Same routine for stereo and mono
|
||||||
|
for (i = 0; i < ilength; i ++)
|
||||||
|
{
|
||||||
|
corr += mixingPos[i] * compare[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
// update normalizer with last samples of this round
|
||||||
|
for (int j = 0; j < channels; j ++)
|
||||||
|
{
|
||||||
|
i --;
|
||||||
|
norm += mixingPos[i] * mixingPos[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
return corr / sqrt((norm < 1e-9 ? 1.0 : norm));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#endif // SOUNDTOUCH_FLOAT_SAMPLES
|
#endif // SOUNDTOUCH_FLOAT_SAMPLES
|
||||||
|
|||||||
@ -13,13 +13,6 @@
|
|||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Last changed : $Date$
|
|
||||||
// File revision : $Revision: 4 $
|
|
||||||
//
|
|
||||||
// $Id$
|
|
||||||
//
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// License :
|
// License :
|
||||||
//
|
//
|
||||||
// SoundTouch audio processing library
|
// SoundTouch audio processing library
|
||||||
@ -112,46 +105,57 @@ class TDStretch : public FIFOProcessor
|
|||||||
protected:
|
protected:
|
||||||
int channels;
|
int channels;
|
||||||
int sampleReq;
|
int sampleReq;
|
||||||
float tempo;
|
|
||||||
|
|
||||||
SAMPLETYPE *pMidBuffer;
|
|
||||||
SAMPLETYPE *pMidBufferUnaligned;
|
|
||||||
int overlapLength;
|
int overlapLength;
|
||||||
int seekLength;
|
int seekLength;
|
||||||
int seekWindowLength;
|
int seekWindowLength;
|
||||||
int overlapDividerBits;
|
int overlapDividerBitsNorm;
|
||||||
|
int overlapDividerBitsPure;
|
||||||
int slopingDivider;
|
int slopingDivider;
|
||||||
float nominalSkip;
|
|
||||||
float skipFract;
|
|
||||||
FIFOSampleBuffer outputBuffer;
|
|
||||||
FIFOSampleBuffer inputBuffer;
|
|
||||||
BOOL bQuickSeek;
|
|
||||||
|
|
||||||
int sampleRate;
|
int sampleRate;
|
||||||
int sequenceMs;
|
int sequenceMs;
|
||||||
int seekWindowMs;
|
int seekWindowMs;
|
||||||
int overlapMs;
|
int overlapMs;
|
||||||
BOOL bAutoSeqSetting;
|
|
||||||
BOOL bAutoSeekSetting;
|
unsigned long maxnorm;
|
||||||
|
float maxnormf;
|
||||||
|
|
||||||
|
double tempo;
|
||||||
|
double nominalSkip;
|
||||||
|
double skipFract;
|
||||||
|
|
||||||
|
bool bQuickSeek;
|
||||||
|
bool bAutoSeqSetting;
|
||||||
|
bool bAutoSeekSetting;
|
||||||
|
bool isBeginning;
|
||||||
|
|
||||||
|
SAMPLETYPE *pMidBuffer;
|
||||||
|
SAMPLETYPE *pMidBufferUnaligned;
|
||||||
|
|
||||||
|
FIFOSampleBuffer outputBuffer;
|
||||||
|
FIFOSampleBuffer inputBuffer;
|
||||||
|
|
||||||
void acceptNewOverlapLength(int newOverlapLength);
|
void acceptNewOverlapLength(int newOverlapLength);
|
||||||
|
|
||||||
virtual void clearCrossCorrState();
|
virtual void clearCrossCorrState();
|
||||||
void calculateOverlapLength(int overlapMs);
|
void calculateOverlapLength(int overlapMs);
|
||||||
|
|
||||||
virtual double calcCrossCorr(const SAMPLETYPE *mixingPos, const SAMPLETYPE *compare) const;
|
virtual double calcCrossCorr(const SAMPLETYPE *mixingPos, const SAMPLETYPE *compare, double &norm);
|
||||||
|
virtual double calcCrossCorrAccumulate(const SAMPLETYPE *mixingPos, const SAMPLETYPE *compare, double &norm);
|
||||||
|
|
||||||
virtual int seekBestOverlapPositionFull(const SAMPLETYPE *refPos);
|
virtual int seekBestOverlapPositionFull(const SAMPLETYPE *refPos);
|
||||||
virtual int seekBestOverlapPositionQuick(const SAMPLETYPE *refPos);
|
virtual int seekBestOverlapPositionQuick(const SAMPLETYPE *refPos);
|
||||||
int seekBestOverlapPosition(const SAMPLETYPE *refPos);
|
virtual int seekBestOverlapPosition(const SAMPLETYPE *refPos);
|
||||||
|
|
||||||
virtual void overlapStereo(SAMPLETYPE *output, const SAMPLETYPE *input) const;
|
virtual void overlapStereo(SAMPLETYPE *output, const SAMPLETYPE *input) const;
|
||||||
virtual void overlapMono(SAMPLETYPE *output, const SAMPLETYPE *input) const;
|
virtual void overlapMono(SAMPLETYPE *output, const SAMPLETYPE *input) const;
|
||||||
|
virtual void overlapMulti(SAMPLETYPE *output, const SAMPLETYPE *input) const;
|
||||||
|
|
||||||
void clearMidBuffer();
|
void clearMidBuffer();
|
||||||
void overlap(SAMPLETYPE *output, const SAMPLETYPE *input, uint ovlPos) const;
|
void overlap(SAMPLETYPE *output, const SAMPLETYPE *input, uint ovlPos) const;
|
||||||
|
|
||||||
void calcSeqParameters();
|
void calcSeqParameters();
|
||||||
|
void adaptNormalizer();
|
||||||
|
|
||||||
/// Changes the tempo of the given sound samples.
|
/// Changes the tempo of the given sound samples.
|
||||||
/// Returns amount of samples returned in the "output" buffer.
|
/// Returns amount of samples returned in the "output" buffer.
|
||||||
@ -161,7 +165,7 @@ protected:
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
TDStretch();
|
TDStretch();
|
||||||
virtual ~TDStretch();
|
virtual ~TDStretch() override;
|
||||||
|
|
||||||
/// Operator 'new' is overloaded so that it automatically creates a suitable instance
|
/// Operator 'new' is overloaded so that it automatically creates a suitable instance
|
||||||
/// depending on if we've a MMX/SSE/etc-capable CPU available or not.
|
/// depending on if we've a MMX/SSE/etc-capable CPU available or not.
|
||||||
@ -180,10 +184,10 @@ public:
|
|||||||
|
|
||||||
/// Sets new target tempo. Normal tempo = 'SCALE', smaller values represent slower
|
/// Sets new target tempo. Normal tempo = 'SCALE', smaller values represent slower
|
||||||
/// tempo, larger faster tempo.
|
/// tempo, larger faster tempo.
|
||||||
void setTempo(float newTempo);
|
void setTempo(double newTempo);
|
||||||
|
|
||||||
/// Returns nonzero if there aren't any samples available for outputting.
|
/// Returns nonzero if there aren't any samples available for outputting.
|
||||||
virtual void clear();
|
virtual void clear() override;
|
||||||
|
|
||||||
/// Clears the input buffer
|
/// Clears the input buffer
|
||||||
void clearInput();
|
void clearInput();
|
||||||
@ -193,10 +197,10 @@ public:
|
|||||||
|
|
||||||
/// Enables/disables the quick position seeking algorithm. Zero to disable,
|
/// Enables/disables the quick position seeking algorithm. Zero to disable,
|
||||||
/// nonzero to enable
|
/// nonzero to enable
|
||||||
void enableQuickSeek(BOOL enable);
|
void enableQuickSeek(bool enable);
|
||||||
|
|
||||||
/// Returns nonzero if the quick seeking algorithm is enabled.
|
/// Returns nonzero if the quick seeking algorithm is enabled.
|
||||||
BOOL isQuickSeekEnabled() const;
|
bool isQuickSeekEnabled() const;
|
||||||
|
|
||||||
/// Sets routine control parameters. These control are certain time constants
|
/// Sets routine control parameters. These control are certain time constants
|
||||||
/// defining how the sound is stretched to the desired duration.
|
/// defining how the sound is stretched to the desired duration.
|
||||||
@ -213,7 +217,7 @@ public:
|
|||||||
);
|
);
|
||||||
|
|
||||||
/// Get routine control parameters, see setParameters() function.
|
/// Get routine control parameters, see setParameters() function.
|
||||||
/// Any of the parameters to this function can be NULL, in such case corresponding parameter
|
/// Any of the parameters to this function can be nullptr, in such case corresponding parameter
|
||||||
/// value isn't returned.
|
/// value isn't returned.
|
||||||
void getParameters(int *pSampleRate, int *pSequenceMs, int *pSeekWindowMs, int *pOverlapMs) const;
|
void getParameters(int *pSampleRate, int *pSequenceMs, int *pSeekWindowMs, int *pOverlapMs) const;
|
||||||
|
|
||||||
@ -223,7 +227,7 @@ public:
|
|||||||
const SAMPLETYPE *samples, ///< Input sample data
|
const SAMPLETYPE *samples, ///< Input sample data
|
||||||
uint numSamples ///< Number of samples in 'samples' so that one sample
|
uint numSamples ///< Number of samples in 'samples' so that one sample
|
||||||
///< contains both channels if stereo
|
///< contains both channels if stereo
|
||||||
);
|
) override;
|
||||||
|
|
||||||
/// return nominal input sample requirement for triggering a processing batch
|
/// return nominal input sample requirement for triggering a processing batch
|
||||||
int getInputSampleReq() const
|
int getInputSampleReq() const
|
||||||
@ -236,8 +240,13 @@ public:
|
|||||||
{
|
{
|
||||||
return seekWindowLength - overlapLength;
|
return seekWindowLength - overlapLength;
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
|
/// return approximate initial input-output latency
|
||||||
|
int getLatency() const
|
||||||
|
{
|
||||||
|
return sampleReq;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
// Implementation-specific class declarations:
|
// Implementation-specific class declarations:
|
||||||
@ -247,9 +256,10 @@ public:
|
|||||||
class TDStretchMMX : public TDStretch
|
class TDStretchMMX : public TDStretch
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
double calcCrossCorr(const short *mixingPos, const short *compare) const;
|
double calcCrossCorr(const short *mixingPos, const short *compare, double &norm) override;
|
||||||
virtual void overlapStereo(short *output, const short *input) const;
|
double calcCrossCorrAccumulate(const short *mixingPos, const short *compare, double &norm) override;
|
||||||
virtual void clearCrossCorrState();
|
virtual void overlapStereo(short *output, const short *input) const override;
|
||||||
|
virtual void clearCrossCorrState() override;
|
||||||
};
|
};
|
||||||
#endif /// SOUNDTOUCH_ALLOW_MMX
|
#endif /// SOUNDTOUCH_ALLOW_MMX
|
||||||
|
|
||||||
@ -259,7 +269,8 @@ public:
|
|||||||
class TDStretchSSE : public TDStretch
|
class TDStretchSSE : public TDStretch
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
double calcCrossCorr(const float *mixingPos, const float *compare) const;
|
double calcCrossCorr(const float *mixingPos, const float *compare, double &norm) override;
|
||||||
|
double calcCrossCorrAccumulate(const float *mixingPos, const float *compare, double &norm) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /// SOUNDTOUCH_ALLOW_SSE
|
#endif /// SOUNDTOUCH_ALLOW_SSE
|
||||||
|
|||||||
@ -12,13 +12,6 @@
|
|||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Last changed : $Date$
|
|
||||||
// File revision : $Revision: 4 $
|
|
||||||
//
|
|
||||||
// $Id$
|
|
||||||
//
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// License :
|
// License :
|
||||||
//
|
//
|
||||||
// SoundTouch audio processing library
|
// SoundTouch audio processing library
|
||||||
|
|||||||
@ -11,13 +11,6 @@
|
|||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Last changed : $Date$
|
|
||||||
// File revision : $Revision: 4 $
|
|
||||||
//
|
|
||||||
// $Id$
|
|
||||||
//
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// License :
|
// License :
|
||||||
//
|
//
|
||||||
// SoundTouch audio processing library
|
// SoundTouch audio processing library
|
||||||
@ -42,23 +35,22 @@
|
|||||||
#include "cpu_detect.h"
|
#include "cpu_detect.h"
|
||||||
#include "STTypes.h"
|
#include "STTypes.h"
|
||||||
|
|
||||||
|
|
||||||
#if defined(SOUNDTOUCH_ALLOW_X86_OPTIMIZATIONS)
|
#if defined(SOUNDTOUCH_ALLOW_X86_OPTIMIZATIONS)
|
||||||
|
|
||||||
#if defined(__GNUC__) && defined(__i386__)
|
#if defined(__GNUC__) && defined(__i386__)
|
||||||
// gcc
|
// gcc
|
||||||
#include "cpuid.h"
|
#include "cpuid.h"
|
||||||
|
#elif defined(_M_IX86)
|
||||||
|
// windows non-gcc
|
||||||
|
#include <intrin.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(_M_IX86)
|
|
||||||
// windows
|
|
||||||
#include <intrin.h>
|
|
||||||
#define bit_MMX (1 << 23)
|
#define bit_MMX (1 << 23)
|
||||||
#define bit_SSE (1 << 25)
|
#define bit_SSE (1 << 25)
|
||||||
#define bit_SSE2 (1 << 26)
|
#define bit_SSE2 (1 << 26)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
@ -76,7 +68,6 @@ void disableExtensions(uint dwDisableMask)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// Checks which instruction set extensions are supported by the CPU.
|
/// Checks which instruction set extensions are supported by the CPU.
|
||||||
uint detectCPUextensions(void)
|
uint detectCPUextensions(void)
|
||||||
{
|
{
|
||||||
|
|||||||
@ -20,13 +20,6 @@
|
|||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Last changed : $Date$
|
|
||||||
// File revision : $Revision: 4 $
|
|
||||||
//
|
|
||||||
// $Id$
|
|
||||||
//
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// License :
|
// License :
|
||||||
//
|
//
|
||||||
// SoundTouch audio processing library
|
// SoundTouch audio processing library
|
||||||
@ -68,7 +61,7 @@ using namespace soundtouch;
|
|||||||
|
|
||||||
|
|
||||||
// Calculates cross correlation of two buffers
|
// Calculates cross correlation of two buffers
|
||||||
double TDStretchMMX::calcCrossCorr(const short *pV1, const short *pV2) const
|
double TDStretchMMX::calcCrossCorr(const short *pV1, const short *pV2, double &dnorm)
|
||||||
{
|
{
|
||||||
const __m64 *pVec1, *pVec2;
|
const __m64 *pVec1, *pVec2;
|
||||||
__m64 shifter;
|
__m64 shifter;
|
||||||
@ -79,7 +72,7 @@ double TDStretchMMX::calcCrossCorr(const short *pV1, const short *pV2) const
|
|||||||
pVec1 = (__m64*)pV1;
|
pVec1 = (__m64*)pV1;
|
||||||
pVec2 = (__m64*)pV2;
|
pVec2 = (__m64*)pV2;
|
||||||
|
|
||||||
shifter = _m_from_int(overlapDividerBits);
|
shifter = _m_from_int(overlapDividerBitsNorm);
|
||||||
normaccu = accu = _mm_setzero_si64();
|
normaccu = accu = _mm_setzero_si64();
|
||||||
|
|
||||||
// Process 4 parallel sets of 2 * stereo samples or 4 * mono samples
|
// Process 4 parallel sets of 2 * stereo samples or 4 * mono samples
|
||||||
@ -93,19 +86,19 @@ double TDStretchMMX::calcCrossCorr(const short *pV1, const short *pV2) const
|
|||||||
// _mm_add_pi32 : 2*32bit add
|
// _mm_add_pi32 : 2*32bit add
|
||||||
// _m_psrad : 32bit right-shift
|
// _m_psrad : 32bit right-shift
|
||||||
|
|
||||||
temp = _mm_add_pi32(_mm_madd_pi16(pVec1[0], pVec2[0]),
|
temp = _mm_add_pi32(_mm_sra_pi32(_mm_madd_pi16(pVec1[0], pVec2[0]), shifter),
|
||||||
_mm_madd_pi16(pVec1[1], pVec2[1]));
|
_mm_sra_pi32(_mm_madd_pi16(pVec1[1], pVec2[1]), shifter));
|
||||||
temp2 = _mm_add_pi32(_mm_madd_pi16(pVec1[0], pVec1[0]),
|
temp2 = _mm_add_pi32(_mm_sra_pi32(_mm_madd_pi16(pVec1[0], pVec1[0]), shifter),
|
||||||
_mm_madd_pi16(pVec1[1], pVec1[1]));
|
_mm_sra_pi32(_mm_madd_pi16(pVec1[1], pVec1[1]), shifter));
|
||||||
accu = _mm_add_pi32(accu, _mm_sra_pi32(temp, shifter));
|
accu = _mm_add_pi32(accu, temp);
|
||||||
normaccu = _mm_add_pi32(normaccu, _mm_sra_pi32(temp2, shifter));
|
normaccu = _mm_add_pi32(normaccu, temp2);
|
||||||
|
|
||||||
temp = _mm_add_pi32(_mm_madd_pi16(pVec1[2], pVec2[2]),
|
temp = _mm_add_pi32(_mm_sra_pi32(_mm_madd_pi16(pVec1[2], pVec2[2]), shifter),
|
||||||
_mm_madd_pi16(pVec1[3], pVec2[3]));
|
_mm_sra_pi32(_mm_madd_pi16(pVec1[3], pVec2[3]), shifter));
|
||||||
temp2 = _mm_add_pi32(_mm_madd_pi16(pVec1[2], pVec1[2]),
|
temp2 = _mm_add_pi32(_mm_sra_pi32(_mm_madd_pi16(pVec1[2], pVec1[2]), shifter),
|
||||||
_mm_madd_pi16(pVec1[3], pVec1[3]));
|
_mm_sra_pi32(_mm_madd_pi16(pVec1[3], pVec1[3]), shifter));
|
||||||
accu = _mm_add_pi32(accu, _mm_sra_pi32(temp, shifter));
|
accu = _mm_add_pi32(accu, temp);
|
||||||
normaccu = _mm_add_pi32(normaccu, _mm_sra_pi32(temp2, shifter));
|
normaccu = _mm_add_pi32(normaccu, temp2);
|
||||||
|
|
||||||
pVec1 += 4;
|
pVec1 += 4;
|
||||||
pVec2 += 4;
|
pVec2 += 4;
|
||||||
@ -123,16 +116,98 @@ double TDStretchMMX::calcCrossCorr(const short *pV1, const short *pV2) const
|
|||||||
// Clear MMS state
|
// Clear MMS state
|
||||||
_m_empty();
|
_m_empty();
|
||||||
|
|
||||||
|
if (norm > (long)maxnorm)
|
||||||
|
{
|
||||||
|
// modify 'maxnorm' inside critical section to avoid multi-access conflict if in OpenMP mode
|
||||||
|
#pragma omp critical
|
||||||
|
if (norm > (long)maxnorm)
|
||||||
|
{
|
||||||
|
maxnorm = norm;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Normalize result by dividing by sqrt(norm) - this step is easiest
|
// Normalize result by dividing by sqrt(norm) - this step is easiest
|
||||||
// done using floating point operation
|
// done using floating point operation
|
||||||
if (norm == 0) norm = 1; // to avoid div by zero
|
dnorm = (double)norm;
|
||||||
|
|
||||||
return (double)corr / sqrt((double)norm);
|
return (double)corr / sqrt(dnorm < 1e-9 ? 1.0 : dnorm);
|
||||||
// Note: Warning about the missing EMMS instruction is harmless
|
// Note: Warning about the missing EMMS instruction is harmless
|
||||||
// as it'll be called elsewhere.
|
// as it'll be called elsewhere.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Update cross-correlation by accumulating "norm" coefficient by previously calculated value
|
||||||
|
double TDStretchMMX::calcCrossCorrAccumulate(const short *pV1, const short *pV2, double &dnorm)
|
||||||
|
{
|
||||||
|
const __m64 *pVec1, *pVec2;
|
||||||
|
__m64 shifter;
|
||||||
|
__m64 accu;
|
||||||
|
long corr, lnorm;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
// cancel first normalizer tap from previous round
|
||||||
|
lnorm = 0;
|
||||||
|
for (i = 1; i <= channels; i ++)
|
||||||
|
{
|
||||||
|
lnorm -= (pV1[-i] * pV1[-i]) >> overlapDividerBitsNorm;
|
||||||
|
}
|
||||||
|
|
||||||
|
pVec1 = (__m64*)pV1;
|
||||||
|
pVec2 = (__m64*)pV2;
|
||||||
|
|
||||||
|
shifter = _m_from_int(overlapDividerBitsNorm);
|
||||||
|
accu = _mm_setzero_si64();
|
||||||
|
|
||||||
|
// Process 4 parallel sets of 2 * stereo samples or 4 * mono samples
|
||||||
|
// during each round for improved CPU-level parallellization.
|
||||||
|
for (i = 0; i < channels * overlapLength / 16; i ++)
|
||||||
|
{
|
||||||
|
__m64 temp;
|
||||||
|
|
||||||
|
// dictionary of instructions:
|
||||||
|
// _m_pmaddwd : 4*16bit multiply-add, resulting two 32bits = [a0*b0+a1*b1 ; a2*b2+a3*b3]
|
||||||
|
// _mm_add_pi32 : 2*32bit add
|
||||||
|
// _m_psrad : 32bit right-shift
|
||||||
|
|
||||||
|
temp = _mm_add_pi32(_mm_sra_pi32(_mm_madd_pi16(pVec1[0], pVec2[0]), shifter),
|
||||||
|
_mm_sra_pi32(_mm_madd_pi16(pVec1[1], pVec2[1]), shifter));
|
||||||
|
accu = _mm_add_pi32(accu, temp);
|
||||||
|
|
||||||
|
temp = _mm_add_pi32(_mm_sra_pi32(_mm_madd_pi16(pVec1[2], pVec2[2]), shifter),
|
||||||
|
_mm_sra_pi32(_mm_madd_pi16(pVec1[3], pVec2[3]), shifter));
|
||||||
|
accu = _mm_add_pi32(accu, temp);
|
||||||
|
|
||||||
|
pVec1 += 4;
|
||||||
|
pVec2 += 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
// copy hi-dword of mm0 to lo-dword of mm1, then sum mmo+mm1
|
||||||
|
// and finally store the result into the variable "corr"
|
||||||
|
|
||||||
|
accu = _mm_add_pi32(accu, _mm_srli_si64(accu, 32));
|
||||||
|
corr = _m_to_int(accu);
|
||||||
|
|
||||||
|
// Clear MMS state
|
||||||
|
_m_empty();
|
||||||
|
|
||||||
|
// update normalizer with last samples of this round
|
||||||
|
pV1 = (short *)pVec1;
|
||||||
|
for (int j = 1; j <= channels; j ++)
|
||||||
|
{
|
||||||
|
lnorm += (pV1[-j] * pV1[-j]) >> overlapDividerBitsNorm;
|
||||||
|
}
|
||||||
|
dnorm += (double)lnorm;
|
||||||
|
|
||||||
|
if (lnorm > (long)maxnorm)
|
||||||
|
{
|
||||||
|
maxnorm = lnorm;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Normalize result by dividing by sqrt(norm) - this step is easiest
|
||||||
|
// done using floating point operation
|
||||||
|
return (double)corr / sqrt((dnorm < 1e-9) ? 1.0 : dnorm);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void TDStretchMMX::clearCrossCorrState()
|
void TDStretchMMX::clearCrossCorrState()
|
||||||
{
|
{
|
||||||
@ -142,7 +217,6 @@ void TDStretchMMX::clearCrossCorrState()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// MMX-optimized version of the function overlapStereo
|
// MMX-optimized version of the function overlapStereo
|
||||||
void TDStretchMMX::overlapStereo(short *output, const short *input) const
|
void TDStretchMMX::overlapStereo(short *output, const short *input) const
|
||||||
{
|
{
|
||||||
@ -166,7 +240,7 @@ void TDStretchMMX::overlapStereo(short *output, const short *input) const
|
|||||||
|
|
||||||
// Overlaplength-division by shifter. "+1" is to account for "-1" deduced in
|
// Overlaplength-division by shifter. "+1" is to account for "-1" deduced in
|
||||||
// overlapDividerBits calculation earlier.
|
// overlapDividerBits calculation earlier.
|
||||||
shifter = _m_from_int(overlapDividerBits + 1);
|
shifter = _m_from_int(overlapDividerBitsPure + 1);
|
||||||
|
|
||||||
for (i = 0; i < overlapLength / 4; i ++)
|
for (i = 0; i < overlapLength / 4; i ++)
|
||||||
{
|
{
|
||||||
@ -220,7 +294,8 @@ void TDStretchMMX::overlapStereo(short *output, const short *input) const
|
|||||||
|
|
||||||
FIRFilterMMX::FIRFilterMMX() : FIRFilter()
|
FIRFilterMMX::FIRFilterMMX() : FIRFilter()
|
||||||
{
|
{
|
||||||
filterCoeffsUnalign = NULL;
|
filterCoeffsAlign = nullptr;
|
||||||
|
filterCoeffsUnalign = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -239,7 +314,7 @@ void FIRFilterMMX::setCoefficients(const short *coeffs, uint newLength, uint uRe
|
|||||||
// Ensure that filter coeffs array is aligned to 16-byte boundary
|
// Ensure that filter coeffs array is aligned to 16-byte boundary
|
||||||
delete[] filterCoeffsUnalign;
|
delete[] filterCoeffsUnalign;
|
||||||
filterCoeffsUnalign = new short[2 * newLength + 8];
|
filterCoeffsUnalign = new short[2 * newLength + 8];
|
||||||
filterCoeffsAlign = (short *)(((ulong)filterCoeffsUnalign + 15) & -16);
|
filterCoeffsAlign = (short *)SOUNDTOUCH_ALIGN_POINTER_16(filterCoeffsUnalign);
|
||||||
|
|
||||||
// rearrange the filter coefficients for mmx routines
|
// rearrange the filter coefficients for mmx routines
|
||||||
for (i = 0;i < length; i += 4)
|
for (i = 0;i < length; i += 4)
|
||||||
@ -257,7 +332,6 @@ void FIRFilterMMX::setCoefficients(const short *coeffs, uint newLength, uint uRe
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// mmx-optimized version of the filter routine for stereo sound
|
// mmx-optimized version of the filter routine for stereo sound
|
||||||
uint FIRFilterMMX::evaluateFilterStereo(short *dest, const short *src, uint numSamples) const
|
uint FIRFilterMMX::evaluateFilterStereo(short *dest, const short *src, uint numSamples) const
|
||||||
{
|
{
|
||||||
@ -314,4 +388,9 @@ uint FIRFilterMMX::evaluateFilterStereo(short *dest, const short *src, uint numS
|
|||||||
return (numSamples & 0xfffffffe) - length;
|
return (numSamples & 0xfffffffe) - length;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
// workaround to not complain about empty module
|
||||||
|
bool _dontcomplain_mmx_empty;
|
||||||
|
|
||||||
#endif // SOUNDTOUCH_ALLOW_MMX
|
#endif // SOUNDTOUCH_ALLOW_MMX
|
||||||
|
|||||||
@ -23,13 +23,6 @@
|
|||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Last changed : $Date$
|
|
||||||
// File revision : $Revision: 4 $
|
|
||||||
//
|
|
||||||
// $Id$
|
|
||||||
//
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// License :
|
// License :
|
||||||
//
|
//
|
||||||
// SoundTouch audio processing library
|
// SoundTouch audio processing library
|
||||||
@ -71,7 +64,7 @@ using namespace soundtouch;
|
|||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
|
||||||
// Calculates cross correlation of two buffers
|
// Calculates cross correlation of two buffers
|
||||||
double TDStretchSSE::calcCrossCorr(const float *pV1, const float *pV2) const
|
double TDStretchSSE::calcCrossCorr(const float *pV1, const float *pV2, double &anorm)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
const float *pVec1;
|
const float *pVec1;
|
||||||
@ -87,13 +80,13 @@ double TDStretchSSE::calcCrossCorr(const float *pV1, const float *pV2) const
|
|||||||
// Compile-time define SOUNDTOUCH_ALLOW_NONEXACT_SIMD_OPTIMIZATION is provided
|
// Compile-time define SOUNDTOUCH_ALLOW_NONEXACT_SIMD_OPTIMIZATION is provided
|
||||||
// for choosing if this little cheating is allowed.
|
// for choosing if this little cheating is allowed.
|
||||||
|
|
||||||
#ifdef SOUNDTOUCH_ALLOW_NONEXACT_SIMD_OPTIMIZATION
|
#ifdef ST_SIMD_AVOID_UNALIGNED
|
||||||
// Little cheating allowed, return valid correlation only for
|
// Little cheating allowed, return valid correlation only for
|
||||||
// aligned locations, meaning every second round for stereo sound.
|
// aligned locations, meaning every second round for stereo sound.
|
||||||
|
|
||||||
#define _MM_LOAD _mm_load_ps
|
#define _MM_LOAD _mm_load_ps
|
||||||
|
|
||||||
if (((ulong)pV1) & 15) return -1e50; // skip unaligned locations
|
if (((ulongptr)pV1) & 15) return -1e50; // skip unaligned locations
|
||||||
|
|
||||||
#else
|
#else
|
||||||
// No cheating allowed, use unaligned load & take the resulting
|
// No cheating allowed, use unaligned load & take the resulting
|
||||||
@ -141,11 +134,11 @@ double TDStretchSSE::calcCrossCorr(const float *pV1, const float *pV2) const
|
|||||||
|
|
||||||
// return value = vSum[0] + vSum[1] + vSum[2] + vSum[3]
|
// return value = vSum[0] + vSum[1] + vSum[2] + vSum[3]
|
||||||
float *pvNorm = (float*)&vNorm;
|
float *pvNorm = (float*)&vNorm;
|
||||||
double norm = sqrt(pvNorm[0] + pvNorm[1] + pvNorm[2] + pvNorm[3]);
|
float norm = (pvNorm[0] + pvNorm[1] + pvNorm[2] + pvNorm[3]);
|
||||||
if (norm < 1e-9) norm = 1.0; // to avoid div by zero
|
anorm = norm;
|
||||||
|
|
||||||
float *pvSum = (float*)&vSum;
|
float *pvSum = (float*)&vSum;
|
||||||
return (double)(pvSum[0] + pvSum[1] + pvSum[2] + pvSum[3]) / norm;
|
return (double)(pvSum[0] + pvSum[1] + pvSum[2] + pvSum[3]) / sqrt(norm < 1e-9 ? 1.0 : norm);
|
||||||
|
|
||||||
/* This is approximately corresponding routine in C-language yet without normalization:
|
/* This is approximately corresponding routine in C-language yet without normalization:
|
||||||
double corr, norm;
|
double corr, norm;
|
||||||
@ -182,6 +175,16 @@ double TDStretchSSE::calcCrossCorr(const float *pV1, const float *pV2) const
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
double TDStretchSSE::calcCrossCorrAccumulate(const float *pV1, const float *pV2, double &norm)
|
||||||
|
{
|
||||||
|
// call usual calcCrossCorr function because SSE does not show big benefit of
|
||||||
|
// accumulating "norm" value, and also the "norm" rolling algorithm would get
|
||||||
|
// complicated due to SSE-specific alignment-vs-nonexact correlation rules.
|
||||||
|
return calcCrossCorr(pV1, pV2, norm);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// implementation of SSE optimized functions of class 'FIRFilter'
|
// implementation of SSE optimized functions of class 'FIRFilter'
|
||||||
@ -192,25 +195,22 @@ double TDStretchSSE::calcCrossCorr(const float *pV1, const float *pV2) const
|
|||||||
|
|
||||||
FIRFilterSSE::FIRFilterSSE() : FIRFilter()
|
FIRFilterSSE::FIRFilterSSE() : FIRFilter()
|
||||||
{
|
{
|
||||||
filterCoeffsAlign = NULL;
|
filterCoeffsAlign = nullptr;
|
||||||
filterCoeffsUnalign = NULL;
|
filterCoeffsUnalign = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
FIRFilterSSE::~FIRFilterSSE()
|
FIRFilterSSE::~FIRFilterSSE()
|
||||||
{
|
{
|
||||||
delete[] filterCoeffsUnalign;
|
delete[] filterCoeffsUnalign;
|
||||||
filterCoeffsAlign = NULL;
|
filterCoeffsAlign = nullptr;
|
||||||
filterCoeffsUnalign = NULL;
|
filterCoeffsUnalign = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// (overloaded) Calculates filter coefficients for SSE routine
|
// (overloaded) Calculates filter coefficients for SSE routine
|
||||||
void FIRFilterSSE::setCoefficients(const float *coeffs, uint newLength, uint uResultDivFactor)
|
void FIRFilterSSE::setCoefficients(const float *coeffs, uint newLength, uint uResultDivFactor)
|
||||||
{
|
{
|
||||||
uint i;
|
|
||||||
float fDivider;
|
|
||||||
|
|
||||||
FIRFilter::setCoefficients(coeffs, newLength, uResultDivFactor);
|
FIRFilter::setCoefficients(coeffs, newLength, uResultDivFactor);
|
||||||
|
|
||||||
// Scale the filter coefficients so that it won't be necessary to scale the filtering result
|
// Scale the filter coefficients so that it won't be necessary to scale the filtering result
|
||||||
@ -218,15 +218,15 @@ void FIRFilterSSE::setCoefficients(const float *coeffs, uint newLength, uint uRe
|
|||||||
// Ensure that filter coeffs array is aligned to 16-byte boundary
|
// Ensure that filter coeffs array is aligned to 16-byte boundary
|
||||||
delete[] filterCoeffsUnalign;
|
delete[] filterCoeffsUnalign;
|
||||||
filterCoeffsUnalign = new float[2 * newLength + 4];
|
filterCoeffsUnalign = new float[2 * newLength + 4];
|
||||||
filterCoeffsAlign = (float *)(((unsigned long)filterCoeffsUnalign + 15) & (ulong)-16);
|
filterCoeffsAlign = (float *)SOUNDTOUCH_ALIGN_POINTER_16(filterCoeffsUnalign);
|
||||||
|
|
||||||
fDivider = (float)resultDivider;
|
const float scale = ::pow(0.5, (int)resultDivFactor);
|
||||||
|
|
||||||
// rearrange the filter coefficients for mmx routines
|
// rearrange the filter coefficients for sse routines
|
||||||
for (i = 0; i < newLength; i ++)
|
for (auto i = 0U; i < newLength; i ++)
|
||||||
{
|
{
|
||||||
filterCoeffsAlign[2 * i + 0] =
|
filterCoeffsAlign[2 * i + 0] =
|
||||||
filterCoeffsAlign[2 * i + 1] = coeffs[i + 0] / fDivider;
|
filterCoeffsAlign[2 * i + 1] = coeffs[i] * scale;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -242,21 +242,24 @@ uint FIRFilterSSE::evaluateFilterStereo(float *dest, const float *source, uint n
|
|||||||
|
|
||||||
if (count < 2) return 0;
|
if (count < 2) return 0;
|
||||||
|
|
||||||
assert(source != NULL);
|
assert(source != nullptr);
|
||||||
assert(dest != NULL);
|
assert(dest != nullptr);
|
||||||
assert((length % 8) == 0);
|
assert((length % 8) == 0);
|
||||||
assert(filterCoeffsAlign != NULL);
|
assert(filterCoeffsAlign != nullptr);
|
||||||
assert(((ulong)filterCoeffsAlign) % 16 == 0);
|
assert(((ulongptr)filterCoeffsAlign) % 16 == 0);
|
||||||
|
|
||||||
// filter is evaluated for two stereo samples with each iteration, thus use of 'j += 2'
|
// filter is evaluated for two stereo samples with each iteration, thus use of 'j += 2'
|
||||||
|
#pragma omp parallel for
|
||||||
for (j = 0; j < count; j += 2)
|
for (j = 0; j < count; j += 2)
|
||||||
{
|
{
|
||||||
const float *pSrc;
|
const float *pSrc;
|
||||||
|
float *pDest;
|
||||||
const __m128 *pFil;
|
const __m128 *pFil;
|
||||||
__m128 sum1, sum2;
|
__m128 sum1, sum2;
|
||||||
uint i;
|
uint i;
|
||||||
|
|
||||||
pSrc = (const float*)source; // source audio data
|
pSrc = (const float*)source + j * 2; // source audio data
|
||||||
|
pDest = dest + j * 2; // destination audio data
|
||||||
pFil = (const __m128*)filterCoeffsAlign; // filter coefficients. NOTE: Assumes coefficients
|
pFil = (const __m128*)filterCoeffsAlign; // filter coefficients. NOTE: Assumes coefficients
|
||||||
// are aligned to 16-byte boundary
|
// are aligned to 16-byte boundary
|
||||||
sum1 = sum2 = _mm_setzero_ps();
|
sum1 = sum2 = _mm_setzero_ps();
|
||||||
@ -289,12 +292,10 @@ uint FIRFilterSSE::evaluateFilterStereo(float *dest, const float *source, uint n
|
|||||||
// to sum the two hi- and lo-floats of these registers together.
|
// to sum the two hi- and lo-floats of these registers together.
|
||||||
|
|
||||||
// post-shuffle & add the filtered values and store to dest.
|
// post-shuffle & add the filtered values and store to dest.
|
||||||
_mm_storeu_ps(dest, _mm_add_ps(
|
_mm_storeu_ps(pDest, _mm_add_ps(
|
||||||
_mm_shuffle_ps(sum1, sum2, _MM_SHUFFLE(1,0,3,2)), // s2_1 s2_0 s1_3 s1_2
|
_mm_shuffle_ps(sum1, sum2, _MM_SHUFFLE(1,0,3,2)), // s2_1 s2_0 s1_3 s1_2
|
||||||
_mm_shuffle_ps(sum1, sum2, _MM_SHUFFLE(3,2,1,0)) // s2_3 s2_2 s1_1 s1_0
|
_mm_shuffle_ps(sum1, sum2, _MM_SHUFFLE(3,2,1,0)) // s2_3 s2_2 s1_1 s1_0
|
||||||
));
|
));
|
||||||
source += 4;
|
|
||||||
dest += 4;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ideas for further improvement:
|
// Ideas for further improvement:
|
||||||
|
|||||||
115
source/SoundTouchDLL/DllTest/DllTest.cpp
Normal file
115
source/SoundTouchDLL/DllTest/DllTest.cpp
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
///
|
||||||
|
/// DllTest.cpp : This is small app main routine used for testing sound processing
|
||||||
|
/// with SoundTouch.dll API
|
||||||
|
///
|
||||||
|
/// Author : Copyright (c) Olli Parviainen
|
||||||
|
/// Author e-mail : oparviai 'at' iki.fi
|
||||||
|
/// SoundTouch WWW: http://www.surina.net/soundtouch
|
||||||
|
///
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <iostream>
|
||||||
|
#include <fstream>
|
||||||
|
#include "../SoundTouchDLL.h"
|
||||||
|
#include "../../SoundStretch/WavFile.h"
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
using namespace soundstretch;
|
||||||
|
|
||||||
|
// DllTest main
|
||||||
|
int wmain(int argc, const wchar_t *argv[])
|
||||||
|
{
|
||||||
|
// Check program arguments
|
||||||
|
if (argc < 4)
|
||||||
|
{
|
||||||
|
cout << "Too few arguments. Usage: DllTest [infile.wav] [outfile.wav] [sampletype]" << endl;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
wstring inFileName = argv[1];
|
||||||
|
wstring outFileName = argv[2];
|
||||||
|
wstring str_sampleType = argv[3];
|
||||||
|
|
||||||
|
bool floatSample;
|
||||||
|
if (str_sampleType == L"float")
|
||||||
|
{
|
||||||
|
floatSample = true;
|
||||||
|
}
|
||||||
|
else if (str_sampleType == L"short")
|
||||||
|
{
|
||||||
|
floatSample = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cerr << "Missing or invalid sampletype. Expected either short or float" << endl;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Open input & output WAV files
|
||||||
|
WavInFile inFile(inFileName);
|
||||||
|
int numChannels = inFile.getNumChannels();
|
||||||
|
int sampleRate = inFile.getSampleRate();
|
||||||
|
WavOutFile outFile(outFileName, sampleRate, inFile.getNumBits(), numChannels);
|
||||||
|
|
||||||
|
// Create SoundTouch DLL instance
|
||||||
|
HANDLE st = soundtouch_createInstance();
|
||||||
|
soundtouch_setChannels(st, numChannels);
|
||||||
|
soundtouch_setSampleRate(st, sampleRate);
|
||||||
|
soundtouch_setPitchSemiTones(st, 2);
|
||||||
|
|
||||||
|
cout << "processing with soundtouch.dll routines";
|
||||||
|
|
||||||
|
if (floatSample)
|
||||||
|
{
|
||||||
|
// Process file with SoundTouch.DLL float sample (default) API
|
||||||
|
float fbuffer[2048];
|
||||||
|
int nmax = 2048 / numChannels;
|
||||||
|
|
||||||
|
cout << " using float api ..." << endl;
|
||||||
|
while (inFile.eof() == false)
|
||||||
|
{
|
||||||
|
int n = inFile.read(fbuffer, nmax * numChannels) / numChannels;
|
||||||
|
soundtouch_putSamples(st, fbuffer, n);
|
||||||
|
do
|
||||||
|
{
|
||||||
|
n = soundtouch_receiveSamples(st, fbuffer, nmax);
|
||||||
|
outFile.write(fbuffer, n * numChannels);
|
||||||
|
} while (n > 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Process file with SoundTouch.DLL int16 (short) sample API.
|
||||||
|
// Notice that SoundTouch.dll does internally processing using floating
|
||||||
|
// point routines so the int16 API is not any faster, but provided for
|
||||||
|
// convenience.
|
||||||
|
short i16buffer[2048];
|
||||||
|
int nmax = 2048 / numChannels;
|
||||||
|
|
||||||
|
cout << " using i16 api ..." << endl;
|
||||||
|
while (inFile.eof() == false)
|
||||||
|
{
|
||||||
|
int n = inFile.read(i16buffer, nmax * numChannels) / numChannels;
|
||||||
|
soundtouch_putSamples_i16(st, i16buffer, n);
|
||||||
|
do
|
||||||
|
{
|
||||||
|
n = soundtouch_receiveSamples_i16(st, i16buffer, nmax);
|
||||||
|
outFile.write(i16buffer, n * numChannels);
|
||||||
|
} while (n > 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
soundtouch_destroyInstance(st);
|
||||||
|
cout << "done." << endl;
|
||||||
|
}
|
||||||
|
catch (const runtime_error &e)
|
||||||
|
{
|
||||||
|
cerr << e.what() << endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
180
source/SoundTouchDLL/DllTest/DllTest.vcxproj
Normal file
180
source/SoundTouchDLL/DllTest/DllTest.vcxproj
Normal file
@ -0,0 +1,180 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<ItemGroup Label="ProjectConfigurations">
|
||||||
|
<ProjectConfiguration Include="Debug|Win32">
|
||||||
|
<Configuration>Debug</Configuration>
|
||||||
|
<Platform>Win32</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
<ProjectConfiguration Include="Release|Win32">
|
||||||
|
<Configuration>Release</Configuration>
|
||||||
|
<Platform>Win32</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
<ProjectConfiguration Include="Debug|x64">
|
||||||
|
<Configuration>Debug</Configuration>
|
||||||
|
<Platform>x64</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
<ProjectConfiguration Include="Release|x64">
|
||||||
|
<Configuration>Release</Configuration>
|
||||||
|
<Platform>x64</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
</ItemGroup>
|
||||||
|
<PropertyGroup Label="Globals">
|
||||||
|
<ProjectGuid>{E3C0726F-28F4-4F0B-8183-B87CA60C063C}</ProjectGuid>
|
||||||
|
<Keyword>Win32Proj</Keyword>
|
||||||
|
<RootNamespace>DllTest</RootNamespace>
|
||||||
|
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||||
|
<ConfigurationType>Application</ConfigurationType>
|
||||||
|
<UseDebugLibraries>true</UseDebugLibraries>
|
||||||
|
<PlatformToolset>v142</PlatformToolset>
|
||||||
|
<CharacterSet>Unicode</CharacterSet>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||||
|
<ConfigurationType>Application</ConfigurationType>
|
||||||
|
<UseDebugLibraries>false</UseDebugLibraries>
|
||||||
|
<PlatformToolset>v142</PlatformToolset>
|
||||||
|
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||||
|
<CharacterSet>Unicode</CharacterSet>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||||
|
<ConfigurationType>Application</ConfigurationType>
|
||||||
|
<UseDebugLibraries>true</UseDebugLibraries>
|
||||||
|
<PlatformToolset>v142</PlatformToolset>
|
||||||
|
<CharacterSet>Unicode</CharacterSet>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||||
|
<ConfigurationType>Application</ConfigurationType>
|
||||||
|
<UseDebugLibraries>false</UseDebugLibraries>
|
||||||
|
<PlatformToolset>v142</PlatformToolset>
|
||||||
|
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||||
|
<CharacterSet>Unicode</CharacterSet>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||||
|
<ImportGroup Label="ExtensionSettings">
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Label="Shared">
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||||
|
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||||
|
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||||
|
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||||
|
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||||
|
</ImportGroup>
|
||||||
|
<PropertyGroup Label="UserMacros" />
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||||
|
<LinkIncremental>true</LinkIncremental>
|
||||||
|
<OutDir>$(Platform)\$(Configuration)\</OutDir>
|
||||||
|
<IntDir>$(Platform)\$(Configuration)\</IntDir>
|
||||||
|
<TargetName>$(ProjectName)D</TargetName>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||||
|
<LinkIncremental>true</LinkIncremental>
|
||||||
|
<OutDir>$(Platform)\$(Configuration)\</OutDir>
|
||||||
|
<TargetName>$(ProjectName)D_x64</TargetName>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||||
|
<LinkIncremental>false</LinkIncremental>
|
||||||
|
<OutDir>$(Platform)\$(Configuration)\</OutDir>
|
||||||
|
<IntDir>$(Platform)\$(Configuration)\</IntDir>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||||
|
<LinkIncremental>false</LinkIncremental>
|
||||||
|
<OutDir>$(Platform)\$(Configuration)\</OutDir>
|
||||||
|
<TargetName>$(ProjectName)_x64</TargetName>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||||
|
<ClCompile>
|
||||||
|
<PrecompiledHeader>
|
||||||
|
</PrecompiledHeader>
|
||||||
|
<WarningLevel>Level3</WarningLevel>
|
||||||
|
<Optimization>Disabled</Optimization>
|
||||||
|
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
|
<AdditionalIncludeDirectories>../../../include</AdditionalIncludeDirectories>
|
||||||
|
<DisableSpecificWarnings>4996</DisableSpecificWarnings>
|
||||||
|
</ClCompile>
|
||||||
|
<Link>
|
||||||
|
<SubSystem>Console</SubSystem>
|
||||||
|
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||||
|
<AdditionalDependencies>..\..\..\lib\SoundTouchDllD.lib</AdditionalDependencies>
|
||||||
|
<OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
|
||||||
|
</Link>
|
||||||
|
</ItemDefinitionGroup>
|
||||||
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||||
|
<ClCompile>
|
||||||
|
<PrecompiledHeader>
|
||||||
|
</PrecompiledHeader>
|
||||||
|
<WarningLevel>Level3</WarningLevel>
|
||||||
|
<Optimization>Disabled</Optimization>
|
||||||
|
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
|
<AdditionalIncludeDirectories>../../../include</AdditionalIncludeDirectories>
|
||||||
|
<DisableSpecificWarnings>4996</DisableSpecificWarnings>
|
||||||
|
</ClCompile>
|
||||||
|
<Link>
|
||||||
|
<SubSystem>Console</SubSystem>
|
||||||
|
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||||
|
<AdditionalDependencies>..\..\..\lib\SoundTouchDllD_x64.lib</AdditionalDependencies>
|
||||||
|
</Link>
|
||||||
|
</ItemDefinitionGroup>
|
||||||
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||||
|
<ClCompile>
|
||||||
|
<WarningLevel>Level3</WarningLevel>
|
||||||
|
<PrecompiledHeader>
|
||||||
|
</PrecompiledHeader>
|
||||||
|
<Optimization>MaxSpeed</Optimization>
|
||||||
|
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||||
|
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||||
|
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
|
<AdditionalIncludeDirectories>../../../include</AdditionalIncludeDirectories>
|
||||||
|
<DisableSpecificWarnings>4996</DisableSpecificWarnings>
|
||||||
|
</ClCompile>
|
||||||
|
<Link>
|
||||||
|
<SubSystem>Console</SubSystem>
|
||||||
|
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||||
|
<OptimizeReferences>true</OptimizeReferences>
|
||||||
|
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||||
|
<AdditionalDependencies>..\..\..\lib\SoundTouchDll.lib</AdditionalDependencies>
|
||||||
|
</Link>
|
||||||
|
</ItemDefinitionGroup>
|
||||||
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||||
|
<ClCompile>
|
||||||
|
<WarningLevel>Level3</WarningLevel>
|
||||||
|
<PrecompiledHeader>
|
||||||
|
</PrecompiledHeader>
|
||||||
|
<Optimization>MaxSpeed</Optimization>
|
||||||
|
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||||
|
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||||
|
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
|
<AdditionalIncludeDirectories>../../../include</AdditionalIncludeDirectories>
|
||||||
|
<DisableSpecificWarnings>4996</DisableSpecificWarnings>
|
||||||
|
</ClCompile>
|
||||||
|
<Link>
|
||||||
|
<SubSystem>Console</SubSystem>
|
||||||
|
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||||
|
<OptimizeReferences>true</OptimizeReferences>
|
||||||
|
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||||
|
<AdditionalDependencies>..\..\..\lib\SoundTouchDll_x64.lib</AdditionalDependencies>
|
||||||
|
</Link>
|
||||||
|
</ItemDefinitionGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Text Include="ReadMe.txt" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ClInclude Include="stdafx.h" />
|
||||||
|
<ClInclude Include="targetver.h" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ClCompile Include="..\..\SoundStretch\WavFile.cpp" />
|
||||||
|
<ClCompile Include="DllTest.cpp" />
|
||||||
|
</ItemGroup>
|
||||||
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||||
|
<ImportGroup Label="ExtensionTargets">
|
||||||
|
</ImportGroup>
|
||||||
|
</Project>
|
||||||
10
source/SoundTouchDLL/LazarusTest/README.txt
Normal file
10
source/SoundTouchDLL/LazarusTest/README.txt
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
This is Lazarus Pascal example that loads the SoundTouch dynamic-load library
|
||||||
|
and queries the library version as a simple example how to load SoundTouch from
|
||||||
|
Pascal / Lazarus.
|
||||||
|
|
||||||
|
Set the SoundTouch dynamic library file name in the 'InitDLL' procedure of
|
||||||
|
file 'SoundTouchDLL.pas' depending on if you're building for Windows or Linux.
|
||||||
|
|
||||||
|
The example expects the the 'libSoundTouchDll.so' (linux) or 'SoundTouch.dll' (Windows)
|
||||||
|
library binary files is found within this project directory, either via soft-link
|
||||||
|
(in Linux) or as a copied file.
|
||||||
@ -2,13 +2,8 @@ unit SoundTouchDLL;
|
|||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// SoundTouch.dll wrapper for accessing SoundTouch routines from Delphi/Pascal
|
// SoundTouch.dll / libSoundTouchDll.so wrapper for accessing SoundTouch
|
||||||
//
|
// routines from Delphi/Pascal/Lazarus
|
||||||
// Module Author : Christian Budde
|
|
||||||
//
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// $Id$
|
|
||||||
//
|
//
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
@ -35,58 +30,60 @@ unit SoundTouchDLL;
|
|||||||
|
|
||||||
interface
|
interface
|
||||||
|
|
||||||
uses
|
//uses
|
||||||
Windows;
|
//Windows;
|
||||||
|
|
||||||
type
|
type
|
||||||
TSoundTouchHandle = THandle;
|
TSoundTouchHandle = THandle;
|
||||||
|
|
||||||
// Create a new instance of SoundTouch processor.
|
// Create a new instance of SoundTouch processor.
|
||||||
TSoundTouchCreateInstance = function : TSoundTouchHandle; stdcall;
|
TSoundTouchCreateInstance = function : TSoundTouchHandle; cdecl;
|
||||||
|
|
||||||
// Destroys a SoundTouch processor instance.
|
// Destroys a SoundTouch processor instance.
|
||||||
TSoundTouchDestroyInstance = procedure (Handle: TSoundTouchHandle); stdcall;
|
TSoundTouchDestroyInstance = procedure (Handle: TSoundTouchHandle); cdecl;
|
||||||
|
|
||||||
// Get SoundTouch library version string
|
// Get SoundTouch library version string
|
||||||
TSoundTouchGetVersionString = function : PChar; stdcall;
|
TSoundTouchGetVersionString = function : PAnsiChar; cdecl;
|
||||||
|
|
||||||
|
// Get SoundTouch library version string 2
|
||||||
|
TSoundTouchGetVersionString2 = procedure(VersionString : PAnsiChar; BufferSize : Integer); cdecl;
|
||||||
|
|
||||||
// Get SoundTouch library version Id
|
// Get SoundTouch library version Id
|
||||||
TSoundTouchGetVersionId = function : Cardinal; stdcall;
|
TSoundTouchGetVersionId = function : Cardinal; cdecl;
|
||||||
|
|
||||||
// Sets new rate control value. Normal rate = 1.0, smaller values
|
// Sets new rate control value. Normal rate = 1.0, smaller values
|
||||||
// represent slower rate, larger faster rates.
|
// represent slower rate, larger faster rates.
|
||||||
TSoundTouchSetRate = procedure (Handle: TSoundTouchHandle; newRate: Single); stdcall;
|
TSoundTouchSetRate = procedure (Handle: TSoundTouchHandle; NewRate: Single); cdecl;
|
||||||
|
|
||||||
// Sets new tempo control value. Normal tempo = 1.0, smaller values
|
// Sets new tempo control value. Normal tempo = 1.0, smaller values
|
||||||
// represent slower tempo, larger faster tempo.
|
// represent slower tempo, larger faster tempo.
|
||||||
TSoundTouchSetTempo = procedure (Handle: TSoundTouchHandle; newTempo: Single); stdcall;
|
TSoundTouchSetTempo = procedure (Handle: TSoundTouchHandle; NewTempo: Single); cdecl;
|
||||||
|
|
||||||
// Sets new rate control value as a difference in percents compared
|
// Sets new rate control value as a difference in percents compared
|
||||||
// to the original rate (-50 .. +100 %);
|
// to the original rate (-50 .. +100 %);
|
||||||
TSoundTouchSetRateChange = procedure (Handle: TSoundTouchHandle; newRate: Single); stdcall;
|
TSoundTouchSetRateChange = procedure (Handle: TSoundTouchHandle; NewRate: Single); cdecl;
|
||||||
|
|
||||||
// Sets new tempo control value as a difference in percents compared
|
// Sets new tempo control value as a difference in percents compared
|
||||||
// to the original tempo (-50 .. +100 %);
|
// to the original tempo (-50 .. +100 %);
|
||||||
TSoundTouchSetTempoChange = procedure (Handle: TSoundTouchHandle; newTempo: Single); stdcall;
|
TSoundTouchSetTempoChange = procedure (Handle: TSoundTouchHandle; NewTempo: Single); cdecl;
|
||||||
|
|
||||||
// Sets new pitch control value. Original pitch = 1.0, smaller values
|
// Sets new pitch control value. Original pitch = 1.0, smaller values
|
||||||
// represent lower pitches, larger values higher pitch.
|
// represent lower pitches, larger values higher pitch.
|
||||||
TSoundTouchSetPitch = procedure (Handle: TSoundTouchHandle; newPitch: Single); stdcall;
|
TSoundTouchSetPitch = procedure (Handle: TSoundTouchHandle; NewPitch: Single); cdecl;
|
||||||
|
|
||||||
// Sets pitch change in octaves compared to the original pitch
|
// Sets pitch change in octaves compared to the original pitch
|
||||||
// (-1.00 .. +1.00);
|
// (-1.00 .. +1.00);
|
||||||
TSoundTouchSetPitchOctaves = procedure (Handle: TSoundTouchHandle; newPitch: Single); stdcall;
|
TSoundTouchSetPitchOctaves = procedure (Handle: TSoundTouchHandle; NewPitch: Single); cdecl;
|
||||||
|
|
||||||
// Sets pitch change in semi-tones compared to the original pitch
|
// Sets pitch change in semi-tones compared to the original pitch
|
||||||
// (-12 .. +12);
|
// (-12 .. +12);
|
||||||
TSoundTouchSetPitchSemiTones = procedure (Handle: TSoundTouchHandle; newPitch: Single); stdcall;
|
TSoundTouchSetPitchSemiTones = procedure (Handle: TSoundTouchHandle; NewPitch: Single); cdecl;
|
||||||
|
|
||||||
|
|
||||||
// Sets the number of channels, 1 = mono, 2 = stereo
|
// Sets the number of channels, 1 = mono, 2 = stereo
|
||||||
TSoundTouchSetChannels = procedure (Handle: TSoundTouchHandle; numChannels: Cardinal); stdcall;
|
TSoundTouchSetChannels = procedure (Handle: TSoundTouchHandle; NumChannels: Cardinal); cdecl;
|
||||||
|
|
||||||
// Sets sample rate.
|
// Sets sample rate.
|
||||||
TSoundTouchSetSampleRate = procedure (Handle: TSoundTouchHandle; SampleRate: Cardinal); stdcall;
|
TSoundTouchSetSampleRate = procedure (Handle: TSoundTouchHandle; SampleRate: Cardinal); cdecl;
|
||||||
|
|
||||||
// Flushes the last samples from the processing pipeline to the output.
|
// Flushes the last samples from the processing pipeline to the output.
|
||||||
// Clears also the internal processing buffers.
|
// Clears also the internal processing buffers.
|
||||||
@ -95,7 +92,7 @@ type
|
|||||||
// stream. This function may introduce additional blank samples in the end
|
// stream. This function may introduce additional blank samples in the end
|
||||||
// of the sound stream, and thus it
|
// of the sound stream, and thus it
|
||||||
// in the middle of a sound stream.
|
// in the middle of a sound stream.
|
||||||
TSoundTouchFlush = procedure (Handle: TSoundTouchHandle); stdcall;
|
TSoundTouchFlush = procedure (Handle: TSoundTouchHandle); cdecl;
|
||||||
|
|
||||||
// Adds 'numSamples' pcs of samples from the 'samples' memory position into
|
// Adds 'numSamples' pcs of samples from the 'samples' memory position into
|
||||||
// the input of the object. Notice that sample rate _has_to_ be set before
|
// the input of the object. Notice that sample rate _has_to_ be set before
|
||||||
@ -105,53 +102,64 @@ type
|
|||||||
NumSamples: Cardinal //< Number of samples in buffer. Notice
|
NumSamples: Cardinal //< Number of samples in buffer. Notice
|
||||||
//< that in case of stereo-sound a single sample
|
//< that in case of stereo-sound a single sample
|
||||||
//< contains data for both channels.
|
//< contains data for both channels.
|
||||||
); stdcall;
|
); cdecl;
|
||||||
|
|
||||||
|
TSoundTouchPutSamplesI16 = procedure (Handle: TSoundTouchHandle;
|
||||||
|
const Samples: Pint16; //< Pointer to sample buffer.
|
||||||
|
NumSamples: Cardinal //< Number of samples in buffer. Notice
|
||||||
|
//< that in case of stereo-sound a single sample
|
||||||
|
//< contains data for both channels.
|
||||||
|
); cdecl;
|
||||||
|
|
||||||
// Clears all the samples in the object's output and internal processing
|
// Clears all the samples in the object's output and internal processing
|
||||||
// buffers.
|
// buffers.
|
||||||
TSoundTouchClear = procedure (Handle: TSoundTouchHandle); stdcall;
|
TSoundTouchClear = procedure (Handle: TSoundTouchHandle); cdecl;
|
||||||
|
|
||||||
// Changes a setting controlling the processing system behaviour. See the
|
// Changes a setting controlling the processing system behaviour. See the
|
||||||
// 'SETTING_...' defines for available setting ID's.
|
// 'SETTING_...' defines for available setting ID's.
|
||||||
//
|
//
|
||||||
// \return 'TRUE' if the setting was succesfully changed
|
// \return 'TRUE' if the setting was successfully changed
|
||||||
TSoundTouchSetSetting = function (Handle: TSoundTouchHandle;
|
TSoundTouchSetSetting = function (Handle: TSoundTouchHandle;
|
||||||
SettingId: Integer; //< Setting ID number. see SETTING_... defines.
|
SettingId: Integer; //< Setting ID number. see SETTING_... defines.
|
||||||
Value: Integer //< New setting value.
|
Value: Integer //< New setting value.
|
||||||
): Boolean; stdcall;
|
): Boolean; cdecl;
|
||||||
|
|
||||||
// Reads a setting controlling the processing system behaviour. See the
|
// Reads a setting controlling the processing system behaviour. See the
|
||||||
// 'SETTING_...' defines for available setting ID's.
|
// 'SETTING_...' defines for available setting ID's.
|
||||||
//
|
//
|
||||||
// \return the setting value.
|
// \return the setting value.
|
||||||
TSoundTouchGetSetting = function (Handle: TSoundTouchHandle;
|
TSoundTouchGetSetting = function (Handle: TSoundTouchHandle;
|
||||||
settingId: Integer //< Setting ID number, see SETTING_... defines.
|
SettingId: Integer //< Setting ID number, see SETTING_... defines.
|
||||||
): Integer; stdcall;
|
): Integer; cdecl;
|
||||||
|
|
||||||
|
|
||||||
// Returns number of samples currently unprocessed.
|
// Returns number of samples currently unprocessed.
|
||||||
TSoundTouchNumUnprocessedSamples = function (Handle: TSoundTouchHandle): Cardinal; stdcall;
|
TSoundTouchNumUnprocessedSamples = function (Handle: TSoundTouchHandle): Cardinal; cdecl;
|
||||||
|
|
||||||
// Adjusts book-keeping so that given number of samples are removed from beginning of the
|
/// Receive ready samples from the processing pipeline.
|
||||||
// sample buffer without copying them anywhere.
|
///
|
||||||
//
|
/// if called with outBuffer=nullptr, just reduces amount of ready samples within the pipeline.
|
||||||
// Used to reduce the number of samples in the buffer when accessing the sample buffer directly
|
|
||||||
// with 'ptrBegin' function.
|
|
||||||
TSoundTouchReceiveSamples = function (Handle: TSoundTouchHandle;
|
TSoundTouchReceiveSamples = function (Handle: TSoundTouchHandle;
|
||||||
outBuffer: PSingle; //< Buffer where to copy output samples.
|
OutBuffer: PSingle; //< Buffer where to copy output samples.
|
||||||
maxSamples: Integer //< How many samples to receive at max.
|
MaxSamples: Integer //< How many samples to receive at max.
|
||||||
): Cardinal; stdcall;
|
): Cardinal; cdecl;
|
||||||
|
|
||||||
|
/// int16 version of soundtouch_receiveSamples(): This converts internal float samples
|
||||||
|
/// into int16 (short) return data type
|
||||||
|
TSoundTouchReceiveSamplesI16 = function (Handle: TSoundTouchHandle;
|
||||||
|
OutBuffer: int16; //< Buffer where to copy output samples.
|
||||||
|
MaxSamples: Integer //< How many samples to receive at max.
|
||||||
|
): Cardinal; cdecl;
|
||||||
// Returns number of samples currently available.
|
// Returns number of samples currently available.
|
||||||
TSoundTouchNumSamples = function (Handle: TSoundTouchHandle): Cardinal; stdcall;
|
TSoundTouchNumSamples = function (Handle: TSoundTouchHandle): Cardinal; cdecl;
|
||||||
|
|
||||||
// Returns nonzero if there aren't any samples available for outputting.
|
// Returns nonzero if there aren't any samples available for outputting.
|
||||||
TSoundTouchIsEmpty = function (Handle: TSoundTouchHandle): Integer; stdcall;
|
TSoundTouchIsEmpty = function (Handle: TSoundTouchHandle): Integer; cdecl;
|
||||||
|
|
||||||
var
|
var
|
||||||
SoundTouchCreateInstance : TSoundTouchCreateInstance;
|
SoundTouchCreateInstance : TSoundTouchCreateInstance;
|
||||||
SoundTouchDestroyInstance : TSoundTouchDestroyInstance;
|
SoundTouchDestroyInstance : TSoundTouchDestroyInstance;
|
||||||
SoundTouchGetVersionString : TSoundTouchGetVersionString;
|
SoundTouchGetVersionString : TSoundTouchGetVersionString;
|
||||||
|
SoundTouchGetVersionString2 : TSoundTouchGetVersionString2;
|
||||||
SoundTouchGetVersionId : TSoundTouchGetVersionId;
|
SoundTouchGetVersionId : TSoundTouchGetVersionId;
|
||||||
SoundTouchSetRate : TSoundTouchSetRate;
|
SoundTouchSetRate : TSoundTouchSetRate;
|
||||||
SoundTouchSetTempo : TSoundTouchSetTempo;
|
SoundTouchSetTempo : TSoundTouchSetTempo;
|
||||||
@ -164,11 +172,13 @@ var
|
|||||||
SoundTouchSetSampleRate : TSoundTouchSetSampleRate;
|
SoundTouchSetSampleRate : TSoundTouchSetSampleRate;
|
||||||
SoundTouchFlush : TSoundTouchFlush;
|
SoundTouchFlush : TSoundTouchFlush;
|
||||||
SoundTouchPutSamples : TSoundTouchPutSamples;
|
SoundTouchPutSamples : TSoundTouchPutSamples;
|
||||||
|
SoundTouchPutSamplesI16 : TSoundTouchPutSamplesI16;
|
||||||
SoundTouchClear : TSoundTouchClear;
|
SoundTouchClear : TSoundTouchClear;
|
||||||
SoundTouchSetSetting : TSoundTouchSetSetting;
|
SoundTouchSetSetting : TSoundTouchSetSetting;
|
||||||
SoundTouchGetSetting : TSoundTouchGetSetting;
|
SoundTouchGetSetting : TSoundTouchGetSetting;
|
||||||
SoundTouchNumUnprocessedSamples : TSoundTouchNumUnprocessedSamples;
|
SoundTouchNumUnprocessedSamples : TSoundTouchNumUnprocessedSamples;
|
||||||
SoundTouchReceiveSamples : TSoundTouchReceiveSamples;
|
SoundTouchReceiveSamples : TSoundTouchReceiveSamples;
|
||||||
|
SoundTouchReceiveSamplesI16 : TSoundTouchReceiveSamplesI16;
|
||||||
SoundTouchNumSamples : TSoundTouchNumSamples;
|
SoundTouchNumSamples : TSoundTouchNumSamples;
|
||||||
SoundTouchIsEmpty : TSoundTouchIsEmpty;
|
SoundTouchIsEmpty : TSoundTouchIsEmpty;
|
||||||
|
|
||||||
@ -210,10 +220,10 @@ type
|
|||||||
procedure Clear; virtual;
|
procedure Clear; virtual;
|
||||||
|
|
||||||
procedure PutSamples(const Samples: PSingle; const NumSamples: Cardinal);
|
procedure PutSamples(const Samples: PSingle; const NumSamples: Cardinal);
|
||||||
function ReceiveSamples(const outBuffer: PSingle; const maxSamples: Integer): Cardinal;
|
function ReceiveSamples(const OutBuffer: PSingle; const MaxSamples: Integer): Cardinal;
|
||||||
|
|
||||||
function SetSetting(const SettingId: Integer; const Value: Integer): Boolean;
|
function SetSetting(const SettingId: Integer; const Value: Integer): Boolean;
|
||||||
function GetSetting(const settingId: Integer): Integer;
|
function GetSetting(const SettingId: Integer): Integer;
|
||||||
|
|
||||||
property VersionString: string read GetVersionString;
|
property VersionString: string read GetVersionString;
|
||||||
property VersionID: Cardinal read GetVersionId;
|
property VersionID: Cardinal read GetVersionId;
|
||||||
@ -231,17 +241,17 @@ type
|
|||||||
property IsEmpty: Integer read GetIsEmpty;
|
property IsEmpty: Integer read GetIsEmpty;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
implementation
|
// list of exported functions and procedures
|
||||||
|
function IsSoundTouchLoaded: Boolean;
|
||||||
|
|
||||||
uses
|
implementation
|
||||||
SysUtils;
|
|
||||||
|
|
||||||
{ TSoundTouch }
|
{ TSoundTouch }
|
||||||
|
|
||||||
constructor TSoundTouch.Create;
|
constructor TSoundTouch.Create;
|
||||||
begin
|
begin
|
||||||
inherited;
|
inherited;
|
||||||
FHandle := SoundTouchCreateInstance;
|
FHandle := SoundTouchCreateInstance();
|
||||||
FRate := 1;
|
FRate := 1;
|
||||||
FTempo := 1;
|
FTempo := 1;
|
||||||
FPitch := 1;
|
FPitch := 1;
|
||||||
@ -299,12 +309,12 @@ end;
|
|||||||
|
|
||||||
class function TSoundTouch.GetVersionId: Cardinal;
|
class function TSoundTouch.GetVersionId: Cardinal;
|
||||||
begin
|
begin
|
||||||
result := SoundTouchGetVersionId;
|
result := SoundTouchGetVersionId();
|
||||||
end;
|
end;
|
||||||
|
|
||||||
class function TSoundTouch.GetVersionString: string;
|
class function TSoundTouch.GetVersionString: string;
|
||||||
begin
|
begin
|
||||||
result := StrPas(SoundTouchGetVersionString);
|
result := StrPas(SoundTouchGetVersionString());
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TSoundTouch.SetChannels(const Value: Cardinal);
|
procedure TSoundTouch.SetChannels(const Value: Cardinal);
|
||||||
@ -347,10 +357,10 @@ begin
|
|||||||
SoundTouchSetRate(FHandle, FRate);
|
SoundTouchSetRate(FHandle, FRate);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TSoundTouch.ReceiveSamples(const outBuffer: PSingle;
|
function TSoundTouch.ReceiveSamples(const OutBuffer: PSingle;
|
||||||
const maxSamples: Integer): Cardinal;
|
const MaxSamples: Integer): Cardinal;
|
||||||
begin
|
begin
|
||||||
result := SoundTouchReceiveSamples(FHandle, outBuffer, maxSamples);
|
result := SoundTouchReceiveSamples(FHandle, OutBuffer, MaxSamples);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TSoundTouch.SetPitchChange(const Value: Single);
|
procedure TSoundTouch.SetPitchChange(const Value: Single);
|
||||||
@ -418,36 +428,55 @@ begin
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
var
|
var
|
||||||
SoundTouchLibHandle: HINST;
|
SoundTouchLibHandle: THandle;
|
||||||
SoundTouchDLL: PAnsiChar = 'SoundTouch.DLL';
|
SoundTouchDLLFile: AnsiString = 'libSoundTouchDll.so';
|
||||||
|
//SoundTouchDLLFile: AnsiString = 'SoundTouch.dll';
|
||||||
|
|
||||||
|
// bpm detect functions. untested -- if these don't work then remove:
|
||||||
|
bpm_createInstance: function(chan: int32; sampleRate : int32): THandle; cdecl;
|
||||||
|
bpm_destroyInstance: procedure(h: THandle); cdecl;
|
||||||
|
bpm_getBpm: function(h: THandle): Single; cdecl;
|
||||||
|
bpm_putSamples: procedure(h: THandle; const samples: PSingle; numSamples: cardinal); cdecl;
|
||||||
|
|
||||||
procedure InitDLL;
|
procedure InitDLL;
|
||||||
begin
|
begin
|
||||||
SoundTouchLibHandle := LoadLibrary(SoundTouchDLL);
|
{$ifdef mswindows} // Windows
|
||||||
|
SoundTouchLibHandle := LoadLibrary('.\SoundTouchDll.dll');
|
||||||
|
{$else} // Unix
|
||||||
|
SoundTouchLibHandle := LoadLibrary('./libSoundTouchDll.so');
|
||||||
|
{$endif}
|
||||||
if SoundTouchLibHandle <> 0 then
|
if SoundTouchLibHandle <> 0 then
|
||||||
try
|
try
|
||||||
SoundTouchCreateInstance := GetProcAddress(SoundTouchLibHandle, PAnsiChar( 2)); //'soundtouch_createInstance');
|
Pointer(SoundTouchCreateInstance) := GetProcAddress(SoundTouchLibHandle, 'soundtouch_createInstance');
|
||||||
SoundTouchDestroyInstance := GetProcAddress(SoundTouchLibHandle, PAnsiChar( 3)); //'soundtouch_destroyInstance');
|
Pointer(SoundTouchDestroyInstance) := GetProcAddress(SoundTouchLibHandle, 'soundtouch_destroyInstance');
|
||||||
SoundTouchGetVersionString := GetProcAddress(SoundTouchLibHandle, PAnsiChar( 7)); //'soundtouch_getVersionString');
|
Pointer(SoundTouchGetVersionString) := GetProcAddress(SoundTouchLibHandle, 'soundtouch_getVersionString');
|
||||||
SoundTouchGetVersionId := GetProcAddress(SoundTouchLibHandle, PAnsiChar( 6)); //'soundtouch_getVersionId');
|
Pointer(SoundTouchGetVersionString2) := GetProcAddress(SoundTouchLibHandle, 'soundtouch_getVersionString2');
|
||||||
SoundTouchSetRate := GetProcAddress(SoundTouchLibHandle, PAnsiChar(17)); //'soundtouch_setRate');
|
Pointer(SoundTouchGetVersionId) := GetProcAddress(SoundTouchLibHandle, 'soundtouch_getVersionId');
|
||||||
SoundTouchSetTempo := GetProcAddress(SoundTouchLibHandle, PAnsiChar(21)); //'soundtouch_setTempo');
|
Pointer(SoundTouchSetRate) := GetProcAddress(SoundTouchLibHandle, 'soundtouch_setRate');
|
||||||
SoundTouchSetRateChange := GetProcAddress(SoundTouchLibHandle, PAnsiChar(18)); //'soundtouch_setRateChange');
|
Pointer(SoundTouchSetTempo) := GetProcAddress(SoundTouchLibHandle, 'soundtouch_setTempo');
|
||||||
SoundTouchSetTempoChange := GetProcAddress(SoundTouchLibHandle, PAnsiChar(22)); //'soundtouch_setTempoChange');
|
Pointer(SoundTouchSetRateChange) := GetProcAddress(SoundTouchLibHandle, 'soundtouch_setRateChange');
|
||||||
SoundTouchSetPitch := GetProcAddress(SoundTouchLibHandle, PAnsiChar(14)); //'soundtouch_setPitch');
|
Pointer(SoundTouchSetTempoChange) := GetProcAddress(SoundTouchLibHandle, 'soundtouch_setTempoChange');
|
||||||
SoundTouchSetPitchOctaves := GetProcAddress(SoundTouchLibHandle, PAnsiChar(15)); //'soundtouch_setPitchOctaves');
|
Pointer(SoundTouchSetPitch) := GetProcAddress(SoundTouchLibHandle, 'soundtouch_setPitch');
|
||||||
SoundTouchSetPitchSemiTones := GetProcAddress(SoundTouchLibHandle, PAnsiChar(16)); //'soundtouch_setPitchSemiTones');
|
Pointer(SoundTouchSetPitchOctaves) := GetProcAddress(SoundTouchLibHandle, 'soundtouch_setPitchOctaves');
|
||||||
SoundTouchSetChannels := GetProcAddress(SoundTouchLibHandle, PAnsiChar(13)); //'soundtouch_setChannels');
|
Pointer(SoundTouchSetPitchSemiTones) := GetProcAddress(SoundTouchLibHandle, 'soundtouch_setPitchSemiTones');
|
||||||
SoundTouchSetSampleRate := GetProcAddress(SoundTouchLibHandle, PAnsiChar(19)); //'soundtouch_setSampleRate');
|
Pointer(SoundTouchSetChannels) := GetProcAddress(SoundTouchLibHandle, 'soundtouch_setChannels');
|
||||||
SoundTouchFlush := GetProcAddress(SoundTouchLibHandle, PAnsiChar(4)); //'soundtouch_flush');
|
Pointer(SoundTouchSetSampleRate) := GetProcAddress(SoundTouchLibHandle, 'soundtouch_setSampleRate');
|
||||||
SoundTouchPutSamples := GetProcAddress(SoundTouchLibHandle, PAnsiChar(11)); //'soundtouch_putSamples');
|
Pointer(SoundTouchFlush) := GetProcAddress(SoundTouchLibHandle, 'soundtouch_flush');
|
||||||
SoundTouchClear := GetProcAddress(SoundTouchLibHandle, PAnsiChar(1)); //'soundtouch_clear');
|
Pointer(SoundTouchPutSamples) := GetProcAddress(SoundTouchLibHandle, 'soundtouch_putSamples');
|
||||||
SoundTouchSetSetting := GetProcAddress(SoundTouchLibHandle, PAnsiChar(20)); //'soundtouch_SetSetting');
|
Pointer(SoundTouchPutSamplesI16) := GetProcAddress(SoundTouchLibHandle, 'soundtouch_putSamples_i16');
|
||||||
SoundTouchGetSetting := GetProcAddress(SoundTouchLibHandle, PAnsiChar(5)); //'soundtouch_setSetting');
|
Pointer(SoundTouchClear) := GetProcAddress(SoundTouchLibHandle, 'soundtouch_clear');
|
||||||
SoundTouchNumUnprocessedSamples := GetProcAddress(SoundTouchLibHandle, PAnsiChar(10)); //'soundtouch_numUnprocessedSamples');
|
Pointer(SoundTouchSetSetting) := GetProcAddress(SoundTouchLibHandle, 'soundtouch_SetSetting');
|
||||||
SoundTouchReceiveSamples := GetProcAddress(SoundTouchLibHandle, PAnsiChar(12)); //'soundtouch_receiveSamples');
|
Pointer(SoundTouchGetSetting) := GetProcAddress(SoundTouchLibHandle, 'soundtouch_setSetting');
|
||||||
SoundTouchNumSamples := GetProcAddress(SoundTouchLibHandle, PAnsiChar(9)); //'soundtouch_numSamples');
|
Pointer(SoundTouchNumUnprocessedSamples) := GetProcAddress(SoundTouchLibHandle, 'soundtouch_numUnprocessedSamples');
|
||||||
SoundTouchIsEmpty := GetProcAddress(SoundTouchLibHandle, PAnsiChar(8)); //'soundtouch_isEmpty');
|
Pointer(SoundTouchReceiveSamples) := GetProcAddress(SoundTouchLibHandle, 'soundtouch_receiveSamples');
|
||||||
|
Pointer(SoundTouchReceiveSamplesI16) := GetProcAddress(SoundTouchLibHandle, 'soundtouch_receiveSamples_i16');
|
||||||
|
Pointer(SoundTouchNumSamples) := GetProcAddress(SoundTouchLibHandle, 'soundtouch_numSamples');
|
||||||
|
Pointer(SoundTouchIsEmpty) := GetProcAddress(SoundTouchLibHandle, 'soundtouch_isEmpty');
|
||||||
|
|
||||||
|
Pointer(bpm_createInstance) :=GetProcAddress(SoundTouchLibHandle, 'bpm_createInstance');
|
||||||
|
Pointer(bpm_destroyInstance) :=GetProcAddress(SoundTouchLibHandle, 'bpm_destroyInstance');
|
||||||
|
Pointer(bpm_getBpm) :=GetProcAddress(SoundTouchLibHandle, 'bpm_getBpm');
|
||||||
|
Pointer(bpm_putSamples) :=GetProcAddress(SoundTouchLibHandle, 'bpm_putSamples');
|
||||||
|
|
||||||
except
|
except
|
||||||
FreeLibrary(SoundTouchLibHandle);
|
FreeLibrary(SoundTouchLibHandle);
|
||||||
@ -460,6 +489,12 @@ begin
|
|||||||
if SoundTouchLibHandle <> 0 then FreeLibrary(SoundTouchLibHandle);
|
if SoundTouchLibHandle <> 0 then FreeLibrary(SoundTouchLibHandle);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
// returns 'true' if SoundTouch dynamic library has been successfully loaded, otherwise 'false'
|
||||||
|
function IsSoundTouchLoaded: Boolean;
|
||||||
|
begin;
|
||||||
|
result := SoundTouchLibHandle <> 0
|
||||||
|
end;
|
||||||
|
|
||||||
initialization
|
initialization
|
||||||
InitDLL;
|
InitDLL;
|
||||||
|
|
||||||
1
source/SoundTouchDLL/LazarusTest/libSoundTouchDll.so
Symbolic link
1
source/SoundTouchDLL/LazarusTest/libSoundTouchDll.so
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
../.libs/libSoundTouchDll.so
|
||||||
36
source/SoundTouchDLL/LazarusTest/main.lfm
Normal file
36
source/SoundTouchDLL/LazarusTest/main.lfm
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
object Form1: TForm1
|
||||||
|
Left = 2237
|
||||||
|
Height = 128
|
||||||
|
Top = 242
|
||||||
|
Width = 381
|
||||||
|
Caption = 'SoundTouch test'
|
||||||
|
ClientHeight = 128
|
||||||
|
ClientWidth = 381
|
||||||
|
LCLVersion = '2.2.0.4'
|
||||||
|
object Load: TButton
|
||||||
|
Left = 19
|
||||||
|
Height = 50
|
||||||
|
Top = 16
|
||||||
|
Width = 144
|
||||||
|
Caption = 'Load SoundTouch'
|
||||||
|
OnClick = LoadClick
|
||||||
|
TabOrder = 0
|
||||||
|
end
|
||||||
|
object EditVersion: TEdit
|
||||||
|
Left = 184
|
||||||
|
Height = 34
|
||||||
|
Top = 80
|
||||||
|
Width = 184
|
||||||
|
TabOrder = 1
|
||||||
|
Text = 'n/a'
|
||||||
|
TextHint = 'click to populate'
|
||||||
|
end
|
||||||
|
object Label1: TLabel
|
||||||
|
Left = 19
|
||||||
|
Height = 17
|
||||||
|
Top = 90
|
||||||
|
Width = 156
|
||||||
|
Caption = 'Soundtouch lib version:'
|
||||||
|
WordWrap = True
|
||||||
|
end
|
||||||
|
end
|
||||||
49
source/SoundTouchDLL/LazarusTest/main.pas
Normal file
49
source/SoundTouchDLL/LazarusTest/main.pas
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
unit main;
|
||||||
|
|
||||||
|
{$mode objfpc}{$H+}
|
||||||
|
|
||||||
|
interface
|
||||||
|
|
||||||
|
uses
|
||||||
|
Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls, SoundTouchDLL;
|
||||||
|
|
||||||
|
|
||||||
|
type
|
||||||
|
|
||||||
|
{ TForm1 }
|
||||||
|
|
||||||
|
TForm1 = class(TForm)
|
||||||
|
EditVersion: TEdit;
|
||||||
|
Label1: TLabel;
|
||||||
|
Load: TButton;
|
||||||
|
|
||||||
|
procedure LoadClick(Sender: TObject);
|
||||||
|
private
|
||||||
|
|
||||||
|
public
|
||||||
|
|
||||||
|
end;
|
||||||
|
|
||||||
|
var
|
||||||
|
Form1: TForm1;
|
||||||
|
|
||||||
|
implementation
|
||||||
|
|
||||||
|
{$R *.lfm}
|
||||||
|
|
||||||
|
{ TForm1 }
|
||||||
|
|
||||||
|
procedure TForm1.LoadClick(Sender: TObject);
|
||||||
|
var
|
||||||
|
version:string;
|
||||||
|
begin
|
||||||
|
if IsSoundTouchLoaded() then
|
||||||
|
version := SoundTouchGetVersionString()
|
||||||
|
else
|
||||||
|
version := '<library loading failed>';
|
||||||
|
|
||||||
|
EditVersion.Text:= version;
|
||||||
|
end;
|
||||||
|
|
||||||
|
end.
|
||||||
|
|
||||||
BIN
source/SoundTouchDLL/LazarusTest/soundtouchtest.ico
Normal file
BIN
source/SoundTouchDLL/LazarusTest/soundtouchtest.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 64 KiB |
78
source/SoundTouchDLL/LazarusTest/soundtouchtest.lpi
Normal file
78
source/SoundTouchDLL/LazarusTest/soundtouchtest.lpi
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<CONFIG>
|
||||||
|
<ProjectOptions>
|
||||||
|
<Version Value="12"/>
|
||||||
|
<General>
|
||||||
|
<SessionStorage Value="InProjectDir"/>
|
||||||
|
<Title Value="soundtouchtest"/>
|
||||||
|
<Scaled Value="True"/>
|
||||||
|
<ResourceType Value="res"/>
|
||||||
|
<UseXPManifest Value="True"/>
|
||||||
|
<XPManifest>
|
||||||
|
<DpiAware Value="True"/>
|
||||||
|
</XPManifest>
|
||||||
|
<Icon Value="0"/>
|
||||||
|
</General>
|
||||||
|
<BuildModes>
|
||||||
|
<Item Name="Default" Default="True"/>
|
||||||
|
</BuildModes>
|
||||||
|
<PublishOptions>
|
||||||
|
<Version Value="2"/>
|
||||||
|
<UseFileFilters Value="True"/>
|
||||||
|
</PublishOptions>
|
||||||
|
<RunParams>
|
||||||
|
<FormatVersion Value="2"/>
|
||||||
|
</RunParams>
|
||||||
|
<RequiredPackages>
|
||||||
|
<Item>
|
||||||
|
<PackageName Value="LCL"/>
|
||||||
|
</Item>
|
||||||
|
</RequiredPackages>
|
||||||
|
<Units>
|
||||||
|
<Unit>
|
||||||
|
<Filename Value="soundtouchtest.lpr"/>
|
||||||
|
<IsPartOfProject Value="True"/>
|
||||||
|
</Unit>
|
||||||
|
<Unit>
|
||||||
|
<Filename Value="main.pas"/>
|
||||||
|
<IsPartOfProject Value="True"/>
|
||||||
|
<ComponentName Value="Form1"/>
|
||||||
|
<HasResources Value="True"/>
|
||||||
|
<ResourceBaseClass Value="Form"/>
|
||||||
|
</Unit>
|
||||||
|
</Units>
|
||||||
|
</ProjectOptions>
|
||||||
|
<CompilerOptions>
|
||||||
|
<Version Value="11"/>
|
||||||
|
<Target>
|
||||||
|
<Filename Value="soundtouchtest"/>
|
||||||
|
</Target>
|
||||||
|
<SearchPaths>
|
||||||
|
<IncludeFiles Value="$(ProjOutDir)"/>
|
||||||
|
<UnitOutputDirectory Value="lib/$(TargetCPU)-$(TargetOS)"/>
|
||||||
|
</SearchPaths>
|
||||||
|
<Linking>
|
||||||
|
<Debugging>
|
||||||
|
<DebugInfoType Value="dsDwarf3"/>
|
||||||
|
</Debugging>
|
||||||
|
<Options>
|
||||||
|
<Win32>
|
||||||
|
<GraphicApplication Value="True"/>
|
||||||
|
</Win32>
|
||||||
|
</Options>
|
||||||
|
</Linking>
|
||||||
|
</CompilerOptions>
|
||||||
|
<Debugging>
|
||||||
|
<Exceptions>
|
||||||
|
<Item>
|
||||||
|
<Name Value="EAbort"/>
|
||||||
|
</Item>
|
||||||
|
<Item>
|
||||||
|
<Name Value="ECodetoolError"/>
|
||||||
|
</Item>
|
||||||
|
<Item>
|
||||||
|
<Name Value="EFOpenError"/>
|
||||||
|
</Item>
|
||||||
|
</Exceptions>
|
||||||
|
</Debugging>
|
||||||
|
</CONFIG>
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user