├── Automation ├── CI │ ├── JenkinsBuildCILinux.sh │ ├── JenkinsBuildCIOSX.sh │ └── JenkinsBuildCIWindows.bat └── Release │ ├── JenkinsBuildReleaseLinux.sh │ ├── JenkinsBuildReleaseOSX.sh │ └── JenkinsBuildReleaseWindows.bat ├── Documentation ├── Images │ ├── JenkinsBuild.png │ ├── JenkinsBuildTriggers.png │ ├── JenkinsParameters.png │ ├── JenkinsPhaseVocoderGitHubProjects.png │ ├── JenkinsReleaseBuild.png │ └── JenkinsSourceCodeManagement.png └── JenkinsSetup.md ├── README.md └── Source ├── Application ├── CMakeLists.txt ├── CommandLineArguments.cpp ├── CommandLineArguments.h ├── PhaseVocoder.cpp ├── PhaseVocoderMediator.cpp ├── PhaseVocoderMediator.h ├── PhaseVocoderProcessor.cpp ├── PhaseVocoderProcessor.h ├── PhaseVocoderSettings.cpp ├── PhaseVocoderSettings.h ├── TransientConfigFile.cpp ├── TransientConfigFile.h ├── Transients.cpp ├── Transients.h ├── UT │ ├── CMakeLists.txt │ ├── CommandLineArguments-UT.cpp │ ├── PhaseVocoderMediator-UT.cpp │ ├── TestAudio │ │ ├── BuiltToSpillBeatAbbrev.wav │ │ └── SweetEmotion.wav │ ├── TestAudioResults │ │ ├── BuiltToSpillBeatAbbrev0.25.wav │ │ ├── BuiltToSpillBeatAbbrev0.50.wav │ │ ├── BuiltToSpillBeatAbbrev0.75.wav │ │ ├── BuiltToSpillBeatAbbrev1.25.wav │ │ ├── BuiltToSpillBeatAbbrev1.50.wav │ │ ├── BuiltToSpillBeatAbbrev1.75.wav │ │ ├── BuiltToSpillBeatAbbrevResample32123.wav │ │ └── BuiltToSpillBeatAbbrevResample48000.wav │ ├── TestTransientConfigFiles │ │ ├── ChannelSpecificTransientConfigFile.yaml │ │ ├── IncorrectTransientConfigFile.yaml │ │ └── TransientConfigFile.yaml │ ├── TransientConfigFile-UT.cpp │ └── main.cpp ├── Usage.cpp └── Usage.h ├── CMakeLists.txt └── CMakeSupport ├── CMakeLists.AudioLib.txt ├── CMakeLists.CompilerSettings.txt ├── CMakeLists.Externals.txt ├── CMakeLists.GoogleTest.txt └── CMakeLists.YamlCpp.txt /Automation/CI/JenkinsBuildCILinux.sh: -------------------------------------------------------------------------------- 1 | # Do a debug build 2 | mkdir DebugBuild 3 | cd DebugBuild 4 | cmake -DCMAKE_BUILD_TYPE=Debug -G "Unix Makefiles" ../Source 5 | cmake --build . 6 | 7 | cd .. 8 | 9 | # Do a release build 10 | mkdir ReleaseBuild 11 | cd ReleaseBuild 12 | cmake -DCMAKE_BUILD_TYPE=Release -G "Unix Makefiles" ../Source 13 | cmake --build . 14 | -------------------------------------------------------------------------------- /Automation/CI/JenkinsBuildCIOSX.sh: -------------------------------------------------------------------------------- 1 | # Do a debug build 2 | mkdir DebugBuild 3 | cd DebugBuild 4 | /usr/local/bin/cmake -G "Xcode" -DCMAKE_BUILD_TYPE=Debug ../Source 5 | xcodebuild -project PhaseVocoder.xcodeproj -configuration Debug 6 | 7 | cd .. 8 | 9 | # Do a releaase build 10 | mkdir ReleaseBuild 11 | cd ReleaseBuild 12 | /usr/local/bin/cmake -G "Xcode" -DCMAKE_BUILD_TYPE=Release ../Source 13 | xcodebuild -project PhaseVocoder.xcodeproj -configuration Release 14 | -------------------------------------------------------------------------------- /Automation/CI/JenkinsBuildCIWindows.bat: -------------------------------------------------------------------------------- 1 | call "%VisualStudioPath%\vcvarsall.bat" amd64 2 | cd %WORKSPACE% 3 | mkdir PhaseVocoderBuilt 4 | cd PhaseVocoderBuilt 5 | cmake -G "%CMakeGeneratorString%" ../Source 6 | devenv PhaseVocoder.sln /Build Debug 7 | devenv PhaseVocoder.sln /Build Release 8 | -------------------------------------------------------------------------------- /Automation/Release/JenkinsBuildReleaseLinux.sh: -------------------------------------------------------------------------------- 1 | echo "Version number is ${Version}" 2 | echo "Build number is ${BUILD_NUMBER}" 3 | 4 | git checkout tags/v${Version} 5 | if [ $? -ne 0 ] 6 | then 7 | echo "Failed to checkout tag" 8 | exit 1 9 | fi 10 | 11 | mkdir ReleaseBuild 12 | if [ $? -ne 0 ] 13 | then 14 | echo "Failed to make ReleaseBuild directory" 15 | exit 1 16 | fi 17 | 18 | cd ReleaseBuild 19 | if [ $? -ne 0 ] 20 | then 21 | echo "Failed to change dir into ReleaseBuild" 22 | exit 1 23 | fi 24 | 25 | cmake -D CMAKE_BUILD_TYPE=Release -D VERSION_NUMBER=${Version} -D BUILD_NUMBER=${BUILD_NUMBER} -G "Unix Makefiles" ../Source 26 | if [ $? -ne 0 ] 27 | then 28 | echo "Executing cmake failed" 29 | exit 1 30 | fi 31 | 32 | cmake --build . # To compile 33 | if [ $? -ne 0 ] 34 | then 35 | echo "CMake --build failed" 36 | exit 1 37 | fi 38 | 39 | zip -j PhaseVocoder.zip "${WORKSPACE}/ReleaseBuild/Application/PhaseVocoder" 40 | if [ $? -ne 0 ] 41 | then 42 | echo "Failed to zip PhaseVocoder" 43 | exit 1 44 | fi 45 | 46 | cp PhaseVocoder.zip "${ReleaseDestination}/PhaseVocoder-Deb64-${Version}-${BUILD_NUMBER}.zip" 47 | if [ $? -ne 0 ] 48 | then 49 | echo "Failed to copy PhaseVocoder.zip to release directory" 50 | exit 1 51 | fi 52 | 53 | exit 0 54 | -------------------------------------------------------------------------------- /Automation/Release/JenkinsBuildReleaseOSX.sh: -------------------------------------------------------------------------------- 1 | echo "Version number is ${Version}" 2 | echo "Build number is ${BUILD_NUMBER}" 3 | 4 | git checkout tags/v${Version} 5 | if [ $? -ne 0 ] 6 | then 7 | echo "Failed to checkout tag" 8 | exit 1 9 | fi 10 | 11 | mkdir ReleaseBuild 12 | if [ $? -ne 0 ] 13 | then 14 | echo "Failed to make ReleaseBuild directory" 15 | exit 1 16 | fi 17 | 18 | cd ReleaseBuild 19 | if [ $? -ne 0 ] 20 | then 21 | echo "Failed to change dir into ReleaseBuild" 22 | exit 1 23 | fi 24 | 25 | /usr/local/bin/cmake -D VERSION_NUMBER=${Version} -D BUILD_NUMBER=${BUILD_NUMBER} -D CMAKE_BUILD_TYPE=Release -G "Xcode" ../Source 26 | if [ $? -ne 0 ] 27 | then 28 | echo "Executing cmake failed" 29 | exit 1 30 | fi 31 | 32 | xcodebuild -project PhaseVocoder.xcodeproj -configuration Release 33 | if [ $? -ne 0 ] 34 | then 35 | echo "xcodebuild failed" 36 | exit 1 37 | fi 38 | 39 | zip -j PhaseVocoder.zip "${WORKSPACE}/ReleaseBuild/Application/Release/PhaseVocoder" 40 | if [ $? -ne 0 ] 41 | then 42 | echo "Failed to zip PhaseVocoder" 43 | exit 1 44 | fi 45 | 46 | cp PhaseVocoder.zip "${ReleaseDestination}/PhaseVocoder-OSXIntel64-${Version}-${BUILD_NUMBER}.zip" 47 | if [ $? -ne 0 ] 48 | then 49 | echo "Failed to copy PhaseVocoder.zip to release directory" 50 | exit 1 51 | fi 52 | 53 | exit 0 54 | -------------------------------------------------------------------------------- /Automation/Release/JenkinsBuildReleaseWindows.bat: -------------------------------------------------------------------------------- 1 | ECHO Building PhaseVocoder version number %Version% 2 | ECHO Build number is %BUILD_NUMBER% 3 | 4 | call "%VisualStudioPath%\vcvarsall.bat" amd64 5 | 6 | REM Switch to the tag 7 | git checkout tags/v%Version% 8 | 9 | REM Run CMake 10 | echo "Creating build dir" 11 | mkdir PhaseVocoderBuilt 12 | echo "Accessing build dir" 13 | cd PhaseVocoderBuilt 14 | echo "Running cmake" 15 | cmake -D VERSION_NUMBER=%Version% -D BUILD_NUMBER=%BUILD_NUMBER% -G "%CMakeGeneratorString%" ..\Source 16 | IF %ERRORLEVEL% NEQ 0 ( 17 | ECHO CMake failed 18 | EXIT 1 19 | ) 20 | 21 | REM Do the build 22 | echo "Building release version of PhaseVocoder" 23 | devenv PhaseVocoder.sln /Build Release 24 | IF %ERRORLEVEL% NEQ 0 ( 25 | ECHO The build failed 26 | EXIT 1 27 | ) 28 | 29 | REM Zip the executable 30 | echo "7z" a -tzip PhaseVocoder.zip "%WORKSPACE%\PhaseVocoderBuilt\Application\Release\PhaseVocoder.exe" 31 | "7z" a -tzip PhaseVocoder.zip "%WORKSPACE%\PhaseVocoderBuilt\Application\Release\PhaseVocoder.exe" 32 | 33 | IF %ERRORLEVEL% NEQ 0 ( 34 | ECHO Zipping PhaseVocoder.exe failed 35 | EXIT 1 36 | ) 37 | 38 | REM Copy the resulting installer to the Releases folder 39 | echo copy PhaseVocoder.zip %ReleaseDestination%\PhaseVocoder-Win64-%Version%-%BUILD_NUMBER%.zip 40 | copy PhaseVocoder.zip %ReleaseDestination%\PhaseVocoder-Win64-%Version%-%BUILD_NUMBER%.zip 41 | 42 | IF %ERRORLEVEL% NEQ 0 ( 43 | ECHO Copying PhaseVocoder.exe to release destination 44 | EXIT 1 45 | ) 46 | 47 | -------------------------------------------------------------------------------- /Documentation/Images/JenkinsBuild.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmdarwen/PhaseVocoder/4093d8bcd5d660df8db68aaf3f21bfed6ccfe1d4/Documentation/Images/JenkinsBuild.png -------------------------------------------------------------------------------- /Documentation/Images/JenkinsBuildTriggers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmdarwen/PhaseVocoder/4093d8bcd5d660df8db68aaf3f21bfed6ccfe1d4/Documentation/Images/JenkinsBuildTriggers.png -------------------------------------------------------------------------------- /Documentation/Images/JenkinsParameters.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmdarwen/PhaseVocoder/4093d8bcd5d660df8db68aaf3f21bfed6ccfe1d4/Documentation/Images/JenkinsParameters.png -------------------------------------------------------------------------------- /Documentation/Images/JenkinsPhaseVocoderGitHubProjects.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmdarwen/PhaseVocoder/4093d8bcd5d660df8db68aaf3f21bfed6ccfe1d4/Documentation/Images/JenkinsPhaseVocoderGitHubProjects.png -------------------------------------------------------------------------------- /Documentation/Images/JenkinsReleaseBuild.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmdarwen/PhaseVocoder/4093d8bcd5d660df8db68aaf3f21bfed6ccfe1d4/Documentation/Images/JenkinsReleaseBuild.png -------------------------------------------------------------------------------- /Documentation/Images/JenkinsSourceCodeManagement.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmdarwen/PhaseVocoder/4093d8bcd5d660df8db68aaf3f21bfed6ccfe1d4/Documentation/Images/JenkinsSourceCodeManagement.png -------------------------------------------------------------------------------- /Documentation/JenkinsSetup.md: -------------------------------------------------------------------------------- 1 | Jenkins Continuous Integration and Automated Release Building 2 | ============================================================= 3 | 4 | [Continuous integration](https://en.wikipedia.org/wiki/Continuous_integration) is a great way to keep software projects in tip-top shape and avoid surprises by finding something that is failing as early as possible. 5 | 6 | For my own development, I use [Jenkins](https://jenkins.io/) as a build server. Jenkins allows for easily setting up a continuous integration system as well as an automated release build system. 7 | 8 | As shown below, I have various Jenkins projects setup for the PhaseVocoder project. These include both continuous integration (CI) and release building on Windows, OS X and Linux. 9 | 10 | ![Jenkins PhaseVocoder Projects](Images/JenkinsPhaseVocoderGitHubProjects.png) 11 | 12 | 13 |   14 | 15 | **Setting Up Jenkins** 16 | 17 | I'm not going to include specific instructions for setting up Jenkins. Doing a simple search will give you numerous helpful instructions on how to do this. 18 | 19 | Currently (Fall 2017), my personal development environment has Jenkins running on my main development machine and uses VMs for Jenkins' build agents. I have VMs running Windows, Linux (Ubuntu) and OS X (El Capitan). As soon as a new commit happens to the master branch of the GitHub PhaseVocoder project, the repo is cloned and built on each of these machines. 20 | 21 | Part of the awesomeness of this is having my latest changes built with various C++ compilers on various platforms and getting very timely feedback of any issues on these platforms/compilers. The compilers I'm currently (Fall 2017) using are MS Visual Studio 2017, GCC 5.3.1 and Apple LLVM version 7.3.0 (clang 703.0.31). 22 | 23 | 24 |   25 | 26 | **Setting Up Continuous Integration Jenkins Projects** 27 | 28 | Once you have Jenkins and your build agents setup, creating a new CI job is easy. Click "New Item" and give it a name. Under "Source Code Management" select "Git", set the repo to https://github.com/tmdarwen/PhaseVocoder.git and branch to master as shown below. 29 | 30 | ![Jenkins Source Code Management](Images/JenkinsSourceCodeManagement.png) 31 | 32 | As shown below, set the build trigger to poll the source code management system. The schedule I've specified checks the PhaseVocoder GitHub repo every five minutes. If it finds a change has occurred since it last checked, it will kick off a new build. 33 | 34 | ![Jenkins Build Triggers](Images/JenkinsBuildTriggers.png) 35 | 36 | Under the "build" section, simply specify the build script Jenkins should run. Note that these scripts can be found in the Automation directory of the PhaseVocoder project. 37 | 38 | ![Jenkins Build](Images/JenkinsBuild.png) 39 | 40 |   41 | 42 | **Setting Up Automated Release Projects** 43 | 44 | Jenkins is super handy for building releases as well. I've setup my environment so that with just a single mouse click Jenkins will build a new tag of the project and deliver the binary to a specific location on a shared drive. Setting this up is very similar to the above instructions on setting up CI projects with the addition of the parameters shown below: 45 | 46 | ![Jenkins Parameters](Images/JenkinsParameters.png) 47 | 48 | When building the project in Jenkins for a new release you'll simply create a tag in the GitHub project and then enter that tag version in Jenkins as shown below. 49 | 50 | ![Jenkins Release Build](Images/JenkinsReleaseBuild.png) 51 | 52 | The release will be built and the resulting binary is placed on the shared drive in the specified release destination. 53 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | PhaseVocoder 2 | ============ 3 | 4 | A cross platform command line utility implementing a [phase vocoder](https://en.wikipedia.org/wiki/Phase_vocoder). The PhaseVocoder allows for time compression/expansion, pitch shifting and resampling of audio with quite a good degree of quality. For audio samples and additional information please see [this page](https://tmdarwen.com/my-awesome-phase-vocoder.html). The PhaseVocoder regularly builds in my continuous integration/release system using Windows 10, OS X (El Capitan) and Linux (Ubuntu 16.04). For building on your platform, please see the steps below. 5 |   6 | 7 | **How the PhaseVocoder Works** 8 | 9 | For information on how the PhaseVocoder works, please see ["How the PhaseVocoder Works"](https://github.com/tmdarwen/AudioLib/blob/master/Documentation/HowThePhaseVocoderWorks.md). Additionally, the PhaseVocoder's quality is dependent on detection of audio transients. Information on how transient detection works can be found [here](https://github.com/tmdarwen/AudioLib/blob/master/Documentation/TransientDetection.md). 10 | 11 |   12 | 13 | **Build Dependencies** 14 | 15 | - Building this project requires [CMake](https://cmake.org) version 3.0 or later. 16 | 17 | - Building this project requires a C++14 compliant compiler. This project is routinely built in my continous integration system which uses MS Visual Studio 2017, GCC 5.3.1 and Apple LLVM version 7.3.0 (clang 703.0.31). 18 | 19 | - External dependencies are my [AudioLib](https://github.com/tmdarwen/AudioLib), [GoogleTest](https://github.com/google/googletest) and [yaml-cpp](https://github.com/jbeder/yaml-cpp). You do *not* need to clone or install these dependencies manually. The GitHub repos will be cloned automatically when CMake runs. 20 | 21 |   22 | 23 | **Steps for Building** 24 | 25 | 1. Clone this repo. 26 | 27 | 1. Create a new directory at the parallel level as the cloned repo. This directory will hold the project files CMake creates. 28 | 29 | 1. cd into this new directory. 30 | 31 | 1. From the command line, run _cmake -G YourDesiredGeneratorType ../PhaseVocoder/Source_ 32 | 33 | 1. Run make or open the project file in an IDE and build. 34 | 35 |   36 | 37 | **Usage Examples** 38 | 39 | Running the PhaseVocoder application from the command line with no arguments will show all possible usage. The following examples show some of the more common usages. 40 | 41 | Time Stretching Example - Increase the length of the input by a factor of two:
42 | ```PhaseVocoder -i in.wav -o out.wav -s 2.0``` 43 | 44 | Time Compression Example - Reduce the length of the input by twenty percent:
45 | ```PhaseVocoder -input in.wav -output out.wav -stretch 0.8``` 46 | 47 | Pitch Shift Example - Raise the pitch of the audio by 2 semitones:
48 | ```PhaseVocoder -i in.wav -o out.wav -s -p 2.0``` 49 | 50 | Pitch Shift Example - Drop the pitch of the audio by 3.1 semitones:
51 | ```PhaseVocoder -i in.wav -o out.wav -s -p -3.1``` 52 | 53 | Resample Example - Change the sample rate to 88,200 Hz:
54 | ```PhaseVocoder -i in.wav -o out.wav -s -r 88200``` 55 | 56 |   57 | 58 | **Tests** 59 | 60 | Unit test coverage is extensive. You'll notice every component within the source directory has a UT directory which contains unit tests. These of course automatically build and run as part of the build process. 61 | 62 |   63 | 64 | **Continuous Integration and Automated Release** 65 | 66 | The [Automation directory](/Automation) contains scripts that can be used with [Jenkins](https://jenkins.io/) to setup continuous integration and automated release building of the PhaseVocoder project. These scripts work on Windows, OS X and Linux. For more information on how to setup Jenkins to use these scripts please see [this document](Documentation/JenkinsSetup.md). 67 | 68 |   69 | 70 | **Miscellaneous Notes Concerning the Project** 71 | 72 | - Currently supports 16 bit wave files as the only form of input. 73 | 74 | 75 |   76 | 77 | **To Do** 78 | 79 | - Support various other audio file formats. 80 | 81 |   82 | 83 | **Licensing** 84 | 85 | The MIT License applies to this software and its supporting documentation: 86 | 87 | *Copyright (c) 2017 - Terence M. Darwen - tmdarwen.com* 88 | 89 | *Permission is hereby granted, free of charge, to any person obtaining a copy of 90 | this software and associated documentation files (the "Software"), to deal in 91 | the Software without restriction, including without limitation the rights to 92 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 93 | the Software, and to permit persons to whom the Software is furnished to do so, 94 | subject to the following conditions:* 95 | 96 | *The above copyright notice and this permission notice shall be included in all 97 | copies or substantial portions of the Software.* 98 | 99 | *THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 100 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 101 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 102 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 103 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 104 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.* 105 | -------------------------------------------------------------------------------- /Source/Application/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | if(DEBUG_BUILD) 2 | add_definitions(-DDEBUG_BUILD) 3 | endif(DEBUG_BUILD) 4 | 5 | if(VERSION_NUMBER) 6 | add_definitions(-DVERSION_NUMBER=${VERSION_NUMBER}) 7 | endif(VERSION_NUMBER) 8 | 9 | if(BUILD_NUMBER) 10 | add_definitions(-DBUILD_NUMBER=${BUILD_NUMBER}) 11 | endif(BUILD_NUMBER) 12 | 13 | include_directories("${PROJECT_SOURCE_DIR}" "${CMAKE_BINARY_DIR}/audiolib-src/Source") 14 | include_directories("${PROJECT_SOURCE_DIR}" "${CMAKE_BINARY_DIR}/yamlcpp-src/include") 15 | 16 | file(GLOB source_files [^.]*.h [^.]*.cpp) 17 | find_package(Threads) 18 | add_executable(PhaseVocoder ${source_files}) 19 | include(${PROJECT_SOURCE_DIR}/CMakeSupport/CMakeLists.CompilerSettings.txt) 20 | target_link_libraries(PhaseVocoder AudioData Signal ThreadSafeAudioFile Utilities WaveFile yaml-cpp ${CMAKE_THREAD_LIBS_INIT}) 21 | set_target_properties(PhaseVocoder PROPERTIES FOLDER Apps) 22 | 23 | add_subdirectory(UT) 24 | 25 | 26 | -------------------------------------------------------------------------------- /Source/Application/CommandLineArguments.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * PhaseVocoder 3 | * 4 | * Copyright (c) 2017 - Terence M. Darwen - tmdarwen.com 5 | * 6 | * The MIT License 7 | * 8 | * Permission is hereby granted, free of charge, to any person obtaining a copy 9 | * of this software and associated documentation files (the "Software"), to deal 10 | * in the Software without restriction, including without limitation the rights 11 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | * copies of the Software, and to permit persons to whom the Software is 13 | * furnished to do so, subject to the following conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be included in 16 | * all copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | * THE SOFTWARE. 25 | */ 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | CommandLineArguments::CommandLineArguments(int argc, char** argv) 34 | { 35 | possibleArguments_["--help"] = ArgumentTraits{"-h", false, false}; 36 | possibleArguments_["--input"] = ArgumentTraits{"-i", true, true}; 37 | possibleArguments_["--output"] = ArgumentTraits{"-o", true, true}; 38 | possibleArguments_["--stretch"] = ArgumentTraits{"-s", true, true}; 39 | possibleArguments_["--pitch"] = ArgumentTraits{"-p", true, true}; 40 | possibleArguments_["--resample"] = ArgumentTraits{"-r", true, true}; 41 | possibleArguments_["--showtransients"] = ArgumentTraits{"-t", false, false}; 42 | possibleArguments_["--transientconfig"] = ArgumentTraits{"-c", true, true}; 43 | possibleArguments_["--valleypeakratio"] = ArgumentTraits{"-a", true, true}; 44 | possibleArguments_["--longhelp"] = ArgumentTraits{"-l", false, false}; 45 | possibleArguments_["--version"] = ArgumentTraits{"-v", false, false}; 46 | 47 | if(ParseArguments(argc, argv)) 48 | { 49 | ValidateArguments(); 50 | } 51 | } 52 | 53 | // Returns false if a problem with given parameters is encountered 54 | bool CommandLineArguments::ParseArguments(int argc, char** argv) 55 | { 56 | for(int i{1}; i < argc; ++i) 57 | { 58 | std::string argument{argv[i]}; 59 | bool argumentFound{false}; 60 | for(auto possibleArgument : possibleArguments_) 61 | { 62 | std::string posArg{possibleArgument.first}; 63 | if(argument == possibleArgument.first || argument == possibleArgument.second.shortArgument_) 64 | { 65 | if(possibleArgument.second.requiresValue_ && i + 1 == argc) 66 | { 67 | valid_ = false; 68 | errorMessage_ = "No value given for argument requiring value"; 69 | return false; 70 | } 71 | 72 | if(possibleArgument.second.acceptsValue_) 73 | { 74 | argumentsGiven_[possibleArgument.first] = argv[i + 1]; 75 | ++i; 76 | } 77 | else 78 | { 79 | argumentsGiven_[possibleArgument.first] = ""; 80 | } 81 | 82 | argumentFound = true; 83 | } 84 | } 85 | 86 | if(!argumentFound) 87 | { 88 | valid_ = false; 89 | errorMessage_ = "Invalid parameter given: "; 90 | errorMessage_.append(argument); 91 | return false; 92 | } 93 | } 94 | 95 | return true; 96 | } 97 | 98 | bool CommandLineArguments::InputFilenameGiven() const 99 | { 100 | auto element = argumentsGiven_.find("--input"); 101 | if(element == argumentsGiven_.end()) 102 | { 103 | return false; 104 | } 105 | 106 | return true; 107 | } 108 | 109 | bool CommandLineArguments::OutputFilenameGiven() const 110 | { 111 | auto element = argumentsGiven_.find("--output"); 112 | if(element == argumentsGiven_.end()) 113 | { 114 | return false; 115 | } 116 | 117 | return true; 118 | } 119 | 120 | bool CommandLineArguments::StretchFactorGiven() const 121 | { 122 | auto element = argumentsGiven_.find("--stretch"); 123 | if(element == argumentsGiven_.end()) 124 | { 125 | return false; 126 | } 127 | 128 | return true; 129 | 130 | } 131 | 132 | bool CommandLineArguments::PitchSettingGiven() const 133 | { 134 | auto element = argumentsGiven_.find("--pitch"); 135 | if(element == argumentsGiven_.end()) 136 | { 137 | return false; 138 | } 139 | 140 | return true; 141 | } 142 | 143 | bool CommandLineArguments::ResampleSettingGiven() const 144 | { 145 | auto element = argumentsGiven_.find("--resample"); 146 | if(element == argumentsGiven_.end()) 147 | { 148 | return false; 149 | } 150 | 151 | return true; 152 | } 153 | 154 | bool CommandLineArguments::ValleyPeakRatioGiven() const 155 | { 156 | auto element = argumentsGiven_.find("--valleypeakratio"); 157 | if(element == argumentsGiven_.end()) 158 | { 159 | return false; 160 | } 161 | 162 | return true; 163 | } 164 | 165 | void CommandLineArguments::ValidateArguments() 166 | { 167 | if(argumentsGiven_.size() == 0) 168 | { 169 | argumentsGiven_["--help"] = ""; 170 | return; 171 | } 172 | 173 | if(Help() || LongHelp() || Version()) 174 | { 175 | return; 176 | } 177 | 178 | if(!InputFilenameGiven()) 179 | { 180 | valid_ = false; 181 | errorMessage_ = "No input file given."; 182 | return; 183 | } 184 | 185 | if(InputFilenameGiven() && !StretchFactorGiven() && !PitchSettingGiven() && !ResampleSettingGiven() && !ShowTransients()) 186 | { 187 | valid_ = false; 188 | errorMessage_ = "Nothing to do. No action specified."; 189 | return; 190 | } 191 | 192 | if(!ValidateStretchSetting() || !ValidatePitchSetting() || !ValidateResampleSetting()) 193 | { 194 | valid_ = false; 195 | return; 196 | } 197 | 198 | if(OutputFilenameGiven() && !StretchFactorGiven() && !PitchSettingGiven() && !ResampleSettingGiven()) 199 | { 200 | valid_ = false; 201 | errorMessage_ = "Output file given but no stretch, pitch or resample setting given."; 202 | return; 203 | } 204 | } 205 | 206 | bool CommandLineArguments::ValidateStretchSetting() 207 | { 208 | auto element = argumentsGiven_.find("--stretch"); 209 | if(element != argumentsGiven_.end() && GetOutputFilename().size() == 0) 210 | { 211 | valid_ = false; 212 | errorMessage_ = "Stretch factor given, but no output file given."; 213 | return false; 214 | } 215 | else if(element != argumentsGiven_.end()) 216 | { 217 | auto stretchFactor{atof(element->second.c_str())}; 218 | if(stretchFactor < minimumStretchFactor_ || stretchFactor > maximumStretchFactor_) 219 | { 220 | errorMessage_ = Utilities::CreateString(" ", "Given stretch factor out of range. Min:", minimumStretchFactor_, " Max:", maximumStretchFactor_); 221 | return false; 222 | } 223 | } 224 | 225 | return true; 226 | } 227 | 228 | bool CommandLineArguments::ValidatePitchSetting() 229 | { 230 | auto element = argumentsGiven_.find("--pitch"); 231 | if(element != argumentsGiven_.end() && GetOutputFilename().size() == 0) 232 | { 233 | valid_ = false; 234 | errorMessage_ = "Pitch setting given, but no output file given."; 235 | return false; 236 | } 237 | else if(element != argumentsGiven_.end()) 238 | { 239 | auto pitchSetting{atof(element->second.c_str())}; 240 | if(pitchSetting < minimumPitchShift_ || pitchSetting > maximumPitchShift_) 241 | { 242 | errorMessage_ = Utilities::CreateString(" ", "Given pitch setting out of range. Min:", minimumPitchShift_, " Max:", maximumPitchShift_); 243 | return false; 244 | } 245 | } 246 | 247 | return true; 248 | } 249 | 250 | bool CommandLineArguments::ValidateResampleSetting() 251 | { 252 | auto element = argumentsGiven_.find("--resample"); 253 | if(element != argumentsGiven_.end() && GetOutputFilename().size() == 0) 254 | { 255 | valid_ = false; 256 | errorMessage_ = "Resample setting given, but no output file given."; 257 | return false; 258 | } 259 | else if(element != argumentsGiven_.end()) 260 | { 261 | auto resampleSetting{atof(element->second.c_str())}; 262 | if(resampleSetting < minimumResampleFrequency_ || resampleSetting > maximumResampleFrequency_) 263 | { 264 | errorMessage_ = Utilities::CreateString(" ", "Given resample setting out of range. Min:", minimumResampleFrequency_, " Max:", maximumResampleFrequency_); 265 | return false; 266 | } 267 | } 268 | 269 | return true; 270 | } 271 | 272 | bool CommandLineArguments::IsValid() const 273 | { 274 | return valid_; 275 | } 276 | 277 | const std::string CommandLineArguments::GetInputFilename() const 278 | { 279 | auto element = argumentsGiven_.find("--input"); 280 | if(element == argumentsGiven_.end()) 281 | { 282 | return ""; 283 | } 284 | 285 | return element->second; 286 | } 287 | 288 | const std::string CommandLineArguments::GetOutputFilename() const 289 | { 290 | auto element = argumentsGiven_.find("--output"); 291 | if(element == argumentsGiven_.end()) 292 | { 293 | return ""; 294 | } 295 | 296 | return element->second; 297 | } 298 | 299 | double CommandLineArguments::GetStretchFactor() const 300 | { 301 | auto element = argumentsGiven_.find("--stretch"); 302 | if(element == argumentsGiven_.end()) 303 | { 304 | return 0.0; 305 | } 306 | 307 | return atof(element->second.c_str()); 308 | } 309 | 310 | double CommandLineArguments::GetPitchSetting() const 311 | { 312 | auto element = argumentsGiven_.find("--pitch"); 313 | if(element == argumentsGiven_.end()) 314 | { 315 | return 0.0; 316 | } 317 | 318 | return atof(element->second.c_str()); 319 | } 320 | 321 | std::size_t CommandLineArguments::GetResampleSetting() const 322 | { 323 | auto element = argumentsGiven_.find("--resample"); 324 | if(element == argumentsGiven_.end()) 325 | { 326 | return 0; 327 | } 328 | 329 | return atoi(element->second.c_str()); 330 | } 331 | 332 | bool CommandLineArguments::ShowTransients() const 333 | { 334 | if(argumentsGiven_.find("--showtransients")== argumentsGiven_.end()) 335 | { 336 | return false; 337 | } 338 | 339 | return true; 340 | } 341 | 342 | bool CommandLineArguments::TransientConfigFileGiven() const 343 | { 344 | if(GetTransientConfigFilename().size()) 345 | { 346 | return true; 347 | } 348 | 349 | return false; 350 | } 351 | 352 | const std::string CommandLineArguments::GetTransientConfigFilename() const 353 | { 354 | auto element = argumentsGiven_.find("--transientconfig"); 355 | if(element == argumentsGiven_.end()) 356 | { 357 | return ""; 358 | } 359 | 360 | return element->second; 361 | } 362 | 363 | double CommandLineArguments::GetValleyPeakRatio() const 364 | { 365 | auto element = argumentsGiven_.find("--valleypeakratio"); 366 | if(element == argumentsGiven_.end()) 367 | { 368 | return 0.0; 369 | } 370 | 371 | return atof(element->second.c_str()); 372 | } 373 | 374 | const std::string& CommandLineArguments::GetErrorMessage() const 375 | { 376 | return errorMessage_; 377 | } 378 | 379 | bool CommandLineArguments::Help() const 380 | { 381 | if(argumentsGiven_.find("--help")== argumentsGiven_.end()) 382 | { 383 | return false; 384 | } 385 | 386 | return true; 387 | } 388 | 389 | bool CommandLineArguments::LongHelp() const 390 | { 391 | if(argumentsGiven_.find("--longhelp")== argumentsGiven_.end()) 392 | { 393 | return false; 394 | } 395 | 396 | return true; 397 | } 398 | 399 | bool CommandLineArguments::Version() const 400 | { 401 | if(argumentsGiven_.find("--version")== argumentsGiven_.end()) 402 | { 403 | return false; 404 | } 405 | 406 | return true; 407 | } 408 | -------------------------------------------------------------------------------- /Source/Application/CommandLineArguments.h: -------------------------------------------------------------------------------- 1 | /* 2 | * PhaseVocoder 3 | * 4 | * Copyright (c) 2017 - Terence M. Darwen - tmdarwen.com 5 | * 6 | * The MIT License 7 | * 8 | * Permission is hereby granted, free of charge, to any person obtaining a copy 9 | * of this software and associated documentation files (the "Software"), to deal 10 | * in the Software without restriction, including without limitation the rights 11 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | * copies of the Software, and to permit persons to whom the Software is 13 | * furnished to do so, subject to the following conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be included in 16 | * all copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | * THE SOFTWARE. 25 | */ 26 | 27 | #include 28 | #include 29 | 30 | class CommandLineArguments 31 | { 32 | public: 33 | CommandLineArguments(int argc, char** argv); 34 | 35 | bool IsValid() const; 36 | 37 | const std::string& GetErrorMessage() const; 38 | 39 | bool InputFilenameGiven() const; 40 | const std::string GetInputFilename() const; 41 | 42 | bool OutputFilenameGiven() const; 43 | const std::string GetOutputFilename() const; 44 | 45 | bool StretchFactorGiven() const; 46 | double GetStretchFactor() const; 47 | 48 | bool PitchSettingGiven() const; 49 | double GetPitchSetting() const; 50 | 51 | bool ResampleSettingGiven() const; 52 | std::size_t GetResampleSetting() const; 53 | 54 | bool ValleyPeakRatioGiven() const; 55 | double GetValleyPeakRatio() const; 56 | 57 | bool ShowTransients() const; 58 | bool TransientConfigFileGiven() const; 59 | const std::string GetTransientConfigFilename() const; 60 | bool Help() const; 61 | bool LongHelp() const; 62 | bool Version() const; 63 | 64 | private: 65 | bool ParseArguments(int argc, char** argv); 66 | void ValidateArguments(); 67 | bool ValidateStretchSetting(); 68 | bool ValidatePitchSetting(); 69 | bool ValidateResampleSetting(); 70 | 71 | bool valid_{true}; 72 | std::string errorMessage_; 73 | 74 | std::map argumentsGiven_; 75 | 76 | // The stretch factor must be between 0.01 and 10.0 77 | const double minimumStretchFactor_{0.01}; 78 | const double maximumStretchFactor_{10.0}; 79 | 80 | // The pitch shift must be between -24.0 and +24.0 semitones 81 | const double minimumPitchShift_{-24.0}; 82 | const double maximumPitchShift_{24.0}; 83 | 84 | // The stretch factor must be between 0.01 and 10.0 85 | const std::size_t minimumResampleFrequency_{1000}; 86 | const std::size_t maximumResampleFrequency_{192000}; 87 | 88 | struct ArgumentTraits 89 | { 90 | ArgumentTraits() : acceptsValue_{false}, requiresValue_{false} { } 91 | ArgumentTraits(const std::string& shortArgument) : shortArgument_{shortArgument}, acceptsValue_{false}, requiresValue_{false} { } 92 | ArgumentTraits(const std::string& shortArgument, bool acceptsValue, bool requiresValue) : 93 | shortArgument_{shortArgument}, acceptsValue_{acceptsValue}, requiresValue_{requiresValue} { } 94 | std::string shortArgument_; 95 | bool acceptsValue_; 96 | bool requiresValue_; 97 | }; 98 | 99 | std::map possibleArguments_; 100 | }; -------------------------------------------------------------------------------- /Source/Application/PhaseVocoder.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * PhaseVocoder 3 | * 4 | * Copyright (c) 2017 - Terence M. Darwen - tmdarwen.com 5 | * 6 | * The MIT License 7 | * 8 | * Permission is hereby granted, free of charge, to any person obtaining a copy 9 | * of this software and associated documentation files (the "Software"), to deal 10 | * in the Software without restriction, including without limitation the rights 11 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | * copies of the Software, and to permit persons to whom the Software is 13 | * furnished to do so, subject to the following conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be included in 16 | * all copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | * THE SOFTWARE. 25 | */ 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | 38 | const uint32_t SUCCESS{0}; 39 | const uint32_t FAILURE{1}; 40 | 41 | void CheckCommandLineArguments(CommandLineArguments& commandLineArguments); 42 | std::unique_ptr GetPhaseVocoderMediator(const CommandLineArguments& commandLineArguments); 43 | int PerformPhaseVocoding(CommandLineArguments& commandLineArguments); 44 | void DisplayTransients(const std::unique_ptr& phaseVocoderMediator); 45 | void DisplayAllTransientsOnChannel(const std::vector& transients); 46 | 47 | int main(int argc, char* argv[]) 48 | { 49 | CommandLineArguments commandLineArguments(argc, argv); 50 | 51 | CheckCommandLineArguments(commandLineArguments); 52 | return PerformPhaseVocoding(commandLineArguments); 53 | } 54 | 55 | void CheckCommandLineArguments(CommandLineArguments& commandLineArguments) 56 | { 57 | if(!commandLineArguments.IsValid()) 58 | { 59 | std::cout << commandLineArguments.GetErrorMessage() << std::endl; 60 | std::cout << std::endl; 61 | DisplayShortHelp(); 62 | exit(FAILURE); 63 | } 64 | 65 | if(commandLineArguments.Help()) 66 | { 67 | DisplayShortHelp(); 68 | exit(SUCCESS); 69 | } 70 | 71 | if(commandLineArguments.LongHelp()) 72 | { 73 | DisplayLongHelp(); 74 | exit(SUCCESS); 75 | } 76 | 77 | if(commandLineArguments.Version()) 78 | { 79 | DisplayVersion(); 80 | exit(SUCCESS); 81 | } 82 | } 83 | 84 | std::unique_ptr GetPhaseVocoderMediator(const CommandLineArguments& commandLineArguments) 85 | { 86 | PhaseVocoderSettings phaseVocoderSettings; 87 | 88 | if(commandLineArguments.InputFilenameGiven()) 89 | { 90 | phaseVocoderSettings.SetInputWaveFile(commandLineArguments.GetInputFilename()); 91 | } 92 | 93 | if(commandLineArguments.OutputFilenameGiven()) 94 | { 95 | phaseVocoderSettings.SetOutputWaveFile(commandLineArguments.GetOutputFilename()); 96 | } 97 | 98 | if(commandLineArguments.StretchFactorGiven()) 99 | { 100 | phaseVocoderSettings.SetStretchFactor(commandLineArguments.GetStretchFactor()); 101 | } 102 | 103 | if(commandLineArguments.ResampleSettingGiven()) 104 | { 105 | phaseVocoderSettings.SetResampleValue(commandLineArguments.GetResampleSetting()); 106 | } 107 | 108 | if(commandLineArguments.PitchSettingGiven()) 109 | { 110 | phaseVocoderSettings.SetPitchShiftValue(commandLineArguments.GetPitchSetting()); 111 | } 112 | 113 | if(commandLineArguments.TransientConfigFileGiven()) 114 | { 115 | phaseVocoderSettings.SetTransientConfigFilename(commandLineArguments.GetTransientConfigFilename()); 116 | } 117 | 118 | if(commandLineArguments.ValleyPeakRatioGiven()) 119 | { 120 | phaseVocoderSettings.SetValleyToPeakRatio(commandLineArguments.GetValleyPeakRatio()); 121 | } 122 | 123 | if(commandLineArguments.ShowTransients()) 124 | { 125 | phaseVocoderSettings.SetDisplayTransients(); 126 | } 127 | 128 | return std::unique_ptr{new PhaseVocoderMediator(phaseVocoderSettings)}; 129 | } 130 | 131 | int PerformPhaseVocoding(CommandLineArguments& commandLineArguments) 132 | { 133 | try 134 | { 135 | auto phaseVocoderMediator{GetPhaseVocoderMediator(commandLineArguments)}; 136 | phaseVocoderMediator->Process(); 137 | 138 | std::cout << "Total Processing Time: " << phaseVocoderMediator->GetTotalProcessingTime() << std::endl; 139 | if(phaseVocoderMediator->GetChannelCount() == 2) 140 | { 141 | std::cout << "Write Buffer Highwater Mark: " << phaseVocoderMediator->GetMaxBufferedSamples() << std::endl; 142 | } 143 | 144 | if(commandLineArguments.ShowTransients()) 145 | { 146 | DisplayTransients(phaseVocoderMediator); 147 | } 148 | 149 | std::cout << std::endl; // Newline so prompt displays below output 150 | } 151 | catch(Utilities::Exception& exception) 152 | { 153 | std::cerr << "Error: " << exception.what() << std::endl; 154 | return FAILURE; 155 | } 156 | 157 | return SUCCESS; 158 | } 159 | 160 | void DisplayTransients(const std::unique_ptr& phaseVocoderMediator) 161 | { 162 | 163 | if(phaseVocoderMediator->GetChannelCount() == 1) 164 | { 165 | auto transients{phaseVocoderMediator->GetTransients(0)}; 166 | 167 | if(transients.size() == 0) 168 | { 169 | std::cout << "No transients found" << std::endl; 170 | } 171 | else 172 | { 173 | std::cout << "Transient sample positions:"; 174 | DisplayAllTransientsOnChannel(transients); 175 | } 176 | } 177 | else if(phaseVocoderMediator->GetChannelCount() == 2) 178 | { 179 | auto leftTransients{phaseVocoderMediator->GetTransients(0)}; 180 | auto rightTransients{phaseVocoderMediator->GetTransients(1)}; 181 | 182 | if(leftTransients.size() == 0 && rightTransients.size() == 0) 183 | { 184 | std::cout << "No transients found" << std::endl; 185 | } 186 | 187 | if(leftTransients.size()) 188 | { 189 | std::cout << "Left channel transient sample positions:"; 190 | DisplayAllTransientsOnChannel(leftTransients); 191 | std::cout << std::endl; 192 | } 193 | 194 | if(rightTransients.size()) 195 | { 196 | std::cout << "Right channel transient sample positions:"; 197 | DisplayAllTransientsOnChannel(rightTransients); 198 | } 199 | } 200 | } 201 | 202 | void DisplayAllTransientsOnChannel(const std::vector& transients) 203 | { 204 | std::size_t counter{0}; 205 | const std::size_t minimumTableSize{8}; 206 | 207 | auto ConvertNumberToStringWithCommas{[](std::size_t number) 208 | { 209 | std::stringstream ss; 210 | ss.imbue(std::locale("")); 211 | ss << std::fixed << number; 212 | return ss.str(); 213 | }}; 214 | 215 | auto PrintSamplePositionInLine{[&ConvertNumberToStringWithCommas](const std::size_t& samplePosition) 216 | { 217 | std::cout << " " << ConvertNumberToStringWithCommas(samplePosition); 218 | }}; 219 | 220 | auto PrintSamplePositionInTable{[&counter, &ConvertNumberToStringWithCommas](const std::size_t& samplePosition) 221 | { 222 | const std::size_t columns{5}; 223 | const std::size_t columnWidth{14}; 224 | 225 | if(counter % columns == 0) 226 | { 227 | std::cout << std::endl; 228 | } 229 | 230 | std::cout << std::fixed << std::setw(columnWidth) << ConvertNumberToStringWithCommas(samplePosition); 231 | 232 | ++counter; 233 | }}; 234 | 235 | if(transients.size() < minimumTableSize) 236 | { 237 | std::for_each(transients.begin(), transients.end(), PrintSamplePositionInLine); 238 | } 239 | else 240 | { 241 | std::for_each(transients.begin(), transients.end(), PrintSamplePositionInTable); 242 | } 243 | } -------------------------------------------------------------------------------- /Source/Application/PhaseVocoderMediator.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * PhaseVocoder 3 | * 4 | * Copyright (c) 2017 - Terence M. Darwen - tmdarwen.com 5 | * 6 | * The MIT License 7 | * 8 | * Permission is hereby granted, free of charge, to any person obtaining a copy 9 | * of this software and associated documentation files (the "Software"), to deal 10 | * in the Software without restriction, including without limitation the rights 11 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | * copies of the Software, and to permit persons to whom the Software is 13 | * furnished to do so, subject to the following conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be included in 16 | * all copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | * THE SOFTWARE. 25 | */ 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | 35 | PhaseVocoderMediator::PhaseVocoderMediator(const PhaseVocoderSettings& settings) : settings_{settings} 36 | { 37 | InstantiateAudioFileObjects(); 38 | } 39 | 40 | PhaseVocoderMediator::~PhaseVocoderMediator() { } 41 | 42 | void PhaseVocoderMediator::InstantiateAudioFileObjects() 43 | { 44 | if(!settings_.InputWaveFileGiven()) 45 | { 46 | Utilities::ThrowException("No input wave file given to PhaseVocoderProcessor"); 47 | } 48 | 49 | audioFileReader_.reset(new ThreadSafeAudioFile::Reader{settings_.GetInputWaveFile()}); 50 | 51 | if(settings_.OutputWaveFileGiven()) 52 | { 53 | std::size_t outputSampleRate{audioFileReader_->GetSampleRate()}; 54 | if(settings_.ResampleValueGiven()) 55 | { 56 | outputSampleRate = settings_.GetResampleValue(); 57 | } 58 | 59 | audioFileWriter_.reset(new ThreadSafeAudioFile::Writer(settings_.GetOutputWaveFile(), 60 | static_cast(audioFileReader_->GetChannels()), 61 | static_cast(outputSampleRate), 62 | static_cast(audioFileReader_->GetBitsPerSample()))); 63 | } 64 | } 65 | void PhaseVocoderMediator::Process() 66 | { 67 | Utilities::Timer timer(Utilities::Timer::Action::START_NOW); 68 | 69 | if(audioFileReader_->GetChannels() == 1) 70 | { 71 | PhaseVocoderProcessor processor(0, settings_, audioFileReader_, audioFileWriter_); 72 | processor.Process(); 73 | transients_.push_back(processor.GetTransients()); 74 | } 75 | else if(audioFileReader_->GetChannels() == 2) 76 | { 77 | PhaseVocoderProcessor leftChannelProcessor(0, settings_, audioFileReader_, audioFileWriter_); 78 | PhaseVocoderProcessor rightChannelProcessor(1, settings_, audioFileReader_, audioFileWriter_); 79 | 80 | auto leftChannelThread{std::thread([&]{leftChannelProcessor.Process();})}; 81 | auto rightChannelThread{std::thread([&]{rightChannelProcessor.Process();})}; 82 | 83 | leftChannelThread.join(); 84 | rightChannelThread.join(); 85 | 86 | transients_.push_back(leftChannelProcessor.GetTransients()); 87 | transients_.push_back(rightChannelProcessor.GetTransients()); 88 | } 89 | else 90 | { 91 | Utilities::ThrowException("PhaseVocoder only handles mono or stereo audio data"); 92 | } 93 | 94 | totalProcessingTime_ = timer.Stop(); 95 | } 96 | 97 | double PhaseVocoderMediator::GetTotalProcessingTime() 98 | { 99 | return totalProcessingTime_; 100 | } 101 | 102 | std::size_t PhaseVocoderMediator::GetChannelCount() const 103 | { 104 | return audioFileReader_->GetChannels(); 105 | } 106 | 107 | std::size_t PhaseVocoderMediator::GetMaxBufferedSamples() 108 | { 109 | if(audioFileWriter_) return audioFileWriter_->GetMaxBufferedSamples(); 110 | else return 0; 111 | } 112 | 113 | const std::vector& PhaseVocoderMediator::GetTransients(std::size_t streamID) 114 | { 115 | return transients_[streamID]; 116 | } 117 | -------------------------------------------------------------------------------- /Source/Application/PhaseVocoderMediator.h: -------------------------------------------------------------------------------- 1 | /* 2 | * PhaseVocoder 3 | * 4 | * Copyright (c) 2017 - Terence M. Darwen - tmdarwen.com 5 | * 6 | * The MIT License 7 | * 8 | * Permission is hereby granted, free of charge, to any person obtaining a copy 9 | * of this software and associated documentation files (the "Software"), to deal 10 | * in the Software without restriction, including without limitation the rights 11 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | * copies of the Software, and to permit persons to whom the Software is 13 | * furnished to do so, subject to the following conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be included in 16 | * all copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | * THE SOFTWARE. 25 | */ 26 | 27 | #include 28 | #include 29 | #include 30 | 31 | class PhaseVocoderMediator 32 | { 33 | public: 34 | PhaseVocoderMediator(const PhaseVocoderSettings& settings); 35 | virtual ~PhaseVocoderMediator(); 36 | 37 | void InstantiateAudioFileObjects(); 38 | void Process(); 39 | 40 | std::size_t GetChannelCount() const; 41 | 42 | std::size_t GetMaxBufferedSamples(); // High water mark for stereo data buffered 43 | 44 | const std::vector& GetTransients(std::size_t streamID); 45 | 46 | double GetTotalProcessingTime(); 47 | double GetTransientProcessingTime(); 48 | double GetPhaseVocoderProcessingTime(); 49 | double GetResamplerProcessingTime(); 50 | 51 | private: 52 | std::shared_ptr audioFileReader_; 53 | std::shared_ptr audioFileWriter_; 54 | 55 | std::vector> transients_; 56 | 57 | PhaseVocoderSettings settings_; 58 | 59 | double totalProcessingTime_{0.0}; 60 | }; -------------------------------------------------------------------------------- /Source/Application/PhaseVocoderProcessor.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * PhaseVocoder 3 | * 4 | * Copyright (c) 2017 - Terence M. Darwen - tmdarwen.com 5 | * 6 | * The MIT License 7 | * 8 | * Permission is hereby granted, free of charge, to any person obtaining a copy 9 | * of this software and associated documentation files (the "Software"), to deal 10 | * in the Software without restriction, including without limitation the rights 11 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | * copies of the Software, and to permit persons to whom the Software is 13 | * furnished to do so, subject to the following conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be included in 16 | * all copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | * THE SOFTWARE. 25 | */ 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | 40 | PhaseVocoderProcessor::PhaseVocoderProcessor(std::size_t streamID, 41 | const PhaseVocoderSettings& settings, 42 | std::shared_ptr audioFileReader, 43 | std::shared_ptr audioFileWriter) : 44 | streamID_{streamID}, 45 | settings_{settings}, 46 | audioFileReader_{audioFileReader}, 47 | audioFileWriter_{audioFileWriter} 48 | { 49 | 50 | } 51 | 52 | PhaseVocoderProcessor::~PhaseVocoderProcessor() 53 | { 54 | 55 | } 56 | 57 | void PhaseVocoderProcessor::Process() 58 | { 59 | InstantiateResampler(); 60 | 61 | if(settings_.StretchFactorGiven() || settings_.PitchShiftValueGiven() || settings_.DisplayTransients()) 62 | { 63 | // Even if we don't stretch the audio, and are just pitch shifting, the pitch shifter requires 64 | // stretching, and if we're stretching, we want to properly handle transients so the audio 65 | // quality doesn't suffer. 66 | ObtainTransients(); 67 | } 68 | 69 | // If we're not stretching, pitch shifting or resampling the audio, we must have only been 70 | // displaying transients (via the transient callback) so we're done processing at this point. 71 | if(!settings_.StretchFactorGiven() && !settings_.PitchShiftValueGiven() && !settings_.ResampleValueGiven()) 72 | { 73 | return; 74 | } 75 | 76 | // We process audio by transient sections. If no stretching of audio is happening (i.e. we're just 77 | // resampling), we just treat the audio as one single transient section. 78 | if(settings_.StretchFactorGiven() || settings_.PitchShiftValueGiven() || settings_.DisplayTransients()) 79 | { 80 | // This handles any leading silence given in the input. 81 | HandleLeadingSilence(); 82 | 83 | std::size_t transientStartIndex{0}; 84 | auto transientPositions{transients_->GetTransients()}; 85 | while(transientStartIndex < (transientPositions.size() - 1)) 86 | { 87 | ProcessAudioSection(transientPositions[transientStartIndex], transientPositions[transientStartIndex + 1]); 88 | ++transientStartIndex; 89 | } 90 | 91 | // Handle the last transient section to the end of input 92 | ProcessAudioSection(transientPositions[transientStartIndex], audioFileReader_->GetSampleCount()); 93 | } 94 | else 95 | { 96 | // If we're here, we are just resampling the audio. No transients, no stretching. 97 | ProcessAudioSection(0, audioFileReader_->GetSampleCount()); 98 | } 99 | 100 | // Flush the Resampler (if we're using it) 101 | if(settings_.ResampleValueGiven() || settings_.PitchShiftValueGiven()) 102 | { 103 | auto audioData{resampler_->FlushAudioData()}; 104 | audioFileWriter_->WriteAudioStream(streamID_, audioData.GetData()); 105 | } 106 | } 107 | 108 | void PhaseVocoderProcessor::ObtainTransients() 109 | { 110 | TransientSettings transientSettings; 111 | 112 | transientSettings.SetStreamID(streamID_); 113 | 114 | if(settings_.InputWaveFileGiven()) 115 | { 116 | transientSettings.SetAudioFile(audioFileReader_); 117 | } 118 | 119 | if(settings_.TransientConfigFilenameGiven()) 120 | { 121 | transientSettings.SetTransientConfigFilename(settings_.GetTransientConfigFilename()); 122 | } 123 | 124 | transientSettings.SetTransientValleyToPeakRatio(settings_.GetValleyToPeakRatio()); 125 | 126 | transients_.reset(new Transients(transientSettings)); 127 | transients_->GetTransients(); 128 | } 129 | 130 | void PhaseVocoderProcessor::HandleLeadingSilence() 131 | { 132 | auto transientPositions{transients_->GetTransients()}; 133 | 134 | if(transientPositions.size() == 0) 135 | { 136 | HandleSilenceInInput(audioFileReader_->GetSampleCount()); 137 | } 138 | else if(transientPositions[0] != 0) 139 | { 140 | HandleSilenceInInput(transientPositions[0]); 141 | } 142 | } 143 | 144 | AudioData PhaseVocoderProcessor::GetAudioInput(std::size_t startSample, std::size_t length) 145 | { 146 | return audioFileReader_->ReadAudioStream(streamID_, startSample, length); 147 | } 148 | 149 | void PhaseVocoderProcessor::HandleSilenceInInput(std::size_t sampleCount) 150 | { 151 | std::size_t samplesToOutput{static_cast(static_cast(sampleCount) * settings_.GetStretchFactor() + 0.5)}; 152 | 153 | std::size_t currentSamplePosition{0}; 154 | while(currentSamplePosition < samplesToOutput) 155 | { 156 | std::size_t currentWriteAmount{std::min(bufferSize_, samplesToOutput - currentSamplePosition)}; 157 | 158 | AudioData silentAudioData; 159 | silentAudioData.AddSilence(currentWriteAmount); 160 | 161 | audioFileWriter_->WriteAudioStream(streamID_, silentAudioData.GetData()); 162 | 163 | currentSamplePosition += currentWriteAmount; 164 | } 165 | } 166 | 167 | void PhaseVocoderProcessor::ProcessAudioSection(std::size_t startSamplePosition, std::size_t endSamplePosition) 168 | { 169 | std::size_t totalSamplesToRead{endSamplePosition - startSamplePosition}; 170 | 171 | InstantiatePhaseVocoder(endSamplePosition - startSamplePosition); 172 | samplesOutputFromCurrentPhaseVocoder_ = 0; 173 | 174 | std::size_t currentSamplePosition{0}; 175 | while(currentSamplePosition < totalSamplesToRead) 176 | { 177 | std::size_t samplesToRead{std::min(bufferSize_, totalSamplesToRead - currentSamplePosition)}; 178 | auto audioInputData{GetAudioInput(startSamplePosition + currentSamplePosition, samplesToRead)}; 179 | ProcessInput(audioInputData); 180 | currentSamplePosition += samplesToRead; 181 | } 182 | 183 | FinalizeAudioSection(totalSamplesToRead); 184 | } 185 | 186 | void PhaseVocoderProcessor::ProcessInput(const AudioData& audioInputData) 187 | { 188 | AudioData resultingAudio; 189 | 190 | if(settings_.PitchShiftValueGiven() || (settings_.StretchFactorGiven() && settings_.ResampleValueGiven())) 191 | { 192 | resultingAudio = ProcessAudioWithResampler(ProcessAudioWithPhaseVocoder(audioInputData)); 193 | } 194 | else if(settings_.StretchFactorGiven() && !settings_.PitchShiftValueGiven()) 195 | { 196 | resultingAudio = ProcessAudioWithPhaseVocoder(audioInputData); 197 | } 198 | else if(settings_.ResampleValueGiven() && !settings_.PitchShiftValueGiven()) 199 | { 200 | resultingAudio = ProcessAudioWithResampler(audioInputData); 201 | } 202 | else 203 | { 204 | Utilities::ThrowException("PhaseVocoderProcessor has no action to perform"); 205 | } 206 | 207 | audioFileWriter_->WriteAudioStream(streamID_, resultingAudio.GetData()); 208 | } 209 | 210 | void PhaseVocoderProcessor::FinalizeAudioSection(std::size_t totalInputSamples) 211 | { 212 | AudioData audioData; 213 | 214 | if(settings_.StretchFactorGiven() || settings_.PitchShiftValueGiven()) 215 | { 216 | std::size_t totalOutputSamplesNeeded{static_cast(totalInputSamples * phaseVocoder_->GetStretchFactor() + 0.5)}; 217 | 218 | // No need to do anything else if we already have the amount of samples we need 219 | if(totalOutputSamplesNeeded < samplesOutputFromCurrentPhaseVocoder_) 220 | { 221 | return; 222 | } 223 | 224 | std::size_t samplesStillNeeded{totalOutputSamplesNeeded - samplesOutputFromCurrentPhaseVocoder_}; 225 | 226 | audioData = FlushPhaseVocoderOutput(samplesStillNeeded); 227 | } 228 | 229 | if(audioData.GetSize() && (settings_.ResampleValueGiven() || settings_.PitchShiftValueGiven())) 230 | { 231 | resampler_->SubmitAudioData(audioData); 232 | } 233 | else if(audioData.GetSize()) 234 | { 235 | audioFileWriter_->WriteAudioStream(streamID_, audioData.GetData()); 236 | } 237 | } 238 | 239 | AudioData PhaseVocoderProcessor::ProcessAudioWithPhaseVocoder(const AudioData& audioInputData) 240 | { 241 | phaseVocoder_->SubmitAudioData(audioInputData); 242 | 243 | AudioData dataToReturn; 244 | 245 | while(phaseVocoder_->OutputSamplesAvailable()) 246 | { 247 | dataToReturn.Append(phaseVocoder_->GetAudioData(std::min(bufferSize_, phaseVocoder_->OutputSamplesAvailable()))); 248 | } 249 | 250 | // If transient overlap data exist, mix it with this output 251 | if(transientSectionOverlap_.GetSize() && (dataToReturn.GetSize() >= transientSectionOverlap_.GetSize())) 252 | { 253 | dataToReturn = LinearCrossfade(transientSectionOverlap_, dataToReturn); 254 | transientSectionOverlap_.Clear(); 255 | } 256 | 257 | samplesOutputFromCurrentPhaseVocoder_ += dataToReturn.GetSize(); 258 | 259 | return dataToReturn; 260 | } 261 | 262 | AudioData PhaseVocoderProcessor::FlushPhaseVocoderOutput(std::size_t samplesNeeded) 263 | { 264 | AudioData audioToReturn; 265 | auto flushedOutput{phaseVocoder_->FlushAudioData()}; 266 | 267 | if(samplesNeeded) 268 | { 269 | if(samplesNeeded > flushedOutput.GetSize()) 270 | { 271 | Utilities::ThrowException("Flushed output has less samples than still needed", samplesNeeded, flushedOutput.GetSize()); 272 | } 273 | 274 | audioToReturn = flushedOutput.RetrieveRemove(samplesNeeded); 275 | 276 | // If transient overlap data exist, mix it with this output 277 | if(transientSectionOverlap_.GetSize()) 278 | { 279 | audioToReturn = LinearCrossfade(transientSectionOverlap_, audioToReturn); 280 | transientSectionOverlap_.Clear(); 281 | } 282 | } 283 | 284 | // Save off transient overlap samples for clean mix/transition to next transient 285 | if(flushedOutput.GetSize() >= transientSectionOverlapSampleCount_) 286 | { 287 | transientSectionOverlap_.Append(flushedOutput.Retrieve(transientSectionOverlapSampleCount_)); 288 | } 289 | 290 | return audioToReturn; 291 | } 292 | 293 | AudioData PhaseVocoderProcessor::ProcessAudioWithResampler(const AudioData& audioInputData) 294 | { 295 | resampler_->SubmitAudioData(audioInputData); 296 | 297 | AudioData dataToReturn; 298 | 299 | while(resampler_->OutputSamplesAvailable()) 300 | { 301 | dataToReturn.Append(resampler_->GetAudioData(std::min(bufferSize_, resampler_->OutputSamplesAvailable()))); 302 | } 303 | 304 | return dataToReturn; 305 | } 306 | 307 | void PhaseVocoderProcessor::InstantiatePhaseVocoder(std::size_t sampleLengthOfAudioToProcess) 308 | { 309 | if(!settings_.GetStretchFactor() && !settings_.PitchShiftValueGiven()) 310 | { 311 | // No Phase Vocoder needed 312 | return; 313 | } 314 | 315 | double stretchFactor{1.0}; 316 | if(settings_.StretchFactorGiven()) 317 | { 318 | stretchFactor = settings_.GetStretchFactor(); 319 | } 320 | 321 | if(settings_.PitchShiftValueGiven()) 322 | { 323 | stretchFactor *= GetPitchShiftRatio(); 324 | } 325 | 326 | phaseVocoder_.reset(new Signal::PhaseVocoder(audioFileReader_->GetSampleRate(), sampleLengthOfAudioToProcess, stretchFactor)); 327 | } 328 | 329 | void PhaseVocoderProcessor::InstantiateResampler() 330 | { 331 | if(!settings_.ResampleValueGiven() && !settings_.PitchShiftValueGiven()) 332 | { 333 | // No Resampler needed 334 | return; 335 | } 336 | 337 | resampler_.reset(new Signal::Resampler(audioFileReader_->GetSampleRate(), GetResampleRatio())); 338 | } 339 | 340 | double PhaseVocoderProcessor::GetPitchShiftRatio() 341 | { 342 | // Google tells me the ratio of a semitone change in pitch can be found by 2^(semitone/12) 343 | return pow(2.0, settings_.GetPitchShiftValue() / 12.0); 344 | } 345 | 346 | double PhaseVocoderProcessor::GetResampleRatio() 347 | { 348 | double resampleRatio{1.0}; 349 | 350 | if(settings_.ResampleValueGiven()) 351 | { 352 | resampleRatio = static_cast(settings_.GetResampleValue()) / static_cast(audioFileReader_->GetSampleRate()); 353 | } 354 | 355 | if(settings_.PitchShiftValueGiven()) 356 | { 357 | resampleRatio = resampleRatio / GetPitchShiftRatio(); 358 | } 359 | 360 | return resampleRatio; 361 | } 362 | 363 | const std::vector& PhaseVocoderProcessor::GetTransients() const 364 | { 365 | if(transients_.get()) return transients_->GetTransients(); 366 | else return noTransients_; 367 | } 368 | -------------------------------------------------------------------------------- /Source/Application/PhaseVocoderProcessor.h: -------------------------------------------------------------------------------- 1 | /* 2 | * PhaseVocoder 3 | * 4 | * Copyright (c) 2017 - Terence M. Darwen - tmdarwen.com 5 | * 6 | * The MIT License 7 | * 8 | * Permission is hereby granted, free of charge, to any person obtaining a copy 9 | * of this software and associated documentation files (the "Software"), to deal 10 | * in the Software without restriction, including without limitation the rights 11 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | * copies of the Software, and to permit persons to whom the Software is 13 | * furnished to do so, subject to the following conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be included in 16 | * all copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | * THE SOFTWARE. 25 | */ 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | 34 | namespace Signal 35 | { 36 | class PhaseVocoder; 37 | class Resampler; 38 | } 39 | 40 | namespace ThreadSafeAudioFile 41 | { 42 | class Reader; 43 | class Writer; 44 | } 45 | 46 | class PhaseVocoderProcessor 47 | { 48 | public: 49 | PhaseVocoderProcessor(std::size_t streamID, const PhaseVocoderSettings& settings, 50 | std::shared_ptr audioFileReader, 51 | std::shared_ptr audioFileWriter); 52 | virtual ~PhaseVocoderProcessor(); 53 | 54 | void Process(); 55 | 56 | const std::vector& GetTransients() const; 57 | 58 | private: 59 | void HandleSilenceInInput(std::size_t sampleCount); 60 | 61 | void ProcessAudioSection(std::size_t startSamplePosition, std::size_t endSamplePosition); 62 | 63 | AudioData GetAudioInput(std::size_t startSample, std::size_t length); 64 | 65 | void HandleLeadingSilence(); 66 | 67 | AudioData FlushPhaseVocoderOutput(std::size_t samplesNeeded); 68 | 69 | void InstantiatePhaseVocoder(std::size_t sampleLengthOfAudioToProcess); 70 | void InstantiateResampler(); 71 | 72 | void ProcessInput(const AudioData& audioInputData); 73 | void FinalizeAudioSection(std::size_t totalInputSamples); 74 | AudioData ProcessAudioWithPhaseVocoder(const AudioData& audioInputData); 75 | AudioData ProcessAudioWithResampler(const AudioData& audioInputData); 76 | 77 | double GetPitchShiftRatio(); 78 | double GetResampleRatio(); 79 | 80 | std::size_t samplesOutputFromCurrentPhaseVocoder_{0}; 81 | 82 | AudioData transientSectionOverlap_; 83 | std::size_t transientSectionOverlapSampleCount_{64}; // The number of samples to crossfade-mix between output transient sections 84 | 85 | std::size_t streamID_; 86 | 87 | PhaseVocoderSettings settings_; 88 | 89 | std::size_t bufferSize_{8192}; 90 | 91 | void ObtainTransients(); 92 | 93 | std::unique_ptr transients_; 94 | std::unique_ptr phaseVocoder_; 95 | std::unique_ptr resampler_; 96 | std::shared_ptr audioFileReader_; 97 | std::shared_ptr audioFileWriter_; 98 | 99 | std::vector noTransients_; 100 | 101 | }; 102 | -------------------------------------------------------------------------------- /Source/Application/PhaseVocoderSettings.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * PhaseVocoder 3 | * 4 | * Copyright (c) 2017 - Terence M. Darwen - tmdarwen.com 5 | * 6 | * The MIT License 7 | * 8 | * Permission is hereby granted, free of charge, to any person obtaining a copy 9 | * of this software and associated documentation files (the "Software"), to deal 10 | * in the Software without restriction, including without limitation the rights 11 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | * copies of the Software, and to permit persons to whom the Software is 13 | * furnished to do so, subject to the following conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be included in 16 | * all copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | * THE SOFTWARE. 25 | */ 26 | 27 | #include 28 | 29 | void PhaseVocoderSettings::SetInputWaveFile(const std::string& filename) 30 | { 31 | inputWaveFilename_ = filename; 32 | inputWaveFilenameGiven_ = true; 33 | } 34 | 35 | void PhaseVocoderSettings::SetOutputWaveFile(const std::string& filename) 36 | { 37 | outputWaveFilename_ = filename; 38 | outputWaveFilenameGiven_ = true; 39 | } 40 | 41 | void PhaseVocoderSettings::SetStretchFactor(double stretchFactor) 42 | { 43 | stretchFactor_ = stretchFactor; 44 | stretchFactorGiven_ = true; 45 | } 46 | 47 | void PhaseVocoderSettings::SetResampleValue(std::size_t resampleValue) 48 | { 49 | resampleValue_ = resampleValue; 50 | resampleValueGiven_ = true; 51 | } 52 | 53 | void PhaseVocoderSettings::SetPitchShiftValue(double pitchShiftValue) 54 | { 55 | pitchShiftValue_ = pitchShiftValue; 56 | pitchShiftValueGiven_ = true; 57 | } 58 | 59 | void PhaseVocoderSettings::SetValleyToPeakRatio(double valleyToPeakRatio) 60 | { 61 | valleyToPeakRatio_ = valleyToPeakRatio; 62 | valleyToPeakRatioGiven_ = true; 63 | } 64 | 65 | void PhaseVocoderSettings::SetDisplayTransients() 66 | { 67 | displayTransients_ = true; 68 | } 69 | 70 | void PhaseVocoderSettings::SetTransientConfigFilename(const std::string& transientConfgFilename) 71 | { 72 | transientConfigFilename_ = transientConfgFilename; 73 | transientConfigFilenameGiven_ = true; 74 | } 75 | 76 | bool PhaseVocoderSettings::InputWaveFileGiven() const 77 | { 78 | return inputWaveFilenameGiven_; 79 | } 80 | 81 | bool PhaseVocoderSettings::OutputWaveFileGiven() const 82 | { 83 | return outputWaveFilenameGiven_; 84 | } 85 | 86 | bool PhaseVocoderSettings::StretchFactorGiven() const 87 | { 88 | return stretchFactorGiven_; 89 | } 90 | 91 | bool PhaseVocoderSettings::ResampleValueGiven() const 92 | { 93 | return resampleValueGiven_; 94 | } 95 | 96 | bool PhaseVocoderSettings::PitchShiftValueGiven() const 97 | { 98 | return pitchShiftValueGiven_; 99 | } 100 | 101 | bool PhaseVocoderSettings::ValleyToPeakRatioGiven() const 102 | { 103 | return valleyToPeakRatioGiven_; 104 | } 105 | 106 | bool PhaseVocoderSettings::TransientConfigFilenameGiven() const 107 | { 108 | return transientConfigFilenameGiven_; 109 | } 110 | 111 | bool PhaseVocoderSettings::DisplayTransients() const 112 | { 113 | return displayTransients_; 114 | } 115 | 116 | const std::string& PhaseVocoderSettings::GetInputWaveFile() const 117 | { 118 | return inputWaveFilename_; 119 | } 120 | 121 | const std::string& PhaseVocoderSettings::GetOutputWaveFile() const 122 | { 123 | return outputWaveFilename_; 124 | } 125 | 126 | double PhaseVocoderSettings::GetStretchFactor() const 127 | { 128 | return stretchFactor_; 129 | } 130 | 131 | std::size_t PhaseVocoderSettings::GetResampleValue() const 132 | { 133 | return resampleValue_; 134 | } 135 | 136 | double PhaseVocoderSettings::GetPitchShiftValue() const 137 | { 138 | return pitchShiftValue_; 139 | } 140 | 141 | const std::string& PhaseVocoderSettings::GetTransientConfigFilename() const 142 | { 143 | return transientConfigFilename_; 144 | } 145 | 146 | double PhaseVocoderSettings::GetValleyToPeakRatio() const 147 | { 148 | return valleyToPeakRatio_; 149 | } -------------------------------------------------------------------------------- /Source/Application/PhaseVocoderSettings.h: -------------------------------------------------------------------------------- 1 | /* 2 | * PhaseVocoder 3 | * 4 | * Copyright (c) 2017 - Terence M. Darwen - tmdarwen.com 5 | * 6 | * The MIT License 7 | * 8 | * Permission is hereby granted, free of charge, to any person obtaining a copy 9 | * of this software and associated documentation files (the "Software"), to deal 10 | * in the Software without restriction, including without limitation the rights 11 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | * copies of the Software, and to permit persons to whom the Software is 13 | * furnished to do so, subject to the following conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be included in 16 | * all copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | * THE SOFTWARE. 25 | */ 26 | 27 | #pragma once 28 | 29 | #include 30 | #include 31 | #include 32 | 33 | class PhaseVocoderSettings 34 | { 35 | public: 36 | // Typical setter methods 37 | void SetInputWaveFile(const std::string& filename); 38 | void SetOutputWaveFile(const std::string& filename); 39 | void SetStretchFactor(double stretchFactor); 40 | void SetResampleValue(std::size_t resampleValue); 41 | void SetPitchShiftValue(double pitchShiftValue); 42 | void SetTransientConfigFilename(const std::string& transientConfgFilename); 43 | void SetDisplayTransients(); 44 | void SetValleyToPeakRatio(double valleyToPeakRatio); 45 | 46 | // Methods to check if a value was actually given 47 | bool InputWaveFileGiven() const; 48 | bool OutputWaveFileGiven() const; 49 | bool StretchFactorGiven() const; 50 | bool ResampleValueGiven() const; 51 | bool PitchShiftValueGiven() const; 52 | bool TransientConfigFilenameGiven() const; 53 | bool DisplayTransients() const; 54 | bool ValleyToPeakRatioGiven() const; 55 | 56 | // Typical getter methods 57 | const std::string& GetInputWaveFile() const; 58 | const std::string& GetOutputWaveFile() const; 59 | double GetStretchFactor() const; 60 | std::size_t GetResampleValue() const; 61 | double GetPitchShiftValue() const; 62 | const std::string& GetTransientConfigFilename() const; 63 | double GetValleyToPeakRatio() const; 64 | 65 | private: 66 | std::string inputWaveFilename_; 67 | bool inputWaveFilenameGiven_{false}; 68 | 69 | std::string outputWaveFilename_; 70 | bool outputWaveFilenameGiven_{false}; 71 | 72 | double stretchFactor_; 73 | bool stretchFactorGiven_{false}; 74 | 75 | std::size_t resampleValue_; 76 | bool resampleValueGiven_{false}; 77 | 78 | double pitchShiftValue_; 79 | bool pitchShiftValueGiven_{false}; 80 | 81 | bool displayTransients_{false}; 82 | 83 | std::string transientConfigFilename_; 84 | bool transientConfigFilenameGiven_{false}; 85 | 86 | double valleyToPeakRatio_{1.5}; 87 | bool valleyToPeakRatioGiven_{false}; 88 | }; -------------------------------------------------------------------------------- /Source/Application/TransientConfigFile.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * PhaseVocoder 3 | * 4 | * Copyright (c) 2017 - Terence M. Darwen - tmdarwen.com 5 | * 6 | * The MIT License 7 | * 8 | * Permission is hereby granted, free of charge, to any person obtaining a copy 9 | * of this software and associated documentation files (the "Software"), to deal 10 | * in the Software without restriction, including without limitation the rights 11 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | * copies of the Software, and to permit persons to whom the Software is 13 | * furnished to do so, subject to the following conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be included in 16 | * all copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | * THE SOFTWARE. 25 | */ 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | 34 | TransientConfigFile::TransientConfigFile(const std::string& filename) 35 | { 36 | ReadTransientConfigFile(filename); 37 | SortTransientVectors(); 38 | } 39 | 40 | TransientConfigFile::~TransientConfigFile() { } 41 | 42 | void TransientConfigFile::ReadTransientConfigFile(const std::string& filename) 43 | { 44 | try 45 | { 46 | YAML::Node config = YAML::LoadFile(filename); 47 | 48 | for(auto transient : config["transients"]) 49 | { 50 | transients_.push_back(transient.as()); 51 | } 52 | 53 | for(auto transient : config["left_channel_transients"]) 54 | { 55 | leftChannelTransients_.push_back(transient.as()); 56 | } 57 | leftChannelTransients_.insert(leftChannelTransients_.begin(), transients_.begin(), transients_.end()); 58 | 59 | for(auto transient : config["right_channel_transients"]) 60 | { 61 | rightChannelTransients_.push_back(transient.as()); 62 | } 63 | rightChannelTransients_.insert(rightChannelTransients_.begin(), transients_.begin(), transients_.end()); 64 | 65 | } 66 | catch(std::exception theException) 67 | { 68 | auto exceptionWhat{Utilities::CreateString(" ", "Exception trying to open transient configuration file", filename, "Message from yaml-cpp lib:", theException.what())}; 69 | Utilities::ThrowException(exceptionWhat); 70 | } 71 | } 72 | 73 | void TransientConfigFile::SortTransientVectors() 74 | { 75 | std::sort(transients_.begin(), transients_.end()); 76 | std::sort(leftChannelTransients_.begin(), leftChannelTransients_.end()); 77 | std::sort(rightChannelTransients_.begin(), rightChannelTransients_.end()); 78 | } 79 | 80 | const std::vector& TransientConfigFile::GetTransients() const 81 | { 82 | return transients_; 83 | } 84 | 85 | const std::vector& TransientConfigFile::GetLeftChannelTransients() const 86 | { 87 | return leftChannelTransients_; 88 | } 89 | 90 | const std::vector& TransientConfigFile::GetRightChannelTransients() const 91 | { 92 | return rightChannelTransients_; 93 | } 94 | -------------------------------------------------------------------------------- /Source/Application/TransientConfigFile.h: -------------------------------------------------------------------------------- 1 | /* 2 | * PhaseVocoder 3 | * 4 | * Copyright (c) 2017 - Terence M. Darwen - tmdarwen.com 5 | * 6 | * The MIT License 7 | * 8 | * Permission is hereby granted, free of charge, to any person obtaining a copy 9 | * of this software and associated documentation files (the "Software"), to deal 10 | * in the Software without restriction, including without limitation the rights 11 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | * copies of the Software, and to permit persons to whom the Software is 13 | * furnished to do so, subject to the following conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be included in 16 | * all copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | * THE SOFTWARE. 25 | */ 26 | 27 | #include 28 | #include 29 | 30 | class TransientConfigFile 31 | { 32 | public: 33 | TransientConfigFile(const std::string& filename); 34 | virtual ~TransientConfigFile(); 35 | 36 | const std::vector& GetTransients() const; 37 | const std::vector& GetLeftChannelTransients() const; 38 | const std::vector& GetRightChannelTransients() const; 39 | 40 | private: 41 | void ReadTransientConfigFile(const std::string& filename); 42 | void SortTransientVectors(); 43 | 44 | std::vector transients_; 45 | std::vector leftChannelTransients_; 46 | std::vector rightChannelTransients_; 47 | }; -------------------------------------------------------------------------------- /Source/Application/Transients.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * PhaseVocoder 3 | * 4 | * Copyright (c) 2017 - Terence M. Darwen - tmdarwen.com 5 | * 6 | * The MIT License 7 | * 8 | * Permission is hereby granted, free of charge, to any person obtaining a copy 9 | * of this software and associated documentation files (the "Software"), to deal 10 | * in the Software without restriction, including without limitation the rights 11 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | * copies of the Software, and to permit persons to whom the Software is 13 | * furnished to do so, subject to the following conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be included in 16 | * all copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | * THE SOFTWARE. 25 | */ 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | 36 | ////////////////////////////////////////////////////////////////////////// 37 | // This first group of methods are TransientSettings 38 | 39 | void TransientSettings::SetStreamID(std::size_t streamID) 40 | { 41 | streamID_ = streamID; 42 | } 43 | 44 | void TransientSettings::SetAudioFile(std::shared_ptr audioFile) 45 | { 46 | audioFile_ = audioFile; 47 | } 48 | 49 | void TransientSettings::SetTransientValleyToPeakRatio(double valleyToPeakRatio) 50 | { 51 | valleyToPeakRatio_ = valleyToPeakRatio; 52 | } 53 | 54 | void TransientSettings::SetTransientConfigFilename(const std::string& transientConfgFilename) 55 | { 56 | transientConfigFilename_ = transientConfgFilename; 57 | transientConfigFilenameGiven_ = true; 58 | } 59 | 60 | std::size_t TransientSettings::GetStreamID() const 61 | { 62 | return streamID_; 63 | } 64 | 65 | bool TransientSettings::TransientConfigFilenameGiven() const 66 | { 67 | return transientConfigFilenameGiven_; 68 | } 69 | 70 | std::shared_ptr TransientSettings::GetAudioFile() const 71 | { 72 | return audioFile_; 73 | } 74 | 75 | const std::string& TransientSettings::GetTransientConfigFilename() const 76 | { 77 | return transientConfigFilename_; 78 | } 79 | 80 | double TransientSettings::GetTransientValleyToPeakRatio() const 81 | { 82 | return valleyToPeakRatio_; 83 | } 84 | 85 | ////////////////////////////////////////////////////////////////////////// 86 | // Now the actual Transient Methods 87 | 88 | Transients::Transients(const TransientSettings& settings) : settings_(settings) { } 89 | 90 | Transients::~Transients() { } 91 | 92 | const std::vector& Transients::GetTransients() 93 | { 94 | if(!transientsProcessed_) 95 | { 96 | if(settings_.TransientConfigFilenameGiven()) 97 | { 98 | GetTransientPositionsFromConfigFile(); 99 | } 100 | else 101 | { 102 | GetTransientPositionsFromAudioFile(); 103 | } 104 | 105 | transientsProcessed_ = true; 106 | } 107 | 108 | return transients_; 109 | } 110 | 111 | // Uses the TransientDetector in our Signal lib to find transients 112 | void Transients::GetTransientPositionsFromAudioFile() 113 | { 114 | auto audioFile{settings_.GetAudioFile()}; 115 | Signal::TransientDetector transientDetector{audioFile->GetSampleRate()}; 116 | transientDetector.SetValleyToPeakRatio(settings_.GetTransientValleyToPeakRatio()); 117 | 118 | std::size_t currentSamplePosition{0}; 119 | const std::size_t bufferSize{8192}; 120 | 121 | std::size_t samplesLeft{settings_.GetAudioFile()->GetSampleCount()}; 122 | while(samplesLeft) 123 | { 124 | std::size_t samplesToRead{std::min(bufferSize, samplesLeft)}; 125 | 126 | auto audioData{audioFile->ReadAudioStream(settings_.GetStreamID(), currentSamplePosition, samplesToRead)}; 127 | 128 | std::vector newTransients; 129 | if(transientDetector.FindTransients(audioData, newTransients)) 130 | { 131 | transients_.insert(transients_.end(), newTransients.begin(), newTransients.end()); 132 | } 133 | 134 | currentSamplePosition += samplesToRead; 135 | samplesLeft -= samplesToRead; 136 | } 137 | } 138 | 139 | void Transients::GetTransientPositionsFromConfigFile() 140 | { 141 | auto transientConfigFile{TransientConfigFile{settings_.GetTransientConfigFilename()}}; 142 | transients_ = transientConfigFile.GetTransients(); 143 | } 144 | -------------------------------------------------------------------------------- /Source/Application/Transients.h: -------------------------------------------------------------------------------- 1 | /* 2 | * PhaseVocoder 3 | * 4 | * Copyright (c) 2017 - Terence M. Darwen - tmdarwen.com 5 | * 6 | * The MIT License 7 | * 8 | * Permission is hereby granted, free of charge, to any person obtaining a copy 9 | * of this software and associated documentation files (the "Software"), to deal 10 | * in the Software without restriction, including without limitation the rights 11 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | * copies of the Software, and to permit persons to whom the Software is 13 | * furnished to do so, subject to the following conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be included in 16 | * all copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | * THE SOFTWARE. 25 | */ 26 | 27 | #pragma once 28 | 29 | #include 30 | #include 31 | #include 32 | #include 33 | 34 | namespace ThreadSafeAudioFile 35 | { 36 | class Reader; 37 | } 38 | 39 | class TransientSettings 40 | { 41 | public: 42 | // Typical setter methods 43 | void SetStreamID(std::size_t streamID); 44 | void SetAudioFile(std::shared_ptr audioFile); 45 | void SetTransientConfigFilename(const std::string& transientConfgFilename); 46 | void SetTransientValleyToPeakRatio(double valleyToPeakRatio); 47 | 48 | // Methods to check if a value was actually given 49 | bool TransientConfigFilenameGiven() const; 50 | 51 | // Typical getter methods 52 | std::size_t GetStreamID() const; 53 | std::shared_ptr GetAudioFile() const; 54 | const std::string& GetTransientConfigFilename() const; 55 | double GetTransientValleyToPeakRatio() const; 56 | 57 | private: 58 | std::size_t streamID_; 59 | 60 | std::string inputWaveFilename_; 61 | bool inputWaveFilenameGiven_{false}; 62 | 63 | std::string transientConfigFilename_; 64 | bool transientConfigFilenameGiven_{false}; 65 | 66 | double valleyToPeakRatio_{1.5}; 67 | 68 | std::shared_ptr audioFile_; 69 | }; 70 | 71 | class Transients 72 | { 73 | public: 74 | Transients(const TransientSettings& settings); 75 | virtual ~Transients(); 76 | 77 | const std::vector& GetTransients(); 78 | 79 | private: 80 | void GetTransientPositionsFromAudioFile(); 81 | void GetTransientPositionsFromConfigFile(); 82 | 83 | TransientSettings settings_; 84 | std::vector transients_; 85 | bool transientsProcessed_{false}; 86 | }; -------------------------------------------------------------------------------- /Source/Application/UT/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include_directories("${PROJECT_SOURCE_DIR}/Externals/googletest/googletest/include") 2 | include_directories("${PROJECT_SOURCE_DIR}/Externals/yaml-cpp/include") 3 | include_directories("${PROJECT_SOURCE_DIR}/Externals/audiolib/Source") 4 | 5 | file(GLOB source_files [^.]*.h [^.]*.cpp 6 | ../PhaseVocoderMediator.h 7 | ../PhaseVocoderMediator.cpp 8 | ../PhaseVocoderProcessor.h 9 | ../PhaseVocoderProcessor.cpp 10 | ../PhaseVocoderSettings.h 11 | ../PhaseVocoderSettings.cpp 12 | ../CommandLineArguments.h 13 | ../CommandLineArguments.cpp 14 | ../Transients.h 15 | ../Transients.cpp 16 | ../TransientConfigFile.h 17 | ../TransientConfigFile.cpp) 18 | 19 | add_executable(PhaseVocoderApp-UT ${source_files}) 20 | include(${PROJECT_SOURCE_DIR}/CMakeSupport/CMakeLists.CompilerSettings.txt) 21 | target_link_libraries(PhaseVocoderApp-UT gtest yaml-cpp AudioData Signal ThreadSafeAudioFile Utilities WaveFile) 22 | 23 | file(GLOB WAV_TEST_FILES ${CMAKE_CURRENT_SOURCE_DIR}/TestAudio/*.wav) 24 | file(COPY ${WAV_TEST_FILES} DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) 25 | 26 | file(GLOB WAV_TEST_RESULTS ${CMAKE_CURRENT_SOURCE_DIR}/TestAudioResults/*.wav) 27 | file(COPY ${WAV_TEST_RESULTS} DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) 28 | 29 | file(GLOB YAML_TEST_FILES ${CMAKE_CURRENT_SOURCE_DIR}/TestTransientConfigFiles/*.yaml) 30 | file(COPY ${YAML_TEST_FILES} DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) 31 | 32 | add_custom_command(TARGET PhaseVocoderApp-UT POST_BUILD COMMAND PhaseVocoderApp-UT --output-on-failure) 33 | 34 | set_target_properties(PhaseVocoderApp-UT PROPERTIES FOLDER Apps) 35 | 36 | -------------------------------------------------------------------------------- /Source/Application/UT/CommandLineArguments-UT.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * PhaseVocoder 3 | * 4 | * Copyright (c) 2017 - Terence M. Darwen - tmdarwen.com 5 | * 6 | * The MIT License 7 | * 8 | * Permission is hereby granted, free of charge, to any person obtaining a copy 9 | * of this software and associated documentation files (the "Software"), to deal 10 | * in the Software without restriction, including without limitation the rights 11 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | * copies of the Software, and to permit persons to whom the Software is 13 | * furnished to do so, subject to the following conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be included in 16 | * all copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | * THE SOFTWARE. 25 | */ 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | // This class constructs command line args as they typically would be by the system before main is called 33 | class FakeCommandLineArguments 34 | { 35 | public: 36 | FakeCommandLineArguments() { } 37 | 38 | ~FakeCommandLineArguments() 39 | { 40 | Deallocate(); 41 | } 42 | 43 | void PushArgument(const std::string& argument) 44 | { 45 | arguments_.push_back(argument); 46 | } 47 | 48 | int GetArgc() 49 | { 50 | return static_cast(arguments_.size()) + 1; 51 | } 52 | 53 | char** GetArgv() 54 | { 55 | Deallocate(); 56 | 57 | char** argv = new char*[GetArgc()]; 58 | argv[0] = new char[64]; 59 | MyStringCopy(argv[0], "ExeName", 63); 60 | 61 | std::size_t index{1}; 62 | for(auto argument : arguments_) 63 | { 64 | argv[index] = new char[argument.size() + 1]; 65 | MyStringCopy(argv[index], argument.c_str(), argument.size()); 66 | ++index; 67 | } 68 | 69 | return argv; 70 | } 71 | 72 | void Deallocate() 73 | { 74 | if(argv_ == nullptr) 75 | { 76 | return; 77 | } 78 | 79 | for(std::size_t i{0}; i < arguments_.size() + 1; ++i) 80 | { 81 | delete [] argv_[i]; 82 | } 83 | 84 | delete [] argv_; 85 | } 86 | 87 | void MyStringCopy(char* destination, const char* source, std::size_t length) 88 | { 89 | 90 | std::size_t i{0}; 91 | for( ; i < length; ++i) 92 | { 93 | destination[i] = source[i]; 94 | } 95 | 96 | destination[i] = 0; 97 | } 98 | 99 | private: 100 | char** argv_{nullptr}; 101 | std::vector arguments_; 102 | }; 103 | 104 | CommandLineArguments CreateCommandLineArguments(const std::string& commandLine) 105 | { 106 | FakeCommandLineArguments fakeCommandLineArguments; 107 | for(auto arg : Utilities::DelimitedStringToVectorOfStrings(commandLine, ' ')) 108 | { 109 | fakeCommandLineArguments.PushArgument(arg); 110 | } 111 | 112 | return CommandLineArguments(fakeCommandLineArguments.GetArgc(), fakeCommandLineArguments.GetArgv()); 113 | } 114 | 115 | void VerifyTypicalUsage(const CommandLineArguments& commandLineArguments) 116 | { 117 | EXPECT_TRUE(commandLineArguments.IsValid()); 118 | EXPECT_STREQ("InputFileName.wav", commandLineArguments.GetInputFilename().c_str()); 119 | EXPECT_STREQ("OutputFileName.wav", commandLineArguments.GetOutputFilename().c_str()); 120 | EXPECT_EQ(1.25, commandLineArguments.GetStretchFactor()); 121 | EXPECT_FALSE(commandLineArguments.ShowTransients()); 122 | EXPECT_FALSE(commandLineArguments.TransientConfigFileGiven()); 123 | EXPECT_STREQ("", commandLineArguments.GetTransientConfigFilename().c_str()); 124 | EXPECT_FALSE(commandLineArguments.Help()); 125 | EXPECT_FALSE(commandLineArguments.LongHelp()); 126 | } 127 | 128 | TEST(CommandLineArguments, TestTypicalUsage) 129 | { 130 | VerifyTypicalUsage(CreateCommandLineArguments("--input InputFileName.wav --output OutputFileName.wav --stretch 1.25")); 131 | VerifyTypicalUsage(CreateCommandLineArguments("-i InputFileName.wav -o OutputFileName.wav -s 1.25")); 132 | } 133 | 134 | void VerifyNoActionGiven(const CommandLineArguments& commandLineArguments) 135 | { 136 | EXPECT_FALSE(commandLineArguments.IsValid()); 137 | EXPECT_STREQ("Nothing to do. No action specified.", commandLineArguments.GetErrorMessage().c_str()); 138 | } 139 | 140 | TEST(CommandLineArguments, TestNoStretchFactorGiven) 141 | { 142 | VerifyNoActionGiven(CreateCommandLineArguments("--input InputFileName.wav --output OutputFileName.wav")); 143 | VerifyNoActionGiven(CreateCommandLineArguments("-i InputFileName.wav -o OutputFileName.wav")); 144 | } 145 | 146 | void VerifyInputFileOnly(const CommandLineArguments& commandLineArguments) 147 | { 148 | EXPECT_FALSE(commandLineArguments.IsValid()); 149 | EXPECT_STREQ("Nothing to do. No action specified.", commandLineArguments.GetErrorMessage().c_str()); 150 | } 151 | 152 | TEST(CommandLineArguments, TestInputFileOnly) 153 | { 154 | VerifyNoActionGiven(CreateCommandLineArguments("--input InputFileName.wav")); 155 | VerifyNoActionGiven(CreateCommandLineArguments("-i InputFileName.wav")); 156 | } 157 | 158 | // Giving no command line parameters shows the "short" help 159 | TEST(CommandLineArguments, TestNoParametersGiven) 160 | { 161 | FakeCommandLineArguments fakeCommandLineArguments; 162 | 163 | CommandLineArguments commandLineArguments(fakeCommandLineArguments.GetArgc(), fakeCommandLineArguments.GetArgv()); 164 | 165 | EXPECT_TRUE(commandLineArguments.IsValid()); 166 | EXPECT_STREQ("", commandLineArguments.GetInputFilename().c_str()); 167 | EXPECT_STREQ("", commandLineArguments.GetOutputFilename().c_str()); 168 | EXPECT_EQ(0.00, commandLineArguments.GetStretchFactor()); 169 | EXPECT_FALSE(commandLineArguments.TransientConfigFileGiven()); 170 | EXPECT_FALSE(commandLineArguments.ShowTransients()); 171 | EXPECT_STREQ("", commandLineArguments.GetTransientConfigFilename().c_str()); 172 | EXPECT_TRUE(commandLineArguments.Help()); 173 | EXPECT_FALSE(commandLineArguments.LongHelp()); 174 | } 175 | 176 | void VerifyJustGettingTransients(const CommandLineArguments& commandLineArguments) 177 | { 178 | EXPECT_TRUE(commandLineArguments.IsValid()); 179 | EXPECT_STREQ("InputFileName.wav", commandLineArguments.GetInputFilename().c_str()); 180 | EXPECT_STREQ("", commandLineArguments.GetOutputFilename().c_str()); 181 | EXPECT_EQ(0.00, commandLineArguments.GetStretchFactor()); 182 | EXPECT_FALSE(commandLineArguments.TransientConfigFileGiven()); 183 | EXPECT_TRUE(commandLineArguments.ShowTransients()); 184 | EXPECT_STREQ("", commandLineArguments.GetTransientConfigFilename().c_str()); 185 | EXPECT_FALSE(commandLineArguments.Help()); 186 | EXPECT_FALSE(commandLineArguments.LongHelp()); 187 | } 188 | 189 | // Just getting the transient positions of an input file is valid 190 | TEST(CommandLineArguments, TestJustGettingTransients) 191 | { 192 | VerifyJustGettingTransients(CreateCommandLineArguments("--input InputFileName.wav --showtransients")); 193 | VerifyJustGettingTransients(CreateCommandLineArguments("-i InputFileName.wav -t")); 194 | } 195 | 196 | void VerifyStretchWithNoOutputFile(const CommandLineArguments& commandLineArguments) 197 | { 198 | EXPECT_FALSE(commandLineArguments.IsValid()); 199 | EXPECT_STREQ("Stretch factor given, but no output file given.", commandLineArguments.GetErrorMessage().c_str()); 200 | } 201 | 202 | TEST(CommandLineArguments, TestStretchWithNoOutputFile) 203 | { 204 | VerifyStretchWithNoOutputFile(CreateCommandLineArguments("--input InputFileName.wav --stretch 1.10 --showtransients")); 205 | VerifyStretchWithNoOutputFile(CreateCommandLineArguments("-i InputFileName.wav -s 1.10 -t")); 206 | } 207 | 208 | void VerifyLongHelp(const CommandLineArguments& commandLineArguments) 209 | { 210 | EXPECT_TRUE(commandLineArguments.IsValid()); 211 | EXPECT_STREQ("", commandLineArguments.GetInputFilename().c_str()); 212 | EXPECT_STREQ("", commandLineArguments.GetOutputFilename().c_str()); 213 | EXPECT_EQ(0.00, commandLineArguments.GetStretchFactor()); 214 | EXPECT_FALSE(commandLineArguments.TransientConfigFileGiven()); 215 | EXPECT_FALSE(commandLineArguments.ShowTransients()); 216 | EXPECT_STREQ("", commandLineArguments.GetTransientConfigFilename().c_str()); 217 | EXPECT_FALSE(commandLineArguments.Help()); 218 | EXPECT_TRUE(commandLineArguments.LongHelp()); 219 | } 220 | 221 | TEST(CommandLineArguments, TestLongHelp) 222 | { 223 | VerifyLongHelp(CreateCommandLineArguments("--longhelp")); 224 | VerifyLongHelp(CreateCommandLineArguments("-l")); 225 | } 226 | 227 | TEST(CommandLineArguments, TestInvalidParameter) 228 | { 229 | FakeCommandLineArguments fakeCommandLineArguments; 230 | fakeCommandLineArguments.PushArgument("MyInvalidParameter"); 231 | 232 | CommandLineArguments commandLineArguments(fakeCommandLineArguments.GetArgc(), fakeCommandLineArguments.GetArgv()); 233 | 234 | EXPECT_FALSE(commandLineArguments.IsValid()); 235 | EXPECT_STREQ("Invalid parameter given: MyInvalidParameter", commandLineArguments.GetErrorMessage().c_str()); 236 | } 237 | 238 | void VerifyTooSmallStretchFactor(const CommandLineArguments& commandLineArguments) 239 | { 240 | EXPECT_FALSE(commandLineArguments.IsValid()); 241 | EXPECT_STREQ("Given stretch factor out of range. Min: 0.010000 Max: 10.000000", commandLineArguments.GetErrorMessage().c_str()); 242 | } 243 | 244 | TEST(CommandLineArguments, TestTooSmallStretchFactor) 245 | { 246 | VerifyTooSmallStretchFactor(CreateCommandLineArguments("--input InputFileName.wav --output OutputFileName.wav --stretch 0.0009")); 247 | VerifyTooSmallStretchFactor(CreateCommandLineArguments("-i InputFileName.wav -o OutputFileName.wav -s 0.0009")); 248 | } 249 | 250 | void VerifyTooLargeStretchFactor(const CommandLineArguments& commandLineArguments) 251 | { 252 | EXPECT_FALSE(commandLineArguments.IsValid()); 253 | EXPECT_STREQ("Given stretch factor out of range. Min: 0.010000 Max: 10.000000", commandLineArguments.GetErrorMessage().c_str()); 254 | } 255 | 256 | TEST(CommandLineArguments, TestTooLargeStretchFactor) 257 | { 258 | VerifyTooLargeStretchFactor(CreateCommandLineArguments("--input InputFileName.wav --output OutputFileName.wav --stretch 11.0")); 259 | VerifyTooLargeStretchFactor(CreateCommandLineArguments("-i InputFileName.wav -o OutputFileName.wav -s 11.0")); 260 | } 261 | 262 | void VerifyValleyToPeakRatio(const CommandLineArguments& commandLineArguments) 263 | { 264 | EXPECT_TRUE(commandLineArguments.IsValid()); 265 | EXPECT_EQ(1.75, commandLineArguments.GetValleyPeakRatio()); 266 | } 267 | 268 | TEST(CommandLineArguments, TestValleyToPeakRatio) 269 | { 270 | VerifyValleyToPeakRatio(CreateCommandLineArguments("--input InputFileName.wav --valleypeakratio 1.75 --showtransients")); 271 | VerifyValleyToPeakRatio(CreateCommandLineArguments("-i InputFileName.wav -a 1.75 -t")); 272 | } 273 | 274 | void VerifyNoValueGivenForRequiredArgument(const CommandLineArguments& commandLineArguments) 275 | { 276 | EXPECT_FALSE(commandLineArguments.IsValid()); 277 | EXPECT_STREQ("No value given for argument requiring value", commandLineArguments.GetErrorMessage().c_str()); 278 | } 279 | 280 | TEST(CommandLineArguments, TestNoValueGivenForRequiredArgument) 281 | { 282 | VerifyNoValueGivenForRequiredArgument(CreateCommandLineArguments("--input InputFileName.wav --output OutputFileName.wav --stretch")); 283 | VerifyNoValueGivenForRequiredArgument(CreateCommandLineArguments("-i InputFileName.wav -o OutputFileName.wav -s")); 284 | } 285 | 286 | -------------------------------------------------------------------------------- /Source/Application/UT/PhaseVocoderMediator-UT.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * PhaseVocoder 3 | * 4 | * Copyright (c) 2017 - Terence M. Darwen - tmdarwen.com 5 | * 6 | * The MIT License 7 | * 8 | * Permission is hereby granted, free of charge, to any person obtaining a copy 9 | * of this software and associated documentation files (the "Software"), to deal 10 | * in the Software without restriction, including without limitation the rights 11 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | * copies of the Software, and to permit persons to whom the Software is 13 | * furnished to do so, subject to the following conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be included in 16 | * all copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | * THE SOFTWARE. 25 | */ 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | 34 | namespace PhaseVocoderMediatorUT { 35 | 36 | void Stretch(const std::string& inputFile, const std::string& outputFile, double stretchFactor) 37 | { 38 | PhaseVocoderSettings phaseVocoderSettings; 39 | phaseVocoderSettings.SetInputWaveFile(inputFile); 40 | phaseVocoderSettings.SetOutputWaveFile(outputFile); 41 | phaseVocoderSettings.SetStretchFactor(stretchFactor); 42 | 43 | PhaseVocoderMediator phaseVocoderMediator(phaseVocoderSettings); 44 | phaseVocoderMediator.Process(); 45 | } 46 | 47 | void Resample(const std::string& inputFile, const std::string& outputFile, std::size_t newSampleRate) 48 | { 49 | PhaseVocoderSettings phaseVocoderSettings; 50 | phaseVocoderSettings.SetInputWaveFile(inputFile); 51 | phaseVocoderSettings.SetOutputWaveFile(outputFile); 52 | phaseVocoderSettings.SetResampleValue(newSampleRate); 53 | 54 | PhaseVocoderMediator phaseVocoderMediator(phaseVocoderSettings); 55 | phaseVocoderMediator.Process(); 56 | } 57 | 58 | void PitchShift(const std::string& inputFile, const std::string& outputFile, double pitchChange) 59 | { 60 | PhaseVocoderSettings phaseVocoderSettings; 61 | phaseVocoderSettings.SetInputWaveFile(inputFile); 62 | phaseVocoderSettings.SetOutputWaveFile(outputFile); 63 | phaseVocoderSettings.SetPitchShiftValue(pitchChange); 64 | 65 | PhaseVocoderMediator phaseVocoderMediator(phaseVocoderSettings); 66 | phaseVocoderMediator.Process(); 67 | } 68 | 69 | std::vector SpecificValleyToPeakRatio(const std::string& inputFile, double valleyToPeakRatio) 70 | { 71 | PhaseVocoderSettings phaseVocoderSettings; 72 | phaseVocoderSettings.SetInputWaveFile(inputFile); 73 | phaseVocoderSettings.SetValleyToPeakRatio(valleyToPeakRatio); 74 | phaseVocoderSettings.SetDisplayTransients(); 75 | 76 | PhaseVocoderMediator phaseVocoderMediator(phaseVocoderSettings); 77 | phaseVocoderMediator.Process(); 78 | 79 | return phaseVocoderMediator.GetTransients(0); 80 | } 81 | 82 | // I'm disabling these for MSVC debug builds b/c they will take minites. It's the FFT that appears to be really slow in the debug build. 83 | // In a release build, running these tests just take a few seconds. 84 | #ifndef _DEBUG 85 | TEST(PhaseVocoderMediator, StretchTest1) 86 | { 87 | PhaseVocoderMediatorUT::Stretch("BuiltToSpillBeatAbbrev.wav", "BuiltToSpillBeatAbbrevCurrentResult1.25.wav", 1.25); 88 | EXPECT_EQ(true, Utilities::File::CheckIfFilesMatch("BuiltToSpillBeatAbbrev1.25.wav", "BuiltToSpillBeatAbbrevCurrentResult1.25.wav")); 89 | } 90 | 91 | TEST(PhaseVocoderMediator, StretchTest2) 92 | { 93 | PhaseVocoderMediatorUT::Stretch("BuiltToSpillBeatAbbrev.wav", "BuiltToSpillBeatAbbrevCurrentResult1.50.wav", 1.50); 94 | EXPECT_EQ(true, Utilities::File::CheckIfFilesMatch("BuiltToSpillBeatAbbrev1.50.wav", "BuiltToSpillBeatAbbrevCurrentResult1.50.wav")); 95 | } 96 | 97 | TEST(PhaseVocoderMediator, StretchTest3) 98 | { 99 | PhaseVocoderMediatorUT::Stretch("BuiltToSpillBeatAbbrev.wav", "BuiltToSpillBeatAbbrevCurrentResult1.75.wav", 1.75); 100 | EXPECT_EQ(true, Utilities::File::CheckIfFilesMatch("BuiltToSpillBeatAbbrev1.75.wav", "BuiltToSpillBeatAbbrev1.75.wav")); 101 | } 102 | 103 | TEST(PhaseVocoderMediator, CompressTest1) 104 | { 105 | PhaseVocoderMediatorUT::Stretch("BuiltToSpillBeatAbbrev.wav", "BuiltToSpillBeatAbbrevCurrentResult0.75.wav", 0.75); 106 | EXPECT_EQ(true, Utilities::File::CheckIfFilesMatch("BuiltToSpillBeatAbbrev0.75.wav", "BuiltToSpillBeatAbbrevCurrentResult0.75.wav")); 107 | } 108 | 109 | TEST(PhaseVocoderMediator, CompressTest2) 110 | { 111 | PhaseVocoderMediatorUT::Stretch("BuiltToSpillBeatAbbrev.wav", "BuiltToSpillBeatAbbrevCurrentResult0.50.wav", 0.50); 112 | EXPECT_EQ(true, Utilities::File::CheckIfFilesMatch("BuiltToSpillBeatAbbrev0.50.wav", "BuiltToSpillBeatAbbrevCurrentResult0.50.wav" )); 113 | } 114 | 115 | TEST(PhaseVocoderMediator, CompressTest3) 116 | { 117 | PhaseVocoderMediatorUT::Stretch("BuiltToSpillBeatAbbrev.wav", "BuiltToSpillBeatAbbrevCurrentResult0.25.wav", 0.25); 118 | EXPECT_EQ(true, Utilities::File::CheckIfFilesMatch("BuiltToSpillBeatAbbrev0.25.wav", "BuiltToSpillBeatAbbrevCurrentResult0.25.wav")); 119 | } 120 | #endif 121 | 122 | TEST(PhaseVocoderMediator, ResampleTest1) 123 | { 124 | PhaseVocoderMediatorUT::Resample("BuiltToSpillBeatAbbrev.wav", "BuiltToSpillBeatAbbrevCurrentResample48000.wav", 48000); 125 | EXPECT_EQ(true, Utilities::File::CheckIfFilesMatch("BuiltToSpillBeatAbbrevResample48000.wav", "BuiltToSpillBeatAbbrevCurrentResample48000.wav")); 126 | } 127 | 128 | TEST(PhaseVocoderMediator, ResampleTest2) 129 | { 130 | PhaseVocoderMediatorUT::Resample("BuiltToSpillBeatAbbrev.wav", "BuiltToSpillBeatAbbrevCurrentResample32123.wav", 32123); 131 | EXPECT_EQ(true, Utilities::File::CheckIfFilesMatch("BuiltToSpillBeatAbbrevResample32123.wav", "BuiltToSpillBeatAbbrevCurrentResample32123.wav")); 132 | } 133 | 134 | 135 | // TODO: Will add these and more UTs after additional enhancements (like low pass filter on Resampler) are added. 136 | 137 | /* 138 | TEST(PhaseVocoderMediator, PitchShift2) 139 | { 140 | PhaseVocoderMediatorUT::PitchShift("M:\\NonSys\\DarwenAudio\\TransientTestAudio\\PianoMiddleC.wav", "p2.wav", 3.0); 141 | //EXPECT_EQ(true, Utilities::File::CheckIfFilesMatch("BuiltToSpillBeatAbbrev0.25.wav", "BuiltToSpillBeatAbbrevCurrentResult0.25.wav")); 142 | } 143 | */ 144 | 145 | /* 146 | TEST(PhaseVocoderMediator, PitchShiftResampleAndStretchTest1) 147 | { 148 | //PhaseVocoderMediatorUT::PitchShiftResampleAndStretch("SinglePianoKey.wav", "SinglePianoKeyOut.wav", 12.0, 48000, 1.5); 149 | //EXPECT_EQ(true, Utilities::File::CheckIfFilesMatch("BuiltToSpillBeatAbbrev0.25.wav", "BuiltToSpillBeatAbbrevCurrentResult0.25.wav")); 150 | 151 | PhaseVocoderMediatorUT::PitchShift("M:\\NonSys\\DarwenAudio\\TransientTestAudio\\TeenSpirit.wav", "out.wav", -2.0); 152 | } 153 | */ 154 | 155 | /* 156 | TEST(PhaseVocoderMediator, PitchShift3) 157 | { 158 | PhaseVocoderMediator phaseVocoderMediator; 159 | phaseVocoderMediator.SetInputWaveFile("M:\\NonSys\\DarwenAudio\\TransientTestAudio\\4GhostsI.wav"); 160 | phaseVocoderMediator.SetOutputWaveFile("out.wav"); 161 | phaseVocoderMediator.SetPitchShiftValue(2.0); 162 | //phaseVocoderMediator.SetTransientConfigFile("c:\\tmp\\GetLuckyTransients.yaml"); 163 | phaseVocoderMediator.Process(); 164 | } 165 | */ 166 | 167 | 168 | 169 | TEST(TransientDetectorTests, DefaultValleyToPeakRatio) 170 | { 171 | PhaseVocoderSettings phaseVocoderSettings; 172 | phaseVocoderSettings.SetInputWaveFile("SweetEmotion.wav"); 173 | phaseVocoderSettings.SetDisplayTransients(); 174 | 175 | PhaseVocoderMediator phaseVocoderMediator(phaseVocoderSettings); 176 | phaseVocoderMediator.Process(); 177 | 178 | auto transientPositions{phaseVocoderMediator.GetTransients(0)}; 179 | 180 | EXPECT_EQ(8, transientPositions.size()); 181 | if(transientPositions.size() == 8) 182 | { 183 | EXPECT_EQ(0, transientPositions[0]); 184 | EXPECT_EQ(28288, transientPositions[1]); 185 | EXPECT_EQ(56416, transientPositions[2]); 186 | EXPECT_EQ(84032, transientPositions[3]); 187 | EXPECT_EQ(97472, transientPositions[4]); 188 | EXPECT_EQ(111296, transientPositions[5]); 189 | EXPECT_EQ(125184, transientPositions[6]); 190 | EXPECT_EQ(139040, transientPositions[7]); 191 | } 192 | } 193 | 194 | TEST(TransientDetectorTests, SpecificValleyToPeakRatioSameAsDefault) 195 | { 196 | auto transientPositions{SpecificValleyToPeakRatio("SweetEmotion.wav", 1.5)}; 197 | 198 | EXPECT_EQ(8, transientPositions.size()); 199 | if(transientPositions.size() == 8) 200 | { 201 | EXPECT_EQ(0, transientPositions[0]); 202 | EXPECT_EQ(28288, transientPositions[1]); 203 | EXPECT_EQ(56416, transientPositions[2]); 204 | EXPECT_EQ(84032, transientPositions[3]); 205 | EXPECT_EQ(97472, transientPositions[4]); 206 | EXPECT_EQ(111296, transientPositions[5]); 207 | EXPECT_EQ(125184, transientPositions[6]); 208 | EXPECT_EQ(139040, transientPositions[7]); 209 | } 210 | } 211 | 212 | TEST(TransientDetectorTests, SpecifcTransientRatioTestStrict) 213 | { 214 | auto transientPositions{SpecificValleyToPeakRatio("SweetEmotion.wav", 0.5)}; 215 | 216 | EXPECT_EQ(17, transientPositions.size()); 217 | if(transientPositions.size() == 17) 218 | { 219 | EXPECT_EQ(0, transientPositions[0]); 220 | EXPECT_EQ(27968, transientPositions[1]); 221 | EXPECT_EQ(43040, transientPositions[2]); 222 | EXPECT_EQ(56224, transientPositions[3]); 223 | EXPECT_EQ(64416, transientPositions[4]); 224 | EXPECT_EQ(66080, transientPositions[5]); 225 | EXPECT_EQ(79744, transientPositions[6]); 226 | EXPECT_EQ(84032, transientPositions[7]); 227 | EXPECT_EQ(91744, transientPositions[8]); 228 | EXPECT_EQ(97376, transientPositions[9]); 229 | EXPECT_EQ(102144, transientPositions[10]); 230 | EXPECT_EQ(111104, transientPositions[11]); 231 | EXPECT_EQ(118368, transientPositions[12]); 232 | EXPECT_EQ(121344, transientPositions[13]); 233 | EXPECT_EQ(125120, transientPositions[14]); 234 | EXPECT_EQ(139040, transientPositions[15]); 235 | EXPECT_EQ(153408, transientPositions[16]); 236 | } 237 | } 238 | 239 | TEST(TransientDetectorTests, SpecifcTransientRatioRelaxed) 240 | { 241 | auto transientPositions{SpecificValleyToPeakRatio("SweetEmotion.wav", 2.0)}; 242 | 243 | EXPECT_EQ(3, transientPositions.size()); 244 | if(transientPositions.size() == 3) 245 | { 246 | EXPECT_EQ(0, transientPositions[0]); 247 | EXPECT_EQ(28512, transientPositions[1]); 248 | EXPECT_EQ(125180, transientPositions[2]); 249 | } 250 | } 251 | 252 | } 253 | -------------------------------------------------------------------------------- /Source/Application/UT/TestAudio/BuiltToSpillBeatAbbrev.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmdarwen/PhaseVocoder/4093d8bcd5d660df8db68aaf3f21bfed6ccfe1d4/Source/Application/UT/TestAudio/BuiltToSpillBeatAbbrev.wav -------------------------------------------------------------------------------- /Source/Application/UT/TestAudio/SweetEmotion.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmdarwen/PhaseVocoder/4093d8bcd5d660df8db68aaf3f21bfed6ccfe1d4/Source/Application/UT/TestAudio/SweetEmotion.wav -------------------------------------------------------------------------------- /Source/Application/UT/TestAudioResults/BuiltToSpillBeatAbbrev0.25.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmdarwen/PhaseVocoder/4093d8bcd5d660df8db68aaf3f21bfed6ccfe1d4/Source/Application/UT/TestAudioResults/BuiltToSpillBeatAbbrev0.25.wav -------------------------------------------------------------------------------- /Source/Application/UT/TestAudioResults/BuiltToSpillBeatAbbrev0.50.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmdarwen/PhaseVocoder/4093d8bcd5d660df8db68aaf3f21bfed6ccfe1d4/Source/Application/UT/TestAudioResults/BuiltToSpillBeatAbbrev0.50.wav -------------------------------------------------------------------------------- /Source/Application/UT/TestAudioResults/BuiltToSpillBeatAbbrev0.75.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmdarwen/PhaseVocoder/4093d8bcd5d660df8db68aaf3f21bfed6ccfe1d4/Source/Application/UT/TestAudioResults/BuiltToSpillBeatAbbrev0.75.wav -------------------------------------------------------------------------------- /Source/Application/UT/TestAudioResults/BuiltToSpillBeatAbbrev1.25.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmdarwen/PhaseVocoder/4093d8bcd5d660df8db68aaf3f21bfed6ccfe1d4/Source/Application/UT/TestAudioResults/BuiltToSpillBeatAbbrev1.25.wav -------------------------------------------------------------------------------- /Source/Application/UT/TestAudioResults/BuiltToSpillBeatAbbrev1.50.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmdarwen/PhaseVocoder/4093d8bcd5d660df8db68aaf3f21bfed6ccfe1d4/Source/Application/UT/TestAudioResults/BuiltToSpillBeatAbbrev1.50.wav -------------------------------------------------------------------------------- /Source/Application/UT/TestAudioResults/BuiltToSpillBeatAbbrev1.75.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmdarwen/PhaseVocoder/4093d8bcd5d660df8db68aaf3f21bfed6ccfe1d4/Source/Application/UT/TestAudioResults/BuiltToSpillBeatAbbrev1.75.wav -------------------------------------------------------------------------------- /Source/Application/UT/TestAudioResults/BuiltToSpillBeatAbbrevResample32123.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmdarwen/PhaseVocoder/4093d8bcd5d660df8db68aaf3f21bfed6ccfe1d4/Source/Application/UT/TestAudioResults/BuiltToSpillBeatAbbrevResample32123.wav -------------------------------------------------------------------------------- /Source/Application/UT/TestAudioResults/BuiltToSpillBeatAbbrevResample48000.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmdarwen/PhaseVocoder/4093d8bcd5d660df8db68aaf3f21bfed6ccfe1d4/Source/Application/UT/TestAudioResults/BuiltToSpillBeatAbbrevResample48000.wav -------------------------------------------------------------------------------- /Source/Application/UT/TestTransientConfigFiles/ChannelSpecificTransientConfigFile.yaml: -------------------------------------------------------------------------------- 1 | transients : [100, 200, 300, 400, 500] 2 | left_channel_transients : [275, 445, 550] 3 | right_channel_transients : [150, 340] 4 | -------------------------------------------------------------------------------- /Source/Application/UT/TestTransientConfigFiles/IncorrectTransientConfigFile.yaml: -------------------------------------------------------------------------------- 1 | This is not a YAML file 2 | -------------------------------------------------------------------------------- /Source/Application/UT/TestTransientConfigFiles/TransientConfigFile.yaml: -------------------------------------------------------------------------------- 1 | transients : [100, 200, 300, 400, 500] 2 | -------------------------------------------------------------------------------- /Source/Application/UT/TransientConfigFile-UT.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * PhaseVocoder 3 | * 4 | * Copyright (c) 2017 - Terence M. Darwen - tmdarwen.com 5 | * 6 | * The MIT License 7 | * 8 | * Permission is hereby granted, free of charge, to any person obtaining a copy 9 | * of this software and associated documentation files (the "Software"), to deal 10 | * in the Software without restriction, including without limitation the rights 11 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | * copies of the Software, and to permit persons to whom the Software is 13 | * furnished to do so, subject to the following conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be included in 16 | * all copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | * THE SOFTWARE. 25 | */ 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | TEST(TransientConfigFile, TestNonExistantFile) 33 | { 34 | EXPECT_THROW(TransientConfigFile("InvalidFilename"), Utilities::Exception); 35 | } 36 | 37 | TEST(TransientConfigFile, TestIllFormattedFile) 38 | { 39 | EXPECT_THROW(TransientConfigFile("IncorrectTransientConfigFile.yaml"), Utilities::Exception); 40 | } 41 | 42 | TEST(TransientConfigFile, TestGettingTransients) 43 | { 44 | TransientConfigFile transientConfigFile("TransientConfigFile.yaml"); 45 | auto transients{transientConfigFile.GetTransients()}; 46 | 47 | EXPECT_EQ(5, transients.size()); 48 | if(transients.size() == 5) 49 | { 50 | EXPECT_EQ(100, transients[0]); 51 | EXPECT_EQ(200, transients[1]); 52 | EXPECT_EQ(300, transients[2]); 53 | EXPECT_EQ(400, transients[3]); 54 | EXPECT_EQ(500, transients[4]); 55 | } 56 | } 57 | 58 | TEST(TransientConfigFile, TestGettingChannelSpecificTransients) 59 | { 60 | TransientConfigFile transientConfigFile("ChannelSpecificTransientConfigFile.yaml"); 61 | 62 | auto leftChannelTransients{transientConfigFile.GetLeftChannelTransients()}; 63 | 64 | EXPECT_EQ(8, leftChannelTransients.size()); 65 | if(leftChannelTransients.size() == 8) 66 | { 67 | EXPECT_EQ(100, leftChannelTransients[0]); 68 | EXPECT_EQ(200, leftChannelTransients[1]); 69 | EXPECT_EQ(275, leftChannelTransients[2]); 70 | EXPECT_EQ(300, leftChannelTransients[3]); 71 | EXPECT_EQ(400, leftChannelTransients[4]); 72 | EXPECT_EQ(445, leftChannelTransients[5]); 73 | EXPECT_EQ(500, leftChannelTransients[6]); 74 | EXPECT_EQ(550, leftChannelTransients[7]); 75 | } 76 | 77 | auto rightChannelTransients{transientConfigFile.GetRightChannelTransients()}; 78 | 79 | EXPECT_EQ(7, rightChannelTransients.size()); 80 | if(rightChannelTransients.size() == 7) 81 | { 82 | EXPECT_EQ(100, rightChannelTransients[0]); 83 | EXPECT_EQ(150, rightChannelTransients[1]); 84 | EXPECT_EQ(200, rightChannelTransients[2]); 85 | EXPECT_EQ(300, rightChannelTransients[3]); 86 | EXPECT_EQ(340, rightChannelTransients[4]); 87 | EXPECT_EQ(400, rightChannelTransients[5]); 88 | EXPECT_EQ(500, rightChannelTransients[6]); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /Source/Application/UT/main.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * PhaseVocoder 3 | * 4 | * Copyright (c) 2017 - Terence M. Darwen - tmdarwen.com 5 | * 6 | * The MIT License 7 | * 8 | * Permission is hereby granted, free of charge, to any person obtaining a copy 9 | * of this software and associated documentation files (the "Software"), to deal 10 | * in the Software without restriction, including without limitation the rights 11 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | * copies of the Software, and to permit persons to whom the Software is 13 | * furnished to do so, subject to the following conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be included in 16 | * all copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | * THE SOFTWARE. 25 | */ 26 | 27 | #include 28 | 29 | int main(int argc, char* argv[]) 30 | { 31 | testing::InitGoogleTest(&argc, argv); 32 | return RUN_ALL_TESTS(); 33 | } 34 | 35 | -------------------------------------------------------------------------------- /Source/Application/Usage.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * PhaseVocoder 3 | * 4 | * Copyright (c) 2017 - Terence M. Darwen - tmdarwen.com 5 | * 6 | * The MIT License 7 | * 8 | * Permission is hereby granted, free of charge, to any person obtaining a copy 9 | * of this software and associated documentation files (the "Software"), to deal 10 | * in the Software without restriction, including without limitation the rights 11 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | * copies of the Software, and to permit persons to whom the Software is 13 | * furnished to do so, subject to the following conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be included in 16 | * all copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | * THE SOFTWARE. 25 | */ 26 | 27 | #include 28 | #include 29 | 30 | #ifndef VERSION_NUMBER 31 | #define VERSION_NUMBER Non-Production Build 32 | #endif 33 | 34 | #ifndef BUILD_NUMBER 35 | #define BUILD_NUMBER Non-Production Build 36 | #endif 37 | 38 | #define MACRO_TO_STRING_INDIRECT(s) #s 39 | #define MACRO_TO_STRING(s) MACRO_TO_STRING_INDIRECT(s) 40 | 41 | void DisplayHeaderInfo() 42 | { 43 | std::cout << "PhaseVocoder" << std::endl; 44 | std::cout << "Version: " << MACRO_TO_STRING(VERSION_NUMBER) << " Build: " << MACRO_TO_STRING(BUILD_NUMBER) << std::endl; 45 | std::cout << "Built: " << __DATE__ << " " __TIME__ << std::endl; 46 | } 47 | 48 | void DisplaySimpleUsage() 49 | { 50 | std::cout << "Command Line Options:" << std::endl; 51 | std::cout << " --help (-h): This info" << std::endl; 52 | std::cout << " --longhelp (-l): More informative help info" << std::endl; 53 | std::cout << " --version (-v): Show version info" << std::endl; 54 | std::cout << " --input (-i): The input wave file you want to process" << std::endl; 55 | std::cout << " --output (-o): The resulting output wave filename" << std::endl; 56 | std::cout << " --stretch (-s): The stretch/compress ratio" << std::endl; 57 | std::cout << " --pitch (-p): Pitch adjustment in semitones" << std::endl; 58 | std::cout << " --resample (-r): Set the sample rate of the output" << std::endl; 59 | std::cout << " --peakvalleyratio (-a): Specific transient valley-to-peak ratio" << std::endl; 60 | std::cout << " --transientconfig (-c): Use a config file for input parameters" << std::endl; 61 | std::cout << " --showtransients (-t): Display transient sample positions" << std::endl; 62 | std::cout << "Usage Example: -i inputfile.wav -o outputfile.wav --stretch 1.25" << std::endl; 63 | } 64 | 65 | void DisplayLimitations() 66 | { 67 | std::cout << "Limitations: Supports 16 bit uncompressed wave files only." << std::endl; 68 | } 69 | 70 | void DisplayCopyrightInfo() 71 | { 72 | std::cout << "Copyright - Terence Darwen - Some rights reserverd" << std::endl; 73 | std::cout << "http://www.tmdarwen.com" << std::endl; 74 | } 75 | 76 | void DisplayDescription() 77 | { 78 | std::cout << "Description: " << std::endl; 79 | std::cout << " The PhaseVocoder allows for high quality stretching and compressing of audio " << std::endl; 80 | std::cout << " with respect to time. The ability to pitch shift and resample audio, as well" << std::endl; 81 | std::cout << " as detect audio transients is also included. Example usage listed below. " << std::endl; 82 | } 83 | 84 | void DisplayExamples() 85 | { 86 | std::cout << "Stretch Example:" << std::endl; 87 | std::cout << " Increase the length of the input by a factor of two:" << std::endl; 88 | std::cout << " -i in.wav -o out.wav -s 2.0" << std::endl; 89 | std::cout << std::endl; 90 | std::cout << "Compress Example:" << std::endl; 91 | std::cout << " Reduce the length of the input by twenty percent:" << std::endl; 92 | std::cout << " -input in.wav -output out.wav -stretch 0.8" << std::endl; 93 | std::cout << std::endl; 94 | std::cout << "Pitch Shift Example - Raise the pitch:" << std::endl; 95 | std::cout << " Raise the pitch of the audio by 2 semitones:" << std::endl; 96 | std::cout << " -i in.wav -o out.wav -s -p 2.0" << std::endl; 97 | std::cout << std::endl; 98 | std::cout << "Pitch Shift Example - Drop the pitch" << std::endl; 99 | std::cout << " Drop the pitch of the audio by 3.1 semitones:" << std::endl; 100 | std::cout << " -i in.wav -o out.wav -s -p -3.1" << std::endl; 101 | std::cout << std::endl; 102 | std::cout << "Resample Example" << std::endl; 103 | std::cout << " Change the sample rate to 88,200 Hz:" << std::endl; 104 | std::cout << " -i in.wav -o out.wav -s -r 88200" << std::endl; 105 | std::cout << std::endl; 106 | std::cout << "Displaying Transient Positions:" << std::endl; 107 | std::cout << " Stretch in.wav by twenty-five percent and also display the sample positions " << std::endl; 108 | std::cout << " of detected transients:" << std::endl; 109 | std::cout << " -input in.wav -output out.wav -stretch 1.25 -showtransients" << std::endl; 110 | std::cout << std::endl; 111 | std::cout << "Stretching Using a Transient Config File:" << std::endl; 112 | std::cout << " Stretch in.wav by ten percent using a config file of specific transient " << std::endl; 113 | std::cout << " positions:" << std::endl; 114 | std::cout << " -input in.wav -output out.wav -stretch 1.10 -transientconfig transients.cfg" << std::endl; 115 | } 116 | 117 | void DisplayTransientConfigExample() 118 | { 119 | std::cout << "Transient Config File Example:" << std::endl; 120 | std::cout << " The transient config file is simply a YAML file consisting of a list of " << std::endl; 121 | std::cout << " increasing integers which are the sample positions of the transients in the" << std::endl; 122 | std::cout << " input wave file. The following example shows transients occurring at four " << std::endl; 123 | std::cout << " different sample positions (100, 14700, 35329, 51922):" << std::endl; 124 | std::cout << " transients : [100, 14700, 35329, 51922]" << std::endl; 125 | } 126 | 127 | void DisplayValleyToPeakRatioInfo() 128 | { 129 | std::cout << "Valley-to-Peak Ratio:" << std::endl; 130 | std::cout << " The valley-to-peak ratio is the amount of growth in audio amplitude " << std::endl; 131 | std::cout << " occurring during a transient event. The ratio value is the required " << std::endl; 132 | std::cout << " minimum ratio for a transient to be treated as a transient event during " << std::endl; 133 | std::cout << " processing. The default ratio is 1.5. A lower value will have lower " << std::endl; 134 | std::cout << " requirements for a transient to be treated as a transient event during " << std::endl; 135 | std::cout << " processing. A higher value will have stricter requirements." << std::endl; 136 | } 137 | 138 | void DisplayShortHelp() 139 | { 140 | DisplayHeaderInfo(); 141 | DisplaySimpleUsage(); 142 | DisplayLimitations(); 143 | DisplayCopyrightInfo(); 144 | } 145 | 146 | void DisplayLongHelp() 147 | { 148 | DisplayHeaderInfo(); 149 | std::cout << std::endl; 150 | 151 | DisplayDescription(); 152 | std::cout << std::endl; 153 | 154 | DisplaySimpleUsage(); 155 | std::cout << std::endl; 156 | 157 | DisplayLimitations(); 158 | std::cout << std::endl; 159 | 160 | DisplayExamples(); 161 | std::cout << std::endl; 162 | 163 | DisplayTransientConfigExample(); 164 | std::cout << std::endl; 165 | 166 | DisplayValleyToPeakRatioInfo(); 167 | std::cout << std::endl; 168 | 169 | DisplayCopyrightInfo(); 170 | } 171 | 172 | void DisplayVersion() 173 | { 174 | DisplayHeaderInfo(); 175 | DisplayCopyrightInfo(); 176 | } 177 | -------------------------------------------------------------------------------- /Source/Application/Usage.h: -------------------------------------------------------------------------------- 1 | /* 2 | * PhaseVocoder 3 | * 4 | * Copyright (c) 2017 - Terence M. Darwen - tmdarwen.com 5 | * 6 | * The MIT License 7 | * 8 | * Permission is hereby granted, free of charge, to any person obtaining a copy 9 | * of this software and associated documentation files (the "Software"), to deal 10 | * in the Software without restriction, including without limitation the rights 11 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | * copies of the Software, and to permit persons to whom the Software is 13 | * furnished to do so, subject to the following conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be included in 16 | * all copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | * THE SOFTWARE. 25 | */ 26 | 27 | #pragma once 28 | 29 | void DisplayHeaderInfo(); 30 | void DisplaySimpleUsage(); 31 | void DisplayLimitations(); 32 | void DisplayCopyrightInfo(); 33 | void DisplayDescription(); 34 | void DisplayExamples(); 35 | void DisplayTransientConfigExample(); 36 | void DisplayValleyToPeakRatioInfo(); 37 | void DisplayShortHelp(); 38 | void DisplayLongHelp(); 39 | void DisplayVersion(); -------------------------------------------------------------------------------- /Source/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.0) 2 | 3 | set_property(GLOBAL PROPERTY USE_FOLDERS ON) 4 | 5 | project(PhaseVocoder) 6 | 7 | if(DEBUG_BUILD) 8 | add_definitions(-DDEBUG_BUILD) 9 | endif(DEBUG_BUILD) 10 | 11 | if(DEBUG_TO_LOG_FILE) 12 | add_definitions(-DDEBUG_BUILD) 13 | add_definitions(-DDEBUG_TO_LOG_FILE) 14 | endif(DEBUG_TO_LOG_FILE) 15 | 16 | # "Externals" consists of GoogleTest, YamlCpp and AudioLib 17 | include(${CMAKE_CURRENT_SOURCE_DIR}/CMakeSupport/CMakeLists.Externals.txt) 18 | include_externals() 19 | 20 | add_subdirectory(Application) 21 | -------------------------------------------------------------------------------- /Source/CMakeSupport/CMakeLists.AudioLib.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.0) 2 | 3 | project(audiolib-download NONE) 4 | 5 | include(ExternalProject) 6 | ExternalProject_Add(audiolib 7 | GIT_REPOSITORY https://github.com/tmdarwen/AudioLib 8 | GIT_TAG "v0.2.0" 9 | SOURCE_DIR "${CMAKE_BINARY_DIR}/audiolib-src" 10 | BINARY_DIR "${CMAKE_BINARY_DIR}/audiolib-build" 11 | CONFIGURE_COMMAND "" 12 | BUILD_COMMAND "" 13 | INSTALL_COMMAND "" 14 | TEST_COMMAND "" 15 | ) 16 | -------------------------------------------------------------------------------- /Source/CMakeSupport/CMakeLists.CompilerSettings.txt: -------------------------------------------------------------------------------- 1 | # Specific compiler settings we want to set in various projects 2 | 3 | if(MSVC) 4 | set(CMAKE_CXX_FLAGS "/Zi /EHsc") 5 | set(CMAKE_CXX_FLAGS_DEBUG "/Od /MTd") 6 | set(CMAKE_CXX_FLAGS_RELEASE "/Ox /MT") 7 | set(CMAKE_EXE_LINKER_FLAGS_RELEASE "/DEBUG /INCREMENTAL:NO /OPT:REF /OPT:ICF") #Generate PDBs for release build 8 | elseif(APPLE) 9 | add_definitions(-DTARGET_MAC) 10 | set(CMAKE_CXX_FLAGS "-std=c++14") 11 | elseif(UNIX) 12 | add_definitions(-DTARGET_LINUX) 13 | set(CMAKE_CXX_FLAGS "-std=c++14 -g") 14 | endif(MSVC) 15 | 16 | -------------------------------------------------------------------------------- /Source/CMakeSupport/CMakeLists.Externals.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.0) 2 | 3 | function(include_external cmake_file project_name subdir) 4 | configure_file(${cmake_file} ${project_name}-download/CMakeLists.txt) 5 | execute_process(COMMAND "${CMAKE_COMMAND}" -G "${CMAKE_GENERATOR}" . WORKING_DIRECTORY "${CMAKE_BINARY_DIR}/${project_name}-download") 6 | execute_process(COMMAND "${CMAKE_COMMAND}" --build . WORKING_DIRECTORY "${CMAKE_BINARY_DIR}/${project_name}-download" ) 7 | add_subdirectory("${CMAKE_BINARY_DIR}/${project_name}-src/${subdir}" "${CMAKE_BINARY_DIR}/${project_name}-build") 8 | endfunction() 9 | 10 | function(include_externals) 11 | # Pulldown GoogleTest 12 | if(MSVC) 13 | # The following is needed for compiling with MSVC C++ compiler v15.5 b/c of deprecation of std::tr1 namespace 14 | add_definitions(-D_SILENCE_TR1_NAMESPACE_DEPRECATION_WARNING) 15 | set(CMAKE_CXX_FLAGS_RELEASE "/MT") 16 | set(CMAKE_CXX_FLAGS_DEBUG "/MTd") 17 | set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) # Prevent GoogleTest from overriding options when building with MSVC 18 | endif(MSVC) 19 | include_external(CMakeSupport/CMakeLists.GoogleTest.txt googletest "") 20 | 21 | # Pulldown YamlCpp 22 | option(YAML_CPP_BUILD_TOOLS "Enable testing and parse tools" OFF) 23 | option(MSVC_SHARED_RT "MSVC: Build with shared runtime libs (/MD)" OFF) 24 | include_external(CMakeSupport/CMakeLists.YamlCpp.txt yamlcpp "") 25 | 26 | # Pulldown AudioLib 27 | option(INCLUDE_GOOGLE_TEST "Pulldown GoogleTest and include it in the project" OFF) 28 | include_external(CMakeSupport/CMakeLists.AudioLib.txt audiolib Source) 29 | endfunction() 30 | -------------------------------------------------------------------------------- /Source/CMakeSupport/CMakeLists.GoogleTest.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.0) 2 | 3 | project(googletest-download NONE) 4 | 5 | include(ExternalProject) 6 | ExternalProject_Add(googletest 7 | GIT_REPOSITORY https://github.com/google/googletest.git 8 | GIT_TAG release-1.8.0 9 | SOURCE_DIR "${CMAKE_BINARY_DIR}/googletest-src" 10 | BINARY_DIR "${CMAKE_BINARY_DIR}/googletest-build" 11 | CONFIGURE_COMMAND "" 12 | BUILD_COMMAND "" 13 | INSTALL_COMMAND "" 14 | TEST_COMMAND "" 15 | ) 16 | -------------------------------------------------------------------------------- /Source/CMakeSupport/CMakeLists.YamlCpp.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.0) 2 | 3 | project(yamlcpp-download NONE) 4 | 5 | include(ExternalProject) 6 | ExternalProject_Add(yamlcpp 7 | GIT_REPOSITORY https://github.com/jbeder/yaml-cpp.git 8 | GIT_TAG 0579ae3 9 | SOURCE_DIR "${CMAKE_BINARY_DIR}/yamlcpp-src" 10 | BINARY_DIR "${CMAKE_BINARY_DIR}/yamlcpp-build" 11 | CONFIGURE_COMMAND "" 12 | BUILD_COMMAND "" 13 | INSTALL_COMMAND "" 14 | TEST_COMMAND "" 15 | ) 16 | --------------------------------------------------------------------------------