├── 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 | 
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 | 
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 | 
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 | 
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 | 
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 | 
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